[libgda] GdaBrowser: misc improvements



commit de77a744b8f3815ec1ad64fd21acdab30448d073
Author: Vivien Malerba <malerba gnome-db org>
Date:   Mon Jul 13 20:55:03 2009 +0200

    GdaBrowser: misc improvements
    
    * reworked menu
    * re-wrote favorites management
    * prepared work to create diagrams

 libgda-ui/data-entries/gdaui-entry-common-time.c   |    5 +-
 po/POTFILES.in                                     |    2 +
 tools/browser/.gitignore                           |    1 +
 tools/browser/Makefile.am                          |   18 +-
 tools/browser/browser-connection.c                 |  400 +---------
 tools/browser/browser-connection.h                 |   21 +-
 tools/browser/browser-favorites.c                  |  874 ++++++++++++++++++++
 tools/browser/browser-favorites.h                  |   82 ++
 tools/browser/canvas/browser-canvas-db-relations.c |   82 ++-
 tools/browser/canvas/browser-canvas-db-relations.h |    2 +
 tools/browser/canvas/browser-canvas-table.c        |   79 +-
 tools/browser/canvas/browser-canvas-table.h        |    1 +
 tools/browser/canvas/browser-canvas-utility.c      |    8 +-
 tools/browser/canvas/browser-canvas.c              |  101 +++-
 tools/browser/canvas/browser-canvas.h              |    3 +
 tools/browser/decl.h                               |    4 +-
 tools/browser/favorites-test.c                     |  260 ++++++
 tools/browser/gda-browser-diagram.png              |  Bin 0 -> 229 bytes
 tools/browser/mgr-favorites.c                      |  114 ++-
 tools/browser/mgr-favorites.h                      |    2 +
 tools/browser/schema-browser/Makefile.am           |    4 +-
 tools/browser/schema-browser/favorite-selector.c   |   33 +-
 tools/browser/schema-browser/relations-diagram.c   |  462 +++++++++++
 tools/browser/schema-browser/relations-diagram.h   |   55 ++
 .../schema-browser/schema-browser-perspective.c    |  102 +--
 tools/browser/schema-browser/table-columns.c       |    4 +-
 tools/browser/schema-browser/table-info.c          |   12 -
 tools/browser/schema-browser/table-relations.c     |    9 +-
 tools/browser/support.c                            |   21 +-
 tools/browser/support.h                            |    6 +
 30 files changed, 2186 insertions(+), 581 deletions(-)
---
diff --git a/libgda-ui/data-entries/gdaui-entry-common-time.c b/libgda-ui/data-entries/gdaui-entry-common-time.c
index cc3af6d..5f6031b 100644
--- a/libgda-ui/data-entries/gdaui-entry-common-time.c
+++ b/libgda-ui/data-entries/gdaui-entry-common-time.c
@@ -203,7 +203,10 @@ gdaui_entry_common_time_dispose (GObject   * object)
 
 	gdaui_entry_common_time = GDAUI_ENTRY_COMMON_TIME (object);
 	if (gdaui_entry_common_time->priv) {
-
+		if (gdaui_entry_common_time->priv->window) {
+			gtk_widget_destroy (gdaui_entry_common_time->priv->window);
+			gdaui_entry_common_time->priv->window = NULL;
+		}
 	}
 
 	/* parent class */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 804a88d..d6bcf2f 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -196,6 +196,7 @@ testing/gdaui-test-data-entries.c
 tools/browser/auth-dialog.c
 tools/browser/browser-connection.c
 tools/browser/browser-connections-list.c
+tools/browser/browser-favorites.c
 tools/browser/browser-window.c
 tools/browser/gda-browser-4.0.desktop.in
 tools/browser/login-dialog.c
@@ -212,6 +213,7 @@ tools/browser/schema-browser/favorite-selector.c
 tools/browser/schema-browser/mgr-columns.c
 tools/browser/schema-browser/objects-index.c
 tools/browser/schema-browser/perspective-main.c
+tools/browser/schema-browser/relations-diagram.c
 tools/browser/schema-browser/schema-browser-perspective.c
 tools/browser/schema-browser/table-columns.c
 tools/browser/schema-browser/table-info.c
diff --git a/tools/browser/.gitignore b/tools/browser/.gitignore
index b6f4891..1b1762b 100644
--- a/tools/browser/.gitignore
+++ b/tools/browser/.gitignore
@@ -3,3 +3,4 @@ gda-browser-4.*.desktop
 canvas-example
 marshal.c
 marshal.h
+favorites-test
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 9a55bac..d4ea7eb 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -1,5 +1,5 @@
 bin_PROGRAMS=gda-browser-4.0
-noinst_PROGRAMS = 
+noinst_PROGRAMS = favorites-test
 
 SUBDIRS = schema-browser dummy-perspective
 if HAVE_GOOCANVAS
@@ -45,6 +45,8 @@ gda_browser_4_0_SOURCES=\
 	browser-spinner.h \
 	browser-connection.c \
 	browser-connection.h \
+	browser-favorites.c \
+	browser-favorites.h \
 	browser-variable.c \
 	browser-variable.h \
 	browser-window.c \
@@ -83,6 +85,17 @@ gda_browser_4_0_LDADD+= \
 	$(top_builddir)/tools/browser/canvas/libcanvas.la
 endif
 
+favorites_test_SOURCES=\
+	browser-favorites.c \
+	browser-favorites.h \
+	favorites-test.c
+
+favorites_test_LDADD=\
+	$(top_builddir)/tools/browser/schema-browser/libperspective.la \
+	$(top_builddir)/tools/browser/dummy-perspective/libperspective.la \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(LIBGDA_LIBS)
+
 @INTLTOOL_DESKTOP_RULE@
 
 desktopdir=$(datadir)/applications
@@ -121,7 +134,8 @@ icons_DATA= \
 	gda-browser-column-fk.png \
 	gda-browser-column-nn.png \
 	gda-browser-column-pk.png \
-	gda-browser-reference.png
+	gda-browser-reference.png \
+	gda-browser-diagram.png
 
 EXTRA_DIST = \
 	marshal.list \
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index b70bd85..45959ce 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -24,6 +24,7 @@
 #include "support.h"
 #include "marshal.h"
 #include <sql-parser/gda-sql-parser.h>
+#include <libgda/gda-sql-builder.h>
 
 /* code inclusion */
 #include "../dict-file-name.c"
@@ -44,7 +45,7 @@ struct _BrowserConnectionPrivate {
 	GdaMetaStruct *p_mstruct; /* private GdaMetaStruct: while it is being created */
 	GdaMetaStruct *mstruct; /* public GdaMetaStruct: once it has been created and is no more modified */
 
-	gboolean       meta_store_addons_init_done;
+	BrowserFavorites *bfav;
 };
 
 
@@ -170,7 +171,7 @@ browser_connection_class_init (BrowserConnectionClass *klass)
 	browser_connection_signals [FAV_CHANGED] =
 		g_signal_new ("favorites-changed",
                               G_TYPE_FROM_CLASS (object_class),
-                              G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
+                              G_SIGNAL_RUN_FIRST,
                               G_STRUCT_OFFSET (BrowserConnectionClass, favorites_changed),
                               NULL, NULL,
                               g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
@@ -201,7 +202,7 @@ browser_connection_init (BrowserConnection *bcnc)
 	bcnc->priv->p_mstruct = NULL;
 	bcnc->priv->mstruct = NULL;
 
-	bcnc->priv->meta_store_addons_init_done = FALSE;
+	bcnc->priv->bfav = NULL;
 }
 
 static void
@@ -224,6 +225,12 @@ clear_dsn_info (BrowserConnection *bcnc)
 }
 
 static void
+fav_changed_cb (BrowserFavorites *bfav, BrowserConnection *bcnc)
+{
+	g_signal_emit (bcnc, browser_connection_signals [FAV_CHANGED], 0);
+}
+
+static void
 browser_connection_dispose (GObject *object)
 {
 	BrowserConnection *bcnc;
@@ -261,6 +268,11 @@ browser_connection_dispose (GObject *object)
 			g_slist_foreach (bcnc->priv->variables, (GFunc) g_object_unref, NULL);
 			g_slist_free (bcnc->priv->variables);
 		}
+		if (bcnc->priv->bfav) {
+			g_signal_handlers_disconnect_by_func (bcnc->priv->bfav,
+							      G_CALLBACK (fav_changed_cb), bcnc);
+			g_object_unref (bcnc->priv->bfav);
+		}
 
 		g_free (bcnc->priv);
 		bcnc->priv = NULL;
@@ -602,382 +614,20 @@ browser_connection_get_dictionary_file (BrowserConnection *bcnc)
 	return bcnc->priv->dict_file_name;
 }
 
-#define FAVORITES_TABLE_NAME "gda_sql_favorites"
-#define FAVORITES_TABLE_DESC \
-        "<table name=\"" FAVORITES_TABLE_NAME "\"> "                            \
-        "   <column name=\"session\" type=\"gint\" pkey=\"TRUE\"/>"             \
-        "   <column name=\"type\" pkey=\"TRUE\"/>"                              \
-        "   <column name=\"contents\" pkey=\"TRUE\"/>"                          \
-        "   <column name=\"descr\" nullok=\"TRUE\"/>"                           \
-        "   <column name=\"rank\" type=\"gint\"/>"                             \
-        "   <unique>"                             \
-        "     <column name=\"type\"/>"                             \
-        "     <column name=\"rank\"/>"                             \
-        "   </unique>"                             \
-        "</table>"
-#define FAVORITE_DELETE \
-        "DELETE FROM " FAVORITES_TABLE_NAME " WHERE session = ##session::gint AND type= ##type::string AND " \
-	"contents = ##contents::string"
-#define FAVORITE_SHIFT \
-	"UPDATE " FAVORITES_TABLE_NAME " SET rank = rank + 1 WHERE rank >= ##rank::gint"
-#define FAVORITE_INSERT \
-        "INSERT INTO " FAVORITES_TABLE_NAME " (session, type, contents, rank, descr) VALUES (##session::gint, " \
-	"##type::string, ##contents::string, ##rank::gint, ##descr::string)"
-#define FAVORITE_SELECT \
-        "SELECT contents, descr, rank FROM " FAVORITES_TABLE_NAME " WHERE session = ##session::gint " \
-	"AND type = ##type::string ORDER by rank"
-#define FAVORITE_UPDATE_RANK \
-	"UPDATE " FAVORITES_TABLE_NAME " SET rank = ##newrank::gint WHERE rank = ##rank::gint AND " \
-	"session = ##session::gint AND type = ##type::string"
-
-static gboolean
-meta_store_addons_init (BrowserConnection *bcnc, GError **error)
-{
-	GdaMetaStore *mstore;
-	GError *lerror = NULL;
-	mstore = gda_connection_get_meta_store (bcnc->priv->cnc);
-	if (!gda_meta_store_schema_add_custom_object (mstore, FAVORITES_TABLE_DESC, &lerror)) {
-                g_set_error (error, 0, 0, "%s",
-                             _("Can't initialize dictionary to store favorites"));
-		g_warning ("Can't initialize dictionary to store favorites :%s",
-			   lerror && lerror->message ? lerror->message : "No detail");
-		if (lerror)
-			g_error_free (lerror);
-                return FALSE;
-        }
-	
-	bcnc->priv->meta_store_addons_init_done = TRUE;
-	return TRUE;
-}
-
-static const gchar *
-favorite_type_to_string (BrowserFavoritesType type)
-{
-	switch (type) {
-	case BROWSER_FAVORITES_TABLES:
-		return "TABLE";
-	default:
-		g_warning ("Unknow type of favorite");
-	}
-
-	return "";
-}
-
 /**
- * browser_connection_add_favorite
- */
-gboolean
-browser_connection_add_favorite (BrowserConnection *bcnc, guint session_id, BrowserConnectionFavorite *fav,
-				 gint pos, GError **error)
-{
-	GdaMetaStore *mstore;
-	GdaConnection *store_cnc;
-	GdaStatement *stmt;
-	GdaSqlParser *parser = NULL;
-	GdaSet *params = NULL;
-
-	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
-	g_return_val_if_fail (fav && fav->contents, FALSE);
-
-	if (! bcnc->priv->meta_store_addons_init_done &&
-	    ! meta_store_addons_init (bcnc, error))
-		return FALSE;
-
-	mstore = gda_connection_get_meta_store (bcnc->priv->cnc);
-	store_cnc = gda_meta_store_get_internal_connection (mstore);
-	
-	if (! gda_lockable_trylock (GDA_LOCKABLE (store_cnc))) {
-		g_set_error (error, 0, 0, "%s",
-                             _("Can't initialize transaction to access favorites"));
-		return FALSE;
-	}
-	/* begin a transaction */
-	if (! gda_connection_begin_transaction (store_cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL)) {
-		g_set_error (error, 0, 0, "%s",
-                             _("Can't initialize transaction to access favorites"));
-		gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
-                return FALSE;
-	}
-
-	/* create parser */
-	parser = gda_connection_create_parser (store_cnc);
-	if (!parser)
-		parser = gda_sql_parser_new ();
-
-	/* DELETE any favorite existing */
-	if (pos < 0)
-		pos = 0;
-	params = gda_set_new_inline (6,
-				     "session", G_TYPE_INT, session_id,
-				     "type", G_TYPE_STRING, favorite_type_to_string (fav->type),
-				     "contents", G_TYPE_STRING, fav->contents,
-				     "rank", G_TYPE_INT, pos,
-				     "newrank", G_TYPE_INT, 0,
-				     "descr", G_TYPE_STRING, fav->descr);
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_DELETE, NULL, error);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_DELETE);
-		goto err;
-	}
-	if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
-		g_object_unref (stmt);
-		goto err;
-	}
-	g_object_unref (stmt);
-	stmt = NULL;
-
-	/* renumber favorites */
-	GdaDataModel *model;
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_SELECT, NULL, NULL);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_SELECT);
-		goto err;
-	}
-	model = gda_connection_statement_execute_select (store_cnc, stmt, params, error);
-	g_object_unref (stmt);
-	stmt = NULL;
-	if (!model)
-		goto err;
-	
-	gint i, nrows;
-	nrows = gda_data_model_get_n_rows (model);
-	for (i = 0; i < nrows; i++) {
-		const GValue *rank;
-
-		rank = gda_data_model_get_value_at (model, 2, i, error);
-		if (!rank) {
-			g_set_error (error, 0, 0, "%s",
-				     _("Can't get current favorites"));
-			g_object_unref (model);
-			goto err;
-		}
-		if (g_value_get_int (rank) != i) {
-			if (!stmt) {
-				stmt = gda_sql_parser_parse_string (parser, FAVORITE_UPDATE_RANK, NULL, NULL);
-				if (!stmt) {
-					g_warning ("Could not parse internal statement: %s", FAVORITE_UPDATE_RANK);
-					g_object_unref (model);
-					goto err;
-				}
-			}
-			gda_set_set_holder_value (params, NULL, "newrank", i);
-			gda_set_set_holder_value (params, NULL, "rank", g_value_get_int (rank));
-			if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
-				g_object_unref (stmt);
-				g_object_unref (model);
-				goto err;
-			}
-		}
-	}
-	if (stmt)
-		g_object_unref (stmt);
-	stmt = NULL;
-	g_object_unref (model);
-	if (pos > nrows)
-		pos = nrows;
-	gda_set_set_holder_value (params, NULL, "rank", pos);
-
-	/* SHIFT favorites */
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_SHIFT, NULL, error);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_SHIFT);
-		goto err;
-	}
-	if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
-		g_object_unref (stmt);
-		goto err;
-	}
-	g_object_unref (stmt);
-	stmt = NULL;
-
-	/* ADD new favorite */
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_INSERT, NULL, error);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_INSERT);
-		goto err;
-	}
-	if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
-		g_object_unref (stmt);
-		goto err;
-	}
-	g_object_unref (stmt);
-	stmt = NULL;
-					    
-	if (! gda_connection_commit_transaction (store_cnc, NULL, NULL)) {
-		g_set_error (error, 0, 0, "%s",
-                             _("Can't commit transaction to access favorites"));
-		goto err;
-	}
-
-	if (params)
-		g_object_unref (params);
-	if (parser)
-		g_object_unref (parser);
-	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
-	g_signal_emit (bcnc, browser_connection_signals [FAV_CHANGED],
-		       g_quark_from_string (favorite_type_to_string (fav->type)));
-	return TRUE;
-
- err:
-	if (params)
-		g_object_unref (params);
-	if (parser)
-		g_object_unref (parser);
-	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
-	gda_connection_rollback_transaction (store_cnc, NULL, NULL);
-	return FALSE;
-}
-
-/**
- * browser_connection_free_favorites_list
- */
-void
-browser_connection_free_favorites_list (GSList *fav_list)
-{
-	GSList *list;
-	if (!fav_list)
-		return;
-	for (list = fav_list; list; list = list->next) {
-		BrowserConnectionFavorite *fav = (BrowserConnectionFavorite*) list->data;
-		g_free (fav->contents);
-		g_free (fav->descr);
-		g_free (fav);
-	}
-	g_slist_free (fav_list);
-}
-
-/**
- * browser_connection_list_favorites
+ * browser_connection_get_favorites
  *
- * Returns: a new list of #BrowserConnectionFavorite pointers. The list has to 
- *          be freed using browser_connection_free_favorites_list()
+ * Get the favorites handler
  */
-GSList *
-browser_connection_list_favorites (BrowserConnection *bcnc, guint session_id, BrowserFavoritesType type, GError **error)
+BrowserFavorites *
+browser_connection_get_favorites (BrowserConnection *bcnc)
 {
-	GdaMetaStore *mstore;
-	GdaConnection *store_cnc;
-	GdaStatement *stmt;
-	GdaSqlParser *parser = NULL;
-	GdaSet *params = NULL;
-	GSList *fav_list = NULL;
-	GdaDataModel *model;
-
-	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
-	if (! bcnc->priv->meta_store_addons_init_done &&
-	    ! meta_store_addons_init (bcnc, NULL))
-		return NULL;
-
-	mstore = gda_connection_get_meta_store (bcnc->priv->cnc);
-	store_cnc = gda_meta_store_get_internal_connection (mstore);
-	
-	if (! gda_lockable_trylock (GDA_LOCKABLE (store_cnc))) {
-		g_set_error (error, 0, 0, "%s",
-			     _("Can't initialize transaction to access favorites"));
-		return NULL;
-	}
-
-	/* create parser */
-	parser = gda_connection_create_parser (store_cnc);
-	if (!parser)
-		parser = gda_sql_parser_new ();
-
-	/* SELECT favorites */
-	params = gda_set_new_inline (2,
-				     "session", G_TYPE_INT, session_id,
-				     "type", G_TYPE_STRING, favorite_type_to_string (type));
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_SELECT, NULL, NULL);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_SELECT);
-		goto out;
-	}
-	model = gda_connection_statement_execute_select (store_cnc, stmt, params, error);
-	g_object_unref (stmt);
-	if (!model)
-		goto out;
-	
-	gint i, nrows;
-	nrows = gda_data_model_get_n_rows (model);
-	for (i = 0; i < nrows; i++) {
-		const GValue *contents, *descr;
-
-		contents = gda_data_model_get_value_at (model, 0, i, error);
-		if (contents)
-			descr = gda_data_model_get_value_at (model, 1, i, error);
-		if (contents && descr) {
-			BrowserConnectionFavorite *fav;
-			fav = g_new0 (BrowserConnectionFavorite, 1);
-			fav->type = type;
-			if (G_VALUE_TYPE (descr) == G_TYPE_STRING)
-				fav->descr = g_value_dup_string (descr);
-			fav->contents = g_value_dup_string (contents);
-			fav_list = g_slist_prepend (fav_list, fav);
-		}
-		else {
-			browser_connection_free_favorites_list (fav_list);
-			fav_list = NULL;
-			goto out;
-		}
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+	if (!bcnc->priv->bfav) {
+		bcnc->priv->bfav = browser_favorites_new (gda_connection_get_meta_store (bcnc->priv->cnc));
+		g_signal_connect (bcnc->priv->bfav, "favorites-changed",
+				  G_CALLBACK (fav_changed_cb), bcnc);
 	}
-
- out:
-	g_object_unref (parser);
-	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
-	return g_slist_reverse (fav_list);
+	return bcnc->priv->bfav;
 }
 
-
-/**
- * browser_connection_delete_favorite
- */
-gboolean
-browser_connection_delete_favorite (BrowserConnection *bcnc, guint session_id,
-				    BrowserConnectionFavorite *fav, GError **error)
-{
-	GdaMetaStore *mstore;
-	GdaConnection *store_cnc;
-	GdaStatement *stmt;
-	GdaSqlParser *parser = NULL;
-	GdaSet *params = NULL;
-	gboolean retval = FALSE;
-
-	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
-	if (! bcnc->priv->meta_store_addons_init_done &&
-	    ! meta_store_addons_init (bcnc, NULL))
-		return NULL;
-
-	mstore = gda_connection_get_meta_store (bcnc->priv->cnc);
-	store_cnc = gda_meta_store_get_internal_connection (mstore);
-	
-	gda_lockable_lock (GDA_LOCKABLE (store_cnc));
-
-	/* create parser */
-	parser = gda_connection_create_parser (store_cnc);
-	if (!parser)
-		parser = gda_sql_parser_new ();
-
-	/* DELETE any favorite existing */
-	params = gda_set_new_inline (3,
-				     "session", G_TYPE_INT, session_id,
-				     "type", G_TYPE_STRING, favorite_type_to_string (fav->type),
-				     "contents", G_TYPE_STRING, fav->contents);
-	stmt = gda_sql_parser_parse_string (parser, FAVORITE_DELETE, NULL, error);
-	if (!stmt) {
-		g_warning ("Could not parse internal statement: %s", FAVORITE_DELETE);
-		goto out;
-	}
-	if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
-		g_object_unref (stmt);
-		goto out;
-	}
-	g_object_unref (stmt);
-	stmt = NULL;
-	retval = TRUE;
-	g_signal_emit (bcnc, browser_connection_signals [FAV_CHANGED],
-		       g_quark_from_string (favorite_type_to_string (fav->type)));
-
- out:
-	g_object_unref (parser);
-	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
-	return retval;
-}
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index fb3d0ef..ed934ec 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -22,6 +22,7 @@
 #define __BROWSER_CONNECTION_H_
 
 #include <libgda/libgda.h>
+#include "browser-favorites.h"
 #include "decl.h"
 
 G_BEGIN_DECLS
@@ -64,27 +65,13 @@ GdaMetaStruct      *browser_connection_get_meta_struct        (BrowserConnection
 GdaMetaStore       *browser_connection_get_meta_store         (BrowserConnection *bcnc);
 const gchar        *browser_connection_get_dictionary_file    (BrowserConnection *bcnc);
 
+BrowserFavorites   *browser_connection_get_favorites          (BrowserConnection *bcnc);
+
 /*
  * statements's execution
  */
+/* TODO */
 
-
-/*
- * Favorites management
- */
-typedef struct {
-	BrowserFavoritesType  type;
-	gchar                *descr;
-	gchar                *contents;
-} BrowserConnectionFavorite;
-gboolean            browser_connection_add_favorite           (BrowserConnection *bcnc, guint session_id,
-							       BrowserConnectionFavorite *fav, gint pos,
-							       GError **error);
-GSList             *browser_connection_list_favorites         (BrowserConnection *bcnc, guint session_id, 
-							       BrowserFavoritesType type, GError **error);
-gboolean            browser_connection_delete_favorite        (BrowserConnection *bcnc, guint session_id,
-							       BrowserConnectionFavorite *fav, GError **error);
-void                browser_connection_free_favorites_list    (GSList *fav_list);
 G_END_DECLS
 
 #endif
diff --git a/tools/browser/browser-favorites.c b/tools/browser/browser-favorites.c
new file mode 100644
index 0000000..4d37ce8
--- /dev/null
+++ b/tools/browser/browser-favorites.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * 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 <string.h>
+#include <glib/gi18n-lib.h>
+#include "browser-favorites.h"
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
+#include "support.h"
+#include "marshal.h"
+#include <libgda/gda-sql-builder.h>
+
+struct _BrowserFavoritesPrivate {
+	GdaMetaStore  *store;
+	GdaConnection *store_cnc;
+};
+
+
+/*
+ * Main static functions
+ */
+static void browser_favorites_class_init (BrowserFavoritesClass *klass);
+static void browser_favorites_init (BrowserFavorites *bfav);
+static void browser_favorites_dispose (GObject *object);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+/* signals */
+enum {
+	FAV_CHANGED,
+	LAST_SIGNAL
+};
+
+gint browser_favorites_signals [LAST_SIGNAL] = { 0 };
+
+GType
+browser_favorites_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (BrowserFavoritesClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) browser_favorites_class_init,
+			NULL,
+			NULL,
+			sizeof (BrowserFavorites),
+			0,
+			(GInstanceInitFunc) browser_favorites_init
+		};
+
+
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (G_TYPE_OBJECT, "BrowserFavorites", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+browser_favorites_class_init (BrowserFavoritesClass *klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	browser_favorites_signals [FAV_CHANGED] =
+		g_signal_new ("favorites-changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
+                              G_STRUCT_OFFSET (BrowserFavoritesClass, favorites_changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+                              0);
+
+	klass->favorites_changed = NULL;
+
+	object_class->dispose = browser_favorites_dispose;
+}
+
+static void
+browser_favorites_init (BrowserFavorites *bfav)
+{
+	bfav->priv = g_new0 (BrowserFavoritesPrivate, 1);
+	bfav->priv->store = NULL;
+	bfav->priv->store_cnc = NULL;
+}
+
+static void
+browser_favorites_dispose (GObject *object)
+{
+	BrowserFavorites *bfav;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (BROWSER_IS_FAVORITES (object));
+
+	bfav = BROWSER_FAVORITES (object);
+	if (bfav->priv) {
+		if (bfav->priv->store)
+			g_object_unref (bfav->priv->store);
+		if (bfav->priv->store_cnc)
+			g_object_unref (bfav->priv->store_cnc);
+
+		g_free (bfav->priv);
+		bfav->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+/**
+ * browser_favorites_new
+ *
+ * Creates a new #BrowserFavorites object
+ *
+ * Returns: the new object
+ */
+BrowserFavorites*
+browser_favorites_new (GdaMetaStore *store)
+{
+	BrowserFavorites *bfav;
+
+	g_return_val_if_fail (GDA_IS_META_STORE (store), NULL);
+
+	bfav = BROWSER_FAVORITES (g_object_new (BROWSER_TYPE_FAVORITES, NULL));
+	bfav->priv->store = g_object_ref (store);
+
+	return bfav;
+}
+
+#define FAVORITES_TABLE_NAME "gda_sql_favorites"
+#define FAVORITES_TABLE_DESC \
+        "<table name=\"" FAVORITES_TABLE_NAME "\"> "                            \
+        "   <column name=\"id\" type=\"gint\" pkey=\"TRUE\" autoinc=\"TRUE\"/>"             \
+        "   <column name=\"session\" type=\"gint\"/>"             \
+        "   <column name=\"type\"/>"                              \
+        "   <column name=\"name\" nullok=\"TRUE\"/>"                              \
+        "   <column name=\"contents\"/>"                          \
+        "   <column name=\"descr\" nullok=\"TRUE\"/>"                           \
+        "   <unique>"                             \
+        "     <column name=\"session\"/>"                             \
+        "     <column name=\"type\"/>"                             \
+        "     <column name=\"contents\"/>"                             \
+        "   </unique>"                             \
+        "</table>"
+#define FAVORDER_TABLE_NAME "gda_sql_favorder"
+#define FAVORDER_TABLE_DESC \
+        "<table name=\"" FAVORDER_TABLE_NAME "\"> "                            \
+        "   <column name=\"order_key\" type=\"gint\" pkey=\"TRUE\"/>"          \
+        "   <column name=\"fav_id\" type=\"gint\" pkey=\"TRUE\"/>"             \
+        "   <column name=\"rank\" type=\"gint\"/>"             \
+        "</table>"
+
+static gboolean
+meta_store_addons_init (BrowserFavorites *bfav, GError **error)
+{
+	GError *lerror = NULL;
+
+	if (!gda_meta_store_schema_add_custom_object (bfav->priv->store, FAVORITES_TABLE_DESC, &lerror)) {
+                g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize dictionary to store favorites"));
+		g_warning ("Can't initialize dictionary to store favorites :%s",
+			   lerror && lerror->message ? lerror->message : "No detail");
+		if (lerror)
+			g_error_free (lerror);
+                return FALSE;
+        }
+	if (!gda_meta_store_schema_add_custom_object (bfav->priv->store, FAVORDER_TABLE_DESC, &lerror)) {
+                g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize dictionary to store favorites"));
+		g_warning ("Can't initialize dictionary to store favorites :%s",
+			   lerror && lerror->message ? lerror->message : "No detail");
+		if (lerror)
+			g_error_free (lerror);
+                return FALSE;
+        }
+
+	bfav->priv->store_cnc = g_object_ref (gda_meta_store_get_internal_connection (bfav->priv->store));
+	return TRUE;
+}
+
+static const gchar *
+favorite_type_to_string (BrowserFavoritesType type)
+{
+	switch (type) {
+	case BROWSER_FAVORITES_TABLES:
+		return "TABLE";
+	case BROWSER_FAVORITES_DIAGRAMS:
+		return "DIAGRAM";
+	default:
+		g_warning ("Unknow type of favorite");
+	}
+
+	return "";
+}
+
+static BrowserFavoritesType
+favorite_string_to_type (const gchar *str)
+{
+	if (*str == 'T')
+		return BROWSER_FAVORITES_TABLES;
+	else if (*str == 'D')
+		return BROWSER_FAVORITES_DIAGRAMS;
+	else
+		g_warning ("Unknow type '%s' of favorite", str);
+	return 0;
+}
+
+
+/*
+ * Find a favorite ID from its ID or from its contents
+ *
+ * Returns: the ID or -1 if not found (and sets ERROR)
+ */
+static gint
+find_favorite (BrowserFavorites *bfav, guint session_id, gint id, const gchar *contents, GError **error)
+{
+	GdaSqlBuilder *b;
+	GdaStatement *stmt;
+	GdaSet *params = NULL;
+	GdaDataModel *model;
+	gint favid = -1;
+
+	g_return_val_if_fail ((id >= 0) || contents, -1);
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "id"), 0);
+	gda_sql_builder_select_add_target (b, 0,
+					   gda_sql_builder_literal (b, 0, FAVORITES_TABLE_NAME),
+					   NULL);
+
+	if (id >= 0) {
+		/* lookup from ID */
+		gda_sql_builder_set_where (b,
+		    gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+					  gda_sql_builder_literal (b, 0, "id"),
+					  gda_sql_builder_param (b, 0, "id", G_TYPE_INT, FALSE), 0));
+	}
+	else {
+		/* lookup using session and contents */
+		gda_sql_builder_set_where (b,
+	            gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_AND,
+					  gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+						   gda_sql_builder_literal (b, 0, "session"),
+						   gda_sql_builder_param (b, 0, "session", G_TYPE_INT, FALSE), 0),
+					  gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+						   gda_sql_builder_literal (b, 0, "contents"),
+						   gda_sql_builder_param (b, 0, "contents", G_TYPE_INT, FALSE), 0), 0));
+	}
+	stmt = gda_sql_builder_get_statement (b, error);
+	g_object_unref (G_OBJECT (b));
+	if (!stmt)
+		return -1;
+	params = gda_set_new_inline (3,
+				     "session", G_TYPE_INT, session_id,
+				     "id", G_TYPE_INT, id,
+				     "contents", G_TYPE_STRING, contents);
+	model = gda_connection_statement_execute_select (bfav->priv->store_cnc, stmt, params, error);
+	g_object_unref (stmt);
+	g_object_unref (params);
+
+	if (!model)
+		return -1;
+
+	gint nrows;
+	nrows = gda_data_model_get_n_rows (model);
+	if (nrows == 1) {
+		const GValue *cvalue;
+		cvalue = gda_data_model_get_value_at (model, 0, 0, error);
+		if (cvalue)
+			favid = g_value_get_int (cvalue);
+	}
+
+	g_object_unref (G_OBJECT (model));
+	return favid;
+}
+
+/*
+ * Reorders the favorites for @order_key, making sure @id is at position @new_pos
+ */
+static gboolean
+favorites_reorder (BrowserFavorites *bfav, gint order_key, gint id, gint new_pos, GError **error)
+{
+	GdaSqlBuilder *b;
+	GdaStatement *stmt;
+	GdaSet *params = NULL;
+	GdaDataModel *model;
+
+	g_assert (id >= 0);
+	g_assert (order_key >= 0);
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	gda_sql_builder_add_field (b, gda_sql_builder_literal (b, 0, "fav_id"), 0);
+
+	gda_sql_builder_select_add_target (b, 0,
+					   gda_sql_builder_literal (b, 0, FAVORDER_TABLE_NAME),
+					   NULL);
+	
+	gda_sql_builder_set_where (b, gda_sql_builder_cond (b, 1, GDA_SQL_OPERATOR_TYPE_EQ,
+				    gda_sql_builder_literal (b, 0, "order_key"),
+				    gda_sql_builder_param (b, 0, "orderkey", G_TYPE_INT, FALSE), 0));
+	gda_sql_builder_select_order_by (b,
+					 gda_sql_builder_literal (b, 0, "rank"), TRUE, NULL);
+	stmt = gda_sql_builder_get_statement (b, error);
+	g_object_unref (G_OBJECT (b));
+	if (!stmt)
+		return FALSE;
+	params = gda_set_new_inline (3,
+				     "orderkey", G_TYPE_INT, order_key,
+				     "rank", G_TYPE_INT, 0,
+				     "id", G_TYPE_INT, id);
+	model = gda_connection_statement_execute_select (bfav->priv->store_cnc, stmt, params, error);
+	g_object_unref (stmt);
+	if (!model) {
+		g_object_unref (params);
+		return FALSE;
+	}
+
+	gint i, nrows;
+	gboolean retval = TRUE;
+	nrows = gda_data_model_get_n_rows (model);
+	if (new_pos < 0)
+		new_pos = 0;
+	else if (new_pos > nrows)
+		new_pos = nrows;
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_UPDATE);
+	gda_sql_builder_set_table (b, FAVORDER_TABLE_NAME);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "rank"),
+				   gda_sql_builder_param (b, 0, "rank", G_TYPE_INT, FALSE));
+	gda_sql_builder_cond (b, 1, GDA_SQL_OPERATOR_TYPE_EQ,
+			      gda_sql_builder_literal (b, 0, "fav_id"),
+			      gda_sql_builder_param (b, 0, "id", G_TYPE_INT, FALSE),
+			      0);
+	gda_sql_builder_cond (b, 2, GDA_SQL_OPERATOR_TYPE_EQ,
+			      gda_sql_builder_literal (b, 0, "order_key"),
+			      gda_sql_builder_param (b, 0, "orderkey", G_TYPE_INT, FALSE),
+			      0);
+	gda_sql_builder_set_where (b, gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_AND, 1, 2, 0));
+	stmt = gda_sql_builder_get_statement (b, error);
+	if (!stmt) {
+		retval = FALSE;
+		goto out;
+	}
+
+	/* reodering the rows */
+	for (i = 0; i < nrows; i++) {
+		const GValue *v;
+		v = gda_data_model_get_value_at (model, 0, i, error);
+		if (v) {
+			g_assert (gda_holder_set_value (gda_set_get_holder (params, "id"), v, NULL));
+			if (g_value_get_int (v) == id)
+				g_assert (gda_set_set_holder_value (params, NULL, "rank", new_pos));
+			else
+				g_assert (gda_set_set_holder_value (params, NULL, "rank", i < new_pos ? i : i + 1));
+			if (gda_connection_statement_execute_non_select (bfav->priv->store_cnc, stmt,
+									 params, NULL, error) == -1) {
+				retval = FALSE;
+				goto out;
+			}
+		}
+		else {
+			retval = FALSE;
+			goto out;
+		}
+	}
+
+ out:
+	g_object_unref (b);
+	g_object_unref (params);
+	g_object_unref (model);
+	if (stmt)
+		g_object_unref (stmt);
+	return retval;
+}
+
+/**
+ * browser_favorites_add
+ *
+ * Add a new favorite, or replace an existing one.
+ * NOTE:
+ *   - if @fav->id is NULL then it's either an update or an insert (depending if fav->contents exists)
+ *     and if it's not it is an UPDATE
+ *   - @fav->type can't be 0
+ *   - @fav->contents can't be %NULL
+ *
+ * if @order_key is negative, then no ordering is done and @pos is ignored.
+ */
+gint
+browser_favorites_add (BrowserFavorites *bfav, guint session_id,
+		       BrowserFavoritesAttributes *fav,
+		       gint order_key, gint pos,
+		       GError **error)
+{
+	GdaConnection *store_cnc;
+	GdaSet *params = NULL;
+	gint favid = -1;
+
+	g_return_val_if_fail (BROWSER_IS_FAVORITES (bfav), FALSE);
+	g_return_val_if_fail (fav, FALSE);
+	g_return_val_if_fail (fav->contents, FALSE);
+	g_return_val_if_fail (fav->type, FALSE);
+
+	if (! bfav->priv->store_cnc &&
+	    ! meta_store_addons_init (bfav, error))
+		return FALSE;
+
+	store_cnc = bfav->priv->store_cnc;
+	if (! gda_lockable_trylock (GDA_LOCKABLE (store_cnc))) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize transaction to access favorites"));
+		return FALSE;
+	}
+	/* begin a transaction */
+	if (! gda_connection_begin_transaction (store_cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL)) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize transaction to access favorites"));
+		gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
+                return FALSE;
+	}
+
+	params = gda_set_new_inline (8,
+				     "session", G_TYPE_INT, session_id,
+				     "id", G_TYPE_INT, fav->id,
+				     "type", G_TYPE_STRING, favorite_type_to_string (fav->type),
+				     "name", G_TYPE_STRING, fav->name,
+				     "contents", G_TYPE_STRING, fav->contents,
+				     "rank", G_TYPE_INT, pos,
+				     "orderkey", G_TYPE_INT, order_key,
+				     "descr", G_TYPE_STRING, fav->descr);
+
+	favid = find_favorite (bfav, session_id, fav->id, fav->contents, NULL);
+	if (favid == -1) {
+		/* insert a favorite */
+		GdaSqlBuilder *builder;
+		GdaStatement *stmt;
+		builder = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
+		gda_sql_builder_set_table (builder, FAVORITES_TABLE_NAME);
+
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "session"),
+					   gda_sql_builder_param (builder, 0, "session", G_TYPE_INT, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "type"),
+					   gda_sql_builder_param (builder, 0, "type", G_TYPE_INT, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "name"),
+					   gda_sql_builder_param (builder, 0, "name", G_TYPE_STRING, TRUE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "contents"),
+					   gda_sql_builder_param (builder, 0, "contents", G_TYPE_STRING, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "descr"),
+					   gda_sql_builder_param (builder, 0, "descr", G_TYPE_STRING, TRUE));
+		stmt = gda_sql_builder_get_statement (builder, error);
+		g_object_unref (G_OBJECT (builder));
+		if (!stmt)
+			goto err;
+		if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
+			g_object_unref (stmt);
+			goto err;
+		}
+		g_object_unref (stmt);
+	}
+	else {
+		/* update favorite's contents */
+		GdaSqlBuilder *builder;
+		GdaStatement *stmt;
+
+		gda_set_set_holder_value (params, NULL, "id", favid);
+		builder = gda_sql_builder_new (GDA_SQL_STATEMENT_UPDATE);
+		gda_sql_builder_set_table (builder, FAVORITES_TABLE_NAME);
+
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "name"),
+					   gda_sql_builder_param (builder, 0, "name", G_TYPE_STRING, TRUE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "contents"),
+					   gda_sql_builder_param (builder, 0, "contents", G_TYPE_STRING, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "descr"),
+					   gda_sql_builder_param (builder, 0, "descr", G_TYPE_STRING, TRUE));
+
+		gda_sql_builder_set_where (builder,
+					   gda_sql_builder_cond (builder, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+								 gda_sql_builder_literal (builder, 0, "id"),
+								 gda_sql_builder_param (builder, 0, "id", G_TYPE_INT, FALSE),
+								 0));
+		stmt = gda_sql_builder_get_statement (builder, error);
+		g_object_unref (G_OBJECT (builder));
+		if (!stmt)
+			goto err;
+		if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
+			g_object_unref (stmt);
+			goto err;
+		}
+		g_object_unref (stmt);
+	}
+
+	if (order_key >= 0) {
+		GdaSqlBuilder *builder;
+		GdaStatement *stmt;
+
+		/* delete and insert favorite in orders table */
+		favid = find_favorite (bfav, session_id, fav->id, fav->contents, error);
+		if (favid < 0) {
+			g_warning ("Could not identify favorite by its ID, make sure it's correct");
+			goto err;
+		}
+		
+		gda_set_set_holder_value (params, NULL, "id", favid);
+
+		builder = gda_sql_builder_new (GDA_SQL_STATEMENT_DELETE);
+		gda_sql_builder_set_table (builder, FAVORDER_TABLE_NAME);
+		gda_sql_builder_set_where (builder,
+		      gda_sql_builder_cond (builder, 0, GDA_SQL_OPERATOR_TYPE_AND,
+			    gda_sql_builder_cond (builder, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+						  gda_sql_builder_literal (builder, 0, "fav_id"),
+						  gda_sql_builder_param (builder, 0, "id", G_TYPE_INT, FALSE),
+						  0),
+			    gda_sql_builder_cond (builder, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+						  gda_sql_builder_literal (builder, 0, "order_key"),
+						  gda_sql_builder_param (builder, 0, "orderkey", G_TYPE_INT, FALSE),
+						  0), 0));
+		stmt = gda_sql_builder_get_statement (builder, error);
+		g_object_unref (G_OBJECT (builder));
+		if (!stmt)
+			goto err;
+		if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
+			g_object_unref (stmt);
+			goto err;
+		}
+		g_object_unref (stmt);
+
+		builder = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
+		gda_sql_builder_set_table (builder, FAVORDER_TABLE_NAME);
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "fav_id"),
+					   gda_sql_builder_param (builder, 0, "id", G_TYPE_INT, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "rank"),
+					   gda_sql_builder_param (builder, 0, "rank", G_TYPE_INT, FALSE));
+		gda_sql_builder_add_field (builder,
+					   gda_sql_builder_literal (builder, 0, "order_key"),
+					   gda_sql_builder_param (builder, 0, "orderkey", G_TYPE_STRING, TRUE));
+		stmt = gda_sql_builder_get_statement (builder, error);
+		g_object_unref (G_OBJECT (builder));
+		if (!stmt)
+			goto err;
+		if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
+			g_object_unref (stmt);
+			goto err;
+		}
+		g_object_unref (stmt);
+
+		/* reorder */
+		if (!favorites_reorder (bfav, order_key, favid, pos, error))
+			goto err;
+	}
+
+	if (! gda_connection_commit_transaction (store_cnc, NULL, NULL)) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't commit transaction to access favorites"));
+		goto err;
+	}
+
+	if (params)
+		g_object_unref (params);
+	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
+	g_signal_emit (bfav, browser_favorites_signals [FAV_CHANGED],
+		       g_quark_from_string (favorite_type_to_string (fav->type)));
+	return TRUE;
+
+ err:
+	if (params)
+		g_object_unref (params);
+	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
+	gda_connection_rollback_transaction (store_cnc, NULL, NULL);
+	return FALSE;
+}
+
+/**
+ * browser_favorites_free_list
+ */
+void
+browser_favorites_free_list (GSList *fav_list)
+{
+	GSList *list;
+	if (!fav_list)
+		return;
+	for (list = fav_list; list; list = list->next) {
+		BrowserFavoritesAttributes *fav = (BrowserFavoritesAttributes*) list->data;
+		g_free (fav->contents);
+		g_free (fav->descr);
+		g_free (fav);
+	}
+	g_slist_free (fav_list);
+}
+
+/**
+ * browser_favorites_list
+ *
+ * Returns: a new list of #BrowserFavoritesAttributes pointers. The list has to
+ *          be freed using browser_favorites_free_list()
+ */
+GSList *
+browser_favorites_list (BrowserFavorites *bfav, guint session_id, BrowserFavoritesType type,
+			gint order_key, GError **error)
+{
+	GdaSqlBuilder *b;
+	GdaSet *params = NULL;
+	GdaStatement *stmt;
+	guint t1, t2;
+	GdaDataModel *model = NULL;
+	GSList *fav_list = NULL;
+
+	guint and_cond_ids [3];
+	int and_cond_size = 0;
+	guint or_cond_ids [BROWSER_FAVORITES_NB_TYPES];
+	int or_cond_size = 0;
+
+	g_return_val_if_fail (BROWSER_IS_FAVORITES (bfav), NULL);
+	g_return_val_if_fail ((type != 0) || (order_key >= 0), NULL);
+
+	if (! bfav->priv->store_cnc &&
+	    ! meta_store_addons_init (bfav, error))
+		return NULL;
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "fav.contents"), 0);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "fav.descr"), 0);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "fav.name"), 0);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "fav.type"), 0);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "fav.id"), 0);
+
+	t1 = gda_sql_builder_select_add_target (b, 0,
+						gda_sql_builder_literal (b, 0, FAVORITES_TABLE_NAME),
+						"fav");
+	if (order_key > 0) {
+		t2 = gda_sql_builder_select_add_target (b, 0,
+							gda_sql_builder_literal (b, 0, FAVORDER_TABLE_NAME),
+							"o");
+		gda_sql_builder_select_join_targets (b, 0, t1, t2, GDA_SQL_SELECT_JOIN_LEFT,
+						     gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+									   gda_sql_builder_literal (b, 0, "fav.id"),
+									   gda_sql_builder_literal (b, 0, "o.fav_id"),
+									   0));
+		gda_sql_builder_select_order_by (b,
+						 gda_sql_builder_literal (b, 0, "o.rank"), TRUE, NULL);
+
+		and_cond_ids [and_cond_size] = gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+							     gda_sql_builder_literal (b, 0, "o.order_key"),
+							     gda_sql_builder_param (b, 0, "okey", G_TYPE_INT, FALSE),
+							     0);
+		and_cond_size++;
+	}
+
+	and_cond_ids [and_cond_size] = gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+						     gda_sql_builder_literal (b, 0, "fav.session"),
+						     gda_sql_builder_param (b, 0, "session", G_TYPE_INT, FALSE), 0);
+	and_cond_size++;
+
+	gint i;
+	gint flag;
+	for (i = 0, flag = 1; i < BROWSER_FAVORITES_NB_TYPES; i++, flag <<= 1) {
+		if (type & flag) {
+			gchar *str;
+			str = g_strdup_printf ("'%s'", favorite_type_to_string (flag));
+			or_cond_ids [or_cond_size] = gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+							     gda_sql_builder_literal (b, 0, "fav.type"),
+							     gda_sql_builder_literal (b, 0, str),
+							     0);
+			g_free (str);
+			or_cond_size++;
+		}
+	}
+	if (or_cond_size >= 1) {
+		and_cond_ids [and_cond_size] = gda_sql_builder_cond_v (b, 0, GDA_SQL_OPERATOR_TYPE_OR,
+								       or_cond_ids, or_cond_size);
+		and_cond_size++;
+	}
+
+	gda_sql_builder_set_where (b,
+				   gda_sql_builder_cond_v (b, 0, GDA_SQL_OPERATOR_TYPE_AND, and_cond_ids, and_cond_size));
+
+	{
+		GdaSqlStatement *sqlst;
+		sqlst = gda_sql_builder_get_sql_statement (b, TRUE);
+		g_print ("=>%s\n", gda_sql_statement_serialize (sqlst));
+	}
+
+	stmt = gda_sql_builder_get_statement (b, error);
+	g_object_unref (G_OBJECT (b));
+	if (!stmt)
+		goto out;
+
+	params = gda_set_new_inline (2,
+				     "session", G_TYPE_INT, session_id,
+				     "okey", G_TYPE_INT, order_key);
+
+	model = gda_connection_statement_execute_select (bfav->priv->store_cnc, stmt, params, error);
+	g_object_unref (stmt);
+	if (!model)
+		goto out;
+
+	gint nrows;
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *contents, *descr = NULL, *name = NULL, *type = NULL, *id = NULL;
+
+		contents = gda_data_model_get_value_at (model, 0, i, error);
+		if (contents)
+			descr = gda_data_model_get_value_at (model, 1, i, error);
+		if (descr)
+			name = gda_data_model_get_value_at (model, 2, i, error);
+		if (name)
+			type = gda_data_model_get_value_at (model, 3, i, error);
+		if (type)
+			id = gda_data_model_get_value_at (model, 4, i, error);
+		if (id) {
+			BrowserFavoritesAttributes *fav;
+			fav = g_new0 (BrowserFavoritesAttributes, 1);
+			fav->id = g_value_get_int (id);
+			fav->type = favorite_string_to_type (g_value_get_string (type));
+			if (G_VALUE_TYPE (descr) == G_TYPE_STRING)
+				fav->descr = g_value_dup_string (descr);
+			if (G_VALUE_TYPE (name) == G_TYPE_STRING)
+				fav->name = g_value_dup_string (name);
+			fav->contents = g_value_dup_string (contents);
+			fav_list = g_slist_prepend (fav_list, fav);
+		}
+		else {
+			browser_favorites_free_list (fav_list);
+			fav_list = NULL;
+			goto out;
+		}
+	}
+
+ out:
+	if (params)
+		g_object_unref (G_OBJECT (params));
+	if (model)
+		g_object_unref (G_OBJECT (model));
+
+	return g_slist_reverse (fav_list);
+}
+
+
+/**
+ * browser_favorites_delete_favorite
+ */
+gboolean
+browser_favorites_delete (BrowserFavorites *bfav, guint session_id,
+			  BrowserFavoritesAttributes *fav, GError **error)
+{
+	GdaSqlBuilder *b;
+	GdaSet *params = NULL;
+	GdaStatement *stmt;
+	gboolean retval = FALSE;
+	gint favid;
+
+	g_return_val_if_fail (BROWSER_IS_FAVORITES (bfav), FALSE);
+	g_return_val_if_fail (fav, FALSE);
+	g_return_val_if_fail ((fav->id >= 0) || fav->contents, FALSE);
+	
+	if (! bfav->priv->store_cnc &&
+	    ! meta_store_addons_init (bfav, error))
+		return FALSE;
+
+	if (! gda_lockable_trylock (GDA_LOCKABLE (bfav->priv->store_cnc))) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize transaction to access favorites"));
+		return FALSE;
+	}
+	/* begin a transaction */
+	if (! gda_connection_begin_transaction (bfav->priv->store_cnc, NULL,
+						GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL)) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't initialize transaction to access favorites"));
+		gda_lockable_unlock (GDA_LOCKABLE (bfav->priv->store_cnc));
+                return FALSE;
+	}
+
+	favid = find_favorite (bfav, session_id, fav->id, fav->contents, error);
+	if (favid < 0)
+		goto out;
+
+	/* remove entry from favorites' list */
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_DELETE);
+	gda_sql_builder_set_table (b, FAVORITES_TABLE_NAME);
+	gda_sql_builder_set_where (b, gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+							    gda_sql_builder_literal (b, 0, "id"),
+							    gda_sql_builder_param (b, 0, "id", G_TYPE_INT, FALSE),
+							    0));
+
+	stmt = gda_sql_builder_get_statement (b, error);
+	g_object_unref (G_OBJECT (b));
+	if (!stmt)
+		goto out;
+
+	params = gda_set_new_inline (1,
+				     "id", G_TYPE_INT, favid);
+
+	if (gda_connection_statement_execute_non_select (bfav->priv->store_cnc, stmt, params, NULL, error) == -1) {
+		g_object_unref (stmt);
+		goto out;
+	}
+	g_object_unref (stmt);
+
+	/* remove entry from favorites' order */
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_DELETE);
+	gda_sql_builder_set_table (b, FAVORDER_TABLE_NAME);
+	gda_sql_builder_set_where (b, gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+							    gda_sql_builder_literal (b, 0, "fav_id"),
+							    gda_sql_builder_param (b, 0, "id", G_TYPE_INT, FALSE),
+							    0));
+
+	stmt = gda_sql_builder_get_statement (b, error);
+	g_object_unref (G_OBJECT (b));
+	if (!stmt)
+		goto out;
+	if (gda_connection_statement_execute_non_select (bfav->priv->store_cnc, stmt, params, NULL, error) == -1) {
+		g_object_unref (stmt);
+		goto out;
+	}
+	g_object_unref (stmt);
+
+	if (! gda_connection_commit_transaction (bfav->priv->store_cnc, NULL, NULL)) {
+		g_set_error (error, 0, 0, "%s",
+                             _("Can't commit transaction to access favorites"));
+		goto out;
+	}
+	retval = TRUE;
+
+ out:
+	if (!retval)
+		gda_connection_rollback_transaction (bfav->priv->store_cnc, NULL, NULL);
+
+	gda_lockable_unlock (GDA_LOCKABLE (bfav->priv->store_cnc));
+	if (retval)
+		g_signal_emit (bfav, browser_favorites_signals [FAV_CHANGED],
+			       g_quark_from_string (favorite_type_to_string (fav->type)));
+	if (params)
+		g_object_unref (G_OBJECT (params));
+
+	return retval;
+}
diff --git a/tools/browser/browser-favorites.h b/tools/browser/browser-favorites.h
new file mode 100644
index 0000000..bd8e6ea
--- /dev/null
+++ b/tools/browser/browser-favorites.h
@@ -0,0 +1,82 @@
+/* 
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * 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 __BROWSER_FAVORITES_H_
+#define __BROWSER_FAVORITES_H_
+
+#include <libgda/libgda.h>
+#include "decl.h"
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_FAVORITES          (browser_favorites_get_type())
+#define BROWSER_FAVORITES(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, browser_favorites_get_type(), BrowserFavorites)
+#define BROWSER_FAVORITES_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, browser_favorites_get_type (), BrowserFavoritesClass)
+#define BROWSER_IS_FAVORITES(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_favorites_get_type ())
+
+typedef struct _BrowserFavorites BrowserFavorites;
+typedef struct _BrowserFavoritesClass BrowserFavoritesClass;
+typedef struct _BrowserFavoritesPrivate BrowserFavoritesPrivate;
+
+typedef enum {
+        BROWSER_FAVORITES_TABLES   = 1 << 0,
+	BROWSER_FAVORITES_DIAGRAMS = 1 << 1
+} BrowserFavoritesType;
+#define BROWSER_FAVORITES_NB_TYPES 2
+
+typedef struct {
+	gint                  id;
+	BrowserFavoritesType  type;
+	gchar                *name;
+	gchar                *descr;
+	gchar                *contents;
+} BrowserFavoritesAttributes;
+
+/* struct for the object's data */
+struct _BrowserFavorites
+{
+	GObject                  object;
+	BrowserFavoritesPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserFavoritesClass
+{
+	GObjectClass              parent_class;
+
+	void                    (*favorites_changed) (BrowserFavorites *bfav);
+};
+
+GType               browser_favorites_get_type               (void) G_GNUC_CONST;
+
+BrowserFavorites   *browser_favorites_new                    (GdaMetaStore *store);
+
+gint                browser_favorites_add          (BrowserFavorites *bfav, guint session_id,
+						    BrowserFavoritesAttributes *fav,
+						    gint order_key, gint pos,
+						    GError **error);
+GSList             *browser_favorites_list         (BrowserFavorites *bcnc, guint session_id, 
+						    BrowserFavoritesType type, gint order_key, GError **error);
+gboolean            browser_favorites_delete       (BrowserFavorites *bcnc, guint session_id,
+						    BrowserFavoritesAttributes *fav, GError **error);
+void                browser_favorites_free_list    (GSList *fav_list);
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
index 805219e..7894854 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.c
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -44,6 +44,7 @@ static void browser_canvas_db_relations_get_property (GObject *object,
 /* virtual functions */
 static void       clean_canvas_items  (BrowserCanvas *canvas);
 static GtkWidget *build_context_menu  (BrowserCanvas *canvas);
+static GSList    *get_layout_items    (BrowserCanvas *canvas);
 
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass *parent_class = NULL;
@@ -60,7 +61,7 @@ struct _BrowserCanvasDbRelationsPrivate
 	GHashTable       *hash_fkeys; /* key = GdaMetaTableForeignKey, value = BrowserCanvasFkey */
 
 	GdaMetaStruct    *mstruct;
-	GooCanvasItem    *level_separator; /* all tables will be above this item and FK lines below */
+	GooCanvasItem    *level_separator; /* all tables items will be above this item and FK lines below */
 };
 
 GType
@@ -104,6 +105,7 @@ browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass * class)
 	/* BrowserCanvas virtual functions */
 	BROWSER_CANVAS_CLASS (class)->clean_canvas_items = clean_canvas_items;
 	BROWSER_CANVAS_CLASS (class)->build_context_menu = build_context_menu;
+	BROWSER_CANVAS_CLASS (class)->get_layout_items = get_layout_items;
 	object_class->dispose = browser_canvas_db_relations_dispose;
 
 	/* properties */
@@ -323,6 +325,68 @@ popup_func_add_depend_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce)
 	}
 }
 
+static GSList *
+complement_layout_items (BrowserCanvasDbRelations *dbrel, BrowserCanvasItem *current, GSList *elist)
+{
+	GSList *items = elist;
+	GdaMetaTable *mtable;
+	mtable = g_hash_table_lookup (dbrel->priv->hash_tables, current);
+	if (!mtable)
+		return items;
+
+	GSList *list;
+	for (list = mtable->fk_list; list; list = list->next) {
+		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+		BrowserCanvasItem *item;
+
+		item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+		if (item && !g_slist_find (items, item)) {
+			items = g_slist_prepend (items, item);
+			items = complement_layout_items (dbrel, item, items);
+		}
+	}
+
+	for (list = mtable->reverse_fk_list; list; list = list->next) {
+		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+		BrowserCanvasItem *item;
+
+		item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+		if (item && !g_slist_find (items, item)) {
+			items = g_slist_prepend (items, item);
+			items = complement_layout_items (dbrel, item, items);
+		}
+	}
+
+	return items;
+}
+
+static GSList *
+get_layout_items (BrowserCanvas *canvas)
+{
+	GSList *items = NULL;
+	BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+
+	if (!canvas->priv->current_selected_item)
+		return g_slist_copy (canvas->priv->items);
+	
+	GdaMetaTable *mtable;
+	mtable = g_hash_table_lookup (dbrel->priv->hash_tables, canvas->priv->current_selected_item);
+	if (!mtable)
+		return g_slist_copy (canvas->priv->items);
+
+	items = g_slist_prepend (NULL, canvas->priv->current_selected_item);
+	items = complement_layout_items (dbrel, canvas->priv->current_selected_item, items);
+	
+	/* add non related items */
+	GSList *list;
+	for (list = canvas->priv->items; list; list = list->next) {
+		if (!g_slist_find (items, list->data))
+			items = g_slist_prepend (items, list->data);
+	}
+
+	return g_slist_reverse (items);
+}
+
 static void popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *canvas);
 static GtkWidget *
 build_context_menu (BrowserCanvas *canvas)
@@ -448,7 +512,6 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 					const GValue *table_name)
 {
 	g_return_val_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas), NULL);
-	g_return_val_if_fail (canvas->priv, NULL);
 
 	GdaMetaTable *mtable;
 	GooCanvas *goocanvas;
@@ -523,4 +586,17 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 	else
 		return NULL;
 }
- 
+
+/**
+ * browser_canvas_db_relations_select_table
+ */
+void
+browser_canvas_db_relations_select_table (BrowserCanvasDbRelations *canvas,
+					  BrowserCanvasTable *table)
+{
+	g_return_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas));
+	g_return_if_fail (!table || IS_BROWSER_CANVAS_ITEM (table));
+
+	browser_canvas_item_toggle_select (BROWSER_CANVAS (canvas), (BrowserCanvasItem*) table);
+}
+
diff --git a/tools/browser/canvas/browser-canvas-db-relations.h b/tools/browser/canvas/browser-canvas-db-relations.h
index bcbefc8..b9270f7 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.h
+++ b/tools/browser/canvas/browser-canvas-db-relations.h
@@ -60,6 +60,8 @@ BrowserCanvasTable *browser_canvas_db_relations_add_table  (BrowserCanvasDbRelat
 							    const GValue *table_catalog,
 							    const GValue *table_schema,
 							    const GValue *table_name);
+void                browser_canvas_db_relations_select_table (BrowserCanvasDbRelations *canvas,
+							      BrowserCanvasTable *table);
 
 G_END_DECLS
 
diff --git a/tools/browser/canvas/browser-canvas-table.c b/tools/browser/canvas/browser-canvas-table.c
index 9d9139d..342f225 100644
--- a/tools/browser/canvas/browser-canvas-table.c
+++ b/tools/browser/canvas/browser-canvas-table.c
@@ -21,6 +21,7 @@
 #include <gtk/gtk.h>
 #include <libgda/libgda.h>
 #include "browser-canvas.h"
+#include "browser-canvas-priv.h"
 #include "browser-canvas-table.h"
 #include "browser-canvas-column.h"
 #include <glib/gi18n-lib.h>
@@ -63,10 +64,6 @@ struct _BrowserCanvasTablePrivate
 	gdouble            *column_ypos; /* array for each column's Y position in this canvas group */
 	GtkWidget          *(*popup_menu_func) (BrowserCanvasTable *ce);
 
-	/* presentation parameters */
-        gdouble             x_text_space;
-        gdouble             y_text_space;
-
 	GooCanvasItem      *selection_mark;
 };
 
@@ -143,9 +140,6 @@ browser_canvas_table_init (BrowserCanvasTable *table)
 	table->priv->column_ypos = NULL;
 	table->priv->popup_menu_func = NULL;
 
-	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",
@@ -292,6 +286,7 @@ create_items (BrowserCanvasTable *ce)
 #define RADIUS_X 5.
 #define RADIUS_Y 5.
 #define MIN_HEIGHT 70.
+#define SELECTION_SIZE 4.
         GooCanvasBounds border_bounds;
         GooCanvasBounds bounds;
 	const gchar *cstr;
@@ -363,6 +358,17 @@ create_items (BrowserCanvasTable *ce)
 				     NULL);		
 	ce->priv->other_items = g_slist_prepend (ce->priv->other_items, frame);
 
+	ce->priv->selection_mark = goo_canvas_rect_new (GOO_CANVAS_ITEM (ce), border_bounds.x1 - SELECTION_SIZE,
+							border_bounds.y1 - SELECTION_SIZE, 
+							border_bounds.x2 + 2 * SELECTION_SIZE,
+							border_bounds.y2 + 2 * SELECTION_SIZE,
+							"radius-x", RADIUS_X,
+							"radius-y", RADIUS_Y,
+							"fill-color", "#11d155",//"#ffea08",
+							"stroke-color", "#11d155",//"#ffea08",
+							NULL);
+	g_object_set (G_OBJECT (ce->priv->selection_mark), "visibility", GOO_CANVAS_ITEM_HIDDEN, NULL);
+
 	/* title's background */
 	gchar *cpath;
 	cpath = g_strdup_printf ("M %d %d H %d V %d H %d Z",
@@ -386,6 +392,7 @@ create_items (BrowserCanvasTable *ce)
 	ce->priv->other_items = g_slist_prepend (ce->priv->other_items, item);
 
 	goo_canvas_item_lower (frame, NULL);
+	goo_canvas_item_lower (ce->priv->selection_mark, NULL);
 
 	/* setting the columns' background width to be the same for all */
 	for (list = ce->priv->column_items; list; list = list->next) 
@@ -532,41 +539,25 @@ browser_canvas_table_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *dr
 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);
-	}
+	g_object_set (G_OBJECT (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark),
+		      "visibility", selected ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_HIDDEN, NULL);
+}
+
+/**
+ * browser_canvas_table_get_anchor_bounds
+ *
+ * Get the bounds to be used to compute anchors, ie. without the selection mark or any other
+ * artefact not part of the table's rectangle.
+ */
+void
+browser_canvas_table_get_anchor_bounds (BrowserCanvasTable *ce, GooCanvasBounds *bounds)
+{
+	g_return_if_fail (IS_BROWSER_CANVAS_TABLE (ce));
+	g_return_if_fail (bounds);
+
+	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ce), bounds);
+	bounds->x1 += SELECTION_SIZE;
+	bounds->y1 += SELECTION_SIZE;
+	bounds->x2 -= SELECTION_SIZE;
+	bounds->y2 -= SELECTION_SIZE;
 }
diff --git a/tools/browser/canvas/browser-canvas-table.h b/tools/browser/canvas/browser-canvas-table.h
index 13aad1a..67d663c 100644
--- a/tools/browser/canvas/browser-canvas-table.h
+++ b/tools/browser/canvas/browser-canvas-table.h
@@ -61,6 +61,7 @@ GooCanvasItem       *browser_canvas_table_new             (GooCanvasItem *parent
 							   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);
+void                 browser_canvas_table_get_anchor_bounds (BrowserCanvasTable *ce, GooCanvasBounds *bounds);
 
 G_END_DECLS
 
diff --git a/tools/browser/canvas/browser-canvas-utility.c b/tools/browser/canvas/browser-canvas-utility.c
index 5c541ea..4435b3c 100644
--- a/tools/browser/canvas/browser-canvas-utility.c
+++ b/tools/browser/canvas/browser-canvas-utility.c
@@ -68,12 +68,12 @@ browser_canvas_util_compute_anchor_shapes (GooCanvasItem *parent, GSList *shapes
 
 	g_return_val_if_fail (nb_anchors > 0, NULL);
 
-	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (fk_ent), &bounds);
+	browser_canvas_table_get_anchor_bounds (fk_ent, &bounds);
 	fx1 = bounds.x1;
 	fy1 = bounds.y1;
 	fx2 = bounds.x2;
 	fy2 = bounds.y2;
-	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ref_pk_ent), &bounds);
+	browser_canvas_table_get_anchor_bounds (ref_pk_ent, &bounds);
 	rx1 = bounds.x1;
 	ry1 = bounds.y1;
 	rx2 = bounds.x2;
@@ -553,11 +553,11 @@ browser_canvas_util_compute_connect_shapes (GooCanvasItem *parent, GSList *shape
 
 	/* line made of 4 points */
 	points = goo_canvas_points_new (4);
-	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ent1), &bounds);
+	browser_canvas_table_get_anchor_bounds (ent1, &bounds);
 	xl1 = bounds.x1;
 	yt1 = bounds.y1;
 	xr1 = bounds.x2;
-	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ent2), &bounds);
+	browser_canvas_table_get_anchor_bounds (ent2, &bounds);
 	xl2 = bounds.x1;
 	yt2 = bounds.y1;
 	xr2 = bounds.x2;
diff --git a/tools/browser/canvas/browser-canvas.c b/tools/browser/canvas/browser-canvas.c
index fdfa06a..f77f84f 100644
--- a/tools/browser/canvas/browser-canvas.c
+++ b/tools/browser/canvas/browser-canvas.c
@@ -234,13 +234,22 @@ static gboolean
 canvas_scroll_event_cb (GooCanvas *gcanvas, GdkEvent *event, BrowserCanvas *canvas)
 {
 	gboolean done = TRUE;
+	GdkEventScroll *ev = (GdkEventScroll *) event;
 
 	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);
+		if (ev->state & GDK_SHIFT_MASK) {
+			if (ev->direction == GDK_SCROLL_UP)
+				browser_canvas_scale_layout (canvas, 1.05);
+			else
+				browser_canvas_scale_layout (canvas, .95);
+		}
+		else {
+			if (ev->direction == GDK_SCROLL_UP)
+				browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) + .03);
+			else if (ev->direction == GDK_SCROLL_DOWN)
+				browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) - .03);
+		}
 		done = TRUE;
 		break;
 	default:
@@ -294,6 +303,8 @@ motion_notify_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanv
 	return done;
 }
 
+static void popup_layout_default_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_layout_radial_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
 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);
@@ -348,6 +359,22 @@ canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
 				mitem = gtk_separator_menu_item_new ();
 				gtk_widget_show (mitem);
 				gtk_menu_append (menu, mitem);
+
+#ifdef HAVE_GRAPHVIZ
+				mitem = gtk_menu_item_new_with_label (_("Linear layout"));
+				gtk_widget_show (mitem);
+				gtk_menu_append (menu, mitem);
+				g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_layout_default_cb), canvas);
+
+				mitem = gtk_menu_item_new_with_label (_("Radial layout"));
+				gtk_widget_show (mitem);
+				gtk_menu_append (menu, mitem);
+				g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_layout_radial_cb), canvas);
+
+				mitem = gtk_separator_menu_item_new ();
+				gtk_widget_show (mitem);
+				gtk_menu_append (menu, mitem);
+#endif
 				
 				mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
 				gtk_widget_show (mitem);
@@ -402,6 +429,18 @@ canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
 }
 
 static void
+popup_layout_default_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+	browser_canvas_perform_auto_layout (canvas, TRUE, BROWSER_CANVAS_LAYOUT_DEFAULT);
+}
+
+static void
+popup_layout_radial_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+	browser_canvas_perform_auto_layout (canvas, TRUE, BROWSER_CANVAS_LAYOUT_RADIAL);
+}
+
+static void
 popup_zoom_in_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
 {
 	browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) + .05);
@@ -835,7 +874,8 @@ browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, Bro
 	g_message ("GraphViz library support not compiled, cannot do graph layout...\n");
 	return FALSE;
 #else
-	GSList *list;
+	BrowserCanvasClass *class = BROWSER_CANVAS_CLASS (G_OBJECT_GET_CLASS (canvas));
+	GSList *list, *layout_items;
 	Agraph_t *graph;
 	GHashTable *nodes_hash; /* key = BrowserCanvasItem, value = Agnode_t *node */
 	GSList *nodes_list = NULL; /* list of NodeLayout structures */
@@ -843,7 +883,7 @@ browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, Bro
 	if (!gvc)
 		gvc = gvContext ();
 
-	graph = agopen ("BrowserCanvasLayout", AGDIGRAPHSTRICT);
+	graph = agopen ("BrowserCanvasLayout", AGRAPH);
         agnodeattr (graph, "shape", "box");
         agnodeattr (graph, "height", ".1");
         agnodeattr (graph, "width", ".1");
@@ -851,9 +891,15 @@ browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, Bro
         agnodeattr (graph, "pack", "true");
 	agnodeattr (graph, "packmode", "node");
 
+
+	if (class->get_layout_items)
+		layout_items = class->get_layout_items (canvas);
+	else
+		layout_items = canvas->priv->items;
+
 	/* Graph nodes creation */
 	nodes_hash = g_hash_table_new (NULL, NULL);
-	for (list = canvas->priv->items; list; list = list->next) {
+	for (list = layout_items; list; list = list->next) {
 		BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
 		Agnode_t *node;
 		gchar *tmp;
@@ -900,7 +946,7 @@ browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, Bro
 		  (bounds.x2 - bounds.x1) / GV_SCALE);*/
 	}
 	/* Graph edges creation */
-	for (list = canvas->priv->items; list; list = list->next) {
+	for (list = layout_items; list; list = list->next) {
 		BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
 		BrowserCanvasItem *from, *to;
 		gboolean moving;
@@ -919,6 +965,9 @@ browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, Bro
 		}
 	}
 
+	if (layout_items != canvas->priv->items)
+		g_slist_free (layout_items);
+
 	switch (algorithm) {
 	default:
 	case BROWSER_CANVAS_LAYOUT_DEFAULT:
@@ -1032,6 +1081,40 @@ canvas_animate_to (GraphLayout *gl)
 #endif
 
 /**
+ * browser_canvas_scale_layout
+ */
+void
+browser_canvas_scale_layout (BrowserCanvas *canvas, gdouble scale)
+{
+	GSList *list;
+	GooCanvasBounds ref_bounds;
+	gdouble refx, refy;
+
+	g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+	if (!canvas->priv->items)
+		return;
+
+	goo_canvas_get_bounds (canvas->priv->goocanvas, &ref_bounds.x1, &ref_bounds.y1,
+			       &ref_bounds.x2, &ref_bounds.y2);
+	refx = (ref_bounds.x2 - ref_bounds.x1) / 2.;
+	refy = (ref_bounds.y2 - ref_bounds.y1) / 2.;
+	for (list = canvas->priv->items; list; list = list->next) {
+		gboolean can_move;
+		g_object_get ((GObject*) list->data, "allow-move", &can_move, NULL);
+		if (can_move) {
+			BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+			GooCanvasBounds bounds;
+			gdouble tx, ty;
+			
+			goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds);
+			tx = (scale - 1.) * (bounds.x1 - refx);
+			ty = (scale - 1.) * (bounds.y1 - refy);
+			browser_canvas_item_translate (item, tx, ty);
+		}
+	}
+}
+
+/**
  * browser_canvas_serialize_items
  */
 gchar *
@@ -1044,6 +1127,8 @@ browser_canvas_serialize_items (BrowserCanvas *canvas)
 		BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
 		TO_IMPLEMENT;
 	}
+
+	return NULL;
 }
 
 /**
diff --git a/tools/browser/canvas/browser-canvas.h b/tools/browser/canvas/browser-canvas.h
index e3e109b..151e006 100644
--- a/tools/browser/canvas/browser-canvas.h
+++ b/tools/browser/canvas/browser-canvas.h
@@ -60,6 +60,7 @@ struct _BrowserCanvasClass
 
 	/* virtual functions */
 	void           (*clean_canvas_items)  (BrowserCanvas *canvas); /* clean any extra structure, not the individual items */
+	GSList        *(*get_layout_items) (BrowserCanvas *canvas);
 
 	GtkWidget     *(*build_context_menu)  (BrowserCanvas *canvas);
 };
@@ -75,6 +76,8 @@ gboolean           browser_canvas_auto_layout_enabled     (BrowserCanvas *canvas
 void               browser_canvas_perform_auto_layout     (BrowserCanvas *canvas, gboolean animate,
 							   BrowserCanvasLayoutAlgorithm algorithm);
 void               browser_canvas_center                  (BrowserCanvas *canvas);
+void               browser_canvas_scale_layout            (BrowserCanvas *canvas, gdouble scale);
+
 gchar             *browser_canvas_serialize_items         (BrowserCanvas *canvas);
 
 void               browser_canvas_item_toggle_select      (BrowserCanvas *canvas, BrowserCanvasItem *item);
diff --git a/tools/browser/decl.h b/tools/browser/decl.h
index e8841f6..401fa75 100644
--- a/tools/browser/decl.h
+++ b/tools/browser/decl.h
@@ -41,9 +41,7 @@ typedef struct {
 } BrowserPerspectiveFactory;
 #define BROWSER_PERSPECTIVE_FACTORY(x) ((BrowserPerspectiveFactory*)(x))
 
-typedef enum {
-        BROWSER_FAVORITES_TABLES
-} BrowserFavoritesType;
+#define ORDER_KEY_SCHEMA 1
 
 G_END_DECLS
 
diff --git a/tools/browser/favorites-test.c b/tools/browser/favorites-test.c
new file mode 100644
index 0000000..b3fbcf2
--- /dev/null
+++ b/tools/browser/favorites-test.c
@@ -0,0 +1,260 @@
+/* 
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <libgda/libgda.h>
+#include "browser-favorites.h"
+#include <string.h>
+
+static void
+dump_favorite (BrowserFavoritesAttributes *f)
+{
+	g_print ("Favorite: ID=>%d\n", f->id);
+	g_print ("          Type=>%d\n", f->type);
+	g_print ("          Name=>%s\n", f->name);
+	g_print ("          Descr=>%s\n", f->descr);
+	g_print ("          Contents=>%s\n", f->contents);
+}
+
+static void
+dump_favorites_in_db (GdaConnection *cnc)
+{
+	GdaDataModel *model;
+	GError *error = NULL;
+
+	g_print ("\n*********** contents of gda_sql_favorites ***********\n");
+	model = gda_meta_store_extract (gda_connection_get_meta_store (cnc), "SELECT * FROM gda_sql_favorites", &error,
+					NULL);
+	if (!model) {
+                g_print ("Could not extract list of favorites: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+	}
+	gda_data_model_dump (model, NULL);
+	g_object_unref (model);
+
+	g_print ("\n************ contents of gda_sql_favorder ***********\n");
+	model = gda_meta_store_extract (gda_connection_get_meta_store (cnc), "SELECT * FROM gda_sql_favorder", &error,
+					NULL);
+	if (!model) {
+                g_print ("Could not extract list of favorites: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+	}
+	gda_data_model_dump (model, NULL);
+	g_object_unref (model);
+}
+
+static void test1 (GdaConnection *cnc, BrowserFavorites *bfav);
+static void test2 (GdaConnection *cnc, BrowserFavorites *bfav);
+static void test3 (GdaConnection *cnc, BrowserFavorites *bfav);
+int
+main (int argc, char *argv[])
+{
+	BrowserFavorites *bfav;
+	GdaConnection *cnc;
+	GError *error = NULL;
+
+	gda_init ();
+
+	cnc = gda_connection_open_from_dsn ("SalesTest", NULL,
+                                            GDA_CONNECTION_OPTIONS_NONE,
+                                            &error);
+        if (!cnc) {
+                g_print ("Could not open connection: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+        }
+	bfav = browser_favorites_new (gda_connection_get_meta_store (cnc));
+	test1 (cnc, bfav);
+	test2 (cnc, bfav);
+	test3 (cnc, bfav);
+	g_object_unref (G_OBJECT (bfav));
+	g_object_unref (G_OBJECT (cnc));
+	
+	g_print ("OK!\n");
+	return 0;
+}
+
+static void
+test1 (GdaConnection *cnc, BrowserFavorites *bfav)
+{
+	gint favid, i;
+	GError *error = NULL;
+	BrowserFavoritesAttributes fav_array[]= {
+		{-1, BROWSER_FAVORITES_TABLES, "table1", "fav1-descr", "fav1 contents"},
+		{-1, BROWSER_FAVORITES_DIAGRAMS, "diagram1", "fav2-descr", "fav2 contents"}
+	};
+	for (i = 0; i < G_N_ELEMENTS (fav_array); i++) {
+		BrowserFavoritesAttributes *f = (BrowserFavoritesAttributes*) &(fav_array[i]);
+		favid = browser_favorites_add (bfav, 0, f, -1, 0, &error);
+		if (!favid < 0) {
+			g_print ("Could not create favorite: %s\n",
+				 error && error->message ? error->message : "No detail");
+			exit (1);
+		}
+	}
+	//dump_favorites_in_db (cnc);
+
+	GSList *favlist, *list;
+	favlist = browser_favorites_list (bfav, 0, BROWSER_FAVORITES_TABLES, -1, &error);
+	if (!favlist && error) {
+		g_print ("Could not list favorites: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+	}
+	for (i = 0, list = favlist; (i < G_N_ELEMENTS (fav_array)) && list; i++, list = list -> next) {
+		BrowserFavoritesAttributes *f1 = (BrowserFavoritesAttributes*) &(fav_array[i]);
+		BrowserFavoritesAttributes *f2 = (BrowserFavoritesAttributes*) list->data;
+		if (f2->id == 0) {
+			g_print ("ID should not be 0:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+		if ((f2->type != f1->type) ||
+		    strcmp (f2->contents, f1->contents) ||
+		    strcmp (f2->descr, f1->descr) ||
+		    strcmp (f2->name, f1->name)) {
+			g_print ("Favorites differ:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+	}
+	if (list || (i != G_N_ELEMENTS (fav_array))) {
+		g_print ("Wrong number of favorites reported\n");
+		exit (1);
+	}
+	browser_favorites_free_list (favlist);	
+}
+
+static void
+test2 (GdaConnection *cnc, BrowserFavorites *bfav)
+{
+	gint favid, i;
+	GError *error = NULL;
+	BrowserFavoritesAttributes fav_array[]= {
+		{-1, BROWSER_FAVORITES_TABLES, "table2", "fav1-descr", "fav1 contents SEPARATE"},
+		{-1, BROWSER_FAVORITES_DIAGRAMS, "diagram2", "fav2-descr", "fav2 contents SEPARATE"},
+		{-1, BROWSER_FAVORITES_TABLES, "table2", "Another description for table2", "fav1 contents SEPARATE"},
+	};
+	BrowserFavoritesAttributes res_fav_array[]= {
+		{-1, BROWSER_FAVORITES_TABLES, "table2", "Another description for table2", "fav1 contents SEPARATE"},
+		{-1, BROWSER_FAVORITES_DIAGRAMS, "diagram2", "fav2-descr", "fav2 contents SEPARATE"},
+	};
+	for (i = 0; i < G_N_ELEMENTS (fav_array); i++) {
+		BrowserFavoritesAttributes *f = (BrowserFavoritesAttributes*) &(fav_array[i]);
+		favid = browser_favorites_add (bfav, 0, f, 1, 0, &error);
+		if (!favid < 0) {
+			g_print ("Could not create favorite: %s\n",
+				 error && error->message ? error->message : "No detail");
+			exit (1);
+		}
+	}
+	dump_favorites_in_db (cnc);
+
+	GSList *favlist, *list;
+	favlist = browser_favorites_list (bfav, 0, BROWSER_FAVORITES_TABLES, 1, &error);
+	if (!favlist && error) {
+		g_print ("Could not list favorites: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+	}
+	for (i = 0, list = favlist; (i < G_N_ELEMENTS (res_fav_array)) && list; i++, list = list -> next) {
+		BrowserFavoritesAttributes *f1 = (BrowserFavoritesAttributes*) &(res_fav_array[i]);
+		BrowserFavoritesAttributes *f2 = (BrowserFavoritesAttributes*) list->data;
+		if (f2->id == 0) {
+			g_print ("ID should not be 0:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+		if ((f2->type != f1->type) ||
+		    strcmp (f2->contents, f1->contents) ||
+		    strcmp (f2->descr, f1->descr) ||
+		    strcmp (f2->name, f1->name)) {
+			g_print ("Favorites differ:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+	}
+	if (list || (i != G_N_ELEMENTS (res_fav_array))) {
+		g_print ("Wrong number of favorites reported\n");
+		exit (1);
+	}
+	browser_favorites_free_list (favlist);	
+}
+
+static void
+test3 (GdaConnection *cnc, BrowserFavorites *bfav)
+{
+	gint favid, i;
+	GError *error = NULL;
+	BrowserFavoritesAttributes fav_array[]= {
+		{-1, BROWSER_FAVORITES_DIAGRAMS, "diagram2", "fav2-descr", "fav2 contents SEPARATE"},
+	};
+	BrowserFavoritesAttributes res_fav_array[]= {
+		{-1, BROWSER_FAVORITES_TABLES, "table2", "Another description for table2", "fav1 contents SEPARATE"},
+	};
+	for (i = 0; i < G_N_ELEMENTS (fav_array); i++) {
+		BrowserFavoritesAttributes *f = (BrowserFavoritesAttributes*) &(fav_array[i]);
+		favid = browser_favorites_delete (bfav, 0, f, &error);
+		if (!favid < 0) {
+			g_print ("Could not delete favorite: %s\n",
+				 error && error->message ? error->message : "No detail");
+			exit (1);
+		}
+	}
+	dump_favorites_in_db (cnc);
+
+	GSList *favlist, *list;
+	favlist = browser_favorites_list (bfav, 0, BROWSER_FAVORITES_TABLES, 1, &error);
+	if (!favlist && error) {
+		g_print ("Could not list favorites: %s\n",
+                         error && error->message ? error->message : "No detail");
+                exit (1);
+	}
+	for (i = 0, list = favlist; (i < G_N_ELEMENTS (res_fav_array)) && list; i++, list = list -> next) {
+		BrowserFavoritesAttributes *f1 = (BrowserFavoritesAttributes*) &(res_fav_array[i]);
+		BrowserFavoritesAttributes *f2 = (BrowserFavoritesAttributes*) list->data;
+		if (f2->id == 0) {
+			g_print ("ID should not be 0:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+		if ((f2->type != f1->type) ||
+		    strcmp (f2->contents, f1->contents) ||
+		    strcmp (f2->descr, f1->descr) ||
+		    strcmp (f2->name, f1->name)) {
+			g_print ("Favorites differ:\n");
+			dump_favorite (f1);
+			dump_favorite (f2);
+			exit (1);
+		}
+	}
+	if (list || (i != G_N_ELEMENTS (res_fav_array))) {
+		g_print ("Wrong number of favorites reported\n");
+		exit (1);
+	}
+	browser_favorites_free_list (favlist);	
+}
diff --git a/tools/browser/gda-browser-diagram.png b/tools/browser/gda-browser-diagram.png
new file mode 100644
index 0000000..c8d9e65
Binary files /dev/null and b/tools/browser/gda-browser-diagram.png differ
diff --git a/tools/browser/mgr-favorites.c b/tools/browser/mgr-favorites.c
index 60a4849..8d89f33 100644
--- a/tools/browser/mgr-favorites.c
+++ b/tools/browser/mgr-favorites.c
@@ -205,14 +205,18 @@ hash_for_existing_nodes (const GSList *nodes)
 	GHashTable *hash;
 	const GSList *list;
 
-	hash = g_hash_table_new (g_str_hash, g_str_equal);
+	hash = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
 	for (list = nodes; list; list = list->next) {
 		const GValue *cvalue;
-		cvalue = gda_tree_node_get_node_attribute ((GdaTreeNode*) list->data, MGR_FAVORITES_CONTENTS_ATT_NAME);
-		if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING)) {
-			const gchar *str = g_value_get_string (cvalue);
-			if (str)
-				g_hash_table_insert (hash, (gpointer) str, list->data);
+		cvalue = gda_tree_node_get_node_attribute ((GdaTreeNode*) list->data, MGR_FAVORITES_ID_ATT_NAME);
+		if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_INT)) {
+			gint id;
+			id = g_value_get_int (cvalue);
+			if (id >= 0) {
+				gint *key = g_new0 (gint, 1);
+				*key = id;
+				g_hash_table_insert (hash, (gpointer) key, list->data);
+			}
 		}
 	}
 	return hash;
@@ -234,53 +238,103 @@ mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const
 	if (children_nodes)
 		ehash = hash_for_existing_nodes (children_nodes);
 
-	fav_list = browser_connection_list_favorites (mgr->priv->bcnc, 0, BROWSER_FAVORITES_TABLES, &lerror);
+	fav_list = browser_favorites_list (browser_connection_get_favorites (mgr->priv->bcnc),
+					   0, BROWSER_FAVORITES_TABLES | BROWSER_FAVORITES_DIAGRAMS,
+					   ORDER_KEY_SCHEMA, &lerror);
 	if (fav_list) {
 		GSList *list;
 		for (list = fav_list; list; list = list->next) {
-			BrowserConnectionFavorite *fav = (BrowserConnectionFavorite *) list->data;
+			BrowserFavoritesAttributes *fav = (BrowserFavoritesAttributes *) list->data;
 			GdaTreeNode* snode = NULL;
+			GValue *av;
 
 			if (ehash)
-				snode = g_hash_table_lookup (ehash, fav->contents);
-			if (snode) {
+				snode = g_hash_table_lookup (ehash, &(fav->id));
+
+			
+			if (snode)
 				/* use the same node */
 				g_object_ref (G_OBJECT (snode));
-			}
-			else {
-				GValue *av;
-				GdaQuarkList *ql;
-				const gchar *fname = NULL;
-				
-				ql = gda_quark_list_new_from_string (fav->contents);
-				if (!ql || !(fname = gda_quark_list_find (ql, "OBJ_SHORT_NAME"))) {
-					g_warning ("Invalid TABLE favorite format: %s", fav->contents);
+
+			if (fav->type == BROWSER_FAVORITES_TABLES) {
+				if (!snode) {
+					GdaQuarkList *ql;
+					const gchar *fname = NULL;
+					
+					ql = gda_quark_list_new_from_string (fav->contents);
+					if (!ql || !(fname = gda_quark_list_find (ql, "OBJ_SHORT_NAME"))) {
+						g_warning ("Invalid TABLE favorite format: %s", fav->contents);
+					}
+					else {
+						snode = gda_tree_manager_create_node (manager, node, NULL);
+						g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), fname);
+						gda_tree_node_set_node_attribute (snode, "markup", av, NULL);
+						gda_value_free (av);
+						
+						g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+								    fav->contents);
+						gda_tree_node_set_node_attribute (snode,
+										  MGR_FAVORITES_CONTENTS_ATT_NAME,
+										  av, NULL);
+						gda_value_free (av);
+						
+						
+						g_value_set_int ((av = gda_value_new (G_TYPE_INT)), fav->id);
+						gda_tree_node_set_node_attribute (snode,
+										  MGR_FAVORITES_ID_ATT_NAME,
+										  av, NULL);
+						gda_value_free (av);
+						
+						/* icon */
+						GdkPixbuf *pixbuf;
+						pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
+						av = gda_value_new (G_TYPE_OBJECT);
+						g_value_set_object (av, pixbuf);
+						gda_tree_node_set_node_attribute (snode, "icon", av, NULL);
+						gda_value_free (av);
+					}
+					if (ql)
+						gda_quark_list_free (ql);
 				}
-				else {
+			}
+			else if (fav->type == BROWSER_FAVORITES_DIAGRAMS) {
+				if (!snode) {
 					snode = gda_tree_manager_create_node (manager, node, NULL);
-					g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), fname);
-					gda_tree_node_set_node_attribute (snode, "markup", av, NULL);
-					gda_value_free (av);
-					
-					g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), fav->contents);
-					gda_tree_node_set_node_attribute (snode, MGR_FAVORITES_CONTENTS_ATT_NAME, av, NULL);
+									
+					g_value_set_int ((av = gda_value_new (G_TYPE_INT)), fav->id);
+					gda_tree_node_set_node_attribute (snode,
+									  MGR_FAVORITES_ID_ATT_NAME,
+									  av, NULL);
 					gda_value_free (av);
 					
 					/* icon */
 					GdkPixbuf *pixbuf;
-					pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
+					pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_DIAGRAM);
 					av = gda_value_new (G_TYPE_OBJECT);
 					g_value_set_object (av, pixbuf);
 					gda_tree_node_set_node_attribute (snode, "icon", av, NULL);
 					gda_value_free (av);
 				}
-				if (ql)
-					gda_quark_list_free (ql);
+
+				g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+						    fav->contents);
+				gda_tree_node_set_node_attribute (snode,
+								  MGR_FAVORITES_CONTENTS_ATT_NAME,
+								  av, NULL);
+				gda_value_free (av);
+
+				g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), fav->name);
+				gda_tree_node_set_node_attribute (snode, "markup", av, NULL);
+				gda_value_free (av);
+			}
+
+			else {
+				
 			}
 			if (snode)
 				nodes_list = g_slist_prepend (nodes_list, snode);
 		}
-		browser_connection_free_favorites_list (fav_list);
+		browser_favorites_free_list (fav_list);
 	}
 	else if (lerror) {
 		if (out_error)
diff --git a/tools/browser/mgr-favorites.h b/tools/browser/mgr-favorites.h
index b002af5..ecb85b7 100644
--- a/tools/browser/mgr-favorites.h
+++ b/tools/browser/mgr-favorites.h
@@ -52,6 +52,8 @@ GdaTreeManager* mgr_favorites_new                      (BrowserConnection *bcnc,
 
 /* name of the attribute which stores the favorite's contents */
 #define MGR_FAVORITES_CONTENTS_ATT_NAME "fav_contents"
+/* name of the attribute which stores the favorite's id */
+#define MGR_FAVORITES_ID_ATT_NAME "fav_id"
 
 G_END_DECLS
 
diff --git a/tools/browser/schema-browser/Makefile.am b/tools/browser/schema-browser/Makefile.am
index aea39bb..c55c42c 100644
--- a/tools/browser/schema-browser/Makefile.am
+++ b/tools/browser/schema-browser/Makefile.am
@@ -27,5 +27,7 @@ libperspective_la_SOURCES = \
 if HAVE_GOOCANVAS
 libperspective_la_SOURCES += \
 	table-relations.c \
-	table-relations.h
+	table-relations.h \
+	relations-diagram.c \
+	relations-diagram.h
 endif
diff --git a/tools/browser/schema-browser/favorite-selector.c b/tools/browser/schema-browser/favorite-selector.c
index 8d903e1..99e8e6f 100644
--- a/tools/browser/schema-browser/favorite-selector.c
+++ b/tools/browser/schema-browser/favorite-selector.c
@@ -30,6 +30,7 @@
 #include "../dnd.h"
 #include "../support.h"
 #include "../cc-gray-bar.h"
+#include "../browser-favorites.h"
 
 struct _FavoriteSelectorPrivate {
 	BrowserConnection *bcnc;
@@ -42,7 +43,7 @@ static void favorite_selector_init       (FavoriteSelector *tsel,
 				       FavoriteSelectorClass *klass);
 static void favorite_selector_dispose   (GObject *object);
 
-static void favorites_changed_cb (BrowserConnection *bcnc, FavoriteSelector *tsel);
+static void favorites_changed_cb (BrowserFavorites *bfav, FavoriteSelector *tsel);
 
 enum {
 	SELECTION_CHANGED,
@@ -99,7 +100,7 @@ favorite_selector_dispose (GObject *object)
 			g_object_unref (tsel->priv->tree);
 
 		if (tsel->priv->bcnc) {
-			g_signal_handlers_disconnect_by_func (tsel->priv->bcnc,
+			g_signal_handlers_disconnect_by_func (browser_connection_get_favorites (tsel->priv->bcnc),
 							      G_CALLBACK (favorites_changed_cb), tsel);
 			g_object_unref (tsel->priv->bcnc);
 		}
@@ -178,7 +179,7 @@ favorite_selector_new (BrowserConnection *bcnc)
 	tsel = FAVORITE_SELECTOR (g_object_new (FAVORITE_SELECTOR_TYPE, NULL));
 
 	tsel->priv->bcnc = g_object_ref (bcnc);
-	g_signal_connect (tsel->priv->bcnc, "favorites-changed::TABLE",
+	g_signal_connect (browser_connection_get_favorites (tsel->priv->bcnc), "favorites-changed",
 			  G_CALLBACK (favorites_changed_cb), tsel);
 	
 	/* create tree managers */
@@ -258,8 +259,8 @@ favorite_selector_new (BrowserConnection *bcnc)
 			  G_CALLBACK (tree_store_drag_can_drag_cb), tsel);
 	g_signal_connect (model, "drag-get",
 			  G_CALLBACK (tree_store_drag_get_cb), tsel);
-	g_signal_connect (model, "drag-delete",
-			  G_CALLBACK (tree_store_drag_delete_cb), tsel);
+	//g_signal_connect (model, "drag-delete",
+	//		  G_CALLBACK (tree_store_drag_delete_cb), tsel);
 
 	/* delete favorite */
 	GtkWidget *image;
@@ -300,19 +301,23 @@ static gboolean
 tree_store_drag_drop_cb (GdauiTreeStore *store, const gchar *path, GtkSelectionData *selection_data,
 			 FavoriteSelector *tsel)
 {
-	BrowserConnectionFavorite fav;
+	BrowserFavorites *bfav;
+	BrowserFavoritesAttributes fav;
 	GError *error = NULL;
 	gint pos;
 
-
+	memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+	fav.id = -1;
 	fav.type = BROWSER_FAVORITES_TABLES;
+	fav.name = NULL;
 	fav.descr = NULL;
 	fav.contents = (gchar*) selection_data->data;
 
 	pos = atoi (path);
 	g_print ("%s() path => %s, pos: %d\n", __FUNCTION__, path, pos);
 	
-	if (! browser_connection_add_favorite (tsel->priv->bcnc, 0, &fav, pos, &error)) {
+	bfav = browser_connection_get_favorites (tsel->priv->bcnc);
+	if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_SCHEMA, pos, &error)) {
 		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
 				    _("Could not add favorite: %s"),
 				    error && error->message ? error->message : _("No detail"));
@@ -369,12 +374,16 @@ tree_store_drag_delete_cb (GdauiTreeStore *store, const gchar *path,
 		cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
 		if (cvalue) {
 			GError *lerror = NULL;
-			BrowserConnectionFavorite fav;
-			memset (&fav, 0, sizeof (BrowserConnectionFavorite));
+			BrowserFavorites *bfav;
+			BrowserFavoritesAttributes fav;
+
+			bfav = browser_connection_get_favorites (tsel->priv->bcnc);
+			memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+			fav.id = -1;
 			fav.type = BROWSER_FAVORITES_TABLES;
 			fav.descr = NULL;
 			fav.contents = (gchar*) g_value_get_string (cvalue);
-			if (! browser_connection_delete_favorite (tsel->priv->bcnc, 0, &fav, &lerror)) {
+			if (! browser_favorites_delete (bfav, 0, &fav, &lerror)) {
 				browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*)tsel),
 						    _("Could not remove favorite: %s"),
 						    lerror && lerror->message ? lerror->message : _("No detail"));
@@ -390,7 +399,7 @@ tree_store_drag_delete_cb (GdauiTreeStore *store, const gchar *path,
 
 
 static void
-favorites_changed_cb (BrowserConnection *bcnc, FavoriteSelector *tsel)
+favorites_changed_cb (BrowserFavorites *bfav, FavoriteSelector *tsel)
 {
 	if (! gda_tree_update_all (tsel->priv->tree, NULL)) {
 		if (tsel->priv->idle_update_favorites == 0)
diff --git a/tools/browser/schema-browser/relations-diagram.c b/tools/browser/schema-browser/relations-diagram.c
new file mode 100644
index 0000000..e0f481e
--- /dev/null
+++ b/tools/browser/schema-browser/relations-diagram.c
@@ -0,0 +1,462 @@
+/*
+ * 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 "relations-diagram.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../canvas/browser-canvas-db-relations.h"
+#include <gdk/gdkkeysyms.h>
+
+struct _RelationsDiagramPrivate {
+	BrowserConnection *bcnc;
+	gchar *name; /* diagram's name */
+
+	CcGrayBar *header;
+	GtkWidget *canvas;
+	GtkWidget *save_button;
+
+	GtkWidget *window; /* to enter canvas's name */
+	GtkWidget *name_entry;
+	GtkWidget *real_save_button;
+};
+
+static void relations_diagram_class_init (RelationsDiagramClass *klass);
+static void relations_diagram_init       (RelationsDiagram *diagram, RelationsDiagramClass *klass);
+static void relations_diagram_dispose   (GObject *object);
+static void relations_diagram_set_property (GObject *object,
+					    guint param_id,
+					    const GValue *value,
+					    GParamSpec *pspec);
+static void relations_diagram_get_property (GObject *object,
+					    guint param_id,
+					    GValue *value,
+					    GParamSpec *pspec);
+
+static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram);
+
+/* properties */
+enum {
+        PROP_0,
+};
+
+enum {
+	SELECTION_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint relations_diagram_signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * RelationsDiagram class implementation
+ */
+
+static void
+relations_diagram_class_init (RelationsDiagramClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* Properties */
+        object_class->set_property = relations_diagram_set_property;
+        object_class->get_property = relations_diagram_get_property;
+
+	object_class->dispose = relations_diagram_dispose;
+}
+
+
+static void
+relations_diagram_init (RelationsDiagram *diagram, RelationsDiagramClass *klass)
+{
+	diagram->priv = g_new0 (RelationsDiagramPrivate, 1);
+	diagram->priv->name = NULL;
+	diagram->priv->window = NULL;
+}
+
+static void
+relations_diagram_dispose (GObject *object)
+{
+	RelationsDiagram *diagram = (RelationsDiagram *) object;
+
+	/* free memory */
+	if (diagram->priv) {
+		if (diagram->priv->bcnc) {
+			g_signal_handlers_disconnect_by_func (diagram->priv->bcnc,
+							      G_CALLBACK (meta_changed_cb), diagram);
+			g_object_unref (diagram->priv->bcnc);
+		}
+
+		if (diagram->priv->window)
+			gtk_widget_destroy (diagram->priv->window);
+
+		g_free (diagram->priv->name);
+		g_free (diagram->priv);
+		diagram->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GType
+relations_diagram_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (RelationsDiagramClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) relations_diagram_class_init,
+			NULL,
+			NULL,
+			sizeof (RelationsDiagram),
+			0,
+			(GInstanceInitFunc) relations_diagram_init
+		};
+		type = g_type_register_static (GTK_TYPE_VBOX, "RelationsDiagram", &info, 0);
+	}
+	return type;
+}
+
+static void
+relations_diagram_set_property (GObject *object,
+				guint param_id,
+				const GValue *value,
+				GParamSpec *pspec)
+{
+	RelationsDiagram *diagram;
+	diagram = RELATIONS_DIAGRAM (object);
+	switch (param_id) {
+	}
+}
+
+static void
+relations_diagram_get_property (GObject *object,
+				guint param_id,
+				GValue *value,
+				GParamSpec *pspec)
+{
+	RelationsDiagram *diagram;
+	diagram = RELATIONS_DIAGRAM (object);
+	switch (param_id) {
+	}
+}
+
+static void
+meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram)
+{
+	g_object_set (G_OBJECT (diagram->priv->canvas), "meta-struct", mstruct, NULL);
+}
+
+
+/*
+ * POPUP
+ */
+static void
+position_popup (RelationsDiagram *diagram)
+{
+        gint x, y;
+        gint bwidth, bheight;
+        GtkRequisition req;
+
+        gtk_widget_size_request (diagram->priv->window, &req);
+
+        gdk_window_get_origin (diagram->priv->save_button->window, &x, &y);
+
+        x += diagram->priv->save_button->allocation.x;
+        y += diagram->priv->save_button->allocation.y;
+        bwidth = diagram->priv->save_button->allocation.width;
+        bheight = diagram->priv->save_button->allocation.height;
+
+        x += bwidth - req.width;
+        y += bheight;
+
+        if (x < 0)
+                x = 0;
+
+        if (y < 0)
+                y = 0;
+
+        gtk_window_move (GTK_WINDOW (diagram->priv->window), x, y);
+}
+
+
+
+static gboolean
+popup_grab_on_window (GdkWindow *window, guint32 activate_time)
+{
+        if ((gdk_pointer_grab (window, TRUE,
+                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                               GDK_POINTER_MOTION_MASK,
+                               NULL, NULL, activate_time) == 0)) {
+                if (gdk_keyboard_grab (window, TRUE,
+                                       activate_time) == 0)
+                        return TRUE;
+                else {
+                        gdk_pointer_ungrab (activate_time);
+                        return FALSE;
+                }
+        }
+        return FALSE;
+}
+
+
+static gboolean
+delete_popup (GtkWidget *widget, RelationsDiagram *diagram)
+{
+	gtk_widget_hide (diagram->priv->window);
+        gtk_grab_remove (diagram->priv->window);
+	return TRUE;
+}
+
+static gboolean
+key_press_popup (GtkWidget *widget, GdkEventKey *event, RelationsDiagram *diagram)
+{
+	if (event->keyval != GDK_Escape)
+                return FALSE;
+
+        g_signal_stop_emission_by_name (widget, "key_press_event");
+	gtk_widget_hide (diagram->priv->window);
+        gtk_grab_remove (diagram->priv->window);
+        return TRUE;
+}
+
+static gboolean
+button_press_popup (GtkWidget *widget, GdkEventButton *event, RelationsDiagram *diagram)
+{
+	GtkWidget *child;
+
+        child = gtk_get_event_widget ((GdkEvent *) event);
+
+        /* We don't ask for button press events on the grab widget, so
+         *  if an event is reported directly to the grab widget, it must
+         *  be on a window outside the application (and thus we remove
+         *  the popup window). Otherwise, we check if the widget is a child
+         *  of the grab widget, and only remove the popup window if it
+         *  is not.
+         */
+        if (child != widget) {
+                while (child) {
+                        if (child == widget)
+                                return FALSE;
+                        child = child->parent;
+                }
+        }
+	gtk_widget_hide (diagram->priv->window);
+        gtk_grab_remove (diagram->priv->window);
+        return TRUE;
+}
+
+
+static void
+real_save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram)
+{
+	gchar *str;
+
+	//str = browser_canvas_serialize_items (BROWSER_CANVAS (diagram->priv->canvas));
+	str = g_strdup ("OOOOO");
+
+	GError *lerror = NULL;
+	BrowserFavorites *bfav;
+	BrowserFavoritesAttributes fav;
+
+	memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+	fav.id = -1;
+	fav.type = BROWSER_FAVORITES_DIAGRAMS;
+	fav.name = gtk_editable_get_chars (GTK_EDITABLE (diagram->priv->name_entry), 0, -1);
+	if (!*fav.name) {
+		g_free (fav.name);
+		fav.name = g_strdup ("Diagram #0");
+	}
+	fav.contents = str;
+	
+	gtk_widget_hide (diagram->priv->window);
+	gtk_grab_remove (diagram->priv->window);
+	
+	bfav = browser_connection_get_favorites (diagram->priv->bcnc);
+	if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_SCHEMA, G_MAXINT, &lerror)) {
+		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel (button),
+				    "<b>%s:</b>\n%s",
+				    _("Could not save diagram"),
+				    lerror && lerror->message ? lerror->message : _("No detail"));
+		if (lerror)
+			g_error_free (lerror);
+	}
+	g_free (fav.name);
+	g_free (str);
+}
+
+static void
+save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram)
+{
+	gchar *str;
+
+	if (!diagram->priv->window) {
+		GtkWidget *window, *wid, *hbox;
+
+		window = gtk_window_new (GTK_WINDOW_POPUP);
+		gtk_widget_set_events (window, gtk_widget_get_events (window) | GDK_KEY_PRESS_MASK);
+		gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+		gtk_container_set_border_width (GTK_CONTAINER (window), 5);
+		g_signal_connect (G_OBJECT (window), "delete_event",
+				  G_CALLBACK (delete_popup), diagram);
+		g_signal_connect (G_OBJECT (window), "key_press_event",
+				  G_CALLBACK (key_press_popup), diagram);
+		g_signal_connect (G_OBJECT (window), "button_press_event",
+				  G_CALLBACK (button_press_popup), diagram);
+		diagram->priv->window = window;
+
+		hbox = gtk_hbox_new (FALSE, 0);
+		gtk_container_add (GTK_CONTAINER (window), hbox);
+		wid = gtk_label_new ("");
+		str = g_strdup_printf ("%s:", _("Canvas's name"));
+		gtk_label_set_markup (GTK_LABEL (wid), str);
+		g_free (str);
+		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
+
+		wid = gtk_entry_new ();
+		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5);
+		diagram->priv->name_entry = wid;
+
+		wid = gtk_button_new_with_label (_("Save"));
+		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
+		g_signal_connect (wid, "clicked",
+				  G_CALLBACK (real_save_clicked_cb), diagram);
+		diagram->priv->real_save_button = wid;
+
+		gtk_widget_show_all (hbox);
+	}
+
+	if (!popup_grab_on_window (button->window, gtk_get_current_event_time ()))
+                return;
+	position_popup (diagram);
+	gtk_grab_add (diagram->priv->window);
+        gtk_widget_show (diagram->priv->window);
+
+	GdkScreen *screen;
+	gint swidth, sheight;
+	gint root_x, root_y;
+	gint wwidth, wheight;
+	gboolean do_move = FALSE;
+	screen = gtk_window_get_screen (GTK_WINDOW (diagram->priv->window));
+	if (screen) {
+		swidth = gdk_screen_get_width (screen);
+		sheight = gdk_screen_get_height (screen);
+	}
+	else {
+		swidth = gdk_screen_width ();
+		sheight = gdk_screen_height ();
+	}
+	gtk_window_get_position (GTK_WINDOW (diagram->priv->window), &root_x, &root_y);
+	gtk_window_get_size (GTK_WINDOW (diagram->priv->window), &wwidth, &wheight);
+	if (root_x + wwidth > swidth) {
+		do_move = TRUE;
+		root_x = swidth - wwidth;
+	}
+	else if (root_x < 0) {
+		do_move = TRUE;
+		root_x = 0;
+	}
+	if (root_y + wheight > sheight) {
+		do_move = TRUE;
+		root_y = sheight - wheight;
+	}
+	else if (root_y < 0) {
+		do_move = TRUE;
+		root_y = 0;
+	}
+	if (do_move)
+		gtk_window_move (GTK_WINDOW (diagram->priv->window), root_x, root_y);
+
+	str = gtk_editable_get_chars (GTK_EDITABLE (diagram->priv->name_entry), 0, -1);
+	if (!*str)
+		gtk_widget_grab_focus (diagram->priv->name_entry);
+	else
+		gtk_widget_grab_focus (diagram->priv->real_save_button);
+	g_free (str);
+        popup_grab_on_window (diagram->priv->window->window,
+                              gtk_get_current_event_time ());
+}
+
+
+/**
+ * relations_diagram_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+relations_diagram_new (BrowserConnection *bcnc)
+{
+	RelationsDiagram *diagram;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+	diagram = RELATIONS_DIAGRAM (g_object_new (RELATIONS_DIAGRAM_TYPE, NULL));
+
+	diagram->priv->bcnc = g_object_ref (bcnc);
+	g_signal_connect (diagram->priv->bcnc, "meta-changed",
+			  G_CALLBACK (meta_changed_cb), diagram);
+
+	/* header */
+	GtkWidget *hbox, *wid;
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (diagram), hbox, FALSE, FALSE, 0);
+
+        GtkWidget *label;
+	gchar *str;
+	str = g_strdup_printf ("<b>%s</b>\n%s", _("Relations diagram"), _("Unsaved diagram"));
+	label = cc_gray_bar_new (str);
+	g_free (str);
+        gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+	diagram->priv->header = CC_GRAY_BAR (label);
+
+	wid = gtk_button_new ();
+	label = gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON);
+	gtk_container_add (GTK_CONTAINER (wid), label);
+	gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
+	g_object_set (G_OBJECT (wid), "label", NULL, NULL);
+	diagram->priv->save_button = wid;
+
+	g_signal_connect (wid, "clicked",
+			  G_CALLBACK (save_clicked_cb), diagram);
+
+        gtk_widget_show_all (hbox);
+
+	/* main contents */
+	wid = browser_canvas_db_relations_new (NULL);
+	diagram->priv->canvas = wid;
+	gtk_box_pack_start (GTK_BOX (diagram), wid, TRUE, TRUE, 0);	
+        gtk_widget_show_all (wid);
+	
+	GdaMetaStruct *mstruct;
+	mstruct = browser_connection_get_meta_struct (diagram->priv->bcnc);
+	if (mstruct)
+		meta_changed_cb (diagram->priv->bcnc, mstruct, diagram);
+
+	return (GtkWidget*) diagram;
+}
diff --git a/tools/browser/schema-browser/relations-diagram.h b/tools/browser/schema-browser/relations-diagram.h
new file mode 100644
index 0000000..d757805
--- /dev/null
+++ b/tools/browser/schema-browser/relations-diagram.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __RELATIONS_DIAGRAM_H__
+#define __RELATIONS_DIAGRAM_H__
+
+#include <gtk/gtkvbox.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define RELATIONS_DIAGRAM_TYPE            (relations_diagram_get_type())
+#define RELATIONS_DIAGRAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, RELATIONS_DIAGRAM_TYPE, RelationsDiagram))
+#define RELATIONS_DIAGRAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, RELATIONS_DIAGRAM_TYPE, RelationsDiagramClass))
+#define IS_RELATIONS_DIAGRAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, RELATIONS_DIAGRAM_TYPE))
+#define IS_RELATIONS_DIAGRAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RELATIONS_DIAGRAM_TYPE))
+
+typedef struct _RelationsDiagram        RelationsDiagram;
+typedef struct _RelationsDiagramClass   RelationsDiagramClass;
+typedef struct _RelationsDiagramPrivate RelationsDiagramPrivate;
+
+struct _RelationsDiagram {
+	GtkVBox               parent;
+	RelationsDiagramPrivate     *priv;
+};
+
+struct _RelationsDiagramClass {
+	GtkVBoxClass          parent_class;
+};
+
+GType                    relations_diagram_get_type (void) G_GNUC_CONST;
+GtkWidget               *relations_diagram_new (BrowserConnection *bcnc);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/schema-browser/schema-browser-perspective.c b/tools/browser/schema-browser/schema-browser-perspective.c
index a57c78e..75c251c 100644
--- a/tools/browser/schema-browser/schema-browser-perspective.c
+++ b/tools/browser/schema-browser/schema-browser-perspective.c
@@ -25,6 +25,9 @@
 #include "../browser-window.h"
 #include "table-info.h"
 #include "../support.h"
+#ifdef HAVE_GOOCANVAS
+#include "relations-diagram.h"
+#endif
 
 /* 
  * Main static functions 
@@ -229,57 +232,46 @@ schema_browser_perspective_dispose (GObject *object)
 	parent_class->dispose (object);
 }
 
+#ifdef HAVE_GOOCANVAS
 static void
-action_create_table_cb (GtkAction *action, BrowserPerspective *bpers)
+action_create_diagram_cb (GtkAction *action, SchemaBrowserPerspective *bpers)
 {
-	TO_IMPLEMENT;
-}
-
-static void
-action_drop_table_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	TO_IMPLEMENT;
-}
-
-static void
-action_rename_table_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	TO_IMPLEMENT;
-}
-
-static void
-action_add_column_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	TO_IMPLEMENT;
-}
-
-static void
-action_drop_column_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	TO_IMPLEMENT;
-}
-
-static void
-action_table_contents_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	TO_IMPLEMENT;
+	GtkWidget *diagram;
+	diagram = relations_diagram_new (browser_window_get_connection (bpers->priv->bwin));
+	if (diagram) {
+		GtkWidget *close_btn;
+		GdkPixbuf *diagram_pixbuf;
+		gint i;
+		
+		diagram_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_DIAGRAM);
+		i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
+					      browser_make_tab_label_with_pixbuf (_("Diagram"),
+										  diagram_pixbuf,
+										  TRUE, &close_btn));
+		g_signal_connect (close_btn, "clicked",
+				  G_CALLBACK (close_button_clicked_cb), diagram);
+		
+		gtk_widget_show (diagram);
+		gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
+					     browser_make_tab_label_with_pixbuf (_("Diagram"),
+										 diagram_pixbuf,
+										 FALSE, NULL));
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+		gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
+						  TRUE);
+		gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
+						 TRUE);
+	}
 }
+#endif
 
 
 static GtkActionEntry ui_actions[] = {
+#ifdef HAVE_GOOCANVAS
         { "Schema", NULL, "_Schema", NULL, "Schema", NULL },
-        { "CREATE_TABLE", GTK_STOCK_ADD, "_New table", NULL, "Create a new table",
-          G_CALLBACK (action_create_table_cb)},
-        { "DROP_TABLE", GTK_STOCK_DELETE, "_Delete", NULL, "Delete the selected table",
-          G_CALLBACK (action_drop_table_cb)},
-        { "RENAME_TABLE", NULL, "_Rename", NULL, "Rename the selected table",
-          G_CALLBACK (action_rename_table_cb)},
-        { "ADD_COLUMN", NULL, "_Add column", NULL, "Add a column to the selected table",
-          G_CALLBACK (action_add_column_cb)},
-        { "DROP_COLUMN", NULL, "_Delete column", NULL, "Delete a column from the selected table",
-          G_CALLBACK (action_drop_column_cb)},
-        { "TableContents", GTK_STOCK_EDIT, "_Contents", NULL, "Display the contents of the selected table",
-          G_CALLBACK (action_table_contents_cb)},
+        { "NewDiagram", GTK_STOCK_ADD, "_New Diagram", NULL, "Create a new diagram",
+          G_CALLBACK (action_create_diagram_cb)},
+#endif
 };
 
 static const gchar *ui_actions_info =
@@ -287,23 +279,13 @@ static const gchar *ui_actions_info =
         "  <menubar name='MenuBar'>"
         "    <placeholder name='MenuExtension'>"
         "      <menu name='Schema' action='Schema'>"
-        "        <menuitem name='CREATE_TABLE' action= 'CREATE_TABLE'/>"
-        "        <menuitem name='DROP_TABLE' action= 'DROP_TABLE'/>"
-        "        <menuitem name='RENAME_TABLE' action= 'RENAME_TABLE'/>"
-        "        <separator/>"
-        "        <menuitem name='ADD_COLUMN' action= 'ADD_COLUMN'/>"
-        "        <menuitem name='DROP_COLUMN' action= 'DROP_COLUMN'/>"
-        "        <separator/>"
-        "        <menuitem name='TableContents' action= 'TableContents'/>"
+        "        <menuitem name='NewDiagram' action= 'NewDiagram'/>"
         "      </menu>"
         "    </placeholder>"
         "  </menubar>"
         "  <toolbar name='ToolBar'>"
         "    <separator/>"
-        "    <toolitem action='CREATE_TABLE'/>"
-        "    <toolitem action='DROP_TABLE'/>"
-        "    <separator/>"
-        "    <toolitem action='TableContents'/>"
+        "    <toolitem action='NewDiagram'/>"
         "  </toolbar>"
         "</ui>";
 
@@ -313,13 +295,6 @@ schema_browser_perspective_get_actions_group (BrowserPerspective *bpers)
 	GtkActionGroup *agroup;
 	agroup = gtk_action_group_new ("SchemaBrowserActions");
 	gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
-
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "CREATE_TABLE"), TRUE);
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "DROP_TABLE"), FALSE);
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "RENAME_TABLE"), FALSE);
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "ADD_COLUMN"), FALSE);
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "DROP_COLUMN"), FALSE);
-	gtk_action_set_sensitive (gtk_action_group_get_action (agroup, "TableContents"), FALSE);
 	
 	return agroup;
 }
@@ -364,7 +339,6 @@ schema_browser_perspective_display_table_info (SchemaBrowserPerspective *bpers,
 		const gchar *tab_name;
 		GdkPixbuf *table_pixbuf;
 		
-		g_object_set (G_OBJECT (ti), "perspective", bpers, NULL);
 		table_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
 		tab_name = table_short_name ? table_short_name : table_name;
 		i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), ti,
diff --git a/tools/browser/schema-browser/table-columns.c b/tools/browser/schema-browser/table-columns.c
index d557379..8156f72 100644
--- a/tools/browser/schema-browser/table-columns.c
+++ b/tools/browser/schema-browser/table-columns.c
@@ -597,7 +597,9 @@ follow_if_link (GtkWidget *text_view, GtkTextIter *iter, TableColumns *tcolumns)
 		table_schema = g_object_get_data (G_OBJECT (tag), "table_schema");
 		table_name = g_object_get_data (G_OBJECT (tag), "table_name");
 		table_short_name = g_object_get_data (G_OBJECT (tag), "table_short_name");
-		g_object_get (tcolumns->priv->tinfo, "perspective", &bpers, NULL);
+
+		bpers = browser_find_parent_widget (GTK_WIDGET (tcolumns),
+						    TYPE_SCHEMA_BROWSER_PERSPECTIVE);
 		if (table_name && table_schema && table_short_name && bpers) {
 			schema_browser_perspective_display_table_info (bpers,
 								       table_schema,
diff --git a/tools/browser/schema-browser/table-info.c b/tools/browser/schema-browser/table-info.c
index 6fc0610..9de12ba 100644
--- a/tools/browser/schema-browser/table-info.c
+++ b/tools/browser/schema-browser/table-info.c
@@ -35,7 +35,6 @@
 
 struct _TableInfoPrivate {
 	BrowserConnection *bcnc;
-	SchemaBrowserPerspective *bpers; /* perspective into which tinfo will be */
 
 	gchar *schema;
 	gchar *table_name;
@@ -64,7 +63,6 @@ static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, Ta
 /* properties */
 enum {
         PROP_0,
-        PROP_PERSPECTIVE
 };
 
 enum {
@@ -91,10 +89,6 @@ table_info_class_init (TableInfoClass *klass)
         object_class->set_property = table_info_set_property;
         object_class->get_property = table_info_get_property;
 
-	g_object_class_install_property (object_class, PROP_PERSPECTIVE,
-                                         g_param_spec_pointer ("perspective", NULL, NULL,
-							       G_PARAM_READABLE | G_PARAM_WRITABLE));
-
 	object_class->dispose = table_info_dispose;
 }
 
@@ -159,9 +153,6 @@ table_info_set_property (GObject *object,
 	TableInfo *tinfo;
 	tinfo = TABLE_INFO (object);
 	switch (param_id) {
-	case PROP_PERSPECTIVE:
-		tinfo->priv->bpers = g_value_get_pointer (value);
-		break;
 	}
 }
 
@@ -174,9 +165,6 @@ table_info_get_property (GObject *object,
 	TableInfo *tinfo;
 	tinfo = TABLE_INFO (object);
 	switch (param_id) {
-	case PROP_PERSPECTIVE:
-		g_value_set_pointer (value, tinfo->priv->bpers);
-		break;
 	}
 }
 
diff --git a/tools/browser/schema-browser/table-relations.c b/tools/browser/schema-browser/table-relations.c
index 6e0ec6b..414671f 100644
--- a/tools/browser/schema-browser/table-relations.c
+++ b/tools/browser/schema-browser/table-relations.c
@@ -121,6 +121,7 @@ meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableRelations
 {
 	GdaMetaDbObject *dbo;
 	GValue *tname, *tschema;
+	BrowserCanvasTable *ctable;
 
 	g_object_set (G_OBJECT (trels->priv->canvas), "meta-struct", mstruct, NULL);
 
@@ -128,8 +129,10 @@ meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableRelations
 			    table_info_get_table_schema (trels->priv->tinfo));
 	g_value_set_string ((tname = gda_value_new (G_TYPE_STRING)),
 			    table_info_get_table_name (trels->priv->tinfo));
-        browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas), NULL,
-					       tschema, tname);
+        ctable = browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas), NULL,
+							tschema, tname);
+	browser_canvas_db_relations_select_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas),
+						  ctable);
 
 	dbo = gda_meta_struct_get_db_object (mstruct, NULL, tschema, tname);
 
@@ -195,7 +198,7 @@ table_relations_new (TableInfo *tinfo)
 	 */
 	trels->priv->canvas = browser_canvas_db_relations_new (NULL);
 	gtk_box_pack_start (GTK_BOX (trels), trels->priv->canvas, TRUE, TRUE, 0);
-	gtk_widget_show (trels);
+	gtk_widget_show (GTK_WIDGET (trels));
 
 	/*
 	 * initial update
diff --git a/tools/browser/support.c b/tools/browser/support.c
index 01e3062..83dff6a 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -228,7 +228,8 @@ browser_get_pixbuf_icon (BrowserIconType type)
 		"gda-browser-column-fk.png",
 		"gda-browser-column-fknn.png",
 		"gda-browser-column-nn.png",
-		"gda-browser-reference.png"
+		"gda-browser-reference.png",
+		"gda-browser-diagram.png"
 	};
 
 	if (!array)
@@ -247,3 +248,21 @@ browser_get_pixbuf_icon (BrowserIconType type)
 	else
 		return array [type];
 }
+
+/**
+ * browser_find_parent_widget
+ *
+ * Finds the 1st parent widget of @current which is of the @requested_type type.
+ */
+GtkWidget *
+browser_find_parent_widget (GtkWidget *current, GType requested_type)
+{
+	GtkWidget *wid;
+	g_return_val_if_fail (GTK_IS_WIDGET (current), NULL);
+
+	for (wid = gtk_widget_get_parent (current); wid; wid = gtk_widget_get_parent (wid)) {
+		if (G_OBJECT_TYPE (wid) == requested_type)
+			return wid;
+	}
+	return NULL;
+}
diff --git a/tools/browser/support.h b/tools/browser/support.h
index cdc1245..264fd67 100644
--- a/tools/browser/support.h
+++ b/tools/browser/support.h
@@ -40,6 +40,11 @@ GtkWidget*         browser_make_tab_label_with_pixbuf (const gchar *label,
 						       GtkWidget **out_close_button);
 
 /*
+ * Widgets navigation
+ */
+GtkWidget        *browser_find_parent_widget (GtkWidget *current, GType requested_type);
+
+/*
  * icons
  */
 typedef enum {
@@ -52,6 +57,7 @@ typedef enum {
 	BROWSER_ICON_COLUMN_FK_NN,
 	BROWSER_ICON_COLUMN_NN,
 	BROWSER_ICON_REFERENCE,
+	BROWSER_ICON_DIAGRAM,
 
 	BROWSER_ICON_LAST
 } BrowserIconType;



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