[libgda] Browser misc. improvements



commit fa62acc960da02478b45a1ec613cef0efe80d33a
Author: Vivien Malerba <malerba gnome-db org>
Date:   Mon Jun 29 21:54:34 2009 +0200

    Browser misc. improvements
    
    * added scrolling and auto zoom to canvas display
    * added fullscreen mode
    * added a status bar
    * misc bug fixes

 tools/browser/browser-spinner.c                    |    2 +-
 tools/browser/browser-window.c                     |  125 +++++++++++++-
 tools/browser/browser-window.h                     |    3 +
 tools/browser/canvas-example.c                     |   23 +++
 tools/browser/canvas/browser-canvas-column.c       |   28 +++-
 tools/browser/canvas/browser-canvas-column.h       |    5 +-
 tools/browser/canvas/browser-canvas-db-relations.c |  104 ++++++++---
 tools/browser/canvas/browser-canvas-fkey.c         |   89 +++++++---
 tools/browser/canvas/browser-canvas-fkey.h         |    3 +-
 tools/browser/canvas/browser-canvas-item.c         |   59 +++----
 tools/browser/canvas/browser-canvas-item.h         |    3 +-
 tools/browser/canvas/browser-canvas-priv.h         |    7 +-
 tools/browser/canvas/browser-canvas-table.c        |   82 ++++++++-
 tools/browser/canvas/browser-canvas-table.h        |    5 +-
 tools/browser/canvas/browser-canvas-utility.c      |    2 +-
 tools/browser/canvas/browser-canvas.c              |  183 ++++++++++++++++++--
 tools/browser/canvas/browser-canvas.h              |    7 +-
 17 files changed, 599 insertions(+), 131 deletions(-)
---
diff --git a/tools/browser/browser-spinner.c b/tools/browser/browser-spinner.c
index f2919b5..d25fa5e 100644
--- a/tools/browser/browser-spinner.c
+++ b/tools/browser/browser-spinner.c
@@ -748,7 +748,7 @@ browser_spinner_stop (BrowserSpinner *spinner)
  **/
 void
 browser_spinner_set_size (BrowserSpinner *spinner,
-		       GtkIconSize size)
+			  GtkIconSize size)
 {
 	if (size == GTK_ICON_SIZE_INVALID)
 	{
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index d740067..60f8220 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -52,7 +52,7 @@ static void browser_window_class_init (BrowserWindowClass *klass);
 static void browser_window_init (BrowserWindow *bwin);
 static void browser_window_dispose (GObject *object);
 
-
+static gboolean window_state_event (GtkWidget *widget, GdkEventWindowState *event);
 static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, BrowserWindow *bwin);
 
 
@@ -67,9 +67,13 @@ struct _BrowserWindowPrivate {
 
 	GtkWidget         *spinner;
 	GtkUIManager      *ui_manager;
+	GtkToolbarStyle    toolbar_style;
 	GtkActionGroup    *cnc_agroup; /* one GtkAction for each BrowserConnection */
 	gulong             cnc_added_sigid;
 	gulong             cnc_removed_sigid;
+
+	GtkWidget         *statusbar;
+	guint              cnc_statusbar_context;
 };
 
 GType
@@ -103,7 +107,10 @@ browser_window_get_type (void)
 static void
 browser_window_class_init (BrowserWindowClass *klass)
 {
-	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	widget_class->window_state_event = window_state_event;
+
 	parent_class = g_type_class_peek_parent (klass);
 
 	object_class->dispose = browser_window_dispose;
@@ -172,6 +179,7 @@ static void connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc,
 static void quit_cb (GtkAction *action, BrowserWindow *bwin);
 static void about_cb (GtkAction *action, BrowserWindow *bwin);
 static void window_close_cb (GtkAction *action, BrowserWindow *bwin);
+static void window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin);
 static void window_new_cb (GtkAction *action, BrowserWindow *bwin);
 static void window_new_with_cnc_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_close_cb (GtkAction *action, BrowserWindow *bwin);
@@ -180,7 +188,12 @@ static void connection_list_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_meta_update_cb (GtkAction *action, BrowserWindow *bwin);
 static void perspective_toggle_cb (GtkRadioAction *action, GtkRadioAction *current, BrowserWindow *bwin);
 
-static GtkActionEntry ui_actions[] = {
+static const GtkToggleActionEntry ui_toggle_actions [] =
+{
+        { "WindowFullScreen", GTK_STOCK_FULLSCREEN, N_("_Fullscreen"), "F11", N_("Use the whole screen"), G_CALLBACK (window_fullscreen_cb), FALSE}
+};
+
+static const GtkActionEntry ui_actions[] = {
         { "Connection", NULL, "_Connection", NULL, "Connection", NULL },
         { "ConnectionOpen", GTK_STOCK_CONNECT, "_Connect", NULL, "Open a connection", G_CALLBACK (connection_open_cb)},
         { "ConnectionList", NULL, "_Connections list", NULL, "Connections list", G_CALLBACK (connection_list_cb)},
@@ -191,7 +204,7 @@ static GtkActionEntry ui_actions[] = {
         { "Window", NULL, "_Window", NULL, "Window", NULL },
         { "WindowNew", GTK_STOCK_NEW, "_New window", NULL, "Open a new window for current connection", G_CALLBACK (window_new_cb)},
         { "WindowNewOthers", NULL, "New window for _connection", NULL, "Open a new window for a connection", NULL},
-        { "WindowClose", GTK_STOCK_CLOSE, "_Close", NULL, "Close this window", G_CALLBACK (window_close_cb)},
+        { "WindowClose", GTK_STOCK_CLOSE, "_Close", "", "Close this window", G_CALLBACK (window_close_cb)},
         { "Help", NULL, "_Help", NULL, "Help", NULL },
         { "HelpAbout", GTK_STOCK_ABOUT, "_About", NULL, "About", G_CALLBACK (about_cb) }
 };
@@ -212,10 +225,13 @@ static const gchar *ui_actions_info =
         "      <placeholder name='PersList'/>"
         "    </menu>"
         "    <menu name='Window' action='Window'>"
+        "      <menuitem name='WindowFullScreen' action= 'WindowFullScreen'/>"
+        "      <separator/>"
         "      <menuitem name='WindowNew' action= 'WindowNew'/>"
         "      <menu name='WindowNewOthers' action='WindowNewOthers'>"
         "          <placeholder name='CncList'/>"
         "      </menu>"
+        "      <separator/>"
         "      <menuitem name='WindowClose' action= 'WindowClose'/>"
         "    </menu>"
 	"    <placeholder name='MenuExtension'/>"
@@ -225,6 +241,7 @@ static const gchar *ui_actions_info =
         "  </menubar>"
         "  <toolbar  name='ToolBar'>"
         "    <toolitem action='WindowClose'/>"
+        "    <toolitem action='WindowFullScreen'/>"
         "  </toolbar>"
         "</ui>";
 
@@ -282,12 +299,17 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
 
         group = gtk_action_group_new ("Actions");
         gtk_action_group_add_actions (group, ui_actions, G_N_ELEMENTS (ui_actions), bwin);
+	gtk_action_group_add_toggle_actions (group, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions), bwin);
 
         ui = gtk_ui_manager_new ();
         gtk_ui_manager_insert_action_group (ui, group, 0);
         gtk_ui_manager_add_ui_from_string (ui, ui_actions_info, -1, NULL);
 	bwin->priv->ui_manager = ui;
 
+	GtkAccelGroup *accel_group;
+        accel_group = gtk_ui_manager_get_accel_group (ui);
+	gtk_window_add_accel_group (GTK_WINDOW (bwin), accel_group);
+
         menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
         gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
         gtk_widget_show (menubar);
@@ -296,6 +318,7 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
         gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0);
 	gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
         gtk_widget_show (toolbar);
+	bwin->priv->toolbar_style = gtk_toolbar_get_style (GTK_TOOLBAR (toolbar));
 
 	GtkToolItem *ti;
 	GtkWidget *spinner;
@@ -383,8 +406,8 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
 		action = GTK_ACTION (gtk_radio_action_new (name, name, NULL, NULL, FALSE));
 
 		if (!active_action && 
-		    (factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == factory)) ||
-		    (!factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == browser_core_get_default_factory ())))
+		    ((factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == factory)) ||
+		     (!factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == browser_core_get_default_factory ()))))
 			active_action = action;
 		gtk_action_group_add_action (agroup, action);
 		
@@ -404,6 +427,13 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
 	
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (active_action), TRUE);
 
+	/* statusbar */
+	bwin->priv->statusbar = gtk_statusbar_new ();
+	gtk_box_pack_start (GTK_BOX (vbox), bwin->priv->statusbar, FALSE, FALSE, 0);
+        gtk_widget_show (bwin->priv->statusbar);
+	bwin->priv->cnc_statusbar_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar),
+									  "cncbusy");
+
         gtk_widget_show (GTK_WIDGET (bwin));
 
 	return bwin;
@@ -466,10 +496,15 @@ connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, Br
 	if (is_busy) {
 		browser_spinner_start (BROWSER_SPINNER (bwin->priv->spinner));
 		gtk_widget_set_tooltip_text (bwin->priv->spinner, reason);
+		gtk_statusbar_push (GTK_STATUSBAR (bwin->priv->statusbar),
+				    bwin->priv->cnc_statusbar_context,
+				    reason);
 	}
 	else {
 		browser_spinner_stop (BROWSER_SPINNER (bwin->priv->spinner));
 		gtk_widget_set_tooltip_text (bwin->priv->spinner, NULL);
+		gtk_statusbar_pop (GTK_STATUSBAR (bwin->priv->statusbar),
+				   bwin->priv->cnc_statusbar_context);
 	}
 }
 
@@ -607,6 +642,53 @@ window_close_cb (GtkAction *action, BrowserWindow *bwin)
 }
 
 static void
+window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin)
+{
+	if (gtk_toggle_action_get_active (action))
+		gtk_window_fullscreen (GTK_WINDOW (bwin));
+	else
+		gtk_window_unfullscreen (GTK_WINDOW (bwin));
+}
+
+static gboolean
+window_state_event (GtkWidget *widget, GdkEventWindowState *event)
+{
+	BrowserWindow *bwin = BROWSER_WINDOW (widget);
+	gboolean (* window_state_event) (GtkWidget *, GdkEventWindowState *);
+	window_state_event = GTK_WIDGET_CLASS (parent_class)->window_state_event;
+
+	/* calling parent's method */
+	if (window_state_event)
+                window_state_event (widget, event);
+
+	if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+                gboolean fullscreen;
+		GtkWidget *wid;
+
+		wid = gtk_ui_manager_get_widget (bwin->priv->ui_manager, "/ToolBar");
+
+                fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
+		if (fullscreen) {
+			gtk_toolbar_set_style (GTK_TOOLBAR (wid), GTK_TOOLBAR_ICONS);
+			browser_spinner_set_size (BROWSER_SPINNER (bwin->priv->spinner), GTK_ICON_SIZE_LARGE_TOOLBAR);
+		}
+		else {
+			gtk_toolbar_set_style (GTK_TOOLBAR (wid), bwin->priv->toolbar_style);
+			browser_spinner_set_size (BROWSER_SPINNER (bwin->priv->spinner), GTK_ICON_SIZE_DIALOG);
+		}
+
+		wid = gtk_ui_manager_get_widget (bwin->priv->ui_manager, "/MenuBar");
+		if (fullscreen)
+			gtk_widget_hide (wid);
+		else
+			gtk_widget_show (wid);
+		
+		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (bwin->priv->statusbar), !fullscreen);
+        }
+	return FALSE;
+}
+
+static void
 window_new_cb (GtkAction *action, BrowserWindow *bwin)
 {
 	BrowserWindow *nbwin;
@@ -732,3 +814,34 @@ perspective_data_free (PerspectiveData *pers)
                 g_object_unref (pers->perspective_widget);
         g_free (pers);
 }
+
+/**
+ * browser_window_push_status
+ */
+guint
+browser_window_push_status (BrowserWindow *bwin, const gchar *context, const gchar *text)
+{
+	guint cid;
+
+	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), 0);
+	g_return_val_if_fail (context, 0);
+	g_return_val_if_fail (text, 0);
+	
+	cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar), context);
+	return gtk_statusbar_push (GTK_STATUSBAR (bwin->priv->statusbar), cid, text);
+}
+
+/**
+ * browser_window_pop_status
+ */
+void
+browser_window_pop_status (BrowserWindow *bwin, const gchar *context)
+{
+	guint cid;
+
+	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
+	g_return_if_fail (context);
+
+	cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar), context);
+	gtk_statusbar_pop (GTK_STATUSBAR (bwin->priv->statusbar), cid);
+}
diff --git a/tools/browser/browser-window.h b/tools/browser/browser-window.h
index d6eb138..5465879 100644
--- a/tools/browser/browser-window.h
+++ b/tools/browser/browser-window.h
@@ -52,6 +52,9 @@ GType               browser_window_get_type               (void) G_GNUC_CONST;
 BrowserWindow      *browser_window_new                    (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory);
 BrowserConnection  *browser_window_get_connection         (BrowserWindow *bwin);
 
+guint               browser_window_push_status            (BrowserWindow *bwin, const gchar *context, const gchar *text);
+void                browser_window_pop_status             (BrowserWindow *bwin, const gchar *context);
+
 G_END_DECLS
 
 #endif
diff --git a/tools/browser/canvas-example.c b/tools/browser/canvas-example.c
index a2660ce..8874558 100644
--- a/tools/browser/canvas-example.c
+++ b/tools/browser/canvas-example.c
@@ -10,6 +10,27 @@ static void label_drag_data_received (GtkWidget *label, GdkDragContext *context,
 				      gint x, gint y, GtkSelectionData *data,
 				      guint info, guint time);
 
+static int
+scroll_event_cb (GtkWidget *wid, GdkEvent *event, gpointer data)
+{
+	gboolean done = TRUE;
+
+	g_print ("%d\n", event->type);
+	switch (event->type) {
+	case GDK_SCROLL:
+		if (((GdkEventScroll *) event)->direction == GDK_SCROLL_UP)
+			g_print ("UP\n");
+		else if (((GdkEventScroll *) event)->direction == GDK_SCROLL_DOWN)
+			g_print ("DOWN\n");
+		done = TRUE;
+		break;
+	default:
+		done = FALSE;
+		break;
+	}
+	return done;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -60,6 +81,8 @@ main (int argc, char *argv[])
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (auto_layout_cb), canvas);
 	gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+	g_signal_connect (button, "scroll-event",
+			  G_CALLBACK (scroll_event_cb), NULL);
 
 	GtkWidget *wid;
 	GtkTargetEntry dbo_table[] = {
diff --git a/tools/browser/canvas/browser-canvas-column.c b/tools/browser/canvas/browser-canvas-column.c
index 47dc979..8de8f5d 100644
--- a/tools/browser/canvas/browser-canvas-column.c
+++ b/tools/browser/canvas/browser-canvas-column.c
@@ -45,11 +45,13 @@ static void browser_canvas_column_extra_event  (BrowserCanvasItem * citem, GdkEv
 enum
 {
 	PROP_0,
+	PROP_META_STRUCT,
 	PROP_COLUMN,
 };
 
 struct _BrowserCanvasColumnPrivate
 {
+	GdaMetaStruct      *mstruct;
 	GdaMetaTableColumn *column;
 };
 
@@ -100,6 +102,11 @@ browser_canvas_column_class_init (BrowserCanvasColumnClass *klass)
 	object_class->get_property = browser_canvas_column_get_property;
 
 	g_object_class_install_property
+                (object_class, PROP_META_STRUCT,
+                 g_param_spec_object ("meta-struct", NULL, NULL, 
+				      GDA_TYPE_META_STRUCT,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+	g_object_class_install_property
                 (object_class, PROP_COLUMN,
                  g_param_spec_pointer ("column", NULL, NULL, 
 				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
@@ -109,6 +116,7 @@ static void
 browser_canvas_column_init (BrowserCanvasColumn * column)
 {
 	column->priv = g_new0 (BrowserCanvasColumnPrivate, 1);
+	column->priv->mstruct = NULL;
 	column->priv->column = NULL;
 }
 
@@ -121,8 +129,8 @@ browser_canvas_column_dispose (GObject   * object)
 
 	cf = BROWSER_CANVAS_COLUMN (object);
 	if (cf->priv) {
-		if (cf->priv->column)
-			cf->priv->column = NULL;
+		if (cf->priv->mstruct)
+			g_object_unref (cf->priv->mstruct);
 		g_free (cf->priv);
 		cf->priv = NULL;
 	}
@@ -144,7 +152,11 @@ browser_canvas_column_set_property (GObject *object,
 	cf = BROWSER_CANVAS_COLUMN (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		cf->priv->mstruct = g_value_dup_object (value);
+		break;
 	case PROP_COLUMN:
+		g_return_if_fail (cf->priv->mstruct);
 		column = g_value_get_pointer (value);
 
 		cf->priv->column = column;
@@ -182,6 +194,9 @@ browser_canvas_column_get_property (GObject *object,
 	cf = BROWSER_CANVAS_COLUMN (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		g_value_set_object (value, cf->priv->mstruct);
+		break;
 	case PROP_COLUMN:
 		g_value_set_pointer (value, cf->priv->column);
 		break;
@@ -198,6 +213,7 @@ browser_canvas_column_extra_event  (BrowserCanvasItem *citem, GdkEventType event
 /**
  * browser_canvas_column_new
  * @parent: the parent item, or %NULL
+ * @mstruct: the #GdaMetaStruct @column is from
  * @column: the represented entity's column
  * @x: the x coordinate of the text
  * @y: the y coordinate of the text
@@ -206,15 +222,17 @@ browser_canvas_column_extra_event  (BrowserCanvasItem *citem, GdkEventType event
  * Creates a new #BrowserCanvasColumn object
  */
 GooCanvasItem*
-browser_canvas_column_new (GooCanvasItem *parent, GdaMetaTableColumn *column,
-			gdouble x, gdouble y, ...)
+browser_canvas_column_new (GooCanvasItem *parent, GdaMetaStruct *mstruct, GdaMetaTableColumn *column,
+			   gdouble x, gdouble y, ...)
 {
 	GooCanvasItem *item;
 	BrowserCanvasColumn *goocolumn;
 	const char *first_property;
 	va_list var_args;
 
-	item = g_object_new (TYPE_BROWSER_CANVAS_COLUMN, NULL);
+	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+	item = g_object_new (TYPE_BROWSER_CANVAS_COLUMN, "meta-struct", mstruct, NULL);
 	goocolumn = (BrowserCanvasColumn*) item;
 
 	if (parent) {
diff --git a/tools/browser/canvas/browser-canvas-column.h b/tools/browser/canvas/browser-canvas-column.h
index b0bc600..0845924 100644
--- a/tools/browser/canvas/browser-canvas-column.h
+++ b/tools/browser/canvas/browser-canvas-column.h
@@ -55,8 +55,9 @@ struct _BrowserCanvasColumnClass
 
 /* generic widget's functions */
 GType                browser_canvas_column_get_type        (void) G_GNUC_CONST;
-GooCanvasItem*       browser_canvas_column_new             (GooCanvasItem *parent, GdaMetaTableColumn *column,
-							  gdouble x, gdouble y, ...);
+GooCanvasItem*       browser_canvas_column_new             (GooCanvasItem *parent,
+							    GdaMetaStruct *mstruct, GdaMetaTableColumn *column,
+							    gdouble x, gdouble y, ...);
 GdaMetaTableColumn  *browser_canvas_column_get_column      (BrowserCanvasColumn *column);
 BrowserCanvasTable  *browser_canvas_column_get_parent_item (BrowserCanvasColumn *column);
 
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
index 4e71463..805219e 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.c
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -58,9 +58,9 @@ struct _BrowserCanvasDbRelationsPrivate
 {
 	GHashTable       *hash_tables; /* key = GdaMetaTable, value = BrowserCanvasMetaTable (and the reverse) */
 	GHashTable       *hash_fkeys; /* key = GdaMetaTableForeignKey, value = BrowserCanvasFkey */
-	GSList           *all_items; /* list of all the BrowserCanvasItem objects */
 
 	GdaMetaStruct    *mstruct;
+	GooCanvasItem    *level_separator; /* all tables will be above this item and FK lines below */
 };
 
 GType
@@ -127,10 +127,8 @@ browser_canvas_db_relations_dispose (GObject *object)
 
 	if (canvas->priv) {
 		clean_canvas_items (BROWSER_CANVAS (canvas));
-		if (canvas->priv->mstruct) {
+		if (canvas->priv->mstruct)
 			g_object_unref (canvas->priv->mstruct);
-			canvas->priv->mstruct = NULL;
-		}
 
 		g_hash_table_destroy (canvas->priv->hash_tables);
 		g_hash_table_destroy (canvas->priv->hash_fkeys);
@@ -145,9 +143,9 @@ browser_canvas_db_relations_dispose (GObject *object)
 
 static void
 browser_canvas_db_relations_set_property (GObject *object,
-					guint param_id,
-					const GValue *value,
-					GParamSpec *pspec)
+					  guint param_id,
+					  const GValue *value,
+					  GParamSpec *pspec)
 {
 	BrowserCanvasDbRelations *canvas;
 
@@ -168,9 +166,9 @@ browser_canvas_db_relations_set_property (GObject *object,
 
 static void
 browser_canvas_db_relations_get_property (GObject *object,
-					guint param_id,
-					GValue *value,
-					GParamSpec *pspec)
+					  guint param_id,
+					  GValue *value,
+					  GParamSpec *pspec)
 {
 	BrowserCanvasDbRelations *canvas;
 
@@ -198,8 +196,15 @@ browser_canvas_db_relations_get_property (GObject *object,
 GtkWidget *
 browser_canvas_db_relations_new (GdaMetaStruct *mstruct)
 {
+	BrowserCanvas *canvas;
+	GooCanvasItem *item;
 	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
-        return GTK_WIDGET (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS, "meta-struct", mstruct, NULL));
+
+	canvas = BROWSER_CANVAS (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS, "meta-struct", mstruct, NULL));
+	item = goo_canvas_group_new (goo_canvas_get_root_item (canvas->priv->goocanvas), NULL);
+	BROWSER_CANVAS_DB_RELATIONS (canvas)->priv->level_separator = item;
+
+        return GTK_WIDGET (canvas);
 }
 
 
@@ -214,10 +219,6 @@ clean_canvas_items (BrowserCanvas *canvas)
 	g_hash_table_destroy (dbrel->priv->hash_fkeys);
 	dbrel->priv->hash_tables = g_hash_table_new (NULL, NULL);
 	dbrel->priv->hash_fkeys = g_hash_table_new (NULL, NULL);
-	if (dbrel->priv->all_items) {
-		g_slist_free (dbrel->priv->all_items);
-		dbrel->priv->all_items = NULL;
-	}
 }
 
 static GtkWidget *canvas_entity_popup_func (BrowserCanvasTable *ce);
@@ -254,7 +255,30 @@ popup_func_delete_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce)
 	g_hash_table_remove (dbrel->priv->hash_tables, ce);
 	g_hash_table_remove (dbrel->priv->hash_tables, mtable);
 	goo_canvas_item_remove (GOO_CANVAS_ITEM (ce));
-	dbrel->priv->all_items = g_slist_remove (dbrel->priv->all_items, ce);
+
+	/* remove FK items */
+	GSList *list;
+	for (list = mtable->fk_list; list; list = list->next) {
+		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+		GooCanvasItem *fk_item;
+		
+		fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+		if (fk_item) {
+			goo_canvas_item_remove (fk_item);
+			g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
+		}
+	}
+	
+	for (list = mtable->reverse_fk_list; list; list = list->next) {
+		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+		GooCanvasItem *fk_item;
+		
+		fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+		if (fk_item) {
+			goo_canvas_item_remove (fk_item);
+			g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
+		}
+	}
 }
 
 static void
@@ -373,7 +397,17 @@ popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
 	mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
 							     table_catalog, table_schema, table_name, NULL);
 	if (mtable) {
-		browser_canvas_db_relations_add_table (dbrel, table_catalog, table_schema, table_name);
+		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");
@@ -434,14 +468,14 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 			return BROWSER_CANVAS_TABLE (table_item);
 
 		table_item = browser_canvas_table_new (goo_canvas_get_root_item (goocanvas), 
-						       mtable, x, y, NULL);
+						       canvas->priv->mstruct, mtable, x, y, NULL);
 		g_hash_table_insert (canvas->priv->hash_tables, mtable, table_item);
 		g_hash_table_insert (canvas->priv->hash_tables, table_item, mtable);
-		canvas->priv->all_items = g_slist_prepend (canvas->priv->all_items, table_item);
 		g_object_set (G_OBJECT (table_item), 
 			      "popup_menu_func", canvas_entity_popup_func, NULL);
 		browser_canvas_declare_item (BROWSER_CANVAS (canvas),
 					     BROWSER_CANVAS_ITEM (table_item));
+		goo_canvas_item_raise (GOO_CANVAS_ITEM (table_item), canvas->priv->level_separator);
 
 		/* if there are some FK links, then also add them */
 		GSList *list;
@@ -451,11 +485,17 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 			ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->depend_on);
 			if (ref_table_item) {
 				GooCanvasItem *fk_item;
-				fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas), fk, NULL);
-				browser_canvas_declare_item (BROWSER_CANVAS (canvas),
-							     BROWSER_CANVAS_ITEM (fk_item));
-
-				g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+				fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
+				if (!fk_item) {
+					fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas),
+									   canvas->priv->mstruct, fk, NULL);
+					browser_canvas_declare_item (BROWSER_CANVAS (canvas),
+								     BROWSER_CANVAS_ITEM (fk_item));
+					
+					g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+					goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
+							       canvas->priv->level_separator);
+				}
 			}
 		}
 		for (list = mtable->reverse_fk_list; list; list = list->next) {
@@ -464,11 +504,17 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 			ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->meta_table);
 			if (ref_table_item) {
 				GooCanvasItem *fk_item;
-				fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas), fk, NULL);
-				browser_canvas_declare_item (BROWSER_CANVAS (canvas),
-							     BROWSER_CANVAS_ITEM (fk_item));
-
-				g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+				fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
+				if (!fk_item) {
+					fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas),
+									   canvas->priv->mstruct, fk, NULL);
+					browser_canvas_declare_item (BROWSER_CANVAS (canvas),
+								     BROWSER_CANVAS_ITEM (fk_item));
+					
+					g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+					goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
+							       canvas->priv->level_separator);
+				}
 			}
 		}
 
diff --git a/tools/browser/canvas/browser-canvas-fkey.c b/tools/browser/canvas/browser-canvas-fkey.c
index 7e06257..b00363a 100644
--- a/tools/browser/canvas/browser-canvas-fkey.c
+++ b/tools/browser/canvas/browser-canvas-fkey.c
@@ -53,11 +53,13 @@ static void update_items (BrowserCanvasFkey *cc);
 enum
 {
 	PROP_0,
+	PROP_META_STRUCT,
 	PROP_FK_CONSTRAINT
 };
 
 struct _BrowserCanvasFkeyPrivate
 {
+	GdaMetaStruct          *mstruct;
 	GdaMetaTableForeignKey *fk;
 	BrowserCanvasTable     *fk_table_item;
 	BrowserCanvasTable     *ref_pk_table_item;
@@ -106,6 +108,12 @@ browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class)
 	/* Properties */
 	object_class->set_property = browser_canvas_fkey_set_property;
 	object_class->get_property = browser_canvas_fkey_get_property;
+
+	g_object_class_install_property
+                (object_class, PROP_META_STRUCT,
+                 g_param_spec_object ("meta-struct", NULL, NULL, 
+				      GDA_TYPE_META_STRUCT,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
 	g_object_class_install_property (object_class, PROP_FK_CONSTRAINT,
 					 g_param_spec_pointer ("fk_constraint", "FK constraint", 
 							       NULL, 
@@ -117,12 +125,33 @@ static void
 browser_canvas_fkey_init (BrowserCanvasFkey *cc)
 {
 	cc->priv = g_new0 (BrowserCanvasFkeyPrivate, 1);
+	cc->priv->mstruct = NULL;
 	cc->priv->fk = NULL;
 	cc->priv->fk_table_item = NULL;
 	cc->priv->ref_pk_table_item = NULL;
 	cc->priv->shapes = NULL;
 }
 
+static gboolean
+idle_remove_item (BrowserCanvasFkey *cc)
+{
+	goo_canvas_item_remove (GOO_CANVAS_ITEM (cc));
+	return FALSE;
+}
+
+static void
+fk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, BrowserCanvasTable *old_table_item)
+{
+	cc->priv->fk_table_item = NULL;
+}
+
+static void
+ref_pk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, BrowserCanvasTable *old_table_item)
+{
+	cc->priv->ref_pk_table_item = NULL;
+}
+
+
 static void
 browser_canvas_fkey_dispose (GObject *object)
 {
@@ -133,9 +162,21 @@ browser_canvas_fkey_dispose (GObject *object)
 	cc = BROWSER_CANVAS_FKEY (object);
 
 	clean_items (cc);
+	if (cc->priv->mstruct) {
+		g_object_unref (cc->priv->mstruct);
+		cc->priv->mstruct = NULL;
+	}
 	cc->priv->fk = NULL;
-	cc->priv->fk_table_item = NULL;
-	cc->priv->ref_pk_table_item = NULL;
+	if (cc->priv->fk_table_item) {
+		g_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
+				     (GWeakNotify) fk_table_item_weak_ref_lost, cc);
+		cc->priv->fk_table_item = NULL;
+	}
+	if (cc->priv->ref_pk_table_item) {
+		g_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
+				     (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
+		cc->priv->ref_pk_table_item = NULL;
+	}
 
 	/* for the parent class */
 	parent_class->dispose (object);
@@ -171,6 +212,9 @@ browser_canvas_fkey_set_property (GObject *object,
 	cc = BROWSER_CANVAS_FKEY (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		cc->priv->mstruct = g_value_dup_object (value);
+		break;
 	case PROP_FK_CONSTRAINT:
 		if (cc->priv->fk != g_value_get_pointer (value)) {
 			cc->priv->fk = g_value_get_pointer (value);
@@ -192,6 +236,9 @@ browser_canvas_fkey_get_property (GObject *object,
 	cc = BROWSER_CANVAS_FKEY (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		g_value_set_object (value, cc->priv->mstruct);
+		break;
 	default:
 		g_warning ("No such property!");
 		break;
@@ -219,7 +266,6 @@ static gboolean single_item_leave_notify_event_cb (GooCanvasItem *ci, GooCanvasI
 static gboolean single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
 						   GdkEventButton *event, BrowserCanvasFkey *cc);
 static void table_item_moved_cb (GooCanvasItem *table, BrowserCanvasFkey *cc);
-static void table_destroy_cb (BrowserCanvasTable *table, BrowserCanvasFkey *cc);
 
 /* 
  * destroy any existing GooCanvasItem objects 
@@ -230,16 +276,16 @@ clean_items (BrowserCanvasFkey *cc)
 	if (cc->priv->fk_table_item) {
 		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_table_item),
 						      G_CALLBACK (table_item_moved_cb), cc);
-		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_table_item),
-                                                      G_CALLBACK (table_destroy_cb), cc);
+		g_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
+				     (GWeakNotify) fk_table_item_weak_ref_lost, cc);
 		cc->priv->fk_table_item = NULL;
 	}
 
 	if (cc->priv->ref_pk_table_item) {
 		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_table_item),
 						      G_CALLBACK (table_item_moved_cb), cc);
-		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_table_item),
-						      G_CALLBACK (table_destroy_cb), cc);
+		g_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
+				     (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
 		cc->priv->ref_pk_table_item = NULL;
 	}
 	
@@ -266,27 +312,23 @@ create_items (BrowserCanvasFkey *cc)
 								 GDA_META_TABLE (cc->priv->fk->meta_table));
 	cc->priv->fk_table_item = table_item;
 	g_return_if_fail (table_item);
+	g_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) fk_table_item_weak_ref_lost, cc);
+
 	g_signal_connect (G_OBJECT (table_item), "moving",
 			  G_CALLBACK (table_item_moved_cb), cc);
 	g_signal_connect (G_OBJECT (table_item), "moved",
 			  G_CALLBACK (table_item_moved_cb), cc);
-	g_signal_connect (G_OBJECT (table_item), "shifted",
-			  G_CALLBACK (table_item_moved_cb), cc);
-	g_signal_connect (G_OBJECT (table_item), "destroy",
-			  G_CALLBACK (table_destroy_cb), cc);
 
 	table_item = browser_canvas_db_relations_get_table_item (BROWSER_CANVAS_DB_RELATIONS (canvas),
 								 GDA_META_TABLE (cc->priv->fk->depend_on));
 	cc->priv->ref_pk_table_item = table_item;
 	g_return_if_fail (table_item);
+
+	g_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
 	g_signal_connect (G_OBJECT (table_item), "moving",
 			  G_CALLBACK (table_item_moved_cb), cc);
 	g_signal_connect (G_OBJECT (table_item), "moved",
 			  G_CALLBACK (table_item_moved_cb), cc);
-	g_signal_connect (G_OBJECT (table_item), "shifted",
-			  G_CALLBACK (table_item_moved_cb), cc);
-	g_signal_connect (G_OBJECT (table_item), "destroy",
-			  G_CALLBACK (table_destroy_cb), cc);
 
 	/* actual line(s) */
 	g_assert (!cc->priv->shapes);
@@ -407,6 +449,8 @@ static gboolean
 single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
 				   GdkEventButton *event, BrowserCanvasFkey *cc)
 {
+	return FALSE;
+	/*
 	GdaMetaTableForeignKey *fkcons = g_object_get_data (G_OBJECT (ci), "fkcons");
 	GtkWidget *menu, *entry;
 
@@ -420,6 +464,7 @@ single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item
 			NULL, NULL, ((GdkEventButton *)event)->button,
 			((GdkEventButton *)event)->time);
 	return TRUE;
+	*/
 }
 
 
@@ -435,12 +480,6 @@ table_item_moved_cb (GooCanvasItem *table, BrowserCanvasFkey *cc)
 	update_items (cc);
 }
 
-static void
-table_destroy_cb (BrowserCanvasTable *table, BrowserCanvasFkey *cc)
-{
-        goo_canvas_item_remove (GOO_CANVAS_ITEM (cc));
-}
-
 /**
  * browser_canvas_fkey_new
  * @parent: the parent item, or NULL. 
@@ -452,13 +491,15 @@ table_destroy_cb (BrowserCanvasTable *table, BrowserCanvasFkey *cc)
  * Returns: a new #GooCanvasItem object
  */
 GooCanvasItem *
-browser_canvas_fkey_new (GooCanvasItem *parent, GdaMetaTableForeignKey *fkcons, ...)
+browser_canvas_fkey_new (GooCanvasItem *parent, GdaMetaStruct *mstruct, GdaMetaTableForeignKey *fkcons, ...)
 {
 	GooCanvasItem *item;
 	const char *first_property;
 	va_list var_args;
-		
-	item = g_object_new (TYPE_BROWSER_CANVAS_FKEY, NULL);
+
+	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+	item = g_object_new (TYPE_BROWSER_CANVAS_FKEY, "meta-struct", mstruct, NULL);
 
 	if (parent) {
 		goo_canvas_item_add_child (parent, item, -1);
diff --git a/tools/browser/canvas/browser-canvas-fkey.h b/tools/browser/canvas/browser-canvas-fkey.h
index e63a901..d0cc99a 100644
--- a/tools/browser/canvas/browser-canvas-fkey.h
+++ b/tools/browser/canvas/browser-canvas-fkey.h
@@ -57,7 +57,8 @@ struct _BrowserCanvasFkeyClass
 };
 
 GType          browser_canvas_fkey_get_type       (void) G_GNUC_CONST;
-GooCanvasItem *browser_canvas_fkey_new            (GooCanvasItem *parent, GdaMetaTableForeignKey *fk, ...);
+GooCanvasItem *browser_canvas_fkey_new            (GooCanvasItem *parent,
+						   GdaMetaStruct *mstruct, GdaMetaTableForeignKey *fk, ...);
 
 G_END_DECLS
 
diff --git a/tools/browser/canvas/browser-canvas-item.c b/tools/browser/canvas/browser-canvas-item.c
index f57412c..ceeb275 100644
--- a/tools/browser/canvas/browser-canvas-item.c
+++ b/tools/browser/canvas/browser-canvas-item.c
@@ -42,6 +42,8 @@ struct _BrowserCanvasItemPrivate
 	double              xstart;
 	double              ystart;
 	gboolean            allow_move;
+	gboolean            allow_select;
+
 	gchar              *tooltip_text;
 };
 
@@ -49,8 +51,6 @@ enum
 {
 	MOVED,
 	MOVING,
-	SHIFTED,
-	DESTROY,
 	LAST_SIGNAL
 };
 
@@ -58,6 +58,7 @@ enum
 {
 	PROP_0,
 	PROP_ALLOW_MOVE,
+	PROP_ALLOW_SELECT,
 	PROP_TOOLTIP_TEXT
 };
 
@@ -113,28 +114,9 @@ browser_canvas_item_class_init (BrowserCanvasItemClass * class)
 			      NULL, NULL,
 			      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
 			      0);
-	browser_canvas_item_signals[SHIFTED] =
-		g_signal_new ("shifted",
-			      G_TYPE_FROM_CLASS (object_class),
-			      G_SIGNAL_RUN_FIRST,
-			      G_STRUCT_OFFSET (BrowserCanvasItemClass, shifted),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
-			      0);
-	browser_canvas_item_signals[DESTROY] =
-		g_signal_new ("destroy",
-			      G_TYPE_FROM_CLASS (object_class),
-			      G_SIGNAL_RUN_FIRST,
-			      G_STRUCT_OFFSET (BrowserCanvasItemClass, destroy),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
-			      0);
-
 
 	class->moved = NULL;
 	class->moving = NULL;
-	class->shifted = NULL;
-	class->destroy = NULL;
 	object_class->dispose = browser_canvas_item_dispose;
 
 	/* virtual funstionc */
@@ -146,7 +128,10 @@ browser_canvas_item_class_init (BrowserCanvasItemClass * class)
 
 	g_object_class_install_property
                 (object_class, PROP_ALLOW_MOVE,
-                 g_param_spec_boolean ("allow-move", NULL, NULL, TRUE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+                 g_param_spec_boolean ("allow-move", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+	g_object_class_install_property
+                (object_class, PROP_ALLOW_SELECT,
+                 g_param_spec_boolean ("allow-select", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 	g_object_class_install_property
 		(object_class, PROP_TOOLTIP_TEXT,
 		 g_param_spec_string ("tip-text", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
@@ -192,8 +177,6 @@ browser_canvas_item_dispose (GObject *object)
 	
 	citem = BROWSER_CANVAS_ITEM (object);
 	if (citem->priv) {
-		g_signal_emit (object, browser_canvas_item_signals[DESTROY], 0);
-
 		if (citem->priv->tooltip_text) 
 			g_free (citem->priv->tooltip_text);
 
@@ -220,6 +203,9 @@ browser_canvas_item_set_property (GObject *object,
 	case PROP_ALLOW_MOVE:
 		citem->priv->allow_move = g_value_get_boolean (value);
 		break;
+	case PROP_ALLOW_SELECT:
+		citem->priv->allow_select = g_value_get_boolean (value);
+		break;
 	case PROP_TOOLTIP_TEXT:
 		str = g_value_get_string (value);
 		if (citem->priv->tooltip_text) {
@@ -245,6 +231,9 @@ browser_canvas_item_get_property (GObject *object,
         case PROP_ALLOW_MOVE:
                 g_value_set_boolean (value, citem->priv->allow_move);
                 break;
+        case PROP_ALLOW_SELECT:
+                g_value_set_boolean (value, citem->priv->allow_select);
+                break;
         case PROP_TOOLTIP_TEXT:
                 g_value_set_string (value, citem->priv->tooltip_text);
                 break;
@@ -282,7 +271,7 @@ browser_canvas_item_get_canvas (BrowserCanvasItem *item)
  */
 void 
 browser_canvas_item_get_edge_nodes (BrowserCanvasItem *item, 
-				  BrowserCanvasItem **from, BrowserCanvasItem **to)
+				    BrowserCanvasItem **from, BrowserCanvasItem **to)
 {
 	BrowserCanvasItemClass *class;
 
@@ -336,13 +325,19 @@ button_press_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
 			}
 			done = TRUE;
 		}
-		else if (citem->priv->allow_move) {
-			/* movement management */
-			goo_canvas_item_raise (GOO_CANVAS_ITEM (citem), NULL);
-			citem->priv->xstart = event->x;
-			citem->priv->ystart = event->y;
-			citem->priv->moving = TRUE;
-			done = TRUE;
+		else {
+			if (citem->priv->allow_select && (event->state & GDK_CONTROL_MASK)) {
+				browser_canvas_item_toggle_select (browser_canvas_item_get_canvas (citem), citem);
+				done = TRUE;
+			}
+			if (citem->priv->allow_move) {
+				/* movement management */
+				goo_canvas_item_raise (GOO_CANVAS_ITEM (citem), NULL);
+				citem->priv->xstart = event->x;
+				citem->priv->ystart = event->y;
+				citem->priv->moving = TRUE;
+				done = FALSE;
+			}
 		}
 		break;
 	default:
diff --git a/tools/browser/canvas/browser-canvas-item.h b/tools/browser/canvas/browser-canvas-item.h
index c43f2b2..066ea5f 100644
--- a/tools/browser/canvas/browser-canvas-item.h
+++ b/tools/browser/canvas/browser-canvas-item.h
@@ -47,14 +47,13 @@ struct _BrowserCanvasItemClass
 	/* signals */
 	void (*moved)        (BrowserCanvasItem *citem);
 	void (*moving)       (BrowserCanvasItem *citem);
-	void (*shifted)      (BrowserCanvasItem *citem);
-	void (*destroy)      (BrowserCanvasItem *citem);
 
 	/* virtual functions */
 	void (*extra_event)  (BrowserCanvasItem *citem, GdkEventType event_type);
 	void (*get_edge_nodes)(BrowserCanvasItem *citem, BrowserCanvasItem **from, BrowserCanvasItem **to);
 	void (*drag_data_get) (BrowserCanvasItem *citem, GdkDragContext *drag_context,
 			       GtkSelectionData *data, guint info, guint time);
+	void (*set_selected)  (BrowserCanvasItem *citem, gboolean selected);
 };
 
 GType              browser_canvas_item_get_type       (void) G_GNUC_CONST;
diff --git a/tools/browser/canvas/browser-canvas-priv.h b/tools/browser/canvas/browser-canvas-priv.h
index 989cb31..ecaf2a3 100644
--- a/tools/browser/canvas/browser-canvas-priv.h
+++ b/tools/browser/canvas/browser-canvas-priv.h
@@ -1,4 +1,4 @@
-/* browser-canvas.h
+/* browser-canvas-priv.h
  *
  * Copyright (C) 2009 Vivien Malerba
  *
@@ -22,6 +22,7 @@
 #define __BROWSER_CANVAS_PRIV__
 
 #include <goocanvas.h>
+#include "browser-canvas-decl.h"
 
 G_BEGIN_DECLS
 
@@ -29,6 +30,10 @@ struct _BrowserCanvasPrivate
 {
 	GooCanvas          *goocanvas;
 	GSList             *items; /* BrowserCanvasItem objects, non ordered */
+
+	gboolean            canvas_moving;
+
+	BrowserCanvasItem  *current_selected_item;
 };
 
 G_END_DECLS
diff --git a/tools/browser/canvas/browser-canvas-table.c b/tools/browser/canvas/browser-canvas-table.c
index 7c2d05a..9d9139d 100644
--- a/tools/browser/canvas/browser-canvas-table.c
+++ b/tools/browser/canvas/browser-canvas-table.c
@@ -24,6 +24,7 @@
 #include "browser-canvas-table.h"
 #include "browser-canvas-column.h"
 #include <glib/gi18n-lib.h>
+#include <string.h>
 
 static void browser_canvas_table_class_init (BrowserCanvasTableClass *class);
 static void browser_canvas_table_init       (BrowserCanvasTable *drag);
@@ -41,16 +42,19 @@ static void browser_canvas_table_get_property (GObject *object,
 
 static void browser_canvas_table_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *drag_context,
 						GtkSelectionData *data, guint info, guint time);
+static void browser_canvas_table_set_selected (BrowserCanvasItem *citem, gboolean selected);
 
 enum
 {
 	PROP_0,
+	PROP_META_STRUCT,
 	PROP_TABLE,
 	PROP_MENU_FUNC
 };
 
 struct _BrowserCanvasTablePrivate
 {
+	GdaMetaStruct      *mstruct;
 	GdaMetaTable       *table;
 
 	/* UI building information */
@@ -62,6 +66,8 @@ struct _BrowserCanvasTablePrivate
 	/* presentation parameters */
         gdouble             x_text_space;
         gdouble             y_text_space;
+
+	GooCanvasItem      *selection_mark;
 };
 
 /* get a pointer to the parents to be able to call their destructor */
@@ -100,6 +106,7 @@ browser_canvas_table_class_init (BrowserCanvasTableClass *class)
 
 	table_parent_class = g_type_class_peek_parent (class);
 	iclass->drag_data_get = browser_canvas_table_drag_data_get;
+	iclass->set_selected = browser_canvas_table_set_selected;
 
 	object_class->dispose = browser_canvas_table_dispose;
 	object_class->finalize = browser_canvas_table_finalize;
@@ -109,6 +116,11 @@ browser_canvas_table_class_init (BrowserCanvasTableClass *class)
 	object_class->get_property = browser_canvas_table_get_property;
 
 	g_object_class_install_property
+                (object_class, PROP_META_STRUCT,
+                 g_param_spec_object ("meta-struct", NULL, NULL, 
+				      GDA_TYPE_META_STRUCT,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+	g_object_class_install_property
                 (object_class, PROP_TABLE,
                  g_param_spec_pointer ("table", NULL, NULL,
 				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
@@ -126,6 +138,7 @@ static void
 browser_canvas_table_init (BrowserCanvasTable *table)
 {
 	table->priv = g_new0 (BrowserCanvasTablePrivate, 1);
+	table->priv->mstruct = NULL;
 	table->priv->table = NULL;
 	table->priv->column_ypos = NULL;
 	table->priv->popup_menu_func = NULL;
@@ -133,6 +146,8 @@ browser_canvas_table_init (BrowserCanvasTable *table)
 	table->priv->x_text_space = 3.;
 	table->priv->y_text_space = 3.;
 
+	table->priv->selection_mark = NULL;
+
 	g_signal_connect (G_OBJECT (table), "button-press-event",
 			  G_CALLBACK (button_press_event_cb), NULL);
 }
@@ -151,6 +166,10 @@ browser_canvas_table_dispose (GObject *object)
 
 	/* REM: let the GooCanvas library destroy the items itself */
 	ce->priv->table = NULL;
+	if (ce->priv->mstruct) {
+		g_object_unref (ce->priv->mstruct);
+		ce->priv->mstruct = NULL;
+	}
 
 	/* for the parent class */
 	table_parent_class->dispose (object);
@@ -158,7 +177,7 @@ browser_canvas_table_dispose (GObject *object)
 
 
 static void
-browser_canvas_table_finalize (GObject   * object)
+browser_canvas_table_finalize (GObject *object)
 {
 	BrowserCanvasTable *ce;
 	g_return_if_fail (object != NULL);
@@ -190,6 +209,9 @@ browser_canvas_table_set_property (GObject *object,
 	ce = BROWSER_CANVAS_TABLE (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		ce->priv->mstruct = g_value_dup_object (value);
+		break;
 	case PROP_TABLE: {
 		GdaMetaTable *table;
 		table = g_value_get_pointer (value);
@@ -224,6 +246,9 @@ browser_canvas_table_get_property (GObject *object,
 	ce = BROWSER_CANVAS_TABLE (object);
 
 	switch (param_id) {
+	case PROP_META_STRUCT:
+		g_value_set_object (value, ce->priv->mstruct);
+		break;
 	case PROP_TABLE:
 		g_value_set_pointer (value, ce->priv->table);
 		break;
@@ -311,6 +336,7 @@ create_items (BrowserCanvasTable *ce)
 	for (column_nb = 0, list = columns; list; list = list->next, column_nb++) {
 		ce->priv->column_ypos [column_nb] = y;
 		item = browser_canvas_column_new (GOO_CANVAS_ITEM (ce),
+						  ce->priv->mstruct,
 						  GDA_META_TABLE_COLUMN (list->data),
 						  X_PAD, ce->priv->column_ypos [column_nb], NULL);
 		ce->priv->column_items = g_slist_append (ce->priv->column_items, item);
@@ -370,7 +396,7 @@ static gboolean
 button_press_event_cb (BrowserCanvasTable *ce, GooCanvasItem  *target_item, GdkEventButton *event,
 		       gpointer unused_data)
 {
-	if (ce->priv->popup_menu_func) {
+	if ((event->button == 3) && ce->priv->popup_menu_func) {
 		GtkWidget *menu;
 		menu = ce->priv->popup_menu_func (ce);
 		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
@@ -447,14 +473,18 @@ browser_canvas_table_get_column_ypos (BrowserCanvasTable *ce, GdaMetaTableColumn
  * Returns: a new #GooCanvasItem object
  */
 GooCanvasItem *
-browser_canvas_table_new (GooCanvasItem *parent, GdaMetaTable *table, 
+browser_canvas_table_new (GooCanvasItem *parent, GdaMetaStruct *mstruct, GdaMetaTable *table, 
 			 gdouble x, gdouble y, ...)
 {
 	GooCanvasItem *item;
 	const char *first_property;
 	va_list var_args;
-		
-	item = g_object_new (TYPE_BROWSER_CANVAS_TABLE, "allow-move", TRUE, NULL);
+
+	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+	item = g_object_new (TYPE_BROWSER_CANVAS_TABLE, "meta-struct", mstruct, 
+			     "allow-move", TRUE,
+			     "allow-select", TRUE, NULL);
 
 	if (parent) {
 		goo_canvas_item_add_child (parent, item, -1);
@@ -498,3 +528,45 @@ browser_canvas_table_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *dr
 	gtk_selection_data_set (data, data->target, 8, str, strlen (str));
 	g_free (str);
 }
+
+static void
+browser_canvas_table_set_selected (BrowserCanvasItem *citem, gboolean selected)
+{
+	GooCanvasBounds bounds;
+
+	if (selected) {
+		if (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark)
+			g_object_set (G_OBJECT (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark),
+				      "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
+		else {
+			GooCanvasItem *root;
+			GooCanvasItem *rect;
+			
+			//root = goo_canvas_get_root_item (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (citem)));
+			root = GOO_CANVAS_ITEM (citem);
+			goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (citem), &bounds);
+			rect = 	goo_canvas_rect_new (root, - 1., - 1., 
+						     bounds.x2 - bounds.x1 + 2.,
+						     bounds.y2 - bounds.y1 + 2.,
+						     "radius-x", RADIUS_X,
+						     "radius-y", RADIUS_Y,
+						     "stroke-color", "#ffea08",
+						     NULL);
+			/*
+			rect = 	goo_canvas_rect_new (root, bounds.x1 - 1., bounds.y1 - 1., 
+						     bounds.x2 - bounds.x1 + 2.,
+						     bounds.y2 - bounds.y1 + 2.,
+						     "radius-x", RADIUS_X,
+						     "radius-y", RADIUS_Y,
+						     "stroke-color", "#ffea08",
+						     NULL);
+			*/
+			BROWSER_CANVAS_TABLE (citem)->priv->selection_mark = rect;
+		}	
+	}
+	else {
+		if (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark)
+			g_object_set (G_OBJECT (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark),
+				      "visibility", GOO_CANVAS_ITEM_HIDDEN, NULL);
+	}
+}
diff --git a/tools/browser/canvas/browser-canvas-table.h b/tools/browser/canvas/browser-canvas-table.h
index 7e90145..13aad1a 100644
--- a/tools/browser/canvas/browser-canvas-table.h
+++ b/tools/browser/canvas/browser-canvas-table.h
@@ -56,8 +56,9 @@ struct _BrowserCanvasTableClass
 
 /* generic widget's functions */
 GType                browser_canvas_table_get_type        (void) G_GNUC_CONST;
-GooCanvasItem       *browser_canvas_table_new             (GooCanvasItem *parent, GdaMetaTable *table, 
-							    gdouble x, gdouble y, ...);
+GooCanvasItem       *browser_canvas_table_new             (GooCanvasItem *parent, 
+							   GdaMetaStruct *mstruct, GdaMetaTable *table, 
+							   gdouble x, gdouble y, ...);
 BrowserCanvasColumn *browser_canvas_table_get_column_item (BrowserCanvasTable *ce, GdaMetaTableColumn *column);
 gdouble              browser_canvas_table_get_column_ypos (BrowserCanvasTable *ce, GdaMetaTableColumn *column);
 
diff --git a/tools/browser/canvas/browser-canvas-utility.c b/tools/browser/canvas/browser-canvas-utility.c
index 4485b22..5c541ea 100644
--- a/tools/browser/canvas/browser-canvas-utility.c
+++ b/tools/browser/canvas/browser-canvas-utility.c
@@ -694,7 +694,7 @@ browser_canvas_util_compute_handle_shapes (GooCanvasItem *parent, GSList *shapes
 					 gdouble x1, gdouble y1, gdouble x2, gdouble y2)
 {
 	/* don't add anything... */
-	/*return shapes;*/
+	return shapes;
 
 	GSList *retval = shapes;
 	gdouble x, y, sq = 5.;
diff --git a/tools/browser/canvas/browser-canvas.c b/tools/browser/canvas/browser-canvas.c
index 15d2911..fdfa06a 100644
--- a/tools/browser/canvas/browser-canvas.c
+++ b/tools/browser/canvas/browser-canvas.c
@@ -61,6 +61,7 @@ static GObjectClass *parent_class = NULL;
 
 enum
 {
+	ITEM_SELECTED,
 	LAST_SIGNAL
 };
 
@@ -69,7 +70,7 @@ enum
 	PROP_0,
 };
 
-static gint canvas_signals[LAST_SIGNAL] = { };
+static gint canvas_signals[LAST_SIGNAL] = { 0 };
 
 GType
 browser_canvas_get_type (void)
@@ -103,6 +104,16 @@ browser_canvas_class_init (BrowserCanvasClass *klass)
 
 	widget_class = (GtkWidgetClass *) klass;
 
+	/* signals */
+	canvas_signals[ITEM_SELECTED] =
+		g_signal_new ("item-selected",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_FIRST,
+			      G_STRUCT_OFFSET (BrowserCanvasClass, item_selected),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+			      TYPE_BROWSER_CANVAS_ITEM);
+
 	/* properties */
 	object_class->set_property = browser_canvas_set_property;
 	object_class->get_property = browser_canvas_get_property;
@@ -115,7 +126,9 @@ browser_canvas_class_init (BrowserCanvasClass *klass)
 	object_class->finalize = browser_canvas_finalize;
 }
 
-static int canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static gboolean canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static gboolean motion_notify_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static gboolean canvas_scroll_event_cb (GooCanvas *gcanvas, GdkEvent *event, BrowserCanvas *canvas);
 static void drag_begin_cb (BrowserCanvas *canvas, GdkDragContext *drag_context, GooCanvas *gcanvas);
 static void drag_data_get_cb (BrowserCanvas *canvas, GdkDragContext   *drag_context,
 			      GtkSelectionData *data, guint info,
@@ -138,12 +151,17 @@ browser_canvas_init (BrowserCanvas *canvas)
         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (canvas), GTK_SHADOW_NONE);
 	g_idle_add ((GSourceFunc) idle_add_canvas_cb, canvas);
 	canvas->priv->items = NULL;
+	canvas->priv->current_selected_item = NULL;
 
 	canvas->xmouse = 50.;
 	canvas->ymouse = 50.;
 	
 	g_signal_connect (canvas, "event",
 			  G_CALLBACK (canvas_event_cb), canvas->priv->goocanvas);
+	g_signal_connect (canvas->priv->goocanvas, "scroll-event",
+			  G_CALLBACK (canvas_scroll_event_cb), canvas);
+	g_signal_connect (canvas, "motion-notify-event",
+			  G_CALLBACK (motion_notify_event_cb), canvas->priv->goocanvas);
 	g_signal_connect (canvas, "drag-begin",
 			  G_CALLBACK (drag_begin_cb), canvas->priv->goocanvas);
 	g_signal_connect (canvas, "drag-data-get",
@@ -212,13 +230,76 @@ drag_data_received_cb (BrowserCanvas *canvas, GdkDragContext *context,
 	}
 }
 
+static gboolean
+canvas_scroll_event_cb (GooCanvas *gcanvas, GdkEvent *event, BrowserCanvas *canvas)
+{
+	gboolean done = TRUE;
+
+	switch (event->type) {
+	case GDK_SCROLL:
+		if (((GdkEventScroll *) event)->direction == GDK_SCROLL_UP)
+			browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) + .03);
+		else if (((GdkEventScroll *) event)->direction == GDK_SCROLL_DOWN)
+			browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) - .03);
+		done = TRUE;
+		break;
+	default:
+		done = FALSE;
+		break;
+	}
+	return done;
+}
+
+static GdkCursor *hand_cursor = NULL;
+
+static gboolean
+motion_notify_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
+{
+	gboolean done = TRUE;
+
+	switch (event->type) {
+	case GDK_MOTION_NOTIFY:
+		if (((GdkEventMotion*) event)->state & GDK_BUTTON1_MASK) {
+			if (canvas->priv->canvas_moving) {
+				GtkAdjustment *ha, *va;
+				gdouble x, y;
+				ha = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (canvas));
+				va = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (canvas));
+
+				x = gtk_adjustment_get_value (ha);
+				x = CLAMP (x + canvas->xmouse - ((GdkEventMotion*) event)->x,
+					   ha->lower, ha->upper - ha->page_size);
+				gtk_adjustment_set_value (ha, x);
+
+				y = gtk_adjustment_get_value (va);
+				y = CLAMP (y + canvas->ymouse - ((GdkEventMotion*) event)->y,
+					   va->lower, va->upper - va->page_size);
+				gtk_adjustment_set_value (va, y);
+			}
+			else {
+				canvas->xmouse = ((GdkEventMotion*) event)->x;
+				canvas->ymouse = ((GdkEventMotion*) event)->y;
+				canvas->priv->canvas_moving = TRUE;
+				if (! hand_cursor)
+					hand_cursor = gdk_cursor_new (GDK_HAND2);
+				gdk_window_set_cursor (GTK_WIDGET (canvas)->window, hand_cursor);
+			}
+		}
+		done = TRUE;
+		break;
+	default:
+		done = FALSE;
+		break;
+	}
+	return done;
+}
 
 static void popup_zoom_in_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
 static void popup_zoom_out_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
 static void popup_zoom_fit_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
 static void popup_export_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
 static void popup_print_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
-static int
+static gboolean
 canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
 {
 	gboolean done = TRUE;
@@ -285,6 +366,34 @@ canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
 		}
 		done = TRUE;
 		break;
+	case GDK_BUTTON_RELEASE:
+		if (canvas->priv->canvas_moving) {
+			canvas->priv->canvas_moving = FALSE;
+			gdk_window_set_cursor (GTK_WIDGET (canvas)->window, NULL);
+		}
+		break;
+	case GDK_2BUTTON_PRESS:
+		x = ((GdkEventButton *) event)->x;
+		y = ((GdkEventButton *) event)->y;
+		goo_canvas_convert_from_pixels (gcanvas, &x, &y);
+		item = goo_canvas_get_item_at (gcanvas, x, y, TRUE);
+		if (item) {
+			GooCanvasItem *bitem;
+			for (bitem = item; bitem; bitem = goo_canvas_item_get_parent (bitem)) {
+				if (IS_BROWSER_CANVAS_ITEM (bitem)) {
+					gboolean allow_select;
+					g_object_get (G_OBJECT (bitem), "allow-select", &allow_select, NULL);
+					if (allow_select) {
+						browser_canvas_item_toggle_select (canvas, BROWSER_CANVAS_ITEM (bitem));
+						break;
+					}
+				}
+			}
+		}
+		else
+			browser_canvas_fit_zoom_factor (canvas);
+		done = TRUE;
+		break;
 	default:
 		done = FALSE;
 		break;
@@ -434,7 +543,17 @@ popup_print_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
 	browser_canvas_print (canvas);
 }
 
-static void item_destroyed_cb (BrowserCanvasItem *item, BrowserCanvas *canvas);
+
+static void
+weak_ref_lost (BrowserCanvas *canvas, BrowserCanvasItem *old_item)
+{
+        canvas->priv->items = g_slist_remove (canvas->priv->items, old_item);
+	if (canvas->priv->current_selected_item == old_item) {
+		canvas->priv->current_selected_item = NULL;
+		g_signal_emit (canvas, canvas_signals [ITEM_SELECTED], 0, NULL);
+	}
+}
+
 static void
 browser_canvas_dispose (GObject   * object)
 {
@@ -448,9 +567,8 @@ browser_canvas_dispose (GObject   * object)
 	/* get rid of the GooCanvasItems */
 	if (canvas->priv->items) {
 		GSList *list;
-		for (list = canvas->priv->items; list; list = list->next) {
-			g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (item_destroyed_cb), canvas);
-		}
+		for (list = canvas->priv->items; list; list = list->next)
+			g_object_weak_unref (G_OBJECT (list->data), (GWeakNotify) weak_ref_lost, canvas);
 		g_slist_free (canvas->priv->items);
 		canvas->priv->items = NULL;
 	}
@@ -459,6 +577,7 @@ browser_canvas_dispose (GObject   * object)
 	parent_class->dispose (object);
 }
 
+
 /**
  * browser_canvas_declare_item
  * @canvas: a #BrowserCanvas widget
@@ -473,22 +592,12 @@ browser_canvas_declare_item (BrowserCanvas *canvas, BrowserCanvasItem *item)
         g_return_if_fail (canvas->priv);
         g_return_if_fail (IS_BROWSER_CANVAS_ITEM (item));
 
-	g_print ("%s (canvas=>%p, item=>%p)\n", __FUNCTION__, canvas, item);
+	/*g_print ("%s (canvas=>%p, item=>%p)\n", __FUNCTION__, canvas, item);*/
         if (g_slist_find (canvas->priv->items, item))
                 return;
 
         canvas->priv->items = g_slist_prepend (canvas->priv->items, item);
-        g_signal_connect (G_OBJECT (item), "destroy",
-                          G_CALLBACK (item_destroyed_cb), canvas);
-}
-
-static void
-item_destroyed_cb (BrowserCanvasItem *item, BrowserCanvas *canvas)
-{
-	g_print ("%s (canvas=>%p, item=>%p)\n", __FUNCTION__, canvas, item);
-        g_return_if_fail (g_slist_find (canvas->priv->items, item));
-        g_signal_handlers_disconnect_by_func (G_OBJECT (item), G_CALLBACK (item_destroyed_cb), canvas);
-        canvas->priv->items = g_slist_remove (canvas->priv->items, item);
+	g_object_weak_ref (G_OBJECT (item), (GWeakNotify) weak_ref_lost, canvas);
 }
 
 
@@ -554,6 +663,10 @@ browser_canvas_set_zoom_factor (BrowserCanvas *canvas, gdouble n)
 	g_return_if_fail (IS_BROWSER_CANVAS (canvas));
 	g_return_if_fail (canvas->priv);
 
+	if (n < 0.01)
+		n = 0.01;
+	else if (n > 1.)
+		n = 1.;
 	goo_canvas_set_scale (canvas->priv->goocanvas, n);
 }
 
@@ -932,3 +1045,35 @@ browser_canvas_serialize_items (BrowserCanvas *canvas)
 		TO_IMPLEMENT;
 	}
 }
+
+/**
+ * browser_canvas_item_toggle_select
+ */
+void
+browser_canvas_item_toggle_select (BrowserCanvas *canvas, BrowserCanvasItem *item)
+{
+	gboolean do_select = TRUE;
+	g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+	g_return_if_fail (!item || IS_BROWSER_CANVAS_ITEM (item));
+
+	if (canvas->priv->current_selected_item == item) {
+		/* deselect item */
+		do_select = FALSE;
+	}
+
+	if (canvas->priv->current_selected_item) {
+		BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (canvas->priv->current_selected_item));
+		if (iclass->set_selected)
+			iclass->set_selected (canvas->priv->current_selected_item, FALSE);
+		canvas->priv->current_selected_item = NULL;
+	}
+
+
+	if (do_select && item) {
+		BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item));
+		if (iclass->set_selected)
+			iclass->set_selected (item, TRUE);
+		canvas->priv->current_selected_item = item;
+	}
+	g_signal_emit (canvas, canvas_signals [ITEM_SELECTED], 0, item);
+}
diff --git a/tools/browser/canvas/browser-canvas.h b/tools/browser/canvas/browser-canvas.h
index 97116e1..e3e109b 100644
--- a/tools/browser/canvas/browser-canvas.h
+++ b/tools/browser/canvas/browser-canvas.h
@@ -42,7 +42,7 @@ struct _BrowserCanvas
 {
 	GtkScrolledWindow   widget;
 
-	/* pointer position when a context menu was last opened */
+	/* pointer position when a context menu was last opened, or while moving around the canvas */
 	gdouble             xmouse;
 	gdouble             ymouse;
 
@@ -55,6 +55,9 @@ struct _BrowserCanvasClass
 {
 	GtkScrolledWindowClass parent_class;
 
+	/* signals */
+	void           (*item_selected) (BrowserCanvas *canvas, BrowserCanvasItem *item);
+
 	/* virtual functions */
 	void           (*clean_canvas_items)  (BrowserCanvas *canvas); /* clean any extra structure, not the individual items */
 
@@ -74,6 +77,8 @@ void               browser_canvas_perform_auto_layout     (BrowserCanvas *canvas
 void               browser_canvas_center                  (BrowserCanvas *canvas);
 gchar             *browser_canvas_serialize_items         (BrowserCanvas *canvas);
 
+void               browser_canvas_item_toggle_select      (BrowserCanvas *canvas, BrowserCanvasItem *item);
+
 G_END_DECLS
 
 #endif



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