[libgda] GdaBrowser: improved usage of cloud widget



commit 93b70571e61e58be72aa3a4fe397cd0b9e8b80d1
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Aug 18 16:03:58 2009 +0200

    GdaBrowser: improved usage of cloud widget
    
    * created a new common/libcommon.la static library to store
      common objects
    * the objects cloud widget is now used to add tables in diagrams

 configure.in                                       |    1 +
 tools/browser/Makefile.am                          |   20 +-
 tools/browser/canvas/Makefile.am                   |    1 -
 tools/browser/canvas/browser-canvas-db-relations.c |  171 +++---
 tools/browser/common/Makefile.am                   |   30 +
 tools/browser/common/marshal.list                  |   25 +
 tools/browser/common/objects-cloud.c               |  633 ++++++++++++++++++++
 tools/browser/common/objects-cloud.h               |   69 +++
 tools/browser/{ => common}/popup-container.c       |   16 +-
 tools/browser/{ => common}/popup-container.h       |    0
 tools/browser/schema-browser/objects-index.c       |  473 +--------------
 tools/browser/schema-browser/relations-diagram.c   |    2 +-
 12 files changed, 899 insertions(+), 542 deletions(-)
---
diff --git a/configure.in b/configure.in
index 95cc9f4..c014879 100644
--- a/configure.in
+++ b/configure.in
@@ -1725,6 +1725,7 @@ control-center/Makefile
 tools/Makefile
 tools/gda-sql-4.0.1:tools/gda-sql.1.in
 tools/browser/Makefile
+tools/browser/common/Makefile
 tools/browser/schema-browser/Makefile
 tools/browser/dummy-perspective/Makefile
 tools/browser/canvas/Makefile
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 71e26de..92b314c 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -1,7 +1,7 @@
 bin_PROGRAMS=gda-browser-4.0
 noinst_PROGRAMS = favorites-test
 
-SUBDIRS = schema-browser dummy-perspective
+SUBDIRS = common schema-browser dummy-perspective
 if HAVE_GOOCANVAS
 SUBDIRS+=canvas
 noinst_PROGRAMS+=canvas-example
@@ -55,9 +55,7 @@ gda_browser_4_0_SOURCES=\
 	browser-connections-list.h \
 	main.c \
 	mgr-favorites.h \
-	mgr-favorites.c \
-	popup-container.h \
-	popup-container.c
+	mgr-favorites.c
 
 $(OBJECTS): marshal.c marshal.h
 
@@ -68,6 +66,10 @@ else
 EXTRALDFLAGS=
 endif
 
+if HAVE_GOOCANVAS
+CANVAS_LDADD=$(top_builddir)/tools/browser/canvas/libcanvas.la
+endif
+
 .rc.o:
 	$(WINDRES) $^ -o $@
 
@@ -78,14 +80,12 @@ gda_browser_4_0_LDFLAGS = $(EXTRALDFLAGS)
 gda_browser_4_0_LDADD=\
 	$(top_builddir)/tools/browser/schema-browser/libperspective.la \
 	$(top_builddir)/tools/browser/dummy-perspective/libperspective.la \
+	$(CANVAS_LDADD) \
+	$(top_builddir)/tools/browser/common/libcommon.la \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(top_builddir)/libgda-ui/libgda-ui-4.0.la \
 	$(LIBGDA_LIBS) $(GTK_LIBS)
 
-if HAVE_GOOCANVAS
-gda_browser_4_0_LDADD+= \
-	$(top_builddir)/tools/browser/canvas/libcanvas.la
-endif
 
 favorites_test_SOURCES=\
 	browser-favorites.c \
@@ -115,8 +115,8 @@ canvas_example_CFLAGS = -DCANVAS_EXAMPLE
 canvas_example_LDFLAGS = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(top_builddir)/libgda-ui/libgda-ui-4.0.la \
-	canvas/libcanvas.la
-
+	$(top_builddir)/tools/browser/canvas/libcanvas.la \
+	$(top_builddir)/tools/browser/common/libcommon.la
 
 # icons
 iconsdir=$(datadir)/pixmaps
diff --git a/tools/browser/canvas/Makefile.am b/tools/browser/canvas/Makefile.am
index 94c5251..59a2b6f 100644
--- a/tools/browser/canvas/Makefile.am
+++ b/tools/browser/canvas/Makefile.am
@@ -32,7 +32,6 @@ libcanvas_la_SOURCES = \
 	browser-canvas-utility.c \
 	browser-canvas-utility.h
 
-
 libcanvas_la_LDFLAGS = \
 	$(GOOCANVAS_LIBS) \
 	$(GRAPHVIZ_LIBS)
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
index 7894854..947cd23 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.c
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -1,6 +1,6 @@
 /* browser-canvas-db-relations.c
  *
- * Copyright (C) 2002 - 2007 Vivien Malerba
+ * Copyright (C) 2002 - 2009 Vivien Malerba
  * Copyright (C) 2002 Fernando Martins
  *
  * This program is free software; you can redistribute it and/or
@@ -27,6 +27,8 @@
 #include "browser-canvas-table.h"
 #include "browser-canvas-column.h"
 #include "browser-canvas-fkey.h"
+#include "../common/objects-cloud.h"
+#include "../common/popup-container.h"
 
 static void browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass *class);
 static void browser_canvas_db_relations_init       (BrowserCanvasDbRelations *canvas);
@@ -62,6 +64,9 @@ struct _BrowserCanvasDbRelationsPrivate
 
 	GdaMetaStruct    *mstruct;
 	GooCanvasItem    *level_separator; /* all tables items will be above this item and FK lines below */
+
+	GtkWidget        *popup_container;
+	ObjectsCloud     *cloud;
 };
 
 GType
@@ -135,6 +140,8 @@ browser_canvas_db_relations_dispose (GObject *object)
 		g_hash_table_destroy (canvas->priv->hash_tables);
 		g_hash_table_destroy (canvas->priv->hash_fkeys);
 
+		gtk_widget_destroy (canvas->priv->popup_container);
+
 		g_free (canvas->priv);
 		canvas->priv = NULL;
 	}
@@ -161,6 +168,8 @@ browser_canvas_db_relations_set_property (GObject *object,
 			canvas->priv->mstruct = g_value_get_object (value);
                         if (canvas->priv->mstruct) 
                                 g_object_ref (canvas->priv->mstruct);
+			if (canvas->priv->cloud)
+				objects_cloud_set_meta_struct (canvas->priv->cloud, canvas->priv->mstruct);
 			break;
 		}
 	}
@@ -184,6 +193,70 @@ browser_canvas_db_relations_get_property (GObject *object,
 	}
 }
 
+static void
+popup_position (PopupContainer *container, gint *out_x, gint *out_y)
+{
+	GtkWidget *canvas;
+        canvas = g_object_get_data (G_OBJECT (container), "__canvas");
+
+        gint x, y;
+        GtkRequisition req;
+
+        gtk_widget_size_request (canvas, &req);
+
+        gdk_window_get_origin (canvas->window, &x, &y);
+
+        x += canvas->allocation.x;
+        y += canvas->allocation.y;
+
+        if (x < 0)
+                x = 0;
+
+        if (y < 0)
+                y = 0;
+        *out_x = x;
+        *out_y = y;
+}
+
+static void
+cloud_object_selected_cb (ObjectsCloud *ocloud, ObjectsCloudObjType sel_type, 
+			  const gchar *sel_contents, BrowserCanvasDbRelations *dbrel)
+{
+	g_print ("-> %s\n", sel_contents);
+
+	GdaMetaTable *mtable;
+	GValue *table_schema;
+	GValue *table_name;
+	GdaQuarkList *ql;
+
+	ql = gda_quark_list_new_from_string (sel_contents);
+	g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)),
+			    gda_quark_list_find (ql, "OBJ_SCHEMA"));
+	g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)),
+			    gda_quark_list_find (ql, "OBJ_NAME"));
+	gda_quark_list_free (ql);
+
+	g_print ("Add %s.%s\n",
+		 g_value_get_string (table_schema), g_value_get_string (table_name));
+	mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
+							     NULL, table_schema, table_name, NULL);
+	if (mtable) {
+		BrowserCanvasTable *ctable;
+		GooCanvasBounds bounds;
+		gdouble x, y;
+		
+		x = BROWSER_CANVAS (dbrel)->xmouse;
+		y = BROWSER_CANVAS (dbrel)->ymouse;
+		//goo_canvas_convert_from_pixels (BROWSER_CANVAS (dbrel)->priv->goocanvas, &x, &y);
+		ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
+		goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
+		browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
+					       x - bounds.x1, y - bounds.y1);
+	}
+	else
+		g_print ("Unknown...\n");
+}
+
 /**
  * browser_canvas_db_relations_new
  * @mstruct: a #GdaMetaStruct object, or %NULL
@@ -199,17 +272,36 @@ GtkWidget *
 browser_canvas_db_relations_new (GdaMetaStruct *mstruct)
 {
 	BrowserCanvas *canvas;
+	BrowserCanvasDbRelations *dbrels;
 	GooCanvasItem *item;
+	GtkWidget *vbox, *cloud, *find;
 	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
 
 	canvas = BROWSER_CANVAS (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS, "meta-struct", mstruct, NULL));
+	dbrels = BROWSER_CANVAS_DB_RELATIONS (canvas);
 	item = goo_canvas_group_new (goo_canvas_get_root_item (canvas->priv->goocanvas), NULL);
-	BROWSER_CANVAS_DB_RELATIONS (canvas)->priv->level_separator = item;
+	dbrels->priv->level_separator = item;
 
-        return GTK_WIDGET (canvas);
-}
+	dbrels->priv->popup_container = popup_container_new_with_func (popup_position);
+	g_object_set_data (G_OBJECT (dbrels->priv->popup_container), "__canvas", canvas);
 
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (dbrels->priv->popup_container), vbox);
 
+	cloud = objects_cloud_new (mstruct, OBJECTS_CLOUD_TYPE_TABLE);
+	dbrels->priv->cloud = OBJECTS_CLOUD (cloud);
+	gtk_widget_set_size_request (GTK_WIDGET (cloud), 200, 300);
+	g_signal_connect (cloud, "selected",
+			  G_CALLBACK (cloud_object_selected_cb), dbrels);
+	gtk_box_pack_start (GTK_BOX (vbox), cloud, TRUE, TRUE, 0);
+
+	find = objects_cloud_create_filter (OBJECTS_CLOUD (cloud));
+	gtk_box_pack_start (GTK_BOX (vbox), find, FALSE, FALSE, 0);
+	
+	gtk_widget_show_all (vbox);
+
+        return GTK_WIDGET (canvas);
+}
 
 static void
 clean_canvas_items (BrowserCanvas *canvas)
@@ -391,8 +483,7 @@ static void popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *ca
 static GtkWidget *
 build_context_menu (BrowserCanvas *canvas)
 {
-	GtkWidget *menu, *mitem, *submenu, *submitem;
-	GSList *dbolist, *list;
+	GtkWidget *menu, *submitem;
 	BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
 
 	if (!dbrel->priv->mstruct)
@@ -402,44 +493,7 @@ build_context_menu (BrowserCanvas *canvas)
 	submitem = gtk_menu_item_new_with_label (_("Add table"));
 	gtk_widget_show (submitem);
 	gtk_menu_append (menu, submitem);
-	submenu = NULL;
-
-	/* build list of tables */
-	dbolist = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
-	for (list = dbolist; list; list = list->next) {
-		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
-		GdaMetaTable *mtable;
-
-		if (dbo->obj_type != GDA_META_DB_TABLE)
-			continue;
-		
-		mtable = GDA_META_TABLE (dbo);
-		if (mtable && g_hash_table_lookup (dbrel->priv->hash_tables, mtable))
-			continue; /* skip that table as it is already present in the canvas */
-
-		if (!submenu) {
-			submenu = gtk_menu_new ();
-			gtk_menu_item_set_submenu (GTK_MENU_ITEM (submitem), submenu);
-			gtk_widget_show (submenu);
-		}
-		
-		mitem = gtk_menu_item_new_with_label (dbo->obj_name);
-		gtk_widget_show (mitem);
-		gtk_menu_append (submenu, mitem);
-
-		GValue *tcatalog, *tschema, *tname;
-		g_value_set_string ((tcatalog = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
-		g_value_set_string ((tschema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
-		g_value_set_string ((tname = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
-		g_object_set_data_full (G_OBJECT (mitem), "tcat", tcatalog, (GDestroyNotify) gda_value_free);
-		g_object_set_data_full (G_OBJECT (mitem), "tschema",  tschema, (GDestroyNotify) gda_value_free);
-		g_object_set_data_full (G_OBJECT (mitem), "tname", tname, (GDestroyNotify) gda_value_free);
-		g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_add_table_cb), canvas);
-	}
-	g_slist_free (dbolist);
-
-	/* sub menu is incensitive if there are no more tables left to add */
-	gtk_widget_set_sensitive (submitem, submenu ? TRUE : FALSE);
+	g_signal_connect (G_OBJECT (submitem), "activate", G_CALLBACK (popup_add_table_cb), canvas);
 
 	return menu;
 }
@@ -447,34 +501,7 @@ build_context_menu (BrowserCanvas *canvas)
 static void
 popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
 {
-	GdaMetaTable *mtable;
-	GValue *table_catalog;
-	GValue *table_schema;
-	GValue *table_name;
-
-	table_catalog = g_object_get_data (G_OBJECT (mitem), "tcat");
-	table_schema = g_object_get_data (G_OBJECT (mitem), "tschema");
-	table_name = g_object_get_data (G_OBJECT (mitem), "tname");
-
-	/*g_print ("Add %s.%s.%s\n", g_value_get_string (table_catalog), 
-	  g_value_get_string (table_schema), g_value_get_string (table_name));*/
-	mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
-							     table_catalog, table_schema, table_name, NULL);
-	if (mtable) {
-		BrowserCanvasTable *ctable;
-		GooCanvasBounds bounds;
-		gdouble x, y;
-		
-		x = BROWSER_CANVAS (dbrel)->xmouse;
-		y = BROWSER_CANVAS (dbrel)->ymouse;
-		//goo_canvas_convert_from_pixels (BROWSER_CANVAS (dbrel)->priv->goocanvas, &x, &y);
-		ctable = browser_canvas_db_relations_add_table (dbrel, table_catalog, table_schema, table_name);
-		goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
-		browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
-					       x - bounds.x1, y - bounds.y1);
-	}
-	else
-		g_print ("Unknown...\n");
+	gtk_widget_show (dbrel->priv->popup_container);
 }
 
 /**
diff --git a/tools/browser/common/Makefile.am b/tools/browser/common/Makefile.am
new file mode 100644
index 0000000..3996a3b
--- /dev/null
+++ b/tools/browser/common/Makefile.am
@@ -0,0 +1,30 @@
+noinst_LTLIBRARIES = libcommon.la
+
+AM_CPPFLAGS = \
+	-I$(top_builddir) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libgda \
+	$(LIBGDA_CFLAGS) \
+	$(GTK_CFLAGS)
+
+marshal.h: marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=_common_marshal > $@
+marshal.c: marshal.list $(GLIB_GENMARSHAL) marshal.h
+	$(GLIB_GENMARSHAL) $< --body --prefix=_common_marshal > $@
+
+libcommon_la_SOURCES = \
+	marshal.c \
+	marshal.h \
+	objects-cloud.c \
+	objects-cloud.h \
+	popup-container.c \
+	popup-container.h
+
+$(OBJECTS): marshal.c marshal.h
+
+EXTRA_DIST = \
+	marshal.list
+
+CLEANFILES = \
+	marshal.h \
+	marshal.c
diff --git a/tools/browser/common/marshal.list b/tools/browser/common/marshal.list
new file mode 100644
index 0000000..c4ac747
--- /dev/null
+++ b/tools/browser/common/marshal.list
@@ -0,0 +1,25 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+#   VOID        indicates   no   return   type,  or  no  extra
+#               parameters. if VOID is used as  the  parameter
+#               list, no additional parameters may be present.
+#   BOOLEAN     for boolean types (gboolean)
+#   CHAR        for signed char types (gchar)
+#   UCHAR       for unsigned char types (guchar)
+#   INT         for signed integer types (gint)
+#   UINT        for unsigned integer types (guint)
+#   LONG        for signed long integer types (glong)
+#   ULONG       for unsigned long integer types (gulong)
+#   ENUM        for enumeration types (gint)
+#   FLAGS       for flag enumeration types (guint)
+#   FLOAT       for single-precision float types (gfloat)
+#   DOUBLE      for double-precision float types (gdouble)
+#   STRING      for string types (gchar*)
+#   PARAM       for GParamSpec or derived types  (GParamSpec*)
+#   BOXED       for boxed (anonymous but reference counted) types (GBoxed*)
+#   POINTER     for anonymous pointer types (gpointer)
+#   OBJECT      for GObject or derived types (GObject*)
+#   NONE        deprecated alias for VOID
+#   BOOL        deprecated alias for BOOLEAN
+VOID:ENUM,STRING
+VOID:INT,ENUM,STRING
diff --git a/tools/browser/common/objects-cloud.c b/tools/browser/common/objects-cloud.c
new file mode 100644
index 0000000..04b4f24
--- /dev/null
+++ b/tools/browser/common/objects-cloud.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <libgda/gda-tree.h>
+#include "objects-cloud.h"
+#include <libgda-ui/gdaui-tree-store.h>
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "marshal.h"
+#include <gdk/gdkkeysyms.h>
+#include "popup-container.h"
+
+struct _ObjectsCloudPrivate {
+	gboolean            show_schemas;
+	ObjectsCloudObjType type;
+	GdaMetaStruct       *mstruct;
+	GtkTextBuffer       *tbuffer;
+	GtkWidget           *tview;
+
+	gboolean             hovering_over_link;
+};
+
+static void objects_cloud_class_init (ObjectsCloudClass *klass);
+static void objects_cloud_init       (ObjectsCloud *cloud,
+				      ObjectsCloudClass *klass);
+static void objects_cloud_dispose   (GObject *object);
+
+enum {
+	SELECTED,
+	LAST_SIGNAL
+};
+
+static guint objects_cloud_signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * ObjectsCloud class implementation
+ */
+
+static void
+objects_cloud_class_init (ObjectsCloudClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* signals */
+	objects_cloud_signals [SELECTED] =
+                g_signal_new ("selected",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (ObjectsCloudClass, selected),
+                              NULL, NULL,
+                              _common_marshal_VOID__ENUM_STRING, G_TYPE_NONE,
+                              2, G_TYPE_UINT, G_TYPE_STRING);
+	klass->selected = NULL;
+
+	object_class->dispose = objects_cloud_dispose;
+}
+
+
+static void
+objects_cloud_init (ObjectsCloud *cloud, ObjectsCloudClass *klass)
+{
+	cloud->priv = g_new0 (ObjectsCloudPrivate, 1);
+	cloud->priv->show_schemas = FALSE;
+	cloud->priv->tbuffer = gtk_text_buffer_new (NULL);
+	gtk_text_buffer_create_tag (cloud->priv->tbuffer, "section",
+                                    "weight", PANGO_WEIGHT_BOLD,
+                                    "foreground", "blue", NULL);
+	gtk_text_buffer_create_tag (cloud->priv->tbuffer, "size0",
+                                    "scale", PANGO_SCALE_MEDIUM, NULL);
+	gtk_text_buffer_create_tag (cloud->priv->tbuffer, "size1",
+                                    "scale", PANGO_SCALE_LARGE, NULL);
+	gtk_text_buffer_create_tag (cloud->priv->tbuffer, "size2",
+                                    "scale", PANGO_SCALE_LARGE, NULL);
+}
+
+static void
+objects_cloud_dispose (GObject *object)
+{
+	ObjectsCloud *cloud = (ObjectsCloud *) object;
+
+	/* free memory */
+	if (cloud->priv) {
+		if (cloud->priv->mstruct)
+			g_object_unref (cloud->priv->mstruct);
+		g_object_unref (cloud->priv->tbuffer);
+		g_free (cloud->priv);
+		cloud->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GType
+objects_cloud_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (ObjectsCloudClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) objects_cloud_class_init,
+			NULL,
+			NULL,
+			sizeof (ObjectsCloud),
+			0,
+			(GInstanceInitFunc) objects_cloud_init
+		};
+		type = g_type_register_static (GTK_TYPE_VBOX, "ObjectsCloud",
+					       &info, 0);
+	}
+	return type;
+}
+
+typedef struct {
+	gchar *schema;
+	GtkTextMark *mark;
+} SchemaData;
+static void add_to_schema_data (ObjectsCloud *cloud, SchemaData *sd, GdaMetaDbObject *dbo);
+static void
+update_display (ObjectsCloud *cloud)
+{
+	gchar *str;
+	GSList *schemas = NULL; /* holds pointers to @SchemaData structures */
+	SchemaData *default_sd = NULL, *sd;
+	
+	GtkTextBuffer *tbuffer;
+        GtkTextIter start, end;
+
+        /* clean all */
+        tbuffer = cloud->priv->tbuffer;
+        gtk_text_buffer_get_start_iter (tbuffer, &start);
+        gtk_text_buffer_get_end_iter (tbuffer, &end);
+        gtk_text_buffer_delete (tbuffer, &start, &end);
+
+	default_sd = g_new0 (SchemaData, 1);
+	default_sd->schema = NULL;
+	default_sd->mark = gtk_text_mark_new (NULL, TRUE);
+	gtk_text_buffer_get_end_iter (tbuffer, &end);
+	
+	gtk_text_buffer_get_end_iter (tbuffer, &end);
+	if (cloud->priv->show_schemas) {
+		gtk_text_buffer_insert_pixbuf (tbuffer, &end,
+					       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
+		gtk_text_buffer_insert (tbuffer, &end, " ", 1);
+		gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
+							  _("Tables:"), -1, "section", NULL);
+		gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
+	}
+	gtk_text_buffer_add_mark (tbuffer, default_sd->mark, &end);
+
+
+	GdaMetaStruct *mstruct;
+	GSList *dbo_list, *list;
+	mstruct = cloud->priv->mstruct;
+	if (!mstruct) {
+		/* nothing to display */
+		return;
+	}
+	dbo_list = g_slist_reverse (gda_meta_struct_get_all_db_objects (mstruct));
+	for (list = dbo_list; list; list = list->next) {
+		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+		GSList *list;
+		gboolean is_default;
+
+		if (dbo->obj_type != GDA_META_DB_TABLE)
+			continue;
+		g_assert (dbo->obj_schema);
+
+		is_default = strcmp (dbo->obj_short_name, dbo->obj_full_name) ? TRUE : FALSE;
+		sd = NULL;
+		if (cloud->priv->show_schemas) {
+			for (list = schemas; list; list = list->next) {
+				if (!strcmp (((SchemaData *) list->data)->schema, dbo->obj_schema)) {
+					sd = (SchemaData *) list->data;
+					break;
+				}
+			}
+			if (!sd) {
+				sd = g_new0 (SchemaData, 1);
+				sd->schema = g_strdup (dbo->obj_schema);
+				sd->mark = gtk_text_mark_new (NULL, TRUE);
+				
+				gtk_text_buffer_get_end_iter (tbuffer, &end);
+				gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
+				gtk_text_buffer_insert_pixbuf (tbuffer, &end,
+							       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
+				gtk_text_buffer_insert (tbuffer, &end, " ", 1);
+				str = g_strdup_printf (_("Tables in schema '%s':"), sd->schema);
+				gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
+									  str, -1, "section", NULL);
+				g_free (str);
+				gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
+				
+				gtk_text_buffer_add_mark (tbuffer, sd->mark, &end);
+				
+				schemas = g_slist_append (schemas, sd);
+			}
+
+			add_to_schema_data (cloud, sd, dbo);
+		}
+		if (is_default)
+			add_to_schema_data (cloud, default_sd, dbo);
+	}
+	g_slist_free (dbo_list);
+
+	if (default_sd)
+		schemas = g_slist_prepend (schemas, default_sd);
+
+	/* get rid of the SchemaData structures */
+	for (list = schemas; list; list = list->next) {
+		sd = (SchemaData*) list->data;
+
+		g_free (sd->schema);
+		g_object_unref (sd->mark);
+		g_free (sd);
+	}
+}
+
+static void
+add_to_schema_data (ObjectsCloud *cloud, SchemaData *sd, GdaMetaDbObject *dbo)
+{
+	GtkTextTag *tag;
+	GtkTextIter iter;
+	gdouble scale = 1.0;
+
+	if (dbo->obj_type == GDA_META_DB_TABLE) {
+		scale = 1.0 + g_slist_length (dbo->depend_list) / 5.;
+	}
+
+	gtk_text_buffer_get_iter_at_mark (cloud->priv->tbuffer, &iter, sd->mark);
+	tag = gtk_text_buffer_create_tag (cloud->priv->tbuffer, NULL, 
+					  "foreground", "#6161F2", 
+					  //"underline", PANGO_UNDERLINE_SINGLE,
+					  "scale", scale,
+					  NULL);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_schema", g_strdup (dbo->obj_schema), g_free);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_name", g_strdup (dbo->obj_name), g_free);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_short_name", g_strdup (dbo->obj_short_name), g_free);
+
+	gtk_text_buffer_insert_with_tags (cloud->priv->tbuffer, &iter, dbo->obj_name, -1,
+					  tag, NULL);
+	gtk_text_buffer_insert (cloud->priv->tbuffer, &iter, " ", -1);
+}
+
+static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsCloud *cloud);
+static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsCloud *cloud);
+static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsCloud *cloud);
+static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsCloud *cloud);
+
+static void
+text_tag_table_foreach_cb (GtkTextTag *tag, const gchar *find)
+{
+	const gchar *name;
+
+	name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+
+	if (!name)
+		return;
+	
+	if (!*find) {
+		g_object_set (tag, "foreground", "#6161F2", NULL);
+	}
+	else {
+		gchar *ptr;
+		gchar *lcname, *lcfind;
+		lcname = g_utf8_strdown (name, -1);
+		lcfind = g_utf8_strdown (find, -1);
+
+		ptr = strstr (lcname, lcfind);
+		if (!ptr) {
+			/* string not present in name */
+			g_object_set (tag, "foreground", "#DBDBDB", NULL);
+		}
+		else if ((ptr == lcname) ||
+			 ((*name == '"') && (ptr == lcname+1))) {
+			/* string present as start of name */
+			g_object_set (tag, "foreground", "#6161F2", NULL);
+		}
+		else {
+			/* string present in name but not at the start */
+			g_object_set (tag, "foreground", "#A0A0A0", NULL);
+		}
+
+		g_free (lcname);
+		g_free (lcfind);
+	}
+}
+
+void
+objects_cloud_filter (ObjectsCloud *cloud, const gchar *filter)
+{
+	g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
+
+	GtkTextTagTable *tags_table = gtk_text_buffer_get_tag_table (cloud->priv->tbuffer);
+
+	gtk_text_tag_table_foreach (tags_table, (GtkTextTagTableForeach) text_tag_table_foreach_cb,
+				    (gpointer) filter);
+}
+
+/**
+ * objects_cloud_new
+ *
+ *
+ *
+ * Returns:
+ */
+GtkWidget *
+objects_cloud_new (GdaMetaStruct *mstruct, ObjectsCloudObjType type)
+{
+	ObjectsCloud *cloud;
+
+	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
+	cloud = OBJECTS_CLOUD (g_object_new (OBJECTS_CLOUD_TYPE, NULL));
+
+	if (mstruct)
+		cloud->priv->mstruct = g_object_ref (mstruct);
+	cloud->priv->type = type;
+
+	/* text contents */
+	GtkWidget *sw, *vbox;
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_box_pack_start (GTK_BOX (cloud), sw, TRUE, TRUE, 0);
+	
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
+
+	cloud->priv->tview = gtk_text_view_new_with_buffer (cloud->priv->tbuffer);
+	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (cloud->priv->tview), GTK_WRAP_WORD);
+	gtk_text_view_set_editable (GTK_TEXT_VIEW (cloud->priv->tview), FALSE);
+	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (cloud->priv->tview), FALSE);
+	gtk_box_pack_start (GTK_BOX (vbox), cloud->priv->tview, TRUE, TRUE, 0);
+	gtk_widget_show_all (sw);
+
+	g_signal_connect (cloud->priv->tview, "key-press-event", 
+			  G_CALLBACK (key_press_event), cloud);
+	g_signal_connect (cloud->priv->tview, "event-after", 
+			  G_CALLBACK (event_after), cloud);
+	g_signal_connect (cloud->priv->tview, "motion-notify-event", 
+			  G_CALLBACK (motion_notify_event), cloud);
+	g_signal_connect (cloud->priv->tview, "visibility-notify-event", 
+			  G_CALLBACK (visibility_notify_event), cloud);
+
+	/* initial update */
+	update_display (cloud);
+
+	return (GtkWidget*) cloud;
+}
+
+/**
+ * objects_cloud_set_meta_struct
+ *
+ */
+void
+objects_cloud_set_meta_struct (ObjectsCloud *cloud, GdaMetaStruct *mstruct)
+{
+	g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
+	g_return_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct));
+
+	if (cloud->priv->mstruct) {
+		g_object_unref (cloud->priv->mstruct);
+		cloud->priv->mstruct = NULL;
+	}
+	if (mstruct)
+		cloud->priv->mstruct = g_object_ref (mstruct);
+	update_display (cloud);
+}
+
+/**
+ * objects_cloud_show_schemas
+ */
+void
+objects_cloud_show_schemas (ObjectsCloud *cloud, gboolean show_schemas)
+{
+	g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
+
+	cloud->priv->show_schemas = show_schemas;
+	update_display (cloud);
+}
+
+static void
+find_entry_changed_cb (GtkWidget *entry, ObjectsCloud *cloud)
+{
+	gchar *find = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
+	objects_cloud_filter (cloud, find);
+	g_free (find);
+}
+
+/**
+ * objects_cloud_create_filter
+ */
+GtkWidget *
+objects_cloud_create_filter (ObjectsCloud *cloud)
+{
+	GtkWidget *hbox, *label, *wid;
+	g_return_val_if_fail (IS_OBJECTS_CLOUD (cloud), NULL);
+	
+	hbox = gtk_hbox_new (FALSE, 0);
+
+	label = gtk_label_new (_("Find:"));
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+	wid = gtk_entry_new ();
+	g_signal_connect (wid, "changed",
+			  G_CALLBACK (find_entry_changed_cb), cloud);
+	gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, TRUE, 0);
+	gtk_widget_show_all (hbox);
+	gtk_widget_hide (hbox);
+
+	return hbox;
+}
+
+static GdkCursor *hand_cursor = NULL;
+static GdkCursor *regular_cursor = NULL;
+
+/* Looks at all tags covering the position (x, y) in the text view, 
+ * and if one of them is a link, change the cursor to the "hands" cursor
+ * typically used by web browsers.
+ */
+static void
+set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ObjectsCloud *cloud)
+{
+	GSList *tags = NULL, *tagp = NULL;
+	GtkTextIter iter;
+	gboolean hovering = FALSE;
+	
+	gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+	
+	tags = gtk_text_iter_get_tags (&iter);
+	for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
+		GtkTextTag *tag = tagp->data;
+		gchar *table_name;
+		
+		table_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+		if (table_name) {
+			hovering = TRUE;
+			break;
+		}
+	}
+	
+	if (hovering != cloud->priv->hovering_over_link) {
+		cloud->priv->hovering_over_link = hovering;
+		
+		if (cloud->priv->hovering_over_link) {
+			if (! hand_cursor)
+				hand_cursor = gdk_cursor_new (GDK_HAND2);
+			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+									 GTK_TEXT_WINDOW_TEXT),
+					       hand_cursor);
+		}
+		else {
+			if (!regular_cursor)
+				regular_cursor = gdk_cursor_new (GDK_XTERM);
+			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+									 GTK_TEXT_WINDOW_TEXT),
+					       regular_cursor);
+		}
+	}
+	
+	if (tags) 
+		g_slist_free (tags);
+}
+
+/* 
+ * Also update the cursor image if the window becomes visible
+ * (e.g. when a window covering it got iconified).
+ */
+static gboolean
+visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsCloud *cloud)
+{
+	gint wx, wy, bx, by;
+	
+	gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       wx, wy, &bx, &by);
+	
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, cloud);
+	
+	return FALSE;
+}
+
+/*
+ * Update the cursor image if the pointer moved. 
+ */
+static gboolean
+motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsCloud *cloud)
+{
+	gint x, y;
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       event->x, event->y, &x, &y);
+	
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, cloud);
+	
+	gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
+	return FALSE;
+}
+
+
+/* Looks at all tags covering the position of iter in the text view, 
+ * and if one of them is a link, follow it by showing the page identified
+ * by the data attached to it.
+ */
+static void
+follow_if_link (GtkWidget *text_view, GtkTextIter *iter, ObjectsCloud *cloud)
+{
+	GSList *tags = NULL, *tagp = NULL;
+	
+	tags = gtk_text_iter_get_tags (iter);
+	for (tagp = tags;  tagp;  tagp = tagp->next) {
+		GtkTextTag *tag = tagp->data;
+		const gchar *schema;
+		
+		schema = g_object_get_data (G_OBJECT (tag), "dbo_obj_schema");
+		if (schema) {
+			const gchar *objects_name, *short_name;
+		
+			objects_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+			short_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_short_name");
+			
+			if (objects_name && short_name) {
+				gchar *str, *tmp1, *tmp2, *tmp3;
+				tmp1 = gda_rfc1738_encode (schema);
+				tmp2 = gda_rfc1738_encode (objects_name);
+				tmp3 = gda_rfc1738_encode (short_name);
+				str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s",
+						       tmp1, tmp2, tmp3);
+				g_free (tmp1);
+				g_free (tmp2);
+				g_free (tmp3);
+				g_signal_emit (cloud, objects_cloud_signals [SELECTED], 0,
+					       cloud->priv->type, str);
+				g_free (str);
+			}
+		}
+        }
+
+	if (tags) 
+		g_slist_free (tags);
+}
+
+/*
+ * Links can also be activated by clicking.
+ */
+static gboolean
+event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsCloud *cloud)
+{
+	GtkTextIter start, end, iter;
+	GtkTextBuffer *buffer;
+	GdkEventButton *event;
+	gint x, y;
+	
+	if (ev->type != GDK_BUTTON_RELEASE)
+		return FALSE;
+	
+	event = (GdkEventButton *)ev;
+	
+	if (event->button != 1)
+		return FALSE;
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+	
+	/* we shouldn't follow a link if the user has selected something */
+	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+		return FALSE;
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       event->x, event->y, &x, &y);
+	
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+	
+	follow_if_link (text_view, &iter, cloud);
+	
+	return FALSE;
+}
+
+/* 
+ * Links can be activated by pressing Enter.
+ */
+static gboolean
+key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsCloud *cloud)
+{
+	GtkTextIter iter;
+	GtkTextBuffer *buffer;
+	
+	switch (event->keyval) {
+	case GDK_Return: 
+	case GDK_KP_Enter:
+		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+		gtk_text_buffer_get_iter_at_mark (buffer, &iter, 
+						  gtk_text_buffer_get_insert (buffer));
+		follow_if_link (text_view, &iter, cloud);
+		break;
+		
+	default:
+		break;
+	}
+	return FALSE;
+}
diff --git a/tools/browser/common/objects-cloud.h b/tools/browser/common/objects-cloud.h
new file mode 100644
index 0000000..e92a853
--- /dev/null
+++ b/tools/browser/common/objects-cloud.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OBJECTS_CLOUD_H__
+#define __OBJECTS_CLOUD_H__
+
+#include <gtk/gtkvbox.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define OBJECTS_CLOUD_TYPE            (objects_cloud_get_type())
+#define OBJECTS_CLOUD(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, OBJECTS_CLOUD_TYPE, ObjectsCloud))
+#define OBJECTS_CLOUD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, OBJECTS_CLOUD_TYPE, ObjectsCloudClass))
+#define IS_OBJECTS_CLOUD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, OBJECTS_CLOUD_TYPE))
+#define IS_OBJECTS_CLOUD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), OBJECTS_CLOUD_TYPE))
+
+typedef struct _ObjectsCloud        ObjectsCloud;
+typedef struct _ObjectsCloudClass   ObjectsCloudClass;
+typedef struct _ObjectsCloudPrivate ObjectsCloudPrivate;
+
+typedef enum {
+	OBJECTS_CLOUD_TYPE_TABLE,
+	OBJECTS_CLOUD_TYPE_VIEW,
+} ObjectsCloudObjType;
+
+struct _ObjectsCloud {
+	GtkVBox               parent;
+	ObjectsCloudPrivate  *priv;
+};
+
+struct _ObjectsCloudClass {
+	GtkVBoxClass          parent_class;
+
+	/* signals */
+	void                (*selected) (ObjectsCloud *sel, ObjectsCloudObjType sel_type, 
+					 const gchar *sel_contents);
+};
+
+GType                    objects_cloud_get_type (void) G_GNUC_CONST;
+
+GtkWidget               *objects_cloud_new      (GdaMetaStruct *mstruct, ObjectsCloudObjType type);
+void                     objects_cloud_set_meta_struct (ObjectsCloud *cloud, GdaMetaStruct *mstruct);
+void                     objects_cloud_show_schemas (ObjectsCloud *cloud, gboolean show_schemas);
+void                     objects_cloud_filter   (ObjectsCloud *cloud, const gchar *filter);
+GtkWidget               *objects_cloud_create_filter (ObjectsCloud *cloud);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/popup-container.c b/tools/browser/common/popup-container.c
similarity index 95%
rename from tools/browser/popup-container.c
rename to tools/browser/common/popup-container.c
index cda8573..e259f32 100644
--- a/tools/browser/popup-container.c
+++ b/tools/browser/common/popup-container.c
@@ -252,21 +252,21 @@ popup_container_get_type (void)
 static void
 popup_position (PopupContainer *container, gint *out_x, gint *out_y)
 {
-	GtkWidget *button;
-	button = g_object_get_data (G_OBJECT (container), "__poswidget");
+	GtkWidget *poswidget;
+	poswidget = g_object_get_data (G_OBJECT (container), "__poswidget");
 
 	gint x, y;
         gint bwidth, bheight;
         GtkRequisition req;
 
-        gtk_widget_size_request (button, &req);
+        gtk_widget_size_request (poswidget, &req);
 
-        gdk_window_get_origin (button->window, &x, &y);
+        gdk_window_get_origin (poswidget->window, &x, &y);
 
-        x += button->allocation.x;
-        y += button->allocation.y;
-        bwidth = button->allocation.width;
-        bheight = button->allocation.height;
+        x += poswidget->allocation.x;
+        y += poswidget->allocation.y;
+        bwidth = poswidget->allocation.width;
+        bheight = poswidget->allocation.height;
 
         x += bwidth - req.width;
         y += bheight;
diff --git a/tools/browser/popup-container.h b/tools/browser/common/popup-container.h
similarity index 100%
rename from tools/browser/popup-container.h
rename to tools/browser/common/popup-container.h
diff --git a/tools/browser/schema-browser/objects-index.c b/tools/browser/schema-browser/objects-index.c
index 772f44c..9f8c58e 100644
--- a/tools/browser/schema-browser/objects-index.c
+++ b/tools/browser/schema-browser/objects-index.c
@@ -31,15 +31,13 @@
 #include "../cc-gray-bar.h"
 #include "marshal.h"
 #include <gdk/gdkkeysyms.h>
-#include "../popup-container.h"
+#include "../common/popup-container.h"
+#include "../common/objects-cloud.h"
 
 struct _ObjectsIndexPrivate {
 	BrowserConnection *bcnc;
-	GtkTextBuffer     *tbuffer;
-	GtkWidget         *tview;
+	ObjectsCloud      *cloud;
 	GtkWidget         *popup_container;
-
-	gboolean hovering_over_link;
 };
 
 static void objects_index_class_init (ObjectsIndexClass *klass);
@@ -88,16 +86,6 @@ static void
 objects_index_init (ObjectsIndex *index, ObjectsIndexClass *klass)
 {
 	index->priv = g_new0 (ObjectsIndexPrivate, 1);
-	index->priv->tbuffer = gtk_text_buffer_new (NULL);
-	gtk_text_buffer_create_tag (index->priv->tbuffer, "section",
-                                    "weight", PANGO_WEIGHT_BOLD,
-                                    "foreground", "blue", NULL);
-	gtk_text_buffer_create_tag (index->priv->tbuffer, "size0",
-                                    "scale", PANGO_SCALE_MEDIUM, NULL);
-	gtk_text_buffer_create_tag (index->priv->tbuffer, "size1",
-                                    "scale", PANGO_SCALE_LARGE, NULL);
-	gtk_text_buffer_create_tag (index->priv->tbuffer, "size2",
-                                    "scale", PANGO_SCALE_LARGE, NULL);
 }
 
 static void
@@ -113,7 +101,6 @@ objects_index_dispose (GObject *object)
 							      G_CALLBACK (meta_changed_cb), index);
 			g_object_unref (index->priv->bcnc);
 		}
-		g_object_unref (index->priv->tbuffer);
 		g_free (index->priv);
 		index->priv = NULL;
 	}
@@ -144,209 +131,13 @@ objects_index_get_type (void)
 	return type;
 }
 
-typedef struct {
-	gchar *schema;
-	GtkTextMark *mark;
-} SchemaData;
-static void add_to_schema_data (ObjectsIndex *index, SchemaData *sd, GdaMetaDbObject *dbo);
 static void
-update_display (ObjectsIndex *index)
+cloud_object_selected_cb (ObjectsCloud *sel, ObjectsCloudObjType sel_type, 
+			  const gchar *sel_contents, ObjectsIndex *index)
 {
-	gchar *str;
-	GSList *schemas = NULL; /* holds pointers to @SchemaData structures */
-	SchemaData *default_sd = NULL, *sd;
-	
-	GtkTextBuffer *tbuffer;
-        GtkTextIter start, end;
-
-        /* clean all */
-        tbuffer = index->priv->tbuffer;
-        gtk_text_buffer_get_start_iter (tbuffer, &start);
-        gtk_text_buffer_get_end_iter (tbuffer, &end);
-        gtk_text_buffer_delete (tbuffer, &start, &end);
-
-	default_sd = g_new0 (SchemaData, 1);
-	default_sd->schema = NULL;
-	default_sd->mark = gtk_text_mark_new (NULL, TRUE);
-	gtk_text_buffer_get_end_iter (tbuffer, &end);
-	
-	gtk_text_buffer_get_end_iter (tbuffer, &end);
-	gtk_text_buffer_insert_pixbuf (tbuffer, &end,
-				       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
-	gtk_text_buffer_insert (tbuffer, &end, " ", 1);
-	gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
-						  _("Default tables:"), -1, "section", NULL);
-	gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
-
-	gtk_text_buffer_add_mark (tbuffer, default_sd->mark, &end);
-
-
-	GdaMetaStruct *mstruct;
-	GSList *dbo_list, *list;
-	mstruct = browser_connection_get_meta_struct (index->priv->bcnc);
-	if (!mstruct) {
-		/* not yet ready */
-		return;
-	}
-	dbo_list = g_slist_reverse (gda_meta_struct_get_all_db_objects (mstruct));
-	for (list = dbo_list; list; list = list->next) {
-		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
-		GSList *list;
-		gboolean is_default;
-
-		if (dbo->obj_type != GDA_META_DB_TABLE)
-			continue;
-		g_assert (dbo->obj_schema);
-
-		is_default = strcmp (dbo->obj_short_name, dbo->obj_full_name) ? TRUE : FALSE;
-		sd = NULL;
-		for (list = schemas; list; list = list->next) {
-			if (!strcmp (((SchemaData *) list->data)->schema, dbo->obj_schema)) {
-				sd = (SchemaData *) list->data;
-				break;
-			}
-		}
-		if (!sd) {
-			sd = g_new0 (SchemaData, 1);
-			sd->schema = g_strdup (dbo->obj_schema);
-			sd->mark = gtk_text_mark_new (NULL, TRUE);
-			
-			gtk_text_buffer_get_end_iter (tbuffer, &end);
-			gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
-			gtk_text_buffer_insert_pixbuf (tbuffer, &end,
-						       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
-			gtk_text_buffer_insert (tbuffer, &end, " ", 1);
-			str = g_strdup_printf (_("Tables in schema '%s':"), sd->schema);
-			gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
-								  str, -1, "section", NULL);
-			g_free (str);
-			gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
-
-			gtk_text_buffer_add_mark (tbuffer, sd->mark, &end);
-
-			schemas = g_slist_append (schemas, sd);
-		}
-
-		add_to_schema_data (index, sd, dbo);
-		if (is_default)
-			add_to_schema_data (index, default_sd, dbo);
-	}
-	g_slist_free (dbo_list);
-
-	if (default_sd)
-		schemas = g_slist_prepend (schemas, default_sd);
-
-	/* get rid of the SchemaData structures */
-	for (list = schemas; list; list = list->next) {
-		sd = (SchemaData*) list->data;
-
-		g_free (sd->schema);
-		g_object_unref (sd->mark);
-		g_free (sd);
-	}
-}
-
-static gchar *
-double_underscores (const gchar *str)
-{
-	gchar *out, *outptr;
-	gint len;
-	if (!str)
-		return NULL;
-
-	len = strlen (str);
-	out = g_malloc (sizeof (gchar) * (len * 2 +1));
-	for (outptr = out; *str; str++, outptr++) {
-		if (*str == '_') {
-			*outptr = '_';
-			outptr++;
-		}
-		*outptr = *str;
-	}
-	*outptr = 0;
-
-	return out;
-}
-
-static void
-add_to_schema_data (ObjectsIndex *index, SchemaData *sd, GdaMetaDbObject *dbo)
-{
-	GtkTextTag *tag;
-	GtkTextIter iter;
-	gdouble scale = 1.0;
-
-	if (dbo->obj_type == GDA_META_DB_TABLE) {
-		scale = 1.0 + g_slist_length (dbo->depend_list) / 5.;
-	}
-
-	gtk_text_buffer_get_iter_at_mark (index->priv->tbuffer, &iter, sd->mark);
-	tag = gtk_text_buffer_create_tag (index->priv->tbuffer, NULL, 
-					  "foreground", "#6161F2", 
-					  //"underline", PANGO_UNDERLINE_SINGLE,
-					  "scale", scale,
-					  NULL);
-	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_schema", g_strdup (dbo->obj_schema), g_free);
-	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_name", g_strdup (dbo->obj_name), g_free);
-	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_short_name", g_strdup (dbo->obj_short_name), g_free);
-
-	gtk_text_buffer_insert_with_tags (index->priv->tbuffer, &iter, dbo->obj_name, -1,
-					  tag, NULL);
-	gtk_text_buffer_insert (index->priv->tbuffer, &iter, " ", -1);
-}
-
-static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsIndex *index);
-static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsIndex *index);
-static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsIndex *index);
-static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsIndex *index);
-
-static void
-text_tag_table_foreach_cb (GtkTextTag *tag, const gchar *find)
-{
-	const gchar *name;
-
-	name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
-
-	if (!name)
-		return;
-	
-	if (!*find) {
-		g_object_set (tag, "foreground", "#6161F2", NULL);
-	}
-	else {
-		gchar *ptr;
-		gchar *lcname, *lcfind;
-		lcname = g_utf8_strdown (name, -1);
-		lcfind = g_utf8_strdown (find, -1);
-
-		ptr = strstr (lcname, lcfind);
-		if (!ptr) {
-			/* string not present in name */
-			g_object_set (tag, "foreground", "#DBDBDB", NULL);
-		}
-		else if ((ptr == lcname) ||
-			 ((*name == '"') && (ptr == lcname+1))) {
-			/* string present as start of name */
-			g_object_set (tag, "foreground", "#6161F2", NULL);
-		}
-		else {
-			/* string present in name but not at the start */
-			g_object_set (tag, "foreground", "#A0A0A0", NULL);
-		}
-
-		g_free (lcname);
-		g_free (lcfind);
-	}
-}
-
-static void
-find_entry_changed_cb (GtkWidget *entry, ObjectsIndex *index)
-{
-	gchar *find = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
-	GtkTextTagTable *tags_table = gtk_text_buffer_get_tag_table (index->priv->tbuffer);
-
-	gtk_text_tag_table_foreach (tags_table, (GtkTextTagTableForeach) text_tag_table_foreach_cb,
-				    find);
-	g_free (find);
+	/* FIXME: adjust with sel->priv->type */
+	g_signal_emit (index, objects_index_signals [SELECTION_CHANGED], 0,
+		       BROWSER_FAVORITES_TABLES, sel_contents);
 }
 
 /**
@@ -382,6 +173,17 @@ objects_index_new (BrowserConnection *bcnc)
         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
         gtk_widget_show (label);
 
+	/* cloud */
+	GdaMetaStruct *mstruct;
+	GtkWidget *cloud;
+	mstruct = browser_connection_get_meta_struct (index->priv->bcnc);
+	cloud = objects_cloud_new (mstruct, OBJECTS_CLOUD_TYPE_TABLE);
+	gtk_box_pack_start (GTK_BOX (index), cloud, TRUE, TRUE, 0);
+	index->priv->cloud = cloud;
+	g_signal_connect (cloud, "selected",
+			  G_CALLBACK (cloud_object_selected_cb), index);
+
+	/* find button */
 	wid = gtk_button_new ();
 	label = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
 	gtk_container_add (GTK_CONTAINER (wid), label);
@@ -395,45 +197,9 @@ objects_index_new (BrowserConnection *bcnc)
 				  G_CALLBACK (gtk_widget_show), popup);
 	g_object_set_data (G_OBJECT (popup), "button", wid);
 
-	hbox = gtk_hbox_new (FALSE, 0);
-	gtk_container_add (GTK_CONTAINER (popup), hbox);
-
-	label = gtk_label_new (_("Find:"));
-	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-	wid = gtk_entry_new ();
-	g_signal_connect (wid, "changed",
-			  G_CALLBACK (find_entry_changed_cb), index);
-	gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, TRUE, 0);
-	gtk_widget_show_all (hbox);
-
-	/* text contents */
-	GtkWidget *sw, *vbox;
-	sw = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_box_pack_start (GTK_BOX (index), sw, TRUE, TRUE, 0);
-	
-	vbox = gtk_vbox_new (FALSE, 0);
-	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
-
-	index->priv->tview = gtk_text_view_new_with_buffer (index->priv->tbuffer);
-	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (index->priv->tview), GTK_WRAP_WORD);
-	gtk_text_view_set_editable (GTK_TEXT_VIEW (index->priv->tview), FALSE);
-	//gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (index->priv->tview), FALSE);
-	gtk_box_pack_start (GTK_BOX (vbox), index->priv->tview, TRUE, TRUE, 0);
-	gtk_widget_show_all (sw);
-
-	g_signal_connect (index->priv->tview, "key-press-event", 
-			  G_CALLBACK (key_press_event), index);
-	g_signal_connect (index->priv->tview, "event-after", 
-			  G_CALLBACK (event_after), index);
-	g_signal_connect (index->priv->tview, "motion-notify-event", 
-			  G_CALLBACK (motion_notify_event), index);
-	g_signal_connect (index->priv->tview, "visibility-notify-event", 
-			  G_CALLBACK (visibility_notify_event), index);
-
-	/* initial update */
-	update_display (index);
+	wid = objects_cloud_create_filter (cloud);
+	gtk_container_add (GTK_CONTAINER (popup), wid);
+	gtk_widget_show (wid);
 
 	return (GtkWidget*) index;
 }
@@ -441,198 +207,5 @@ objects_index_new (BrowserConnection *bcnc)
 static void
 meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, ObjectsIndex *index)
 {
-	update_display (index);
-}
-
-static GdkCursor *hand_cursor = NULL;
-static GdkCursor *regular_cursor = NULL;
-
-/* Looks at all tags covering the position (x, y) in the text view, 
- * and if one of them is a link, change the cursor to the "hands" cursor
- * typically used by web browsers.
- */
-static void
-set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ObjectsIndex *index)
-{
-	GSList *tags = NULL, *tagp = NULL;
-	GtkTextIter iter;
-	gboolean hovering = FALSE;
-	
-	gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
-	
-	tags = gtk_text_iter_get_tags (&iter);
-	for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
-		GtkTextTag *tag = tagp->data;
-		gchar *table_name;
-		
-		table_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
-		if (table_name) {
-			hovering = TRUE;
-			break;
-		}
-	}
-	
-	if (hovering != index->priv->hovering_over_link) {
-		index->priv->hovering_over_link = hovering;
-		
-		if (index->priv->hovering_over_link) {
-			if (! hand_cursor)
-				hand_cursor = gdk_cursor_new (GDK_HAND2);
-			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
-									 GTK_TEXT_WINDOW_TEXT),
-					       hand_cursor);
-		}
-		else {
-			if (!regular_cursor)
-				regular_cursor = gdk_cursor_new (GDK_XTERM);
-			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
-									 GTK_TEXT_WINDOW_TEXT),
-					       regular_cursor);
-		}
-	}
-	
-	if (tags) 
-		g_slist_free (tags);
-}
-
-/* 
- * Also update the cursor image if the window becomes visible
- * (e.g. when a window covering it got iconified).
- */
-static gboolean
-visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsIndex *index)
-{
-	gint wx, wy, bx, by;
-	
-	gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
-	
-	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
-					       GTK_TEXT_WINDOW_WIDGET,
-					       wx, wy, &bx, &by);
-	
-	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, index);
-	
-	return FALSE;
-}
-
-/*
- * Update the cursor image if the pointer moved. 
- */
-static gboolean
-motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsIndex *index)
-{
-	gint x, y;
-	
-	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
-					       GTK_TEXT_WINDOW_WIDGET,
-					       event->x, event->y, &x, &y);
-	
-	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, index);
-	
-	gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
-	return FALSE;
-}
-
-
-/* Looks at all tags covering the position of iter in the text view, 
- * and if one of them is a link, follow it by showing the page identified
- * by the data attached to it.
- */
-static void
-follow_if_link (GtkWidget *text_view, GtkTextIter *iter, ObjectsIndex *index)
-{
-	GSList *tags = NULL, *tagp = NULL;
-	
-	tags = gtk_text_iter_get_tags (iter);
-	for (tagp = tags;  tagp;  tagp = tagp->next) {
-		GtkTextTag *tag = tagp->data;
-		const gchar *schema;
-		
-		schema = g_object_get_data (G_OBJECT (tag), "dbo_obj_schema");
-		if (schema) {
-			const gchar *objects_name, *short_name;
-		
-			objects_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
-			short_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_short_name");
-			
-			if (objects_name && short_name) {
-				gchar *str, *tmp1, *tmp2, *tmp3;
-				tmp1 = gda_rfc1738_encode (schema);
-				tmp2 = gda_rfc1738_encode (objects_name);
-				tmp3 = gda_rfc1738_encode (short_name);
-				str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s",
-						       tmp1, tmp2, tmp3);
-				g_free (tmp1);
-				g_free (tmp2);
-				g_free (tmp3);
-				g_signal_emit (index, objects_index_signals [SELECTION_CHANGED], 0,
-					       BROWSER_FAVORITES_TABLES, str);
-				g_free (str);
-			}
-		}
-        }
-
-	if (tags) 
-		g_slist_free (tags);
-}
-
-/*
- * Links can also be activated by clicking.
- */
-static gboolean
-event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsIndex *index)
-{
-	GtkTextIter start, end, iter;
-	GtkTextBuffer *buffer;
-	GdkEventButton *event;
-	gint x, y;
-	
-	if (ev->type != GDK_BUTTON_RELEASE)
-		return FALSE;
-	
-	event = (GdkEventButton *)ev;
-	
-	if (event->button != 1)
-		return FALSE;
-	
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-	
-	/* we shouldn't follow a link if the user has selected something */
-	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
-	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
-		return FALSE;
-	
-	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
-					       GTK_TEXT_WINDOW_WIDGET,
-					       event->x, event->y, &x, &y);
-	
-	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
-	
-	follow_if_link (text_view, &iter, index);
-	
-	return FALSE;
-}
-
-/* 
- * Links can be activated by pressing Enter.
- */
-static gboolean
-key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsIndex *index)
-{
-	GtkTextIter iter;
-	GtkTextBuffer *buffer;
-	
-	switch (event->keyval) {
-	case GDK_Return: 
-	case GDK_KP_Enter:
-		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-		gtk_text_buffer_get_iter_at_mark (buffer, &iter, 
-						  gtk_text_buffer_get_insert (buffer));
-		follow_if_link (text_view, &iter, index);
-		break;
-		
-	default:
-		break;
-	}
-	return FALSE;
+	objects_cloud_set_meta_struct (index->priv->cloud, mstruct);
 }
diff --git a/tools/browser/schema-browser/relations-diagram.c b/tools/browser/schema-browser/relations-diagram.c
index 9e2facd..da2533c 100644
--- a/tools/browser/schema-browser/relations-diagram.c
+++ b/tools/browser/schema-browser/relations-diagram.c
@@ -28,7 +28,7 @@
 #include "../cc-gray-bar.h"
 #include "../canvas/browser-canvas-db-relations.h"
 #include <gdk/gdkkeysyms.h>
-#include "../popup-container.h"
+#include "../common/popup-container.h"
 
 struct _RelationsDiagramPrivate {
 	BrowserConnection *bcnc;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]