[libgda] GdaBrowser: new data source editor for the data manager perspective



commit 2ad2914689d6686dcde552b57a4777e022706d84
Author: Vivien Malerba <malerba gnome-db org>
Date:   Wed Sep 1 15:55:56 2010 +0200

    GdaBrowser: new data source editor for the data manager perspective

 po/POTFILES.in                                     |    1 +
 tools/browser/browser-window.c                     |   41 +-
 tools/browser/browser-window.h                     |    1 +
 tools/browser/data-manager/DataSourceEdition.dia   |  Bin 0 -> 2492 bytes
 tools/browser/data-manager/Makefile.am             |    2 +
 tools/browser/data-manager/data-console.c          |  192 +++++-
 .../data-manager/data-manager-perspective.c        |   23 +-
 tools/browser/data-manager/data-source-editor.c    |  319 ++++++++
 tools/browser/data-manager/data-source-editor.h    |   60 ++
 tools/browser/data-manager/data-source-manager.c   |   92 ++-
 tools/browser/data-manager/data-source-manager.h   |    7 +-
 tools/browser/data-manager/data-source.c           |  842 +++++++++++++-------
 tools/browser/data-manager/data-source.h           |   26 +-
 tools/browser/data-manager/marshal.list            |    1 +
 tools/browser/data-manager/ui-spec-editor.c        |  282 ++++++-
 tools/browser/data-manager/ui-spec-editor.h        |    5 +-
 tools/browser/data-manager/xml-spec-editor.c       |   79 ++-
 tools/browser/doc/gda-browser-sections.txt         |    1 +
 tools/browser/doc/tmpl/browser-window.sgml         |    9 +
 tools/browser/support.c                            |    3 +
 20 files changed, 1642 insertions(+), 344 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 54a4c70..4826287 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -255,6 +255,7 @@ tools/browser/data-manager/data-console.c
 tools/browser/data-manager/data-favorite-selector.c
 tools/browser/data-manager/data-manager-perspective.c
 tools/browser/data-manager/data-source.c
+tools/browser/data-manager/data-source-editor.c
 tools/browser/data-manager/data-widget.c
 tools/browser/data-manager/ui-spec-editor.c
 tools/browser/data-manager/xml-spec-editor.c
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index d3d8f5e..9a1b728 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -30,6 +30,7 @@
 #include "browser-spinner.h"
 #include "browser-stock-icons.h"
 #include "connection-binding-properties.h"
+#include <gdk/gdkkeysyms.h>
 
 /*
  * structure representing a 'tab' in a window
@@ -59,6 +60,7 @@ static void browser_window_init (BrowserWindow *bwin);
 static void browser_window_dispose (GObject *object);
 
 static gboolean window_state_event (GtkWidget *widget, GdkEventWindowState *event);
+static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event);
 static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, BrowserWindow *bwin);
 
 static void connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin);
@@ -137,6 +139,7 @@ browser_window_class_init (BrowserWindowClass *klass)
 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
 	widget_class->window_state_event = window_state_event;
+	widget_class->key_press_event = key_press_event;
 	parent_class = g_type_class_peek_parent (klass);
 
 	browser_window_signals[FULLSCREEN_CHANGED] =
@@ -883,13 +886,30 @@ window_close_cb (GtkAction *action, BrowserWindow *bwin)
 static void
 window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin)
 {
-	if (gtk_toggle_action_get_active (action))
+	if (gtk_toggle_action_get_active (action)) {
 		gtk_window_fullscreen (GTK_WINDOW (bwin));
+		browser_show_notice (GTK_WINDOW (bwin),
+				     "fullscreen-esc",
+				     _("Hit the Escape key\nto leave the fullscreen mode"));
+	}
 	else
 		gtk_window_unfullscreen (GTK_WINDOW (bwin));
 }
 
 static gboolean
+key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+	if ((event->keyval == GDK_Escape) &&
+	    browser_window_is_fullscreen (BROWSER_WINDOW (widget))) {
+		browser_window_set_fullscreen (BROWSER_WINDOW (widget), FALSE);
+		return TRUE;
+	}
+
+	/* parent class */
+	return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+}
+
+static gboolean
 window_state_event (GtkWidget *widget, GdkEventWindowState *event)
 {
 	BrowserWindow *bwin = BROWSER_WINDOW (widget);
@@ -1330,6 +1350,8 @@ browser_window_change_perspective (BrowserWindow *bwin, const gchar *perspective
 /**
  * browser_window_is_fullscreen
  * @bwin: a #BrowserWindow
+ *
+ * Returns: %TRUE if @bwin is fullscreen
  */
 gboolean
 browser_window_is_fullscreen (BrowserWindow *bwin)
@@ -1337,3 +1359,20 @@ browser_window_is_fullscreen (BrowserWindow *bwin)
 	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), FALSE);
 	return bwin->priv->fullscreen;
 }
+
+/**
+ * browser_window_set_fullscreen
+ * @bwin: a #BrowserWindow
+ * @fullscreen:
+ *
+ * Requires @bwin to be fullscreen if @fullscreen is %TRUE
+ */
+void
+browser_window_set_fullscreen (BrowserWindow *bwin, gboolean fullscreen)
+{
+	GtkAction *action;
+	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
+	
+	action = gtk_action_group_get_action (bwin->priv->agroup, "WindowFullScreen");
+	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), fullscreen);
+}
diff --git a/tools/browser/browser-window.h b/tools/browser/browser-window.h
index 302214a..3c97bd3 100644
--- a/tools/browser/browser-window.h
+++ b/tools/browser/browser-window.h
@@ -65,6 +65,7 @@ void                browser_window_customize_perspective_ui (BrowserWindow *bwin
 
 BrowserPerspective *browser_window_change_perspective     (BrowserWindow *bwin, const gchar *perspective);
 
+void                browser_window_set_fullscreen         (BrowserWindow *bwin, gboolean fullscreen);
 gboolean            browser_window_is_fullscreen          (BrowserWindow *bwin);
 
 G_END_DECLS
diff --git a/tools/browser/data-manager/DataSourceEdition.dia b/tools/browser/data-manager/DataSourceEdition.dia
new file mode 100644
index 0000000..97ca7af
Binary files /dev/null and b/tools/browser/data-manager/DataSourceEdition.dia differ
diff --git a/tools/browser/data-manager/Makefile.am b/tools/browser/data-manager/Makefile.am
index 7548c98..f9e130a 100644
--- a/tools/browser/data-manager/Makefile.am
+++ b/tools/browser/data-manager/Makefile.am
@@ -34,6 +34,8 @@ libperspective_la_SOURCES = \
 	ui-spec-editor.h \
 	data-manager-perspective.h \
 	data-manager-perspective.c \
+	data-source-editor.h \
+	data-source-editor.c \
 	data-source-manager.h \
 	data-source-manager.c
 
diff --git a/tools/browser/data-manager/data-console.c b/tools/browser/data-manager/data-console.c
index a935668..8c4b24b 100644
--- a/tools/browser/data-manager/data-console.c
+++ b/tools/browser/data-manager/data-console.c
@@ -37,6 +37,7 @@
 #include <libgda/sql-parser/gda-sql-parser.h>
 #include <libgda-ui/libgda-ui.h>
 #include "data-source-manager.h"
+#include <gdk/gdkkeysyms.h>
 
 #define MAIN_PAGE_EDITORS 0
 #define MAIN_PAGE_DATA 1
@@ -78,6 +79,11 @@ struct _DataConsolePrivate {
         GtkWidget *popup_container; /* to enter canvas's name */
         GtkWidget *name_entry;
         GtkWidget *real_save_button;
+
+	GtkWidget *clear_xml_button;
+	GtkWidget *add_source_button;
+	GtkWidget *add_source_menu;
+	gpointer   add_source_menu_index; /* used to know if @add_source_menu needs to be rebuilt */
 };
 
 static void data_console_class_init (DataConsoleClass *klass);
@@ -85,8 +91,11 @@ static void data_console_init       (DataConsole *dconsole, DataConsoleClass *kl
 static void data_console_dispose    (GObject *object);
 static void data_console_show_all   (GtkWidget *widget);
 static void data_console_grab_focus (GtkWidget *widget);
+static gboolean key_press_event     (GtkWidget *widget, GdkEventKey *event);
+
 
 static void data_source_mgr_changed_cb (DataSourceManager *mgr, DataConsole *dconsole);
+static void data_source_source_changed_cb (DataSourceManager *mgr, DataSource *source, DataConsole *dconsole);
 
 /* BrowserPage interface */
 static void                 data_console_page_init (BrowserPageIface *iface);
@@ -109,6 +118,7 @@ data_console_class_init (DataConsoleClass *klass)
 
 	GTK_WIDGET_CLASS (klass)->show_all = data_console_show_all;
 	GTK_WIDGET_CLASS (klass)->grab_focus = data_console_grab_focus;
+	GTK_WIDGET_CLASS (klass)->key_press_event = key_press_event;
 	object_class->dispose = data_console_dispose;
 }
 
@@ -131,6 +141,25 @@ data_console_grab_focus (GtkWidget *widget)
 	gtk_widget_grab_focus (GTK_WIDGET (dconsole->priv->xml_sped));
 }
 
+static gboolean
+key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+	DataConsole *dconsole;
+	dconsole = DATA_CONSOLE (widget);
+	if ((event->keyval == GDK_Escape) &&
+	    (gtk_notebook_get_current_page (GTK_NOTEBOOK (dconsole->priv->main_notebook)) == MAIN_PAGE_DATA)) {
+		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), TRUE);
+			return TRUE;
+		}
+	}
+
+	/* parent class */
+	return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+}
+
 static void
 data_console_page_init (BrowserPageIface *iface)
 {
@@ -155,6 +184,8 @@ data_console_dispose (GObject *object)
 
 	/* free memory */
 	if (dconsole->priv) {
+		if (dconsole->priv->add_source_menu)
+			gtk_widget_destroy (dconsole->priv->add_source_menu);
 		if (dconsole->priv->params_form)
 			gtk_widget_destroy (dconsole->priv->params_form);
 		if (dconsole->priv->popup_container)
@@ -167,6 +198,9 @@ data_console_dispose (GObject *object)
 			g_signal_handlers_disconnect_by_func (dconsole->priv->mgr,
 							      G_CALLBACK (data_source_mgr_changed_cb),
 							      dconsole);
+			g_signal_handlers_disconnect_by_func (dconsole->priv->mgr,
+							      G_CALLBACK (data_source_source_changed_cb),
+							      dconsole);
 			g_object_unref (dconsole->priv->mgr);
 		}
 		g_free (dconsole->priv);
@@ -222,6 +256,7 @@ data_console_new_with_fav_id (BrowserConnection *bcnc, gint fav_id)
 }
 
 static void editor_clear_clicked_cb (GtkButton *button, DataConsole *dconsole);
+static void add_source_clicked_cb (GtkButton *button, DataConsole *dconsole);
 static void variables_clicked_cb (GtkToggleButton *button, DataConsole *dconsole);
 static void execute_clicked_cb (GtkButton *button, DataConsole *dconsole);
 #ifdef HAVE_GDU
@@ -246,8 +281,10 @@ data_console_new (BrowserConnection *bcnc)
 
 	dconsole->priv->bcnc = g_object_ref (bcnc);
 	dconsole->priv->mgr = data_source_manager_new (bcnc);
-	g_signal_connect (dconsole->priv->mgr, "changed",
+	g_signal_connect (dconsole->priv->mgr, "list-changed",
 			  G_CALLBACK (data_source_mgr_changed_cb), dconsole);
+	g_signal_connect (dconsole->priv->mgr, "source-changed",
+			  G_CALLBACK (data_source_source_changed_cb), dconsole);
 	
 	/* header */
         GtkWidget *hbox, *label, *wid;
@@ -329,10 +366,12 @@ data_console_new (BrowserConnection *bcnc)
 	dconsole->priv->editors_notebook = nb;
 
 	dconsole->priv->xml_sped = xml_spec_editor_new (dconsole->priv->mgr);
+	gtk_widget_show (dconsole->priv->xml_sped);
 	gtk_notebook_append_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
 				  dconsole->priv->xml_sped, NULL);
 
 	dconsole->priv->ui_sped = ui_spec_editor_new (dconsole->priv->mgr);
+	gtk_widget_show (dconsole->priv->ui_sped);
 	gtk_notebook_append_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
 				  dconsole->priv->ui_sped, NULL);
 
@@ -342,11 +381,20 @@ data_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 = browser_make_small_button (FALSE, _("Clear"), GTK_STOCK_CLEAR, _("Clear the editor's\ncontents"));
+	button = browser_make_small_button (FALSE, _("Reset"), GTK_STOCK_CLEAR,
+					    _("Reset the editor's\ncontents"));
+	dconsole->priv->clear_xml_button = button;
 	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 (FALSE, _("Add data source"), GTK_STOCK_ADD,
+					    _("Add a new data source"));
+	dconsole->priv->add_source_button = button;
+	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (add_source_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);
@@ -362,7 +410,7 @@ data_console_new (BrowserConnection *bcnc)
 	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), TRUE);
+	spec_editor_toggled_cb (GTK_TOGGLE_BUTTON (button), dconsole);
 	g_signal_connect (button, "toggled",
 			  G_CALLBACK (spec_editor_toggled_cb), dconsole);
 
@@ -416,8 +464,22 @@ data_console_new (BrowserConnection *bcnc)
 "        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>"
+#undef DEFAULT_XML
+
+
+#define DEFAULT_XML \
+"<data>\n" \
+"    <!--\n\n" \
+"    <table name=\"\"/>\n" \
+"        <depend foreign_key_table=\"\"/>\n" \
+"    </table>\n" \
+"    <query title=\"\" id=\"\">\n" \
+"        SELECT ...\n" \
+"    </query>\n\n" \
+"    -->\n" \
+"</data>"
 
-	//xml_spec_editor_set_xml_text (XML_SPEC_EDITOR (dconsole->priv->xml_sped), DEFAULT_XML);
+	xml_spec_editor_set_xml_text (XML_SPEC_EDITOR (dconsole->priv->xml_sped), DEFAULT_XML);
 
 	gtk_widget_grab_focus (dconsole->priv->xml_sped);
 	return (GtkWidget*) dconsole;
@@ -578,8 +640,22 @@ variables_clicked_cb (GtkToggleButton *button, DataConsole *dconsole)
 static void
 spec_editor_toggled_cb (GtkToggleButton *button, DataConsole *dconsole)
 {
+	gint display;
+	display = gtk_toggle_button_get_active (button) ? EDITOR_PAGE_XML : EDITOR_PAGE_UI;
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
-				       (gtk_toggle_button_get_active (button) ? EDITOR_PAGE_XML : EDITOR_PAGE_UI));
+				       display);
+	switch (display) {
+	case EDITOR_PAGE_XML:
+		gtk_widget_set_sensitive (dconsole->priv->clear_xml_button, TRUE);
+		gtk_widget_set_sensitive (dconsole->priv->add_source_button, FALSE);
+		break;
+	case EDITOR_PAGE_UI:
+		gtk_widget_set_sensitive (dconsole->priv->clear_xml_button, FALSE);
+		gtk_widget_set_sensitive (dconsole->priv->add_source_button, TRUE);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
 }
 
 static void
@@ -593,6 +669,12 @@ param_activated_cb (GdauiBasicForm *form, DataConsole *dconsole)
 }
 
 static void
+data_source_source_changed_cb (DataSourceManager *mgr, DataSource *source, DataConsole *dconsole)
+{
+	data_source_mgr_changed_cb (mgr, dconsole);
+}
+
+static void
 data_source_mgr_changed_cb (DataSourceManager *mgr, DataConsole *dconsole)
 {
 	if (dconsole->priv->params_form) {
@@ -618,18 +700,100 @@ data_source_mgr_changed_cb (DataSourceManager *mgr, DataConsole *dconsole)
 	gtk_container_add (GTK_CONTAINER (dconsole->priv->params_form_box),
 			   dconsole->priv->params_form);
 	gtk_widget_show (dconsole->priv->params_form);
-
-	/* force showing variables */
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dconsole->priv->params_toggle), show_variables);
 }
 
 static void
 editor_clear_clicked_cb (GtkButton *button, DataConsole *dconsole)
 {
-	xml_spec_editor_set_xml_text (XML_SPEC_EDITOR (dconsole->priv->xml_sped), "");
+	xml_spec_editor_set_xml_text (XML_SPEC_EDITOR (dconsole->priv->xml_sped), DEFAULT_XML);
 	gtk_widget_grab_focus (dconsole->priv->xml_sped);
 }
 
+static void
+add_source_mitem_activated_cb (GtkMenuItem *mitem, DataConsole *dconsole)
+{
+	const gchar *table;
+	DataSource *source;
+	gchar *str;
+	GSList *list;
+
+	table = (gchar*) g_object_get_data ((GObject*) mitem, "_table");
+	g_print ("Add data source for [%s]\n", table);
+
+	source = data_source_new (dconsole->priv->bcnc, DATA_SOURCE_UNKNOWN);
+	list = (GSList*) data_source_manager_get_sources (dconsole->priv->mgr);
+	str = g_strdup_printf (_("source%d"), g_slist_length (list) + 1);
+	data_source_set_id (source, str);
+	g_free (str);
+	if (table)
+		data_source_set_table (source, table, NULL);
+	else
+		data_source_set_query (source, "SELECT", NULL);
+	data_source_manager_add_source (dconsole->priv->mgr, source);
+	ui_spec_editor_select_source (UI_SPEC_EDITOR (dconsole->priv->ui_sped), source);
+	g_object_unref (source);
+}
+
+static void
+add_source_clicked_cb (GtkButton *button, DataConsole *dconsole)
+{
+	GdaMetaStruct *mstruct;
+	mstruct = browser_connection_get_meta_struct (dconsole->priv->bcnc);
+
+	if (dconsole->priv->add_source_menu &&
+	    (dconsole->priv->add_source_menu_index != (gpointer) mstruct)) {
+		gtk_widget_destroy (dconsole->priv->add_source_menu);
+		dconsole->priv->add_source_menu = NULL;
+		dconsole->priv->add_source_menu_index = NULL;
+	}
+	if (dconsole->priv->add_source_menu) {
+		gtk_menu_popup (GTK_MENU (dconsole->priv->add_source_menu), NULL, NULL,
+			NULL, NULL, 0,
+			gtk_get_current_event_time ());
+		return;
+	}
+
+	GtkWidget *menu, *mitem;
+	menu = gtk_menu_new ();
+	mitem = gtk_menu_item_new_with_label (_("Data source from query"));
+	g_signal_connect (mitem, "activate",
+			  G_CALLBACK (add_source_mitem_activated_cb), dconsole);
+	gtk_widget_show (mitem);
+	gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+
+	if (mstruct) {
+		gboolean sep_added = FALSE;
+		GSList *dbo_list, *list;
+		dbo_list = gda_meta_struct_get_all_db_objects (mstruct);
+		for (list = dbo_list; list; list = list->next) {
+			GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+			gchar *str;
+			if (dbo->obj_type != GDA_META_DB_TABLE)
+				continue;
+			if (! sep_added) {
+				mitem = gtk_separator_menu_item_new ();
+				gtk_widget_show (mitem);
+				gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+				sep_added = TRUE;
+			}
+			str = g_strdup_printf (_("For table: %s"), dbo->obj_short_name);
+			mitem = gtk_menu_item_new_with_label (str);
+			g_object_set_data_full ((GObject*) mitem, "_table",
+						g_strdup (dbo->obj_short_name), g_free);
+			g_signal_connect (mitem, "activate",
+					  G_CALLBACK (add_source_mitem_activated_cb), dconsole);
+			gtk_widget_show (mitem);
+			gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);			
+		}
+		g_slist_free (dbo_list);
+	}
+	dconsole->priv->add_source_menu_index = (gpointer) mstruct;
+	dconsole->priv->add_source_menu = menu;
+	gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+			NULL, NULL, 0,
+			gtk_get_current_event_time ());
+}
+
 static GtkWidget *
 create_widget (DataConsole *dconsole, GArray *sources_array, GError **error)
 {
@@ -666,6 +830,11 @@ compose_mode_toggled_cb (GtkToggleAction *action, DataConsole *dconsole)
 {
 	gint pagenb;
 
+	/* show variables if necessary */
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dconsole->priv->params_toggle),
+				      dconsole->priv->params_form &&
+				      GDAUI_IS_BASIC_FORM (dconsole->priv->params_form) ? TRUE : FALSE);
+
 	if (dconsole->priv->toggling) {
 		dconsole->priv->toggling = FALSE;
 		return;
@@ -710,6 +879,11 @@ compose_mode_toggled_cb (GtkToggleAction *action, DataConsole *dconsole)
 		pagenb = MAIN_PAGE_EDITORS;
 	}
 
+	if (pagenb == MAIN_PAGE_DATA)
+		browser_show_notice ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) dconsole),
+				     "data-manager-exec-mode-switched",
+				     _("Switching to execution mode.\n\nHit the Escape key\n"
+				       "to return to the compose mode"));
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (dconsole->priv->main_notebook), pagenb);
 }
 
diff --git a/tools/browser/data-manager/data-manager-perspective.c b/tools/browser/data-manager/data-manager-perspective.c
index bc330f7..e41bbf7 100644
--- a/tools/browser/data-manager/data-manager-perspective.c
+++ b/tools/browser/data-manager/data-manager-perspective.c
@@ -249,15 +249,34 @@ fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType f
 {
 	/* find or create page for this favorite */
         GtkWidget *page_contents;
-	gint current_page;
+	gint current_page, npages, i;
 	DataConsole *page_to_reuse = NULL;
 
 	if (fav_type != BROWSER_FAVORITES_DATA_MANAGERS)
 		return;
 
+	/* change current page if possible */
+	npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (perspective->priv->notebook));
+	for (i = 0; i < npages; i++) {
+		page_contents = gtk_notebook_get_nth_page (GTK_NOTEBOOK (perspective->priv->notebook),
+							   i);
+		
+		if (IS_DATA_CONSOLE (page_contents)) {
+			gchar *text;
+			text = data_console_get_text (DATA_CONSOLE (page_contents));
+			if (text && selection && !strcmp (text, selection)) {
+				gtk_notebook_set_current_page (GTK_NOTEBOOK (perspective->priv->notebook),
+							       i);
+				return;
+			}
+		}
+	}
+
+	/* create a new page, or reuse the current empty one */
 	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);
+		page_contents = gtk_notebook_get_nth_page (GTK_NOTEBOOK (perspective->priv->notebook),
+							   current_page);
 		
 		if (IS_DATA_CONSOLE (page_contents)) {
 			gchar *text;
diff --git a/tools/browser/data-manager/data-source-editor.c b/tools/browser/data-manager/data-source-editor.c
new file mode 100644
index 0000000..1026d33
--- /dev/null
+++ b/tools/browser/data-manager/data-source-editor.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 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-source-editor.h"
+
+/* signals */
+enum {
+	CHANGED,
+	LAST_SIGNAL
+};
+
+gint data_source_editor_signals [LAST_SIGNAL] = { 0 };
+
+/* 
+ * Main static functions 
+ */
+static void data_source_editor_class_init (DataSourceEditorClass *klass);
+static void data_source_editor_init (DataSourceEditor *editor);
+static void data_source_editor_dispose (GObject *object);
+
+static void attribute_changed_cb (GdaSet *set, GdaHolder *holder, DataSourceEditor *editor);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+struct _DataSourceEditorPrivate {
+	DataSource *source;
+        GdaSet *attributes;
+	GdauiBasicForm *form;
+};
+
+GType
+data_source_editor_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (DataSourceEditorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_source_editor_class_init,
+			NULL,
+			NULL,
+			sizeof (DataSourceEditor),
+			0,
+			(GInstanceInitFunc) data_source_editor_init
+		};
+
+		
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GTK_TYPE_VBOX, "DataSourceEditor", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+data_source_editor_class_init (DataSourceEditorClass *klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* signals */
+	/*data_source_editor_signals [CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataSourceEditorClass, changed),
+                              NULL, NULL,
+                              _dm_marshal_VOID__VOID, G_TYPE_NONE, 0);*/
+
+	object_class->dispose = data_source_editor_dispose;
+}
+
+static void
+data_source_editor_init (DataSourceEditor *editor)
+{
+	editor->priv = g_new0 (DataSourceEditorPrivate, 1);
+	editor->priv->attributes = gda_set_new_inline (4,
+						       "id", G_TYPE_STRING, "",
+						       "descr", G_TYPE_STRING, "",
+						       "table", G_TYPE_STRING, "",
+						       "sql", G_TYPE_STRING, "");
+	g_signal_connect (editor->priv->attributes, "holder-changed",
+			  G_CALLBACK (attribute_changed_cb), editor);
+
+	GdaHolder *holder;
+	GValue *value;
+	holder = gda_set_get_holder (editor->priv->attributes, "id");
+	g_object_set ((GObject*) holder, "name", _("Id"),
+		      "description",
+		      _("Data source's ID\n"
+			"(as referenced by other data sources)"), NULL);
+
+	holder = gda_set_get_holder (editor->priv->attributes, "descr");
+	g_object_set ((GObject*) holder, "name", _("Description"),
+		      "description", _("Data source's description"), NULL);
+
+	holder = gda_set_get_holder (editor->priv->attributes, "table");
+	g_object_set ((GObject*) holder, "name", _("Table"),
+		      "description", _("Table to display data from, leave empty\n"
+				       "to specify a SELECT statement instead"), NULL);
+
+	holder = gda_set_get_holder (editor->priv->attributes, "sql");
+	g_object_set ((GObject*) holder, "name", _("SELECT\nSQL"),
+		      "description", _("SQL to select data"), NULL);
+	value = gda_value_new_from_string ("text:PROG_LANG=gda-sql", G_TYPE_STRING);
+        gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
+        gda_value_free (value);
+
+	GtkWidget *form;
+	form = gdaui_basic_form_new (editor->priv->attributes);
+	editor->priv->form = GDAUI_BASIC_FORM (form);
+	gtk_box_pack_start (GTK_BOX (editor), form, TRUE, TRUE, 0);
+	gtk_widget_show (form);
+}
+
+static void
+data_source_editor_dispose (GObject *object)
+{
+	DataSourceEditor *editor;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (IS_DATA_SOURCE_EDITOR (object));
+
+	editor = DATA_SOURCE_EDITOR (object);
+	if (editor->priv) {
+		if (editor->priv->source)
+			g_object_unref (editor->priv->source);
+		if (editor->priv->attributes) {
+			g_signal_handlers_disconnect_by_func (editor->priv->attributes,
+							      G_CALLBACK (attribute_changed_cb), editor);
+
+			g_object_unref (editor->priv->attributes);
+		}
+
+		g_free (editor->priv);
+		editor->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+/**
+ * data_source_editor_new
+ *
+ * Creates a new #DataSourceEditor widget
+ *
+ * Returns: a new widget
+ */
+GtkWidget *
+data_source_editor_new (void)
+{
+	DataSourceEditor *editor;
+
+	editor = DATA_SOURCE_EDITOR (g_object_new (DATA_SOURCE_EDITOR_TYPE, NULL));
+
+	return (GtkWidget*) editor;
+}
+
+/**
+ * data_source_editor_display_source
+ * @editor: a #DataSourceEditor widget
+ * @source: the #DataSource for which properties are displayed, or %NULL
+ *
+ * Update @editor's display with @source's properties
+ */
+void
+data_source_editor_display_source (DataSourceEditor *editor, DataSource *source)
+{
+	g_return_if_fail (IS_DATA_SOURCE_EDITOR (editor));
+	g_return_if_fail (! source || IS_DATA_SOURCE (source));
+	GdaStatement *stmt;
+	gchar *sql = NULL;
+
+	g_signal_handlers_block_by_func (editor->priv->attributes,
+					 G_CALLBACK (attribute_changed_cb), editor);
+
+	if (editor->priv->source)
+		g_object_unref (editor->priv->source);
+	if (source) {
+		editor->priv->source = g_object_ref (source);
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "id", data_source_get_id (source)));
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "descr", data_source_get_title (source)));
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "table", data_source_get_table (source)));
+		stmt = data_source_get_statement (source);
+		if (stmt)
+			sql = gda_statement_to_sql_extended (stmt, NULL, NULL,
+							     GDA_STATEMENT_SQL_PRETTY | GDA_STATEMENT_SQL_PARAMS_SHORT,
+							     NULL, NULL);
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL, "sql", sql));
+		g_free (sql);
+		
+		switch (data_source_get_source_type (source)) {
+		case DATA_SOURCE_TABLE:
+			gdaui_basic_form_entry_set_editable (editor->priv->form,
+							     gda_set_get_holder (editor->priv->attributes, "sql"),
+							     FALSE);
+			break;
+		case DATA_SOURCE_SELECT:
+			gdaui_basic_form_entry_set_editable (editor->priv->form,
+							     gda_set_get_holder (editor->priv->attributes, "sql"),
+							     TRUE);
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+		
+	}
+	else {
+		editor->priv->source = NULL;
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "id", NULL));
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "descr", NULL));
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "table", NULL));
+		g_assert (gda_set_set_holder_value (editor->priv->attributes, NULL,
+						    "sql", NULL));
+	}
+	gtk_widget_set_sensitive (GTK_WIDGET (editor), source ? TRUE : FALSE);
+
+	g_signal_handlers_unblock_by_func (editor->priv->attributes,
+					   G_CALLBACK (attribute_changed_cb), editor);
+}
+
+static void
+attribute_changed_cb (GdaSet *set, GdaHolder *holder, DataSourceEditor *editor)
+{
+	const gchar *id, *str = NULL;
+	const GValue *cvalue;
+
+	if (! editor->priv->source)
+		return;
+
+	g_signal_handlers_block_by_func (editor->priv->attributes,
+					 G_CALLBACK (attribute_changed_cb), editor);
+
+	id = gda_holder_get_id (holder);
+	cvalue = gda_holder_get_value (holder);
+	if (G_VALUE_TYPE (cvalue) == G_TYPE_STRING)
+		str = g_value_get_string (cvalue);
+
+	g_assert (id);
+	if (!strcmp (id, "id")) {
+		data_source_set_id (editor->priv->source, str);
+	}
+	else if (!strcmp (id, "descr")) {
+		data_source_set_title (editor->priv->source, str);
+	}
+	else if (!strcmp (id, "table")) {
+		GdaHolder *holder;
+		holder = gda_set_get_holder (editor->priv->attributes, "sql");
+
+		if (!str || !*str) {
+			GdaStatement *stmt;
+			gchar *sql = NULL;
+
+			stmt = data_source_get_statement (editor->priv->source);
+			if (stmt)
+				sql = gda_statement_to_sql_extended (stmt, NULL, NULL,
+								     GDA_STATEMENT_SQL_PRETTY |
+								     GDA_STATEMENT_SQL_PARAMS_SHORT,
+								     NULL, NULL);
+			data_source_set_query (editor->priv->source, sql, NULL);
+			g_free (sql);
+			gdaui_basic_form_entry_set_editable (editor->priv->form, holder, TRUE);
+		}
+		else {
+			data_source_set_table (editor->priv->source, str, NULL);
+			gdaui_basic_form_entry_set_editable (editor->priv->form, holder, FALSE);
+
+			GdaStatement *stmt;
+			gchar *sql = NULL;
+
+			stmt = data_source_get_statement (editor->priv->source);
+			if (stmt)
+				sql = gda_statement_to_sql_extended (stmt, NULL, NULL,
+								     GDA_STATEMENT_SQL_PRETTY |
+								     GDA_STATEMENT_SQL_PARAMS_SHORT,
+								     NULL, NULL);
+			g_assert (gda_holder_set_value_str (holder, NULL, sql, NULL));
+			g_free (sql);
+		}
+	}
+	else if (!strcmp (id, "sql"))
+		data_source_set_query (editor->priv->source, str, NULL);
+	else
+		g_assert_not_reached ();
+
+	g_signal_handlers_unblock_by_func (editor->priv->attributes,
+					   G_CALLBACK (attribute_changed_cb), editor);
+}
diff --git a/tools/browser/data-manager/data-source-editor.h b/tools/browser/data-manager/data-source-editor.h
new file mode 100644
index 0000000..7ca4437
--- /dev/null
+++ b/tools/browser/data-manager/data-source-editor.h
@@ -0,0 +1,60 @@
+/* 
+ * Copyright (C) 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_EDITOR_H_
+#define __DATA_SOURCE_EDITOR_H_
+
+#include "data-source.h"
+
+G_BEGIN_DECLS
+
+#define DATA_SOURCE_EDITOR_TYPE          (data_source_editor_get_type())
+#define DATA_SOURCE_EDITOR(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, data_source_editor_get_type(), DataSourceEditor)
+#define DATA_SOURCE_EDITOR_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, data_source_editor_get_type (), DataSourceEditorClass)
+#define IS_DATA_SOURCE_EDITOR(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, data_source_editor_get_type ())
+
+typedef struct _DataSourceEditor DataSourceEditor;
+typedef struct _DataSourceEditorClass DataSourceEditorClass;
+typedef struct _DataSourceEditorPrivate DataSourceEditorPrivate;
+
+/* struct for the object's data */
+struct _DataSourceEditor
+{
+	GtkVBox                  object;
+	DataSourceEditorPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _DataSourceEditorClass
+{
+	GtkVBoxClass             parent_class;
+
+	/* signals */
+	/*void             (*changed) (DataSourceEditor *mgr);*/
+};
+
+GType         data_source_editor_get_type            (void) G_GNUC_CONST;
+
+GtkWidget    *data_source_editor_new                 (void);
+void          data_source_editor_display_source      (DataSourceEditor *editor, DataSource *source);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/data-manager/data-source-manager.c b/tools/browser/data-manager/data-source-manager.c
index ebabcc1..0986773 100644
--- a/tools/browser/data-manager/data-source-manager.c
+++ b/tools/browser/data-manager/data-source-manager.c
@@ -33,11 +33,12 @@
 
 /* signals */
 enum {
-	CHANGED,
+	LIST_CHANGED,
+	SOURCE_CHANGED,
 	LAST_SIGNAL
 };
 
-gint data_source_manager_signals [LAST_SIGNAL] = { 0 };
+gint data_source_manager_signals [LAST_SIGNAL] = { 0, 0 };
 
 /* 
  * Main static functions 
@@ -53,6 +54,7 @@ struct _DataSourceManagerPrivate {
 	BrowserConnection *bcnc;
 	GSList *sources_list;
         GdaSet *params; /* execution params */
+	gboolean emit_changes;
 };
 
 GType
@@ -90,13 +92,23 @@ data_source_manager_class_init (DataSourceManagerClass *klass)
 	parent_class = g_type_class_peek_parent (klass);
 
 	/* signals */
-	data_source_manager_signals [CHANGED] =
-                g_signal_new ("changed",
+	data_source_manager_signals [LIST_CHANGED] =
+                g_signal_new ("list-changed",
                               G_TYPE_FROM_CLASS (object_class),
                               G_SIGNAL_RUN_FIRST,
-                              G_STRUCT_OFFSET (DataSourceManagerClass, changed),
+                              G_STRUCT_OFFSET (DataSourceManagerClass, list_changed),
                               NULL, NULL,
                               _dm_marshal_VOID__VOID, G_TYPE_NONE, 0);
+	data_source_manager_signals [SOURCE_CHANGED] =
+                g_signal_new ("source-changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataSourceManagerClass, source_changed),
+                              NULL, NULL,
+                              _dm_marshal_VOID__OBJECT, G_TYPE_NONE, 1, DATA_SOURCE_TYPE);
+
+	klass->list_changed = NULL;
+	klass->source_changed = NULL;
 
 	object_class->dispose = data_source_manager_dispose;
 }
@@ -105,6 +117,13 @@ static void
 data_source_manager_init (DataSourceManager *mgr)
 {
 	mgr->priv = g_new0 (DataSourceManagerPrivate, 1);
+	mgr->priv->emit_changes = TRUE;
+}
+
+static void
+source_changed_cb (DataSource *source, DataSourceManager *mgr)
+{
+	g_signal_emit (mgr, data_source_manager_signals[SOURCE_CHANGED], 0, source);
 }
 
 static void
@@ -121,7 +140,13 @@ data_source_manager_dispose (GObject *object)
 			g_object_unref (mgr->priv->params);
 
 		if (mgr->priv->sources_list) {
-			g_slist_foreach (mgr->priv->sources_list, (GFunc) g_object_unref, NULL);
+			GSList *list;
+			for (list = mgr->priv->sources_list; list; list = list->next) {
+				g_signal_handlers_disconnect_by_func (list->data,
+								      G_CALLBACK (source_changed_cb), mgr);
+				g_object_unref ((GObject*) list->data);
+
+			}
 			g_slist_free (mgr->priv->sources_list);
 			mgr->priv->sources_list = NULL;
 		}
@@ -265,43 +290,50 @@ data_source_manager_add_source (DataSourceManager *mgr, DataSource *source)
 			}
 		}
 	}
-	g_signal_emit (mgr, data_source_manager_signals[CHANGED], 0);
+	if (mgr->priv->emit_changes)
+		g_signal_emit (mgr, data_source_manager_signals[LIST_CHANGED], 0);
+
+	g_signal_connect (source, "changed",
+			  G_CALLBACK (source_changed_cb), mgr);
 
 #ifdef DEBUG_SOURCES_SORT
 	g_print ("Sources in manager:\n");
-#endif
 	GSList *list;
 	gint i;
 	for (i = 0, list = mgr->priv->sources_list; list; list = list->next, i++) {
 		DataSource *source = DATA_SOURCE (list->data);
-#ifdef DEBUG_SOURCES_SORT
 		g_print ("\t %d ... %s\n", i, 
 			 data_source_get_title (source));
-#endif
 	}
-#ifdef DEBUG_SOURCES_SORT
 	g_print ("\n");
 #endif
 }
 
 /**
- * data_source_manager_remove_all
- * @mgr:
+ * data_source_manager_replace_all
+ * @mgr: a #DataSourceManager object
+ * @sources_list: a list of #DataSource objects, or %NULL
+ *
+ * Replaces all the data sources by the ones in @sources_list.
  */
 void
-data_source_manager_remove_all (DataSourceManager *mgr)
+data_source_manager_replace_all (DataSourceManager *mgr, const GSList *sources_list)
 {
+	GSList *list;
 	g_return_if_fail (IS_DATA_SOURCE_MANAGER (mgr));
 
-	if (mgr->priv->sources_list) {
-		GSList *list;
-		list = mgr->priv->sources_list;
-		mgr->priv->sources_list = NULL;
-		g_signal_emit (mgr, data_source_manager_signals[CHANGED], 0);
+	mgr->priv->emit_changes = FALSE;
 
-		g_slist_foreach (list, (GFunc) g_object_unref, NULL);
-		g_slist_free (list);
-	}
+	/* remove any existing data source */
+	for (; mgr->priv->sources_list; )
+		data_source_manager_remove_source (mgr,
+						   DATA_SOURCE (mgr->priv->sources_list->data));
+
+	for (list = sources_list; list; list = list->next)
+		data_source_manager_add_source (mgr, DATA_SOURCE (list->data));
+
+	mgr->priv->emit_changes = TRUE;
+	g_signal_emit (mgr, data_source_manager_signals[LIST_CHANGED], 0);
 }
 
 /**
@@ -316,8 +348,11 @@ data_source_manager_remove_source (DataSourceManager *mgr, DataSource *source)
 	g_return_if_fail (IS_DATA_SOURCE (source));
 
 	g_return_if_fail (g_slist_find (mgr->priv->sources_list, source));
+	g_signal_handlers_disconnect_by_func (source,
+					      G_CALLBACK (source_changed_cb), mgr);
 	mgr->priv->sources_list = g_slist_remove (mgr->priv->sources_list, source);
-	g_signal_emit (mgr, data_source_manager_signals[CHANGED], 0);
+	if (mgr->priv->emit_changes)
+		g_signal_emit (mgr, data_source_manager_signals[LIST_CHANGED], 0);
 	g_object_unref (source);
 }
 
@@ -537,3 +572,14 @@ data_source_manager_get_browser_cnc (DataSourceManager *mgr)
 	g_return_val_if_fail (IS_DATA_SOURCE_MANAGER (mgr), NULL);
 	return mgr->priv->bcnc;
 }
+
+/**
+ * data_source_manager_get_sources
+ * @mgr:
+ */
+const GSList *
+data_source_manager_get_sources (DataSourceManager *mgr)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE_MANAGER (mgr), NULL);
+	return mgr->priv->sources_list;
+}
diff --git a/tools/browser/data-manager/data-source-manager.h b/tools/browser/data-manager/data-source-manager.h
index c5d320c..02bd50d 100644
--- a/tools/browser/data-manager/data-source-manager.h
+++ b/tools/browser/data-manager/data-source-manager.h
@@ -48,7 +48,8 @@ struct _DataSourceManagerClass
 	GObjectClass       parent_class;
 
 	/* signals */
-	void             (*changed) (DataSourceManager *mgr);
+	void             (*list_changed) (DataSourceManager *mgr);
+	void             (*source_changed) (DataSourceManager *mgr, DataSource *source);
 };
 
 GType               data_source_manager_get_type            (void) G_GNUC_CONST;
@@ -56,7 +57,7 @@ GType               data_source_manager_get_type            (void) G_GNUC_CONST;
 DataSourceManager  *data_source_manager_new                 (BrowserConnection *bcnc);
 void                data_source_manager_add_source          (DataSourceManager *mgr, DataSource *source);
 void                data_source_manager_remove_source       (DataSourceManager *mgr, DataSource *source);
-void                data_source_manager_remove_all          (DataSourceManager *mgr);
+void                data_source_manager_replace_all         (DataSourceManager *mgr, const GSList *sources_list);
 
 GdaSet              *data_source_manager_get_params (DataSourceManager *mgr);
 BrowserConnection   *data_source_manager_get_browser_cnc (DataSourceManager *mgr);
@@ -64,6 +65,8 @@ BrowserConnection   *data_source_manager_get_browser_cnc (DataSourceManager *mgr
 GArray              *data_source_manager_get_sources_array (DataSourceManager *mgr, GError **error);
 void                 data_source_manager_destroy_sources_array (GArray *array);
 
+const GSList        *data_source_manager_get_sources       (DataSourceManager *mgr);
+
 
 G_END_DECLS
 
diff --git a/tools/browser/data-manager/data-source.c b/tools/browser/data-manager/data-source.c
index 74cfdfe..b53efaf 100644
--- a/tools/browser/data-manager/data-source.c
+++ b/tools/browser/data-manager/data-source.c
@@ -32,12 +32,13 @@
 
 /* signals */
 enum {
+	CHANGED,
 	EXEC_STARTED,
 	EXEC_FINISHED,
 	LAST_SIGNAL
 };
 
-gint data_source_signals [LAST_SIGNAL] = {0, 0};
+gint data_source_signals [LAST_SIGNAL] = {0, 0, 0};
 
 /* 
  * Main static functions 
@@ -46,22 +47,33 @@ static void data_source_class_init (DataSourceClass *klass);
 static void data_source_init (DataSource *source);
 static void data_source_dispose (GObject *object);
 
+
+static void compute_stmt_and_params (DataSource *source);
+static void compute_import_params (DataSource *source);
+
+
 /* 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             *impl_title;
 	gchar             *id;
+	DataSourceType     source_type;
+
 	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;
 	gboolean           executing;
 	gboolean           exec_again;
 
+	gchar             *tablename;
+	GdaSqlBuilder     *builder;
+	GSList            *dependencies; /* list of strings of table name or ID */
+
 	GdaStatement      *stmt;
 	GdaSet            *ext_params; /* "free" parameters */
 	GdaSet            *params; /* all the params used when executing @stmt */
@@ -107,6 +119,13 @@ data_source_class_init (DataSourceClass *klass)
 	parent_class = g_type_class_peek_parent (klass);
 
 	/* signals */
+	data_source_signals [CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (DataSourceClass, changed),
+                              NULL, NULL,
+                              _dm_marshal_VOID__VOID, G_TYPE_NONE, 0);
 	data_source_signals [EXEC_STARTED] =
                 g_signal_new ("execution-started",
                               G_TYPE_FROM_CLASS (object_class),
@@ -121,6 +140,7 @@ data_source_class_init (DataSourceClass *klass)
                               G_STRUCT_OFFSET (DataSourceClass, execution_finished),
                               NULL, NULL,
                               _dm_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+        klass->changed = NULL;
         klass->execution_started = NULL;
         klass->execution_finished = NULL;
 
@@ -132,6 +152,7 @@ data_source_init (DataSource *source)
 {
 	source->priv = g_new0 (DataSourcePrivate, 1);
 	source->priv->bcnc = NULL;
+	source->priv->source_type = DATA_SOURCE_UNKNOWN;
 	source->priv->need_rerun = FALSE;
 	source->priv->exec_id = 0;
 	source->priv->executing = FALSE;
@@ -155,6 +176,61 @@ ext_params_changed_cb (GdaSet *params, GdaHolder *holder, DataSource *source)
 }
 
 static void
+data_source_reset (DataSource *source)
+{
+	source->priv->source_type = DATA_SOURCE_UNKNOWN;
+	g_clear_error (& source->priv->init_error);
+
+	if (source->priv->builder) {
+		g_object_unref (source->priv->builder);
+		source->priv->builder = NULL;
+	}
+	if (source->priv->stmt) {
+		g_object_unref (source->priv->stmt);
+		source->priv->stmt = NULL;
+	}
+	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);
+		source->priv->params = NULL;
+	}
+	if (source->priv->ext_params) {
+		g_signal_handlers_disconnect_by_func (source->priv->ext_params,
+						      G_CALLBACK (ext_params_changed_cb),
+						      source);
+		g_object_unref (source->priv->ext_params);
+		source->priv->ext_params = NULL;
+	}
+	
+	if (source->priv->tablename) {
+		g_free (source->priv->tablename);
+		source->priv->tablename = NULL;
+	}
+	
+	if (source->priv->dependencies) {
+		g_slist_foreach (source->priv->dependencies, (GFunc) g_free, NULL);
+		g_slist_free (source->priv->dependencies);
+		source->priv->dependencies = NULL;
+	}
+	
+	if (source->priv->model) {
+		g_object_unref (source->priv->model);
+		source->priv->model = NULL;
+	}
+	
+	if (source->priv->export_names) {
+		g_array_free (source->priv->export_names, TRUE);
+		source->priv->export_names = NULL;
+	}
+	
+	if (source->priv->export_columns) {
+		g_hash_table_destroy (source->priv->export_columns);
+		source->priv->export_columns = NULL;
+	}
+}
+
+static void
 data_source_dispose (GObject *object)
 {
 	DataSource *source;
@@ -166,33 +242,10 @@ data_source_dispose (GObject *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);
-		}
-		if (source->priv->ext_params) {
-			g_signal_handlers_disconnect_by_func (source->priv->ext_params,
-							      G_CALLBACK (ext_params_changed_cb),
-							      source);
-			g_object_unref (source->priv->ext_params);
-		}
-
+		data_source_reset (source);
 		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->impl_title);
 
 		g_free (source->priv);
 		source->priv = NULL;
@@ -208,6 +261,28 @@ static gboolean init_from_table_node (DataSource *source, xmlNodePtr node, GErro
 /**
  * data_source_new
  * @bcnc: a #BrowserConnection
+ * @type: the new data source's requested type
+ *
+ * Returns: a new #DataSource object
+ */
+DataSource *
+data_source_new (BrowserConnection *bcnc, DataSourceType type)
+{
+	DataSource *source;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+	source = DATA_SOURCE (g_object_new (DATA_SOURCE_TYPE, NULL));
+	source->priv->bcnc = g_object_ref (bcnc);
+	source->priv->source_type = type;
+	source->priv->id = g_strdup ("NewSource");
+
+	return source;
+}
+
+/**
+ * data_source_new_from_xml_node
+ * @bcnc: a #BrowserConnection
  * @node:
  * @error:
  *
@@ -260,97 +335,14 @@ data_source_new_from_xml_node (BrowserConnection *bcnc, xmlNodePtr node, GError
 static void
 init_from_query (DataSource *source, xmlNodePtr node)
 {
-	GdaSqlParser *parser;
-	const gchar *remain;
 	xmlChar *contents;
 
 #ifdef DEBUG_SOURCE
 	g_print ("%s(%s [%s])\n", __FUNCTION__, source->priv->id, source->priv->title);
 #endif
 	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+1);
-					g_array_append_val (source->priv->export_names, tmp);
-					g_hash_table_insert (source->priv->export_columns, tmp,
-							     GINT_TO_POINTER (i + 1));
-#ifdef DEBUG_SOURCE
-					g_print ("\tEXPORT [%s]\n", tmp);
-#endif
-
-					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));
-#ifdef DEBUG_SOURCE
-						g_print ("\tEXPORT [%s]\n", tmp);
-#endif
-					}
-				}
-			}
-		}
-		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);
-#ifdef DEBUG_SOURCE
-				g_print ("\tIMPORT [%s]\n", gda_holder_get_id (GDA_HOLDER (list->data)));
-#endif
-			}
-
-			g_signal_connect (source->priv->params, "holder-changed",
-					  G_CALLBACK (params_changed_cb), source);
-		}
-	}
-
-#ifdef DEBUG_SOURCE
-	g_print ("\n");
-#endif
+	data_source_set_query (source, (gchar*) contents, &source->priv->init_error);
 }
 
 static GdaMetaTable *
@@ -404,6 +396,7 @@ static gboolean
 init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 {
 	xmlChar *tname;
+	gboolean retval;
 
 #ifdef DEBUG_SOURCE
 	g_print ("%s(%s [%s])\n", __FUNCTION__, source->priv->id, source->priv->title);
@@ -415,79 +408,8 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 			     _("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;
-	}
-	gda_sql_builder_select_set_limit (b,
-					  gda_sql_builder_add_expr (b, NULL,
-								    G_TYPE_INT, DEFAULT_DATA_SELECT_LIMIT),
-					  0);
-
-	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);
 
-		if (mcol->pkey) {
-			/* ORDER BY */
-			gda_sql_builder_select_order_by (b,
-							 gda_sql_builder_add_id (b,
-										 mcol->column_name),
-							 FALSE, 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));
-#ifdef DEBUG_SOURCE
-		g_print ("\tEXPORT [%s]\n", tmp);
-#endif
-
-		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));
-#ifdef DEBUG_SOURCE
-		g_print ("\tEXPORT [%s]\n", tmp);
-#endif
-	}
+	retval = data_source_set_table (source, (gchar*) tname, error);
 	xmlFree (tname);
 
 	/* linking */
@@ -495,82 +417,11 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 	for (subnode = node->children; subnode; subnode = subnode->next) {
 		if (!strcmp ((gchar*)subnode->name, "depend")) {
 			xmlChar *fk_table, *id;
-			GdaMetaTable *mlinked;
 			
 			fk_table = xmlGetProp (subnode, BAD_CAST "foreign_key_table");
-			mlinked = get_meta_table (source, (gchar*) fk_table, error);
-			if (!mlinked) {
-				xmlFree (fk_table);
-				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*) fk_table);
-				xmlFree (fk_table);
-				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*) fk_table);
-				xmlFree (fk_table);
-				if (id) xmlFree (id);
-				g_object_unref (b);	
-				return FALSE;
-			}
-			else if (fk->cols_nb == 1) {
-				gchar *tmp;
-				GdaMetaTableColumn *col;
-				const GdaSqlBuilderId id1 = gda_sql_builder_add_id (b, fk->fk_names_array [0]);
-				tmp = g_strdup_printf ("%s %s", id ? (gchar*) id : (gchar*) fk_table,
-						       fk->ref_pk_names_array [0]);
-
-				col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
-									       fk->ref_pk_cols_array [0] - 1));
-				g_assert (col);
-				const GdaSqlBuilderId id2 = gda_sql_builder_add_param (b, tmp, col->gtype, FALSE);
-				g_free (tmp);
-				const GdaSqlBuilderId id_cond = gda_sql_builder_add_cond (b, GDA_SQL_OPERATOR_TYPE_EQ, id1, id2, 0);
-				gda_sql_builder_set_where (b, id_cond);
-			}
-			else {
-				gchar *tmp;
-				gint i;
-				GdaMetaTableColumn *col;
-				GdaSqlBuilderId andid;
-				GdaSqlBuilderId *op_ids;
-				op_ids = g_new (GdaSqlBuilderId, fk->cols_nb);
-				
-				for (i = 0; i < fk->cols_nb; i++) {
-					const GdaSqlBuilderId id1 = gda_sql_builder_add_id (b, fk->fk_names_array [i]);
-					tmp = g_strdup_printf ("%s %s", id ? (gchar*) id : (gchar*) fk_table,
-							       fk->ref_pk_names_array [i]);
-
-					col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
-										       fk->ref_pk_cols_array [i] - 1));
-					g_assert (col);
-					const GdaSqlBuilderId id2 = gda_sql_builder_add_param (b, tmp, col->gtype, FALSE);
-					g_free (tmp);
-					op_ids [i] = gda_sql_builder_add_cond (b, GDA_SQL_OPERATOR_TYPE_EQ, id1, id2, 0);
-				}
-				andid = gda_sql_builder_add_cond_v (b, GDA_SQL_OPERATOR_TYPE_AND, op_ids, fk->cols_nb);
-				g_free (op_ids);
-				gda_sql_builder_set_where (b, andid);
-			}
-
+			data_source_add_dependendency (source, (gchar *) fk_table, (gchar*) id, error);
 			xmlFree (fk_table);
 			if (id)
 				xmlFree (id);
@@ -578,31 +429,108 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 		}
 	}
 
-	source->priv->stmt = gda_sql_builder_get_statement (b, error);
+	return retval;
+}
 
-	/* 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);
-#ifdef DEBUG_SOURCE
-			g_print ("\tIMPORT [%s]\n", gda_holder_get_id (GDA_HOLDER (list->data)));
-#endif
-		}
+/**
+ * data_source_add_dependendency
+ * @source: a #DataSource
+ * @table: the name of the referenced table
+ * @id: the ID of the referenced data source, or %NULL if its ID is the same as the table name
+ *
+ * Adds a dependency on the @table table, only for DATA_SOURCE_TABLE sources
+ */
+gboolean
+data_source_add_dependendency (DataSource *source, const gchar *table,
+			       const char *id, GError **error)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), FALSE);
+	g_return_val_if_fail (table && *table, FALSE);
+	g_return_val_if_fail (source->priv->source_type == DATA_SOURCE_TABLE, FALSE);
+	g_return_val_if_fail (source->priv->builder, FALSE);
 
-		g_signal_connect (source->priv->params, "holder-changed",
-				  G_CALLBACK (params_changed_cb), source);
+	if (g_slist_find (source->priv->dependencies, id ? id : table))
+		return TRUE;
+
+	GdaMetaTable *mtable, *mlinked;
+	mtable = get_meta_table (source, source->priv->tablename, error);
+	if (!mtable)
+		return FALSE;
+
+	mlinked = get_meta_table (source, table, error);
+	if (!mlinked)
+		return FALSE;
+	
+	/* find foreign key to linked table */
+	GdaMetaTableForeignKey *fk = NULL;
+	GSList *list;
+	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\""), table);
+		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"),
+			     table);
+		return FALSE;
+	}
+	else if (fk->cols_nb == 1) {
+		gchar *tmp;
+		GdaMetaTableColumn *col;
+		const GdaSqlBuilderId id1 = gda_sql_builder_add_id (source->priv->builder,
+								    fk->fk_names_array [0]);
+		tmp = g_strdup_printf ("%s %s", id ? id : table, fk->ref_pk_names_array [0]);
+		
+		col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
+							       fk->ref_pk_cols_array [0] - 1));
+		g_assert (col);
+		const GdaSqlBuilderId id2 = gda_sql_builder_add_param (source->priv->builder, tmp,
+								       col->gtype, FALSE);
+		g_free (tmp);
+		const GdaSqlBuilderId id_cond = gda_sql_builder_add_cond (source->priv->builder,
+									  GDA_SQL_OPERATOR_TYPE_EQ,
+									  id1, id2, 0);
+		gda_sql_builder_set_where (source->priv->builder, id_cond);
+	}
+	else {
+		gchar *tmp;
+		gint i;
+		GdaMetaTableColumn *col;
+		GdaSqlBuilderId andid;
+		GdaSqlBuilderId *op_ids;
+		op_ids = g_new (GdaSqlBuilderId, fk->cols_nb);
+		
+		for (i = 0; i < fk->cols_nb; i++) {
+			const GdaSqlBuilderId id1 = gda_sql_builder_add_id (source->priv->builder,
+									    fk->fk_names_array [i]);
+			tmp = g_strdup_printf ("%s %s", id ? id : table, fk->ref_pk_names_array [i]);
+			
+			col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
+								       fk->ref_pk_cols_array [i] - 1));
+			g_assert (col);
+			const GdaSqlBuilderId id2 = gda_sql_builder_add_param (source->priv->builder,
+									       tmp, col->gtype, FALSE);
+			g_free (tmp);
+			op_ids [i] = gda_sql_builder_add_cond (source->priv->builder, GDA_SQL_OPERATOR_TYPE_EQ,
+							       id1, id2, 0);
+		}
+		andid = gda_sql_builder_add_cond_v (source->priv->builder, GDA_SQL_OPERATOR_TYPE_AND,
+						    op_ids, fk->cols_nb);
+		g_free (op_ids);
+		gda_sql_builder_set_where (source->priv->builder, andid);
 	}
 
-	/*g_print ("SQL [%s]\n", gda_statement_to_sql (source->priv->stmt, NULL, NULL));*/
-	g_object_unref (b);
+	source->priv->dependencies = g_slist_append (source->priv->dependencies, g_strdup (id ? id : table));
 
-#ifdef DEBUG_SOURCE
-	g_print ("\n");
-#endif
-	return source->priv->stmt ? TRUE : FALSE;
+	compute_stmt_and_params (source);
+	return TRUE;
 }
 
 
@@ -612,8 +540,54 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 xmlNodePtr
 data_source_to_xml_node (DataSource *source)
 {
-	TO_IMPLEMENT;
-	return NULL;
+	xmlNodePtr node = NULL;
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	switch (source->priv->source_type) {
+	case DATA_SOURCE_TABLE:
+		node = xmlNewNode (NULL, BAD_CAST "table");
+		if (source->priv->id && g_strcmp0 (source->priv->id, source->priv->tablename))
+			xmlSetProp (node, BAD_CAST "id", BAD_CAST source->priv->id);
+		if (source->priv->title && g_strcmp0 (source->priv->title, source->priv->tablename))
+			xmlSetProp (node, BAD_CAST "title", BAD_CAST source->priv->title);
+		xmlSetProp (node, BAD_CAST "name",
+			    BAD_CAST (source->priv->tablename ? source->priv->tablename : ""));
+
+		if (source->priv->dependencies) {
+			GSList *list;
+			for (list = source->priv->dependencies; list; list = list->next) {
+				xmlNodePtr depnode;
+				depnode = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
+				xmlSetProp (depnode, BAD_CAST "foreign_key_table",
+					    BAD_CAST (list->data));
+			}
+		}
+		break;
+	case DATA_SOURCE_SELECT: {
+		node = xmlNewNode (NULL, BAD_CAST "query");
+		if (source->priv->id)
+			xmlSetProp (node, BAD_CAST "id", BAD_CAST source->priv->id);
+		if (source->priv->title)
+			xmlSetProp (node, BAD_CAST "title", BAD_CAST source->priv->title);
+
+		if (source->priv->stmt) {
+			gchar *sql;
+			sql = gda_statement_to_sql_extended (source->priv->stmt, NULL, NULL,
+							     GDA_STATEMENT_SQL_PRETTY |
+							     GDA_STATEMENT_SQL_PARAMS_SHORT, NULL, NULL);
+			if (sql) {
+				xmlNodeSetContent (node, BAD_CAST sql);
+				g_free (sql);
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	if (node) {
+	}
+	return node;
 }
 
 static gboolean
@@ -856,7 +830,7 @@ data_source_create_grid (DataSource *source)
 
 	GtkWidget *fg;
 	GdauiRawGrid *grid;
-	fg = (GdauiRawGrid*) ui_formgrid_new (source->priv->model, 0);
+	fg = ui_formgrid_new (source->priv->model, 0);
 	grid = ui_formgrid_get_grid_widget (UI_FORMGRID (fg));
 
 	GList *columns, *list;
@@ -899,7 +873,61 @@ data_source_create_grid (DataSource *source)
 }
 
 /**
+ * data_source_set_id
+ * @source: a #DataSource
+ * @id: the new source's ID
+ *
+ * @source MUST NOT be executed when calling this method.
+ */
+void
+data_source_set_id (DataSource *source, const gchar * id)
+{
+	g_return_if_fail (IS_DATA_SOURCE (source));
+	g_return_if_fail (! data_source_execution_going_on (source));
+
+	g_free (source->priv->id);
+	if (id)
+		source->priv->id = g_strdup (id);
+	g_signal_emit (source, data_source_signals [CHANGED], 0);
+}
+
+
+/**
+ * data_source_get_id
+ * @source: a #DataSource
+ *
+ * Returns: the ID, or %NULL if no ID has been defined
+ */
+const gchar *
+data_source_get_id (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+
+	return source->priv->id;
+}
+
+/**
+ * data_source_set_title
+ * @source: a #DataSource
+ * @title: the new source's TITLE
+ *
+ * @source MUST NOT be executed when calling this method.
+ */
+void
+data_source_set_title (DataSource *source, const gchar * title)
+{
+	g_return_if_fail (IS_DATA_SOURCE (source));
+	g_return_if_fail (! data_source_execution_going_on (source));
+
+	g_free (source->priv->title);
+	if (title)
+		source->priv->title = g_strdup (title);
+	g_signal_emit (source, data_source_signals [CHANGED], 0);
+}
+
+/**
  * data_source_get_title
+ * @source: a #DataSource
  */
 const gchar *
 data_source_get_title (DataSource *source)
@@ -908,6 +936,8 @@ data_source_get_title (DataSource *source)
 
 	if (source->priv->title)
 		return source->priv->title;
+	else if (source->priv->impl_title)
+		return source->priv->impl_title;
 	else if (source->priv->id)
 		return source->priv->id;
 	else
@@ -915,6 +945,262 @@ data_source_get_title (DataSource *source)
 }
 
 /**
+ * data_source_set_table
+ *
+ * @source MUST NOT be executed when calling this method.
+ */
+gboolean
+data_source_set_table (DataSource *source, const gchar *table, GError **error)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), FALSE);
+	g_return_val_if_fail (! data_source_execution_going_on (source), FALSE);
+
+	data_source_reset (source);
+	if (!table)
+		return FALSE;
+
+	/* locate table */
+	GdaMetaTable *mtable;
+	mtable = get_meta_table (source, table, error);
+	if (!mtable)
+		return FALSE;
+
+	source->priv->source_type = DATA_SOURCE_TABLE;
+	source->priv->tablename = g_strdup (table);
+
+	g_free (source->priv->impl_title);
+	source->priv->impl_title = g_strdup_printf (_("Contents of '%s'"), table);
+
+	g_free (source->priv->id);
+	source->priv->id = g_strdup ((gchar*) table);
+
+	/* build statement */
+	GSList *list;
+	GdaSqlBuilder *b;
+	gint i;
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	source->priv->builder = b;
+	if (! gda_sql_builder_select_add_target (b, table, NULL)) {
+		g_set_error (error, 0, 0,
+			     _("Could not build SELECT statement"));
+		return FALSE;
+	}
+	gda_sql_builder_select_set_limit (b,
+					  gda_sql_builder_add_expr (b, NULL,
+								    G_TYPE_INT, DEFAULT_DATA_SELECT_LIMIT),
+					  0);
+
+	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);
+
+		if (mcol->pkey) {
+			/* ORDER BY */
+			gda_sql_builder_select_order_by (b,
+							 gda_sql_builder_add_id (b,
+										 mcol->column_name),
+							 FALSE, 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", table, 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));
+#ifdef DEBUG_SOURCE
+		g_print ("\tEXPORT [%s]\n", tmp);
+#endif
+
+		if (source->priv->id)
+			tmp = g_strdup_printf ("%s %d", source->priv->id, i + 1);
+		else
+			tmp = g_strdup_printf ("%s %d", table, 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));
+#ifdef DEBUG_SOURCE
+		g_print ("\tEXPORT [%s]\n", tmp);
+#endif
+	}
+
+	/* compute statement & parameters */
+	compute_stmt_and_params (source);
+	/*g_print ("SQL [%s]\n", gda_statement_to_sql (source->priv->stmt, NULL, NULL));*/
+
+#ifdef DEBUG_SOURCE
+	g_print ("\n");
+#endif
+
+	g_signal_emit (source, data_source_signals [CHANGED], 0);
+	return source->priv->stmt ? TRUE : FALSE;
+}
+
+/**
+ * data_source_set_query
+ *
+ * @source MUST NOT be executed when calling this method.
+ */
+void
+data_source_set_query (DataSource *source, const gchar *sql, GError **warning)
+{
+	g_return_if_fail (IS_DATA_SOURCE (source));
+	g_return_if_fail (! data_source_execution_going_on (source));
+
+	data_source_reset (source);
+
+	source->priv->source_type = DATA_SOURCE_SELECT;
+	if (!sql) {
+		g_signal_emit (source, data_source_signals [CHANGED], 0);
+		return;
+	}
+
+	GdaSqlParser *parser;
+	const gchar *remain;
+	parser = browser_connection_create_parser (source->priv->bcnc);
+	source->priv->stmt = gda_sql_parser_parse_string (parser, sql,
+							  &remain, warning);
+	g_object_unref (parser);
+	if (!source->priv->stmt) {
+		g_signal_emit (source, data_source_signals [CHANGED], 0);
+		return;
+	}
+
+	if (remain)
+		g_set_error (warning, 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+1);
+				g_array_append_val (source->priv->export_names, tmp);
+				g_hash_table_insert (source->priv->export_columns, tmp,
+						     GINT_TO_POINTER (i + 1));
+#ifdef DEBUG_SOURCE
+				g_print ("\tEXPORT [%s]\n", tmp);
+#endif
+				
+				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));
+#ifdef DEBUG_SOURCE
+					g_print ("\tEXPORT [%s]\n", tmp);
+#endif
+				}
+			}
+		}
+	}
+	gda_sql_statement_free (sqlst);
+	
+	/* compute parameters */
+	source->priv->need_rerun = FALSE;
+	compute_import_params (source);
+
+#ifdef DEBUG_SOURCE
+	g_print ("\n");
+#endif
+
+	g_signal_emit (source, data_source_signals [CHANGED], 0);
+}
+
+static void
+compute_stmt_and_params (DataSource *source)
+{
+	g_assert (source->priv->builder);
+	if (source->priv->stmt)
+		g_object_unref (source->priv->stmt);
+	source->priv->stmt = gda_sql_builder_get_statement (source->priv->builder, NULL);
+	compute_import_params (source);
+}
+
+static void
+compute_import_params (DataSource *source)
+{
+	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);
+		source->priv->params = NULL;
+	}
+	g_clear_error (& source->priv->init_error);
+
+	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);
+#ifdef DEBUG_SOURCE
+			g_print ("\tIMPORT [%s]\n", gda_holder_get_id (GDA_HOLDER (list->data)));
+#endif
+		}
+
+		g_signal_connect (source->priv->params, "holder-changed",
+				  G_CALLBACK (params_changed_cb), source);
+	}
+}
+
+/**
+ * data_source_get_table
+ * @source: a #DataSource
+ *
+ * Returns: the name of the table used by @source, if its type is %DATA_SOURCE_TABLE
+ */
+const gchar *
+data_source_get_table (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
+	return source->priv->tablename;	
+}
+
+/**
+ * data_source_get_source_type
+ */
+DataSourceType
+data_source_get_source_type (DataSource *source)
+{
+	g_return_val_if_fail (IS_DATA_SOURCE (source), DATA_SOURCE_UNKNOWN);
+	return source->priv->source_type;
+}
+
+/**
  * data_source_should_rerun
  *
  * The SELECT statement will be re-executed the next time
diff --git a/tools/browser/data-manager/data-source.h b/tools/browser/data-manager/data-source.h
index b7dd4c9..5968931 100644
--- a/tools/browser/data-manager/data-source.h
+++ b/tools/browser/data-manager/data-source.h
@@ -36,6 +36,12 @@ typedef struct _DataSource DataSource;
 typedef struct _DataSourceClass DataSourceClass;
 typedef struct _DataSourcePrivate DataSourcePrivate;
 
+typedef enum {
+	DATA_SOURCE_UNKNOWN,
+	DATA_SOURCE_TABLE,
+	DATA_SOURCE_SELECT,
+} DataSourceType;
+
 /* struct for the object's data */
 struct _DataSource
 {
@@ -49,15 +55,34 @@ struct _DataSourceClass
 	GObjectClass       parent_class;
 
 	/* signals */
+	void             (*changed) (DataSource *source);
 	void             (*execution_started) (DataSource *source);
 	void             (*execution_finished) (DataSource *source, GError *error);
 };
 
 GType               data_source_get_type            (void) G_GNUC_CONST;
 
+DataSourceType      data_source_get_source_type     (DataSource *source);
+void                data_source_set_id              (DataSource *source, const gchar *id);
+const gchar        *data_source_get_id              (DataSource *source);
+void                data_source_set_title           (DataSource *source, const gchar *title);
+const gchar        *data_source_get_title           (DataSource *source);
+
+/* Data source as table API */
+gboolean            data_source_set_table           (DataSource *source, const gchar *table, GError **error);
+const gchar        *data_source_get_table           (DataSource *source);
+gboolean            data_source_add_dependendency   (DataSource *source, const gchar *table,
+						     const char *id, GError **error);
+
+/* Data source as SQL query API */
+void                data_source_set_query           (DataSource *source, const gchar *sql, GError **warning);
+
+/* other API */
+DataSource         *data_source_new                 (BrowserConnection *bcnc, DataSourceType type);
 DataSource         *data_source_new_from_xml_node   (BrowserConnection *bcnc, xmlNodePtr node, GError **error);
 void                data_source_set_params          (DataSource *source, GdaSet *params);
 xmlNodePtr          data_source_to_xml_node         (DataSource *source);
+
 GdaStatement       *data_source_get_statement       (DataSource *source);
 
 GdaSet             *data_source_get_import          (DataSource *source);
@@ -67,7 +92,6 @@ GHashTable         *data_source_get_export_columns  (DataSource *source);
 void                data_source_execute             (DataSource *source, GError **error);
 gboolean            data_source_execution_going_on  (DataSource *source);
 GtkWidget          *data_source_create_grid         (DataSource *source);
-const gchar        *data_source_get_title           (DataSource *source);
 
 /*
 DataSource         *data_source_new_from_table      (BrowserConnection *bcnc,
diff --git a/tools/browser/data-manager/marshal.list b/tools/browser/data-manager/marshal.list
index 9ab3792..d2dbeee 100644
--- a/tools/browser/data-manager/marshal.list
+++ b/tools/browser/data-manager/marshal.list
@@ -25,3 +25,4 @@
 VOID:VOID
 VOID:POINTER
 VOID:INT,ENUM,STRING
+VOID:OBJECT
diff --git a/tools/browser/data-manager/ui-spec-editor.c b/tools/browser/data-manager/ui-spec-editor.c
index 2e43b09..8272428 100644
--- a/tools/browser/data-manager/ui-spec-editor.c
+++ b/tools/browser/data-manager/ui-spec-editor.c
@@ -26,22 +26,20 @@
 #include "data-source.h"
 #include <libgda/libgda.h>
 #include "../support.h"
+#include "data-source-editor.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
+enum
+{
+	COLUMN_DATA_SOURCE,
+	NUM_COLUMNS
+};
 
 struct _UiSpecEditorPrivate {
 	DataSourceManager *mgr;
-	
+	GtkListStore *sources_model;
+	GtkWidget *sources_tree;
+	DataSourceEditor *propsedit;
+
 	/* warnings */
 	GtkWidget  *info;
 };
@@ -65,6 +63,73 @@ ui_spec_editor_class_init (UiSpecEditorClass *klass)
 	object_class->dispose = ui_spec_editor_dispose;
 }
 
+static void
+cell_text_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+		     GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+	DataSource *source;
+	gtk_tree_model_get (tree_model, iter, COLUMN_DATA_SOURCE, &source, -1);
+	g_assert (source);
+
+	GString *mark;
+	const gchar *id, *str;
+	mark = g_string_new ("");
+	/* FIXME: add source ID */
+	id = NULL;
+
+	str = data_source_get_id (source);
+	if (str)
+		g_string_append (mark, str);
+	else
+		g_string_append_c (mark, '-');
+	str = data_source_get_title (source);
+        if (str) {
+		if (!id || strcmp ((gchar*) id, (gchar*) str)) {
+			gchar *tmp;
+			tmp = g_markup_escape_text ((gchar*) str, -1);
+			g_string_append_printf (mark, "\n<small><i>%s</i></small>", tmp);
+			g_free (tmp);
+		}
+        }
+
+	g_object_set (cell, "markup", mark->str, NULL);
+	g_string_free (mark, TRUE);
+}
+
+static void
+cell_pixbuf_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+		       GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+	DataSource *source;
+	gtk_tree_model_get (tree_model, iter, COLUMN_DATA_SOURCE, &source, -1);
+	g_assert (source);
+
+	DataSourceType stype;
+	stype = data_source_get_source_type (source);
+	switch (stype) {
+	case DATA_SOURCE_TABLE:
+		g_object_set (cell, "pixbuf", browser_get_pixbuf_icon (BROWSER_ICON_TABLE), NULL);
+		break;
+	case DATA_SOURCE_SELECT:
+		g_object_set (cell, "pixbuf", browser_get_pixbuf_icon (BROWSER_ICON_QUERY), NULL);
+		break;
+	default:
+		g_object_set (cell, "pixbuf", NULL, NULL);
+		break;
+	}
+}
+
+static void
+data_source_selection_changed_cb (GtkTreeSelection *sel, UiSpecEditor *uied)
+{
+	GtkTreeIter iter;
+	if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
+		DataSource *source;
+		gtk_tree_model_get (GTK_TREE_MODEL (uied->priv->sources_model), &iter,
+				    COLUMN_DATA_SOURCE, &source, -1);
+		data_source_editor_display_source (uied->priv->propsedit, source);
+	}
+}
 
 static void
 ui_spec_editor_init (UiSpecEditor *sped, UiSpecEditorClass *klass)
@@ -73,6 +138,84 @@ ui_spec_editor_init (UiSpecEditor *sped, UiSpecEditorClass *klass)
 
 	/* allocate private structure */
 	sped->priv = g_new0 (UiSpecEditorPrivate, 1);
+	
+	GtkWidget *hpaned;
+	hpaned = gtk_hpaned_new ();
+	gtk_box_pack_start (GTK_BOX (sped), hpaned, TRUE, TRUE, 0);
+	
+	GtkWidget *vbox;
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_paned_add1 (GTK_PANED (hpaned), vbox);
+
+	GtkWidget *label;
+	gchar *str;
+	label = gtk_label_new ("");
+	str = g_strdup_printf ("<b>%s</b>", _("Data sources:"));
+	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);
+
+	/* data sources model & view */
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	sped->priv = g_new0 (UiSpecEditorPrivate, 1);
+	sped->priv->sources_model = gtk_list_store_new (NUM_COLUMNS,
+							G_TYPE_POINTER);
+
+	sped->priv->sources_tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (sped->priv->sources_model));
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (sped->priv->sources_tree), FALSE);
+	gtk_widget_set_size_request (sped->priv->sources_tree, 120, -1);
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_tree_view_column_set_cell_data_func (column, renderer,
+						 (GtkTreeCellDataFunc) cell_pixbuf_data_func,
+                                                 NULL, NULL);
+	
+	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_text_data_func,
+                                                 NULL, NULL);
+
+        gtk_tree_view_append_column (GTK_TREE_VIEW (sped->priv->sources_tree), column);
+	
+	GtkTreeSelection *sel;
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (sped->priv->sources_tree));
+	g_signal_connect (sel, "changed",
+			  G_CALLBACK (data_source_selection_changed_cb), sped);
+
+	/* data sources tree */
+	GtkWidget *sw;
+	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);
+	gtk_container_add (GTK_CONTAINER (sw), sped->priv->sources_tree);
+	gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_paned_add2 (GTK_PANED (hpaned), vbox);
+
+	label = gtk_label_new ("");
+	str = g_strdup_printf ("<b>%s</b>", _("Selected data source'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);
+
+	GtkWidget *pe;
+	pe = data_source_editor_new ();
+	gtk_box_pack_start (GTK_BOX (vbox), pe, TRUE, TRUE, 0);
+	sped->priv->propsedit = DATA_SOURCE_EDITOR (pe);
+
+ 	/* warning, not shown */
+	sped->priv->info = NULL;
+
+	gtk_widget_show_all (hpaned);
 }
 
 GType
@@ -111,6 +254,77 @@ ui_spec_editor_dispose (GObject *object)
 	parent_class->dispose (object);
 }
 
+static void
+mgr_changed_cb (DataSourceManager *mgr, UiSpecEditor *sped)
+{
+	const GSList *list;
+
+	/* keep selected source, if any */
+	GtkTreeIter iter;
+	GtkTreeSelection *sel;
+	DataSource *current_source = NULL;
+	GtkTreePath *current_path = NULL;
+
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (sped->priv->sources_tree));
+	if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
+		gtk_tree_model_get (GTK_TREE_MODEL (sped->priv->sources_model),
+				    &iter, COLUMN_DATA_SOURCE, &current_source, -1);
+		current_path = gtk_tree_model_get_path (GTK_TREE_MODEL (sped->priv->sources_model),
+							&iter);
+	}
+
+	/* reset sources list */
+	gtk_list_store_clear (sped->priv->sources_model);
+	for (list = data_source_manager_get_sources (mgr); list; list = list->next) {
+		DataSource *source = DATA_SOURCE (list->data);
+
+		gtk_list_store_append (sped->priv->sources_model, &iter);
+		gtk_list_store_set (sped->priv->sources_model, &iter,
+				    COLUMN_DATA_SOURCE, source,
+				    -1);
+	}
+
+	/* reset selected source */
+	data_source_editor_display_source (sped->priv->propsedit, current_source);
+	if (current_path) {
+		gtk_tree_selection_select_path (sel, current_path);
+		gtk_tree_path_free (current_path);
+	}
+}
+
+static void
+mgr_source_changed_cb (DataSourceManager *mgr, DataSource *source, UiSpecEditor *sped)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *tree_model = GTK_TREE_MODEL (sped->priv->sources_model);
+	DataSource *msource;
+
+	if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
+		gtk_tree_model_get (tree_model, &iter, COLUMN_DATA_SOURCE, &msource, -1);
+		if (source == msource) {
+			GtkTreePath *path;
+			path = gtk_tree_path_new_from_indices (0, -1);
+			gtk_tree_model_row_changed (tree_model, path, &iter);
+			gtk_tree_path_free (path);
+			return;
+		}
+
+		gint pos;
+		for (pos = 1; gtk_tree_model_iter_next (tree_model, &iter); pos++) {
+			gtk_tree_model_get (tree_model, &iter, COLUMN_DATA_SOURCE, &msource, -1);
+			if (source == msource) {
+				GtkTreePath *path;
+				path = gtk_tree_path_new_from_indices (pos, -1);
+				gtk_tree_model_row_changed (tree_model, path, &iter);
+				gtk_tree_path_free (path);
+				return;
+			}
+		}
+	}
+
+	g_assert_not_reached ();
+}
+
 
 /**
  * ui_spec_editor_new
@@ -121,19 +335,53 @@ GtkWidget *
 ui_spec_editor_new (DataSourceManager *mgr)
 {
 	UiSpecEditor *sped;
-	GtkWidget *label;
 
 	g_return_val_if_fail (IS_DATA_SOURCE_MANAGER (mgr), NULL);
 
 	sped = g_object_new (UI_SPEC_EDITOR_TYPE, NULL);
 	sped->priv->mgr = g_object_ref (mgr);
-
-	label = gtk_label_new ("TODO");
-	gtk_box_pack_start (GTK_BOX (sped), label, TRUE, TRUE, 0);
-	gtk_widget_show (label);
+	g_signal_connect (mgr, "list-changed",
+			  G_CALLBACK (mgr_changed_cb), sped);
+	g_signal_connect (mgr, "source-changed",
+			  G_CALLBACK (mgr_source_changed_cb), sped);
+	mgr_changed_cb (mgr, sped);
 
 	/* warning */
 	sped->priv->info = NULL;
 
 	return (GtkWidget*) sped;
 }
+
+/**
+ * ui_spec_editor_select_source
+ * @editor: a #UiSpecEditor widget
+ * @source: a #DataSource
+ *
+ * Selects and displays @source's propserties
+ */
+void
+ui_spec_editor_select_source (UiSpecEditor *editor, DataSource *source)
+{
+	GtkTreeSelection *sel;
+	GtkTreeIter iter;
+	GtkTreeModel *tree_model = GTK_TREE_MODEL (editor->priv->sources_model);
+	DataSource *msource;
+
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->priv->sources_tree));
+	if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
+		gtk_tree_model_get (tree_model, &iter, COLUMN_DATA_SOURCE, &msource, -1);
+		if (source == msource) {
+			gtk_tree_selection_select_iter (sel, &iter);
+			return;
+		}
+
+		gint pos;
+		for (pos = 1; gtk_tree_model_iter_next (tree_model, &iter); pos++) {
+			gtk_tree_model_get (tree_model, &iter, COLUMN_DATA_SOURCE, &msource, -1);
+			if (source == msource) {
+				gtk_tree_selection_select_iter (sel, &iter);
+				return;
+			}
+		}
+	}
+}
diff --git a/tools/browser/data-manager/ui-spec-editor.h b/tools/browser/data-manager/ui-spec-editor.h
index 0c4bc37..4fd6bc1 100644
--- a/tools/browser/data-manager/ui-spec-editor.h
+++ b/tools/browser/data-manager/ui-spec-editor.h
@@ -49,8 +49,9 @@ struct _UiSpecEditorClass {
 	GtkVBoxClass parent_class;
 };
 
-GType          ui_spec_editor_get_type     (void) G_GNUC_CONST;
-GtkWidget     *ui_spec_editor_new          (DataSourceManager *mgr);
+GType          ui_spec_editor_get_type      (void) G_GNUC_CONST;
+GtkWidget     *ui_spec_editor_new           (DataSourceManager *mgr);
+void           ui_spec_editor_select_source (UiSpecEditor *editor, DataSource *source);
 
 G_END_DECLS
 
diff --git a/tools/browser/data-manager/xml-spec-editor.c b/tools/browser/data-manager/xml-spec-editor.c
index 0dff0e0..47898a0 100644
--- a/tools/browser/data-manager/xml-spec-editor.c
+++ b/tools/browser/data-manager/xml-spec-editor.c
@@ -59,6 +59,9 @@ static void xml_spec_editor_init       (XmlSpecEditor *sped, XmlSpecEditorClass
 static void xml_spec_editor_dispose    (GObject *object);
 static void xml_spec_editor_grab_focus (GtkWidget *widget);
 
+static void source_list_changed_cb (DataSourceManager *mgr, XmlSpecEditor *sped);
+static void data_source_changed_cb (DataSourceManager *mgr, DataSource *source, XmlSpecEditor *sped);
+
 static GObjectClass *parent_class = NULL;
 
 /*
@@ -120,8 +123,14 @@ xml_spec_editor_dispose (GObject *object)
 	if (sped->priv) {
 		if (sped->priv->signal_editor_changed_id)
 			g_source_remove (sped->priv->signal_editor_changed_id);
-		if (sped->priv->mgr)
+		if (sped->priv->mgr) {
+			g_signal_handlers_disconnect_by_func (sped->priv->mgr,
+							      G_CALLBACK (source_list_changed_cb), sped);
+			g_signal_handlers_disconnect_by_func (sped->priv->mgr,
+							      G_CALLBACK (data_source_changed_cb), sped);
+
 			g_object_unref (sped->priv->mgr);
+		}
 
 		g_free (sped->priv);
 		sped->priv = NULL;
@@ -132,8 +141,9 @@ xml_spec_editor_dispose (GObject *object)
 static gboolean
 signal_editor_changed (XmlSpecEditor *sped)
 {
-	/* modify the DataSourceManager */
-	data_source_manager_remove_all (sped->priv->mgr);
+	GSList *newlist = NULL;
+	g_signal_handlers_block_by_func (sped->priv->mgr,
+					 G_CALLBACK (source_list_changed_cb), sped);
 
 	/* create new DataSource objects */
 	GError *lerror = NULL;
@@ -173,11 +183,11 @@ signal_editor_changed (XmlSpecEditor *sped)
 	if (strcmp ((gchar*) node->name, "data")) {
 		g_set_error (&lerror, 0, 0,
 			     _("Expecting <%s> root node"), "data");
+		xmlFreeDoc (doc);
 		goto out;
 	}
 
 	BrowserConnection *bcnc;
-
 	bcnc = data_source_manager_get_browser_cnc (sped->priv->mgr);
 	for (node = node->children; node; node = node->next) {
 		if (!strcmp ((gchar*) node->name, "table") ||
@@ -185,18 +195,26 @@ signal_editor_changed (XmlSpecEditor *sped)
 			DataSource *source;
 			source = data_source_new_from_xml_node (bcnc, node, &lerror);
 			if (!source) {
-				data_source_manager_remove_all (sped->priv->mgr);
-				TO_IMPLEMENT;
+				if (newlist) {
+					g_slist_foreach (newlist, (GFunc) g_object_unref, NULL);
+					g_slist_free (newlist);
+					newlist = NULL;
+				}
+				xmlFreeDoc (doc);
 				goto out;
 			}
-			
-			data_source_manager_add_source (sped->priv->mgr, source);
-			g_object_unref (source);
+			else
+				newlist = g_slist_prepend (newlist, source);
 		}
 	}
 	xmlFreeDoc (doc);
 
  out:
+	newlist = g_slist_reverse (newlist);
+	data_source_manager_replace_all (sped->priv->mgr, newlist);
+	if (newlist)
+		g_slist_free (newlist);
+
 	if (lerror) {
 		if (! sped->priv->info) {
 #if GTK_CHECK_VERSION (2,18,0)
@@ -226,6 +244,10 @@ signal_editor_changed (XmlSpecEditor *sped)
 
 	/* remove timeout */
 	sped->priv->signal_editor_changed_id = 0;
+
+	g_signal_handlers_unblock_by_func (sped->priv->mgr,
+					   G_CALLBACK (source_list_changed_cb), sped);
+
 	return FALSE;
 }
 
@@ -237,6 +259,41 @@ editor_changed_cb (GtkTextBuffer *buffer, XmlSpecEditor *sped)
 	sped->priv->signal_editor_changed_id = g_timeout_add_seconds (1, (GSourceFunc) signal_editor_changed, sped);
 }
 
+static void
+source_list_changed_cb (DataSourceManager *mgr, XmlSpecEditor *sped)
+{
+	xmlDocPtr doc;
+	xmlNodePtr root;
+	doc = xmlNewDoc (BAD_CAST "1.0");
+	root = xmlNewNode (NULL, BAD_CAST "data");
+	xmlDocSetRootElement (doc, root);
+
+	const GSList *list;
+	for (list = data_source_manager_get_sources (sped->priv->mgr); list; list = list->next) {
+		xmlNodePtr node;
+		node = data_source_to_xml_node (DATA_SOURCE (list->data));
+		xmlAddChild (root, node);
+	}
+
+	xmlChar *mem;
+	int size;
+	xmlDocDumpFormatMemory (doc, &mem, &size, 1);
+	xmlFreeDoc (doc);
+
+	g_signal_handlers_block_by_func (sped->priv->buffer,
+					 G_CALLBACK (editor_changed_cb), sped);
+	gtk_text_buffer_set_text (sped->priv->buffer, (gchar*) mem, -1);
+	g_signal_handlers_unblock_by_func (sped->priv->buffer,
+					   G_CALLBACK (editor_changed_cb), sped);
+	xmlFree (mem);
+}
+
+static void
+data_source_changed_cb (DataSourceManager *mgr, DataSource *source, XmlSpecEditor *sped)
+{
+	source_list_changed_cb (mgr, sped);
+}
+
 /**
  * xml_spec_editor_new
  *
@@ -253,6 +310,10 @@ xml_spec_editor_new (DataSourceManager *mgr)
 
 	sped = g_object_new (XML_SPEC_EDITOR_TYPE, NULL);
 	sped->priv->mgr = g_object_ref (mgr);
+	g_signal_connect (mgr, "list-changed",
+			  G_CALLBACK (source_list_changed_cb), sped);
+	g_signal_connect (mgr, "source-changed",
+			  G_CALLBACK (data_source_changed_cb), sped);
 
 	/* XML editor */
 	label = gtk_label_new ("");
diff --git a/tools/browser/doc/gda-browser-sections.txt b/tools/browser/doc/gda-browser-sections.txt
index 08817ad..3904917 100644
--- a/tools/browser/doc/gda-browser-sections.txt
+++ b/tools/browser/doc/gda-browser-sections.txt
@@ -10,6 +10,7 @@ browser_window_pop_status
 browser_window_customize_perspective_ui
 browser_window_change_perspective
 browser_window_is_fullscreen
+browser_window_set_fullscreen
 <SUBSECTION Standard>
 BROWSER_WINDOW
 BROWSER_IS_WINDOW
diff --git a/tools/browser/doc/tmpl/browser-window.sgml b/tools/browser/doc/tmpl/browser-window.sgml
index 0881a1f..d62f097 100644
--- a/tools/browser/doc/tmpl/browser-window.sgml
+++ b/tools/browser/doc/tmpl/browser-window.sgml
@@ -111,3 +111,12 @@ Top level browser window
 @Returns: 
 
 
+<!-- ##### FUNCTION browser_window_set_fullscreen ##### -->
+<para>
+
+</para>
+
+ bwin: 
+ fullscreen: 
+
+
diff --git a/tools/browser/support.c b/tools/browser/support.c
index 3cdc874..a4b70c5 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -313,6 +313,8 @@ browser_show_help (GtkWindow *parent, const gchar *topic)
 		
 		g_error_free (error);
 	}
+	else
+		browser_show_notice (parent, "show-help", _("Help is being loaded, please wait..."));
 
 	g_free (uri);
 }
@@ -568,6 +570,7 @@ browser_make_small_button (gboolean is_toggle, const gchar *label, const gchar *
 	if (label) {
 		GtkWidget *wid;
 		wid = gtk_label_new (label);
+		gtk_misc_set_alignment (GTK_MISC (wid), 0., -1);
 		if (hbox)
 			gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5);
 		else



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