[libgda] GdaBrowser: improved Data Manager perspective



commit e825358dc3eab0938f6e3778cf20db3f072fd292
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sun Sep 5 15:00:24 2010 +0200

    GdaBrowser: improved Data Manager perspective

 tools/browser/Makefile.am                          |    3 +-
 tools/browser/browser-connection.c                 |   18 +
 tools/browser/browser-connection.h                 |    2 +
 tools/browser/browser-window.c                     |   20 +-
 tools/browser/browser-window.h                     |    8 +-
 tools/browser/canvas/browser-canvas-db-relations.c |    4 -
 tools/browser/data-manager/data-console.c          |   90 +--
 tools/browser/data-manager/data-source-manager.c   |   58 ++-
 tools/browser/data-manager/data-source.c           |  106 +++-
 tools/browser/data-manager/data-widget.c           |  650 ++++++++++++++++----
 tools/browser/data-manager/data-widget.h           |    3 +-
 tools/browser/doc/gda-browser-sections.txt         |    1 +
 tools/browser/doc/tmpl/browser-connection.sgml     |   11 +
 tools/browser/doc/tmpl/browser-window.sgml         |    2 +
 tools/browser/gda-browser-menu-ind.png             |  Bin 0 -> 472 bytes
 tools/browser/query-exec/query-console.c           |   15 +-
 tools/browser/schema-browser/table-info.c          |    5 +-
 tools/browser/support.c                            |   14 +-
 tools/browser/support.h                            |    2 +
 19 files changed, 780 insertions(+), 232 deletions(-)
---
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 7a3013b..b0f3695 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -149,7 +149,8 @@ icons_DATA= \
 	gda-browser-diagram.png \
 	gda-browser-query.png \
 	gda-browser-form.png \
-	gda-browser-grid.png
+	gda-browser-grid.png \
+	gda-browser-menu-ind.png
 
 # app icon
 appiconsdir=$(datadir)/pixmaps
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index b04b735..6ed0495 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -1376,6 +1376,24 @@ browser_connection_normalize_sql_statement (BrowserConnection *bcnc,
 	return gda_sql_statement_normalize (sqlst, bcnc->priv->cnc, error);
 }
 
+/**
+ * browser_connection_check_sql_statement_validify
+ */
+gboolean
+browser_connection_check_sql_statement_validify (BrowserConnection *bcnc,
+						 GdaSqlStatement *sqlst, GError **error)
+{
+	g_return_val_if_fail (sqlst, FALSE);
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
+
+	/* check the structure first */
+        if (!gda_sql_statement_check_structure (sqlst, error))
+                return FALSE;
+
+	return gda_sql_statement_check_validity_m (sqlst, bcnc->priv->mstruct, error);
+}
+
+
 
 /*
  * DOES NOT emit any signal
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index 763d80f..8908ffc 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -94,6 +94,8 @@ GObject            *browser_connection_execution_get_result   (BrowserConnection
 							       GdaSet **last_insert_row, GError **error);
 gboolean            browser_connection_normalize_sql_statement(BrowserConnection *bcnc,
 							       GdaSqlStatement *sqlst, GError **error);
+gboolean            browser_connection_check_sql_statement_validify (BrowserConnection *bcnc,
+								     GdaSqlStatement *sqlst, GError **error);
 /**
  * BrowserConnectionExecuteCallback
  *
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index 3a9b6db..4b81bd4 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -913,9 +913,9 @@ window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin)
 {
 	if (gtk_toggle_action_get_active (action)) {
 		gtk_window_fullscreen (GTK_WINDOW (bwin));
-		browser_window_show_notice_printf (bwin,
-						"fullscreen-esc",
-						_("Hit the Escape key to leave the fullscreen mode"));
+		browser_window_show_notice_printf (bwin, GTK_MESSAGE_INFO,
+						   "fullscreen-esc",
+						   _("Hit the Escape key to leave the fullscreen mode"));
 	}
 	else
 		gtk_window_unfullscreen (GTK_WINDOW (bwin));
@@ -1271,7 +1271,7 @@ browser_window_pop_status (BrowserWindow *bwin, const gchar *context)
  * Make @bwin display a notice
  */
 void
-browser_window_show_notice_printf (BrowserWindow *bwin, const gchar *context,
+browser_window_show_notice_printf (BrowserWindow *bwin, GtkMessageType type, const gchar *context,
 				   const gchar *format, ...)
 {
 	va_list args;
@@ -1283,7 +1283,7 @@ browser_window_show_notice_printf (BrowserWindow *bwin, const gchar *context,
         va_start (args, format);
         vsnprintf (sz, sizeof (sz), format, args);
         va_end (args);
-	browser_window_show_notice (bwin, context, sz);
+	browser_window_show_notice (bwin, type, context, sz);
 }
 
 
@@ -1318,12 +1318,12 @@ hide_notice_toggled_cb (GtkToggleButton *toggle, gchar *context)
  * Makes @bwin display a notice
  */
 void
-browser_window_show_notice (BrowserWindow *bwin, const gchar *context, const gchar *text)
+browser_window_show_notice (BrowserWindow *bwin, GtkMessageType type, const gchar *context, const gchar *text)
 {
 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
 	gboolean hide = FALSE;
 
-	if (context) {
+	if ((type != GTK_MESSAGE_ERROR) && context) {
 		if (!hidden_contexts)
 			hidden_contexts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 		hide = GPOINTER_TO_INT (g_hash_table_lookup (hidden_contexts, context));
@@ -1344,7 +1344,7 @@ browser_window_show_notice (BrowserWindow *bwin, const gchar *context, const gch
 	}
 	else {
 		GtkWidget *cb = NULL;
-		if (context) {
+		if (context && (type == GTK_MESSAGE_INFO)) {
 			cb = gtk_check_button_new_with_label (_("Don't show this message again"));
 			g_signal_connect_data (cb, "toggled",
 					       G_CALLBACK (hide_notice_toggled_cb), g_strdup (context),
@@ -1356,7 +1356,7 @@ browser_window_show_notice (BrowserWindow *bwin, const gchar *context, const gch
 		GtkWidget *ibar, *content_area, *label;
 		
 		ibar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, 1, NULL);
-		gtk_info_bar_set_message_type (GTK_INFO_BAR (ibar), GTK_MESSAGE_INFO);
+		gtk_info_bar_set_message_type (GTK_INFO_BAR (ibar), type);
 		label = gtk_label_new ("");
 		gtk_label_set_markup (GTK_LABEL (label), text);
 		gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
@@ -1511,7 +1511,7 @@ browser_window_change_perspective (BrowserWindow *bwin, const gchar *perspective
 				       current_pdata->factory->menu_shortcut);
 
 			
-	browser_window_show_notice (bwin, "Perspective change", tmp);
+	browser_window_show_notice (bwin, GTK_MESSAGE_INFO, "Perspective change", tmp);
 	g_free (tmp);
 
 	return bpers;
diff --git a/tools/browser/browser-window.h b/tools/browser/browser-window.h
index ee79e9c..4797953 100644
--- a/tools/browser/browser-window.h
+++ b/tools/browser/browser-window.h
@@ -58,10 +58,10 @@ BrowserConnection  *browser_window_get_connection         (BrowserWindow *bwin);
 guint               browser_window_push_status            (BrowserWindow *bwin, const gchar *context,
 							   const gchar *text, gboolean auto_clear);
 void                browser_window_pop_status             (BrowserWindow *bwin, const gchar *context);
-void                browser_window_show_notice            (BrowserWindow *bwin, const gchar *context,
-							   const gchar *text);
-void                browser_window_show_notice_printf     (BrowserWindow *bwin, const gchar *context,
-							   const gchar *format, ...);
+void                browser_window_show_notice            (BrowserWindow *bwin, GtkMessageType type,
+							   const gchar *context, const gchar *text);
+void                browser_window_show_notice_printf     (BrowserWindow *bwin, GtkMessageType type,
+							   const gchar *context, const gchar *format, ...);
 
 void                browser_window_customize_perspective_ui (BrowserWindow *bwin, BrowserPerspective *bpers,
 							     GtkActionGroup *actions_group,
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
index 619313b..6ba01ea 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.c
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -612,10 +612,6 @@ browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
 	goocanvas = BROWSER_CANVAS (canvas)->priv->goocanvas;
 	mtable = (GdaMetaTable *) gda_meta_struct_complement (canvas->priv->mstruct, GDA_META_DB_TABLE,
 							      table_catalog, table_schema, table_name, &lerror);
-	g_print ("%s () mstruct=%p (%s,%s,%s)=>%p\n", __FUNCTION__, canvas->priv->mstruct,
-		 g_value_get_string (table_catalog),
-		 g_value_get_string (table_schema),
-		 g_value_get_string (table_name), mtable);
 	if (mtable) {
 		gdouble x = 0, y = 0;
 		GooCanvasItem *table_item;
diff --git a/tools/browser/data-manager/data-console.c b/tools/browser/data-manager/data-console.c
index a4a64ab..7dc6886 100644
--- a/tools/browser/data-manager/data-console.c
+++ b/tools/browser/data-manager/data-console.c
@@ -794,34 +794,6 @@ add_source_clicked_cb (GtkButton *button, DataConsole *dconsole)
 			gtk_get_current_event_time ());
 }
 
-static GtkWidget *
-create_widget (DataConsole *dconsole, GArray *sources_array, GError **error)
-{
-	GtkWidget *sw, *vp, *dwid;
-
-	if (! sources_array) {
-		g_set_error (error, 0, 0,
-			     _("No data source defined"));
-		return NULL;
-	}
-
-	sw = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
-					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
-
-	vp = gtk_viewport_new (NULL, NULL);
-	gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
-	gtk_container_add (GTK_CONTAINER (sw), vp);
-
-	dwid = data_widget_new (sources_array);
-	gtk_container_add (GTK_CONTAINER (vp), dwid);
-	g_object_set_data ((GObject*) sw, "data-widget", dwid);
-
-	gtk_widget_show_all (vp);
-	return sw;
-}
-
 /*
  * UI actions
  */
@@ -843,36 +815,33 @@ compose_mode_toggled_cb (GtkToggleAction *action, DataConsole *dconsole)
 	pagenb = gtk_notebook_get_current_page (GTK_NOTEBOOK (dconsole->priv->main_notebook));
 	if (pagenb == MAIN_PAGE_EDITORS) {
 		/* Get Data sources */
-		GArray *sources_array;
-		GError *lerror = NULL;
-		sources_array = data_source_manager_get_sources_array (dconsole->priv->mgr, &lerror);
-		if (sources_array) {
-			if (dconsole->priv->data) {
-				/* destroy existing data widgets */
-				gtk_widget_destroy (dconsole->priv->data);
-				dconsole->priv->data = NULL;
-			}
-
-			GtkWidget *wid;
-			wid = create_widget (dconsole, sources_array, &lerror);
-			data_source_manager_destroy_sources_array (sources_array);
-			if (wid) {
-				dconsole->priv->data = wid;
-				gtk_box_pack_start (GTK_BOX (dconsole->priv->data_box), wid, TRUE, TRUE, 0);
-				gtk_widget_show (wid);
-				pagenb = MAIN_PAGE_DATA;
-			}
-		}
-		if (lerror) {
-			browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) dconsole),
-					    lerror && lerror->message ? lerror->message :
-					    _("Error parsing XML specifications"));
-			g_clear_error (&lerror);
-		}
-		if (pagenb == MAIN_PAGE_EDITORS) {
-			dconsole->priv->toggling = TRUE;
-			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+		pagenb = MAIN_PAGE_DATA;
+		if (dconsole->priv->data) {
+			/* destroy existing data widgets */
+			gtk_widget_destroy (dconsole->priv->data);
+			dconsole->priv->data = NULL;
 		}
+
+		GtkWidget *sw, *vp, *dwid;
+		
+		sw = gtk_scrolled_window_new (NULL, NULL);
+		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+						GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+		gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+		
+		vp = gtk_viewport_new (NULL, NULL);
+		gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
+		gtk_container_add (GTK_CONTAINER (sw), vp);
+		
+		dwid = data_widget_new (dconsole->priv->mgr);
+		gtk_container_add (GTK_CONTAINER (vp), dwid);
+		g_object_set_data ((GObject*) sw, "data-widget", dwid);
+		
+		gtk_widget_show_all (vp);
+
+		dconsole->priv->data = sw;
+		gtk_box_pack_start (GTK_BOX (dconsole->priv->data_box), sw, TRUE, TRUE, 0);
+		gtk_widget_show (sw);
 	}
 	else {
 		/* simply change the current page */
@@ -881,9 +850,10 @@ compose_mode_toggled_cb (GtkToggleAction *action, DataConsole *dconsole)
 
 	if (pagenb == MAIN_PAGE_DATA)
 		browser_window_show_notice_printf (BROWSER_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) dconsole)),
-				     "data-manager-exec-mode-switched",
-				     _("Switching to execution mode. Hit the Escape key "
-				       "to return to the compose mode"));
+						   GTK_MESSAGE_INFO,
+						   "data-manager-exec-mode-switched",
+						   _("Switching to execution mode. Hit the Escape key "
+						     "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-source-manager.c b/tools/browser/data-manager/data-source-manager.c
index 0986773..375c67b 100644
--- a/tools/browser/data-manager/data-source-manager.c
+++ b/tools/browser/data-manager/data-source-manager.c
@@ -47,6 +47,8 @@ static void data_source_manager_class_init (DataSourceManagerClass *klass);
 static void data_source_manager_init (DataSourceManager *mgr);
 static void data_source_manager_dispose (GObject *object);
 
+static void ensure_source_unique_id (DataSourceManager *mgr, DataSource *source);
+
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass  *parent_class = NULL;
 
@@ -120,13 +122,64 @@ data_source_manager_init (DataSourceManager *mgr)
 	mgr->priv->emit_changes = TRUE;
 }
 
+/*
+ * find_data_source
+ *
+ * Finds a data source which id is @id, exclusing @excl_source if not %NULL
+ */
+static DataSource *
+find_data_source (DataSourceManager *mgr, const gchar *id, DataSource *excl_source)
+{
+	GSList *list;
+	g_return_val_if_fail (id && *id, NULL);
+	for (list = mgr->priv->sources_list; list; list = list->next) {
+		DataSource *source = (DataSource *) list->data;
+		if (excl_source && (source == excl_source))
+			continue;
+		const gchar *sid = data_source_get_id (source);
+		if (!sid) {
+			g_warning ("Data source has no ID!");
+			continue;
+		}
+		if (!strcmp (id, sid))
+			return source;
+	}
+	return NULL;
+}
+
 static void
 source_changed_cb (DataSource *source, DataSourceManager *mgr)
 {
+	ensure_source_unique_id (mgr, source);
 	g_signal_emit (mgr, data_source_manager_signals[SOURCE_CHANGED], 0, source);
 }
 
 static void
+ensure_source_unique_id (DataSourceManager *mgr, DataSource *source)
+{
+	/* make sure the source's ID is unique among @mgr's data sources */
+	DataSource *es;
+	es = find_data_source (mgr, data_source_get_id (source), source);
+	if (es) {
+		gint i;
+		for (i = 1; ; i++) {
+			gchar *tmp;
+			tmp = g_strdup_printf ("%s_%d", data_source_get_id (source), i);
+			if (! find_data_source (mgr, tmp, NULL)) {
+				g_signal_handlers_block_by_func (source,
+								 G_CALLBACK (source_changed_cb), mgr);
+				data_source_set_id (source, tmp);
+				g_signal_handlers_unblock_by_func (source,
+								   G_CALLBACK (source_changed_cb), mgr);
+				g_free (tmp);
+				break;
+			}
+			g_free (tmp);
+		}
+	}
+}
+
+static void
 data_source_manager_dispose (GObject *object)
 {
 	DataSourceManager *mgr;
@@ -225,6 +278,8 @@ source_depends_on (DataSource *source1, DataSource *source2)
  * data_source_manager_add_source
  * @mgr:
  * @source:
+ *
+ * @source is referenced by @mgr
  */
 void
 data_source_manager_add_source (DataSourceManager *mgr, DataSource *source)
@@ -237,6 +292,7 @@ data_source_manager_add_source (DataSourceManager *mgr, DataSource *source)
 #ifdef DEBUG_SOURCES_SORT
 	g_print ("Adding source [%s]\n", data_source_get_title (source));
 #endif
+	ensure_source_unique_id (mgr, source);
 	if (! mgr->priv->sources_list)
 		mgr->priv->sources_list = g_slist_append (NULL, g_object_ref (source));
 	else {
@@ -319,7 +375,7 @@ data_source_manager_add_source (DataSourceManager *mgr, DataSource *source)
 void
 data_source_manager_replace_all (DataSourceManager *mgr, const GSList *sources_list)
 {
-	GSList *list;
+	const GSList *list;
 	g_return_if_fail (IS_DATA_SOURCE_MANAGER (mgr));
 
 	mgr->priv->emit_changes = FALSE;
diff --git a/tools/browser/data-manager/data-source.c b/tools/browser/data-manager/data-source.c
index b53efaf..fa0c246 100644
--- a/tools/browser/data-manager/data-source.c
+++ b/tools/browser/data-manager/data-source.c
@@ -303,11 +303,13 @@ data_source_new_from_xml_node (BrowserConnection *bcnc, xmlNodePtr node, GError
 	xmlChar *prop;
 	prop = xmlGetProp (node, BAD_CAST "title");
 	if (prop) {
+		g_free (source->priv->title);
 		source->priv->title = g_strdup ((gchar*) prop);
 		xmlFree (prop);
 	}
 	prop = xmlGetProp (node, BAD_CAST "id");
 	if (prop) {
+		g_free (source->priv->id);
 		source->priv->id = g_strdup ((gchar*) prop);
 		xmlFree (prop);
 	}
@@ -421,8 +423,11 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
 			fk_table = xmlGetProp (subnode, BAD_CAST "foreign_key_table");
 			id = xmlGetProp (subnode, BAD_CAST "id");
 
-			data_source_add_dependendency (source, (gchar *) fk_table, (gchar*) id, error);
-			xmlFree (fk_table);
+			if (fk_table &&
+			    ! data_source_add_dependendency (source, (gchar *) fk_table, (gchar*) id, error))
+				retval = FALSE;
+			if (fk_table)
+				xmlFree (fk_table);
 			if (id)
 				xmlFree (id);
 			break;
@@ -464,6 +469,7 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
 	/* find foreign key to linked table */
 	GdaMetaTableForeignKey *fk = NULL;
 	GSList *list;
+	gboolean reverse = FALSE;
 	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);
@@ -471,6 +477,15 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
 		}
 	}
 	if (!fk) {
+		for (list = mlinked->fk_list; list; list = list->next) {
+			if (GDA_META_TABLE_FOREIGN_KEY (list->data)->depend_on == GDA_META_DB_OBJECT (mtable)) {
+				fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
+				reverse = TRUE;
+				break;
+			}
+		}
+	}
+	if (!fk) {
 		g_set_error (error, 0, 0,
 			     _("Could not find any foreign key to \"%s\""), table);
 		return FALSE;
@@ -484,19 +499,33 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
 	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);
+		GdaSqlBuilderId id1, id2, id_cond;
+		if (reverse) {
+			id1 = gda_sql_builder_add_id (source->priv->builder, fk->ref_pk_names_array [0]);
+			tmp = g_strdup_printf ("%s %s", id ? id : table, fk->fk_names_array [0]);
+
+			col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
+								       fk->fk_cols_array [0] - 1));
+			g_assert (col);
+			id2 = gda_sql_builder_add_param (source->priv->builder, tmp, col->gtype, FALSE);
+			g_free (tmp);
+		}
+		else {
+			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);
+			id2 = gda_sql_builder_add_param (source->priv->builder, tmp, col->gtype, FALSE);
+			g_free (tmp);
+			id_cond = gda_sql_builder_add_cond (source->priv->builder,
+							    GDA_SQL_OPERATOR_TYPE_EQ,
+							    id1, id2, 0);
+		}
+		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 {
@@ -505,20 +534,32 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
 		GdaMetaTableColumn *col;
 		GdaSqlBuilderId andid;
 		GdaSqlBuilderId *op_ids;
+		GdaSqlBuilderId id1, id2;
 		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,
+			if (reverse) {
+				id1 = gda_sql_builder_add_id (source->priv->builder, fk->ref_pk_names_array [i]);
+				tmp = g_strdup_printf ("%s %s", id ? id : table, fk->fk_names_array [i]);
+
+				col = GDA_META_TABLE_COLUMN (g_slist_nth_data (mlinked->columns,
+									       fk->fk_cols_array [i] - 1));
+				g_assert (col);
+				id2 = gda_sql_builder_add_param (source->priv->builder, tmp, col->gtype, FALSE);
+				g_free (tmp);
+			}
+			else {
+				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);
+				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,
@@ -888,6 +929,8 @@ data_source_set_id (DataSource *source, const gchar * id)
 	g_free (source->priv->id);
 	if (id)
 		source->priv->id = g_strdup (id);
+	else
+		source->priv->id = NULL;
 	g_signal_emit (source, data_source_signals [CHANGED], 0);
 }
 
@@ -922,6 +965,8 @@ data_source_set_title (DataSource *source, const gchar * title)
 	g_free (source->priv->title);
 	if (title)
 		source->priv->title = g_strdup (title);
+	else
+		source->priv->title = NULL;
 	g_signal_emit (source, data_source_signals [CHANGED], 0);
 }
 
@@ -971,8 +1016,8 @@ data_source_set_table (DataSource *source, const gchar *table, GError **error)
 	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);
+	if (! source->priv->id)
+		source->priv->id = g_strdup ((gchar*) table);
 
 	/* build statement */
 	GSList *list;
@@ -1148,6 +1193,11 @@ compute_stmt_and_params (DataSource *source)
 		g_object_unref (source->priv->stmt);
 	source->priv->stmt = gda_sql_builder_get_statement (source->priv->builder, NULL);
 	compute_import_params (source);
+
+	gchar *sql;
+	sql = gda_statement_to_sql (source->priv->stmt, NULL, NULL);
+	g_print ("[%s]\n", sql);
+	g_free (sql);
 }
 
 static void
diff --git a/tools/browser/data-manager/data-widget.c b/tools/browser/data-manager/data-widget.c
index a20667d..9cc8483 100644
--- a/tools/browser/data-manager/data-widget.c
+++ b/tools/browser/data-manager/data-widget.c
@@ -27,7 +27,11 @@
 #include "../browser-spinner.h"
 #include "../common/ui-formgrid.h"
 #include "../browser-window.h"
+#include "../support.h"
 
+/*
+ * The DataPart structure represents the execution of a single DataSource 
+ */
 typedef struct {
 	DataWidget *dwid;
 	DataSource *source;
@@ -44,14 +48,19 @@ typedef struct {
 
 	GSList *dep_parts; /* list of DataPart which need to be re-run when anything in @export_data
 			    * changes */
+	GtkWidget *menu;
 } DataPart;
 #define DATA_PART(x) ((DataPart*)(x))
 
 static DataPart *data_part_find (DataWidget *dwid, DataSource *source);
-static void data_part_free (DataPart *part);
+static void data_part_free (DataPart *part, GSList *all_parts);
 
 struct _DataWidgetPrivate {
-	GtkWidget *hpaned;
+	DataSourceManager *mgr;
+	GtkWidget *top_nb; /* page 0 => error, page 1 => contents */
+	GtkWidget *info_label;
+	GtkWidget *contents_page_vbox;
+	GtkWidget *contents_page;
 	GSList *parts;
 };
 
@@ -59,6 +68,8 @@ static void data_widget_class_init (DataWidgetClass *klass);
 static void data_widget_init       (DataWidget *dwid, DataWidgetClass *klass);
 static void data_widget_dispose    (GObject *object);
 
+static void mgr_list_changed_cb (DataSourceManager *mgr, DataWidget *dwid);
+
 static GObjectClass *parent_class = NULL;
 
 /*
@@ -82,6 +93,36 @@ data_widget_init (DataWidget *dwid, DataWidgetClass *klass)
 
 	/* allocate private structure */
 	dwid->priv = g_new0 (DataWidgetPrivate, 1);
+
+	/* init Widgets's structure */
+	dwid->priv->top_nb = gtk_notebook_new ();
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (dwid->priv->top_nb), FALSE);
+	gtk_notebook_set_show_border (GTK_NOTEBOOK (dwid->priv->top_nb), FALSE);
+	gtk_box_pack_start (GTK_BOX (dwid), dwid->priv->top_nb, TRUE, TRUE, 0);
+
+	/* error page */
+#if GTK_CHECK_VERSION (2,18,0)
+	GtkWidget *info;
+	info = gtk_info_bar_new ();
+	gtk_notebook_append_page (GTK_NOTEBOOK (dwid->priv->top_nb), info, NULL);
+	dwid->priv->info_label = gtk_label_new ("");
+	gtk_misc_set_alignment (GTK_MISC (dwid->priv->info_label), 0., -1);
+	gtk_label_set_ellipsize (GTK_LABEL (dwid->priv->info_label), PANGO_ELLIPSIZE_END);
+	gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (info))),
+			   dwid->priv->info_label);
+#else
+	dwid->priv->info = gtk_label_new ("");
+	gtk_notebook_append_page (GTK_NOTEBOOK (dwid->priv->top_nb), info);
+	dwid->priv->info_label = dwid->priv->info;
+#endif
+
+	/* contents page */
+	GtkWidget *vbox;
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_notebook_append_page (GTK_NOTEBOOK (dwid->priv->top_nb), vbox, NULL);
+	dwid->priv->contents_page_vbox = vbox;
+
+	gtk_widget_show_all (dwid->priv->top_nb);
 }
 
 GType
@@ -107,12 +148,23 @@ data_widget_get_type (void)
 }
 
 static void
+data_part_free_func (DataPart *part)
+{
+	data_part_free (part, NULL);
+}
+
+static void
 data_widget_dispose (GObject *object)
 {
 	DataWidget *dwid = (DataWidget*) object;
 	if (dwid->priv) {
+		if (dwid->priv->mgr) {
+			g_signal_handlers_disconnect_by_func (dwid->priv->mgr,
+							      G_CALLBACK (mgr_list_changed_cb), dwid);
+			g_object_unref (dwid->priv->mgr);
+		}
 		if (dwid->priv->parts) {
-			g_slist_foreach (dwid->priv->parts, (GFunc) data_part_free, NULL);
+			g_slist_foreach (dwid->priv->parts, (GFunc) data_part_free_func, NULL);
 			g_slist_free (dwid->priv->parts);
 		}
 		g_free (dwid->priv);
@@ -123,11 +175,27 @@ data_widget_dispose (GObject *object)
 
 static void source_exec_started_cb (DataSource *source, DataPart *part);
 static void source_exec_finished_cb (DataSource *source, GError *error, DataPart *part);
+static void data_source_menu_clicked_cb (GtkButton *button, DataPart *part);
 
 static DataPart *
-create_part (DataWidget *dwid, DataSource *source)
+create_or_reuse_part (DataWidget *dwid, DataSource *source, gboolean *out_reused)
 {
+	
 	DataPart *part;
+	*out_reused = FALSE;
+
+	part = data_part_find (dwid, source);
+	if (part) {
+		GtkWidget *parent;
+		parent = gtk_widget_get_parent (part->top);
+		if (parent) {
+			g_object_ref ((GObject*) part->top);
+			gtk_container_remove (GTK_CONTAINER (parent), part->top);
+		}
+		*out_reused = TRUE;
+		return part;
+	}
+
 	part = g_new0 (DataPart, 1);
 	part->dwid = dwid;
 	part->source = g_object_ref (source);
@@ -141,23 +209,41 @@ create_part (DataWidget *dwid, DataSource *source)
 	GtkWidget *vbox;
 	vbox = gtk_vbox_new (FALSE, 0);
 	part->top = vbox;
+	g_object_ref_sink ((GObject*) part->top);
 
-	GtkWidget *header;
+	GtkWidget *header, *label, *button, *image;
 	const gchar *cstr;
-	header = gtk_label_new ("");
+
+	header = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), header, FALSE, FALSE, 0);
+
+	label = gtk_label_new ("");
 	cstr = data_source_get_title (source);
 	if (cstr) {
 		gchar *tmp;
 		tmp = g_markup_printf_escaped ("<b><small>%s</small></b>", cstr);
-		gtk_label_set_markup (GTK_LABEL (header), tmp);
+		gtk_label_set_markup (GTK_LABEL (label), tmp);
 		g_free (tmp);
 	}
 	else
-		gtk_label_set_markup (GTK_LABEL (header), "<b><small> </small></b>");
-	gtk_misc_set_alignment (GTK_MISC (header), 0., -1);
-	gtk_widget_set_size_request (header, 150, -1);
-	gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
-	gtk_box_pack_start (GTK_BOX (vbox), header, FALSE, FALSE, 0);
+		gtk_label_set_markup (GTK_LABEL (label), "<b><small> </small></b>");
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_widget_set_size_request (label, 150, -1);
+	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+	gtk_box_pack_start (GTK_BOX (header), label, TRUE, TRUE, 0);
+
+	image = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_MENU_INDICATOR));
+	button = gtk_button_new ();
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+	gtk_widget_set_name (button, "browser-tab-close-button");
+	gtk_widget_set_tooltip_text (button, _("Link to other data"));
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (data_source_menu_clicked_cb), part);
+		
+	gtk_container_add (GTK_CONTAINER (button), image);
+	gtk_container_set_border_width (GTK_CONTAINER (button), 0);
+	gtk_box_pack_start (GTK_BOX (header), button, FALSE, FALSE, 0);
 
 	GtkWidget *nb, *page;
 	nb = gtk_notebook_new ();
@@ -222,43 +308,313 @@ pack_in_paned_list (GSList *paned_list, gint length, gint pos, GtkWidget *wid)
 	}
 }
 
-/**
- * data_widget_new
- *
- * Returns: the newly created widget.
+static void
+remove_data_source_mitem_activated_cb (GtkMenuItem *mitem, DataPart *part)
+{
+	data_source_manager_remove_source (part->dwid->priv->mgr, part->source);
+}
+
+static void
+add_data_source_mitem_activated_cb (GtkMenuItem *mitem, DataPart *part)
+{
+	DataSource *source;
+	GError *lerror = NULL;
+	xmlNodePtr sourcespec;
+	BrowserConnection *bcnc;
+
+	bcnc = data_source_manager_get_browser_cnc (part->dwid->priv->mgr);
+	sourcespec = (xmlNodePtr) g_object_get_data ((GObject*) mitem, "xml");
+	source = data_source_new_from_xml_node (bcnc, sourcespec, &lerror);
+	if (source) {
+		data_source_manager_add_source (part->dwid->priv->mgr, source);
+		g_object_unref (source);
+		g_print ("Source Added!\n");
+	}
+	else {
+		BrowserWindow *bwin;
+		bwin = BROWSER_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (part->dwid)));
+		browser_window_show_notice_printf (bwin, GTK_MESSAGE_ERROR, "data-widget-add-new-source",
+						   _("Error adding new data source: %s"),
+						   lerror && lerror->message ? lerror->message :
+						   _("No detail"));
+		g_clear_error (&lerror);
+	}
+
+#ifdef GDA_DEBUG_NO
+	xmlBufferPtr buffer;
+	buffer = xmlBufferCreate ();
+	xmlNodeDump (buffer, NULL, sourcespec, 0, 1);
+	g_print ("Source to ADD: [%s]\n", (gchar *) xmlBufferContent (buffer));
+	xmlBufferFree (buffer);
+#endif
+}
+
+static gchar *compute_fk_dependency (GdaMetaTableForeignKey *fkey, GSList *selfields, gboolean reverse,
+				     DataPart *part, xmlNodePtr *out_sourcespec);
+static void
+data_source_menu_clicked_cb (GtkButton *button, DataPart *part)
+{
+	if (! part->menu) {
+		GtkWidget *menu, *mitem;
+		menu = gtk_menu_new ();
+		part->menu = menu;
+
+		mitem = gtk_menu_item_new_with_label (_("Remove data source"));
+		g_signal_connect (mitem, "activate",
+				  G_CALLBACK (remove_data_source_mitem_activated_cb), part);
+		gtk_widget_show (mitem);
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+
+		GdaStatement *stmt;
+		stmt = data_source_get_statement (part->source);
+		if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) {
+			GSList *fields = NULL, *flist;
+			GdaSqlStatement *sql_statement;
+			BrowserConnection *bcnc;
+			GHashTable *hash; /* key = a menu string, value= 0x1 */
+
+			g_object_get (G_OBJECT (stmt), "structure", &sql_statement, NULL);
+
+			bcnc = data_source_manager_get_browser_cnc (part->dwid->priv->mgr);
+			if (browser_connection_check_sql_statement_validify (bcnc, sql_statement, NULL))
+				fields = ((GdaSqlStatementSelect *) sql_statement->contents)->expr_list;
+
+			hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+			for (flist = fields; flist; flist = flist->next) {
+				GdaSqlSelectField *select_field = (GdaSqlSelectField*) flist->data;
+				if (!select_field->validity_meta_table_column)
+					continue;
+				GdaMetaDbObject *dbo;
+				GdaMetaTableColumn *field;
+				dbo = select_field->validity_meta_object;
+				field = select_field->validity_meta_table_column;
+				if (dbo->obj_type == GDA_META_DB_TABLE) {
+					GdaMetaTable *mtable;
+					mtable = GDA_META_TABLE (dbo);
+
+					GSList *fklist;
+					for (fklist = mtable->reverse_fk_list; fklist; fklist = fklist->next) {
+						GdaMetaTableForeignKey *fkey;
+						fkey = (GdaMetaTableForeignKey *) fklist->data;
+						gchar *tmp;
+						xmlNodePtr sourcespec = NULL;
+						if (fkey->meta_table->obj_type != GDA_META_DB_TABLE)
+							continue;
+						tmp = compute_fk_dependency (fkey, fields, FALSE,
+									     part, &sourcespec);
+						if (!tmp)
+							continue;
+						if (g_hash_table_lookup (hash, tmp)) {
+							g_free (tmp);
+							continue;
+						}
+
+						mitem = gtk_menu_item_new_with_label (tmp);
+						g_object_set_data_full ((GObject*) mitem, "xml", sourcespec,
+									(GDestroyNotify) xmlFreeNode);
+						g_signal_connect (mitem, "activate",
+								  G_CALLBACK (add_data_source_mitem_activated_cb),
+								  part);
+						gtk_widget_show (mitem);
+						gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+						g_hash_table_insert (hash, tmp, (gpointer) 0x1);
+					}
+					for (fklist = mtable->fk_list; fklist; fklist = fklist->next) {
+						GdaMetaTableForeignKey *fkey;
+						fkey = (GdaMetaTableForeignKey *) fklist->data;
+						gchar *tmp;
+						xmlNodePtr sourcespec = NULL;
+						if (fkey->depend_on->obj_type != GDA_META_DB_TABLE)
+							continue;
+						tmp = compute_fk_dependency (fkey, fields, TRUE,
+									     part, &sourcespec);
+						if (!tmp)
+							continue;
+						if (g_hash_table_lookup (hash, tmp)) {
+							g_free (tmp);
+							continue;
+						}
+						mitem = gtk_menu_item_new_with_label (tmp);
+						g_object_set_data_full ((GObject*) mitem, "xml", sourcespec,
+									(GDestroyNotify) xmlFreeNode);
+						g_signal_connect (mitem, "activate",
+								  G_CALLBACK (add_data_source_mitem_activated_cb),
+								  part);
+						gtk_widget_show (mitem);
+						gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+						g_hash_table_insert (hash, tmp, (gpointer) 0x1);
+					}
+				}
+			}
+			g_hash_table_destroy (hash);
+			gda_sql_statement_free (sql_statement);
+		}
+	}
+	gtk_menu_popup (GTK_MENU (part->menu), NULL, NULL,
+                        NULL, NULL, 0,
+                        gtk_get_current_event_time ());
+}
+
+/*
+ * Returns: a new list of #GdaSqlSelectField (which are already listed in @selfields), or %NULL
  */
-GtkWidget *
-data_widget_new (GArray *sources_array)
+static gchar *
+compute_fk_dependency (GdaMetaTableForeignKey *fkey, GSList *selfields, gboolean reverse,
+		       DataPart *part, xmlNodePtr *out_sourcespec)
 {
-	DataWidget *dwid;
+	GString *string = NULL;
+	gint i;
+	gint *index_array;
+	GdaMetaTable *table;
 
-	g_return_val_if_fail (sources_array, NULL); 
-	dwid = g_object_new (DATA_WIDGET_TYPE, NULL);
+	*out_sourcespec = NULL;
+
+	if (reverse) {
+		table = GDA_META_TABLE (fkey->meta_table);
+		index_array = fkey->fk_cols_array;
+	}
+	else {
+		table = GDA_META_TABLE (fkey->depend_on);
+		index_array = fkey->ref_pk_cols_array;
+	}
+	for (i = 0; i < fkey->cols_nb; i++) {
+		gint pos;
+		GdaMetaTableColumn *col;
+		pos = index_array[i] - 1;
+		col = g_slist_nth_data (table->columns, pos);
+
+		/* make sure @col is among @selfields */
+		GSList *flist;
+		gboolean found = FALSE;
+		for (flist = selfields; flist; flist = flist->next) {
+			GdaSqlSelectField *select_field = (GdaSqlSelectField*) flist->data;
+			if (!select_field->validity_meta_object ||
+			    !select_field->validity_meta_table_column)
+				continue;
+			GdaMetaTableColumn *field;
+			field = select_field->validity_meta_table_column;
+			if (field == col) {
+				found = TRUE;
+				if (reverse) {
+					if (!string) {
+						string = g_string_new (_("Obtain referenced data in table "));
+						g_string_append_printf (string, "%s from ",
+									fkey->depend_on->obj_short_name);
+					}
+					else
+						g_string_append (string, ", ");
+					g_string_append_printf (string, "column n.%d (",
+								g_slist_position (selfields, flist) + 1);
+					if (select_field->as)
+						g_string_append (string, select_field->as);
+					else
+						g_string_append (string,
+								 select_field->validity_meta_table_column->column_name);
+					g_string_append_c (string, ')');
+				}
+				else {
+					if (!string) {
+						string = g_string_new (_("List referencing data in "));
+						g_string_append_printf (string, "%s.",
+									fkey->meta_table->obj_short_name);
+					}
+					else
+						g_string_append (string, ", ");
+					g_string_append (string, fkey->fk_names_array [i]);
+				}
+				break;
+			}
+		}
+
+		if (! found) {
+			if (string) {
+				g_string_free (string, TRUE);
+				string = NULL;
+			}
+			break;
+		}
+	}
+
+	if (string) {
+		xmlNodePtr node, snode;
+		node = xmlNewNode (NULL, BAD_CAST "table");
+		xmlSetProp (node, BAD_CAST "name",
+ 			    BAD_CAST (reverse ? GDA_META_DB_OBJECT (fkey->depend_on)->obj_short_name : 
+				      GDA_META_DB_OBJECT (fkey->meta_table)->obj_short_name));
+
+		snode = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
+		xmlSetProp (snode, BAD_CAST "foreign_key_table",
+			    BAD_CAST GDA_META_DB_OBJECT (table)->obj_short_name);
+
+		xmlSetProp (snode, BAD_CAST "id", BAD_CAST data_source_get_id (part->source));
+		*out_sourcespec = node;
+		return g_string_free (string, FALSE);
+	}
+	else
+		return NULL;
+}
+
+static void
+update_layout (DataWidget *dwid)
+{
+	GSList *newparts_list = NULL;
+	GArray *sources_array;
+	GError *lerror = NULL;
+	GtkWidget *new_contents;
+
+	new_contents = gtk_vbox_new (FALSE, 0);
+
+	sources_array = data_source_manager_get_sources_array (dwid->priv->mgr, &lerror);
+	if (!sources_array) {
+		gchar *str;
+		if (lerror && lerror->message)
+			str = g_strdup_printf (_("Error: %s"), lerror->message);
+		else {
+			const GSList *list;
+			list = data_source_manager_get_sources (dwid->priv->mgr);
+			if (list)
+				str = g_strdup_printf (_("Error: %s"), _("No detail"));
+			else
+				str = g_strdup_printf (_("Error: %s"), _("No data source defined"));
+		}
+                g_clear_error (&lerror);
+                gtk_label_set_text (GTK_LABEL (dwid->priv->info_label), str);
+                g_free (str);
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (dwid->priv->top_nb), 0);
+		goto cleanups;
+	}
 
 	if (sources_array->len == 1) {
 		GArray *subarray = g_array_index (sources_array, GArray*, 0);
 		if (subarray->len == 1) {
 			DataPart *part;
+			gboolean reused;
 			DataSource *source;
 			source = g_array_index (subarray, DataSource*, 0);
-			part = create_part (dwid, source);
-			gtk_box_pack_start (GTK_BOX (dwid), part->top, TRUE, TRUE, 0);
-
-			data_source_execute (source, NULL);
+			part = create_or_reuse_part (dwid, source, &reused);
+			gtk_box_pack_start (GTK_BOX (new_contents), part->top, TRUE, TRUE, 0);
+			g_object_unref ((GObject*) part->top);
+			newparts_list = g_slist_prepend (newparts_list, part);
+			if (!reused)
+				data_source_execute (source, NULL);
 		}
 		else {
 			GSList *paned_list;
 			gint i;
 			paned_list = make_paned_list (subarray->len, FALSE);
-			gtk_box_pack_start (GTK_BOX (dwid), GTK_WIDGET (paned_list->data), TRUE, TRUE, 0);
+			gtk_box_pack_start (GTK_BOX (new_contents),
+					    GTK_WIDGET (paned_list->data), TRUE, TRUE, 0);
 			for (i = 0; i < subarray->len; i++) {
 				DataPart *part;
+				gboolean reused;
 				DataSource *source;
 				source = g_array_index (subarray, DataSource*, i);
-				part = create_part (dwid, source);
+				part = create_or_reuse_part (dwid, source, &reused);
 				pack_in_paned_list (paned_list, subarray->len, i, part->top);
-
-				data_source_execute (source, NULL);
+				g_object_unref ((GObject*) part->top);
+				newparts_list = g_slist_prepend (newparts_list, part);
+				if (!reused)
+					data_source_execute (source, NULL);
 			}
 			g_slist_free (paned_list);
 		}
@@ -266,20 +622,24 @@ data_widget_new (GArray *sources_array)
 	else {
 		GSList *top_paned_list;
 		gint j;
-
+		
 		top_paned_list = make_paned_list (sources_array->len, TRUE);
-		gtk_box_pack_start (GTK_BOX (dwid), GTK_WIDGET (top_paned_list->data), TRUE, TRUE, 0);
+		gtk_box_pack_start (GTK_BOX (new_contents),
+				    GTK_WIDGET (top_paned_list->data), TRUE, TRUE, 0);
 		for (j = 0; j < sources_array->len; j++) {
 			GArray *subarray = g_array_index (sources_array, GArray*, j);
 			DataSource *source;
-
+			
 			if (subarray->len == 1) {
 				DataPart *part;
+				gboolean reused;
 				source = g_array_index (subarray, DataSource*, 0);
-				part = create_part (dwid, source);
+				part = create_or_reuse_part (dwid, source, &reused);
 				pack_in_paned_list (top_paned_list, sources_array->len, j, part->top);
-				
-				data_source_execute (source, NULL);
+				g_object_unref ((GObject*) part->top);
+				newparts_list = g_slist_prepend (newparts_list, part);
+				if (!reused)
+					data_source_execute (source, NULL);
 			}
 			else {
 				GSList *paned_list;
@@ -289,24 +649,90 @@ data_widget_new (GArray *sources_array)
 						    GTK_WIDGET (paned_list->data));
 				for (i = 0; i < subarray->len; i++) {
 					DataPart *part;
+					gboolean reused;
 					source = g_array_index (subarray, DataSource*, i);
-					part = create_part (dwid, source);
+					part = create_or_reuse_part (dwid, source, &reused);
 					pack_in_paned_list (paned_list, subarray->len, i, part->top);
-					
-					data_source_execute (source, NULL);
+					g_object_unref ((GObject*) part->top);
+					newparts_list = g_slist_prepend (newparts_list, part);
+					if (!reused)
+						data_source_execute (source, NULL);
 				}
 				g_slist_free (paned_list);
 			}
 		}
 		g_slist_free (top_paned_list);
 	}
+	data_source_manager_destroy_sources_array (sources_array);
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (dwid->priv->top_nb), 1);
+	
+ cleanups:
+	{
+		GSList *list, *nlist = NULL;
+		for (list = dwid->priv->parts; list; list = list->next) {
+			if (!g_slist_find (newparts_list, list->data)) {
+				/* useless part */
+				data_part_free ((DataPart *) list->data, dwid->priv->parts);
+			}
+			else
+				nlist = g_slist_prepend (nlist, list->data);
+		}
+		g_slist_free (newparts_list);
+		g_slist_free (dwid->priv->parts);
+		dwid->priv->parts = g_slist_reverse (nlist);
+	}
+
+	gtk_box_pack_start (GTK_BOX (dwid->priv->contents_page_vbox), new_contents, TRUE, TRUE, 0);
+	gtk_widget_show_all (new_contents);
+	if (dwid->priv->contents_page)
+		gtk_widget_destroy (dwid->priv->contents_page);
+	dwid->priv->contents_page = new_contents;
+}
 
+static void
+mgr_list_changed_cb (DataSourceManager *mgr, DataWidget *dwid)
+{
+	update_layout (dwid);
+}
+
+/**
+ * data_widget_new
+ *
+ * Returns: the newly created widget.
+ */
+GtkWidget *
+data_widget_new (DataSourceManager *mgr)
+{
+	DataWidget *dwid;
+
+	g_return_val_if_fail (IS_DATA_SOURCE_MANAGER (mgr), NULL); 
+
+	dwid = g_object_new (DATA_WIDGET_TYPE, NULL);
+	dwid->priv->mgr = DATA_SOURCE_MANAGER (mgr);
+	g_object_ref ((GObject*) dwid->priv->mgr);
+	g_signal_connect (mgr, "list_changed",
+			  G_CALLBACK (mgr_list_changed_cb), dwid);
+
+	update_layout (dwid);
 	return GTK_WIDGET (dwid);
 }
 
 static void
-data_part_free (DataPart *part)
+data_part_free (DataPart *part, GSList *all_parts)
 {
+	if (all_parts) {
+		GSList *list;
+		for (list = all_parts; list; list = list->next) {
+			DataPart *apart = (DataPart *) list->data;
+			if (apart == part)
+				continue;
+			apart->dep_parts = g_slist_remove_all (apart->dep_parts, part);
+		}
+	}
+
+	if (part->top)
+		gtk_widget_destroy (part->top);
+
 	if (part->source) {
 		g_signal_handlers_disconnect_by_func (part->source,
 						      G_CALLBACK (source_exec_started_cb), part);
@@ -319,6 +745,8 @@ data_part_free (DataPart *part)
 		g_object_unref (part->export_data);
 	if (part->dep_parts)
 		g_slist_free (part->dep_parts);
+	if (part->menu)
+		gtk_widget_destroy (part->menu);
 	g_free (part);
 }
 
@@ -351,89 +779,89 @@ source_exec_finished_cb (DataSource *source, GError *error, DataPart *part)
 #ifdef GDA_DEBUG_NO
 	g_print ("==== Execution of source [%s] finished\n", data_source_get_title (part->source));*/
 #endif
-	if (error) {
-		gchar *tmp;
-		tmp = g_markup_printf_escaped ("\n<b>Error:\n</b>%s",
-					       error->message ? error->message : _("Error: no detail"));
-		if (! part->error_widget) {
-			part->error_widget = gtk_label_new ("");
-			gtk_misc_set_alignment (GTK_MISC (part->error_widget), 0., 0.);
-			part->error_widget_page = gtk_notebook_append_page (part->nb, part->error_widget,
-									    NULL);
-			gtk_widget_show (part->error_widget);
+		if (error) {
+			gchar *tmp;
+			tmp = g_markup_printf_escaped ("\n<b>Error:\n</b>%s",
+						       error->message ? error->message : _("Error: no detail"));
+			if (! part->error_widget) {
+				part->error_widget = gtk_label_new ("");
+				gtk_misc_set_alignment (GTK_MISC (part->error_widget), 0., 0.);
+				part->error_widget_page = gtk_notebook_append_page (part->nb, part->error_widget,
+										    NULL);
+				gtk_widget_show (part->error_widget);
+			}
+			gtk_label_set_markup (GTK_LABEL (part->error_widget), tmp);
+			g_free (tmp);
+			gtk_notebook_set_current_page (part->nb, part->error_widget_page);
+			return;
 		}
-		gtk_label_set_markup (GTK_LABEL (part->error_widget), tmp);
-		g_free (tmp);
-		gtk_notebook_set_current_page (part->nb, part->error_widget_page);
-		return;
-	}
 	
-	if (! part->data_widget) {
-		GtkWidget *cwid;
-		BrowserConnection *bcnc;
-		bcnc = browser_window_get_connection ((BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) part->dwid));
-		cwid = (GtkWidget*) data_source_create_grid (part->source);
-		ui_formgrid_handle_user_prefs (UI_FORMGRID (cwid), bcnc,
-					       data_source_get_statement (part->source));
-
-		wid = (GtkWidget*) ui_formgrid_get_grid_widget (UI_FORMGRID (cwid));
-		part->data_widget = wid;
-		part->data_widget_page = gtk_notebook_append_page (part->nb, cwid, NULL);
-		gtk_widget_show (cwid);
+		if (! part->data_widget) {
+			GtkWidget *cwid;
+			BrowserConnection *bcnc;
+			bcnc = browser_window_get_connection ((BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) part->dwid));
+			cwid = (GtkWidget*) data_source_create_grid (part->source);
+			ui_formgrid_handle_user_prefs (UI_FORMGRID (cwid), bcnc,
+						       data_source_get_statement (part->source));
+
+			wid = (GtkWidget*) ui_formgrid_get_grid_widget (UI_FORMGRID (cwid));
+			part->data_widget = wid;
+			part->data_widget_page = gtk_notebook_append_page (part->nb, cwid, NULL);
+			gtk_widget_show (cwid);
 #ifdef GDA_DEBUG_NO
-		g_print ("Creating data widget for source [%s]\n",
-			 data_source_get_title (part->source));
+			g_print ("Creating data widget for source [%s]\n",
+				 data_source_get_title (part->source));
 #endif
 
-		/* compute part->export_data */
-		GArray *export_names;
-		export_names = data_source_get_export_names (part->source);
-		if (export_names && (export_names->len > 0)) {
-			GSList *holders = NULL;
-			GdaDataModel *model;
-			GHashTable *export_columns;
-			gint i;
-			GdaDataModelIter *iter;
-
-			iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (wid));
-			g_object_get (wid, "model", &model, NULL);
-
-			export_columns = data_source_get_export_columns (part->source);
-			for (i = 0; i < export_names->len; i++) {
-				gint col;
-				GdaHolder *bindto;
-				col = GPOINTER_TO_INT (g_hash_table_lookup (export_columns,
-									    g_array_index (export_names,
-											   gchar*, i))) - 1;
-				bindto = gda_data_model_iter_get_holder_for_field (iter, col);
-				if (bindto) {
-					GdaHolder *holder;
-					holder = gda_holder_copy (bindto);
-					g_object_set ((GObject*) holder, "id",
-						      g_array_index (export_names, gchar*, i), NULL);
-					holders = g_slist_prepend (holders, holder);
+			/* compute part->export_data */
+			GArray *export_names;
+			export_names = data_source_get_export_names (part->source);
+			if (export_names && (export_names->len > 0)) {
+				GSList *holders = NULL;
+				GdaDataModel *model;
+				GHashTable *export_columns;
+				gint i;
+				GdaDataModelIter *iter;
+
+				iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (wid));
+				g_object_get (wid, "model", &model, NULL);
+
+				export_columns = data_source_get_export_columns (part->source);
+				for (i = 0; i < export_names->len; i++) {
+					gint col;
+					GdaHolder *bindto;
+					col = GPOINTER_TO_INT (g_hash_table_lookup (export_columns,
+										    g_array_index (export_names,
+												   gchar*, i))) - 1;
+					bindto = gda_data_model_iter_get_holder_for_field (iter, col);
+					if (bindto) {
+						GdaHolder *holder;
+						holder = gda_holder_copy (bindto);
+						g_object_set ((GObject*) holder, "id",
+							      g_array_index (export_names, gchar*, i), NULL);
+						holders = g_slist_prepend (holders, holder);
 #ifdef DEBUG_NO
-					g_print ("HOLDER [%s::%s]\n",
-						 gda_holder_get_id (holder),
-						 g_type_name (gda_holder_get_g_type (holder)));
+						g_print ("HOLDER [%s::%s]\n",
+							 gda_holder_get_id (holder),
+							 g_type_name (gda_holder_get_g_type (holder)));
 #endif
-					g_assert (gda_holder_set_bind (holder, bindto, NULL));
+						g_assert (gda_holder_set_bind (holder, bindto, NULL));
+					}
 				}
-			}
 			
-			g_object_unref (model);
-			if (holders) {
-				part->export_data = gda_set_new (holders);
-				g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
-				g_slist_free (holders);
-
-				g_signal_connect (wid, "selection-changed",
-						  G_CALLBACK (data_part_selection_changed_cb), part);
+				g_object_unref (model);
+				if (holders) {
+					part->export_data = gda_set_new (holders);
+					g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+					g_slist_free (holders);
+
+					g_signal_connect (wid, "selection-changed",
+							  G_CALLBACK (data_part_selection_changed_cb), part);
+				}
 			}
 		}
-	}
-	gtk_notebook_set_current_page (part->nb, part->data_widget_page);
-	compute_sources_dependencies (part);
+		gtk_notebook_set_current_page (part->nb, part->data_widget_page);
+		compute_sources_dependencies (part);
 }
 
 static void
diff --git a/tools/browser/data-manager/data-widget.h b/tools/browser/data-manager/data-widget.h
index 82d79ff..8756cd0 100644
--- a/tools/browser/data-manager/data-widget.h
+++ b/tools/browser/data-manager/data-widget.h
@@ -26,6 +26,7 @@
 #include <gtk/gtk.h>
 #include <libgda/libgda.h>
 #include "data-source.h"
+#include "data-source-manager.h"
 
 G_BEGIN_DECLS
 
@@ -51,7 +52,7 @@ struct _DataWidgetClass {
 
 
 GType      data_widget_get_type   (void) G_GNUC_CONST;
-GtkWidget *data_widget_new        (GArray *sources_array);
+GtkWidget *data_widget_new        (DataSourceManager *mgr);
 GdaSet    *data_widget_get_export (DataWidget *dwid, DataSource *source);
 void       data_widget_rerun      (DataWidget *dwid);
 
diff --git a/tools/browser/doc/gda-browser-sections.txt b/tools/browser/doc/gda-browser-sections.txt
index d05208d..ca20544 100644
--- a/tools/browser/doc/gda-browser-sections.txt
+++ b/tools/browser/doc/gda-browser-sections.txt
@@ -93,6 +93,7 @@ browser_connection_execution_get_result
 BrowserConnectionExecuteCallback
 browser_connection_execute_statement_cb
 browser_connection_normalize_sql_statement
+browser_connection_check_sql_statement_validify
 browser_connection_rerun_select
 browser_connection_rerun_select_cb
 <SUBSECTION>
diff --git a/tools/browser/doc/tmpl/browser-connection.sgml b/tools/browser/doc/tmpl/browser-connection.sgml
index 2c9a52f..a393308 100644
--- a/tools/browser/doc/tmpl/browser-connection.sgml
+++ b/tools/browser/doc/tmpl/browser-connection.sgml
@@ -282,6 +282,17 @@ An opened connection
 @Returns: 
 
 
+<!-- ##### FUNCTION browser_connection_check_sql_statement_validify ##### -->
+<para>
+
+</para>
+
+ bcnc: 
+ sqlst: 
+ error: 
+ Returns: 
+
+
 <!-- ##### FUNCTION browser_connection_rerun_select ##### -->
 <para>
 
diff --git a/tools/browser/doc/tmpl/browser-window.sgml b/tools/browser/doc/tmpl/browser-window.sgml
index 32634b7..3746160 100644
--- a/tools/browser/doc/tmpl/browser-window.sgml
+++ b/tools/browser/doc/tmpl/browser-window.sgml
@@ -87,6 +87,7 @@ Top level browser window
 </para>
 
 @bwin: 
+ type: 
 @context: 
 @text: 
 
@@ -97,6 +98,7 @@ Top level browser window
 </para>
 
 @bwin: 
+ type: 
 @context: 
 @format: 
 @Varargs: 
diff --git a/tools/browser/gda-browser-menu-ind.png b/tools/browser/gda-browser-menu-ind.png
new file mode 100644
index 0000000..de0ac6e
Binary files /dev/null and b/tools/browser/gda-browser-menu-ind.png differ
diff --git a/tools/browser/query-exec/query-console.c b/tools/browser/query-exec/query-console.c
index 74dd0ab..a741625 100644
--- a/tools/browser/query-exec/query-console.c
+++ b/tools/browser/query-exec/query-console.c
@@ -935,13 +935,14 @@ query_exec_fetch_cb (QueryConsole *tconsole)
 				    browser_connection_get_transaction_status (tconsole->priv->bcnc) &&
 				    gda_statement_get_statement_type (estmt->stmt) != GDA_SQL_STATEMENT_BEGIN) {
 					browser_window_show_notice_printf (BROWSER_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tconsole)),
-									"QueryExecTransactionStarted",
-									"%s", _("A transaction has automatically been started\n"
-										"during this statement's execution, this usually\n"
-										"happens when blobs are selected (and the transaction\n"
-										"will have to remain opened while the blobs are still\n"
-										"accessible, clear the corresponding history item before\n"
-										"closing the transaction)."));
+									   GTK_MESSAGE_INFO,
+									   "QueryExecTransactionStarted",
+									   "%s", _("A transaction has automatically been started\n"
+										   "during this statement's execution, this usually\n"
+										   "happens when blobs are selected (and the transaction\n"
+										   "will have to remain opened while the blobs are still\n"
+										   "accessible, clear the corresponding history item before\n"
+										   "closing the transaction)."));
 				}
 				
 				query_editor_add_history_item (tconsole->priv->history, history);
diff --git a/tools/browser/schema-browser/table-info.c b/tools/browser/schema-browser/table-info.c
index 271c160..3668c91 100644
--- a/tools/browser/schema-browser/table-info.c
+++ b/tools/browser/schema-browser/table-info.c
@@ -522,8 +522,9 @@ static void statement_executed_cb (BrowserConnection *bcnc,
 				    error->message : _("No detail"));
 	else
 		browser_window_show_notice_printf (BROWSER_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tinfo)),
-						"DataInsertQuery",
-						_("Data successfully inserted"));
+						   GTK_MESSAGE_INFO,
+						   "DataInsertQuery",
+						   _("Data successfully inserted"));
 }
 
 static void
diff --git a/tools/browser/support.c b/tools/browser/support.c
index ec1131e..750f4eb 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -122,7 +122,8 @@ browser_connection_close (GtkWindow *parent, BrowserConnection *bcnc)
  * @format: printf() style format string
  * @...: arguments for @format
  *
- * Displays a modal error until the user aknowledges it.
+ * Displays an error message until the user aknowledges it. I @parent is a #BrowserWindow, then
+ * the error message is displayed in the window if possible
  */
 void
 browser_show_error (GtkWindow *parent, const gchar *format, ...)
@@ -137,6 +138,12 @@ browser_show_error (GtkWindow *parent, const gchar *format, ...)
         vsnprintf (sz, sizeof (sz), format, args);
         va_end (args);
 
+	if (BROWSER_IS_WINDOW (parent)) {
+		browser_window_show_notice (BROWSER_WINDOW (parent), GTK_MESSAGE_ERROR,
+					    NULL, sz);
+		return;
+	}
+
         /* create the error message dialog */
 	dialog = gtk_message_dialog_new (parent,
 					 GTK_DIALOG_DESTROY_WITH_PARENT |
@@ -230,8 +237,8 @@ browser_show_help (GtkWindow *parent, const gchar *topic)
 		g_error_free (error);
 	}
 	else
-		browser_window_show_notice (BROWSER_WINDOW (parent),
-					 "show-help", _("Help is being loaded, please wait..."));
+		browser_window_show_notice (BROWSER_WINDOW (parent), GTK_MESSAGE_INFO,
+					    "show-help", _("Help is being loaded, please wait..."));
 
 	g_free (uri);
 }
@@ -342,6 +349,7 @@ browser_get_pixbuf_icon (BrowserIconType type)
 		"gda-browser-query.png",
 		"gda-browser-grid.png",
 		"gda-browser-form.png",
+		"gda-browser-menu-ind.png",
 	};
 
 	if (!array)
diff --git a/tools/browser/support.h b/tools/browser/support.h
index 4b9b470..f6c7100 100644
--- a/tools/browser/support.h
+++ b/tools/browser/support.h
@@ -75,6 +75,8 @@ typedef enum {
 	BROWSER_ICON_GRID,
 	BROWSER_ICON_FORM,
 
+	BROWSER_ICON_MENU_INDICATOR,
+
 	BROWSER_ICON_LAST
 } BrowserIconType;
 



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