[libgda] Initial Data Manager perspective in Gda-Browser



commit d4311c780e3c9434b77f98e1c2a4163351b079f9
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Mar 2 21:38:28 2010 +0100

    Initial Data Manager perspective in Gda-Browser

 configure.in                                       |    1 +
 doc/C/howto.xml                                    |    2 +-
 po/POTFILES.in                                     |    6 +
 tools/browser/Makefile.am                          |    4 +-
 tools/browser/browser-connection.c                 |   48 ++-
 tools/browser/browser-connection.h                 |    3 +
 tools/browser/browser-favorites.c                  |   10 +-
 tools/browser/browser-favorites.h                  |    5 +-
 tools/browser/browser-stock-icons.c                |   11 +-
 tools/browser/browser-stock-icons.h                |    1 +
 tools/browser/browser-window.c                     |  116 +++-
 tools/browser/data-manager/Makefile.am             |   42 ++
 tools/browser/data-manager/data-console.c          |  518 ++++++++++++++
 tools/browser/data-manager/data-console.h          |   58 ++
 .../browser/data-manager/data-favorite-selector.c  |  688 ++++++++++++++++++
 .../browser/data-manager/data-favorite-selector.h  |   59 ++
 .../data-manager/data-manager-perspective.c        |  327 +++++++++
 .../data-manager/data-manager-perspective.h        |   56 ++
 tools/browser/data-manager/data-source.c           |  733 ++++++++++++++++++++
 tools/browser/data-manager/data-source.h           |   78 ++
 tools/browser/data-manager/data-widget.c           |  499 +++++++++++++
 tools/browser/data-manager/data-widget.h           |   59 ++
 tools/browser/data-manager/marshal.list            |   27 +
 tools/browser/data-manager/perspective-main.c      |   37 +
 tools/browser/data-manager/perspective-main.h      |   29 +
 tools/browser/data-manager/spec-editor.c           |  485 +++++++++++++
 tools/browser/data-manager/spec-editor.h           |   70 ++
 tools/browser/data/Makefile.am                     |    5 +
 tools/browser/data/hicolor_actions_16x16_glade.png |  Bin 0 -> 527 bytes
 tools/browser/data/hicolor_actions_22x22_glade.png |  Bin 0 -> 1024 bytes
 tools/browser/data/hicolor_actions_24x24_glade.png |  Bin 0 -> 1050 bytes
 tools/browser/data/hicolor_actions_32x32_glade.png |  Bin 0 -> 1643 bytes
 .../data/hicolor_actions_scalable_glade.svg        |  648 +++++++++++++++++
 tools/browser/decl.h                               |    1 +
 tools/browser/doc/tmpl/browser-favorites.sgml      |    1 +
 tools/browser/main.c                               |    6 +-
 tools/browser/mgr-favorites.c                      |   37 +
 tools/browser/query-exec/perspective-main.c        |    2 +-
 tools/browser/query-exec/query-console.c           |   71 +--
 tools/browser/query-exec/query-console.h           |    2 +-
 tools/browser/query-exec/query-exec-perspective.c  |  143 +----
 tools/browser/query-exec/query-favorite-selector.c |   10 +-
 tools/browser/support.c                            |   41 ++-
 tools/browser/support.h                            |   22 +-
 44 files changed, 4731 insertions(+), 230 deletions(-)
---
diff --git a/configure.in b/configure.in
index 6da2d6c..b181669 100644
--- a/configure.in
+++ b/configure.in
@@ -1764,6 +1764,7 @@ tools/browser/data/Makefile
 tools/browser/common/Makefile
 tools/browser/schema-browser/Makefile
 tools/browser/query-exec/Makefile
+tools/browser/data-manager/Makefile
 tools/browser/dummy-perspective/Makefile
 tools/browser/canvas/Makefile
 tools/browser/doc/Makefile
diff --git a/doc/C/howto.xml b/doc/C/howto.xml
index e3a2aaa..55300d7 100644
--- a/doc/C/howto.xml
+++ b/doc/C/howto.xml
@@ -99,7 +99,7 @@ g_object_unref (b);
       <para>
 	<programlisting>
 GdaSqlBuilder *b;
-b = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
+b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
 gda_sql_builder_select_add_field (b, "firstname", "people", "person");
 gda_sql_builder_select_add_field (b, "lastname", "people", NULL);
 gda_sql_builder_select_add_field (b, "date", NULL, "birthdate");
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 75712fb..b578672 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -224,6 +224,7 @@ tools/browser/login-dialog.c
 tools/browser/main.c
 tools/browser/mgr-favorites.c
 tools/browser/support.c
+tools/browser/support.h
 tools/browser/canvas/browser-canvas-column.c
 tools/browser/canvas/browser-canvas-db-relations.c
 tools/browser/canvas/browser-canvas-fkey.c
@@ -233,6 +234,11 @@ tools/browser/canvas/browser-canvas.c
 tools/browser/common/gdaui-data-import.c
 tools/browser/common/gdaui-entry-import.c
 tools/browser/common/objects-cloud.c
+tools/browser/data-manager/data-console.c
+tools/browser/data-manager/data-favorite-selector.c
+tools/browser/data-manager/data-source.c
+tools/browser/data-manager/data-widget.c
+tools/browser/data-manager/spec-editor.c
 tools/browser/query-exec/gda-sql.lang
 tools/browser/query-exec/query-console.c
 tools/browser/query-exec/query-editor.c
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index a33dc98..ff0c5d8 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -1,7 +1,7 @@
 bin_PROGRAMS=gda-browser-4.0
 noinst_LTLIBRARIES = libbrowser.la
 
-SUBDIRS = data common schema-browser query-exec dummy-perspective
+SUBDIRS = data common schema-browser query-exec data-manager dummy-perspective
 if HAVE_GOOCANVAS
 SUBDIRS+=canvas
 noinst_PROGRAMS=canvas-example
@@ -94,7 +94,7 @@ gda_browser_4_0_LDFLAGS = $(EXTRALDFLAGS)
 gda_browser_4_0_LDADD=\
 	schema-browser/libperspective.la \
 	query-exec/libperspective.la \
-	dummy-perspective/libperspective.la \
+	data-manager/libperspective.la \
 	libbrowser.la \
 	$(CANVAS_LDADD) \
 	common/libcommon.la \
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index dc2b914..0afa9cb 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -101,6 +101,7 @@ push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const
 	wj->job_type = job_type;
 	if (reason)
 		wj->reason = g_strdup (reason);
+
 	bcnc->priv->wrapper_jobs = g_slist_append (bcnc->priv->wrapper_jobs, wj);
 
 	if (! bcnc->priv->wrapper_jobs->next)
@@ -713,9 +714,10 @@ browser_connection_update_meta_data (BrowserConnection *bcnc)
 	if (bcnc->priv->wrapper_jobs) {
 		WrapperJob *wj;
 		wj = (WrapperJob*) g_slist_last (bcnc->priv->wrapper_jobs)->data;
-		if (wj->job_type == JOB_TYPE_META_STORE_UPDATE)
+		if (wj->job_type == JOB_TYPE_META_STORE_UPDATE) {
 			/* nothing to do */
 			return;
+		}
 	}
 
 	guint job_id;
@@ -985,6 +987,50 @@ browser_connection_execute_statement (BrowserConnection *bcnc,
 	return job_id;
 }
 
+typedef struct {
+	GdaConnection *cnc;
+	GdaDataModel *model;
+} RerunSelectData;
+
+/* executed in @bcnc->priv->wrapper's sub thread */
+static gpointer
+wrapper_rerun_select (RerunSelectData *data, GError **error)
+{
+	gboolean retval;
+
+	retval = gda_data_select_rerun (GDA_DATA_SELECT (data->model), error);
+	return retval ? data->model : (gpointer) 0x01;
+}
+
+/**
+ * browser_connection_rerun_select
+ */
+guint
+browser_connection_rerun_select (BrowserConnection *bcnc,
+				 GdaDataModel *model,
+				 GError **error)
+{
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
+	g_return_val_if_fail (GDA_IS_DATA_SELECT (model), 0);
+
+	RerunSelectData *data;
+	guint job_id;
+
+	data = g_new0 (RerunSelectData, 1);
+	data->cnc = bcnc->priv->cnc;
+	data->model = model;
+
+	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+					     (GdaThreadWrapperFunc) wrapper_rerun_select,
+					     data, (GDestroyNotify) g_free, error);
+	if (job_id > 0)
+		push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
+				  _("Executing a query"));
+
+	return job_id;
+}
+
+
 /**
  * browser_connection_execution_get_result
  * @bcnc: a #BrowserConnection
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index 0d057ba..3c89947 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -86,6 +86,9 @@ guint               browser_connection_execute_statement      (BrowserConnection
 							       GdaStatementModelUsage model_usage,
 							       gboolean need_last_insert_row,
 							       GError **error);
+guint               browser_connection_rerun_select           (BrowserConnection *bcnc,
+							       GdaDataModel *model,
+							       GError **error);
 GObject            *browser_connection_execution_get_result   (BrowserConnection *bcnc,
 							       guint exec_id,
 							       GdaSet **last_insert_row, GError **error);
diff --git a/tools/browser/browser-favorites.c b/tools/browser/browser-favorites.c
index 49d9d89..81943c2 100644
--- a/tools/browser/browser-favorites.c
+++ b/tools/browser/browser-favorites.c
@@ -209,6 +209,8 @@ favorite_type_to_string (BrowserFavoritesType type)
 		return "DIAGRAM";
 	case BROWSER_FAVORITES_QUERIES:
 		return "QUERY";
+	case BROWSER_FAVORITES_DATA_MANAGERS:
+		return "DATAMAN";
 	default:
 		g_warning ("Unknown type of favorite");
 	}
@@ -221,8 +223,12 @@ favorite_string_to_type (const gchar *str)
 {
 	if (*str == 'T')
 		return BROWSER_FAVORITES_TABLES;
-	else if (*str == 'D')
-		return BROWSER_FAVORITES_DIAGRAMS;
+	else if (*str == 'D') {
+		if (str[1] == 'I')
+			return BROWSER_FAVORITES_DIAGRAMS;
+		else
+			return BROWSER_FAVORITES_DATA_MANAGERS;
+	}
 	else if (*str == 'Q')
 		return BROWSER_FAVORITES_QUERIES;
 	else
diff --git a/tools/browser/browser-favorites.h b/tools/browser/browser-favorites.h
index f74f5c9..3a0ec0e 100644
--- a/tools/browser/browser-favorites.h
+++ b/tools/browser/browser-favorites.h
@@ -45,9 +45,10 @@ typedef struct _BrowserFavoritesPrivate BrowserFavoritesPrivate;
 typedef enum {
         BROWSER_FAVORITES_TABLES   = 1 << 0,
 	BROWSER_FAVORITES_DIAGRAMS = 1 << 1,
-	BROWSER_FAVORITES_QUERIES  = 1 << 2
+	BROWSER_FAVORITES_QUERIES  = 1 << 2,
+	BROWSER_FAVORITES_DATA_MANAGERS  = 1 << 3
 } BrowserFavoritesType;
-#define BROWSER_FAVORITES_NB_TYPES 3
+#define BROWSER_FAVORITES_NB_TYPES 4
 
 /**
  * BrowserFavoritesAttributes:
diff --git a/tools/browser/browser-stock-icons.c b/tools/browser/browser-stock-icons.c
index bb55b6c..0fa9d4d 100644
--- a/tools/browser/browser-stock-icons.c
+++ b/tools/browser/browser-stock-icons.c
@@ -42,11 +42,12 @@ browser_stock_icons_init (void)
 
 	static const GtkStockItem items[] =
 	{
-		{ BROWSER_STOCK_HISTORY,   N_("History"),		0, 0, NULL },
-		{ BROWSER_STOCK_BOOKMARKS, N_("Bookmarks"),	0, 0, NULL },
-		{ BROWSER_STOCK_BEGIN, N_("Begin"),	0, 0, NULL },
-		{ BROWSER_STOCK_COMMIT, N_("Commit"),	0, 0, NULL },
-		{ BROWSER_STOCK_ROLLBACK, N_("Rollback"),	0, 0, NULL },
+		{ BROWSER_STOCK_HISTORY, N_("History"), 0, 0, NULL },
+		{ BROWSER_STOCK_BOOKMARKS, N_("Bookmarks"), 0, 0, NULL },
+		{ BROWSER_STOCK_BEGIN, N_("Begin"), 0, 0, NULL },
+		{ BROWSER_STOCK_COMMIT, N_("Commit"), 0, 0, NULL },
+		{ BROWSER_STOCK_ROLLBACK, N_("Rollback"), 0, 0, NULL },
+		{ BROWSER_STOCK_BUILDER, N_("Builder"), 0, 0, NULL },
 	};
 
 	factory = gtk_icon_factory_new ();
diff --git a/tools/browser/browser-stock-icons.h b/tools/browser/browser-stock-icons.h
index 2ade8cf..4f132d4 100644
--- a/tools/browser/browser-stock-icons.h
+++ b/tools/browser/browser-stock-icons.h
@@ -27,6 +27,7 @@ G_BEGIN_DECLS
 #define BROWSER_STOCK_BEGIN           "transaction-begin"
 #define BROWSER_STOCK_COMMIT          "transaction-commit"
 #define BROWSER_STOCK_ROLLBACK        "transaction-rollback"
+#define BROWSER_STOCK_BUILDER         "glade"
 
 /* Named icons defined in fd.o Icon Naming Spec */
 #define STOCK_NEW_WINDOW           "window-new"
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index 9298984..df38f14 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -64,6 +64,8 @@ static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar
 static void connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin);
 static void connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin);
 
+static void transaction_status_changed_cb (BrowserConnection *bcnc, BrowserWindow *bwin);
+
 
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass  *parent_class = NULL;
@@ -78,6 +80,7 @@ struct _BrowserWindowPrivate {
 	GtkWidget         *spinner;
 	GtkUIManager      *ui_manager;
 	GtkActionGroup    *agroup;
+	gboolean           updating_transaction_status;
 
 	GtkToolbarStyle    toolbar_style;
 	GtkActionGroup    *cnc_agroup; /* one GtkAction for each BrowserConnection */
@@ -138,6 +141,7 @@ browser_window_init (BrowserWindow *bwin)
 	bwin->priv->cnc_agroup = NULL;
 	bwin->priv->cnc_added_sigid = 0;
 	bwin->priv->cnc_removed_sigid = 0;
+	bwin->priv->updating_transaction_status = FALSE;
 }
 
 static void
@@ -165,8 +169,12 @@ browser_window_dispose (GObject *object)
 		if (bwin->priv->cnc_agroup)
 			g_object_unref (bwin->priv->cnc_agroup);
 
-		if (bwin->priv->bcnc)
+		if (bwin->priv->bcnc) {
+			g_signal_handlers_disconnect_by_func (bwin->priv->bcnc,
+							      G_CALLBACK (transaction_status_changed_cb),
+							      bwin);
 			g_object_unref (bwin->priv->bcnc);
+		}
 		if (bwin->priv->perspectives) {
 			g_slist_foreach (bwin->priv->perspectives, (GFunc) perspective_data_free, NULL);
 			g_slist_free (bwin->priv->perspectives);
@@ -189,6 +197,9 @@ delete_event (GtkWidget *widget, GdkEvent *event, BrowserWindow *bwin)
         return TRUE;
 }
 
+static void transaction_begin_cb (GtkAction *action, BrowserWindow *bwin);
+static void transaction_commit_cb (GtkAction *action, BrowserWindow *bwin);
+static void transaction_rollback_cb (GtkAction *action, BrowserWindow *bwin);
 static void quit_cb (GtkAction *action, BrowserWindow *bwin);
 static void about_cb (GtkAction *action, BrowserWindow *bwin);
 static void window_close_cb (GtkAction *action, BrowserWindow *bwin);
@@ -211,7 +222,7 @@ static const GtkToggleActionEntry ui_toggle_actions [] =
 static const GtkActionEntry ui_actions[] = {
         { "Connection", NULL, "_Connection", NULL, "Connection", NULL },
         { "ConnectionOpen", GTK_STOCK_CONNECT, "_Connect", NULL, "Open a connection", G_CALLBACK (connection_open_cb)},
-        { "ConnectionBind", NULL, N_("_Bind connection"), "<control>V", N_("Use connection to create\n"
+        { "ConnectionBind", NULL, N_("_Bind connection"), "<control>I", N_("Use connection to create\n"
 						    "a new binding connection to access data\n"
 						    "from multiple databases at once"), G_CALLBACK (connection_bind_cb)},
         { "ConnectionProps", GTK_STOCK_PROPERTIES, "_Properties", NULL, "Connection properties", G_CALLBACK (connection_properties_cb)},
@@ -226,7 +237,13 @@ static const GtkActionEntry ui_actions[] = {
         { "WindowNewOthers", NULL, "New window for _connection", NULL, "Open a new window for a connection", NULL},
         { "WindowClose", GTK_STOCK_CLOSE, "_Close", "", "Close this window", G_CALLBACK (window_close_cb)},
         { "Help", NULL, "_Help", NULL, "Help", NULL },
-        { "HelpAbout", GTK_STOCK_ABOUT, "_About", NULL, "About", G_CALLBACK (about_cb) }
+        { "HelpAbout", GTK_STOCK_ABOUT, "_About", NULL, "About", G_CALLBACK (about_cb) },
+	{ "TransactionBegin", BROWSER_STOCK_BEGIN, N_("Begin"), NULL, N_("Begin a new transaction"),
+          G_CALLBACK (transaction_begin_cb)},
+        { "TransactionCommit", BROWSER_STOCK_COMMIT, N_("Commit"), NULL, N_("Commit current transaction"),
+          G_CALLBACK (transaction_commit_cb)},
+        { "TransactionRollback", BROWSER_STOCK_ROLLBACK, N_("Rollback"), NULL, N_("Rollback current transaction"),
+          G_CALLBACK (transaction_rollback_cb)},
 };
 
 static const gchar *ui_actions_info =
@@ -241,6 +258,10 @@ static const gchar *ui_actions_info =
         "      <menuitem name='ConnectionBind' action= 'ConnectionBind'/>"
         "      <menuitem name='ConnectionClose' action= 'ConnectionClose'/>"
         "      <separator/>"
+	"      <menuitem name='TransactionBegin' action= 'TransactionBegin'/>"
+        "      <menuitem name='TransactionCommit' action= 'TransactionCommit'/>"
+        "      <menuitem name='TransactionRollback' action= 'TransactionRollback'/>"
+        "      <separator/>"
         "      <menuitem name='Quit' action= 'Quit'/>"
         "      <separator/>"
         "    </menu>"
@@ -267,6 +288,10 @@ static const gchar *ui_actions_info =
         "  <toolbar  name='ToolBar'>"
         "    <toolitem action='WindowClose'/>"
         "    <toolitem action='WindowFullScreen'/>"
+	"    <separator/>"
+        "    <toolitem action='TransactionBegin'/>"
+        "    <toolitem action='TransactionCommit'/>"
+        "    <toolitem action='TransactionRollback'/>"
         "  </toolbar>"
         "</ui>";
 
@@ -297,6 +322,9 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
 
 	bwin = BROWSER_WINDOW (g_object_new (BROWSER_TYPE_WINDOW, NULL));
 	bwin->priv->bcnc = g_object_ref (bcnc);
+	g_signal_connect (bcnc, "transaction-status-changed",
+			  G_CALLBACK (transaction_status_changed_cb), bwin);
+
 	cncname = browser_connection_get_name (bcnc);
 	if (!cncname)
 		cncname = _("unnamed");
@@ -333,6 +361,7 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
 	bwin->priv->agroup = group;
         gtk_action_group_add_actions (group, ui_actions, G_N_ELEMENTS (ui_actions), bwin);
 	gtk_action_group_add_toggle_actions (group, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions), bwin);
+	transaction_status_changed_cb (bwin->priv->bcnc, bwin);
 
         ui = gtk_ui_manager_new ();
         gtk_ui_manager_insert_action_group (ui, group, 0);
@@ -560,6 +589,20 @@ connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, Br
 	action = gtk_action_group_get_action (bwin->priv->agroup, "ConnectionMetaSync");
 	gtk_action_set_sensitive (action, !is_busy);
 
+	gboolean bsens = FALSE, csens = FALSE;
+	if (!is_busy) {
+		if (browser_connection_get_transaction_status (bcnc))
+			csens = TRUE;
+		else
+			bsens = TRUE;
+	}
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionBegin");
+	gtk_action_set_sensitive (action, bsens);
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionCommit");
+	gtk_action_set_sensitive (action, csens);
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionRollback");
+	gtk_action_set_sensitive (action, csens);	
+
 	const gchar *cncname;
 	cncname = browser_connection_get_name (bcnc);
 	action = gtk_action_group_get_action (bwin->priv->cnc_agroup, cncname);
@@ -706,6 +749,73 @@ quit_cb (GtkAction *action, BrowserWindow *bwin)
 }
 
 static void
+transaction_status_changed_cb (BrowserConnection *bcnc, BrowserWindow *bwin)
+{
+	if (!bwin->priv->agroup)
+		return;
+
+	GtkAction *action;
+	gboolean trans_started;
+
+	trans_started = browser_connection_get_transaction_status (bcnc) ? TRUE : FALSE;
+	bwin->priv->updating_transaction_status = TRUE;
+
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionBegin");
+	gtk_action_set_sensitive (action, !trans_started);
+
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionCommit");
+	gtk_action_set_sensitive (action, trans_started);
+
+	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionRollback");
+	gtk_action_set_sensitive (action, trans_started);
+				      
+	bwin->priv->updating_transaction_status = FALSE;
+}
+
+static void
+transaction_begin_cb (GtkAction *action, BrowserWindow *bwin)
+{
+	if (!bwin->priv->updating_transaction_status) {
+		GError *error = NULL;
+		if (! browser_connection_begin (bwin->priv->bcnc, &error)) {
+			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
+					    _("Error starting transaction: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+	}
+}
+
+static void
+transaction_commit_cb (GtkAction *action, BrowserWindow *bwin)
+{
+	if (!bwin->priv->updating_transaction_status) {
+		GError *error = NULL;
+		if (! browser_connection_commit (bwin->priv->bcnc, &error)) {
+			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
+					    _("Error committing transaction: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+	}
+}
+
+static void
+transaction_rollback_cb (GtkAction *action, BrowserWindow *bwin)
+{
+	if (!bwin->priv->updating_transaction_status) {
+		GError *error = NULL;
+		if (! browser_connection_rollback (bwin->priv->bcnc, &error)) {
+			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
+					    _("Error rolling back transaction: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+	}
+}
+
+
+static void
 window_close_cb (GtkAction *action, BrowserWindow *bwin)
 {
 	delete_event (NULL, NULL, bwin);
diff --git a/tools/browser/data-manager/Makefile.am b/tools/browser/data-manager/Makefile.am
new file mode 100644
index 0000000..275bf61
--- /dev/null
+++ b/tools/browser/data-manager/Makefile.am
@@ -0,0 +1,42 @@
+noinst_LTLIBRARIES = libperspective.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/tools/browser \
+	-I$(top_builddir) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libgda \
+	$(LIBGDA_CFLAGS) \
+	$(GTK_CFLAGS) \
+	$(GTKSOURCEVIEW_CFLAGS)
+
+marshal.h: marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=_dm_marshal > $@
+marshal.c: marshal.list $(GLIB_GENMARSHAL) marshal.h
+	$(GLIB_GENMARSHAL) $< --body --prefix=_dm_marshal > $@
+
+libperspective_la_SOURCES = \
+	marshal.c \
+	marshal.h \
+	data-console.h \
+	data-console.c \
+	data-source.h \
+	data-source.c \
+	data-widget.h \
+	data-widget.c \
+	data-favorite-selector.c \
+	data-favorite-selector.h \
+	perspective-main.c \
+	perspective-main.h \
+	spec-editor.c \
+	spec-editor.h \
+	data-manager-perspective.h \
+	data-manager-perspective.c
+
+$(OBJECTS): marshal.c marshal.h
+
+EXTRA_DIST= \
+        marshal.list
+
+CLEANFILES = \
+        marshal.h \
+        marshal.c
\ No newline at end of file
diff --git a/tools/browser/data-manager/data-console.c b/tools/browser/data-manager/data-console.c
new file mode 100644
index 0000000..79cb246
--- /dev/null
+++ b/tools/browser/data-manager/data-console.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2009 - 2010 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 "data-console.h"
+#include "data-widget.h"
+#include "spec-editor.h"
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../browser-window.h"
+#include "../browser-page.h"
+#include "../browser-stock-icons.h"
+#include "../common/popup-container.h"
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <libgda-ui/libgda-ui.h>
+
+#define PAGE_XML 0
+#define PAGE_DATA 1
+
+typedef enum {
+	LAYOUT_HORIZ,
+	LAYOUT_VERT
+} LayoutType;
+
+struct _DataConsolePrivate {
+	LayoutType layout_type;
+	BrowserConnection *bcnc;
+	GtkWidget *notebook;
+	SpecEditor *sped;
+	GtkWidget *data_box; /* in notebook */
+	GtkWidget *data;
+	GtkActionGroup *agroup;
+
+	GtkToggleButton *params_toggle;
+	GtkWidget *params_top;
+	GtkWidget *params_form_box;
+	GtkWidget *params_form;
+};
+
+static void data_console_class_init (DataConsoleClass *klass);
+static void data_console_init       (DataConsole *dconsole, DataConsoleClass *klass);
+static void data_console_dispose    (GObject *object);
+static void data_console_show_all   (GtkWidget *widget);
+
+/* BrowserPage interface */
+static void                 data_console_page_init (BrowserPageIface *iface);
+static GtkActionGroup      *data_console_page_get_actions_group (BrowserPage *page);
+static const gchar         *data_console_page_get_actions_ui (BrowserPage *page);
+static GtkWidget           *data_console_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
+
+enum {
+	LAST_SIGNAL
+};
+
+static guint data_console_signals[LAST_SIGNAL] = { };
+static GObjectClass *parent_class = NULL;
+
+/*
+ * DataConsole class implementation
+ */
+
+static void
+data_console_class_init (DataConsoleClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	GTK_WIDGET_CLASS (klass)->show_all = data_console_show_all;
+	object_class->dispose = data_console_dispose;
+}
+
+static void
+data_console_show_all (GtkWidget *widget)
+{
+	DataConsole *dconsole = (DataConsole *) widget;
+	GTK_WIDGET_CLASS (parent_class)->show_all (widget);
+
+	if (!gtk_toggle_button_get_active (dconsole->priv->params_toggle))
+		gtk_widget_hide (dconsole->priv->params_top);
+}
+
+static void
+data_console_page_init (BrowserPageIface *iface)
+{
+	iface->i_get_actions_group = data_console_page_get_actions_group;
+	iface->i_get_actions_ui = data_console_page_get_actions_ui;
+	iface->i_get_tab_label = data_console_page_get_tab_label;
+}
+
+static void
+data_console_init (DataConsole *dconsole, DataConsoleClass *klass)
+{
+	dconsole->priv = g_new0 (DataConsolePrivate, 1);
+	dconsole->priv->layout_type = LAYOUT_HORIZ;
+}
+
+static void
+data_console_dispose (GObject *object)
+{
+	DataConsole *dconsole = (DataConsole *) object;
+
+	/* free memory */
+	if (dconsole->priv) {
+		if (dconsole->priv->bcnc)
+			g_object_unref (dconsole->priv->bcnc);
+		if (dconsole->priv->agroup)
+			g_object_unref (dconsole->priv->agroup);
+		g_free (dconsole->priv);
+		dconsole->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GType
+data_console_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo console = {
+			sizeof (DataConsoleClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_console_class_init,
+			NULL,
+			NULL,
+			sizeof (DataConsole),
+			0,
+			(GInstanceInitFunc) data_console_init
+		};
+
+		static GInterfaceInfo page_console = {
+                        (GInterfaceInitFunc) data_console_page_init,
+			NULL,
+                        NULL
+                };
+
+		type = g_type_register_static (GTK_TYPE_VBOX, "DataConsole", &console, 0);
+		g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_console);
+	}
+	return type;
+}
+
+static void editor_clear_clicked_cb (GtkButton *button, DataConsole *dconsole);
+static void variables_clicked_cb (GtkToggleButton *button, DataConsole *dconsole);
+static void execute_clicked_cb (GtkButton *button, DataConsole *dconsole);
+static void spec_editor_toggled_cb (GtkToggleButton *button, DataConsole *dconsole);
+
+/**
+ * data_console_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+data_console_new (BrowserConnection *bcnc)
+{
+	DataConsole *dconsole;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+	dconsole = DATA_CONSOLE (g_object_new (DATA_CONSOLE_TYPE, NULL));
+
+	dconsole->priv->bcnc = g_object_ref (bcnc);
+	
+	/* header */
+        GtkWidget *label;
+	gchar *str;
+	str = g_strdup_printf ("<b>%s</b>", _("Data Manager"));
+	label = cc_gray_bar_new (str);
+	g_free (str);
+        gtk_box_pack_start (GTK_BOX (dconsole), label, FALSE, FALSE, 0);
+        gtk_widget_show (label);
+
+	/* main container */
+	GtkWidget *hpaned, *nb;
+	hpaned = gtk_hpaned_new ();
+	gtk_box_pack_start (GTK_BOX (dconsole), hpaned, TRUE, TRUE, 0);
+
+	/* variables */
+        GtkWidget *vbox, *sw;
+	vbox = gtk_vbox_new (FALSE, 0);
+	dconsole->priv->params_top = vbox;
+	gtk_paned_pack1 (GTK_PANED (hpaned), vbox, FALSE, TRUE);
+
+	label = gtk_label_new ("");
+	str = g_strdup_printf ("<b>%s</b>", _("Variables' values:"));
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	g_free (str);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+	
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	dconsole->priv->params_form_box = gtk_viewport_new (NULL, NULL);
+	gtk_viewport_set_shadow_type (GTK_VIEWPORT (dconsole->priv->params_form_box), GTK_SHADOW_NONE);
+	gtk_container_add (GTK_CONTAINER (sw), dconsole->priv->params_form_box);
+	gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+	gtk_widget_set_size_request (dconsole->priv->params_form_box, 250, -1);
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), VARIABLES_HELP);
+	gtk_misc_set_alignment (GTK_MISC (label), -1, 0.);
+	gtk_container_add (GTK_CONTAINER (dconsole->priv->params_form_box), label);
+	dconsole->priv->params_form = label;
+
+	/* main contents */
+	nb = gtk_notebook_new ();
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
+	gtk_paned_pack2 (GTK_PANED (hpaned), nb, TRUE, FALSE);
+	dconsole->priv->notebook = nb;
+
+	/* editor page */
+	GtkWidget *hbox;
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_notebook_append_page (GTK_NOTEBOOK (nb), vbox, NULL);
+
+	label = gtk_label_new ("");
+	str = g_strdup_printf ("<b>%s</b>", _("SQL code to execute:"));
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	g_free (str);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+	dconsole->priv->sped = spec_editor_new (dconsole->priv->bcnc);
+	gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (dconsole->priv->sped), TRUE, TRUE, 0);
+
+#define DEFAULT_XML \
+"<data>\n" \
+"    <!-- This is an example of XML code, this editor will be replaced by a UI editing,\n" \
+"         but will still be visible and useable for quick access\n" \
+"    -->\n" \
+"\n    <!-- specifies that we want the contents of the 'customers' table -->\n" \
+"    <table name=\"customers\"/>\n" \
+"\n    <!-- specifies that we want the contents of the 'orders' table, where the order ID \n" \
+"         depends on the selected customer's ID -->\n"						\
+"    <table name=\"orders\">\n" \
+"        <link_with>customers</link_with>\n" \
+"    </table>\n" \
+"\n    <!-- specifies that we want the result of the execution of a SELECT query, notice the WHERE clause\n" \
+"          which enables to filter on the previously selected order ID-->\n" \
+"    <query title=\"Order's products\" id=\"products\">\n" \
+"        SELECT p.ref, p.name FROM products p INNER JOIN order_contents o ON (o.product_ref=p.ref) WHERE o order_id=##orders id::int\n" \
+"    </query>\n" \
+"\n    <!-- a more complicated query this time giving all the orders (for any customer) containing the \n" \
+"         selected product from the previous SELECT query-->\n" \
+"    <query title=\"All orders with this product\" id=\"aorders\">\n" \
+"        SELECT distinct c.name, o.creation_date, o.delivery_date FROM customers c INNER JOIN orders o ON (o.customer=c.id) INNER JOIN order_contents oc ON (oc.order_id=o.id) INNER JOIN products p ON (oc product_ref=##products ref::string)\n" \
+"    </query>\n" \
+"</data>"
+
+	spec_editor_set_xml_text (dconsole->priv->sped, DEFAULT_XML);
+
+	/* buttons */
+	GtkWidget *bbox, *button;
+	bbox = gtk_vbutton_box_new ();
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 5);
+
+	button = browser_make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear the editor's\ncontents"));
+	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (editor_clear_clicked_cb), dconsole);
+
+	button = browser_make_small_button (TRUE, _("Variables"), NULL, _("Show variables needed"));
+	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+	dconsole->priv->params_toggle = GTK_TOGGLE_BUTTON (button);
+	g_signal_connect (button, "toggled",
+			  G_CALLBACK (variables_clicked_cb), dconsole);
+
+	button = browser_make_small_button (FALSE, _("Execute"), GTK_STOCK_EXECUTE, _("Execute specified\n"
+										      "data manager"));
+	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (execute_clicked_cb), dconsole);
+
+	button = browser_make_small_button (TRUE, _("View XML"), NULL, _("View specifications\n"
+									 "as XML (advanced)"));
+	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+				      spec_editor_get_mode (dconsole->priv->sped) == SPEC_EDITOR_XML ? 
+				      TRUE : FALSE);
+	g_signal_connect (button, "toggled",
+			  G_CALLBACK (spec_editor_toggled_cb), dconsole);
+
+
+	/* data contents page */
+	GtkWidget *wid;
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	dconsole->priv->data_box = vbox;
+	gtk_notebook_append_page (GTK_NOTEBOOK (nb), vbox, NULL);
+
+	wid = gtk_label_new ("");
+	str = g_strdup_printf ("<b>%s</b>", _("BBB:"));
+	gtk_label_set_markup (GTK_LABEL (wid), str);
+	g_free (str);
+	gtk_misc_set_alignment (GTK_MISC (wid), 0., -1);
+
+	gtk_box_pack_start (GTK_BOX (dconsole->priv->data_box), wid, TRUE, TRUE, 0);
+	dconsole->priv->data = wid;
+
+	/* show everything */
+        gtk_widget_show_all (hpaned);
+	gtk_widget_hide (dconsole->priv->params_top);
+
+	return (GtkWidget*) dconsole;
+}
+
+static void
+execute_clicked_cb (GtkButton *button, DataConsole *dconsole)
+{
+	if (dconsole->priv->agroup) {
+		GtkAction *action;
+		action = gtk_action_group_get_action (dconsole->priv->agroup, "ComposeMode");
+		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+	}
+}
+
+static void
+variables_clicked_cb (GtkToggleButton *button, DataConsole *dconsole)
+{
+	if (gtk_toggle_button_get_active (button))
+		gtk_widget_show (dconsole->priv->params_top);
+	else
+		gtk_widget_hide (dconsole->priv->params_top);
+}
+
+static void
+spec_editor_toggled_cb (GtkToggleButton *button, DataConsole *dconsole)
+{
+	if (gtk_toggle_button_get_active (button))
+		spec_editor_set_mode (dconsole->priv->sped, SPEC_EDITOR_XML);
+	else
+		spec_editor_set_mode (dconsole->priv->sped, SPEC_EDITOR_UI);
+}
+
+static void
+editor_clear_clicked_cb (GtkButton *button, DataConsole *dconsole)
+{
+	spec_editor_set_xml_text (dconsole->priv->sped, "");
+	gtk_widget_grab_focus (GTK_WIDGET (dconsole->priv->sped));
+}
+
+static GtkWidget *
+create_widget (DataConsole *dconsole, GArray *sources_array, GError **error)
+{
+	GtkWidget *sw, *vp, *dwid;
+
+	if (! sources_array) {
+		g_set_error (error, 0, 0,
+			     _("No data source defined"));
+		return NULL;
+	}
+
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+
+	vp = gtk_viewport_new (NULL, NULL);
+	gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
+	gtk_container_add (GTK_CONTAINER (sw), vp);
+
+	dwid = data_widget_new (sources_array);
+	gtk_container_add (GTK_CONTAINER (vp), dwid);
+
+	gtk_widget_show_all (vp);
+	return sw;
+}
+
+
+/*
+ * UI actions
+ */
+static void
+compose_mode_toggled_cb (GtkToggleAction *action, DataConsole *dconsole)
+{
+	gint pagenb;
+
+	pagenb = gtk_notebook_get_current_page (GTK_NOTEBOOK (dconsole->priv->notebook));
+	if (pagenb == PAGE_XML) {
+		/* parse XML and create the data */
+		GArray *sources_array;
+		GError *lerror = NULL;
+		sources_array = spec_editor_get_sources_array (dconsole->priv->sped, &lerror);
+		if (sources_array) {
+			if (dconsole->priv->data) {
+				/* destroy existing data widgets */
+				gtk_widget_destroy (dconsole->priv->data);
+				dconsole->priv->data = NULL;
+			}
+
+			GtkWidget *wid;
+			wid = create_widget (dconsole, sources_array, &lerror);
+			spec_editor_destroy_sources_array (sources_array);
+			if (wid) {
+				dconsole->priv->data = wid;
+				gtk_box_pack_start (GTK_BOX (dconsole->priv->data_box), wid, TRUE, TRUE, 0);
+				gtk_widget_show (wid);
+				pagenb = PAGE_DATA;
+			}
+		}
+		if (lerror) {
+			browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) dconsole),
+					    lerror && lerror->message ? lerror->message :
+					    _("Error parsing XML specifications"));
+			g_clear_error (&lerror);
+		}
+	}
+	else {
+		/* simply change the current page */
+		pagenb = PAGE_XML;
+	}
+
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (dconsole->priv->notebook), pagenb);
+}
+
+static GtkToggleActionEntry ui_actions[] = {
+	{ "ComposeMode", BROWSER_STOCK_BUILDER, N_("_Toggle mode"), NULL, N_("Switch between compose and execute modes"),
+	  G_CALLBACK (compose_mode_toggled_cb), TRUE},
+};
+static const gchar *ui_actions_console =
+	"<ui>"
+        "  <menubar name='MenuBar'>"
+	"    <placeholder name='MenuExtension'>"
+        "      <menu name='Data manager' action='DataManagerMenu'>"
+        "        <menuitem name='ComposeMode' action= 'ComposeMode'/>"
+        "      </menu>"
+	"    </placeholder>"
+        "  </menubar>"
+	"  <toolbar name='ToolBar'>"
+	"    <separator/>"
+	"    <toolitem action='ComposeMode'/>"
+	"  </toolbar>"
+	"</ui>";
+
+static GtkActionGroup *
+data_console_page_get_actions_group (BrowserPage *page)
+{
+	DataConsole *dconsole;
+	dconsole = DATA_CONSOLE (page);
+	if (! dconsole->priv->agroup) {
+		dconsole->priv->agroup = gtk_action_group_new ("QueryExecConsoleActions");
+		gtk_action_group_add_toggle_actions (dconsole->priv->agroup,
+						     ui_actions, G_N_ELEMENTS (ui_actions), page);
+	}
+	return g_object_ref (dconsole->priv->agroup);
+}
+
+static const gchar *
+data_console_page_get_actions_ui (BrowserPage *page)
+{
+	return ui_actions_console;
+}
+
+static GtkWidget *
+data_console_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
+{
+	DataConsole *dconsole;
+	const gchar *tab_name;
+
+	dconsole = DATA_CONSOLE (page);
+	tab_name = _("Data manager");
+	return browser_make_tab_label_with_stock (tab_name,
+						  STOCK_CONSOLE,
+						  out_close_button ? TRUE : FALSE, out_close_button);
+}
+
+/**
+ * data_console_set_text
+ */
+void
+data_console_set_text (DataConsole *console, const gchar *text)
+{
+	g_return_if_fail (IS_DATA_CONSOLE (console));
+
+	spec_editor_set_xml_text (console->priv->sped, text);
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (console->priv->notebook), PAGE_XML);
+}
+
+/**
+ * data_console_get_text
+ */
+gchar *
+data_console_get_text (DataConsole *console)
+{
+	g_return_val_if_fail (IS_DATA_CONSOLE (console), NULL);
+	
+	return spec_editor_get_xml_text (console->priv->sped);
+}
diff --git a/tools/browser/data-manager/data-console.h b/tools/browser/data-manager/data-console.h
new file mode 100644
index 0000000..4b5eeb3
--- /dev/null
+++ b/tools/browser/data-manager/data-console.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 - 2010 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 __DATA_CONSOLE_H__
+#define __DATA_CONSOLE_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define DATA_CONSOLE_TYPE            (data_console_get_type())
+#define DATA_CONSOLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, DATA_CONSOLE_TYPE, DataConsole))
+#define DATA_CONSOLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, DATA_CONSOLE_TYPE, DataConsoleClass))
+#define IS_DATA_CONSOLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, DATA_CONSOLE_TYPE))
+#define IS_DATA_CONSOLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DATA_CONSOLE_TYPE))
+
+typedef struct _DataConsole        DataConsole;
+typedef struct _DataConsoleClass   DataConsoleClass;
+typedef struct _DataConsolePrivate DataConsolePrivate;
+
+struct _DataConsole {
+	GtkVBox               parent;
+	DataConsolePrivate   *priv;
+};
+
+struct _DataConsoleClass {
+	GtkVBoxClass          parent_class;
+};
+
+GType                    data_console_get_type (void) G_GNUC_CONST;
+
+GtkWidget               *data_console_new      (BrowserConnection *bcnc);
+void                     data_console_set_text (DataConsole *console, const gchar *text);
+gchar                   *data_console_get_text (DataConsole *console);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/data-favorite-selector.c b/tools/browser/data-manager/data-favorite-selector.c
new file mode 100644
index 0000000..9450e14
--- /dev/null
+++ b/tools/browser/data-manager/data-favorite-selector.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2009 - 2010 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <libgda/gda-tree.h>
+#include "data-favorite-selector.h"
+#include "../mgr-favorites.h"
+#include <libgda-ui/gdaui-tree-store.h>
+#include "../dnd.h"
+#include "../support.h"
+#include "marshal.h"
+#include "../cc-gray-bar.h"
+#include "../browser-favorites.h"
+#include <gdk/gdkkeysyms.h>
+#include "../common/popup-container.h"
+
+#ifdef HAVE_GTKSOURCEVIEW
+  #ifdef GTK_DISABLE_SINGLE_INCLUDES
+    #undef GTK_DISABLE_SINGLE_INCLUDES
+  #endif
+
+  #include <gtksourceview/gtksourceview.h>
+  #include <gtksourceview/gtksourcelanguagemanager.h>
+  #include <gtksourceview/gtksourcebuffer.h>
+  #include <gtksourceview/gtksourcestyleschememanager.h>
+  #include <gtksourceview/gtksourcestylescheme.h>
+#endif
+
+struct _DataFavoriteSelectorPrivate {
+	BrowserConnection *bcnc;
+	GdaTree *tree;
+	GtkWidget *treeview;
+	guint idle_update_favorites;
+
+	GtkWidget *popup_menu;
+	GtkWidget *popup_properties;
+	GtkWidget *properties_name;
+	GtkWidget *properties_text;
+	gint       properties_id;
+	gint       properties_position;
+	guint      prop_save_timeout;
+};
+
+static void data_favorite_selector_class_init (DataFavoriteSelectorClass *klass);
+static void data_favorite_selector_init       (DataFavoriteSelector *tsel,
+				       DataFavoriteSelectorClass *klass);
+static void data_favorite_selector_dispose   (GObject *object);
+
+static void favorites_changed_cb (BrowserFavorites *bfav, DataFavoriteSelector *tsel);
+
+enum {
+	SELECTION_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint data_favorite_selector_signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+/* columns of the resulting GtkTreeModel */
+enum {
+	COLUMN_POSITION = 0,
+	COLUMN_ICON = 1,
+	COLUMN_CONTENTS = 2,
+	COLUMN_TYPE = 3,
+	COLUMN_ID = 4,
+	COLUMN_NAME = 5,
+};
+
+
+/*
+ * DataFavoriteSelector class implementation
+ */
+
+static void
+data_favorite_selector_class_init (DataFavoriteSelectorClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* signals */
+	data_favorite_selector_signals [SELECTION_CHANGED] =
+                g_signal_new ("selection-changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataFavoriteSelectorClass, selection_changed),
+                              NULL, NULL,
+                              _dm_marshal_VOID__INT_ENUM_STRING, G_TYPE_NONE,
+                              3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING);
+	klass->selection_changed = NULL;
+
+	object_class->dispose = data_favorite_selector_dispose;
+}
+
+
+static void
+data_favorite_selector_init (DataFavoriteSelector *tsel,	DataFavoriteSelectorClass *klass)
+{
+	tsel->priv = g_new0 (DataFavoriteSelectorPrivate, 1);
+	tsel->priv->idle_update_favorites = 0;
+	tsel->priv->prop_save_timeout = 0;
+}
+
+static void
+data_favorite_selector_dispose (GObject *object)
+{
+	DataFavoriteSelector *tsel = (DataFavoriteSelector *) object;
+
+	/* free memory */
+	if (tsel->priv) {
+		if (tsel->priv->idle_update_favorites != 0)
+			g_source_remove (tsel->priv->idle_update_favorites);
+		if (tsel->priv->tree)
+			g_object_unref (tsel->priv->tree);
+
+		if (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);
+		}
+		
+		if (tsel->priv->popup_properties)
+			gtk_widget_destroy (tsel->priv->popup_properties);
+		if (tsel->priv->popup_menu)
+			gtk_widget_destroy (tsel->priv->popup_menu);
+		if (tsel->priv->prop_save_timeout)
+			g_source_remove (tsel->priv->prop_save_timeout);
+
+		g_free (tsel->priv);
+		tsel->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GType
+data_favorite_selector_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (DataFavoriteSelectorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_favorite_selector_class_init,
+			NULL,
+			NULL,
+			sizeof (DataFavoriteSelector),
+			0,
+			(GInstanceInitFunc) data_favorite_selector_init
+		};
+		type = g_type_register_static (GTK_TYPE_VBOX, "DataFavoriteSelector",
+					       &info, 0);
+	}
+	return type;
+}
+
+static gboolean
+key_press_event_cb (GtkTreeView *treeview, GdkEventKey *event, DataFavoriteSelector *tsel)
+{
+	if (event->keyval == GDK_Delete) {
+		GtkTreeModel *model;
+		GtkTreeSelection *select;
+		GtkTreeIter iter;
+		
+		select = gtk_tree_view_get_selection (treeview);
+		if (gtk_tree_selection_get_selected (select, &model, &iter)) {
+			BrowserFavorites *bfav;
+			BrowserFavoritesAttributes fav;
+			GError *lerror = NULL;
+
+			memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+			gtk_tree_model_get (model, &iter,
+					    COLUMN_ID, &(fav.id), -1);
+			bfav = browser_connection_get_favorites (tsel->priv->bcnc);
+			if (!browser_favorites_delete (bfav, 0, &fav, NULL)) {
+				browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*)tsel),
+						    _("Could not remove favorite: %s"),
+						    lerror && lerror->message ? lerror->message : _("No detail"));
+				if (lerror)
+					g_error_free (lerror);
+			}
+		}
+		
+		return TRUE;
+	}
+	return FALSE; /* not handled */
+}
+
+
+static void
+selection_changed_cb (GtkTreeView *treeview, GtkTreePath *path,
+		      GtkTreeViewColumn *column, DataFavoriteSelector *tsel)
+{
+	GtkTreeModel *model;
+	GtkTreeSelection *select;
+	GtkTreeIter iter;
+	
+	select = gtk_tree_view_get_selection (treeview);
+	if (gtk_tree_selection_get_selected (select, &model, &iter)) {
+		gchar *str;
+		guint type;
+		gint fav_id;
+		gtk_tree_model_get (model, &iter,
+				    COLUMN_ID, &fav_id,
+				    COLUMN_TYPE, &type,
+				    COLUMN_CONTENTS, &str, -1);
+		g_signal_emit (tsel, data_favorite_selector_signals [SELECTION_CHANGED], 0, fav_id, type, str);
+		g_free (str);
+	}
+}
+
+static gboolean
+prop_save_timeout (DataFavoriteSelector *tsel)
+{
+	BrowserFavorites *bfav;
+	BrowserFavoritesAttributes fav;
+	GError *error = NULL;
+
+	memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+	fav.id = tsel->priv->properties_id;
+	fav.type = BROWSER_FAVORITES_DATA_MANAGERS;
+	fav.name = (gchar*) gtk_entry_get_text (GTK_ENTRY (tsel->priv->properties_name));
+	fav.descr = NULL;
+
+	GtkTextBuffer *buffer;
+	GtkTextIter start, end;
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tsel->priv->properties_text));
+	gtk_text_buffer_get_start_iter (buffer, &start);
+	gtk_text_buffer_get_end_iter (buffer, &end);
+	fav.contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+	bfav = browser_connection_get_favorites (tsel->priv->bcnc);
+	if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_DATA_MANAGERS, tsel->priv->properties_position, &error)) {
+		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
+				    _("Could not add favorite: %s"),
+				    error && error->message ? error->message : _("No detail"));
+		if (error)
+			g_error_free (error);
+	}
+
+	g_free (fav.contents);
+	tsel->priv->prop_save_timeout = 0;
+	return FALSE; /* remove timeout */
+}
+
+static void
+property_changed_cb (GtkWidget *multiple, DataFavoriteSelector *tsel)
+{
+	if (tsel->priv->prop_save_timeout)
+		g_source_remove (tsel->priv->prop_save_timeout);
+	tsel->priv->prop_save_timeout = g_timeout_add (100, (GSourceFunc) prop_save_timeout, tsel);
+}
+
+static void
+properties_activated_cb (GtkMenuItem *mitem, DataFavoriteSelector *tsel)
+{
+	if (! tsel->priv->popup_properties) {
+		GtkWidget *pcont, *vbox, *hbox, *label, *entry, *text, *table;
+		gchar *str;
+		
+		pcont = popup_container_new (GTK_WIDGET (mitem));
+		vbox = gtk_vbox_new (FALSE, 0);
+		gtk_container_add (GTK_CONTAINER (pcont), vbox);
+		
+		label = gtk_label_new ("");
+		str = g_strdup_printf ("<b>%s:</b>", _("Favorite's properties"));
+		gtk_label_set_markup (GTK_LABEL (label), str);
+		g_free (str);
+		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+		gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+		
+		hbox = gtk_hbox_new (FALSE, 0); /* HIG */
+		gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
+		label = gtk_label_new ("      ");
+		gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+		
+		table = gtk_table_new (2, 2, FALSE);
+		gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
+		
+		label = gtk_label_new ("");
+		str = g_strdup_printf ("<b>%s:</b>", _("Name"));
+		gtk_label_set_markup (GTK_LABEL (label), str);
+		g_free (str);
+		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+		gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+		
+		label = gtk_label_new ("");
+		str = g_strdup_printf ("<b>%s:</b>", _("Specifications"));
+		gtk_label_set_markup (GTK_LABEL (label), str);
+		g_free (str);
+		gtk_misc_set_alignment (GTK_MISC (label), 0., 0.);
+		gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+		
+		entry = gtk_entry_new ();
+		gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+		tsel->priv->properties_name = entry;
+		g_signal_connect (entry, "changed",
+				  G_CALLBACK (property_changed_cb), tsel);
+		
+#ifdef HAVE_GTKSOURCEVIEW
+		text = gtk_source_view_new ();
+		gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text))),
+							TRUE);
+		gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text))),
+						gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (),
+											  "xml"));
+#else
+		text = gtk_text_view_new ();
+#endif
+		GtkWidget *sw;
+		gtk_widget_set_size_request (GTK_WIDGET (text), 400, 300);
+
+		sw = gtk_scrolled_window_new (NULL, NULL);
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+						GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+		gtk_table_attach_defaults (GTK_TABLE (table), sw, 1, 2, 1, 2);
+		gtk_container_add (GTK_CONTAINER (sw), text);
+		tsel->priv->properties_text = text;
+		g_signal_connect (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)), "changed",
+				  G_CALLBACK (property_changed_cb), tsel);
+
+		tsel->priv->popup_properties = pcont;
+		gtk_widget_show_all (vbox);
+	}
+
+	/* adjust contents */
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tsel->priv->treeview));
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gchar *name, *contents;
+		GtkTextBuffer *buffer;
+		
+		gtk_tree_model_get (model, &iter,
+				    COLUMN_ID, &(tsel->priv->properties_id),
+				    COLUMN_POSITION, &(tsel->priv->properties_position),
+				    COLUMN_NAME, &name,
+				    COLUMN_CONTENTS, &contents, -1);
+
+		g_signal_handlers_block_by_func (tsel->priv->properties_name,
+						 G_CALLBACK (property_changed_cb), tsel);
+		gtk_entry_set_text (GTK_ENTRY (tsel->priv->properties_name), name);
+		g_signal_handlers_unblock_by_func (tsel->priv->properties_name,
+						   G_CALLBACK (property_changed_cb), tsel);
+		g_free (name);
+
+		g_signal_handlers_block_by_func (tsel->priv->properties_text,
+						 G_CALLBACK (property_changed_cb), tsel);
+		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tsel->priv->properties_text));
+		if (contents)
+			gtk_text_buffer_set_text (buffer, contents, -1);
+		else
+			gtk_text_buffer_set_text (buffer, "", -1);
+		g_signal_handlers_unblock_by_func (tsel->priv->properties_text,
+						   G_CALLBACK (property_changed_cb), tsel);
+		g_free (contents);
+
+		gtk_widget_show (tsel->priv->popup_properties);
+	}
+}
+
+static void
+do_popup_menu (GtkWidget *widget, GdkEventButton *event, DataFavoriteSelector *tsel)
+{
+	int button, event_time;
+
+	if (! tsel->priv->popup_menu) {
+		GtkWidget *menu, *mitem;
+		
+		menu = gtk_menu_new ();
+		g_signal_connect (menu, "deactivate", 
+				  G_CALLBACK (gtk_widget_hide), NULL);
+		
+		mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL);
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+		gtk_widget_show (mitem);
+		g_signal_connect (mitem, "activate",
+				  G_CALLBACK (properties_activated_cb), tsel);
+
+		tsel->priv->popup_menu = menu;
+	}
+		
+	if (event) {
+		button = event->button;
+		event_time = event->time;
+	}
+	else {
+		button = 0;
+		event_time = gtk_get_current_event_time ();
+	}
+
+	gtk_menu_popup (GTK_MENU (tsel->priv->popup_menu), NULL, NULL, NULL, NULL, 
+			button, event_time);
+}
+
+
+static gboolean
+popup_menu_cb (GtkWidget *widget, DataFavoriteSelector *tsel)
+{
+	do_popup_menu (widget, NULL, tsel);
+	return TRUE;
+}
+
+static gboolean
+button_press_event_cb (GtkTreeView *treeview, GdkEventButton *event, DataFavoriteSelector *tsel)
+{
+	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+		do_popup_menu ((GtkWidget*) treeview, event, tsel);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+			    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
+static gboolean idle_update_favorites (DataFavoriteSelector *tsel);
+static gboolean tree_store_drag_drop_cb (GdauiTreeStore *store, const gchar *path,
+					 GtkSelectionData *selection_data, DataFavoriteSelector *tsel);
+static gboolean tree_store_drag_can_drag_cb (GdauiTreeStore *store, const gchar *path,
+					     DataFavoriteSelector *tsel);
+static gboolean tree_store_drag_get_cb (GdauiTreeStore *store, const gchar *path,
+					GtkSelectionData *selection_data, DataFavoriteSelector *tsel);
+static void trash_data_received_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+				    GtkSelectionData *selection_data, guint target_type, guint time,
+				    DataFavoriteSelector *tsel);
+/**
+ * data_favorite_selector_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+data_favorite_selector_new (BrowserConnection *bcnc)
+{
+	DataFavoriteSelector *tsel;
+	GdaTreeManager *manager;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+	tsel = DATA_FAVORITE_SELECTOR (g_object_new (DATA_FAVORITE_SELECTOR_TYPE, NULL));
+
+	tsel->priv->bcnc = g_object_ref (bcnc);
+	g_signal_connect (browser_connection_get_favorites (tsel->priv->bcnc), "favorites-changed",
+			  G_CALLBACK (favorites_changed_cb), tsel);
+	
+	/* create tree managers */
+	tsel->priv->tree = gda_tree_new ();
+	manager = mgr_favorites_new (bcnc, BROWSER_FAVORITES_DATA_MANAGERS, ORDER_KEY_DATA_MANAGERS);
+        gda_tree_add_manager (tsel->priv->tree, manager);
+	g_object_unref (manager);
+
+	/* update the tree's contents */
+	if (! gda_tree_update_all (tsel->priv->tree, NULL)) {
+		if (tsel->priv->idle_update_favorites == 0)
+			tsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, tsel);
+	}
+
+	/* header */
+	GtkWidget *label;
+	gchar *str;
+	str = g_strdup_printf ("<b>%s</b>", _("Favorites"));
+	label = cc_gray_bar_new (str);
+	g_free (str);
+	cc_gray_bar_set_icon_from_pixbuf (CC_GRAY_BAR (label), browser_get_pixbuf_icon (BROWSER_ICON_BOOKMARK));
+        gtk_box_pack_start (GTK_BOX (tsel), label, FALSE, FALSE, 0);
+        gtk_widget_show (label);
+
+	/* tree model */
+	GtkTreeModel *model;
+	GtkWidget *treeview;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+
+	model = gdaui_tree_store_new (tsel->priv->tree, 6,
+				      G_TYPE_INT, MGR_FAVORITES_POSITION_ATT_NAME,
+				      G_TYPE_OBJECT, "icon",
+				      G_TYPE_STRING, MGR_FAVORITES_CONTENTS_ATT_NAME,
+				      G_TYPE_UINT, MGR_FAVORITES_TYPE_ATT_NAME,
+				      G_TYPE_INT, MGR_FAVORITES_ID_ATT_NAME,
+				      G_TYPE_STRING, MGR_FAVORITES_NAME_ATT_NAME);
+	treeview = gtk_tree_view_new_with_model (model);
+	tsel->priv->treeview = treeview;
+	g_object_unref (model);
+
+	/* icon */
+	column = gtk_tree_view_column_new ();
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
+	g_object_set ((GObject*) renderer, "yalign", 0., NULL);
+
+	/* text */
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) cell_data_func,
+						 NULL, NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+	
+	/* scrolled window packing */
+	GtkWidget *sw;
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+					     GTK_SHADOW_ETCHED_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_NEVER,
+					GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (sw), treeview);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);	
+
+	gtk_box_pack_start (GTK_BOX (tsel), sw, TRUE, TRUE, 0);
+	gtk_widget_show_all (sw);
+	g_signal_connect (G_OBJECT (treeview), "row-activated",
+			  G_CALLBACK (selection_changed_cb), tsel);
+	g_signal_connect (G_OBJECT (treeview), "key-press-event",
+			  G_CALLBACK (key_press_event_cb), tsel);
+	g_signal_connect (G_OBJECT (treeview), "popup-menu",
+			  G_CALLBACK (popup_menu_cb), tsel);
+	g_signal_connect (G_OBJECT (treeview), "button-press-event",
+			  G_CALLBACK (button_press_event_cb), tsel);
+
+	/* DnD */
+	gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview), dbo_table, G_N_ELEMENTS (dbo_table),
+					      GDK_ACTION_COPY);
+	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview), GDK_BUTTON1_MASK,
+						dbo_table, G_N_ELEMENTS (dbo_table),
+						GDK_ACTION_COPY | GDK_ACTION_MOVE);
+	g_signal_connect (model, "drag-drop",
+			  G_CALLBACK (tree_store_drag_drop_cb), tsel);
+	g_signal_connect (model, "drag-can-drag",
+			  G_CALLBACK (tree_store_drag_can_drag_cb), tsel);
+	g_signal_connect (model, "drag-get",
+			  G_CALLBACK (tree_store_drag_get_cb), tsel);
+
+	return (GtkWidget*) tsel;
+}
+
+static void
+cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+		GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+	gchar *name;
+	gchar *tmp;
+
+	gtk_tree_model_get (tree_model, iter,
+			    COLUMN_NAME, &name, -1);
+	tmp = g_markup_printf_escaped ("%s", name);
+	g_free (name);
+
+	g_object_set ((GObject*) cell, "markup", tmp, NULL);
+	g_free (tmp);
+}
+
+
+static gboolean
+idle_update_favorites (DataFavoriteSelector *tsel)
+{
+	gboolean done;
+	done = gda_tree_update_all (tsel->priv->tree, NULL);
+	if (done)
+		tsel->priv->idle_update_favorites = 0;
+	else
+		tsel->priv->idle_update_favorites = g_timeout_add_seconds (1, (GSourceFunc) idle_update_favorites,
+									   tsel);
+	return FALSE;
+}
+
+static gboolean
+tree_store_drag_drop_cb (GdauiTreeStore *store, const gchar *path, GtkSelectionData *selection_data,
+			 DataFavoriteSelector *tsel)
+{
+	BrowserFavorites *bfav;
+	BrowserFavoritesAttributes fav;
+	GError *error = NULL;
+	gint pos;
+	gboolean retval = TRUE;
+	gint id;
+	bfav = browser_connection_get_favorites (tsel->priv->bcnc);
+
+#if GTK_CHECK_VERSION(2,18,0)
+	id = browser_favorites_find (bfav, 0, (gchar*) gtk_selection_data_get_data (selection_data),
+				     &fav, NULL);
+#else
+	id = browser_favorites_find (bfav, 0, (gchar*) selection_data->data, &fav, NULL);
+#endif
+	if (id < 0) {
+		memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+		fav.id = -1;
+		fav.type = BROWSER_FAVORITES_DATA_MANAGERS;
+		fav.name = _("Unnamed data manager");
+		fav.descr = NULL;
+#if GTK_CHECK_VERSION(2,18,0)
+		fav.contents = (gchar*) gtk_selection_data_get_data (selection_data);
+#else
+		fav.contents = (gchar*) selection_data->data;
+#endif
+	}
+
+	pos = atoi (path);
+	/*g_print ("%s() path => %s, pos: %d\n", __FUNCTION__, path, pos);*/
+	
+	if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_DATA_MANAGERS, pos, &error)) {
+		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
+				    _("Could not add favorite: %s"),
+				    error && error->message ? error->message : _("No detail"));
+		if (error)
+			g_error_free (error);
+		retval = FALSE;
+	}
+	
+	if (id >= 0)
+		browser_favorites_reset_attributes (&fav);
+
+	return retval;
+}
+
+static gboolean
+tree_store_drag_can_drag_cb (GdauiTreeStore *store, const gchar *path, DataFavoriteSelector *tsel)
+{
+	GdaTreeNode *node;
+	node = gda_tree_get_node (tsel->priv->tree, path, FALSE);
+	if (node) {
+		const GValue *cvalue;
+		cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
+		if (cvalue)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+tree_store_drag_get_cb (GdauiTreeStore *store, const gchar *path, GtkSelectionData *selection_data,
+			DataFavoriteSelector *tsel)
+{
+	GdaTreeNode *node;
+	node = gda_tree_get_node (tsel->priv->tree, path, FALSE);
+	if (node) {
+		const GValue *cvalue;
+		cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
+		if (cvalue) {
+			const gchar *str;
+			str = g_value_get_string (cvalue);
+#if GTK_CHECK_VERSION(2,18,0)
+			gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
+						8, (guchar*) str, strlen (str));
+#else
+			gtk_selection_data_set (selection_data, selection_data->target,
+						8, (guchar*) str, strlen (str));
+#endif
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static void
+favorites_changed_cb (BrowserFavorites *bfav, DataFavoriteSelector *tsel)
+{
+	if (! gda_tree_update_all (tsel->priv->tree, NULL)) {
+		if (tsel->priv->idle_update_favorites == 0)
+			tsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, tsel);
+
+	}
+}
diff --git a/tools/browser/data-manager/data-favorite-selector.h b/tools/browser/data-manager/data-favorite-selector.h
new file mode 100644
index 0000000..aa7a76d
--- /dev/null
+++ b/tools/browser/data-manager/data-favorite-selector.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 - 2010 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 __DATA_FAVORITE_SELECTOR_H__
+#define __DATA_FAVORITE_SELECTOR_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define DATA_FAVORITE_SELECTOR_TYPE            (data_favorite_selector_get_type())
+#define DATA_FAVORITE_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, DATA_FAVORITE_SELECTOR_TYPE, DataFavoriteSelector))
+#define DATA_FAVORITE_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, DATA_FAVORITE_SELECTOR_TYPE, DataFavoriteSelectorClass))
+#define IS_DATA_FAVORITE_SELECTOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, DATA_FAVORITE_SELECTOR_TYPE))
+#define IS_DATA_FAVORITE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DATA_FAVORITE_SELECTOR_TYPE))
+
+typedef struct _DataFavoriteSelector        DataFavoriteSelector;
+typedef struct _DataFavoriteSelectorClass   DataFavoriteSelectorClass;
+typedef struct _DataFavoriteSelectorPrivate DataFavoriteSelectorPrivate;
+
+struct _DataFavoriteSelector {
+	GtkVBox               parent;
+	DataFavoriteSelectorPrivate *priv;
+};
+
+struct _DataFavoriteSelectorClass {
+	GtkVBoxClass          parent_class;
+
+	void                (*selection_changed) (DataFavoriteSelector *sel, gint fav_id,
+						  BrowserFavoritesType fav_type, const gchar *fav_contents);
+};
+
+GType                    data_favorite_selector_get_type (void) G_GNUC_CONST;
+
+GtkWidget               *data_favorite_selector_new      (BrowserConnection *bcnc);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/data-manager-perspective.c b/tools/browser/data-manager/data-manager-perspective.c
new file mode 100644
index 0000000..6fe4650
--- /dev/null
+++ b/tools/browser/data-manager/data-manager-perspective.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2009 - 2010 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 "data-manager-perspective.h"
+#include "data-console.h"
+#include "../browser-favorites.h"
+#include "../browser-window.h"
+#include "../browser-page.h"
+#include "data-favorite-selector.h"
+
+/* 
+ * Main static functions 
+ */
+static void data_manager_perspective_class_init (DataManagerPerspectiveClass *klass);
+static void data_manager_perspective_init (DataManagerPerspective *stmt);
+static void data_manager_perspective_dispose (GObject *object);
+
+/* BrowserPerspective interface */
+static void                 data_manager_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static GtkActionGroup      *data_manager_perspective_get_actions_group (BrowserPerspective *perspective);
+static const gchar         *data_manager_perspective_get_actions_ui (BrowserPerspective *perspective);
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+struct _DataManagerPerspectivePriv {
+	GtkWidget *notebook;
+	BrowserWindow *bwin;
+        BrowserConnection *bcnc;
+};
+
+GType
+data_manager_perspective_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (DataManagerPerspectiveClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_manager_perspective_class_init,
+			NULL,
+			NULL,
+			sizeof (DataManagerPerspective),
+			0,
+			(GInstanceInitFunc) data_manager_perspective_init
+		};
+
+		static GInterfaceInfo perspective_info = {
+                        (GInterfaceInitFunc) data_manager_perspective_perspective_init,
+			NULL,
+                        NULL
+                };
+		
+		g_static_mutex_lock (&registering);
+		if (type == 0) {
+			type = g_type_register_static (GTK_TYPE_VBOX, "DataManagerPerspective", &info, 0);
+			g_type_add_interface_static (type, BROWSER_PERSPECTIVE_TYPE, &perspective_info);
+		}
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+data_manager_perspective_class_init (DataManagerPerspectiveClass * klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = data_manager_perspective_dispose;
+}
+
+static void
+data_manager_perspective_perspective_init (BrowserPerspectiveIface *iface)
+{
+	iface->i_get_actions_group = data_manager_perspective_get_actions_group;
+	iface->i_get_actions_ui = data_manager_perspective_get_actions_ui;
+}
+
+
+static void
+data_manager_perspective_init (DataManagerPerspective *perspective)
+{
+	perspective->priv = g_new0 (DataManagerPerspectivePriv, 1);
+}
+
+static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
+                                      const gchar *selection, DataManagerPerspective *perspective);
+static void nb_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page, gint page_num,
+                               DataManagerPerspective *perspective);
+static void nb_page_removed_cb (GtkNotebook *nb, GtkNotebookPage *page, gint page_num,
+                               DataManagerPerspective *perspective);
+static void close_button_clicked_cb (GtkWidget *wid, GtkWidget *page_widget);
+
+/**
+ * data_manager_perspective_new
+ *
+ * Creates new #BrowserPerspective widget which 
+ */
+BrowserPerspective *
+data_manager_perspective_new (BrowserWindow *bwin)
+{
+	BrowserConnection *bcnc;
+	BrowserPerspective *bpers;
+	DataManagerPerspective *perspective;
+	bpers = (BrowserPerspective*) g_object_new (TYPE_DATA_MANAGER_PERSPECTIVE, NULL);
+	perspective = (DataManagerPerspective*) bpers;
+
+	perspective->priv->bwin = bwin;
+        bcnc = browser_window_get_connection (bwin);
+        perspective->priv->bcnc = g_object_ref (bcnc);
+
+	/* contents */
+        GtkWidget *paned, *nb, *wid;
+        paned = gtk_hpaned_new ();
+        wid = data_favorite_selector_new (bcnc);
+	g_signal_connect (wid, "selection-changed",
+			  G_CALLBACK (fav_selection_changed_cb), bpers);
+        gtk_paned_pack1 (GTK_PANED (paned), wid, FALSE, TRUE);
+
+	nb = gtk_notebook_new ();
+        perspective->priv->notebook = nb;
+        gtk_paned_pack2 (GTK_PANED (paned), nb, TRUE, TRUE);
+        gtk_notebook_set_scrollable (GTK_NOTEBOOK (nb), TRUE);
+        gtk_notebook_popup_enable (GTK_NOTEBOOK (nb));
+        g_signal_connect (G_OBJECT (nb), "switch-page",
+                          G_CALLBACK (nb_switch_page_cb), perspective);
+        g_signal_connect (G_OBJECT (nb), "page-removed",
+                          G_CALLBACK (nb_page_removed_cb), perspective);
+
+	GtkWidget *page, *tlabel, *button;
+	page = data_console_new (bcnc);
+	tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
+        g_signal_connect (button, "clicked",
+                          G_CALLBACK (close_button_clicked_cb), page);
+
+        gtk_notebook_append_page (GTK_NOTEBOOK (nb), page, tlabel);
+
+        gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (nb), page, TRUE);
+        gtk_notebook_set_group (GTK_NOTEBOOK (nb), bcnc + 0x02); /* add 0x01 to differentiate it from SchemaBrowser */
+        gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (perspective->priv->notebook), page,
+                                         TRUE);
+
+	tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
+        gtk_notebook_set_menu_label (GTK_NOTEBOOK (nb), page, tlabel);
+
+	gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
+	gtk_widget_show_all (paned);
+
+	return bpers;
+}
+
+static DataConsole *
+add_new_data_console (BrowserPerspective *bpers)
+{
+	GtkWidget *page, *tlabel, *button;
+	DataManagerPerspective *perspective;
+	gint page_nb;
+
+	perspective = DATA_MANAGER_PERSPECTIVE (bpers);
+	page = data_console_new (perspective->priv->bcnc);
+	tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
+        g_signal_connect (button, "clicked",
+                          G_CALLBACK (close_button_clicked_cb), page);
+
+        page_nb = gtk_notebook_append_page (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+	gtk_widget_show (page);
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (perspective->priv->notebook), page_nb);
+
+        gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (perspective->priv->notebook), page,
+                                         TRUE);
+
+	tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
+        gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+
+	return DATA_CONSOLE (page);
+}
+
+static void
+fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
+                          const gchar *selection, DataManagerPerspective *perspective)
+{
+	/* find or create page for this favorite */
+        GtkWidget *page_contents;
+	gint current_page;
+	DataConsole *page_to_reuse = NULL;
+
+	current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (perspective->priv->notebook));
+	if (current_page >= 0) {
+		page_contents = gtk_notebook_get_nth_page (GTK_NOTEBOOK (perspective->priv->notebook), current_page);
+		
+		if (IS_DATA_CONSOLE (page_contents)) {
+			gchar *text;
+			text = data_console_get_text (DATA_CONSOLE (page_contents));
+			if (!text || !*text)
+				page_to_reuse = DATA_CONSOLE (page_contents);
+		}
+	}
+
+	if (! page_to_reuse)
+		page_to_reuse = add_new_data_console ((BrowserPerspective*) perspective);
+	
+	data_console_set_text (page_to_reuse, selection);
+}
+
+static void
+nb_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page, gint page_num,
+                   DataManagerPerspective *perspective)
+{
+        GtkWidget *page_contents;
+        GtkActionGroup *actions = NULL;
+        const gchar *ui = NULL;
+
+        page_contents = gtk_notebook_get_nth_page (nb, page_num);
+        if (IS_BROWSER_PAGE (page_contents)) {
+                actions = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
+                ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
+        }
+        browser_window_customize_perspective_ui (perspective->priv->bwin,
+                                                 BROWSER_PERSPECTIVE (perspective), actions,
+                                                 ui);
+        if (actions)
+                g_object_unref (actions);
+}
+
+static void
+nb_page_removed_cb (GtkNotebook *nb, GtkNotebookPage *page, gint page_num,
+                    DataManagerPerspective *perspective)
+{
+        if (gtk_notebook_get_n_pages (nb) == 0) {
+                browser_window_customize_perspective_ui (perspective->priv->bwin,
+                                                         BROWSER_PERSPECTIVE (perspective),
+                                                         NULL, NULL);
+        }
+}
+
+static void
+close_button_clicked_cb (GtkWidget *wid, GtkWidget *page_widget)
+{
+        gtk_widget_destroy (page_widget);
+}
+
+static void
+data_manager_perspective_dispose (GObject *object)
+{
+	DataManagerPerspective *perspective;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (IS_DATA_MANAGER_PERSPECTIVE (object));
+
+	perspective = DATA_MANAGER_PERSPECTIVE (object);
+	if (perspective->priv) {
+                if (perspective->priv->bcnc)
+                        g_object_unref (perspective->priv->bcnc);
+
+                g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
+                                                      G_CALLBACK (nb_page_removed_cb), perspective);
+                g_free (perspective->priv);
+                perspective->priv = NULL;
+        }
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+static void
+manager_new_cb (GtkAction *action, BrowserPerspective *bpers)
+{
+	add_new_data_console (bpers);
+}
+
+static GtkActionEntry ui_actions[] = {
+        { "DataManagerMenu", NULL, "_Manager", NULL, "ManagerMenu", NULL },
+        { "NewDataManager", GTK_STOCK_NEW, "_New data manager", NULL, "New data manager",
+          G_CALLBACK (manager_new_cb)},
+};
+
+static const gchar *ui_actions_info =
+        "<ui>"
+        "  <menubar name='MenuBar'>"
+	"    <placeholder name='MenuExtension'>"
+        "      <menu name='Data manager' action='DataManagerMenu'>"
+        "        <menuitem name='NewDataManager' action= 'NewDataManager'/>"
+        "      </menu>"
+	"    </placeholder>"
+        "  </menubar>"
+        "  <toolbar name='ToolBar'>"
+        "    <separator/>"
+        "    <toolitem action='NewDataManager'/>"
+        "  </toolbar>"
+        "</ui>";
+
+static GtkActionGroup *
+data_manager_perspective_get_actions_group (BrowserPerspective *bpers)
+{
+	GtkActionGroup *agroup;
+	agroup = gtk_action_group_new ("DataManagerActions");
+	gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
+	
+	return agroup;
+}
+
+static const gchar *
+data_manager_perspective_get_actions_ui (BrowserPerspective *bpers)
+{
+	return ui_actions_info;
+}
diff --git a/tools/browser/data-manager/data-manager-perspective.h b/tools/browser/data-manager/data-manager-perspective.h
new file mode 100644
index 0000000..f98879a
--- /dev/null
+++ b/tools/browser/data-manager/data-manager-perspective.h
@@ -0,0 +1,56 @@
+/* 
+ * Copyright (C) 2009 - 2010 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 __DATA_MANAGER_PERSPECTIVE_H_
+#define __DATA_MANAGER_PERSPECTIVE_H_
+
+#include <glib-object.h>
+#include "../browser-perspective.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_DATA_MANAGER_PERSPECTIVE          (data_manager_perspective_get_type())
+#define DATA_MANAGER_PERSPECTIVE(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, data_manager_perspective_get_type(), DataManagerPerspective)
+#define DATA_MANAGER_PERSPECTIVE_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, data_manager_perspective_get_type (), DataManagerPerspectiveClass)
+#define IS_DATA_MANAGER_PERSPECTIVE(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, data_manager_perspective_get_type ())
+
+typedef struct _DataManagerPerspective DataManagerPerspective;
+typedef struct _DataManagerPerspectiveClass DataManagerPerspectiveClass;
+typedef struct _DataManagerPerspectivePriv DataManagerPerspectivePriv;
+
+/* struct for the object's data */
+struct _DataManagerPerspective
+{
+	GtkVBox                     object;
+	DataManagerPerspectivePriv *priv;
+};
+
+/* struct for the object's class */
+struct _DataManagerPerspectiveClass
+{
+	GtkVBoxClass                parent_class;
+};
+
+GType                data_manager_perspective_get_type               (void) G_GNUC_CONST;
+BrowserPerspective  *data_manager_perspective_new                    (BrowserWindow *bwin);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/data-source.c b/tools/browser/data-manager/data-source.c
new file mode 100644
index 0000000..9c3521b
--- /dev/null
+++ b/tools/browser/data-manager/data-source.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2009 - 2010 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-connection.h"
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
+#include "support.h"
+#include "marshal.h"
+#include <sql-parser/gda-sql-parser.h>
+#include <libgda/gda-data-model-extra.h>
+#include <libgda/gda-sql-builder.h>
+
+#include "data-source.h"
+
+
+/* signals */
+enum {
+	EXEC_STARTED,
+	EXEC_FINISHED,
+	LAST_SIGNAL
+};
+
+gint data_source_signals [LAST_SIGNAL] = {0, 0};
+
+/* 
+ * Main static functions 
+ */
+static void data_source_class_init (DataSourceClass *klass);
+static void data_source_init (DataSource *source);
+static void data_source_dispose (GObject *object);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+struct _DataSourcePrivate {
+	BrowserConnection *bcnc;
+	gchar             *title;
+	gchar             *id;
+	GError            *init_error;
+	GArray            *export_names; /* array of strings, memory allocated in export_columns */
+	GHashTable        *export_columns; /* key = export name, value = column number */
+
+	gchar             *select_sql;
+	guint              exec_id;
+
+	GdaStatement      *stmt;
+	GdaSet            *params;
+	gboolean           need_rerun; /* set to %TRUE if @params has changed since the last exec */
+
+	GError            *exec_error;
+
+	GdaDataModel      *model;
+};
+
+GType
+data_source_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (DataSourceClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_source_class_init,
+			NULL,
+			NULL,
+			sizeof (DataSource),
+			0,
+			(GInstanceInitFunc) data_source_init
+		};
+
+		
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (G_TYPE_OBJECT, "DataSource", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+data_source_class_init (DataSourceClass *klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* signals */
+	data_source_signals [EXEC_STARTED] =
+                g_signal_new ("execution-started",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataSourceClass, execution_started),
+                              NULL, NULL,
+                              _dm_marshal_VOID__VOID, G_TYPE_NONE, 0);
+	data_source_signals [EXEC_FINISHED] =
+                g_signal_new ("execution-finished",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataSourceClass, execution_finished),
+                              NULL, NULL,
+                              _dm_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+        klass->execution_started = NULL;
+        klass->execution_finished = NULL;
+
+	object_class->dispose = data_source_dispose;
+}
+
+static void
+data_source_init (DataSource *source)
+{
+	source->priv = g_new0 (DataSourcePrivate, 1);
+	source->priv->bcnc = NULL;
+	source->priv->need_rerun = FALSE;
+}
+
+static void
+params_changed_cb (GdaSet *params, GdaHolder *holder, DataSource *source)
+{
+	source->priv->need_rerun = TRUE;
+}
+
+static void
+data_source_dispose (GObject *object)
+{
+	DataSource *source;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (IS_DATA_SOURCE (object));
+
+	source = DATA_SOURCE (object);
+	if (source->priv) {
+		if (source->priv->bcnc)
+			g_object_unref (source->priv->bcnc);
+		g_clear_error (& source->priv->init_error);
+		if (source->priv->stmt)
+			g_object_unref (source->priv->stmt);
+		if (source->priv->params) {
+			g_signal_handlers_disconnect_by_func (source->priv->params,
+							      G_CALLBACK (params_changed_cb), source);
+			g_object_unref (source->priv->params);
+		}
+
+		g_free (source->priv->id);
+		g_free (source->priv->title);
+		g_free (source->priv->select_sql);
+
+		g_clear_error (&source->priv->exec_error);
+		if (source->priv->model)
+			g_object_unref (source->priv->model);
+		
+		if (source->priv->export_names)
+			g_array_free (source->priv->export_names, TRUE);
+		if (source->priv->export_columns)
+			g_hash_table_destroy (source->priv->export_columns);
+
+		g_free (source->priv);
+		source->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+static void init_from_query (DataSource *source, xmlNodePtr node);
+static gboolean init_from_table_node (DataSource *source, xmlNodePtr node, GError **error);
+
+/**
+ * data_source_new
+ * @bcnc: a #BrowserConnection
+ * @node:
+ * @error:
+ *
+ * Creates a new #DataSource object
+ *
+ * Returns: a new object
+ */
+DataSource*
+data_source_new_from_xml_node (BrowserConnection *bcnc, xmlNodePtr node, GError **error)
+{
+	DataSource *source;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+	g_return_val_if_fail (node, NULL);
+
+	source = DATA_SOURCE (g_object_new (DATA_SOURCE_TYPE, NULL));
+	source->priv->bcnc = g_object_ref (bcnc);
+	xmlChar *prop;
+	prop = xmlGetProp (node, BAD_CAST "title");
+	if (prop) {
+		source->priv->title = g_strdup ((gchar*) prop);
+		xmlFree (prop);
+	}
+	prop = xmlGetProp (node, BAD_CAST "id");
+	if (prop) {
+		source->priv->id = g_strdup ((gchar*) prop);
+		xmlFree (prop);
+	}
+
+	if (!strcmp ((gchar*)node->name, "table")) {
+		if (! init_from_table_node (source, node, error)) {
+			g_object_unref (source);
+			source = NULL;
+		}
+	}
+	else if (!strcmp ((gchar*)node->name, "query")) {
+		init_from_query (source, node);
+	}
+	else {
+		g_set_error (error, 0, 0,
+			     _("Node must be \"table\" or \"query\", and is \"%s\""), (gchar*)node->name);
+		g_object_unref (source);
+		source = NULL;
+	}
+	
+	return source;
+}
+
+static void
+init_from_query (DataSource *source, xmlNodePtr node)
+{
+	GdaSqlParser *parser;
+	const gchar *remain;
+	xmlChar *contents;
+
+	contents = xmlNodeGetContent (node);
+
+	g_clear_error (& source->priv->init_error);
+	parser = browser_connection_create_parser (source->priv->bcnc);
+	if (contents) {
+		source->priv->stmt = gda_sql_parser_parse_string (parser, (gchar*) contents,
+								  &remain, &source->priv->init_error);
+		xmlFree (contents);
+	}
+	g_object_unref (parser);
+
+	if (source->priv->stmt) {
+		if (remain)
+			g_set_error (& source->priv->init_error, 0, 0,
+				     _("Multiple statements detected, only the first will be used"));
+
+		/* try to normalize the statement */
+		GdaSqlStatement *sqlst;
+		g_object_get ((GObject*) source->priv->stmt, "structure", &sqlst, NULL);
+		if (browser_connection_normalize_sql_statement (source->priv->bcnc, sqlst, NULL))
+			g_object_set ((GObject*) source->priv->stmt, "structure", sqlst, NULL);
+
+		/* compute export data */
+		if (source->priv->id) {
+			if (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT) {
+				GdaSqlStatementSelect *selst;
+				selst = (GdaSqlStatementSelect*) sqlst->contents;
+				GSList *list;
+				gint i;
+				for (i = 0, list = selst->expr_list; list; i++, list = list->next) {
+					gchar *tmp;
+					if (! source->priv->export_names)
+						source->priv->export_names = g_array_new (FALSE, FALSE,
+											  sizeof (gchar*));
+					if (! source->priv->export_columns)
+						source->priv->export_columns =
+							g_hash_table_new_full (g_str_hash, g_str_equal,
+									       g_free, NULL);
+
+					tmp = g_strdup_printf ("%s %d", source->priv->id, i);
+					g_array_append_val (source->priv->export_names, tmp);
+					g_hash_table_insert (source->priv->export_columns, tmp,
+							     GINT_TO_POINTER (i + 1));
+					/* g_print ("EXPORT [%s]\n", tmp); */
+
+					GdaSqlSelectField *sf = (GdaSqlSelectField *) list->data;
+					if (sf->validity_meta_table_column) {
+						tmp = g_strdup_printf ("%s %s", source->priv->id,
+								       sf->validity_meta_table_column->column_name);
+						g_array_append_val (source->priv->export_names, tmp);
+						g_hash_table_insert (source->priv->export_columns, tmp,
+								     GINT_TO_POINTER (i + 1));
+						/* g_print ("EXPORT [%s]\n", tmp); */
+					}
+				}
+			}
+		}
+		gda_sql_statement_free (sqlst);
+
+		/* compute parameters */
+		source->priv->need_rerun = FALSE;
+		gda_statement_get_parameters (source->priv->stmt, &source->priv->params,
+					      &source->priv->init_error);
+		if (source->priv->params) {
+			GSList *list;
+			for (list = source->priv->params->holders; list; list = list->next)
+				gda_holder_set_not_null (GDA_HOLDER (list->data), FALSE);
+
+			g_signal_connect (source->priv->params, "holder-changed",
+					  G_CALLBACK (params_changed_cb), source);
+		}
+	}
+}
+
+static GdaMetaTable *
+get_meta_table (DataSource *source, const gchar *table_name, GError **error)
+{
+	GdaMetaStruct *mstruct;
+	GdaMetaDbObject *dbo;
+	GValue *vname;
+
+	mstruct = browser_connection_get_meta_struct (source->priv->bcnc);
+	if (! mstruct) {
+		g_set_error (error, 0, 0,
+			     _("Not ready"));
+		return NULL;
+	}
+
+	g_value_set_string ((vname = gda_value_new (G_TYPE_STRING)), table_name);
+	dbo = gda_meta_struct_get_db_object (mstruct, NULL, NULL, vname);
+	gda_value_free (vname);
+	if (! dbo) {
+		g_set_error (error, 0, 0,
+			     _("Could not find the \"%s\" table"), table_name);
+		return NULL;
+	}
+	if ((dbo->obj_type != GDA_META_DB_TABLE) && (dbo->obj_type != GDA_META_DB_VIEW)) {
+		g_set_error (error, 0, 0,
+			     _("The \"%s\" object is not a table"), table_name);
+		return NULL;
+	}
+	return GDA_META_TABLE (dbo);
+}
+
+static gboolean
+init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
+{
+	xmlChar *tname;
+	tname = xmlGetProp (node, BAD_CAST "name");
+	if (!tname) {
+		g_set_error (error, 0, 0,
+			     _("Missing attribute \"name\" for table"));
+		return FALSE;
+	}
+	
+	if (! source->priv->title)
+		source->priv->title = g_strdup_printf (_("Contents of '%s'"), (gchar*) tname);
+
+	/* locate table */
+	GdaMetaTable *mtable;
+	mtable = get_meta_table (source, (gchar*) tname, error);
+	if (!mtable) {
+		xmlFree (tname);
+		return FALSE;
+	}
+
+	/* build statement */
+	GSList *list;
+	GdaSqlBuilder *b;
+	gint i;
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	if (! gda_sql_builder_select_add_target (b, (gchar*) tname, NULL)) {
+		g_set_error (error, 0, 0,
+			     _("Could not build SELECT statement"));
+		xmlFree (tname);
+		return FALSE;
+	}
+
+	for (i = 0, list = mtable->columns; list; i++, list = list->next) {
+		GdaMetaTableColumn *mcol;
+		mcol = GDA_META_TABLE_COLUMN (list->data);
+		gda_sql_builder_select_add_field (b, mcol->column_name, NULL, NULL);
+
+		/* export value */
+		gchar *tmp;
+		if (source->priv->id)
+			tmp = g_strdup_printf ("%s %s", source->priv->id, mcol->column_name);
+		else
+			tmp = g_strdup_printf ("%s %s", (gchar*) tname, mcol->column_name);
+		if (! source->priv->export_names)
+			source->priv->export_names = g_array_new (FALSE, FALSE,
+								  sizeof (gchar*));
+		if (! source->priv->export_columns)
+			source->priv->export_columns =
+				g_hash_table_new_full (g_str_hash, g_str_equal,
+						       g_free, NULL);
+		g_array_append_val (source->priv->export_names, tmp);
+		g_hash_table_insert (source->priv->export_columns, tmp,
+				     GINT_TO_POINTER (i + 1));
+		g_print ("EXPORT [%s]\n", tmp);
+
+		if (source->priv->id)
+			tmp = g_strdup_printf ("%s %d", source->priv->id, i + 1);
+		else
+			tmp = g_strdup_printf ("%s %d", (gchar*) tname, i + 1);
+		g_array_append_val (source->priv->export_names, tmp);
+		g_hash_table_insert (source->priv->export_columns, tmp,
+				     GINT_TO_POINTER (i + 1));
+		g_print ("EXPORT [%s]\n", tmp);
+	}
+	xmlFree (tname);
+
+	/* linking */
+	xmlNodePtr subnode;
+	for (subnode = node->children; subnode; subnode = subnode->next) {
+		if (!strcmp ((gchar*)subnode->name, "link_with")) {
+			xmlChar *contents, *id;
+			GdaMetaTable *mlinked;
+			
+			contents = xmlNodeGetContent (subnode);
+			mlinked = get_meta_table (source, (gchar*) contents, error);
+			if (!mlinked) {
+				xmlFree (contents);
+				g_object_unref (b);	
+				return FALSE;
+			}
+			id = xmlGetProp (subnode, BAD_CAST "id");
+
+			/* find foreign key to linked table */
+			GdaMetaTableForeignKey *fk = NULL;
+			for (list = mtable->fk_list; list; list = list->next) {
+				if (GDA_META_TABLE_FOREIGN_KEY (list->data)->depend_on == GDA_META_DB_OBJECT (mlinked)) {
+					fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
+					break;
+				}
+			}
+			if (!fk) {
+				g_set_error (error, 0, 0,
+					     _("Could not find any foreign key to \"%s\""), (gchar*) contents);
+				xmlFree (contents);
+				if (id) xmlFree (id);
+				g_object_unref (b);	
+				return FALSE;
+			}
+			else if (fk->cols_nb <= 0) {
+				g_set_error (error, 0, 0,
+					     _("The fields involved in the foreign key to \"%s\" are not known"),
+					     (gchar*) contents);
+				xmlFree (contents);
+				if (id) xmlFree (id);
+				g_object_unref (b);	
+				return FALSE;
+			}
+			else if (fk->cols_nb == 1) {
+				gchar *tmp;
+				GdaMetaTableColumn *col;
+				col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns, fk->fk_cols_array [0]));
+				g_assert (col);
+				gda_sql_builder_add_id (b, 1, fk->fk_names_array [0]);
+				tmp = g_strdup_printf ("%s %s", id ? (gchar*) id : (gchar*) contents,
+						       fk->ref_pk_names_array [0]);
+				gda_sql_builder_add_param (b, 2, tmp, col->gtype, FALSE);
+				g_free (tmp);
+				gda_sql_builder_add_cond (b, 3, GDA_SQL_OPERATOR_TYPE_EQ, 1, 2, 0);
+				gda_sql_builder_set_where (b, 3);
+			}
+			else {
+				TO_IMPLEMENT;
+			}
+
+			xmlFree (contents);
+			if (id)
+				xmlFree (id);
+			break;
+		}
+	}
+
+	source->priv->stmt = gda_sql_builder_get_statement (b, error);
+
+	/* compute parameters */
+	gda_statement_get_parameters (source->priv->stmt, &source->priv->params,
+				      &source->priv->init_error);
+	if (source->priv->params) {
+		GSList *list;
+		for (list = source->priv->params->holders; list; list = list->next)
+			gda_holder_set_not_null (GDA_HOLDER (list->data), FALSE);
+
+		g_signal_connect (source->priv->params, "holder-changed",
+				  G_CALLBACK (params_changed_cb), source);
+	}
+
+	/* g_print ("SQL [%s]\n", gda_statement_to_sql (source->priv->stmt, NULL, NULL)); */
+	g_object_unref (b);
+
+	return source->priv->stmt ? TRUE : FALSE;
+}
+
+
+/**
+ * data_source_to_xml_node
+ */
+xmlNodePtr
+data_source_to_xml_node (DataSource *source)
+{
+	TO_IMPLEMENT;
+	return NULL;
+}
+
+static gboolean
+exec_end_timeout_cb (DataSource *source)
+{
+	GObject *obj;
+
+	g_clear_error (&source->priv->exec_error);
+	obj = browser_connection_execution_get_result (source->priv->bcnc,
+						       source->priv->exec_id, NULL,
+						       &source->priv->exec_error);
+	if (obj) {
+		if (GDA_IS_DATA_MODEL (obj)) {
+			if (source->priv->model != GDA_DATA_MODEL (obj)) {
+				if (source->priv->model)
+					g_object_unref (source->priv->model);
+				source->priv->model = GDA_DATA_MODEL (obj);
+				g_object_set (source->priv->model, "auto-reset", FALSE, NULL);
+			}
+			else {
+				gda_data_model_thaw (source->priv->model);
+				gda_data_model_reset (source->priv->model);
+			}
+		}
+		else {
+			g_object_unref (obj);
+			g_set_error (&source->priv->exec_error, 0, 0,
+				     _("Statement to execute is not a selection statement"));
+		}
+		source->priv->exec_id = 0;
+		g_signal_emit (source, data_source_signals [EXEC_FINISHED], 0, source->priv->exec_error);
+		return FALSE;
+	}
+	else if (source->priv->exec_error) {
+		source->priv->exec_id = 0;
+		g_signal_emit (source, data_source_signals [EXEC_FINISHED], 0, source->priv->exec_error);
+		return FALSE;
+	}
+	else
+		return TRUE; /* keep timer */
+}
+
+/**
+ * data_source_execution_going_on
+ */
+gboolean
+data_source_execution_going_on (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), FALSE);
+	return source->priv->exec_id == 0 ? FALSE : TRUE;
+}
+
+/**
+ * data_source_get_import
+ *
+ * Returns: a pointer to a read-only #GdaSet, or %NULL (must not be modified)
+ */
+GdaSet *
+data_source_get_import (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	return source->priv->params;
+}
+
+/**
+ * data_source_get_export_template
+ *
+ * Returns: an array of strings, or %NULL
+ */
+GArray *
+data_source_get_export_names (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	return source->priv->export_names;
+}
+
+/**
+ * data_source_get_export_columns
+ *
+ * Returns: a #GHashTable where key is an export name and value is its column number (use GPOINTER_TO_INT)
+ */
+GHashTable *
+data_source_get_export_columns (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	return source->priv->export_columns;	
+}
+
+/**
+ * data_source_start_execution
+ */
+void
+data_source_execute (DataSource *source, GError **error)
+{
+	GError *lerror = NULL;
+	gboolean has_exec = TRUE;
+	g_return_if_fail (IS_DATA_SOURCE (source));
+	
+	if (! source->priv->stmt) {
+		if (source->priv->init_error)
+			g_propagate_error (error, source->priv->init_error);
+		else
+			g_set_error (error, 0, 0,
+				     _("No SELECT statement to execute"));
+	}
+
+	if (source->priv->model) {
+		if (source->priv->need_rerun) {
+			/* freeze source->priv->model to avoid that it emits signals while being in the
+			 * wrong thread */
+			source->priv->need_rerun = FALSE;
+			gda_data_model_freeze (source->priv->model);
+			source->priv->exec_id = browser_connection_rerun_select (source->priv->bcnc,
+										 source->priv->model, &lerror);
+		}
+		else
+			has_exec = FALSE;
+	}
+	else
+		source->priv->exec_id = browser_connection_execute_statement (source->priv->bcnc,
+									      source->priv->stmt,
+									      source->priv->params,
+									      GDA_STATEMENT_MODEL_RANDOM_ACCESS | GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
+									      FALSE, &lerror);
+	if (has_exec) {
+		g_signal_emit (source, data_source_signals [EXEC_STARTED], 0);
+		if (! source->priv->exec_id) {
+			g_signal_emit (source, data_source_signals [EXEC_FINISHED], 0, lerror);
+			g_propagate_error (error, lerror);
+		}
+		else {
+			/* monitor the end of execution */
+			g_timeout_add (50, (GSourceFunc) exec_end_timeout_cb, source);
+		}
+	}
+}
+
+/*
+ * creates a new string where double underscores '__' are replaced by a single underscore '_'
+ */
+static gchar *
+replace_double_underscores (const gchar *str)
+{
+        gchar **arr;
+        gchar *ret;
+
+        arr = g_strsplit (str, "__", 0);
+        ret = g_strjoinv ("_", arr);
+        g_strfreev (arr);
+
+        return ret;
+}
+
+/**
+ * data_source_create_grid
+ *
+ * Returns: a new #GdauiRawGrid, or %NULL if an error occurred
+ */
+GdauiRawGrid *
+data_source_create_grid (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+
+	if (! source->priv->model)
+		return NULL;
+
+	GdauiRawGrid *grid;
+	grid = (GdauiRawGrid*) gdaui_raw_grid_new (source->priv->model);
+	
+	GList *columns, *list;
+	columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
+	for (list = columns; list; list = list->next) {
+		/* reduce column's title */
+		const gchar *title;
+		GtkWidget *header;
+		title = gtk_tree_view_column_get_title (GTK_TREE_VIEW_COLUMN (list->data));
+		header = gtk_label_new ("");
+		if (title) {
+			gchar *tmp, *str;
+			str = replace_double_underscores (title);
+			tmp = g_markup_printf_escaped ("<small>%s</small>", str);
+			g_free (str);
+			gtk_label_set_markup (GTK_LABEL (header), tmp);
+			g_free (tmp);
+		}
+		else
+			gtk_label_set_markup (GTK_LABEL (header), "<small></small>");
+		gtk_widget_show (header);
+		gtk_tree_view_column_set_widget (GTK_TREE_VIEW_COLUMN (list->data),
+						 header);
+		
+		/* reduce text's size */
+		GList *renderers, *list2;
+		renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (list->data));
+		for (list2 = renderers; list2; list2 = list2->next) {
+			if (GTK_IS_CELL_RENDERER_TEXT (list2->data))
+				g_object_set ((GObject*) list2->data,
+					      "scale", 0.7, NULL);
+		}
+		g_list_free (renderers);
+	}
+	
+	/*if (!columns || !columns->next)*/
+		gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (grid), FALSE);
+	g_list_free (columns);
+	return grid;
+}
+
+/**
+ * data_source_get_title
+ */
+const gchar *
+data_source_get_title (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+
+	if (source->priv->title)
+		return source->priv->title;
+	else if (source->priv->id)
+		return source->priv->id;
+	else
+		return _("No name");
+}
diff --git a/tools/browser/data-manager/data-source.h b/tools/browser/data-manager/data-source.h
new file mode 100644
index 0000000..391eca6
--- /dev/null
+++ b/tools/browser/data-manager/data-source.h
@@ -0,0 +1,78 @@
+/* 
+ * Copyright (C) 2009 - 2010 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 __DATA_SOURCE_H_
+#define __DATA_SOURCE_H_
+
+#include <libgda-ui/libgda-ui.h>
+#include "browser-favorites.h"
+#include "decl.h"
+
+G_BEGIN_DECLS
+
+#define DATA_SOURCE_TYPE          (data_source_get_type())
+#define DATA_SOURCE(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, data_source_get_type(), DataSource)
+#define DATA_SOURCE_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, data_source_get_type (), DataSourceClass)
+#define IS_DATA_SOURCE(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, data_source_get_type ())
+
+typedef struct _DataSource DataSource;
+typedef struct _DataSourceClass DataSourceClass;
+typedef struct _DataSourcePrivate DataSourcePrivate;
+
+/* struct for the object's data */
+struct _DataSource
+{
+	GObject            object;
+	DataSourcePrivate *priv;
+};
+
+/* struct for the object's class */
+struct _DataSourceClass
+{
+	GObjectClass       parent_class;
+
+	/* signals */
+	void             (*execution_started) (DataSource *source);
+	void             (*execution_finished) (DataSource *source, GError *error);
+};
+
+GType               data_source_get_type            (void) G_GNUC_CONST;
+
+DataSource         *data_source_new_from_xml_node   (BrowserConnection *bcnc, xmlNodePtr node, GError **error);
+xmlNodePtr          data_source_to_xml_node         (DataSource *source);
+
+GdaSet             *data_source_get_import          (DataSource *source);
+GArray             *data_source_get_export_names    (DataSource *source);
+GHashTable         *data_source_get_export_columns  (DataSource *source);
+
+void                data_source_execute             (DataSource *source, GError **error);
+gboolean            data_source_execution_going_on  (DataSource *source);
+GdauiRawGrid       *data_source_create_grid         (DataSource *source);
+const gchar        *data_source_get_title           (DataSource *source);
+
+/*
+DataSource         *data_source_new_from_table      (BrowserConnection *bcnc,
+						     const gchar *table_schema, const gchar *table_name);
+DataSource         *data_source_new_from_select     (BrowserConnection *bcnc, const gchar *select_sql);
+*/
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/data-widget.c b/tools/browser/data-manager/data-widget.c
new file mode 100644
index 0000000..7a3bc46
--- /dev/null
+++ b/tools/browser/data-manager/data-widget.c
@@ -0,0 +1,499 @@
+/* GNOME DB library
+ * Copyright (C) 2010 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 "data-widget.h"
+#include "../browser-connection.h"
+#include "../browser-spinner.h"
+
+typedef struct {
+	DataWidget *dwid;
+	DataSource *source;
+
+	GtkWidget *top;
+	GtkNotebook *nb; /* page 0: spinner
+			    page 1 or 2, depends on @data_widget_page */
+	gint data_widget_page;
+	gint error_widget_page;
+	BrowserSpinner *spinner;
+	GtkWidget *data_widget;
+	GtkWidget *error_widget;
+	GdaSet *export_data;
+
+	GSList *dep_parts; /* list of DataPart which need to be re-run when anything in @export_data
+			    * changes */
+} DataPart;
+#define DATA_PART(x) ((DataPart*)(x))
+
+static DataPart *data_part_find (DataWidget *dwid, DataSource *source);
+static void data_part_free (DataPart *part);
+
+struct _DataWidgetPrivate {
+	GtkWidget *hpaned;
+	GSList *parts;
+};
+
+static void data_widget_class_init (DataWidgetClass *klass);
+static void data_widget_init       (DataWidget *dwid, DataWidgetClass *klass);
+static void data_widget_dispose    (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * DataWidget class implementation
+ */
+static void
+data_widget_class_init (DataWidgetClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = data_widget_dispose;
+}
+
+
+static void
+data_widget_init (DataWidget *dwid, DataWidgetClass *klass)
+{
+	g_return_if_fail (IS_DATA_WIDGET (dwid));
+
+	/* allocate private structure */
+	dwid->priv = g_new0 (DataWidgetPrivate, 1);
+}
+
+GType
+data_widget_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (DataWidgetClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_widget_class_init,
+			NULL,
+			NULL,
+			sizeof (DataWidget),
+			0,
+			(GInstanceInitFunc) data_widget_init
+		};
+		type = g_type_register_static (GTK_TYPE_VBOX, "DataWidget", &info, 0);
+	}
+	return type;
+}
+
+static void
+data_widget_dispose (GObject *object)
+{
+	DataWidget *dwid = (DataWidget*) object;
+	if (dwid->priv) {
+		if (dwid->priv->parts) {
+			g_slist_foreach (dwid->priv->parts, (GFunc) data_part_free, NULL);
+			g_slist_free (dwid->priv->parts);
+		}
+		g_free (dwid->priv);
+		dwid->priv = NULL;
+	}
+	parent_class->dispose (object);
+}
+
+static void source_exec_started_cb (DataSource *source, DataPart *part);
+static void source_exec_finished_cb (DataSource *source, GError *error, DataPart *part);
+
+static DataPart *
+create_part (DataWidget *dwid, DataSource *source)
+{
+	DataPart *part;
+	part = g_new0 (DataPart, 1);
+	part->dwid = dwid;
+	part->source = g_object_ref (source);
+	g_signal_connect (source, "execution-started",
+			  G_CALLBACK (source_exec_started_cb), part);
+	g_signal_connect (source, "execution-finished",
+			  G_CALLBACK (source_exec_finished_cb), part);
+
+	dwid->priv->parts = g_slist_append (dwid->priv->parts, part);
+
+	GtkWidget *vbox;
+	vbox = gtk_vbox_new (FALSE, 0);
+	part->top = vbox;
+
+	GtkWidget *header;
+	const gchar *cstr;
+	header = gtk_label_new ("");
+	cstr = data_source_get_title (source);
+	if (cstr) {
+		gchar *tmp;
+		tmp = g_markup_printf_escaped ("<b><small>%s</small></b>", cstr);
+		gtk_label_set_markup (GTK_LABEL (header), tmp);
+		g_free (tmp);
+	}
+	else
+		gtk_label_set_markup (GTK_LABEL (header), "<b><small> </small></b>");
+	gtk_misc_set_alignment (GTK_MISC (header), 0., -1);
+	gtk_widget_set_size_request (header, 150, -1);
+	gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
+	gtk_box_pack_start (GTK_BOX (vbox), header, FALSE, FALSE, 0);
+
+	GtkWidget *nb;
+	nb = gtk_notebook_new ();
+	gtk_notebook_set_show_border (GTK_NOTEBOOK (nb), FALSE);
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
+	part->nb = GTK_NOTEBOOK (nb);
+
+	part->spinner = BROWSER_SPINNER (browser_spinner_new ());
+	gtk_notebook_append_page (GTK_NOTEBOOK (nb), GTK_WIDGET (part->spinner), NULL);
+	part->data_widget = NULL;
+
+	gtk_box_pack_start (GTK_BOX (vbox), nb, TRUE, TRUE, 0);
+
+	gtk_widget_show_all (vbox);
+	if (data_source_execution_going_on (source))
+		source_exec_started_cb (source, part);
+
+	return part;
+}
+
+/* make a super-container to contain @size items: the list
+ * will have @size-2 paned widgets */
+GSList *
+make_paned_list (gint size, gboolean horiz)
+{
+	GSList *list = NULL;
+	gint i;
+	GtkWidget *paned;
+
+	g_assert (size >= 2);
+	paned = horiz ? gtk_hpaned_new () : gtk_vpaned_new ();
+	list = g_slist_prepend (list, paned);
+
+	for (i = 2; i < size; i++) {
+		GtkWidget *paned2;
+		paned2 = horiz ? gtk_hpaned_new () : gtk_vpaned_new ();
+		gtk_paned_add2 (GTK_PANED (paned), paned2);
+		list = g_slist_prepend (list, paned2);
+		paned = paned2;
+	}
+	return g_slist_reverse (list);
+}
+
+static void
+pack_in_paned_list (GSList *paned_list, gint length, gint pos, GtkWidget *wid)
+{
+	GtkPaned *paned;
+	if (pos < length - 1) {
+		paned = g_slist_nth_data (paned_list, pos);
+		gtk_paned_add1 (paned, wid);
+	}
+	else {
+		paned = g_slist_nth_data (paned_list, pos - 1);
+		gtk_paned_add2 (paned, wid);
+	}
+}
+
+/**
+ * data_widget_new
+ *
+ * Returns: the newly created widget.
+ */
+GtkWidget *
+data_widget_new (GArray *sources_array)
+{
+	DataWidget *dwid;
+
+	g_return_val_if_fail (sources_array, NULL); 
+	dwid = g_object_new (DATA_WIDGET_TYPE, NULL);
+
+	if (sources_array->len == 1) {
+		GArray *subarray = g_array_index (sources_array, GArray*, 0);
+		if (subarray->len == 1) {
+			DataPart *part;
+			DataSource *source;
+			source = g_array_index (subarray, DataSource*, 0);
+			part = create_part (dwid, source);
+			gtk_box_pack_start (GTK_BOX (dwid), part->top, TRUE, TRUE, 0);
+
+			data_source_execute (source, NULL);
+		}
+		else {
+			GSList *paned_list;
+			gint i;
+			paned_list = make_paned_list (subarray->len, FALSE);
+			gtk_box_pack_start (GTK_BOX (dwid), GTK_WIDGET (paned_list->data), TRUE, TRUE, 0);
+			for (i = 0; i < subarray->len; i++) {
+				DataPart *part;
+				DataSource *source;
+				source = g_array_index (subarray, DataSource*, i);
+				part = create_part (dwid, source);
+				pack_in_paned_list (paned_list, subarray->len, i, part->top);
+
+				data_source_execute (source, NULL);
+			}
+			g_slist_free (paned_list);
+		}
+	}
+	else {
+		GSList *top_paned_list;
+		gint j;
+
+		top_paned_list = make_paned_list (sources_array->len, TRUE);
+		gtk_box_pack_start (GTK_BOX (dwid), GTK_WIDGET (top_paned_list->data), TRUE, TRUE, 0);
+		for (j = 0; j < sources_array->len; j++) {
+			GArray *subarray = g_array_index (sources_array, GArray*, j);
+			DataSource *source;
+
+			if (subarray->len == 1) {
+				DataPart *part;
+				source = g_array_index (subarray, DataSource*, 0);
+				part = create_part (dwid, source);
+				pack_in_paned_list (top_paned_list, sources_array->len, j, part->top);
+				
+				data_source_execute (source, NULL);
+			}
+			else {
+				GSList *paned_list;
+				gint i;
+				paned_list = make_paned_list (subarray->len, FALSE);
+				pack_in_paned_list (top_paned_list, sources_array->len, j,
+						    GTK_WIDGET (paned_list->data));
+				for (i = 0; i < subarray->len; i++) {
+					DataPart *part;
+					source = g_array_index (subarray, DataSource*, i);
+					part = create_part (dwid, source);
+					pack_in_paned_list (paned_list, subarray->len, i, part->top);
+					
+					data_source_execute (source, NULL);
+				}
+				g_slist_free (paned_list);
+			}
+		}
+		g_slist_free (top_paned_list);
+	}
+
+	return GTK_WIDGET (dwid);
+}
+
+static void
+data_part_free (DataPart *part)
+{
+	if (part->source)
+		g_object_unref (part->source);
+	if (part->export_data)
+		g_object_unref (part->export_data);
+	if (part->dep_parts)
+		g_slist_free (part->dep_parts);
+	g_free (part);
+}
+
+static DataPart *
+data_part_find (DataWidget *dwid, DataSource *source)
+{
+	GSList *list;
+	for (list = dwid->priv->parts; list; list = list->next) {
+		if (DATA_PART (list->data)->source == source)
+			return DATA_PART (list->data);
+	}
+	return NULL;
+}
+
+static void
+source_exec_started_cb (DataSource *source, DataPart *part)
+{
+	gtk_notebook_set_current_page (part->nb, 0);
+	browser_spinner_start (part->spinner);
+}
+
+static void data_part_selection_changed_cb (GdauiDataSelector *gdauidataselector, DataPart *part);
+static void compute_sources_dependencies (DataPart *part);
+static void
+source_exec_finished_cb (DataSource *source, GError *error, DataPart *part)
+{
+	GtkWidget *wid;
+	browser_spinner_stop (part->spinner);
+	
+	/*g_print ("Execution of source [%s] finished\n", data_source_get_title (part->source));*/
+	if (error) {
+		gchar *tmp;
+		tmp = g_markup_printf_escaped ("\n<b>Error:\n</b>%s",
+					       error->message ? error->message : _("Error: no detail"));
+		if (! part->error_widget) {
+			part->error_widget = gtk_label_new ("");
+			gtk_misc_set_alignment (GTK_MISC (part->error_widget), 0., 0.);
+			part->error_widget_page = gtk_notebook_append_page (part->nb, part->error_widget,
+									    NULL);
+			gtk_widget_show (part->error_widget);
+		}
+		gtk_label_set_markup (GTK_LABEL (part->error_widget), tmp);
+		g_free (tmp);
+		gtk_notebook_set_current_page (part->nb, part->error_widget_page);
+		return;
+	}
+	
+	if (! part->data_widget) {
+		wid = (GtkWidget*) data_source_create_grid (part->source);
+		part->data_widget = wid;
+		part->data_widget_page = gtk_notebook_append_page (part->nb, wid, NULL);
+		gtk_widget_show (part->data_widget);
+		g_print ("Creating data widget for source [%s]\n", data_source_get_title (part->source));
+
+		/* compute part->export_data */
+		GArray *export_names;
+		export_names = data_source_get_export_names (part->source);
+		if (export_names && (export_names->len > 0)) {
+			GSList *holders = NULL;
+			GdaDataModel *model;
+			GHashTable *export_columns;
+			gint i;
+			GdaDataModelIter *iter;
+
+			iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (wid));
+			g_object_get (wid, "model", &model, NULL);
+
+			export_columns = data_source_get_export_columns (part->source);
+			for (i = 0; i < export_names->len; i++) {
+				gint col;
+				GdaHolder *bindto;
+				col = GPOINTER_TO_INT (g_hash_table_lookup (export_columns,
+									    g_array_index (export_names,
+											   gchar*, i))) - 1;
+				bindto = gda_data_model_iter_get_holder_for_field (iter, col);
+				if (bindto) {
+					GdaHolder *holder;
+					holder = gda_holder_copy (bindto);
+					g_object_set ((GObject*) holder, "id",
+						      g_array_index (export_names, gchar*, i), NULL);
+					holders = g_slist_prepend (holders, holder);
+#ifdef DEBUG_NO
+					g_print ("HOLDER [%s::%s]\n",
+						 gda_holder_get_id (holder),
+						 g_type_name (gda_holder_get_g_type (holder)));
+#endif
+					g_assert (gda_holder_set_bind (holder, bindto, NULL));
+				}
+			}
+			
+			g_object_unref (model);
+			if (holders) {
+				part->export_data = gda_set_new (holders);
+				g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+				g_slist_free (holders);
+
+				g_signal_connect (wid, "selection-changed",
+						  G_CALLBACK (data_part_selection_changed_cb), part);
+			}
+		}
+	}
+	gtk_notebook_set_current_page (part->nb, part->data_widget_page);
+	compute_sources_dependencies (part);
+}
+
+static void
+data_part_selection_changed_cb (GdauiDataSelector *gdauidataselector, DataPart *part)
+{
+	if (part->export_data) {
+		GSList *list;
+#ifdef DEBUG_NO
+		for (list = part->export_data->holders; list; list = list->next) {
+			GdaHolder *holder = GDA_HOLDER (list->data);
+			gchar *tmp;
+			tmp = gda_value_stringify (gda_holder_get_value (holder));
+			g_print ("%s=>[%s]\n", gda_holder_get_id (holder), tmp);
+			g_free (tmp);
+		}
+#endif
+
+		for (list = part->dep_parts; list; list = list->next) {
+			DataPart *spart = DATA_PART (list->data);
+			data_source_execute (spart->source, NULL);
+		}
+	}
+}
+
+static void
+compute_sources_dependencies (DataPart *part)
+{
+	GdaSet *import;
+	import = data_source_get_import (part->source);
+	if (!import)
+		return;
+
+	GSList *holders;
+	for (holders = import->holders; holders; holders = holders->next) {
+		GdaHolder *holder = (GdaHolder*) holders->data;
+		const gchar *hid = gda_holder_get_id (holder);
+		GSList *list;
+	
+		for (list = part->dwid->priv->parts; list; list = list->next) {
+			DataPart *opart = DATA_PART (list->data);
+			if (opart == part)
+				continue;
+
+			GdaSet *export;
+			GdaHolder *holder2;
+			export = data_widget_get_export (part->dwid, opart->source);
+			if (!export)
+				continue;
+
+			holder2 = gda_set_get_holder (export, hid);
+			if (holder2) {
+				GError *lerror = NULL;
+				if (! gda_holder_set_bind (holder, holder2, &lerror)) {
+					TO_IMPLEMENT;
+					g_print ("Error: %s\n", lerror && lerror->message ? 
+						 lerror->message : "???");
+					g_clear_error (&lerror);
+				}
+				g_print ("[%s.][%s] bound to [%s].[%s]\n",
+					 data_source_get_title (part->source),
+					 hid,
+					 data_source_get_title (opart->source),
+					 gda_holder_get_id (holder2));
+				
+				if (! g_slist_find (opart->dep_parts, part))
+					opart->dep_parts = g_slist_append (opart->dep_parts, part);
+				continue;
+			}
+		}
+	}
+}
+
+/**
+ * data_widget_get_export
+ *
+ * Returns: the #GdaSet or %NULL, no ref held to it by the caller
+ */
+GdaSet *
+data_widget_get_export (DataWidget *dwid, DataSource *source)
+{
+	DataPart *part;
+	g_return_val_if_fail (IS_DATA_WIDGET (dwid), NULL);
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	part = data_part_find (dwid, source);
+	if (!part) {
+		g_warning ("Can't find DataPart for DataSource");
+		return NULL;
+	}
+	return part->export_data;
+}
diff --git a/tools/browser/data-manager/data-widget.h b/tools/browser/data-manager/data-widget.h
new file mode 100644
index 0000000..03bb1b3
--- /dev/null
+++ b/tools/browser/data-manager/data-widget.h
@@ -0,0 +1,59 @@
+/* GNOME DB library
+ * Copyright (C) 2010 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 __DATA_WIDGET_H__
+#define __DATA_WIDGET_H__
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "data-source.h"
+
+G_BEGIN_DECLS
+
+#define DATA_WIDGET_TYPE            (data_widget_get_type())
+#define DATA_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, DATA_WIDGET_TYPE, DataWidget))
+#define DATA_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, DATA_WIDGET_TYPE, DataWidgetClass))
+#define IS_DATA_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, DATA_WIDGET_TYPE))
+#define IS_DATA_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DATA_WIDGET_TYPE))
+
+
+typedef struct _DataWidget        DataWidget;
+typedef struct _DataWidgetClass   DataWidgetClass;
+typedef struct _DataWidgetPrivate DataWidgetPrivate;
+
+struct _DataWidget {
+	GtkVBox parent;
+	DataWidgetPrivate *priv;
+};
+
+struct _DataWidgetClass {
+	GtkVBoxClass parent_class;
+};
+
+
+GType      data_widget_get_type   (void) G_GNUC_CONST;
+GtkWidget *data_widget_new        (GArray *sources_array);
+GdaSet    *data_widget_get_export (DataWidget *dwid, DataSource *source);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/marshal.list b/tools/browser/data-manager/marshal.list
new file mode 100644
index 0000000..9ab3792
--- /dev/null
+++ b/tools/browser/data-manager/marshal.list
@@ -0,0 +1,27 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+#   VOID        indicates   no   return   type,  or  no  extra
+#               parameters. if VOID is used as  the  parameter
+#               list, no additional parameters may be present.
+#   BOOLEAN     for boolean types (gboolean)
+#   CHAR        for signed char types (gchar)
+#   UCHAR       for unsigned char types (guchar)
+#   INT         for signed integer types (gint)
+#   UINT        for unsigned integer types (guint)
+#   LONG        for signed long integer types (glong)
+#   ULONG       for unsigned long integer types (gulong)
+#   ENUM        for enumeration types (gint)
+#   FLAGS       for flag enumeration types (guint)
+#   FLOAT       for single-precision float types (gfloat)
+#   DOUBLE      for double-precision float types (gdouble)
+#   STRING      for string types (gchar*)
+#   PARAM       for GParamSpec or derived types  (GParamSpec*)
+#   BOXED       for boxed (anonymous but reference counted) types (GBoxed*)
+#   POINTER     for anonymous pointer types (gpointer)
+#   OBJECT      for GObject or derived types (GObject*)
+#   NONE        deprecated alias for VOID
+#   BOOL        deprecated alias for BOOLEAN
+
+VOID:VOID
+VOID:POINTER
+VOID:INT,ENUM,STRING
diff --git a/tools/browser/data-manager/perspective-main.c b/tools/browser/data-manager/perspective-main.c
new file mode 100644
index 0000000..3d1df2b
--- /dev/null
+++ b/tools/browser/data-manager/perspective-main.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 - 2010 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include "perspective-main.h"
+#include "data-manager-perspective.h"
+
+static BrowserPerspectiveFactory bfact;
+
+BrowserPerspectiveFactory *
+data_manager_perspective_get_factory (void)
+{
+	bfact.perspective_name = "Data manager";
+	bfact.menu_shortcut ="<control>D";
+	bfact.perspective_create = data_manager_perspective_new;
+
+	return &bfact;
+}
diff --git a/tools/browser/data-manager/perspective-main.h b/tools/browser/data-manager/perspective-main.h
new file mode 100644
index 0000000..3a2e085
--- /dev/null
+++ b/tools/browser/data-manager/perspective-main.h
@@ -0,0 +1,29 @@
+/* 
+ * Copyright (C) 2009 - 2010 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "../decl.h"
+
+G_BEGIN_DECLS
+
+BrowserPerspectiveFactory *data_manager_perspective_get_factory (void);
+
+G_END_DECLS
+
diff --git a/tools/browser/data-manager/spec-editor.c b/tools/browser/data-manager/spec-editor.c
new file mode 100644
index 0000000..dabc74b
--- /dev/null
+++ b/tools/browser/data-manager/spec-editor.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2010 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 "spec-editor.h"
+#include "data-source.h"
+#include <libgda/libgda.h>
+#include "../support.h"
+
+#ifdef HAVE_GTKSOURCEVIEW
+  #ifdef GTK_DISABLE_SINGLE_INCLUDES
+    #undef GTK_DISABLE_SINGLE_INCLUDES
+  #endif
+
+  #include <gtksourceview/gtksourceview.h>
+  #include <gtksourceview/gtksourcelanguagemanager.h>
+  #include <gtksourceview/gtksourcebuffer.h>
+  #include <gtksourceview/gtksourcestyleschememanager.h>
+  #include <gtksourceview/gtksourcestylescheme.h>
+#endif
+
+struct _SpecEditorPrivate {
+	BrowserConnection *bcnc;
+
+	SpecEditorMode mode;
+	GtkNotebook *notebook;
+
+	guint params_compute_id; /* timout ID to compute params */
+
+	/* reference for all views */
+	xmlDocPtr doc;
+
+	/* XML view */
+	gboolean xml_view_up_to_date;
+	GtkWidget *text;
+	GtkTextBuffer *buffer;
+
+	/* UI view */
+	gboolean ui_view_up_to_date;
+};
+
+static void spec_editor_class_init (SpecEditorClass *klass);
+static void spec_editor_init       (SpecEditor *sped, SpecEditorClass *klass);
+static void spec_editor_dispose    (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * SpecEditor class implementation
+ */
+static void
+spec_editor_class_init (SpecEditorClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = spec_editor_dispose;
+}
+
+
+static void
+spec_editor_init (SpecEditor *sped, SpecEditorClass *klass)
+{
+	g_return_if_fail (IS_SPEC_EDITOR (sped));
+
+	/* allocate private structure */
+	sped->priv = g_new0 (SpecEditorPrivate, 1);
+	sped->priv->params_compute_id = 0;
+	sped->priv->mode = SPEC_EDITOR_XML;
+}
+
+GType
+spec_editor_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (SpecEditorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) spec_editor_class_init,
+			NULL,
+			NULL,
+			sizeof (SpecEditor),
+			0,
+			(GInstanceInitFunc) spec_editor_init
+		};
+		type = g_type_register_static (GTK_TYPE_VBOX, "SpecEditor", &info, 0);
+	}
+	return type;
+}
+
+static void
+spec_editor_dispose (GObject *object)
+{
+	SpecEditor *sped = (SpecEditor*) object;
+	if (sped->priv) {
+		if (sped->priv->params_compute_id)
+			g_source_remove (sped->priv->params_compute_id);
+
+		if (sped->priv->bcnc)
+			g_object_unref (sped->priv->bcnc);
+
+		if (sped->priv->doc)
+			xmlFreeDoc (sped->priv->doc);
+		g_free (sped->priv);
+		sped->priv = NULL;
+	}
+	parent_class->dispose (object);
+}
+
+static gboolean
+compute_params (SpecEditor *sped)
+{
+	TO_IMPLEMENT;
+	
+	/* remove timeout */
+	sped->priv->params_compute_id = 0;
+	return FALSE;
+}
+
+
+static void
+editor_changed_cb (GtkTextBuffer *buffer, SpecEditor *sped)
+{
+	if (sped->priv->params_compute_id)
+		g_source_remove (sped->priv->params_compute_id);
+	sped->priv->params_compute_id = g_timeout_add_seconds (1, (GSourceFunc) compute_params, sped);
+}
+
+/**
+ * spec_editor_new
+ *
+ * Returns: the newly created editor.
+ */
+SpecEditor *
+spec_editor_new (BrowserConnection *bcnc)
+{
+	SpecEditor *sped;
+	GtkWidget *sw, *nb;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+	sped = g_object_new (SPEC_EDITOR_TYPE, NULL);
+	sped->priv->bcnc = g_object_ref (bcnc);
+
+	nb = gtk_notebook_new ();
+	gtk_box_pack_start (GTK_BOX (sped), nb, TRUE, TRUE, 0);
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
+	gtk_notebook_set_show_border (GTK_NOTEBOOK (nb), FALSE);
+	sped->priv->notebook = (GtkNotebook*) nb;
+
+	/* XML editor page */
+	sw = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_OUT);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	
+	gtk_notebook_append_page (GTK_NOTEBOOK (nb), sw, NULL);
+
+#ifdef HAVE_GTKSOURCEVIEW
+        sped->priv->text = gtk_source_view_new ();
+        gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (sped->priv->text))),
+						TRUE);
+
+	gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (sped->priv->text))),
+					gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (),
+										  "xml"));
+#else
+        sped->priv->text = gtk_text_view_new ();
+#endif
+	gtk_container_add (GTK_CONTAINER (sw), sped->priv->text);	
+	sped->priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (sped->priv->text));
+
+	g_signal_connect (sped->priv->buffer, "changed",
+			  G_CALLBACK (editor_changed_cb), sped);
+
+	/* UI page */
+	GtkWidget *wid;
+	wid = gtk_label_new ("TODO");
+	gtk_notebook_append_page (GTK_NOTEBOOK (nb), wid, NULL);
+
+	gtk_widget_show_all (nb);
+
+	return SPEC_EDITOR (sped);
+}
+
+/**
+ * spec_editor_set_xml_text
+ */
+void
+spec_editor_set_xml_text (SpecEditor *sped, const gchar *xml)
+{
+	g_return_if_fail (IS_SPEC_EDITOR (sped));
+
+	gtk_text_buffer_set_text (sped->priv->buffer, xml, -1);
+}
+
+gchar *
+spec_editor_get_xml_text (SpecEditor *sped)
+{
+	GtkTextIter start, end;
+	g_return_val_if_fail (IS_SPEC_EDITOR (sped), NULL);
+	gtk_text_buffer_get_start_iter (sped->priv->buffer, &start);
+	gtk_text_buffer_get_end_iter (sped->priv->buffer, &end);
+
+	return gtk_text_buffer_get_text (sped->priv->buffer, &start, &end, FALSE);
+}
+
+/**
+ * spec_editor_set_mode
+ */
+void
+spec_editor_set_mode (SpecEditor *sped, SpecEditorMode mode)
+{
+	g_return_if_fail (IS_SPEC_EDITOR (sped));
+	switch (mode) {
+	case SPEC_EDITOR_XML:
+		gtk_notebook_set_current_page (sped->priv->notebook, 0);
+		break;
+	case SPEC_EDITOR_UI:
+		gtk_notebook_set_current_page (sped->priv->notebook, 1);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+/**
+ * spec_editor_get_mode
+ */
+SpecEditorMode
+spec_editor_get_mode (SpecEditor *sped)
+{
+	g_return_val_if_fail (IS_SPEC_EDITOR (sped), SPEC_EDITOR_UI);
+	return sped->priv->mode;
+}
+
+static GArray *compute_sources (SpecEditor *sped, GError **error);
+
+
+/**
+ * spec_editor_get_sources_array
+ */
+GArray *
+spec_editor_get_sources_array (SpecEditor *sped, GError **error)
+{
+	g_return_val_if_fail (IS_SPEC_EDITOR (sped), NULL);
+	if (sped->priv->params_compute_id > 0) {
+		g_source_remove (sped->priv->params_compute_id);
+		sped->priv->params_compute_id = 0;
+		//compute_params (tconsole);
+	}
+	return compute_sources (sped, error);
+}
+
+/**
+ * spec_editor_destroy_sources_array
+ */
+void
+spec_editor_destroy_sources_array (GArray *array)
+{
+	gint i;
+	for (i = 0; i < array->len; i++) {
+		GArray *subarray;
+		subarray = g_array_index (array, GArray *, i);
+		gint j;
+		for (j = 0; j < subarray->len; j++) {
+			DataSource *source;
+			source = g_array_index (subarray, DataSource *, j);
+			g_object_unref (source);
+		}
+
+		g_array_free (subarray, TRUE);
+	}
+	g_array_free (array, TRUE);
+}
+
+/*
+ * Tells if @source1 has an import which can be satisfied by an export in @source2
+ * Returns: %TRUE if there is a dependency
+ */
+static gboolean
+source_depends_on (DataSource *source1, DataSource *source2)
+{
+	GdaSet *import;
+	import = data_source_get_import (source1);
+	if (!import)
+		return FALSE;
+
+	GSList *holders;
+	GHashTable *export_columns;
+	export_columns = data_source_get_export_columns (source2);
+	for (holders = import->holders; holders; holders = holders->next) {
+		GdaHolder *holder = (GdaHolder*) holders->data;
+		if (GPOINTER_TO_INT (g_hash_table_lookup (export_columns, gda_holder_get_id (holder))) >= 1)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+/*
+ * Returns: an array of arrays of pointer to the #DataSource objects
+ *
+ * No ref is actually held by any of these pointers, all refs to DataSource are held by
+ * the @sources_list pointers
+ */
+static GArray *
+create_sources_array (GSList *sources_list, GError **error)
+{
+	GSList *list;
+	GArray *array = NULL;
+	g_print ("** Creating DataSource arrays\n");
+	for (list = sources_list; list; list = list->next) {
+		DataSource *source;
+		source = DATA_SOURCE (g_object_ref (G_OBJECT (list->data)));
+		g_print ("Taking into account source [%s]\n",
+			 data_source_get_title (source));
+
+		GdaSet *import;
+		import = data_source_get_import (source);
+		if (!import) {
+			if (! array) {
+				array = g_array_new (FALSE, FALSE, sizeof (GArray*));
+				GArray *subarray = g_array_new (FALSE, FALSE, sizeof (DataSource*));
+				g_array_append_val (array, subarray);
+				g_array_append_val (subarray, source);
+			}
+			else {
+				GArray *subarray = g_array_index (array, GArray*, 0);
+				g_array_append_val (subarray, source);
+			}
+			continue;
+		}
+		
+		if (array) {
+			gint i;
+			gboolean dep_found = FALSE;
+			for (i = array->len - 1; i >= 0 ; i--) {
+				GArray *subarray = g_array_index (array, GArray*, i);
+				gint j;
+				for (j = 0; j < subarray->len; j++) {
+					DataSource *source2 = g_array_index (subarray, DataSource*, j);
+					g_print ("Source [%s] %s on source [%s]\n",
+						 data_source_get_title (source),
+						 source_depends_on (source, source2) ?
+						 "depends" : "does not depend",
+						 data_source_get_title (source2));
+					if (source_depends_on (source, source2)) {
+						dep_found = TRUE;
+						/* add source to column i+1 */
+						if (i == array->len - 1) {
+							GArray *subarray = g_array_new (FALSE, FALSE,
+											sizeof (DataSource*));
+							g_array_append_val (array, subarray);
+							g_array_append_val (subarray, source);
+						}
+						else {
+							GArray *subarray = g_array_index (array, GArray*, i+1);
+							g_array_append_val (subarray, source);
+						}
+						continue;
+					}
+				}
+
+				if (dep_found)
+					break;
+			}
+			if (! dep_found) {
+				/* add source to column 0 */
+				GArray *subarray = g_array_index (array, GArray*, 0);
+				g_array_append_val (subarray, source);
+			}
+		}
+		else {
+			/* add source to column 0 */
+			array = g_array_new (FALSE, FALSE, sizeof (GArray*));
+			GArray *subarray = g_array_new (FALSE, FALSE, sizeof (DataSource*));
+			g_array_append_val (array, subarray);
+			g_array_append_val (subarray, source);
+		}
+	}
+
+	g_print ("** DataSource arrays created\n");
+	return array;
+}
+
+static GArray *
+compute_sources (SpecEditor *sped, GError **error)
+{
+	gchar *xml;
+	xmlDocPtr doc = NULL;
+	GError *lerror = NULL;
+	GSList *sources_list = NULL;
+
+	/* create sources_list from XML */
+	GtkTextIter start, end;
+	gtk_text_buffer_get_start_iter (sped->priv->buffer, &start);
+	gtk_text_buffer_get_end_iter (sped->priv->buffer, &end);
+	xml = gtk_text_buffer_get_text (sped->priv->buffer, &start, &end, FALSE);
+	if (xml) {
+		doc = xmlParseDoc (BAD_CAST xml);
+		g_free (xml);
+	}
+
+	if (!doc) {
+		g_set_error (&lerror, 0, 0,
+			     _("Error parsing XML specifications"));
+		goto onerror;
+	}
+
+	xmlNodePtr node;
+	node = xmlDocGetRootElement (doc);
+	if (!node) {
+		/* nothing to do => finished */
+		xmlFreeDoc (doc);
+		return NULL;
+	}
+
+	for (node = node->children; node; node = node->next) {
+		if (!strcmp ((gchar*) node->name, "table") ||
+		    !strcmp ((gchar*) node->name, "query")) {
+			DataSource *source;
+			source = data_source_new_from_xml_node (sped->priv->bcnc, node, &lerror);
+			if (!source)
+				goto onerror;
+			else
+				sources_list = g_slist_prepend (sources_list, source);
+		}
+	}
+	xmlFreeDoc (doc);
+	doc = NULL;
+	sources_list = g_slist_reverse (sources_list);
+
+	/* reorder sources_list */
+	GArray *sources_array;
+	sources_array = create_sources_array (sources_list, &lerror);
+	if (sources_list) {
+		g_slist_foreach (sources_list, (GFunc) g_object_unref, NULL);
+		g_slist_free (sources_list);
+		sources_list = NULL;
+	}
+	if (! sources_array)
+		goto onerror;
+
+	return sources_array;
+
+ onerror:
+	if (doc)
+		xmlFreeDoc (doc);
+	if (sources_list) {
+		g_slist_foreach (sources_list, (GFunc) g_object_unref, NULL);
+		g_slist_free (sources_list);
+	}
+	if (lerror) {
+		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) sped),
+				    lerror && lerror->message ? lerror->message :_("Error parsing XML specifications"));
+		g_clear_error (&lerror);
+	}
+	
+	return NULL;
+}
diff --git a/tools/browser/data-manager/spec-editor.h b/tools/browser/data-manager/spec-editor.h
new file mode 100644
index 0000000..624975b
--- /dev/null
+++ b/tools/browser/data-manager/spec-editor.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 __SPEC_EDITOR_H__
+#define __SPEC_EDITOR_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define SPEC_EDITOR_TYPE            (spec_editor_get_type())
+#define SPEC_EDITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, SPEC_EDITOR_TYPE, SpecEditor))
+#define SPEC_EDITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, SPEC_EDITOR_TYPE, SpecEditorClass))
+#define IS_SPEC_EDITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, SPEC_EDITOR_TYPE))
+#define IS_SPEC_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPEC_EDITOR_TYPE))
+
+
+typedef struct _SpecEditor        SpecEditor;
+typedef struct _SpecEditorClass   SpecEditorClass;
+typedef struct _SpecEditorPrivate SpecEditorPrivate;
+
+struct _SpecEditor {
+	GtkVBox parent;
+	SpecEditorPrivate *priv;
+};
+
+struct _SpecEditorClass {
+	GtkVBoxClass parent_class;
+};
+
+typedef enum {
+	SPEC_EDITOR_XML,
+	SPEC_EDITOR_UI
+} SpecEditorMode;
+
+
+GType       spec_editor_get_type   (void) G_GNUC_CONST;
+SpecEditor *spec_editor_new        (BrowserConnection *bcnc);
+
+void        spec_editor_set_xml_text (SpecEditor *sped, const gchar *xml);
+gchar      *spec_editor_get_xml_text (SpecEditor *sped);
+void        spec_editor_set_mode     (SpecEditor *sped, SpecEditorMode mode);
+SpecEditorMode spec_editor_get_mode  (SpecEditor *sped);
+
+GArray     *spec_editor_get_sources_array (SpecEditor *sped, GError **error);
+void        spec_editor_destroy_sources_array (GArray *array);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data/Makefile.am b/tools/browser/data/Makefile.am
index a7697e2..bea8cab 100644
--- a/tools/browser/data/Makefile.am
+++ b/tools/browser/data/Makefile.am
@@ -10,17 +10,22 @@ public_icons = \
 private_icons = \
 	hicolor_actions_16x16_bookmark-view.png \
 	hicolor_actions_16x16_history-view.png \
+	hicolor_actions_16x16_glade.png \
 	hicolor_actions_22x22_bookmark-view.png \
 	hicolor_actions_22x22_history-view.png \
+	hicolor_actions_22x22_glade.png \
 	hicolor_actions_24x24_bookmark-view.png \
 	hicolor_actions_24x24_history-view.png \
 	hicolor_actions_24x24_transaction-begin.png \
 	hicolor_actions_24x24_transaction-commit.png \
 	hicolor_actions_24x24_transaction-rollback.png \
+	hicolor_actions_24x24_glade.png \
 	hicolor_actions_32x32_bookmark-view.png \
 	hicolor_actions_32x32_history-view.png \
+	hicolor_actions_32x32_glade.png \
 	hicolor_actions_scalable_bookmark-view.svg \
 	hicolor_actions_scalable_history-view.svg \
+	hicolor_actions_scalable_glade.svg \
 	$(NULL)
 
 noinst_DATA = \
diff --git a/tools/browser/data/hicolor_actions_16x16_glade.png b/tools/browser/data/hicolor_actions_16x16_glade.png
new file mode 100644
index 0000000..4a962f7
Binary files /dev/null and b/tools/browser/data/hicolor_actions_16x16_glade.png differ
diff --git a/tools/browser/data/hicolor_actions_22x22_glade.png b/tools/browser/data/hicolor_actions_22x22_glade.png
new file mode 100644
index 0000000..e29ea62
Binary files /dev/null and b/tools/browser/data/hicolor_actions_22x22_glade.png differ
diff --git a/tools/browser/data/hicolor_actions_24x24_glade.png b/tools/browser/data/hicolor_actions_24x24_glade.png
new file mode 100644
index 0000000..7edbe84
Binary files /dev/null and b/tools/browser/data/hicolor_actions_24x24_glade.png differ
diff --git a/tools/browser/data/hicolor_actions_32x32_glade.png b/tools/browser/data/hicolor_actions_32x32_glade.png
new file mode 100644
index 0000000..9b6dd6b
Binary files /dev/null and b/tools/browser/data/hicolor_actions_32x32_glade.png differ
diff --git a/tools/browser/data/hicolor_actions_scalable_glade.svg b/tools/browser/data/hicolor_actions_scalable_glade.svg
new file mode 100644
index 0000000..a549daa
--- /dev/null
+++ b/tools/browser/data/hicolor_actions_scalable_glade.svg
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="48px"
+   height="48px"
+   id="svg4908"
+   sodipodi:version="0.32"
+   inkscape:version="0.44+devel"
+   sodipodi:docname="glade5.svg"
+   sodipodi:docbase="/home/andreas/project/application icons/48x48"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/andreas/project/application icons/48x48/glade5.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   sodipodi:modified="true">
+  <defs
+     id="defs4910">
+    <linearGradient
+       id="linearGradient22140">
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="0"
+         id="stop22142" />
+      <stop
+         id="stop22148"
+         offset="0.5"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop22144" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22140"
+       id="linearGradient23044"
+       gradientUnits="userSpaceOnUse"
+       x1="18.142136"
+       y1="35"
+       x2="18.142136"
+       y2="42.040661" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22122"
+       id="radialGradient23042"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,-1.000001,1.142856,0,-89.10259,-31.49999)"
+       cx="7"
+       cy="39.464806"
+       fx="7"
+       fy="39.464806"
+       r="3.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient22122">
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0"
+         id="stop22124" />
+      <stop
+         style="stop-color:black;stop-opacity:0;"
+         offset="1"
+         id="stop22126" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22122"
+       id="radialGradient23040"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,-1.000001,1.142856,0,-41.10259,45.50001)"
+       cx="7"
+       cy="39.464806"
+       fx="7"
+       fy="39.464806"
+       r="3.5" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7025">
+      <stop
+         style="stop-color:#e6ce46;stop-opacity:1"
+         offset="0"
+         id="stop7027" />
+      <stop
+         style="stop-color:#d6ba1c;stop-opacity:1"
+         offset="1"
+         id="stop7029" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7025"
+       id="linearGradient10840"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(53,1.428571)"
+       x1="13.630114"
+       y1="28.5"
+       x2="25.208096"
+       y2="41.180992" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7780">
+      <stop
+         style="stop-color:#888a85;stop-opacity:1;"
+         offset="0"
+         id="stop7782" />
+      <stop
+         style="stop-color:#888a85;stop-opacity:0;"
+         offset="1"
+         id="stop7784" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7752">
+      <stop
+         style="stop-color:#888a85;stop-opacity:1"
+         offset="0"
+         id="stop7754" />
+      <stop
+         style="stop-color:#888a85;stop-opacity:0"
+         offset="1"
+         id="stop7756" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7744">
+      <stop
+         style="stop-color:#888a85;stop-opacity:1"
+         offset="0"
+         id="stop7746" />
+      <stop
+         style="stop-color:#888a85;stop-opacity:0"
+         offset="1"
+         id="stop7748" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7736">
+      <stop
+         style="stop-color:#c1c7bc;stop-opacity:1;"
+         offset="0"
+         id="stop7738" />
+      <stop
+         style="stop-color:#e8eae6;stop-opacity:1"
+         offset="1"
+         id="stop7740" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7728">
+      <stop
+         style="stop-color:#d3d7cf;stop-opacity:1"
+         offset="0"
+         id="stop7730" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1"
+         offset="1"
+         id="stop7732" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7720">
+      <stop
+         style="stop-color:#555753;stop-opacity:1;"
+         offset="0"
+         id="stop7722" />
+      <stop
+         style="stop-color:#555753;stop-opacity:0;"
+         offset="1"
+         id="stop7724" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7686">
+      <stop
+         style="stop-color:#555753;stop-opacity:1;"
+         offset="0"
+         id="stop7688" />
+      <stop
+         style="stop-color:#555753;stop-opacity:0;"
+         offset="1"
+         id="stop7690" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6997">
+      <stop
+         style="stop-color:#babdb6;stop-opacity:1;"
+         offset="0"
+         id="stop6999" />
+      <stop
+         style="stop-color:#d7d9d5;stop-opacity:1"
+         offset="1"
+         id="stop7001" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6905">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop6907" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop6909" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4790">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4792" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4794" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4790"
+       id="radialGradient4796"
+       cx="37.030354"
+       cy="12.98915"
+       fx="37.030354"
+       fy="12.98915"
+       r="4.2929163"
+       gradientTransform="matrix(-2.1313511,0,0,1.283833,86.543976,-0.1277984)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         id="stop270"
+         offset="0.0000000"
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
+      <stop
+         id="stop271"
+         offset="1"
+         style="stop-color:#8a8a8a;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         id="stop260"
+         offset="0.0000000"
+         style="stop-color:#729fcf;stop-opacity:1" />
+      <stop
+         id="stop261"
+         offset="1.0000000"
+         style="stop-color:#204a87;stop-opacity:1.0000000" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2251">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop2253" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop2255" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2251"
+       id="linearGradient8166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.277938e-2,-1.0071302,-0.999463,-3.303084e-2,45.620229,49.233341)"
+       x1="32.862488"
+       y1="36.028366"
+       x2="34.170048"
+       y2="38.070381" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6905"
+       id="linearGradient6959"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(11.30501,19.746952)"
+       x1="-15.909902"
+       y1="16.003418"
+       x2="-23"
+       y2="16.091806" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6905"
+       id="linearGradient6962"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-35,-4.5078057)"
+       x1="-15.909902"
+       y1="16.003418"
+       x2="-23"
+       y2="16.091806" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6997"
+       id="linearGradient7003"
+       x1="-17.235727"
+       y1="17.947962"
+       x2="-17.324116"
+       y2="32.53204"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7686"
+       id="linearGradient7692"
+       x1="35.47406"
+       y1="36.912945"
+       x2="35.47406"
+       y2="39.351414"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.0045696,0,-0.1855194)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7720"
+       id="linearGradient7726"
+       x1="-8.28125"
+       y1="12.475584"
+       x2="-8.28125"
+       y2="6.2509766"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2909091,0,0,0.3497268,-33.090909,9.251366)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7728"
+       id="radialGradient7734"
+       cx="8.3376141"
+       cy="40.582108"
+       fx="8.3376141"
+       fy="40.582108"
+       r="20.500002"
+       gradientTransform="matrix(-0.9959857,6.9120707e-8,1.181422e-6,0.8780487,46.845968,3.0487798)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7736"
+       id="linearGradient7742"
+       x1="38.944931"
+       y1="12.11484"
+       x2="37.306168"
+       y2="13.392819"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-1,0,0,1,47.000004,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7744"
+       id="linearGradient7750"
+       x1="18.384777"
+       y1="22"
+       x2="23.292892"
+       y2="22"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7752"
+       id="linearGradient7758"
+       x1="15.114408"
+       y1="27"
+       x2="20.019962"
+       y2="27"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7780"
+       id="linearGradient7786"
+       x1="20.875"
+       y1="14.249998"
+       x2="20.875"
+       y2="18.437498"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22122"
+       id="radialGradient5157"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,-1.000001,1.142856,0,-41.10259,45.50001)"
+       cx="7"
+       cy="39.464806"
+       fx="7"
+       fy="39.464806"
+       r="3.5" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22122"
+       id="radialGradient5159"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,-1.000001,1.142856,0,-89.10259,-31.49999)"
+       cx="7"
+       cy="39.464806"
+       fx="7"
+       fy="39.464806"
+       r="3.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient22140"
+       id="linearGradient5161"
+       gradientUnits="userSpaceOnUse"
+       x1="18.142136"
+       y1="35"
+       x2="18.142136"
+       y2="42.040661" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="15.999999"
+     inkscape:cx="27.406385"
+     inkscape:cy="33.127804"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1674"
+     inkscape:window-height="968"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata4913">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g5149"
+       style="opacity:0.3"
+       transform="matrix(-0.9583333,0,0,0.714282,47,15.000156)">
+      <rect
+         y="35"
+         x="0"
+         height="7"
+         width="4"
+         id="rect5151"
+         style="opacity:1;fill:url(#radialGradient5157);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         transform="scale(-1,-1)"
+         y="-42"
+         x="-48"
+         height="7"
+         width="4"
+         id="rect5153"
+         style="opacity:1;fill:url(#radialGradient5159);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         y="35"
+         x="4"
+         height="7"
+         width="40"
+         id="rect5155"
+         style="opacity:1;fill:url(#linearGradient5161);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+    </g>
+    <path
+       style="fill:url(#radialGradient7734);fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 43.215995,7.5 L 13.471509,7.5 C 13.314168,7.5 3.5,17.314168 3.5,17.471508 L 3.5,42.215992 C 3.5,42.373332 3.626668,42.5 3.784009,42.5 L 43.215995,42.5 C 43.373336,42.5 43.500004,42.373332 43.500004,42.215992 L 43.500004,7.7840084 C 43.500004,7.6266678 43.373336,7.5 43.215995,7.5 z "
+       id="rect4916"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient7692);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 35.532117,40.598798 L 35.532117,14.5 L 7.5130099,14.5 L 7.5130099,36.544822 L 24.393398,36.544822"
+       id="path4974"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="opacity:0.64130435;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.5,16.875001 L 13.062499,8.5 L 42.5,8.5 L 42.5,41.5 L 4.5,41.5 L 4.5,16.875001 z "
+       id="rect5887"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#555753;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 6.0648146,18.566679 L 35.435184,18.566679"
+       id="path4976" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       style="opacity:0.35714285;color:#000000;fill:url(#radialGradient4796);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 4,17.212006 C 4.8898017,15.865378 11.455677,13.481495 14.873341,12.681969 C 14.666262,14.254915 15.023334,19 15.023334,19 C 12.503686,17.625 4.9537451,17.049892 4,17.212006 z "
+       id="path5348"
+       sodipodi:nodetypes="cccc" />
+    <rect
+       style="opacity:0.64130435;fill:url(#linearGradient7786);fill-opacity:1.0;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect7778"
+       width="27"
+       height="3"
+       x="8"
+       y="15" />
+    <path
+       style="opacity:1;color:#000000;fill:url(#linearGradient7742);fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+       d="M 3.500024,17.60468 C 3.486859,16.189538 9.852559,7.3974268 13.735143,7.500906 C 12.762105,7.733901 11.984788,13.668936 13.374508,16.481436 C 10.624508,16.481436 4.463889,15.746506 3.500024,17.60468 z "
+       id="path2210"
+       sodipodi:nodetypes="cccc"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="path2247"
+       d="M 5.78902,15.499999 C 6.517382,14.143041 9.947557,10.391559 11.870143,9.1450438 C 11.631203,10.581831 11.333088,12.855721 11.948728,15.437757 C 11.948728,15.437757 6.569724,15.336642 5.78902,15.499999 z "
+       style="opacity:1;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8166);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.7826087;fill:none;fill-opacity:1;stroke:#555753;stroke-width:1.62018538;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="path4978"
+       sodipodi:cx="11.136932"
+       sodipodi:cy="23.604815"
+       sodipodi:rx="2.3864856"
+       sodipodi:ry="2.4748738"
+       d="M 13.523418 23.604815 A 2.3864856 2.4748738 0 1 1  8.7504468,23.604815 A 2.3864856 2.4748738 0 1 1  13.523418 23.604815 z"
+       transform="matrix(0.6285393,0,0,0.6060913,3.9999997,7.6933267)" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.7826087;fill:none;fill-opacity:1;stroke:#555753;stroke-width:1.62018538;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="path5951"
+       sodipodi:cx="11.136932"
+       sodipodi:cy="23.604815"
+       sodipodi:rx="2.3864856"
+       sodipodi:ry="2.4748738"
+       d="M 13.523418 23.604815 A 2.3864856 2.4748738 0 1 1  8.7504468,23.604815 A 2.3864856 2.4748738 0 1 1  13.523418 23.604815 z"
+       transform="matrix(0.6285393,0,0,0.6060915,4.0000003,12.693323)" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient7750);fill-opacity:1.0;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5955"
+       width="10"
+       height="2"
+       x="14"
+       y="21"
+       rx="0"
+       ry="0" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient7758);fill-opacity:1.0;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5957"
+       width="7"
+       height="2"
+       x="14"
+       y="26"
+       rx="0"
+       ry="0" />
+    <rect
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:#555753;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5963"
+       width="0.15951318"
+       height="3"
+       x="-14.659513"
+       y="-38.5"
+       transform="matrix(0,-1,-1,0,0,0)" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#555753;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+       d="M 23.906407,31.559768 L 15.5,31.559768 L 15.5,34.500001 L 25.525275,34.500001 L 25.525275,33.295103"
+       id="path5965"
+       sodipodi:nodetypes="ccccc" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient7726);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect7718"
+       width="1"
+       height="4"
+       x="-36"
+       y="11"
+       transform="scale(-1,1)" />
+    <g
+       id="g22150"
+       style="opacity:0.3"
+       transform="matrix(-0.916667,0,0,0.714282,48,18.00012)">
+      <rect
+         y="35"
+         x="0"
+         height="7"
+         width="4"
+         id="rect22120"
+         style="opacity:1;fill:url(#radialGradient23040);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         transform="scale(-1,-1)"
+         y="-42"
+         x="-48"
+         height="7"
+         width="4"
+         id="rect22134"
+         style="opacity:1;fill:url(#radialGradient23042);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+      <rect
+         y="35"
+         x="4"
+         height="7"
+         width="40"
+         id="rect22138"
+         style="opacity:1;fill:url(#linearGradient23044);fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" />
+    </g>
+    <g
+       id="g10824"
+       transform="matrix(-1,0,0,1,103,0.5714291)">
+      <path
+         id="path4319"
+         d="M 57.5,14.928571 L 57.5,44.928571 L 96.5,44.928571 L 57.5,14.928571 z M 63.5,27.928571 L 78.5,38.928571 L 63.5,38.928571 L 63.5,27.928571 z "
+         style="fill:url(#linearGradient10840);fill-opacity:1;fill-rule:evenodd;stroke:#a38503;stroke-width:1.00000024px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4326"
+         d="M 61.5,44.928571 L 61.5,41.928571"
+         style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#a38503;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4328"
+         d="M 67.5,44.928571 L 67.5,41.928571"
+         style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#a38503;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4330"
+         d="M 73.5,44.928571 L 73.5,41.964285"
+         style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#a38503;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4332"
+         d="M 79.5,44.928571 L 79.5,41.928571"
+         style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#a38503;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path4334"
+         d="M 85.5,44.928571 L 85.5,41.928571"
+         style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#a38503;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path6126"
+         d="M 58.500002,16.928575 L 58.500002,43.928586 L 93.500014,43.928586 L 58.500002,16.928575 z "
+         style="opacity:0.4;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.0000006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    </g>
+    <path
+       style="opacity:0.4;fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 40.498252,40.542446 L 40.542447,26.532894 L 21.458408,40.563262 L 40.498252,40.542446 z "
+       id="path12221"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/tools/browser/decl.h b/tools/browser/decl.h
index d800ffa..7bcc667 100644
--- a/tools/browser/decl.h
+++ b/tools/browser/decl.h
@@ -47,6 +47,7 @@ typedef struct {
 
 #define ORDER_KEY_SCHEMA 1
 #define ORDER_KEY_QUERIES 2
+#define ORDER_KEY_DATA_MANAGERS 3
 
 G_END_DECLS
 
diff --git a/tools/browser/doc/tmpl/browser-favorites.sgml b/tools/browser/doc/tmpl/browser-favorites.sgml
index 3146364..520b444 100644
--- a/tools/browser/doc/tmpl/browser-favorites.sgml
+++ b/tools/browser/doc/tmpl/browser-favorites.sgml
@@ -34,6 +34,7 @@ Favorites management
 @BROWSER_FAVORITES_TABLES: 
 @BROWSER_FAVORITES_DIAGRAMS: 
 @BROWSER_FAVORITES_QUERIES: 
+ BROWSER_FAVORITES_DATA_MANAGERS: 
 
 <!-- ##### MACRO BROWSER_FAVORITES_NB_TYPES ##### -->
 <para>
diff --git a/tools/browser/main.c b/tools/browser/main.c
index 00b81bf..ef8f74e 100644
--- a/tools/browser/main.c
+++ b/tools/browser/main.c
@@ -34,7 +34,8 @@
 /* Perspectives' factories */
 #include "schema-browser/perspective-main.h"
 #include "query-exec/perspective-main.h"
-#include "dummy-perspective/perspective-main.h"
+#include "data-manager/perspective-main.h"
+/* #include "dummy-perspective/perspective-main.h" */
 
 
 extern BrowserCoreInitFactories browser_core_init_factories;
@@ -45,7 +46,8 @@ main_browser_core_init_factories (void)
 	GSList *factories = NULL;
 	factories = g_slist_append (factories, schema_browser_perspective_get_factory ());
 	factories = g_slist_append (factories, query_exec_perspective_get_factory ());
-	factories = g_slist_append (factories, dummy_perspective_get_factory ());
+	factories = g_slist_append (factories, data_manager_perspective_get_factory ());
+	/* factories = g_slist_append (factories, dummy_perspective_get_factory ()); */
 	return factories;
 }
 
diff --git a/tools/browser/mgr-favorites.c b/tools/browser/mgr-favorites.c
index c5f1035..14336cf 100644
--- a/tools/browser/mgr-favorites.c
+++ b/tools/browser/mgr-favorites.c
@@ -484,6 +484,43 @@ mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const
 								  av, NULL);
 				gda_value_free (av);
 			}
+			else if (fav->type == BROWSER_FAVORITES_DATA_MANAGERS) {
+				if (!snode) {
+					snode = gda_tree_manager_create_node (manager, node, 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);
+
+					g_value_set_int ((av = gda_value_new (G_TYPE_INT)), pos);
+					gda_tree_node_set_node_attribute (snode,
+									  MGR_FAVORITES_POSITION_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);
+				}
+
+				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, MGR_FAVORITES_NAME_ATT_NAME,
+								  av, NULL);
+				gda_value_free (av);
+			}
 			else {
 				TO_IMPLEMENT;
 			}
diff --git a/tools/browser/query-exec/perspective-main.c b/tools/browser/query-exec/perspective-main.c
index c2d4914..9f53593 100644
--- a/tools/browser/query-exec/perspective-main.c
+++ b/tools/browser/query-exec/perspective-main.c
@@ -29,7 +29,7 @@ static BrowserPerspectiveFactory bfact;
 BrowserPerspectiveFactory *
 query_exec_perspective_get_factory (void)
 {
-	bfact.perspective_name = "Query execution";
+	bfact.perspective_name = "Query execution (SQL)";
 	bfact.menu_shortcut = "<control>S";
 	bfact.perspective_create = query_exec_perspective_new;
 
diff --git a/tools/browser/query-exec/query-console.c b/tools/browser/query-exec/query-console.c
index 2493578..91e774e 100644
--- a/tools/browser/query-exec/query-console.c
+++ b/tools/browser/query-exec/query-console.c
@@ -36,17 +36,6 @@
 #include <libgda/sql-parser/gda-sql-parser.h>
 #include <libgda-ui/libgda-ui.h>
 
-#define VARIABLES_HELP _("<small>This area allows to give values to\n" \
-			 "variables defined in the SQL code\n"		\
-			 "using the following syntax:\n"		\
-			 "<b><tt>##&lt;variable name&gt;::&lt;type&gt;[::null]</tt></b>\n" \
-			 "For example:\n"				\
-			 "<span foreground=\"#4e9a06\"><b><tt>##id::int</tt></b></span>\n      defines <b>id</b> as a non NULL integer\n" \
-			 "<span foreground=\"#4e9a06\"><b><tt>##age::string::null</tt></b></span>\n      defines <b>age</b> as a a string\n\n" \
-			 "Valid types are: <tt>string</tt>, <tt>boolean</tt>, <tt>int</tt>,\n" \
-			 "<tt>date</tt>, <tt>time</tt>, <tt>timestamp</tt>, <tt>guint</tt>, <tt>blob</tt> and\n" \
-			 "<tt>binary</tt></small>")
-
 /*
  * Statement execution structures
  */
@@ -230,6 +219,8 @@ query_console_dispose (GObject *object)
 			g_source_remove (tconsole->priv->params_compute_id);
 		if (tconsole->priv->params_popup)
 			gtk_widget_destroy (tconsole->priv->params_popup);
+		if (tconsole->priv->agroup)
+			g_object_unref (tconsole->priv->agroup);
 
 		g_free (tconsole->priv);
 		tconsole->priv = NULL;
@@ -268,9 +259,6 @@ query_console_get_type (void)
 	return type;
 }
 
-static GtkWidget *make_small_button (gboolean is_toggle,
-				     const gchar *label, const gchar *stock_id, const gchar *tooltip);
-
 static void editor_changed_cb (QueryEditor *editor, QueryConsole *tconsole);
 static void editor_execute_request_cb (QueryEditor *editor, QueryConsole *tconsole);
 static void sql_clear_clicked_cb (GtkButton *button, QueryConsole *tconsole);
@@ -367,6 +355,7 @@ query_console_new (BrowserConnection *bcnc)
 
 	wid = gtk_label_new ("");
 	gtk_label_set_markup (GTK_LABEL (wid), VARIABLES_HELP);
+	gtk_misc_set_alignment (GTK_MISC (wid), -1, 0.);
 	gtk_container_add (GTK_CONTAINER (tconsole->priv->params_form_box), wid);
 	tconsole->priv->params_form = wid;
 	
@@ -374,24 +363,24 @@ query_console_new (BrowserConnection *bcnc)
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
 	gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 5);
 
-	button = make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear the editor"));
+	button = browser_make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear the editor's\ncontents"));
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (sql_clear_clicked_cb), tconsole);
 
-	button = make_small_button (TRUE, _("Variables"), NULL, _("Show variables needed\nto execute SQL"));
+	button = browser_make_small_button (TRUE, _("Variables"), NULL, _("Show variables needed\nto execute SQL"));
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	tconsole->priv->params_toggle = GTK_TOGGLE_BUTTON (button);
 	g_signal_connect (button, "toggled",
 			  G_CALLBACK (sql_variables_clicked_cb), tconsole);
 
-	button = make_small_button (FALSE, _("Execute"), GTK_STOCK_EXECUTE, _("Execute SQL in editor"));
+	button = browser_make_small_button (FALSE, _("Execute"), GTK_STOCK_EXECUTE, _("Execute SQL in editor"));
 	tconsole->priv->exec_button = button;
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (sql_execute_clicked_cb), tconsole);
 	
-	button = make_small_button (FALSE, _("Indent"), GTK_STOCK_INDENT, _("Indent SQL in editor\n"
+	button = browser_make_small_button (FALSE, _("Indent"), GTK_STOCK_INDENT, _("Indent SQL in editor\n"
 									    "and make the code more readable\n"
 									    "(removes comments)"));
 	tconsole->priv->indent_button = button;
@@ -399,7 +388,7 @@ query_console_new (BrowserConnection *bcnc)
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (sql_indent_clicked_cb), tconsole);
 
-	button = make_small_button (FALSE, _("Favorite"), STOCK_ADD_BOOKMARK, _("Add SQL to favorite"));
+	button = browser_make_small_button (FALSE, _("Favorite"), STOCK_ADD_BOOKMARK, _("Add SQL to favorite"));
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (sql_favorite_clicked_cb), tconsole);
@@ -431,14 +420,14 @@ query_console_new (BrowserConnection *bcnc)
 	gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
 
-	button = make_small_button (FALSE, _("Copy"), GTK_STOCK_COPY, _("Copy selected history\nto editor"));
+	button = browser_make_small_button (FALSE, _("Copy"), GTK_STOCK_COPY, _("Copy selected history\nto editor"));
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (history_copy_clicked_cb), tconsole);
 	tconsole->priv->history_copy_button = button;
 	gtk_widget_set_sensitive (button, FALSE);
 
-	button = make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear history"));
+	button = browser_make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear history"));
 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (history_clear_clicked_cb), tconsole);
@@ -489,45 +478,6 @@ connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, Qu
 	}
 }
 
-static GtkWidget *
-make_small_button (gboolean is_toggle, const gchar *label, const gchar *stock_id, const gchar *tooltip)
-{
-	GtkWidget *button, *hbox = NULL;
-
-	if (is_toggle)
-		button = gtk_toggle_button_new ();
-	else
-		button = gtk_button_new ();
-	if (label && stock_id) {
-		hbox = gtk_hbox_new (FALSE, 0);
-		gtk_container_add (GTK_CONTAINER (button), hbox);
-		gtk_widget_show (hbox);
-	}
-
-	if (stock_id) {
-		GtkWidget *image;
-		image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
-		if (hbox)
-			gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
-		else
-			gtk_container_add (GTK_CONTAINER (button), image);
-		gtk_widget_show (image);
-	}
-	if (label) {
-		GtkWidget *wid;
-		wid = gtk_label_new (label);
-		if (hbox)
-			gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5);
-		else
-			gtk_container_add (GTK_CONTAINER (button), wid);
-		gtk_widget_show (wid);
-	}
-
-	if (tooltip)
-		gtk_widget_set_tooltip_text (button, tooltip);
-	return button;
-}
-
 static void
 history_changed_cb (QueryEditor *history, QueryConsole *tconsole)
 {
@@ -717,6 +667,7 @@ static void
 sql_clear_clicked_cb (GtkButton *button, QueryConsole *tconsole)
 {
 	query_editor_set_text (tconsole->priv->editor, NULL);
+	gtk_widget_grab_focus (GTK_WIDGET (tconsole->priv->editor));
 }
 
 static void
diff --git a/tools/browser/query-exec/query-console.h b/tools/browser/query-exec/query-console.h
index 3b26769..6f86e66 100644
--- a/tools/browser/query-exec/query-console.h
+++ b/tools/browser/query-exec/query-console.h
@@ -20,7 +20,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __QUERY_INFO_H__
+#ifndef __QUERY_CONSOLE_H__
 #define __QUERY_CONSOLE_H__
 
 #include <gtk/gtk.h>
diff --git a/tools/browser/query-exec/query-exec-perspective.c b/tools/browser/query-exec/query-exec-perspective.c
index 52f1601..cceea2d 100644
--- a/tools/browser/query-exec/query-exec-perspective.c
+++ b/tools/browser/query-exec/query-exec-perspective.c
@@ -52,7 +52,6 @@ struct _QueryExecPerspectivePrivate {
 	BrowserConnection *bcnc;
 	
 	GtkActionGroup *action_group;
-	gboolean updating_transaction_status;
 };
 
 GType
@@ -134,10 +133,6 @@ static void nb_page_removed_cb (GtkNotebook *nb, GtkNotebookPage *page, gint pag
 			       QueryExecPerspective *perspective);
 static void close_button_clicked_cb (GtkWidget *wid, GtkWidget *page_widget);
 
-static void transaction_status_changed_cb (BrowserConnection *bcnc, QueryExecPerspective *perspective);
-
-static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy,
-				gchar *reason, QueryExecPerspective *perspective);
 /**
  * query_exec_perspective_new
  *
@@ -195,44 +190,12 @@ query_exec_perspective_new (BrowserWindow *bwin)
 	gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
 	gtk_widget_show_all (paned);
 
-	/* transaction status detection */
-	g_signal_connect (bcnc, "transaction-status-changed",
-			  G_CALLBACK (transaction_status_changed_cb), bpers);
-
 	gtk_widget_grab_focus (page);
 
-	/* busy connection handling */
-	connection_busy_cb (perspective->priv->bcnc, browser_connection_is_busy (perspective->priv->bcnc, NULL),
-			    NULL, perspective);
-	g_signal_connect (perspective->priv->bcnc, "busy",
-			  G_CALLBACK (connection_busy_cb), perspective);
-
 	return bpers;
 }
 
 static void
-connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy,
-		    gchar *reason, QueryExecPerspective *perspective)
-{
-	if (perspective->priv->action_group) {
-		GtkAction *action;
-		gboolean bsens = FALSE, csens = FALSE;
-		if (!is_busy) {
-			if (browser_connection_get_transaction_status (bcnc))
-				csens = TRUE;
-			else
-				bsens = TRUE;
-		}
-		action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecBegin");
-		gtk_action_set_sensitive (action, bsens);
-		action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecCommit");
-		gtk_action_set_sensitive (action, csens);
-		action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecRollback");
-		gtk_action_set_sensitive (action, csens);
-	}
-}
-
-static void
 fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
 			  const gchar *selection, QueryExecPerspective *perspective)
 {
@@ -299,13 +262,8 @@ query_exec_perspective_dispose (GObject *object)
 
 	perspective = QUERY_EXEC_PERSPECTIVE (object);
 	if (perspective->priv) {
-		g_signal_handlers_disconnect_by_func (perspective->priv->bcnc,
-						      G_CALLBACK (transaction_status_changed_cb), perspective);
-		if (perspective->priv->bcnc) {
-			g_signal_handlers_disconnect_by_func (perspective->priv->bcnc,
-							      G_CALLBACK (connection_busy_cb), perspective);
+		if (perspective->priv->bcnc)
 			g_object_unref (perspective->priv->bcnc);
-		}
 
 		if (perspective->priv->action_group)
 			g_object_unref (perspective->priv->action_group);
@@ -351,98 +309,10 @@ query_exec_add_cb (GtkAction *action, BrowserPerspective *bpers)
 	gtk_widget_grab_focus (page);
 }
 
-static void
-transaction_status_changed_cb (BrowserConnection *bcnc, QueryExecPerspective *perspective)
-{
-	if (!perspective->priv->action_group)
-		return;
-
-	GtkAction *action;
-	gboolean trans_started;
-
-	trans_started = browser_connection_get_transaction_status (bcnc) ? TRUE : FALSE;
-	perspective->priv->updating_transaction_status = TRUE;
-
-	action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecBegin");
-	gtk_action_set_sensitive (action, !trans_started);
-
-	action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecCommit");
-	gtk_action_set_sensitive (action, trans_started);
-
-	action = gtk_action_group_get_action (perspective->priv->action_group, "QueryExecRollback");
-	gtk_action_set_sensitive (action, trans_started);
-				      
-	perspective->priv->updating_transaction_status = FALSE;
-}
-
-static void
-transaction_begin_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	QueryExecPerspective *perspective;
-	BrowserConnection *bcnc;
-
-	perspective = QUERY_EXEC_PERSPECTIVE (bpers);
-	bcnc = perspective->priv->bcnc;
-	if (!perspective->priv->updating_transaction_status) {
-		GError *error = NULL;
-		if (! browser_connection_begin (bcnc, &error)) {
-			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bpers)),
-					    _("Error starting transaction: %s"),
-					    error && error->message ? error->message : _("No detail"));
-			g_clear_error (&error);
-		}
-	}
-}
-
-static void
-transaction_commit_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	QueryExecPerspective *perspective;
-	BrowserConnection *bcnc;
-
-	perspective = QUERY_EXEC_PERSPECTIVE (bpers);
-	bcnc = perspective->priv->bcnc;
-	if (!perspective->priv->updating_transaction_status) {
-		GError *error = NULL;
-		if (! browser_connection_commit (bcnc, &error)) {
-			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bpers)),
-					    _("Error committing transaction: %s"),
-					    error && error->message ? error->message : _("No detail"));
-			g_clear_error (&error);
-		}
-	}
-}
-
-static void
-transaction_rollback_cb (GtkAction *action, BrowserPerspective *bpers)
-{
-	QueryExecPerspective *perspective;
-	BrowserConnection *bcnc;
-
-	perspective = QUERY_EXEC_PERSPECTIVE (bpers);
-	bcnc = perspective->priv->bcnc;
-	if (!perspective->priv->updating_transaction_status) {
-		GError *error = NULL;
-		if (! browser_connection_rollback (bcnc, &error)) {
-			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bpers)),
-					    _("Error rolling back transaction: %s"),
-					    error && error->message ? error->message : _("No detail"));
-			g_clear_error (&error);
-		}
-	}
-}
-
 static GtkActionEntry ui_actions[] = {
         { "QueryExecMenu", NULL, N_("_Query"), NULL, "QueryExecMenu", NULL },
         { "QueryExecItem1", STOCK_CONSOLE, N_("_New editor"), "<control>T", N_("Open a new query editor"),
           G_CALLBACK (query_exec_add_cb)},
-        { "QueryExecBegin", BROWSER_STOCK_BEGIN, N_("_Begin"), NULL, N_("Begin a new transaction"),
-          G_CALLBACK (transaction_begin_cb)},
-        { "QueryExecCommit", BROWSER_STOCK_COMMIT, N_("_Commit"), NULL, N_("Commit current transaction"),
-          G_CALLBACK (transaction_commit_cb)},
-        { "QueryExecRollback", BROWSER_STOCK_ROLLBACK, N_("_Rollback"), NULL, N_("Rollback current transaction"),
-          G_CALLBACK (transaction_rollback_cb)},
-
 };
 
 static const gchar *ui_actions_info =
@@ -451,18 +321,12 @@ static const gchar *ui_actions_info =
 	"    <placeholder name='MenuExtension'>"
         "      <menu name='QueryExec' action='QueryExecMenu'>"
         "        <menuitem name='QueryExecItem1' action= 'QueryExecItem1'/>"
-        "        <menuitem name='QueryExecBegin' action= 'QueryExecBegin'/>"
-        "        <menuitem name='QueryExecCommit' action= 'QueryExecCommit'/>"
-        "        <menuitem name='QueryExecRollback' action= 'QueryExecRollback'/>"
         "      </menu>"
 	"    </placeholder>"
         "  </menubar>"
         "  <toolbar name='ToolBar'>"
         "    <separator/>"
         "    <toolitem action='QueryExecItem1'/>"
-        "    <toolitem action='QueryExecBegin'/>"
-        "    <toolitem action='QueryExecCommit'/>"
-        "    <toolitem action='QueryExecRollback'/>"
         "  </toolbar>"
         "</ui>";
 
@@ -477,13 +341,8 @@ query_exec_perspective_get_actions_group (BrowserPerspective *perspective)
 		agroup = gtk_action_group_new ("QueryExecActions");
 		gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
 		bpers->priv->action_group = g_object_ref (agroup);
-
-		connection_busy_cb (bpers->priv->bcnc, browser_connection_is_busy (bpers->priv->bcnc, NULL),
-				    NULL, bpers);
 	}
 	
-	transaction_status_changed_cb (bpers->priv->bcnc, bpers);
-
 	return bpers->priv->action_group;
 }
 
diff --git a/tools/browser/query-exec/query-favorite-selector.c b/tools/browser/query-exec/query-favorite-selector.c
index 00e24fd..3aebbb2 100644
--- a/tools/browser/query-exec/query-favorite-selector.c
+++ b/tools/browser/query-exec/query-favorite-selector.c
@@ -245,6 +245,7 @@ prop_save_timeout (QueryFavoriteSelector *tsel)
 			g_error_free (error);
 	}
 
+	g_free (fav.contents);
 	tsel->priv->prop_save_timeout = 0;
 	return FALSE; /* remove timeout */
 }
@@ -535,12 +536,15 @@ cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
 			    COLUMN_NAME, &name, COLUMN_SUMMARY, &summary, -1);
 	tmp1 = g_markup_printf_escaped ("%s", name);
 	tmp2 = g_markup_printf_escaped ("%s", summary);
+	g_free (name);
+	g_free (summary);
+
 	markup = g_strdup_printf ("%s\n<small>%s</small>", tmp1, tmp2);
-	g_object_set ((GObject*) cell, "markup", markup, NULL);
 	g_free (tmp1);
 	g_free (tmp2);
-	g_free (name);
-	g_free (summary);
+
+	g_object_set ((GObject*) cell, "markup", markup, NULL);
+	g_free (markup);
 }
 
 
diff --git a/tools/browser/support.c b/tools/browser/support.c
index 4f54b4c..c3363c6 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2009 The GNOME Foundation.
+ * Copyright (C) 2009 - 2010The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -429,3 +429,42 @@ connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, GdaDataModel
 		}
 	}
 }
+
+GtkWidget *
+browser_make_small_button (gboolean is_toggle, const gchar *label, const gchar *stock_id, const gchar *tooltip)
+{
+	GtkWidget *button, *hbox = NULL;
+
+	if (is_toggle)
+		button = gtk_toggle_button_new ();
+	else
+		button = gtk_button_new ();
+	if (label && stock_id) {
+		hbox = gtk_hbox_new (FALSE, 0);
+		gtk_container_add (GTK_CONTAINER (button), hbox);
+		gtk_widget_show (hbox);
+	}
+
+	if (stock_id) {
+		GtkWidget *image;
+		image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
+		if (hbox)
+			gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+		else
+			gtk_container_add (GTK_CONTAINER (button), image);
+		gtk_widget_show (image);
+	}
+	if (label) {
+		GtkWidget *wid;
+		wid = gtk_label_new (label);
+		if (hbox)
+			gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5);
+		else
+			gtk_container_add (GTK_CONTAINER (button), wid);
+		gtk_widget_show (wid);
+	}
+
+	if (tooltip)
+		gtk_widget_set_tooltip_text (button, tooltip);
+	return button;
+}
diff --git a/tools/browser/support.h b/tools/browser/support.h
index 8409d97..8f15e1f 100644
--- a/tools/browser/support.h
+++ b/tools/browser/support.h
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2009 The GNOME Foundation.
+ * Copyright (C) 2009 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  * 	Vivien Malerba <malerba gnome-db org>
@@ -40,10 +40,13 @@ GtkWidget*         browser_make_tab_label_with_pixbuf (const gchar *label,
 						       GdkPixbuf *pixbuf, gboolean with_close,
 						       GtkWidget **out_close_button);
 
+GtkWidget          *browser_make_small_button (gboolean is_toggle,
+					       const gchar *label, const gchar *stock_id, const gchar *tooltip);
+
 /*
  * Widgets navigation
  */
-GtkWidget        *browser_find_parent_widget (GtkWidget *current, GType requested_type);
+GtkWidget          *browser_find_parent_widget (GtkWidget *current, GType requested_type);
 
 /*
  * icons
@@ -64,7 +67,7 @@ typedef enum {
 	BROWSER_ICON_LAST
 } BrowserIconType;
 
-GdkPixbuf         *browser_get_pixbuf_icon (BrowserIconType type);
+GdkPixbuf          *browser_get_pixbuf_icon (BrowserIconType type);
 
 /*
  * Connections list
@@ -75,7 +78,18 @@ enum
 	CNC_LIST_COLUMN_NAME = 1,
 	CNC_LIST_NUM_COLUMNS
 };
-GdaDataModel *browser_get_connections_list (void);
+GdaDataModel       *browser_get_connections_list (void);
+
+#define VARIABLES_HELP _("<small>This area allows to give values to\n" \
+			 "variables defined in the SQL code\n"		\
+			 "using the following syntax:\n"		\
+			 "<b><tt>##&lt;variable name&gt;::&lt;type&gt;[::null]</tt></b>\n" \
+			 "For example:\n"				\
+			 "<span foreground=\"#4e9a06\"><b><tt>##id::int</tt></b></span>\n      defines <b>id</b> as a non NULL integer\n" \
+			 "<span foreground=\"#4e9a06\"><b><tt>##age::string::null</tt></b></span>\n      defines <b>age</b> as a a string\n\n" \
+			 "Valid types are: <tt>string</tt>, <tt>boolean</tt>, <tt>int</tt>,\n" \
+			 "<tt>date</tt>, <tt>time</tt>, <tt>timestamp</tt>, <tt>guint</tt>, <tt>blob</tt> and\n" \
+			 "<tt>binary</tt></small>")
 
 G_END_DECLS
 



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