[libgda] GdaBrowser: initial support for virtual connections



commit f62fe219749f5401a02616f8e5f684aba25ab56c
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sat Oct 17 12:18:11 2009 +0200

    GdaBrowser: initial support for virtual connections

 tools/browser/Makefile.am                     |    4 +
 tools/browser/browser-connection-priv.h       |   45 +++
 tools/browser/browser-connection.c            |  396 ++++++++------------
 tools/browser/browser-connection.h            |    4 +-
 tools/browser/browser-connections-list.c      |   42 +--
 tools/browser/browser-virtual-connection.c    |  506 +++++++++++++++++++++++++
 tools/browser/browser-virtual-connection.h    |   94 +++++
 tools/browser/browser-window.c                |   40 ++
 tools/browser/connection-binding-properties.c |  394 +++++++++++++++++++
 tools/browser/connection-binding-properties.h |   61 +++
 tools/browser/support.c                       |   70 ++++
 tools/browser/support.h                       |   11 +
 12 files changed, 1393 insertions(+), 274 deletions(-)
---
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 4814fe3..1010f13 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -54,10 +54,14 @@ libbrowser_la_SOURCES=\
 	browser-favorites.h \
 	browser-variable.c \
 	browser-variable.h \
+	browser-virtual-connection.c \
+	browser-virtual-connection.h \
 	browser-window.c \
 	browser-window.h \
 	browser-connections-list.c \
 	browser-connections-list.h \
+	connection-binding-properties.c \
+	connection-binding-properties.h \
 	mgr-favorites.h \
 	mgr-favorites.c \
 	browser-stock-icons.c \
diff --git a/tools/browser/browser-connection-priv.h b/tools/browser/browser-connection-priv.h
new file mode 100644
index 0000000..27c5c38
--- /dev/null
+++ b/tools/browser/browser-connection-priv.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __BROWSER_CONNECTION_PRIVATE_H__
+#define __BROWSER_CONNECTION_PRIVATE_H__
+
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
+
+struct _BrowserConnectionPrivate {
+	GdaThreadWrapper *wrapper;
+	GSList           *wrapper_jobs;
+	guint             wrapper_results_timer;
+	GHashTable       *executed_statements; /* key = guint exec ID, value = a StatementResult pointer */
+
+	gchar         *name;
+	GdaConnection *cnc;
+	gchar         *dict_file_name;
+        GdaSqlParser  *parser;
+        GSList        *variables; /* list of BrowserVariable pointer, owned here */
+
+	GdaDsnInfo     dsn_info;
+	GMutex        *p_mstruct_mutex;
+	GdaMetaStruct *p_mstruct; /* private GdaMetaStruct: while it is being created */
+	GdaMetaStruct *mstruct; /* public GdaMetaStruct: once it has been created and is no more modified */
+
+	BrowserFavorites *bfav;
+};
+
+#endif
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index 8219042..ae1ae9b 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -25,7 +25,8 @@
 #include "marshal.h"
 #include <sql-parser/gda-sql-parser.h>
 #include <libgda/gda-sql-builder.h>
-#include <virtual/libgda-virtual.h>
+
+#include "browser-connection-priv.h"
 
 /* code inclusion */
 #include "../dict-file-name.c"
@@ -47,26 +48,6 @@ statement_result_free (StatementResult *res)
 	g_free (res);
 }
 
-struct _BrowserConnectionPrivate {
-	GdaThreadWrapper *wrapper;
-	GSList           *wrapper_jobs;
-	guint             wrapper_results_timer;
-	GHashTable       *executed_statements; /* key = guint exec ID, value = a StatementResult pointer */
-
-	gchar         *name;
-	GdaConnection *cnc;
-	gchar         *dict_file_name;
-        GdaSqlParser  *parser;
-        GSList        *variables; /* list of BrowserVariable pointer, owned here */
-
-	GdaDsnInfo     dsn_info;
-	GMutex        *p_mstruct_mutex;
-	GdaMetaStruct *p_mstruct; /* private GdaMetaStruct: while it is being created */
-	GdaMetaStruct *mstruct; /* public GdaMetaStruct: once it has been created and is no more modified */
-
-	BrowserFavorites *bfav;
-};
-
 /* signals */
 enum {
 	BUSY,
@@ -140,10 +121,23 @@ pop_wrapper_job (BrowserConnection *bcnc, WrapperJob *wj)
 static void browser_connection_class_init (BrowserConnectionClass *klass);
 static void browser_connection_init (BrowserConnection *bcnc);
 static void browser_connection_dispose (GObject *object);
-
+static void browser_connection_set_property (GObject *object,
+					     guint param_id,
+					     const GValue *value,
+					     GParamSpec *pspec);
+static void browser_connection_get_property (GObject *object,
+					     guint param_id,
+					     GValue *value,
+					     GParamSpec *pspec);
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass  *parent_class = NULL;
 
+/* properties */
+enum {
+        PROP_0,
+        PROP_GDA_CNC
+};
+
 GType
 browser_connection_get_type (void)
 {
@@ -216,6 +210,17 @@ browser_connection_class_init (BrowserConnectionClass *klass)
 	klass->favorites_changed = NULL;
 	klass->transaction_status_changed = NULL;
 
+	klass->is_busy = NULL;
+
+	/* Properties */
+        object_class->set_property = browser_connection_set_property;
+        object_class->get_property = browser_connection_get_property;
+	g_object_class_install_property (object_class, PROP_GDA_CNC,
+                                         g_param_spec_object ("gda-connection", NULL, "Connection to use",
+                                                              GDA_TYPE_CONNECTION,
+                                                              G_PARAM_READABLE | G_PARAM_WRITABLE |
+							      G_PARAM_CONSTRUCT_ONLY));
+
 	object_class->dispose = browser_connection_dispose;
 }
 
@@ -242,6 +247,139 @@ browser_connection_init (BrowserConnection *bcnc)
 }
 
 static void
+transaction_status_changed_cb (GdaConnection *cnc, BrowserConnection *bcnc)
+{
+	g_signal_emit (bcnc, browser_connection_signals [TRANSACTION_STATUS_CHANGED], 0);
+}
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_meta_store_update (BrowserConnection *bcnc, GError **error)
+{
+	gboolean retval;
+	GdaMetaContext context = {"_tables", 0, NULL, NULL};
+	retval = gda_connection_update_meta_store (bcnc->priv->cnc, &context, error);
+
+	return GINT_TO_POINTER (retval ? 2 : 1);
+}
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_meta_struct_sync (BrowserConnection *bcnc, GError **error)
+{
+	gboolean retval;
+
+	g_mutex_lock (bcnc->priv->p_mstruct_mutex);
+	g_assert (bcnc->priv->p_mstruct);
+	g_object_ref (G_OBJECT (bcnc->priv->p_mstruct));
+	retval = gda_meta_struct_complement_all (bcnc->priv->p_mstruct, error);
+	g_object_unref (G_OBJECT (bcnc->priv->p_mstruct));
+	g_mutex_unlock (bcnc->priv->p_mstruct_mutex);
+
+	return GINT_TO_POINTER (retval ? 2 : 1);
+}
+
+static void
+browser_connection_set_property (GObject *object,
+				 guint param_id,
+				 const GValue *value,
+				 GParamSpec *pspec)
+{
+        BrowserConnection *bcnc;
+
+        bcnc = BROWSER_CONNECTION (object);
+        if (bcnc->priv) {
+                switch (param_id) {
+                case PROP_GDA_CNC:
+                        bcnc->priv->cnc = (GdaConnection*) g_value_get_object (value);
+                        if (!bcnc->priv->cnc)
+				return;
+
+			g_object_ref (bcnc->priv->cnc);
+			g_signal_connect (bcnc->priv->cnc, "transaction-status-changed",
+					  G_CALLBACK (transaction_status_changed_cb), bcnc);
+
+			/* meta store */
+			gchar *dict_file_name = NULL;
+			gboolean update_store = FALSE;
+			GdaMetaStore *store;
+			gchar *cnc_string, *cnc_info;
+			
+			g_object_get (G_OBJECT (bcnc->priv->cnc),
+				      "dsn", &cnc_info,
+				      "cnc-string", &cnc_string, NULL);
+			dict_file_name = compute_dict_file_name (cnc_info ? gda_config_get_dsn_info (cnc_info) : NULL,
+								 cnc_string);
+			g_free (cnc_string);
+			if (dict_file_name) {
+				if (! g_file_test (dict_file_name, G_FILE_TEST_EXISTS))
+					update_store = TRUE;
+				store = gda_meta_store_new_with_file (dict_file_name);
+			}
+			else {
+				store = gda_meta_store_new (NULL);
+				if (store)
+					update_store = TRUE;
+			}
+			bcnc->priv->dict_file_name = dict_file_name;
+			g_object_set (G_OBJECT (bcnc->priv->cnc), "meta-store", store, NULL);
+			if (update_store) {
+				GError *lerror = NULL;
+				guint job_id;
+				job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+								     (GdaThreadWrapperFunc) wrapper_meta_store_update,
+								     g_object_ref (bcnc), g_object_unref, &lerror);
+				if (job_id > 0)
+					push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STORE_UPDATE,
+							  _("Getting database schema information"));
+				else if (lerror) {
+					browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
+							    lerror->message ? lerror->message : _("No detail"));
+					g_error_free (lerror);
+				}
+			}
+			else {
+				guint job_id;
+				GError *lerror = NULL;
+				
+				bcnc->priv->p_mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_ALL);
+				job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+								     (GdaThreadWrapperFunc) wrapper_meta_struct_sync,
+								     g_object_ref (bcnc), g_object_unref, &lerror);
+				if (job_id > 0)
+					push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STRUCT_SYNC,
+							  _("Analysing database schema"));
+				else if (lerror) {
+					browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
+							    lerror->message ? lerror->message : _("No detail"));
+					g_error_free (lerror);
+				}
+				g_object_unref (store);
+			}
+                        break;
+                }
+        }
+}
+
+static void
+browser_connection_get_property (GObject *object,
+				 guint param_id,
+				 GValue *value,
+				 GParamSpec *pspec)
+{
+        BrowserConnection *bcnc;
+
+        bcnc = BROWSER_CONNECTION (object);
+        if (bcnc->priv) {
+                switch (param_id) {
+                case PROP_GDA_CNC:
+                        g_value_set_object (value, bcnc->priv->cnc);
+                        break;
+                }
+        }
+}
+
+static void
 clear_dsn_info (BrowserConnection *bcnc)
 {
         g_free (bcnc->priv->dsn_info.name);
@@ -267,12 +405,6 @@ fav_changed_cb (BrowserFavorites *bfav, BrowserConnection *bcnc)
 }
 
 static void
-transaction_status_changed_cb (GdaConnection *cnc, BrowserConnection *bcnc)
-{
-	g_signal_emit (bcnc, browser_connection_signals [TRANSACTION_STATUS_CHANGED], 0);
-}
-
-static void
 browser_connection_dispose (GObject *object)
 {
 	BrowserConnection *bcnc;
@@ -331,33 +463,6 @@ browser_connection_dispose (GObject *object)
 	parent_class->dispose (object);
 }
 
-/* executed in sub @bcnc->priv->wrapper's thread */
-static gpointer
-wrapper_meta_store_update (BrowserConnection *bcnc, GError **error)
-{
-	gboolean retval;
-	GdaMetaContext context = {"_tables", 0, NULL, NULL};
-	retval = gda_connection_update_meta_store (bcnc->priv->cnc, &context, error);
-
-	return GINT_TO_POINTER (retval ? 2 : 1);
-}
-
-/* executed in sub @bcnc->priv->wrapper's thread */
-static gpointer
-wrapper_meta_struct_sync (BrowserConnection *bcnc, GError **error)
-{
-	gboolean retval;
-
-	g_mutex_lock (bcnc->priv->p_mstruct_mutex);
-	g_assert (bcnc->priv->p_mstruct);
-	g_object_ref (G_OBJECT (bcnc->priv->p_mstruct));
-	retval = gda_meta_struct_complement_all (bcnc->priv->p_mstruct, error);
-	g_object_unref (G_OBJECT (bcnc->priv->p_mstruct));
-	g_mutex_unlock (bcnc->priv->p_mstruct_mutex);
-
-	return GINT_TO_POINTER (retval ? 2 : 1);
-}
-
 static gboolean
 check_for_wrapper_result (BrowserConnection *bcnc)
 {
@@ -491,188 +596,11 @@ browser_connection_new (GdaConnection *cnc)
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 
-	bcnc = BROWSER_CONNECTION (g_object_new (BROWSER_TYPE_CONNECTION, NULL));
-	bcnc->priv->cnc = g_object_ref (cnc);
-	g_signal_connect (cnc, "transaction-status-changed",
-			  G_CALLBACK (transaction_status_changed_cb), bcnc);
-
-	/* meta store */
-	gchar *dict_file_name = NULL;
-	gboolean update_store = FALSE;
-	GdaMetaStore *store;
-	gchar *cnc_string, *cnc_info;
-	
-	g_object_get (G_OBJECT (cnc),
-		      "dsn", &cnc_info,
-		      "cnc-string", &cnc_string, NULL);
-	dict_file_name = compute_dict_file_name (cnc_info ? gda_config_get_dsn_info (cnc_info) : NULL,
-						 cnc_string);
-	g_free (cnc_string);
-	if (dict_file_name) {
-		if (! g_file_test (dict_file_name, G_FILE_TEST_EXISTS))
-			update_store = TRUE;
-		store = gda_meta_store_new_with_file (dict_file_name);
-	}
-	else {
-		store = gda_meta_store_new (NULL);
-		if (store)
-			update_store = TRUE;
-	}
-	bcnc->priv->dict_file_name = dict_file_name;
-	g_object_set (G_OBJECT (cnc), "meta-store", store, NULL);
-	if (update_store) {
-		GError *lerror = NULL;
-		guint job_id;
-		job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
-						     (GdaThreadWrapperFunc) wrapper_meta_store_update,
-						     g_object_ref (bcnc), g_object_unref, &lerror);
-		if (job_id > 0)
-			push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STORE_UPDATE,
-					  _("Getting database schema information"));
-		else if (lerror) {
-			browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
-					    lerror->message ? lerror->message : _("No detail"));
-			g_error_free (lerror);
-		}
-	}
-	else {
-		guint job_id;
-		GError *lerror = NULL;
-
-		bcnc->priv->p_mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_ALL);
-		job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
-						     (GdaThreadWrapperFunc) wrapper_meta_struct_sync,
-						     g_object_ref (bcnc), g_object_unref, &lerror);
-		if (job_id > 0)
-			push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STRUCT_SYNC,
-					  _("Analysing database schema"));
-		else if (lerror) {
-			browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
-					    lerror->message ? lerror->message : _("No detail"));
-			g_error_free (lerror);
-		}
-		g_object_unref (store);
-	}
+	bcnc = BROWSER_CONNECTION (g_object_new (BROWSER_TYPE_CONNECTION, "gda-connection", cnc, NULL));
 
 	return bcnc;
 }
 
-static GdaThreadWrapper *wrapper = NULL;
-typedef struct {
-        guint cncid;
-        GMainLoop *loop;
-        GError **error;
-
-        /* out */
-        GdaConnection *cnc;
-} MainloopData;
-
-static gboolean
-check_for_cnc (MainloopData *data)
-{
-        GdaConnection *cnc;
-        GError *lerror = NULL;
-        cnc = gda_thread_wrapper_fetch_result (wrapper, FALSE, data->cncid, &lerror);
-        if (cnc || (!cnc && lerror)) {
-                /* waiting is finished! */
-                data->cnc = cnc;
-                if (lerror)
-                        g_propagate_error (data->error, lerror);
-                g_main_loop_quit (data->loop);
-                return FALSE;
-        }
-        return TRUE;
-}
-
-typedef struct {
-	GdaConnection *cnc_to_wrap;
-	const gchar *ns;
-} VirtualConnectionData;
-/*
- * executed in a sub thread
- */
-static GdaConnection *
-sub_thread_open_cnc (VirtualConnectionData *vcdata, GError **error)
-{
-#ifndef DUMMY
-	static GdaVirtualProvider *provider = NULL;
-	GdaConnection *virtual, *cnc;
-	if (!provider)
-		provider = gda_vprovider_hub_new ();
-
-	virtual = gda_virtual_connection_open_extended (provider, GDA_CONNECTION_OPTIONS_THREAD_SAFE, NULL);
-	cnc = g_object_get_data (G_OBJECT (virtual), "gda-virtual-connection");
-	if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (cnc), vcdata->cnc_to_wrap,
-				      vcdata->ns, error)) {
-		g_object_unref (virtual);
-		return NULL;
-	}
-	else
-		return virtual;
-#else
-        sleep (5);
-        g_set_error (error, 0, 0, "Oooo");
-        return NULL;
-#endif
-}
-/**
- * browser_connection_new_virtual
- * @bcnc: a #BrowserConnection
- * @ns: the namespace for @bcnc's tables
- * @error: a place to store errors, or %NULL
- *
- * Creates a new virtual connection using #BrowserConnection as a starting point.
- *
- * To close the new connection, use browser_core_close_connection().
- *
- * Returns: a new object
- */
-BrowserConnection *
-browser_connection_new_virtual (BrowserConnection *bcnc, const gchar *ns, GError **error)
-{
-	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
-
-	if (!wrapper)
-                wrapper = gda_thread_wrapper_new ();
-
-	guint cncid;
-	VirtualConnectionData vcdata;
-
-	vcdata.cnc_to_wrap = bcnc->priv->cnc;
-	vcdata.ns = ns ? ns : bcnc->priv->name;
-	cncid = gda_thread_wrapper_execute (wrapper,
-                                            (GdaThreadWrapperFunc) sub_thread_open_cnc,
-                                            (gpointer) &vcdata,
-                                            (GDestroyNotify) NULL,
-                                            error);
-        if (cncid == 0)
-                return NULL;
-
-	GMainLoop *loop;
-        guint source_id;
-        MainloopData data;
-	
-        loop = g_main_loop_new (NULL, FALSE);
-	data.cncid = cncid;
-        data.error = error;
-        data.loop = loop;
-        data.cnc = NULL;
-
-        source_id = g_timeout_add (200, (GSourceFunc) check_for_cnc, &data);
-        g_main_loop_run (loop);
-        g_main_loop_unref (loop);
-
-        if (data.cnc) {
-		BrowserConnection *nbcnc;
-                g_object_set (data.cnc, "monitor-wrapped-in-mainloop", TRUE, NULL);
-		nbcnc = browser_connection_new (data.cnc);
-		g_object_unref (data.cnc);
-		return nbcnc;
-	}
-	else
-		return NULL;
-}
-
 /**
  * browser_connection_get_name
  * @bcnc: a #BrowserConnection
@@ -756,6 +684,8 @@ browser_connection_is_busy (BrowserConnection *bcnc, gchar **out_reason)
 			*out_reason = g_strdup (wj->reason);
 		return TRUE;
 	}
+	else if (BROWSER_CONNECTION_CLASS (G_OBJECT_GET_CLASS (bcnc))->is_busy)
+		return BROWSER_CONNECTION_CLASS (G_OBJECT_GET_CLASS (bcnc))->is_busy (bcnc, out_reason);
 	else
 		return FALSE;
 }
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index 49b3025..4ad0737 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -52,12 +52,14 @@ struct _BrowserConnectionClass
 	void                    (*meta_changed) (BrowserConnection *bcnc, GdaMetaStruct *mstruct);
 	void                    (*favorites_changed) (BrowserConnection *bcnc);
 	void                    (*transaction_status_changed) (BrowserConnection *bcnc);
+
+	/* virtual methods */
+	gboolean                (*is_busy) (BrowserConnection *bcnc,  gchar **out_reason);
 };
 
 GType               browser_connection_get_type               (void) G_GNUC_CONST;
 
 BrowserConnection  *browser_connection_new                    (GdaConnection *cnc);
-BrowserConnection  *browser_connection_new_virtual            (BrowserConnection *bcnc, const gchar *ns, GError **error);
 const gchar        *browser_connection_get_name               (BrowserConnection *bcnc);
 const GdaDsnInfo   *browser_connection_get_information        (BrowserConnection *bcnc);
 
diff --git a/tools/browser/browser-connections-list.c b/tools/browser/browser-connections-list.c
index 28887f6..c96d261 100644
--- a/tools/browser/browser-connections-list.c
+++ b/tools/browser/browser-connections-list.c
@@ -304,37 +304,8 @@ connection_new_cb (GtkButton *button, BrowserConnectionsList *clist)
 	}
 }
 
-static void
-connection_bind_cb (GtkButton *button, BrowserConnectionsList *clist)
-{
-	GtkTreeSelection *select;
-	GtkTreeModel *model;
-	GtkTreeIter iter;
-	
-	select = gtk_tree_view_get_selection (clist->priv->treeview);
-	if (gtk_tree_selection_get_selected (select, &model, &iter)) {
-		BrowserConnection *bcnc, *nbcnc;
-		GError *error = NULL;
-		gtk_tree_model_get (model, &iter, COLUMN_BCNC, &bcnc, -1);
-		nbcnc = browser_connection_new_virtual (bcnc, NULL, &error);
-		if (nbcnc) {
-			BrowserWindow *nbwin;
-			nbwin = browser_window_new (nbcnc, NULL);
-			
-			browser_core_take_window (nbwin);
-			browser_core_take_connection (nbcnc);
-		}
-		else {
-			browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) clist),
-					    _("Could not open binding connection: %s"),
-					    error && error->message ? error->message : _("No detail"));
-			g_clear_error (&error);
-		}
-	}
-}
-
 /**
- * browser_connections_list_new
+ * browser_connections_list_show
  *
  * Creates a new #BrowserConnectionsList widget and displays it.
  * Only one is created and shown (singleton)
@@ -422,21 +393,12 @@ browser_connections_list_show (void)
 		gtk_widget_set_tooltip_text (button, _("Close selected connection"));
 		_clist->priv->close_cnc_button = button;
 
-		button = gtk_button_new_with_label (_("Open"));
+		button = gtk_button_new_with_label (_("Connect"));
 		gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
 		g_signal_connect (button, "clicked",
 				  G_CALLBACK (connection_new_cb), clist);
 		gtk_widget_set_tooltip_text (button, _("Open a new connection"));
 
-		button = gtk_button_new_with_label (_("Bind"));
-		gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
-		g_signal_connect (button, "clicked",
-				  G_CALLBACK (connection_bind_cb), clist);
-		gtk_widget_set_tooltip_text (button, _("Use selected connection to create\n"
-						       "a new binding connection to access data\n"
-						       "from multiple databases at once"));
-
-
 		/* GtkTreeModel and view */
 		GtkListStore *store;
 		store = gtk_list_store_new (NUM_COLUMNS,
diff --git a/tools/browser/browser-virtual-connection.c b/tools/browser/browser-virtual-connection.c
new file mode 100644
index 0000000..d3e728e
--- /dev/null
+++ b/tools/browser/browser-virtual-connection.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "browser-virtual-connection.h"
+#include "browser-connection-priv.h"
+#include <virtual/libgda-virtual.h>
+
+/* 
+ * Main static functions 
+ */
+static void browser_virtual_connection_class_init (BrowserVirtualConnectionClass *klass);
+static void browser_virtual_connection_init (BrowserVirtualConnection *stmt);
+static void browser_virtual_connection_dispose (GObject *object);
+static void browser_virtual_connection_set_property (GObject *object,
+						     guint param_id,
+						     const GValue *value,
+						     GParamSpec *pspec);
+static void browser_virtual_connection_get_property (GObject *object,
+						     guint param_id,
+						     GValue *value,
+						     GParamSpec *pspec);
+
+struct _BrowserVirtualConnectionPrivate
+{
+	GtkTable    *layout_table;
+	BrowserVirtualConnectionSpecs *specs;
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+/* properties */
+enum {
+        PROP_0,
+        PROP_SPECS
+};
+
+GType
+browser_virtual_connection_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (BrowserVirtualConnectionClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) browser_virtual_connection_class_init,
+			NULL,
+			NULL,
+			sizeof (BrowserVirtualConnection),
+			0,
+			(GInstanceInitFunc) browser_virtual_connection_init
+		};
+
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (BROWSER_TYPE_CONNECTION, "BrowserVirtualConnection", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+source_cnc_busy_cb (BrowserConnection *bcnc, gboolean is_busy, const gchar *reason,
+		    BrowserConnection *virtual)
+{
+	g_signal_emit_by_name (virtual, "busy", is_busy,
+			       is_busy ? _("Bound connection is used") : NULL);
+}
+
+static gboolean
+is_busy (BrowserConnection *bcnc, gchar **out_reason)
+{
+	GSList *list;
+	if (out_reason)
+		*out_reason = NULL;
+
+	if (! BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs)
+		return FALSE;
+
+	for (list = BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs->parts; list; list = list->next) {
+		BrowserVirtualConnectionPart *part;
+		part = (BrowserVirtualConnectionPart*) list->data;
+		if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
+			BrowserVirtualConnectionCnc *cnc;
+			cnc = &(part->u.cnc);
+			if (browser_connection_is_busy (cnc->source_cnc, out_reason))
+				return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+m_busy (BrowserConnection *bcnc, gboolean is_busy, const gchar *reason)
+{
+	/*
+	 * declare all the source connections as busy
+	 */
+	GSList *list;
+	if (! BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs)
+		return;
+
+	for (list = BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs->parts; list; list = list->next) {
+		BrowserVirtualConnectionPart *part;
+		part = (BrowserVirtualConnectionPart*) list->data;
+		if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
+			BrowserVirtualConnectionCnc *cnc;
+			cnc = &(part->u.cnc);
+			g_signal_handlers_block_by_func (cnc->source_cnc,
+							 G_CALLBACK (source_cnc_busy_cb),
+							 bcnc);
+			g_signal_emit_by_name (cnc->source_cnc, "busy", is_busy,
+					       is_busy ? _("Virtual connection using this connection is busy") : NULL);
+			g_signal_handlers_unblock_by_func (cnc->source_cnc,
+							   G_CALLBACK (source_cnc_busy_cb),
+							   bcnc);			
+		}
+	}
+}
+
+static void
+browser_virtual_connection_class_init (BrowserVirtualConnectionClass * klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	BROWSER_CONNECTION_CLASS (klass)->busy = m_busy;
+	BROWSER_CONNECTION_CLASS (klass)->is_busy = is_busy;
+
+	/* Properties */
+        object_class->set_property = browser_virtual_connection_set_property;
+        object_class->get_property = browser_virtual_connection_get_property;
+	g_object_class_install_property (object_class, PROP_SPECS,
+                                         g_param_spec_pointer ("specs", NULL,
+							       "Specifications as a BrowserVirtualConnectionSpecs pointer",
+							       G_PARAM_READABLE | G_PARAM_WRITABLE |
+							       G_PARAM_CONSTRUCT_ONLY));
+	object_class->dispose = browser_virtual_connection_dispose;
+}
+
+static void
+browser_virtual_connection_init (BrowserVirtualConnection *bcnc)
+{
+	bcnc->priv = g_new0 (BrowserVirtualConnectionPrivate, 1);
+}
+
+static void
+browser_virtual_connection_dispose (GObject *object)
+{
+	BrowserVirtualConnection *bcnc;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (BROWSER_IS_VIRTUAL_CONNECTION (object));
+
+	bcnc = BROWSER_VIRTUAL_CONNECTION (object);
+
+	if (bcnc->priv) {
+		if (bcnc->priv->specs) {
+			GSList *list;
+			for (list = bcnc->priv->specs->parts; list; list = list->next) {
+				BrowserVirtualConnectionPart *part;
+				part = (BrowserVirtualConnectionPart*) list->data;
+				if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
+					BrowserVirtualConnectionCnc *cnc;
+					cnc = &(part->u.cnc);
+					g_signal_handlers_disconnect_by_func (cnc->source_cnc,
+									      G_CALLBACK (source_cnc_busy_cb),
+									      bcnc);
+				}
+			}
+			browser_virtual_connection_specs_free (bcnc->priv->specs);
+		}
+
+		g_free (bcnc->priv);
+		bcnc->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+static void
+browser_virtual_connection_set_property (GObject *object,
+					 guint param_id,
+					 const GValue *value,
+					 GParamSpec *pspec)
+{
+        BrowserVirtualConnection *bcnc;
+
+        bcnc = BROWSER_VIRTUAL_CONNECTION (object);
+        if (bcnc->priv) {
+                switch (param_id) {
+                case PROP_SPECS:
+			bcnc->priv->specs = browser_virtual_connection_specs_copy (g_value_get_pointer (value));
+			GSList *list;
+			for (list = bcnc->priv->specs->parts; list; list = list->next) {
+				BrowserVirtualConnectionPart *part;
+				part = (BrowserVirtualConnectionPart*) list->data;
+				if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
+					BrowserVirtualConnectionCnc *cnc;
+					cnc = &(part->u.cnc);
+					g_signal_connect (cnc->source_cnc, "busy",
+							  G_CALLBACK (source_cnc_busy_cb), bcnc);
+				}
+			}
+
+			break;
+		}
+	}
+}
+
+static void
+browser_virtual_connection_get_property (GObject *object,
+					 guint param_id,
+					 GValue *value,
+					 GParamSpec *pspec)
+{
+        BrowserVirtualConnection *bcnc;
+
+        bcnc = BROWSER_VIRTUAL_CONNECTION (object);
+        if (bcnc->priv) {
+                switch (param_id) {
+                case PROP_SPECS:
+			g_value_set_pointer (value, bcnc->priv->specs);
+			break;
+		}
+	}
+}
+
+
+
+typedef struct {
+        guint cncid;
+        GMainLoop *loop;
+        GError **error;
+	GdaThreadWrapper *wrapper;
+
+        /* out */
+        GdaConnection *cnc;
+} MainloopData;
+
+static gboolean
+check_for_cnc (MainloopData *data)
+{
+        GdaConnection *cnc;
+        GError *lerror = NULL;
+        cnc = gda_thread_wrapper_fetch_result (data->wrapper, FALSE, data->cncid, &lerror);
+        if (cnc || (!cnc && lerror)) {
+                /* waiting is finished! */
+                data->cnc = cnc;
+                if (lerror)
+                        g_propagate_error (data->error, lerror);
+                g_main_loop_quit (data->loop);
+                return FALSE;
+        }
+        return TRUE;
+}
+
+/*
+ * executed in a sub thread
+ */
+static GdaConnection *
+sub_thread_open_cnc (BrowserVirtualConnectionSpecs *specs, GError **error)
+{
+#ifndef DUMMY
+	/* create GDA virtual connection */
+	static GdaVirtualProvider *provider = NULL;
+	GdaConnection *virtual, *cnc;
+	if (!provider)
+		provider = gda_vprovider_hub_new ();
+
+	virtual = gda_virtual_connection_open_extended (provider, GDA_CONNECTION_OPTIONS_THREAD_SAFE, NULL);
+	cnc = g_object_get_data (G_OBJECT (virtual), "gda-virtual-connection");
+
+	/* add parts to connection as specified by @specs */
+	GSList *list;
+	for (list = specs->parts; list; list = list->next) {
+		BrowserVirtualConnectionPart *part;
+		part = (BrowserVirtualConnectionPart*) list->data;
+		switch (part->part_type) {
+		case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
+			BrowserVirtualConnectionModel *pm;
+			pm = &(part->u.model);
+			if (! gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (cnc),
+								    pm->model, pm->table_name, error)) {
+				g_object_unref (virtual);
+				return NULL;
+			}
+			break;
+		}
+		case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
+			BrowserVirtualConnectionCnc *scnc;
+			scnc = &(part->u.cnc);
+			if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (cnc), scnc->source_cnc->priv->cnc,
+						      scnc->table_schema, error)) {
+				g_object_unref (virtual);
+				return NULL;
+			}
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+		}
+	}
+	
+	return virtual;
+#else
+        sleep (5);
+        g_set_error (error, 0, 0, "Timeout!!!");
+        return NULL;
+#endif
+}
+
+/**
+ * browser_virtual_connection_new
+ * @specs: the specifications of the virtual connection's contents
+ * @error: a place to store errors, or %NULL
+ *
+ * Creates a new #BrowserVirtualConnection connection.
+ *
+ * Returns: the new connection
+ */
+BrowserConnection *
+browser_virtual_connection_new (BrowserVirtualConnectionSpecs *specs, GError **error)
+{
+	/* open virtual GdaConnection in sub thread */
+	GdaThreadWrapper *wrapper;
+	guint cncid;
+
+	g_return_val_if_fail (specs, NULL);
+
+	wrapper = gda_thread_wrapper_new ();
+	cncid = gda_thread_wrapper_execute (wrapper,
+                                            (GdaThreadWrapperFunc) sub_thread_open_cnc,
+                                            (gpointer) specs,
+                                            (GDestroyNotify) NULL,
+                                            error);
+        if (cncid == 0)
+                return NULL;
+
+	GMainLoop *loop;
+        guint source_id;
+        MainloopData data;
+	
+        loop = g_main_loop_new (NULL, FALSE);
+	data.wrapper = wrapper;
+	data.cncid = cncid;
+        data.error = error;
+        data.loop = loop;
+        data.cnc = NULL;
+
+        source_id = g_timeout_add (200, (GSourceFunc) check_for_cnc, &data);
+        g_main_loop_run (loop);
+        g_main_loop_unref (loop);
+	g_object_unref (wrapper);
+
+	/* create the BrowserConnection object */
+        if (data.cnc) {
+		BrowserConnection *nbcnc;
+                g_object_set (data.cnc, "monitor-wrapped-in-mainloop", TRUE, NULL);
+		nbcnc = g_object_new (BROWSER_TYPE_VIRTUAL_CONNECTION, "specs", specs,
+				      "gda-connection", data.cnc, NULL);
+		g_object_unref (data.cnc);
+
+		return nbcnc;
+	}
+	else
+		return NULL;
+}
+
+/*
+ * Spec manipulations
+ */
+
+/**
+ * browser_virtual_connection_part_copy
+ */
+BrowserVirtualConnectionPart *
+browser_virtual_connection_part_copy (const BrowserVirtualConnectionPart *part)
+{
+	BrowserVirtualConnectionPart *npart;
+	g_return_val_if_fail (part, NULL);
+
+	npart = g_new0 (BrowserVirtualConnectionPart, 1);
+	npart->part_type = part->part_type;
+
+	switch (part->part_type) {
+	case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
+		const BrowserVirtualConnectionModel *spm;
+		BrowserVirtualConnectionModel *npm;
+		spm = &(part->u.model);
+		npm = &(npart->u.model);
+		if (spm->table_schema)
+			npm->table_schema = g_strdup (spm->table_schema);
+		if (spm->table_name)
+			npm->table_name = g_strdup (spm->table_name);
+		if (spm->model)
+			npm->model = g_object_ref (G_OBJECT (spm->model));
+		break;
+	}
+	case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
+		const BrowserVirtualConnectionCnc *scnc;
+		BrowserVirtualConnectionCnc *ncnc;
+		scnc = &(part->u.cnc);
+		ncnc = &(npart->u.cnc);
+		if (scnc->table_schema)
+			ncnc->table_schema = g_strdup (scnc->table_schema);
+		if (scnc->source_cnc)
+			ncnc->source_cnc = g_object_ref (G_OBJECT (scnc->source_cnc));
+		break;
+	}
+	default:
+		g_assert_not_reached ();
+	}
+
+	return npart;
+}
+
+/**
+ * browser_virtual_connection_part_free
+ */
+void
+browser_virtual_connection_part_free (BrowserVirtualConnectionPart *part)
+{
+	if (!part)
+		return;
+
+	switch (part->part_type) {
+	case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
+		BrowserVirtualConnectionModel *pm;
+		pm = &(part->u.model);
+		g_free (pm->table_schema);
+		g_free (pm->table_name);
+		if (pm->model)
+			g_object_unref (pm->model);
+		break;
+	}
+	case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
+		BrowserVirtualConnectionCnc *cnc;
+		cnc = &(part->u.cnc);
+		g_free (cnc->table_schema);
+		if (cnc->source_cnc)
+			g_object_unref (cnc->source_cnc);
+		break;
+	}
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+/**
+ * browser_virtual_connection_specs_copy
+ */
+BrowserVirtualConnectionSpecs *
+browser_virtual_connection_specs_copy (const BrowserVirtualConnectionSpecs *specs)
+{
+	BrowserVirtualConnectionSpecs *ns;
+	GSList *list;
+
+	g_return_val_if_fail (specs, NULL);
+
+	ns = g_new0 (BrowserVirtualConnectionSpecs, 1);
+	for (list = specs->parts; list; list = list->next) {
+		BrowserVirtualConnectionPart *npart;
+		npart = browser_virtual_connection_part_copy ((BrowserVirtualConnectionPart*) list->data);
+		ns->parts = g_slist_prepend (ns->parts, npart);
+	}
+	ns->parts = g_slist_reverse (ns->parts);
+
+	return ns;
+}
+
+/**
+ * browser_virtual_connection_specs_free
+ */
+void
+browser_virtual_connection_specs_free (BrowserVirtualConnectionSpecs *specs)
+{
+	if (!specs)
+		return;
+	g_slist_foreach (specs->parts, (GFunc) browser_virtual_connection_part_free, NULL);
+	g_slist_free (specs->parts);
+	g_free (specs);
+}
diff --git a/tools/browser/browser-virtual-connection.h b/tools/browser/browser-virtual-connection.h
new file mode 100644
index 0000000..840e1bc
--- /dev/null
+++ b/tools/browser/browser-virtual-connection.h
@@ -0,0 +1,94 @@
+/* 
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __BROWSER_VIRTUAL_CONNECTION_H_
+#define __BROWSER_VIRTUAL_CONNECTION_H_
+
+#include <gtk/gtk.h>
+#include "browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_VIRTUAL_CONNECTION          (browser_virtual_connection_get_type())
+#define BROWSER_VIRTUAL_CONNECTION(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, browser_virtual_connection_get_type(), BrowserVirtualConnection)
+#define BROWSER_VIRTUAL_CONNECTION_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, browser_virtual_connection_get_type (), BrowserVirtualConnectionClass)
+#define BROWSER_IS_VIRTUAL_CONNECTION(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_virtual_connection_get_type ())
+
+typedef struct _BrowserVirtualConnection BrowserVirtualConnection;
+typedef struct _BrowserVirtualConnectionPrivate BrowserVirtualConnectionPrivate;
+typedef struct _BrowserVirtualConnectionClass BrowserVirtualConnectionClass;
+
+/* struct for the object's data */
+struct _BrowserVirtualConnection
+{
+	BrowserConnection                object;
+	BrowserVirtualConnectionPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserVirtualConnectionClass
+{
+	BrowserConnectionClass           parent_class;
+};
+
+/* parts types */
+typedef enum {
+	BROWSER_VIRTUAL_CONNECTION_PART_MODEL,
+	BROWSER_VIRTUAL_CONNECTION_PART_CNC,
+} BrowserVirtualConnectionType;
+
+/* part: a table from a GdaDataModel */
+typedef struct {
+	gchar *table_schema;
+	gchar *table_name;
+	GdaDataModel *model;	
+} BrowserVirtualConnectionModel;
+
+/* part: tables from a BrowserConnection */
+typedef struct {
+	gchar *table_schema;
+	BrowserConnection *source_cnc;
+} BrowserVirtualConnectionCnc;
+
+/* generic part */
+typedef struct {
+	BrowserVirtualConnectionType part_type;
+	union {
+		BrowserVirtualConnectionModel model;
+		BrowserVirtualConnectionCnc   cnc;
+	} u;
+} BrowserVirtualConnectionPart;
+BrowserVirtualConnectionPart *browser_virtual_connection_part_copy (const BrowserVirtualConnectionPart *part);
+void                          browser_virtual_connection_part_free (BrowserVirtualConnectionPart *part);
+
+/* specs */
+typedef struct {
+	GSList *parts;
+} BrowserVirtualConnectionSpecs;
+BrowserVirtualConnectionSpecs *browser_virtual_connection_specs_copy (const BrowserVirtualConnectionSpecs *specs);
+void                           browser_virtual_connection_specs_free (BrowserVirtualConnectionSpecs *specs);
+
+GType              browser_virtual_connection_get_type               (void) G_GNUC_CONST;
+BrowserConnection *browser_virtual_connection_new                    (BrowserVirtualConnectionSpecs *specs,
+								      GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index 108e6c4..fd06c0b 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -29,6 +29,7 @@
 #include "browser-connections-list.h"
 #include "browser-spinner.h"
 #include "browser-stock-icons.h"
+#include "connection-binding-properties.h"
 
 /*
  * structure representing a 'tab' in a window
@@ -192,6 +193,7 @@ static void window_new_cb (GtkAction *action, BrowserWindow *bwin);
 static void window_new_with_cnc_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_close_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_open_cb (GtkAction *action, BrowserWindow *bwin);
+static void connection_bind_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_list_cb (GtkAction *action, BrowserWindow *bwin);
 static void connection_meta_update_cb (GtkAction *action, BrowserWindow *bwin);
 static void perspective_toggle_cb (GtkRadioAction *action, GtkRadioAction *current, BrowserWindow *bwin);
@@ -204,6 +206,9 @@ static const GtkToggleActionEntry ui_toggle_actions [] =
 static const GtkActionEntry ui_actions[] = {
         { "Connection", NULL, "_Connection", NULL, "Connection", NULL },
         { "ConnectionOpen", GTK_STOCK_CONNECT, "_Connect", NULL, "Open a connection", G_CALLBACK (connection_open_cb)},
+        { "ConnectionBind", NULL, N_("_Bind connection"), "<control>B", N_("Use connection to create\n"
+						    "a new binding connection to access data\n"
+						    "from multiple databases at once"), G_CALLBACK (connection_bind_cb)},
         { "ConnectionList", NULL, "_Connections list", NULL, "Connections list", G_CALLBACK (connection_list_cb)},
         { "ConnectionMetaSync", GTK_STOCK_REFRESH, "_Fetch meta data", NULL, "Fetch meta data", G_CALLBACK (connection_meta_update_cb)},
         { "ConnectionClose", GTK_STOCK_CLOSE, "_Close connection", NULL, "Close this connection", G_CALLBACK (connection_close_cb)},
@@ -226,7 +231,9 @@ static const gchar *ui_actions_info =
         "      <menuitem name='ConnectionList' action= 'ConnectionList'/>"
         "      <menuitem name='ConnectionMetaSync' action= 'ConnectionMetaSync'/>"
         "      <separator/>"
+        "      <menuitem name='ConnectionBind' action= 'ConnectionBind'/>"
         "      <menuitem name='ConnectionClose' action= 'ConnectionClose'/>"
+        "      <separator/>"
         "      <menuitem name='Quit' action= 'Quit'/>"
         "      <separator/>"
         "    </menu>"
@@ -771,6 +778,39 @@ connection_open_cb (GtkAction *action, BrowserWindow *bwin)
 }
 
 static void
+connection_bind_cb (GtkAction *action, BrowserWindow *bwin)
+{
+	GtkWidget *win;
+	gint res;
+
+	win = connection_binding_properties_new_create (bwin->priv->bcnc);
+	gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (bwin));
+	gtk_widget_show (win);
+
+	res = gtk_dialog_run (GTK_DIALOG (win));
+	if (res == GTK_RESPONSE_OK) {
+		BrowserConnection *bcnc;
+		GError *error = NULL;
+		bcnc = browser_virtual_connection_new (connection_binding_properties_get_specs
+						       (CONNECTION_BINDING_PROPERTIES (win)), &error);
+		if (bcnc) {
+			BrowserWindow *bwin;
+			bwin = browser_window_new (bcnc, NULL);
+			
+			browser_core_take_window (bwin);
+			browser_core_take_connection (bcnc);
+		}
+		else {
+			browser_show_error ((GtkWindow*) bwin,
+					    _("Could not open binding connection: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+	}
+	gtk_widget_destroy (win);
+}
+
+static void
 connection_list_cb (GtkAction *action, BrowserWindow *bwin)
 {
 	browser_connections_list_show ();
diff --git a/tools/browser/connection-binding-properties.c b/tools/browser/connection-binding-properties.c
new file mode 100644
index 0000000..eaf6b87
--- /dev/null
+++ b/tools/browser/connection-binding-properties.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "connection-binding-properties.h"
+#include "browser-connection.h"
+#include "support.h"
+#include <libgda-ui/libgda-ui.h>
+
+/* 
+ * Main static functions 
+ */
+static void connection_binding_properties_class_init (ConnectionBindingPropertiesClass *klass);
+static void connection_binding_properties_init (ConnectionBindingProperties *stmt);
+static void connection_binding_properties_dispose (GObject *object);
+
+static void create_layout (ConnectionBindingProperties *cprop);
+static void update_display (ConnectionBindingProperties *cprop);
+
+struct _ConnectionBindingPropertiesPrivate
+{
+	BrowserVirtualConnectionSpecs *specs;
+	GtkTable    *layout_table;
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+GType
+connection_binding_properties_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (ConnectionBindingPropertiesClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) connection_binding_properties_class_init,
+			NULL,
+			NULL,
+			sizeof (ConnectionBindingProperties),
+			0,
+			(GInstanceInitFunc) connection_binding_properties_init
+		};
+
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GTK_TYPE_DIALOG, "ConnectionBindingProperties", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+connection_binding_properties_class_init (ConnectionBindingPropertiesClass * klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = connection_binding_properties_dispose;
+}
+
+static void
+connection_binding_properties_init (ConnectionBindingProperties *cprop)
+{
+	cprop->priv = g_new0 (ConnectionBindingPropertiesPrivate, 1);
+}
+
+static void
+connection_binding_properties_dispose (GObject *object)
+{
+	ConnectionBindingProperties *cprop;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (CONNECTION_IS_BINDING_PROPERTIES (object));
+
+	cprop = CONNECTION_BINDING_PROPERTIES (object);
+
+	if (cprop->priv) {
+		if (cprop->priv->specs)
+			browser_virtual_connection_specs_free (cprop->priv->specs);
+		g_free (cprop->priv);
+		cprop->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+/**
+ * connection_binding_properties_new_create
+ * @bcnc: a #BrowserConnection
+ *
+ * Creates a new #ConnectionBindingProperties window. The window will allow a new
+ * virtual connection to be opened using tables from @bcnc.
+ *
+ * Returns: the new object
+ */
+GtkWidget *
+connection_binding_properties_new_create (BrowserConnection *bcnc)
+{
+	ConnectionBindingProperties *cprop;
+	BrowserVirtualConnectionSpecs *specs;
+	BrowserVirtualConnectionPart *part;
+
+	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+	specs = g_new0 (BrowserVirtualConnectionSpecs, 1);
+	part = g_new0 (BrowserVirtualConnectionPart, 1);
+	part->part_type = BROWSER_VIRTUAL_CONNECTION_PART_CNC;
+	part->u.cnc.table_schema = g_strdup (browser_connection_get_name (bcnc));
+	part->u.cnc.source_cnc = g_object_ref (G_OBJECT (bcnc));
+	specs->parts = g_slist_append (NULL, part);
+
+	cprop = CONNECTION_BINDING_PROPERTIES (g_object_new (CONNECTION_TYPE_BINDING_PROPERTIES, NULL));
+	cprop->priv->specs = specs;
+	gtk_window_set_title (GTK_WINDOW (cprop), _("New virtual connection"));
+
+	create_layout (cprop);
+	update_display (cprop);
+
+	gtk_widget_show (gtk_dialog_add_button (GTK_DIALOG (cprop), GTK_STOCK_NEW, GTK_RESPONSE_OK));
+	gtk_widget_show (gtk_dialog_add_button (GTK_DIALOG (cprop), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL));
+
+	return (GtkWidget*) cprop;
+}
+
+static void
+create_layout (ConnectionBindingProperties *cprop)
+{
+	GtkWidget *label, *hbox;
+	gchar *str;
+
+	str = g_strdup_printf ("<b>%s:</b>\n<small>%s</small>",
+			       _("Virtual connection's properties"),
+			       _("Define the sources of data for which tables will\n"
+				 "appear in the virtual connection"));
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	g_free (str);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cprop)->vbox), label, FALSE, FALSE, 5);
+
+	hbox = gtk_hbox_new (FALSE, 0); /* HIG */
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cprop)->vbox), hbox, TRUE, TRUE, 0);
+	label = gtk_label_new ("      ");
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	cprop->priv->layout_table = GTK_TABLE (gtk_table_new (2, 2, FALSE));
+	gtk_box_pack_start (GTK_BOX (hbox), (GtkWidget*) cprop->priv->layout_table, TRUE, TRUE, 0);
+
+	gtk_widget_show_all (GTK_DIALOG (cprop)->vbox);
+}
+
+static void add_part_clicked_cb (GtkWidget *button, ConnectionBindingProperties *cprop);
+static void del_part_clicked_cb (GtkWidget *button, BrowserVirtualConnectionPart *part);
+
+static GtkWidget *create_part_for_model (BrowserVirtualConnectionModel *pm);
+static GtkWidget *create_part_for_cnc (BrowserVirtualConnectionCnc *cnc);
+
+static void
+update_display (ConnectionBindingProperties *cprop)
+{
+	/* clear any previous setting */
+	gtk_container_foreach (GTK_CONTAINER (cprop->priv->layout_table), (GtkCallback) gtk_widget_destroy, NULL);
+
+	/* new contents */
+	gint top = 0;
+	GtkWidget *button, *label;
+	if (cprop->priv->specs) {
+		GSList *list;
+		for (list = cprop->priv->specs->parts; list; list = list->next, top++) {
+			BrowserVirtualConnectionPart *part;
+			GtkWidget *display = NULL;
+
+			part = (BrowserVirtualConnectionPart*) list->data;
+			switch (part->part_type) {
+			case BROWSER_VIRTUAL_CONNECTION_PART_MODEL:
+				display = create_part_for_model (&(part->u.model));
+				break;
+			case BROWSER_VIRTUAL_CONNECTION_PART_CNC:
+				display = create_part_for_cnc (&(part->u.cnc));
+				break;
+			default:
+				g_assert_not_reached ();
+			}
+
+			gtk_table_attach (cprop->priv->layout_table, display, 0, 1, top, top + 1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+			button = gtk_button_new ();
+			label = browser_make_tab_label_with_stock (NULL, GTK_STOCK_REMOVE, FALSE, NULL);
+			gtk_container_add (GTK_CONTAINER (button), label);
+			gtk_table_attach (cprop->priv->layout_table, button, 1, 2, top, top + 1, 0, GTK_FILL, 0, 0);
+
+			g_signal_connect (button, "clicked",
+					  G_CALLBACK (del_part_clicked_cb), part);
+		}
+	}
+
+	/* bottom button to add a part */
+	button = gtk_button_new ();
+	label = browser_make_tab_label_with_stock (_("Add part"), GTK_STOCK_ADD, FALSE, NULL);
+	gtk_container_add (GTK_CONTAINER (button), label);
+	gtk_table_attach (cprop->priv->layout_table, button, 0, 2, top, top + 1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (add_part_clicked_cb), cprop);
+	
+	gtk_widget_show_all ((GtkWidget*) cprop->priv->layout_table);
+}
+
+static void
+add_part_clicked_cb (GtkWidget *button, ConnectionBindingProperties *cprop)
+{
+	TO_IMPLEMENT;
+}
+
+static void
+del_part_clicked_cb (GtkWidget *button, BrowserVirtualConnectionPart *part)
+{
+	TO_IMPLEMENT;
+}
+
+static GtkWidget *
+create_part_for_model (BrowserVirtualConnectionModel *pm)
+{
+	GtkWidget *vbox, *label;
+	gchar *str;
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	label = gtk_label_new ("");
+	str = g_markup_printf_escaped ("<b>%s</b>", _("Table from a data set:"));
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	g_free (str);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	GdaSet *set;
+	GdaHolder *holder;
+	GtkWidget *form;
+	set = gda_set_new_inline (2,
+				  "SCHEMA", G_TYPE_STRING, pm->table_schema,
+				  "NAME", G_TYPE_STRING, pm->table_name);
+
+	holder = gda_holder_new (G_TYPE_POINTER);
+	g_object_set (holder, "id", "DATASET", "name", "Data set", NULL);
+	g_assert (gda_set_add_holder (set, holder));
+	g_object_unref (holder);
+
+	g_object_set (gda_set_get_holder (set, "SCHEMA"), "name", "Table's schema", NULL);
+	g_object_set (gda_set_get_holder (set, "NAME"), "name", "Table's name", NULL);
+							  
+	form = gdaui_basic_form_new (set);
+	g_object_unref (set);
+	gtk_box_pack_start (GTK_BOX (vbox), form, TRUE, TRUE, 0);
+
+	return vbox;
+}
+
+static GError *
+part_for_cnc_validate_holder_change_cb (GdaSet *set, GdaHolder *holder, GValue *new_value,
+					BrowserVirtualConnectionCnc *cnc)
+{
+	const gchar *hid;
+
+	hid = gda_holder_get_id (holder);
+	g_assert (hid);
+
+	if (!strcmp (hid, "SCHEMA")) {
+		const gchar *str, *ptr;
+		str = g_value_get_string (new_value);
+		for (ptr = str; *ptr; ptr++) {
+			if (((ptr == str) && ! g_ascii_isalpha (*ptr)) ||
+			    (*ptr != '_') ||
+			    ! g_ascii_isalnum (*ptr)) {
+				GError *error = NULL;
+				g_set_error (&error, 0, 0,
+					     _("Invalid schema name"));
+				return error;
+			}
+		}
+	}
+
+	/* no error */
+	return NULL;
+}
+
+static void
+part_for_cnc_holder_changed_cb (GdaSet *set, GdaHolder *holder, BrowserVirtualConnectionCnc *cnc)
+{
+	const gchar *hid;
+	const GValue *value;
+
+	hid = gda_holder_get_id (holder);
+	g_assert (hid);
+	value = gda_holder_get_value (holder);
+
+	if (!strcmp (hid, "SCHEMA")) {
+		g_free (cnc->table_schema);
+		cnc->table_schema = g_value_dup_string (value);
+	}
+	else if (!strcmp (hid, "CNC")) {
+		if (cnc->source_cnc)
+			g_object_unref (cnc->source_cnc);
+		cnc->source_cnc = g_value_get_object (value);
+		if (cnc->source_cnc)
+			g_object_ref (cnc->source_cnc);
+	}
+	else
+		g_assert_not_reached ();
+}
+
+static GtkWidget *
+create_part_for_cnc (BrowserVirtualConnectionCnc *cnc)
+{
+	GtkWidget *vbox, *label;
+	gchar *str;
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	label = gtk_label_new ("");
+	str = g_markup_printf_escaped ("<b>%s</b>", _("All tables of a connection:"));
+	gtk_label_set_markup (GTK_LABEL (label), str);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	g_free (str);
+	gtk_widget_set_tooltip_text (label, _("Each table in the selected connection will appear\n"
+					      "as a table in the virtual connection"));
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	GdaSet *set;
+	GdaHolder *holder;
+	GtkWidget *form;
+	GValue *value;
+	set = gda_set_new_inline (1,
+				  "SCHEMA", G_TYPE_STRING, cnc->table_schema);
+
+	holder = gda_holder_new (BROWSER_TYPE_CONNECTION);
+	g_object_set (holder, "id", "CNC", "name", "Connection", "not-null", TRUE, NULL);
+	g_assert (gda_set_add_holder (set, holder));
+
+	g_value_set_object ((value = gda_value_new (BROWSER_TYPE_CONNECTION)), cnc->source_cnc);
+	g_assert (gda_holder_set_value (holder, value, NULL));
+	gda_value_free (value);
+
+	g_assert (gda_holder_set_source_model (holder, browser_get_connections_list (),
+					       CNC_LIST_COLUMN_BCNC, NULL));
+	g_object_unref (holder);
+
+	holder = gda_set_get_holder (set, "SCHEMA");
+	g_object_set (holder, "name", "Table's schema",
+		      "description", _("Name of the schema the\ntables will be in"), NULL);
+							  
+	form = gdaui_basic_form_new (set);
+	g_signal_connect (set, "validate-holder-change",
+			  G_CALLBACK (part_for_cnc_validate_holder_change_cb), cnc);
+	g_signal_connect (set, "holder-changed",
+			  G_CALLBACK (part_for_cnc_holder_changed_cb), cnc);
+
+	g_object_unref (set);
+	gtk_box_pack_start (GTK_BOX (vbox), form, TRUE, TRUE, 0);
+
+	return vbox;
+}
+
+/**
+ * connection_binding_properties_get_specs
+ * @prop: a #ConnectionBindingProperties widget
+ *
+ * Returns: a pointer to a read only #BrowserVirtualConnectionSpecs
+ */
+const BrowserVirtualConnectionSpecs *
+connection_binding_properties_get_specs (ConnectionBindingProperties *prop)
+{
+	g_return_val_if_fail (CONNECTION_IS_BINDING_PROPERTIES (prop), NULL);
+
+	return prop->priv->specs;
+}
diff --git a/tools/browser/connection-binding-properties.h b/tools/browser/connection-binding-properties.h
new file mode 100644
index 0000000..110d4db
--- /dev/null
+++ b/tools/browser/connection-binding-properties.h
@@ -0,0 +1,61 @@
+/* 
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __CONNECTION_BINDING_PROPERTIES_H_
+#define __CONNECTION_BINDING_PROPERTIES_H_
+
+#include <gtk/gtk.h>
+#include "browser-virtual-connection.h"
+#include "decl.h"
+
+G_BEGIN_DECLS
+
+#define CONNECTION_TYPE_BINDING_PROPERTIES          (connection_binding_properties_get_type())
+#define CONNECTION_BINDING_PROPERTIES(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, connection_binding_properties_get_type(), ConnectionBindingProperties)
+#define CONNECTION_BINDING_PROPERTIES_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, connection_binding_properties_get_type (), ConnectionBindingPropertiesClass)
+#define CONNECTION_IS_BINDING_PROPERTIES(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, connection_binding_properties_get_type ())
+
+typedef struct _ConnectionBindingProperties ConnectionBindingProperties;
+typedef struct _ConnectionBindingPropertiesPrivate ConnectionBindingPropertiesPrivate;
+typedef struct _ConnectionBindingPropertiesClass ConnectionBindingPropertiesClass;
+
+/* struct for the object's data */
+struct _ConnectionBindingProperties
+{
+	GtkDialog                           object;
+	ConnectionBindingPropertiesPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _ConnectionBindingPropertiesClass
+{
+	GtkDialogClass                      parent_class;
+};
+
+GType           connection_binding_properties_get_type               (void) G_GNUC_CONST;
+GtkWidget      *connection_binding_properties_new_create             (BrowserConnection *bcnc);
+
+GtkWidget      *connection_binding_properties_new_edit               (const BrowserVirtualConnectionSpecs *specs);
+
+const BrowserVirtualConnectionSpecs *connection_binding_properties_get_specs (ConnectionBindingProperties *prop);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/support.c b/tools/browser/support.c
index 7e916af..4466734 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -355,3 +355,73 @@ browser_find_parent_widget (GtkWidget *current, GType requested_type)
 	}
 	return NULL;
 }
+
+static void connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, GdaDataModel *model);
+static void connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, GdaDataModel *model);
+
+/**
+ * browser_get_connections_list
+ *
+ * Creates a unique instance of tree model listing all the connections, and returns
+ * a pointer to it. The object will always exist after it has been created, so no need
+ * to reference it.
+ *
+ * Returns: a pointer to the #GtkTreeModel, the caller must not assume it has a reference to it.
+ */
+GdaDataModel *
+browser_get_connections_list (void)
+{
+	static GdaDataModel *model = NULL;
+	if (!model) {
+		model = gda_data_model_array_new_with_g_types (CNC_LIST_NUM_COLUMNS,
+							       BROWSER_TYPE_CONNECTION,
+							       G_TYPE_STRING);
+		/* initial filling */
+		GSList *connections, *list;
+		connections =  browser_core_get_connections ();
+		for (list = connections; list; list = list->next)
+			connection_added_cb (browser_core_get(), BROWSER_CONNECTION (list->data),
+					     model);
+		g_slist_free (connections);
+
+		g_signal_connect (browser_core_get (), "connection-added",
+				  G_CALLBACK (connection_added_cb), model);
+		g_signal_connect (browser_core_get (), "connection-removed",
+				  G_CALLBACK (connection_removed_cb), model);
+	}
+
+	return model;
+}
+
+static void
+connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, GdaDataModel *model)
+{
+	GList *values;
+	GValue *value;
+	
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), browser_connection_get_name (bcnc));
+	values = g_list_prepend (NULL, value);
+	g_value_set_object ((value = gda_value_new (BROWSER_TYPE_CONNECTION)), bcnc);
+	values = g_list_prepend (values, value);
+
+	g_assert (gda_data_model_append_values (model, values, NULL) >= 0);
+
+	g_list_foreach (values, (GFunc) gda_value_free, NULL);
+	g_list_free (values);
+}
+
+static void
+connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, GdaDataModel *model)
+{
+	gint i, nrows;
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *value;
+		value = gda_data_model_get_value_at (model, 0, i, NULL);
+		g_assert (value);
+		if (g_value_get_object (value) == bcnc) {
+			g_assert (gda_data_model_remove_row (model, i, NULL));
+			break;
+		}
+	}
+}
diff --git a/tools/browser/support.h b/tools/browser/support.h
index aa13d91..8409d97 100644
--- a/tools/browser/support.h
+++ b/tools/browser/support.h
@@ -66,6 +66,17 @@ typedef enum {
 
 GdkPixbuf         *browser_get_pixbuf_icon (BrowserIconType type);
 
+/*
+ * Connections list
+ */
+enum
+{
+	CNC_LIST_COLUMN_BCNC = 0,
+	CNC_LIST_COLUMN_NAME = 1,
+	CNC_LIST_NUM_COLUMNS
+};
+GdaDataModel *browser_get_connections_list (void);
+
 G_END_DECLS
 
 #endif



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