[libgda] Initial support for canvas-based relations view in the Browser
- From: Vivien Malerba <vivien src gnome org>
- To: svn-commits-list gnome org
- Subject: [libgda] Initial support for canvas-based relations view in the Browser
- Date: Tue, 23 Jun 2009 15:21:02 -0400 (EDT)
commit 1e30677c5b134aa3f9bc630132bb00dae37e5881
Author: Vivien Malerba <malerba gnome-db org>
Date: Tue Jun 23 21:17:22 2009 +0200
Initial support for canvas-based relations view in the Browser
Merged Libgnomedb's canvas into the gda-browser tool to display
tables' relations using a canvas from the GooCanvas library
configure.in | 1 +
tools/browser/.gitignore | 1 +
tools/browser/Makefile.am | 24 +
tools/browser/browser-core.c | 31 +-
tools/browser/browser-core.h | 1 +
tools/browser/browser-window.c | 17 +-
tools/browser/canvas-example.c | 176 ++++
tools/browser/canvas/Makefile.am | 37 +
tools/browser/canvas/browser-canvas-column.c | 307 +++++++
tools/browser/canvas/browser-canvas-column.h | 65 ++
tools/browser/canvas/browser-canvas-db-relations.c | 476 ++++++++++
tools/browser/canvas/browser-canvas-db-relations.h | 66 ++
tools/browser/canvas/browser-canvas-decl.h | 46 +
tools/browser/canvas/browser-canvas-fkey.c | 477 ++++++++++
tools/browser/canvas/browser-canvas-fkey.h | 64 ++
tools/browser/canvas/browser-canvas-item.c | 405 +++++++++
tools/browser/canvas/browser-canvas-item.h | 69 ++
tools/browser/canvas/browser-canvas-print.c | 456 ++++++++++
tools/browser/canvas/browser-canvas-print.h | 27 +
tools/browser/canvas/browser-canvas-priv.h | 36 +
tools/browser/canvas/browser-canvas-table.c | 500 +++++++++++
tools/browser/canvas/browser-canvas-table.h | 66 ++
tools/browser/canvas/browser-canvas-text.c | 527 +++++++++++
tools/browser/canvas/browser-canvas-text.h | 71 ++
tools/browser/canvas/browser-canvas-utility.c | 836 ++++++++++++++++++
tools/browser/canvas/browser-canvas-utility.h | 51 ++
tools/browser/canvas/browser-canvas.c | 934 ++++++++++++++++++++
tools/browser/canvas/browser-canvas.h | 79 ++
tools/browser/main.c | 23 +
tools/browser/schema-browser/Makefile.am | 6 +
tools/browser/schema-browser/favorite-selector.c | 3 +-
tools/browser/schema-browser/table-columns.c | 2 -
tools/browser/schema-browser/table-info.c | 16 +
tools/browser/schema-browser/table-relations.c | 200 +++++
tools/browser/schema-browser/table-relations.h | 56 ++
35 files changed, 6138 insertions(+), 14 deletions(-)
---
diff --git a/configure.in b/configure.in
index 2df2bf2..3c5fa35 100644
--- a/configure.in
+++ b/configure.in
@@ -1702,6 +1702,7 @@ tools/gda-sql-4.0.1:tools/gda-sql.1.in
tools/browser/Makefile
tools/browser/schema-browser/Makefile
tools/browser/dummy-perspective/Makefile
+tools/browser/canvas/Makefile
tools/binreloc/Makefile
testing/Makefile
tests/Makefile
diff --git a/tools/browser/.gitignore b/tools/browser/.gitignore
index d6da0c2..b6f4891 100644
--- a/tools/browser/.gitignore
+++ b/tools/browser/.gitignore
@@ -1,4 +1,5 @@
gda-browser-4.*
gda-browser-4.*.desktop
+canvas-example
marshal.c
marshal.h
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index df839ff..92d93db 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -1,6 +1,11 @@
bin_PROGRAMS=gda-browser-4.0
+noinst_PROGRAMS =
SUBDIRS = schema-browser dummy-perspective
+if HAVE_GOOCANVAS
+SUBDIRS+=canvas
+noinst_PROGRAMS+=canvas-example
+endif
AM_CPPFLAGS = \
-I$(top_srcdir) \
@@ -73,12 +78,31 @@ gda_browser_4_0_LDADD=\
$(top_builddir)/libgdaui/libgdaui-4.0.la \
$(LIBGDA_LIBS) $(GTK_LIBS)
+if HAVE_GOOCANVAS
+gda_browser_4_0_LDADD+= \
+ $(top_builddir)/tools/browser/canvas/libcanvas.la
+endif
+
@INTLTOOL_DESKTOP_RULE@
desktopdir=$(datadir)/applications
Desktop_in_files = gda-browser-4.0.desktop.in
desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop)
+# canvas example
+canvas_example_DEPENDENCIES = \
+ canvas/libcanvas.la
+canvas_example_SOURCES = \
+ canvas-example.c \
+ dnd.c \
+ dnd.h
+canvas_example_CFLAGS = -DCANVAS_EXAMPLE
+canvas_example_LDFLAGS = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(top_builddir)/libgdaui/libgdaui-4.0.la \
+ canvas/libcanvas.la
+
+
# icons
iconsdir=$(datadir)/pixmaps
icons_DATA= \
diff --git a/tools/browser/browser-core.c b/tools/browser/browser-core.c
index 5dff518..46dcd22 100644
--- a/tools/browser/browser-core.c
+++ b/tools/browser/browser-core.c
@@ -119,9 +119,13 @@ browser_core_init (BrowserCore *bcore)
bcore->priv = g_new0 (BrowserCorePrivate, 1);
bcore->priv->factories = NULL;
- bcore->priv->factories = g_slist_append (bcore->priv->factories, schema_browser_perspective_get_factory ());
- bcore->priv->default_factory = (BrowserPerspectiveFactory*) bcore->priv->factories->data; /* set default perspective */
- bcore->priv->factories = g_slist_append (bcore->priv->factories, dummy_perspective_get_factory ());
+ bcore->priv->factories = g_slist_append (bcore->priv->factories,
+ schema_browser_perspective_get_factory ());
+ /* set default perspective */
+ bcore->priv->default_factory = (BrowserPerspectiveFactory*) bcore->priv->factories->data;
+
+ bcore->priv->factories = g_slist_append (bcore->priv->factories,
+ dummy_perspective_get_factory ());
bcore->priv->windows = NULL;
}
@@ -296,6 +300,27 @@ browser_core_get_default_factory (void)
}
/**
+ * browser_core_set_default_factory
+ */
+void
+browser_core_set_default_factory (const gchar *factory)
+{
+ GSList *list;
+ _bcore = browser_core_get ();
+
+ if (!factory)
+ return;
+
+ for (list = _bcore->priv->factories; list; list = list->next) {
+ BrowserPerspectiveFactory *fact = (BrowserPerspectiveFactory*) list->data;
+ if (strstr (fact->perspective_name, factory)) {
+ _bcore->priv->default_factory = fact;
+ break;
+ }
+ }
+}
+
+/**
* browser_core_get_factories
*
* Get a list of all the known Perspective factories
diff --git a/tools/browser/browser-core.h b/tools/browser/browser-core.h
index dcdac1d..0378c66 100644
--- a/tools/browser/browser-core.h
+++ b/tools/browser/browser-core.h
@@ -66,6 +66,7 @@ void browser_core_close_connection (BrowserConnection *bcnc);
void browser_core_quit (void);
BrowserPerspectiveFactory *browser_core_get_default_factory (void);
+void browser_core_set_default_factory (const gchar *factory);
const GSList *browser_core_get_factories (void);
G_END_DECLS
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index 79b7f9b..d740067 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -374,19 +374,18 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, agroup, 0);
g_object_unref (agroup);
+ GtkAction *active_action = NULL;
for (plist = browser_core_get_factories (); plist; plist = plist->next) {
GtkAction *action;
const gchar *name;
- gboolean active;
- if ((factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == factory)) ||
- (!factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == browser_core_get_default_factory ())))
- active = TRUE;
- else
- active = FALSE;
name = BROWSER_PERSPECTIVE_FACTORY (plist->data)->perspective_name;
- action = GTK_ACTION (gtk_radio_action_new (name, name, NULL, NULL, active));
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+ action = GTK_ACTION (gtk_radio_action_new (name, name, NULL, NULL, FALSE));
+
+ if (!active_action &&
+ (factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == factory)) ||
+ (!factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == browser_core_get_default_factory ())))
+ active_action = action;
gtk_action_group_add_action (agroup, action);
gtk_radio_action_set_group (GTK_RADIO_ACTION (action), radio_group);
@@ -402,6 +401,8 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
name, name,
GTK_UI_MANAGER_AUTO, FALSE);
}
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (active_action), TRUE);
gtk_widget_show (GTK_WIDGET (bwin));
diff --git a/tools/browser/canvas-example.c b/tools/browser/canvas-example.c
new file mode 100644
index 0000000..a2660ce
--- /dev/null
+++ b/tools/browser/canvas-example.c
@@ -0,0 +1,176 @@
+#include <libgda/libgda.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include <libgdaui/libgdaui.h>
+#include "canvas/browser-canvas-db-relations.h"
+#include "dnd.h"
+
+static gboolean on_delete_event (GtkWidget *window, GdkEvent *event, gpointer unused_data);
+static void auto_layout_cb (GtkWidget *button, BrowserCanvas *canvas);
+static void label_drag_data_received (GtkWidget *label, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time);
+
+int
+main (int argc, char *argv[])
+{
+ GdaConnection* connection;
+ GError* error = NULL;
+
+ gdaui_init ();
+ gtk_init (&argc, &argv);
+
+ /* open connection to the SalesTest data source */
+ connection = gda_connection_open_from_dsn ("SalesTest", NULL, GDA_CONNECTION_OPTIONS_NONE, &error);
+ if (!connection) {
+ fprintf (stderr, "%s\n", error->message);
+ return -1;
+ }
+
+ /* mate store update */
+ g_print ("Metastore update...\n");
+ if (!gda_connection_update_meta_store (connection, NULL, &error))
+ return -1;
+
+ /* GdaMetaStruct */
+ GdaMetaStruct *mstruct;
+ mstruct = gda_meta_struct_new (gda_connection_get_meta_store (connection),
+ GDA_META_STRUCT_FEATURE_ALL);
+
+ /* UI Part */
+ GtkWidget *window, *table, *canvas;
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
+ g_signal_connect (window, "delete_event", G_CALLBACK (on_delete_event),
+ NULL);
+
+ table = gtk_table_new (3, 1, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 15);
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ canvas = browser_canvas_db_relations_new (mstruct);
+ g_object_unref (mstruct);
+
+ gtk_table_attach_defaults (GTK_TABLE (table), canvas,
+ 0, 1, 0, 1);
+
+ GtkWidget *bbox, *button;
+ bbox = gtk_hbutton_box_new ();
+ gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2, 0, 0, 0, 0);
+ button = gtk_button_new_with_label ("Auto layout");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (auto_layout_cb), canvas);
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+
+ GtkWidget *wid;
+ GtkTargetEntry dbo_table[] = {
+ { "text/plain", 0, 0 },
+ { "key-value", 0, 1 },
+ { "application/x-rootwindow-drop", 0, 2 }
+ };
+ wid = gtk_label_new ("\nDROP ZONE\n(hold SHIFT to drag and drop)\n");
+ gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 2, 3, 0, 0, 0, 0);
+ gtk_drag_dest_set (wid,
+ GTK_DEST_DEFAULT_ALL,
+ dbo_table, G_N_ELEMENTS (dbo_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (wid, "drag_data_received",
+ G_CALLBACK (label_drag_data_received), NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 450);
+ gtk_widget_show_all (window);
+
+ /* add some tables */
+ GValue *tname;
+ g_value_set_string ((tname = gda_value_new (G_TYPE_STRING)), "customers");
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (canvas), NULL, NULL, tname);
+ g_value_set_string (tname, "orders");
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (canvas), NULL, NULL, tname);
+ g_value_set_string (tname, "order_contents");
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (canvas), NULL, NULL, tname);
+ g_value_set_string (tname, "products");
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (canvas), NULL, NULL, tname);
+ g_value_set_string (tname, "locations");
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (canvas), NULL, NULL, tname);
+ gda_value_free (tname);
+
+ /* Pass control to the GTK+ main event loop. */
+ gtk_main ();
+
+ return 0;
+}
+
+static void
+auto_layout_cb (GtkWidget *button, BrowserCanvas *canvas)
+{
+ browser_canvas_perform_auto_layout (BROWSER_CANVAS (canvas), TRUE, BROWSER_CANVAS_LAYOUT_RADIAL);
+}
+
+static void
+label_drag_data_received (GtkWidget *label, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time)
+{
+ if ((data->length >= 0) && (data->format == 8)) {
+ g_print ("Received \"%s\" in drop zone\n", (gchar *)data->data);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+ return;
+ }
+
+ gtk_drag_finish (context, FALSE, FALSE, time);
+}
+
+static gboolean
+on_delete_event (GtkWidget *window, GdkEvent *event, gpointer unused_data)
+{
+ exit (0);
+}
+
+/*
+ * icons
+ */
+typedef enum {
+ BROWSER_ICON_BOOKMARK,
+ BROWSER_ICON_SCHEMA,
+ BROWSER_ICON_TABLE,
+ BROWSER_ICON_COLUMN,
+ BROWSER_ICON_COLUMN_PK,
+ BROWSER_ICON_COLUMN_FK,
+ BROWSER_ICON_COLUMN_FK_NN,
+ BROWSER_ICON_COLUMN_NN,
+ BROWSER_ICON_REFERENCE,
+
+ BROWSER_ICON_LAST
+} BrowserIconType;
+
+GdkPixbuf *
+browser_get_pixbuf_icon (BrowserIconType type)
+{
+ static GdkPixbuf **array = NULL;
+ static const gchar* names[] = {
+ "gda-browser-bookmark.png",
+ "gda-browser-schema.png",
+ "gda-browser-table.png",
+ "gda-browser-column.png",
+ "gda-browser-column-pk.png",
+ "gda-browser-column-fk.png",
+ "gda-browser-column-fknn.png",
+ "gda-browser-column-nn.png",
+ "gda-browser-reference.png"
+ };
+
+ if (!array)
+ array = g_new0 (GdkPixbuf *, BROWSER_ICON_LAST);
+ if (!array [type]) {
+ gchar *path;
+ path = gda_gbr_get_file_path (GDA_DATA_DIR, "pixmaps", names[type], NULL);
+ array [type] = gdk_pixbuf_new_from_file (path, NULL);
+ g_free (path);
+
+ if (!array [type])
+ array [type] = (GdkPixbuf*) 0x01;
+ }
+ if (array [type] == (GdkPixbuf*) 0x01)
+ return NULL;
+ else
+ return array [type];
+}
diff --git a/tools/browser/canvas/Makefile.am b/tools/browser/canvas/Makefile.am
new file mode 100644
index 0000000..971ace4
--- /dev/null
+++ b/tools/browser/canvas/Makefile.am
@@ -0,0 +1,37 @@
+noinst_LTLIBRARIES = libcanvas.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/tools/browser \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ $(LIBGDA_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GOOCANVAS_CFLAGS) \
+ $(GRAPHVIZ_CFLAGS)
+
+libcanvas_la_SOURCES = \
+ browser-canvas.c \
+ browser-canvas.h \
+ browser-canvas-column.c \
+ browser-canvas-column.h \
+ browser-canvas-db-relations.c \
+ browser-canvas-db-relations.h \
+ browser-canvas-decl.h \
+ browser-canvas-fkey.c \
+ browser-canvas-fkey.h \
+ browser-canvas-item.c \
+ browser-canvas-item.h \
+ browser-canvas-print.c \
+ browser-canvas-print.h \
+ browser-canvas-table.c \
+ browser-canvas-table.h \
+ browser-canvas-text.c \
+ browser-canvas-text.h \
+ browser-canvas-utility.c \
+ browser-canvas-utility.h
+
+
+libcanvas_la_LDFLAGS = \
+ $(GOOCANVAS_LIBS) \
+ $(GRAPHVIZ_LIBS)
diff --git a/tools/browser/canvas/browser-canvas-column.c b/tools/browser/canvas/browser-canvas-column.c
new file mode 100644
index 0000000..47dc979
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-column.c
@@ -0,0 +1,307 @@
+/* browser-canvas-column.c
+ *
+ * Copyright (C) 2002 - 2008 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <libgda/libgda.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "browser-canvas.h"
+#include "browser-canvas-column.h"
+#include "browser-canvas-table.h"
+
+static void browser_canvas_column_class_init (BrowserCanvasColumnClass * class);
+static void browser_canvas_column_init (BrowserCanvasColumn * drag);
+static void browser_canvas_column_dispose (GObject *object);
+
+static void browser_canvas_column_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_column_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void browser_canvas_column_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info, guint time);
+
+static void browser_canvas_column_extra_event (BrowserCanvasItem * citem, GdkEventType event_type);
+enum
+{
+ PROP_0,
+ PROP_COLUMN,
+};
+
+struct _BrowserCanvasColumnPrivate
+{
+ GdaMetaTableColumn *column;
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *column_parent_class = NULL;
+
+GType
+browser_canvas_column_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasColumnClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_column_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasColumn),
+ 0,
+ (GInstanceInitFunc) browser_canvas_column_init
+ };
+
+ type = g_type_register_static (TYPE_BROWSER_CANVAS_TEXT, "BrowserCanvasColumn", &info, 0);
+ }
+
+ return type;
+}
+
+
+
+static void
+browser_canvas_column_class_init (BrowserCanvasColumnClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (klass);
+
+ column_parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = browser_canvas_column_dispose;
+
+ iclass->drag_data_get = browser_canvas_column_drag_data_get;
+ iclass->extra_event = browser_canvas_column_extra_event;
+
+ /* Properties */
+ object_class->set_property = browser_canvas_column_set_property;
+ object_class->get_property = browser_canvas_column_get_property;
+
+ g_object_class_install_property
+ (object_class, PROP_COLUMN,
+ g_param_spec_pointer ("column", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+browser_canvas_column_init (BrowserCanvasColumn * column)
+{
+ column->priv = g_new0 (BrowserCanvasColumnPrivate, 1);
+ column->priv->column = NULL;
+}
+
+static void
+browser_canvas_column_dispose (GObject * object)
+{
+ BrowserCanvasColumn *cf;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_COLUMN (object));
+
+ cf = BROWSER_CANVAS_COLUMN (object);
+ if (cf->priv) {
+ if (cf->priv->column)
+ cf->priv->column = NULL;
+ g_free (cf->priv);
+ cf->priv = NULL;
+ }
+
+ /* for the parent class */
+ column_parent_class->dispose (object);
+}
+
+static void
+browser_canvas_column_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasColumn *cf = NULL;
+ GdaMetaTableColumn* column = NULL;
+ GString *string = NULL;
+
+ cf = BROWSER_CANVAS_COLUMN (object);
+
+ switch (param_id) {
+ case PROP_COLUMN:
+ column = g_value_get_pointer (value);
+
+ cf->priv->column = column;
+ /* column name */
+ g_object_set (object, "text", column->column_name, NULL);
+
+ /* attributes setting */
+ string = g_string_new ("");
+ if (column->column_type)
+ g_string_append_printf (string, _("Type: %s"), column->column_type);
+
+ g_object_set (object,
+ "highlight_color", BROWSER_CANVAS_DB_TABLE_COLOR,
+ "text_underline", !column->nullok,
+ "text_bold", column->pkey,
+ NULL);
+
+ if (*string->str)
+ g_object_set (object, "tip-text", string->str, NULL);
+ else
+ g_object_set (object, "tip-text", NULL, NULL);
+ g_string_free (string, TRUE);
+ break;
+ }
+}
+
+static void
+browser_canvas_column_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasColumn *cf;
+
+ cf = BROWSER_CANVAS_COLUMN (object);
+
+ switch (param_id) {
+ case PROP_COLUMN:
+ g_value_set_pointer (value, cf->priv->column);
+ break;
+ }
+}
+
+static void
+browser_canvas_column_extra_event (BrowserCanvasItem *citem, GdkEventType event_type)
+{
+ if (event_type == GDK_LEAVE_NOTIFY)
+ browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (citem), FALSE);
+}
+
+/**
+ * browser_canvas_column_new
+ * @parent: the parent item, or %NULL
+ * @column: the represented entity's column
+ * @x: the x coordinate of the text
+ * @y: the y coordinate of the text
+ * @...: optional pairs of property names and values, and a terminating %NULL.
+ *
+ * Creates a new #BrowserCanvasColumn object
+ */
+GooCanvasItem*
+browser_canvas_column_new (GooCanvasItem *parent, GdaMetaTableColumn *column,
+ gdouble x, gdouble y, ...)
+{
+ GooCanvasItem *item;
+ BrowserCanvasColumn *goocolumn;
+ const char *first_property;
+ va_list var_args;
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_COLUMN, NULL);
+ goocolumn = (BrowserCanvasColumn*) item;
+
+ if (parent) {
+ goo_canvas_item_add_child (parent, item, -1);
+ g_object_unref (item);
+ }
+
+ va_start (var_args, y);
+ first_property = va_arg (var_args, char*);
+ if (first_property)
+ g_object_set_valist ((GObject*) item, first_property, var_args);
+ va_end (var_args);
+
+ g_object_set (G_OBJECT (item), "column", column, NULL);
+ goo_canvas_item_translate (item, x, y);
+
+ return item;
+}
+
+
+/**
+ * browser_canvas_column_get_column
+ * @column: a #BrowserCanvasColumn object
+ *
+ * Get the #GdaMetaTableColumn which @column represents
+ *
+ * Returns: the object implementing the #GdaMetaTableColumn interface
+ */
+GdaMetaTableColumn *
+browser_canvas_column_get_column (BrowserCanvasColumn *column)
+{
+ g_return_val_if_fail (IS_BROWSER_CANVAS_COLUMN (column), NULL);
+ g_return_val_if_fail (column->priv, NULL);
+
+ return column->priv->column;
+}
+
+
+/**
+ * browser_canvas_column_get_parent_item
+ * @column: a #BrowserCanvasColumn object
+ *
+ * Get the #BrowserCanvasTable in which @column is
+ *
+ * Returns: the #BrowserCanvasTable in which @column is, or %NULL
+ */
+BrowserCanvasTable *
+browser_canvas_column_get_parent_item (BrowserCanvasColumn *column)
+{
+ GooCanvasItem *ci;
+
+ g_return_val_if_fail (IS_BROWSER_CANVAS_COLUMN (column), NULL);
+ for (ci = goo_canvas_item_get_parent (GOO_CANVAS_ITEM (column));
+ ci && !IS_BROWSER_CANVAS_TABLE (ci);
+ ci = goo_canvas_item_get_parent (ci));
+
+ return (BrowserCanvasTable *) ci;
+}
+
+static void
+browser_canvas_column_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info, guint time)
+{
+ BrowserCanvasColumn *column;
+ BrowserCanvasTable *ctable;
+ GdaMetaTable *mtable;
+
+ column = BROWSER_CANVAS_COLUMN (citem);
+ ctable = browser_canvas_column_get_parent_item (column);
+ g_object_get (G_OBJECT (ctable), "table", &mtable, NULL);
+ if (!column->priv->column || !mtable)
+ return;
+
+ GdaMetaDbObject *dbo;
+ gchar *str, *tmp1, *tmp2, *tmp3, *tmp4;
+
+ dbo = GDA_META_DB_OBJECT (mtable);
+ tmp1 = gda_rfc1738_encode (dbo->obj_schema);
+ tmp2 = gda_rfc1738_encode (dbo->obj_name);
+ tmp3 = gda_rfc1738_encode (dbo->obj_short_name);
+ tmp4 = gda_rfc1738_encode (column->priv->column->column_name);
+ str = g_strdup_printf ("OBJ_TYPE=tablecolumn;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s;COL_NAME=%s",
+ tmp1, tmp2, tmp3, tmp4);
+ g_free (tmp1);
+ g_free (tmp2);
+ g_free (tmp3);
+ g_free (tmp4);
+ gtk_selection_data_set (data, data->target, 8, str, strlen (str));
+ g_free (str);
+}
diff --git a/tools/browser/canvas/browser-canvas-column.h b/tools/browser/canvas/browser-canvas-column.h
new file mode 100644
index 0000000..b0bc600
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-column.h
@@ -0,0 +1,65 @@
+/* browser-canvas-column.h
+ *
+ * Copyright (C) 2004 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_COLUMN__
+#define __BROWSER_CANVAS_COLUMN__
+
+#include "browser-canvas-text.h"
+#include "browser-canvas-decl.h"
+
+G_BEGIN_DECLS
+
+/*
+ *
+ * "Drag item" GooCanvas item: a BrowserCanvasItem item which is used to represent
+ * an element being dragged, and destroys itself when the mouse button is released
+ *
+ */
+
+#define TYPE_BROWSER_CANVAS_COLUMN (browser_canvas_column_get_type())
+#define BROWSER_CANVAS_COLUMN(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_column_get_type(), BrowserCanvasColumn)
+#define BROWSER_CANVAS_COLUMN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_column_get_type (), BrowserCanvasColumnClass)
+#define IS_BROWSER_CANVAS_COLUMN(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_column_get_type ())
+
+
+/* struct for the object's data */
+struct _BrowserCanvasColumn
+{
+ BrowserCanvasText object;
+
+ BrowserCanvasColumnPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasColumnClass
+{
+ BrowserCanvasTextClass parent_class;
+};
+
+/* generic widget's functions */
+GType browser_canvas_column_get_type (void) G_GNUC_CONST;
+GooCanvasItem* browser_canvas_column_new (GooCanvasItem *parent, GdaMetaTableColumn *column,
+ gdouble x, gdouble y, ...);
+GdaMetaTableColumn *browser_canvas_column_get_column (BrowserCanvasColumn *column);
+BrowserCanvasTable *browser_canvas_column_get_parent_item (BrowserCanvasColumn *column);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
new file mode 100644
index 0000000..ea50309
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -0,0 +1,476 @@
+/* browser-canvas-db-relations.c
+ *
+ * Copyright (C) 2002 - 2007 Vivien Malerba
+ * Copyright (C) 2002 Fernando Martins
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include <glib/gi18n-lib.h>
+#include "browser-canvas-priv.h"
+#include "browser-canvas-db-relations.h"
+#include "browser-canvas-table.h"
+#include "browser-canvas-column.h"
+#include "browser-canvas-fkey.h"
+
+static void browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass *class);
+static void browser_canvas_db_relations_init (BrowserCanvasDbRelations *canvas);
+static void browser_canvas_db_relations_dispose (GObject *object);
+
+static void browser_canvas_db_relations_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_db_relations_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* virtual functions */
+static void clean_canvas_items (BrowserCanvas *canvas);
+static GtkWidget *build_context_menu (BrowserCanvas *canvas);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+enum
+{
+ PROP_0,
+ PROP_META_STRUCT
+};
+
+struct _BrowserCanvasDbRelationsPrivate
+{
+ GHashTable *hash_tables; /* key = GdaMetaTable, value = BrowserCanvasMetaTable (and the reverse) */
+ GHashTable *hash_fkeys; /* key = GdaMetaTableForeignKey, value = BrowserCanvasFkey */
+ GSList *all_items; /* list of all the BrowserCanvasItem objects */
+
+ GdaMetaStruct *mstruct;
+};
+
+GType
+browser_canvas_db_relations_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasDbRelationsClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_db_relations_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasDbRelations),
+ 0,
+ (GInstanceInitFunc) browser_canvas_db_relations_init
+ };
+
+ type = g_type_register_static (TYPE_BROWSER_CANVAS, "BrowserCanvasDbRelations", &info, 0);
+ }
+ return type;
+}
+
+static void
+browser_canvas_db_relations_init (BrowserCanvasDbRelations * canvas)
+{
+ canvas->priv = g_new0 (BrowserCanvasDbRelationsPrivate, 1);
+ canvas->priv->hash_tables = g_hash_table_new (NULL, NULL);
+ canvas->priv->hash_fkeys = g_hash_table_new (NULL, NULL);
+ canvas->priv->mstruct = NULL;
+}
+
+static void
+browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass * class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
+ /* BrowserCanvas virtual functions */
+ BROWSER_CANVAS_CLASS (class)->clean_canvas_items = clean_canvas_items;
+ BROWSER_CANVAS_CLASS (class)->build_context_menu = build_context_menu;
+ object_class->dispose = browser_canvas_db_relations_dispose;
+
+ /* properties */
+ object_class->set_property = browser_canvas_db_relations_set_property;
+ object_class->get_property = browser_canvas_db_relations_get_property;
+ g_object_class_install_property (object_class, PROP_META_STRUCT,
+ g_param_spec_object ("meta-struct", "GdaMetaStruct", NULL,
+ GDA_TYPE_META_STRUCT,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+}
+
+static void
+browser_canvas_db_relations_dispose (GObject *object)
+{
+ BrowserCanvasDbRelations *canvas;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (object));
+
+ canvas = BROWSER_CANVAS_DB_RELATIONS (object);
+
+ if (canvas->priv) {
+ clean_canvas_items (BROWSER_CANVAS (canvas));
+ if (canvas->priv->mstruct) {
+ g_object_unref (canvas->priv->mstruct);
+ canvas->priv->mstruct = NULL;
+ }
+
+ g_hash_table_destroy (canvas->priv->hash_tables);
+ g_hash_table_destroy (canvas->priv->hash_fkeys);
+
+ g_free (canvas->priv);
+ canvas->priv = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->dispose (object);
+}
+
+static void
+browser_canvas_db_relations_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasDbRelations *canvas;
+
+ canvas = BROWSER_CANVAS_DB_RELATIONS (object);
+ if (canvas->priv) {
+ switch (param_id) {
+ case PROP_META_STRUCT:
+ if (canvas->priv->mstruct)
+ clean_canvas_items (BROWSER_CANVAS (canvas));
+
+ canvas->priv->mstruct = g_value_get_object (value);
+ if (canvas->priv->mstruct)
+ g_object_ref (canvas->priv->mstruct);
+ break;
+ }
+ }
+}
+
+static void
+browser_canvas_db_relations_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasDbRelations *canvas;
+
+ canvas = BROWSER_CANVAS_DB_RELATIONS (object);
+ if (canvas->priv) {
+ switch (param_id) {
+ case PROP_META_STRUCT:
+ g_value_set_object (value, canvas->priv->mstruct);
+ break;
+ }
+ }
+}
+
+/**
+ * browser_canvas_db_relations_new
+ * @mstruct: a #GdaMetaStruct object, or %NULL
+ *
+ * Creates a new canvas widget to display the relations between the database's tables.
+ *
+ * After the #BrowserCanvasDbRelations has been created, it is possible to display tables
+ * using browser_canvas_db_relations_add_table().
+ *
+ * Returns: a new #GtkWidget widget
+ */
+GtkWidget *
+browser_canvas_db_relations_new (GdaMetaStruct *mstruct)
+{
+ g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
+ return GTK_WIDGET (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS, "meta-struct", mstruct, NULL));
+}
+
+
+
+static void
+clean_canvas_items (BrowserCanvas *canvas)
+{
+ BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+
+ /* clean memory */
+ g_hash_table_destroy (dbrel->priv->hash_tables);
+ g_hash_table_destroy (dbrel->priv->hash_fkeys);
+ dbrel->priv->hash_tables = g_hash_table_new (NULL, NULL);
+ dbrel->priv->hash_fkeys = g_hash_table_new (NULL, NULL);
+ if (dbrel->priv->all_items) {
+ g_slist_free (dbrel->priv->all_items);
+ dbrel->priv->all_items = NULL;
+ }
+}
+
+static GtkWidget *canvas_entity_popup_func (BrowserCanvasTable *ce);
+
+static void popup_func_delete_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
+static void popup_func_add_depend_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
+static GtkWidget *
+canvas_entity_popup_func (BrowserCanvasTable *ce)
+{
+ GtkWidget *menu, *entry;
+
+ menu = gtk_menu_new ();
+ entry = gtk_menu_item_new_with_label (_("Remove"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_delete_cb), ce);
+ gtk_menu_append (GTK_MENU (menu), entry);
+ gtk_widget_show (entry);
+ entry = gtk_menu_item_new_with_label (_("Add referenced tables"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_depend_cb), ce);
+ gtk_menu_append (GTK_MENU (menu), entry);
+ gtk_widget_show (entry);
+
+ return menu;
+}
+
+static void
+popup_func_delete_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce)
+{
+ GdaMetaTable *mtable;
+ BrowserCanvasDbRelations *dbrel;
+
+ dbrel = BROWSER_CANVAS_DB_RELATIONS (browser_canvas_item_get_canvas (BROWSER_CANVAS_ITEM (ce)));
+
+ mtable = g_hash_table_lookup (dbrel->priv->hash_tables, ce);
+ g_hash_table_remove (dbrel->priv->hash_tables, ce);
+ g_hash_table_remove (dbrel->priv->hash_tables, mtable);
+ goo_canvas_item_remove (GOO_CANVAS_ITEM (ce));
+ dbrel->priv->all_items = g_slist_remove (dbrel->priv->all_items, ce);
+}
+
+static void
+popup_func_add_depend_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce)
+{
+ BrowserCanvasDbRelations *dbrel;
+ GdaMetaDbObject *dbo;
+
+ dbrel = BROWSER_CANVAS_DB_RELATIONS (browser_canvas_item_get_canvas (BROWSER_CANVAS_ITEM (ce)));
+ dbo = g_hash_table_lookup (dbrel->priv->hash_tables, ce);
+ if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE))
+ return;
+
+ if (!dbrel->priv->mstruct)
+ return;
+
+ GdaMetaTable *mtable = GDA_META_TABLE (dbo);
+ GSList *list;
+ GooCanvasBounds bounds;
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ce), &bounds);
+ bounds.y1 = bounds.y2 + 35.;
+ bounds.x2 = bounds.x1 - 20.;
+
+ for (list = mtable->fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
+ if (fk->depend_on->obj_type != GDA_META_DB_TABLE)
+ continue;
+ if (g_hash_table_lookup (dbrel->priv->hash_tables, fk->depend_on))
+ continue;
+
+ BrowserCanvasTable *new_item;
+ GValue *v1, *v2, *v3;
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_catalog);
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_schema);
+ g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_name);
+ new_item = browser_canvas_db_relations_add_table (dbrel, v1, v2, v3);
+ gda_value_free (v1);
+ gda_value_free (v2);
+ gda_value_free (v3);
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (new_item), &bounds);
+ }
+}
+
+static void popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *canvas);
+static GtkWidget *
+build_context_menu (BrowserCanvas *canvas)
+{
+ GtkWidget *menu, *mitem, *submenu, *submitem;
+ GSList *dbolist, *list;
+ BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+
+ if (!dbrel->priv->mstruct)
+ return NULL;
+
+ menu = gtk_menu_new ();
+ submitem = gtk_menu_item_new_with_label (_("Add table"));
+ gtk_widget_show (submitem);
+ gtk_menu_append (menu, submitem);
+ submenu = NULL;
+
+ /* build list of tables */
+ dbolist = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
+ for (list = dbolist; list; list = list->next) {
+ GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+ GdaMetaTable *mtable;
+
+ if (dbo->obj_type != GDA_META_DB_TABLE)
+ continue;
+
+ mtable = GDA_META_TABLE (dbo);
+ if (mtable && g_hash_table_lookup (dbrel->priv->hash_tables, mtable))
+ continue; /* skip that table as it is already present in the canvas */
+
+ if (!submenu) {
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (submitem), submenu);
+ gtk_widget_show (submenu);
+ }
+
+ mitem = gtk_menu_item_new_with_label (dbo->obj_name);
+ gtk_widget_show (mitem);
+ gtk_menu_append (submenu, mitem);
+
+ GValue *tcatalog, *tschema, *tname;
+ g_value_set_string ((tcatalog = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
+ g_value_set_string ((tschema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
+ g_value_set_string ((tname = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+ g_object_set_data_full (G_OBJECT (mitem), "tcat", tcatalog, (GDestroyNotify) gda_value_free);
+ g_object_set_data_full (G_OBJECT (mitem), "tschema", tschema, (GDestroyNotify) gda_value_free);
+ g_object_set_data_full (G_OBJECT (mitem), "tname", tname, (GDestroyNotify) gda_value_free);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_add_table_cb), canvas);
+ }
+ g_slist_free (dbolist);
+
+ /* sub menu is incensitive if there are no more tables left to add */
+ gtk_widget_set_sensitive (submitem, submenu ? TRUE : FALSE);
+
+ return menu;
+}
+
+static void
+popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
+{
+ GdaMetaTable *mtable;
+ GValue *table_catalog;
+ GValue *table_schema;
+ GValue *table_name;
+
+ table_catalog = g_object_get_data (G_OBJECT (mitem), "tcat");
+ table_schema = g_object_get_data (G_OBJECT (mitem), "tschema");
+ table_name = g_object_get_data (G_OBJECT (mitem), "tname");
+
+ /*g_print ("Add %s.%s.%s\n", g_value_get_string (table_catalog),
+ g_value_get_string (table_schema), g_value_get_string (table_name));*/
+ mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
+ table_catalog, table_schema, table_name, NULL);
+ if (mtable) {
+ browser_canvas_db_relations_add_table (dbrel, table_catalog, table_schema, table_name);
+ }
+ else
+ g_print ("Unknown...\n");
+}
+
+/**
+ * browser_canvas_db_relations_get_table_item
+ * @canvas:
+ * @table:
+ *
+ * Returns:
+ */
+BrowserCanvasTable *
+browser_canvas_db_relations_get_table_item (BrowserCanvasDbRelations *canvas, GdaMetaTable *table)
+{
+ BrowserCanvasTable *table_item;
+ g_return_val_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas), NULL);
+ g_return_val_if_fail (canvas->priv, NULL);
+
+ table_item = g_hash_table_lookup (canvas->priv->hash_tables, table);
+ return BROWSER_CANVAS_TABLE (table_item);
+}
+
+/**
+ * browser_canvas_db_relations_add_table
+ * @canvas: a #BrowserCanvasDbRelations canvas
+ * @table_catalog: the catalog in which the table is, or %NULL
+ * @table_schema: the schema in which the table is, or %NULL
+ * @table_name: the table's name
+ *
+ * Add a table to @canvas.
+ *
+ * Returns: the corresponding canvas item, or %NULL if the table was not found.
+ */
+BrowserCanvasTable *
+browser_canvas_db_relations_add_table (BrowserCanvasDbRelations *canvas,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name)
+{
+ g_return_val_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas), NULL);
+ g_return_val_if_fail (canvas->priv, NULL);
+
+ GdaMetaTable *mtable;
+ GooCanvas *goocanvas;
+
+ if (!canvas->priv->mstruct)
+ return NULL;
+
+ 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, NULL);
+ if (mtable) {
+ gdouble x = 0, y = 0;
+ GooCanvasItem *table_item;
+
+ table_item = browser_canvas_table_new (goo_canvas_get_root_item (goocanvas),
+ mtable, x, y, NULL);
+ g_hash_table_insert (canvas->priv->hash_tables, mtable, table_item);
+ g_hash_table_insert (canvas->priv->hash_tables,table_item, mtable);
+ canvas->priv->all_items = g_slist_prepend (canvas->priv->all_items, table_item);
+ g_object_set (G_OBJECT (table_item),
+ "popup_menu_func", canvas_entity_popup_func, NULL);
+ browser_canvas_declare_item (BROWSER_CANVAS (canvas),
+ BROWSER_CANVAS_ITEM (table_item));
+
+ /* if there are some FK links, then also add them */
+ GSList *list;
+ for (list = mtable->fk_list; list; list = list->next) {
+ GooCanvasItem *ref_table_item;
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->depend_on);
+ if (ref_table_item) {
+ GooCanvasItem *fk_item;
+ fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas), fk, NULL);
+ browser_canvas_declare_item (BROWSER_CANVAS (canvas),
+ BROWSER_CANVAS_ITEM (fk_item));
+
+ g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+ }
+ }
+ for (list = mtable->reverse_fk_list; list; list = list->next) {
+ GooCanvasItem *ref_table_item;
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->meta_table);
+ if (ref_table_item) {
+ GooCanvasItem *fk_item;
+ fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas), fk, NULL);
+ browser_canvas_declare_item (BROWSER_CANVAS (canvas),
+ BROWSER_CANVAS_ITEM (fk_item));
+
+ g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
+ }
+ }
+
+ return BROWSER_CANVAS_TABLE (table_item);
+ }
+ else
+ return NULL;
+}
+
diff --git a/tools/browser/canvas/browser-canvas-db-relations.h b/tools/browser/canvas/browser-canvas-db-relations.h
new file mode 100644
index 0000000..bcbefc8
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-db-relations.h
@@ -0,0 +1,66 @@
+/* browser-canvas-db-relations.h
+ *
+ * Copyright (C) 2004 - 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_DB_RELATIONS__
+#define __BROWSER_CANVAS_DB_RELATIONS__
+
+#include "browser-canvas.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_BROWSER_CANVAS_DB_RELATIONS (browser_canvas_db_relations_get_type())
+#define BROWSER_CANVAS_DB_RELATIONS(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_db_relations_get_type(), BrowserCanvasDbRelations)
+#define BROWSER_CANVAS_DB_RELATIONS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_db_relations_get_type (), BrowserCanvasDbRelationsClass)
+#define IS_BROWSER_CANVAS_DB_RELATIONS(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_db_relations_get_type ())
+
+
+typedef struct _BrowserCanvasDbRelations BrowserCanvasDbRelations;
+typedef struct _BrowserCanvasDbRelationsClass BrowserCanvasDbRelationsClass;
+typedef struct _BrowserCanvasDbRelationsPrivate BrowserCanvasDbRelationsPrivate;
+
+
+/* struct for the object's data */
+struct _BrowserCanvasDbRelations
+{
+ BrowserCanvas widget;
+
+ BrowserCanvasDbRelationsPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasDbRelationsClass
+{
+ BrowserCanvasClass parent_class;
+};
+
+/* generic widget's functions */
+GType browser_canvas_db_relations_get_type (void) G_GNUC_CONST;
+
+GtkWidget *browser_canvas_db_relations_new (GdaMetaStruct *mstruct);
+
+BrowserCanvasTable *browser_canvas_db_relations_get_table_item (BrowserCanvasDbRelations *canvas, GdaMetaTable *table);
+BrowserCanvasTable *browser_canvas_db_relations_add_table (BrowserCanvasDbRelations *canvas,
+ const GValue *table_catalog,
+ const GValue *table_schema,
+ const GValue *table_name);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-decl.h b/tools/browser/canvas/browser-canvas-decl.h
new file mode 100644
index 0000000..9b506d8
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-decl.h
@@ -0,0 +1,46 @@
+/* browser-canvas-decl.h
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_DECL_H_
+#define __BROWSER_CANVAS_DECL_H_
+
+typedef struct _BrowserCanvas BrowserCanvas;
+typedef struct _BrowserCanvasClass BrowserCanvasClass;
+typedef struct _BrowserCanvasPrivate BrowserCanvasPrivate;
+
+typedef struct _BrowserCanvasItem BrowserCanvasItem;
+typedef struct _BrowserCanvasItemClass BrowserCanvasItemClass;
+typedef struct _BrowserCanvasItemPrivate BrowserCanvasItemPrivate;
+
+typedef struct _BrowserCanvasTable BrowserCanvasTable;
+typedef struct _BrowserCanvasTableClass BrowserCanvasTableClass;
+typedef struct _BrowserCanvasTablePrivate BrowserCanvasTablePrivate;
+
+typedef struct _BrowserCanvasColumn BrowserCanvasColumn;
+typedef struct _BrowserCanvasColumnClass BrowserCanvasColumnClass;
+typedef struct _BrowserCanvasColumnPrivate BrowserCanvasColumnPrivate;
+
+#define BROWSER_CANVAS_ENTITY_COLOR "yellow"
+#define BROWSER_CANVAS_DB_TABLE_COLOR "#b9b9b9"
+
+#define BROWSER_CANVAS_OBJ_BG_COLOR "#f8f8f8"
+#define BROWSER_CANVAS_FONT "Sans 10"
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-fkey.c b/tools/browser/canvas/browser-canvas-fkey.c
new file mode 100644
index 0000000..7e06257
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-fkey.c
@@ -0,0 +1,477 @@
+/* browser-canvas-fkey.c
+ *
+ * Copyright (C) 2004 - 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <libgda/libgda.h>
+#include <glib/gi18n-lib.h>
+#include "browser-canvas.h"
+#include "browser-canvas-fkey.h"
+#include "browser-canvas-table.h"
+#include "browser-canvas-text.h"
+#include "browser-canvas-utility.h"
+#include "browser-canvas-db-relations.h"
+
+static void browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class);
+static void browser_canvas_fkey_init (BrowserCanvasFkey * cc);
+static void browser_canvas_fkey_dispose (GObject *object);
+static void browser_canvas_fkey_finalize (GObject *object);
+
+static void browser_canvas_fkey_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_fkey_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void browser_canvas_fkey_get_edge_nodes (BrowserCanvasItem *citem,
+ BrowserCanvasItem **from, BrowserCanvasItem **to);
+
+static void clean_items (BrowserCanvasFkey *cc);
+static void create_items (BrowserCanvasFkey *cc);
+static void update_items (BrowserCanvasFkey *cc);
+
+enum
+{
+ PROP_0,
+ PROP_FK_CONSTRAINT
+};
+
+struct _BrowserCanvasFkeyPrivate
+{
+ GdaMetaTableForeignKey *fk;
+ BrowserCanvasTable *fk_table_item;
+ BrowserCanvasTable *ref_pk_table_item;
+ GSList *shapes; /* list of BrowserCanvasCanvasShape structures */
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+GType
+browser_canvas_fkey_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasFkeyClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_fkey_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasFkey),
+ 0,
+ (GInstanceInitFunc) browser_canvas_fkey_init
+ };
+
+ type = g_type_register_static (TYPE_BROWSER_CANVAS_ITEM, "BrowserCanvasFkey", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ BROWSER_CANVAS_ITEM_CLASS (class)->get_edge_nodes = browser_canvas_fkey_get_edge_nodes;
+
+ object_class->dispose = browser_canvas_fkey_dispose;
+ object_class->finalize = browser_canvas_fkey_finalize;
+
+ /* Properties */
+ object_class->set_property = browser_canvas_fkey_set_property;
+ object_class->get_property = browser_canvas_fkey_get_property;
+ g_object_class_install_property (object_class, PROP_FK_CONSTRAINT,
+ g_param_spec_pointer ("fk_constraint", "FK constraint",
+ NULL,
+ G_PARAM_WRITABLE));
+
+}
+
+static void
+browser_canvas_fkey_init (BrowserCanvasFkey *cc)
+{
+ cc->priv = g_new0 (BrowserCanvasFkeyPrivate, 1);
+ cc->priv->fk = NULL;
+ cc->priv->fk_table_item = NULL;
+ cc->priv->ref_pk_table_item = NULL;
+ cc->priv->shapes = NULL;
+}
+
+static void
+browser_canvas_fkey_dispose (GObject *object)
+{
+ BrowserCanvasFkey *cc;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_FKEY (object));
+
+ cc = BROWSER_CANVAS_FKEY (object);
+
+ clean_items (cc);
+ cc->priv->fk = NULL;
+ cc->priv->fk_table_item = NULL;
+ cc->priv->ref_pk_table_item = NULL;
+
+ /* for the parent class */
+ parent_class->dispose (object);
+}
+
+
+static void
+browser_canvas_fkey_finalize (GObject *object)
+{
+ BrowserCanvasFkey *cc;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_FKEY (object));
+
+ cc = BROWSER_CANVAS_FKEY (object);
+ if (cc->priv) {
+ g_slist_free (cc->priv->shapes);
+ g_free (cc->priv);
+ cc->priv = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->finalize (object);
+}
+
+static void
+browser_canvas_fkey_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasFkey *cc;
+
+ cc = BROWSER_CANVAS_FKEY (object);
+
+ switch (param_id) {
+ case PROP_FK_CONSTRAINT:
+ if (cc->priv->fk != g_value_get_pointer (value)) {
+ cc->priv->fk = g_value_get_pointer (value);
+ clean_items (cc);
+ create_items (cc);
+ }
+ break;
+ }
+}
+
+static void
+browser_canvas_fkey_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasFkey *cc;
+
+ cc = BROWSER_CANVAS_FKEY (object);
+
+ switch (param_id) {
+ default:
+ g_warning ("No such property!");
+ break;
+ }
+}
+
+static void
+browser_canvas_fkey_get_edge_nodes (BrowserCanvasItem *citem,
+ BrowserCanvasItem **from, BrowserCanvasItem **to)
+{
+ BrowserCanvasFkey *cc;
+
+ cc = BROWSER_CANVAS_FKEY (citem);
+
+ if (from)
+ *from = (BrowserCanvasItem*) cc->priv->fk_table_item;
+ if (to)
+ *to = (BrowserCanvasItem*) cc->priv->ref_pk_table_item;
+}
+
+static gboolean single_item_enter_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventCrossing *event, BrowserCanvasFkey *cc);
+static gboolean single_item_leave_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventCrossing *event, BrowserCanvasFkey *cc);
+static gboolean single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventButton *event, BrowserCanvasFkey *cc);
+static void table_item_moved_cb (GooCanvasItem *table, BrowserCanvasFkey *cc);
+static void table_destroy_cb (BrowserCanvasTable *table, BrowserCanvasFkey *cc);
+
+/*
+ * destroy any existing GooCanvasItem objects
+ */
+static void
+clean_items (BrowserCanvasFkey *cc)
+{
+ if (cc->priv->fk_table_item) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_table_item),
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_table_item),
+ G_CALLBACK (table_destroy_cb), cc);
+ cc->priv->fk_table_item = NULL;
+ }
+
+ if (cc->priv->ref_pk_table_item) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_table_item),
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_table_item),
+ G_CALLBACK (table_destroy_cb), cc);
+ cc->priv->ref_pk_table_item = NULL;
+ }
+
+ /* remove all the GooCanvasItem objects */
+ browser_canvas_canvas_shapes_remove_all (cc->priv->shapes);
+ cc->priv->shapes = NULL;
+}
+
+/*
+ * create new GooCanvasItem objects
+ */
+static void
+create_items (BrowserCanvasFkey *cc)
+{
+ GSList *list, *canvas_shapes;
+ BrowserCanvasTable *table_item;
+ BrowserCanvas *canvas = g_object_get_data (G_OBJECT (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (cc))),
+ "browsercanvas");
+
+ g_assert (cc->priv->fk);
+
+ /* Analyse FK constraint */
+ table_item = browser_canvas_db_relations_get_table_item (BROWSER_CANVAS_DB_RELATIONS (canvas),
+ GDA_META_TABLE (cc->priv->fk->meta_table));
+ cc->priv->fk_table_item = table_item;
+ g_return_if_fail (table_item);
+ g_signal_connect (G_OBJECT (table_item), "moving",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "moved",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "shifted",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "destroy",
+ G_CALLBACK (table_destroy_cb), cc);
+
+ table_item = browser_canvas_db_relations_get_table_item (BROWSER_CANVAS_DB_RELATIONS (canvas),
+ GDA_META_TABLE (cc->priv->fk->depend_on));
+ cc->priv->ref_pk_table_item = table_item;
+ g_return_if_fail (table_item);
+ g_signal_connect (G_OBJECT (table_item), "moving",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "moved",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "shifted",
+ G_CALLBACK (table_item_moved_cb), cc);
+ g_signal_connect (G_OBJECT (table_item), "destroy",
+ G_CALLBACK (table_destroy_cb), cc);
+
+ /* actual line(s) */
+ g_assert (!cc->priv->shapes);
+ canvas_shapes = browser_canvas_util_compute_anchor_shapes (GOO_CANVAS_ITEM (cc), NULL,
+ cc->priv->fk_table_item,
+ cc->priv->ref_pk_table_item,
+ /*MAX (cc->priv->fk->cols_nb, 1)*/ 1,
+ 0, TRUE);
+
+ cc->priv->shapes = browser_canvas_canvas_shapes_remove_obsolete_shapes (canvas_shapes);
+ for (list = canvas_shapes; list; list = list->next) {
+ GooCanvasItem *item = BROWSER_CANVAS_CANVAS_SHAPE (list->data)->item;
+ gchar *color = "black";
+ g_object_set (G_OBJECT (item),
+ "stroke-color", color,
+ NULL);
+
+ if (G_OBJECT_TYPE (item) == GOO_TYPE_CANVAS_POLYLINE) {
+ g_object_set (G_OBJECT (item),
+ "start-arrow", TRUE,
+ "arrow-tip-length", 4.,
+ "arrow-length", 5.,
+ "arrow-width", 4.,
+ NULL);
+ }
+ else if (G_OBJECT_TYPE (item) == GOO_TYPE_CANVAS_ELLIPSE)
+ g_object_set (G_OBJECT (item),
+ "fill-color", color,
+ NULL);
+
+ g_object_set_data (G_OBJECT (item), "fkcons", cc->priv->fk);
+ g_signal_connect (G_OBJECT (item), "enter-notify-event",
+ G_CALLBACK (single_item_enter_notify_event_cb), cc);
+ g_signal_connect (G_OBJECT (item), "leave-notify-event",
+ G_CALLBACK (single_item_leave_notify_event_cb), cc);
+ g_signal_connect (G_OBJECT (item), "button-press-event",
+ G_CALLBACK (single_item_button_press_event_cb), cc);
+
+ }
+}
+
+/*
+ * update GooCanvasItem objects
+ */
+static void
+update_items (BrowserCanvasFkey *cc)
+{
+ cc->priv->shapes = browser_canvas_util_compute_anchor_shapes (GOO_CANVAS_ITEM (cc), cc->priv->shapes,
+ cc->priv->fk_table_item,
+ cc->priv->ref_pk_table_item,
+ /*MAX (cc->priv->fk->cols_nb, 1)*/ 1,
+ 0, TRUE);
+ cc->priv->shapes = browser_canvas_canvas_shapes_remove_obsolete_shapes (cc->priv->shapes);
+}
+
+static void popup_delete_cb (GtkMenuItem *mitem, BrowserCanvasFkey *cc);
+
+/*
+ * item is for a single FK constraint
+ */
+static gboolean
+single_item_enter_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventCrossing *event, BrowserCanvasFkey *cc)
+{
+ gint i;
+
+ for (i = 0; i < cc->priv->fk->cols_nb; i++) {
+ GdaMetaTableColumn *tcol;
+ BrowserCanvasColumn *column;
+
+ /* fk column */
+ tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->meta_table)->columns,
+ cc->priv->fk->fk_cols_array[i] - 1);
+
+ column = browser_canvas_table_get_column_item (cc->priv->fk_table_item, tcol);
+ browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), TRUE);
+
+ /* ref pk column */
+ tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->depend_on)->columns,
+ cc->priv->fk->ref_pk_cols_array[i] - 1);
+
+ column = browser_canvas_table_get_column_item (cc->priv->ref_pk_table_item, tcol);
+ browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), TRUE);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+single_item_leave_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventCrossing *event, BrowserCanvasFkey *cc)
+{
+ gint i;
+
+ for (i = 0; i < cc->priv->fk->cols_nb; i++) {
+ GdaMetaTableColumn *tcol;
+ BrowserCanvasColumn *column;
+
+ /* fk column */
+ tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->meta_table)->columns,
+ cc->priv->fk->fk_cols_array[i] - 1);
+
+ column = browser_canvas_table_get_column_item (cc->priv->fk_table_item, tcol);
+ browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), FALSE);
+
+ /* ref pk column */
+ tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->depend_on)->columns,
+ cc->priv->fk->ref_pk_cols_array[i] - 1);
+
+ column = browser_canvas_table_get_column_item (cc->priv->ref_pk_table_item, tcol);
+ browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), FALSE);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
+ GdkEventButton *event, BrowserCanvasFkey *cc)
+{
+ GdaMetaTableForeignKey *fkcons = g_object_get_data (G_OBJECT (ci), "fkcons");
+ GtkWidget *menu, *entry;
+
+ menu = gtk_menu_new ();
+ entry = gtk_menu_item_new_with_label (_("Remove"));
+ g_object_set_data (G_OBJECT (entry), "fkcons", fkcons);
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_delete_cb), cc);
+ gtk_menu_append (GTK_MENU (menu), entry);
+ gtk_widget_show (entry);
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ NULL, NULL, ((GdkEventButton *)event)->button,
+ ((GdkEventButton *)event)->time);
+ return TRUE;
+}
+
+
+static void
+popup_delete_cb (GtkMenuItem *mitem, BrowserCanvasFkey *cc)
+{
+ TO_IMPLEMENT;
+}
+
+static void
+table_item_moved_cb (GooCanvasItem *table, BrowserCanvasFkey *cc)
+{
+ update_items (cc);
+}
+
+static void
+table_destroy_cb (BrowserCanvasTable *table, BrowserCanvasFkey *cc)
+{
+ goo_canvas_item_remove (GOO_CANVAS_ITEM (cc));
+}
+
+/**
+ * browser_canvas_fkey_new
+ * @parent: the parent item, or NULL.
+ * @fkcons: the #GdaMetaTableForeignKey to represent
+ * @...: optional pairs of property names and values, and a terminating NULL.
+ *
+ * Creates a new canvas item to represent the @fkcons FK constraint
+ *
+ * Returns: a new #GooCanvasItem object
+ */
+GooCanvasItem *
+browser_canvas_fkey_new (GooCanvasItem *parent, GdaMetaTableForeignKey *fkcons, ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_FKEY, NULL);
+
+ if (parent) {
+ goo_canvas_item_add_child (parent, item, -1);
+ g_object_unref (item);
+ }
+
+ g_object_set (item, "fk_constraint", fkcons, NULL);
+
+ va_start (var_args, fkcons);
+ first_property = va_arg (var_args, char*);
+ if (first_property)
+ g_object_set_valist ((GObject*) item, first_property, var_args);
+ va_end (var_args);
+
+ return item;
+}
diff --git a/tools/browser/canvas/browser-canvas-fkey.h b/tools/browser/canvas/browser-canvas-fkey.h
new file mode 100644
index 0000000..e63a901
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-fkey.h
@@ -0,0 +1,64 @@
+/* browser-canvas-fkey.h
+ *
+ * Copyright (C) 2005 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_FKEY__
+#define __BROWSER_CANVAS_FKEY__
+
+#include "browser-canvas-item.h"
+
+G_BEGIN_DECLS
+
+/*
+ *
+ * "Drag item" GooCanvas item: a BrowserCanvasItem item which is used to represent
+ * an element being dragged, and destroys itself when the mouse button is released
+ *
+ */
+
+#define TYPE_BROWSER_CANVAS_FKEY (browser_canvas_fkey_get_type())
+#define BROWSER_CANVAS_FKEY(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_fkey_get_type(), BrowserCanvasFkey)
+#define BROWSER_CANVAS_FKEY_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_fkey_get_type (), BrowserCanvasFkeyClass)
+#define IS_BROWSER_CANVAS_FKEY(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_fkey_get_type ())
+
+
+typedef struct _BrowserCanvasFkey BrowserCanvasFkey;
+typedef struct _BrowserCanvasFkeyClass BrowserCanvasFkeyClass;
+typedef struct _BrowserCanvasFkeyPrivate BrowserCanvasFkeyPrivate;
+
+
+/* struct for the object's data */
+struct _BrowserCanvasFkey
+{
+ BrowserCanvasItem object;
+ BrowserCanvasFkeyPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasFkeyClass
+{
+ BrowserCanvasItemClass parent_class;
+};
+
+GType browser_canvas_fkey_get_type (void) G_GNUC_CONST;
+GooCanvasItem *browser_canvas_fkey_new (GooCanvasItem *parent, GdaMetaTableForeignKey *fk, ...);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-item.c b/tools/browser/canvas/browser-canvas-item.c
new file mode 100644
index 0000000..f57412c
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-item.c
@@ -0,0 +1,405 @@
+/* browser-canvas-item.c
+ *
+ * Copyright (C) 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "browser-canvas-item.h"
+#include "../dnd.h"
+
+static void browser_canvas_item_class_init (BrowserCanvasItemClass * class);
+static void browser_canvas_item_init (BrowserCanvasItem * item);
+static void browser_canvas_item_dispose (GObject *object);
+
+
+static void browser_canvas_item_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_item_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+struct _BrowserCanvasItemPrivate
+{
+ gboolean moving;
+ double xstart;
+ double ystart;
+ gboolean allow_move;
+ gchar *tooltip_text;
+};
+
+enum
+{
+ MOVED,
+ MOVING,
+ SHIFTED,
+ DESTROY,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ALLOW_MOVE,
+ PROP_TOOLTIP_TEXT
+};
+
+static gint browser_canvas_item_signals[LAST_SIGNAL] = { 0, 0, 0, 0 };
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *base_parent_class = NULL;
+
+GType
+browser_canvas_item_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasItemClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_item_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasItem),
+ 0,
+ (GInstanceInitFunc) browser_canvas_item_init
+ };
+ type = g_type_register_static (GOO_TYPE_CANVAS_GROUP, "BrowserCanvasItem", &info, 0);
+ }
+
+ return type;
+}
+
+
+static void
+browser_canvas_item_class_init (BrowserCanvasItemClass * class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ base_parent_class = g_type_class_peek_parent (class);
+
+ browser_canvas_item_signals[MOVED] =
+ g_signal_new ("moved",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (BrowserCanvasItemClass, moved),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+ 0);
+ browser_canvas_item_signals[MOVING] =
+ g_signal_new ("moving",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (BrowserCanvasItemClass, moving),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+ 0);
+ browser_canvas_item_signals[SHIFTED] =
+ g_signal_new ("shifted",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (BrowserCanvasItemClass, shifted),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+ 0);
+ browser_canvas_item_signals[DESTROY] =
+ g_signal_new ("destroy",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (BrowserCanvasItemClass, destroy),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+ 0);
+
+
+ class->moved = NULL;
+ class->moving = NULL;
+ class->shifted = NULL;
+ class->destroy = NULL;
+ object_class->dispose = browser_canvas_item_dispose;
+
+ /* virtual funstionc */
+ class->extra_event = NULL;
+
+ /* Properties */
+ object_class->set_property = browser_canvas_item_set_property;
+ object_class->get_property = browser_canvas_item_get_property;
+
+ g_object_class_install_property
+ (object_class, PROP_ALLOW_MOVE,
+ g_param_spec_boolean ("allow-move", NULL, NULL, TRUE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (object_class, PROP_TOOLTIP_TEXT,
+ g_param_spec_string ("tip-text", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static gboolean leave_notify_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventCrossing *event, gpointer data);
+static gboolean button_press_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventButton *event, gpointer data);
+static gboolean button_release_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventButton *event, gpointer data);
+static gboolean motion_notify_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventMotion *event, gpointer data);
+
+
+static void
+browser_canvas_item_init (BrowserCanvasItem * item)
+{
+ item->priv = g_new0 (BrowserCanvasItemPrivate, 1);
+ item->priv->moving = FALSE;
+ item->priv->xstart = 0;
+ item->priv->ystart = 0;
+ item->priv->allow_move = FALSE;
+ item->priv->tooltip_text = NULL;
+
+ g_signal_connect (G_OBJECT (item), "leave-notify-event",
+ G_CALLBACK (leave_notify_event), NULL);
+ g_signal_connect (G_OBJECT (item), "motion-notify-event",
+ G_CALLBACK (motion_notify_event), NULL);
+ g_signal_connect (G_OBJECT (item), "button-press-event",
+ G_CALLBACK (button_press_event), NULL);
+ g_signal_connect (G_OBJECT (item), "button-release-event",
+ G_CALLBACK (button_release_event), NULL);
+
+}
+
+static void
+browser_canvas_item_dispose (GObject *object)
+{
+ BrowserCanvasItem *citem;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_ITEM (object));
+
+ citem = BROWSER_CANVAS_ITEM (object);
+ if (citem->priv) {
+ g_signal_emit (object, browser_canvas_item_signals[DESTROY], 0);
+
+ if (citem->priv->tooltip_text)
+ g_free (citem->priv->tooltip_text);
+
+ g_free (citem->priv);
+ citem->priv = NULL;
+ }
+
+ /* for the parent class */
+ base_parent_class->dispose (object);
+}
+
+static void
+browser_canvas_item_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasItem *citem = NULL;
+ const gchar *str = NULL;
+
+ citem = BROWSER_CANVAS_ITEM (object);
+
+ switch (param_id) {
+ case PROP_ALLOW_MOVE:
+ citem->priv->allow_move = g_value_get_boolean (value);
+ break;
+ case PROP_TOOLTIP_TEXT:
+ str = g_value_get_string (value);
+ if (citem->priv->tooltip_text) {
+ g_free (citem->priv->tooltip_text);
+ citem->priv->tooltip_text = NULL;
+ }
+ if (str)
+ citem->priv->tooltip_text = g_strdup (str);
+ break;
+ }
+}
+
+static void
+browser_canvas_item_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasItem *citem = NULL;
+
+ citem = BROWSER_CANVAS_ITEM (object);
+ switch (param_id) {
+ case PROP_ALLOW_MOVE:
+ g_value_set_boolean (value, citem->priv->allow_move);
+ break;
+ case PROP_TOOLTIP_TEXT:
+ g_value_set_string (value, citem->priv->tooltip_text);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+ break;
+ }
+
+}
+
+/**
+ * browser_canvas_item_get_canvas
+ * @item: a #BrowserCanvasItem object
+ *
+ * Get the #BrowserCanvas on which @item is drawn
+ *
+ * Returns: the #BrowserCanvas widget
+ */
+BrowserCanvas *
+browser_canvas_item_get_canvas (BrowserCanvasItem *item)
+{
+ g_return_val_if_fail (IS_BROWSER_CANVAS_ITEM (item), NULL);
+ return (BrowserCanvas *) g_object_get_data (G_OBJECT (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (item))),
+ "browsercanvas");
+}
+
+/**
+ * browser_canvas_item_get_edge_nodes
+ * @item: a #BrowserCanvasItem object
+ * @from: a place to store the FROM part of the edge, or %NULL
+ * @to: a place to store the TO part of the edge, or %NULL
+ *
+ * If the @item canvas item represents a "link" between two other canvas items (an edge), then
+ * set @from and @to to those items.
+ */
+void
+browser_canvas_item_get_edge_nodes (BrowserCanvasItem *item,
+ BrowserCanvasItem **from, BrowserCanvasItem **to)
+{
+ BrowserCanvasItemClass *class;
+
+ g_return_if_fail (IS_BROWSER_CANVAS_ITEM (item));
+
+ class = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item));
+ if (class->get_edge_nodes)
+ (class->get_edge_nodes) (item, from, to);
+ else {
+ if (from)
+ *from = NULL;
+ if (to)
+ *to = NULL;
+ }
+}
+
+static gboolean
+leave_notify_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventCrossing *event, gpointer data)
+{
+ gtk_widget_set_has_tooltip (GTK_WIDGET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (citem))),
+ FALSE);
+ return FALSE;
+}
+
+static gboolean
+button_press_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventButton *event, gpointer data)
+{
+ gboolean done = FALSE;
+
+ switch (event->button) {
+ case 1:
+ if (event->state & GDK_SHIFT_MASK) {
+ GdkDragContext *context;
+ GtkTargetList *target_list;
+ BrowserCanvas *canvas;
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (citem));
+
+ if (iclass->drag_data_get) {
+ canvas = browser_canvas_item_get_canvas (citem);
+ target_list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add_table (target_list, dbo_table, G_N_ELEMENTS (dbo_table));
+ context = gtk_drag_begin (GTK_WIDGET (canvas),
+ target_list,
+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT,
+ event->button, (GdkEvent*) event);
+ gtk_drag_set_icon_default (context);
+ gtk_target_list_unref (target_list);
+ g_object_set_data (G_OBJECT (canvas), "__drag_src_item", citem);
+ }
+ done = TRUE;
+ }
+ else if (citem->priv->allow_move) {
+ /* movement management */
+ goo_canvas_item_raise (GOO_CANVAS_ITEM (citem), NULL);
+ citem->priv->xstart = event->x;
+ citem->priv->ystart = event->y;
+ citem->priv->moving = TRUE;
+ done = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return done;
+}
+
+static gboolean
+button_release_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventButton *event, gpointer data)
+{
+ if (citem->priv->allow_move) {
+ citem->priv->moving = FALSE;
+#ifdef debug_signal
+ g_print (">> 'MOVED' from %s::item_event()\n", __FILE__);
+#endif
+ g_signal_emit (G_OBJECT (citem), browser_canvas_item_signals[MOVED], 0);
+#ifdef debug_signal
+ g_print ("<< 'MOVED' from %s::item_event()\n", __FILE__);
+#endif
+ }
+
+ return FALSE;
+}
+
+static gboolean
+motion_notify_event (BrowserCanvasItem *citem, GooCanvasItem *target_item,
+ GdkEventMotion *event, gpointer data)
+{
+ gboolean retval = FALSE;
+
+ if (citem->priv->moving && (event->state & GDK_BUTTON1_MASK)) {
+ g_assert (IS_BROWSER_CANVAS_ITEM (citem));
+ goo_canvas_item_translate (GOO_CANVAS_ITEM (citem),
+ (gdouble) event->x - citem->priv->xstart,
+ (gdouble) event->y - citem->priv->ystart);
+ g_signal_emit (G_OBJECT (citem), browser_canvas_item_signals[MOVING], 0);
+ retval = TRUE;
+ }
+ else {
+ if (citem->priv->tooltip_text)
+ gtk_widget_set_tooltip_text (GTK_WIDGET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (citem))),
+ citem->priv->tooltip_text);
+ }
+
+ return retval;
+}
+
+/**
+ * browser_canvas_item_translate
+ */
+void
+browser_canvas_item_translate (BrowserCanvasItem *item, gdouble dx, gdouble dy)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS_ITEM (item));
+ goo_canvas_item_translate (GOO_CANVAS_ITEM (item), dx, dy);
+ g_signal_emit (G_OBJECT (item), browser_canvas_item_signals [MOVED], 0);
+}
diff --git a/tools/browser/canvas/browser-canvas-item.h b/tools/browser/canvas/browser-canvas-item.h
new file mode 100644
index 0000000..c43f2b2
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-item.h
@@ -0,0 +1,69 @@
+/* browser-canvas-item.h
+ *
+ * Copyright (C) 2007 - 2008 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_ITEM__
+#define __BROWSER_CANVAS_ITEM__
+
+#include <goocanvas.h>
+#include "browser-canvas-decl.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_BROWSER_CANVAS_ITEM (browser_canvas_item_get_type())
+#define BROWSER_CANVAS_ITEM(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_item_get_type(), BrowserCanvasItem)
+#define BROWSER_CANVAS_ITEM_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_item_get_type (), BrowserCanvasItemClass)
+#define IS_BROWSER_CANVAS_ITEM(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_item_get_type ())
+
+/* struct for the object's data */
+struct _BrowserCanvasItem
+{
+ GooCanvasGroup object;
+
+ BrowserCanvasItemPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasItemClass
+{
+ GooCanvasGroupClass parent_class;
+
+ /* signals */
+ void (*moved) (BrowserCanvasItem *citem);
+ void (*moving) (BrowserCanvasItem *citem);
+ void (*shifted) (BrowserCanvasItem *citem);
+ void (*destroy) (BrowserCanvasItem *citem);
+
+ /* virtual functions */
+ void (*extra_event) (BrowserCanvasItem *citem, GdkEventType event_type);
+ void (*get_edge_nodes)(BrowserCanvasItem *citem, BrowserCanvasItem **from, BrowserCanvasItem **to);
+ void (*drag_data_get) (BrowserCanvasItem *citem, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info, guint time);
+};
+
+GType browser_canvas_item_get_type (void) G_GNUC_CONST;
+
+void browser_canvas_item_get_edge_nodes (BrowserCanvasItem *item,
+ BrowserCanvasItem **from, BrowserCanvasItem **to);
+void browser_canvas_item_translate (BrowserCanvasItem *item, gdouble dx, gdouble dy);
+BrowserCanvas *browser_canvas_item_get_canvas (BrowserCanvasItem *item);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-print.c b/tools/browser/canvas/browser-canvas-print.c
new file mode 100644
index 0000000..17ad7fa
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-print.c
@@ -0,0 +1,456 @@
+/* browser-canvas-print.c
+ *
+ * Copyright (C) 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include "browser-canvas.h"
+#include "browser-canvas-priv.h"
+#include <cairo.h>
+#include <goocanvas.h>
+
+typedef struct {
+ BrowserCanvas *canvas;
+ GtkPrintSettings *settings;
+ GtkPageSetup *page_setup;
+ gboolean show_page_numbers;
+
+ gdouble page_width, page_height;
+ gint h_npages, v_npages;
+ gdouble scale;
+} PrintPageData;
+
+static GObject *print_create_custom_widget_cb (GtkPrintOperation *operation, PrintPageData *pdata);
+static void print_begin (GtkPrintOperation *operation, GtkPrintContext *context, PrintPageData *pdata);
+static void print_end (GtkPrintOperation *operation, GtkPrintContext *context, PrintPageData *pdata);
+static void print_draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, PrintPageData *pdata);
+
+static GtkPrintSettings *print_settings = NULL;
+static GtkPageSetup *page_setup = NULL;
+static gboolean show_page_numbers = TRUE;
+
+/**
+ * browser_canvas_print
+ * @canvas: the #BrowserCanvas to print
+ *
+ * Prints @canvas using the GTK+ printing framework (displays printing options)
+ */
+void
+browser_canvas_print (BrowserCanvas *canvas)
+{
+ GtkPrintOperation *print;
+ GtkPrintOperationResult res;
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (canvas));
+ PrintPageData *pdata;
+
+ if (!print_settings)
+ print_settings = gtk_print_settings_new ();
+ if (!page_setup)
+ page_setup = gtk_page_setup_new ();
+
+ pdata = g_new0 (PrintPageData, 1);
+ pdata->canvas = canvas;
+ pdata->settings = print_settings;
+ pdata->page_setup = page_setup;
+ pdata->show_page_numbers = show_page_numbers;
+
+ print = gtk_print_operation_new ();
+
+ gtk_print_operation_set_print_settings (print, print_settings);
+ gtk_print_operation_set_default_page_setup (print, pdata->page_setup);
+
+ g_signal_connect (print, "create-custom-widget", G_CALLBACK (print_create_custom_widget_cb), pdata);
+ g_signal_connect (print, "begin_print", G_CALLBACK (print_begin), pdata);
+ g_signal_connect (print, "end_print", G_CALLBACK (print_end), pdata);
+ g_signal_connect (print, "draw_page", G_CALLBACK (print_draw_page), pdata);
+ gtk_print_operation_set_custom_tab_label (print, _("Page size and zoom"));
+ res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ (GtkWindow*) toplevel, NULL);
+
+ if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+ g_object_unref (print_settings);
+ print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
+ if (page_setup != pdata->page_setup) {
+ g_object_unref (page_setup);
+ page_setup = pdata->page_setup;
+ }
+ show_page_numbers = pdata->show_page_numbers;
+ }
+ else if (page_setup != pdata->page_setup)
+ g_object_unref (pdata->page_setup);
+
+ g_object_unref (print);
+ g_free (pdata);
+}
+
+static void
+print_begin (GtkPrintOperation *operation, GtkPrintContext *context, PrintPageData *pdata)
+{
+ gtk_print_operation_set_n_pages (operation, pdata->h_npages * pdata->v_npages);
+ gtk_print_operation_set_default_page_setup (operation, pdata->page_setup);
+}
+
+static void
+print_end (GtkPrintOperation *operation, GtkPrintContext *context, PrintPageData *pdata)
+{
+
+}
+
+static void
+print_draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, PrintPageData *pdata)
+{
+ cairo_t *cr;
+ GooCanvasBounds bounds, canvas_bounds;
+ gint col_page, line_page;
+#define DRAW_DEBUG
+#undef DRAW_DEBUG
+
+ cr = gtk_print_context_get_cairo_context (context);
+
+ line_page = page_nr / pdata->h_npages;
+ col_page = page_nr % pdata->h_npages;
+
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (pdata->canvas->priv->goocanvas), &canvas_bounds);
+
+ /*g_print ("Printing page col%d line%d\n", col_page, line_page);*/
+
+#ifdef DRAW_DEBUG
+ cairo_save (cr);
+ /* X axis */
+ cairo_set_source_rgba (cr, 0., 1., 0., 0.8);
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 10.);
+ cairo_move_to (cr, 50., -2.);
+ cairo_show_text (cr, "X");
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, 0., 0.);
+ cairo_line_to (cr, 50., 0.);
+ cairo_rel_line_to (cr, -10., 10.);
+ cairo_stroke (cr);
+ cairo_move_to (cr, 50., 0.);
+ cairo_rel_line_to (cr, -10., -10.);
+ cairo_stroke (cr);
+
+ /* Y axis */
+ cairo_set_source_rgba (cr, 0., 0., 1., 0.8);
+
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 10.);
+ cairo_move_to (cr, -12., 55.);
+ cairo_show_text (cr, "Y");
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, 0., 0.);
+ cairo_line_to (cr, 0., 50.);
+ cairo_rel_line_to (cr, 10., -10.);
+ cairo_stroke (cr);
+ cairo_move_to (cr, 0., 50.);
+ cairo_rel_line_to (cr, -10., -10.);
+ cairo_stroke (cr);
+
+ cairo_rectangle (cr, 0, 0, gtk_print_context_get_width (context), gtk_print_context_get_height (context));
+ cairo_set_source_rgba (cr, 0., 0., 0., 1.);
+ cairo_set_line_width (cr, 0.5);
+ gdouble dash_length = 2.;
+ cairo_set_dash (cr, &dash_length, 1, 0.);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+#endif
+
+ if (pdata->show_page_numbers) {
+ cairo_text_extents_t extents;
+ gchar *str = g_strdup_printf (_("Page %d of %d horizontally and %d of %d vertically"),
+ col_page + 1, pdata->h_npages,
+ line_page + 1, pdata->v_npages);
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 10.);
+ cairo_text_extents (cr, str, &extents);
+ cairo_move_to (cr, gtk_print_context_get_width (context) - extents.width - extents.x_bearing,
+ gtk_print_context_get_height (context) - extents.height - extents.y_bearing);
+ cairo_show_text (cr, str);
+ g_free (str);
+ cairo_stroke (cr);
+ }
+
+ bounds.x1 = col_page * pdata->page_width + canvas_bounds.x1;
+ bounds.x2 = bounds.x1 + pdata->page_width;
+ bounds.y1 = line_page * pdata->page_height + canvas_bounds.y1;
+ bounds.y2 = bounds.y1 + pdata->page_height;
+
+ cairo_scale (cr, pdata->scale, pdata->scale);
+ cairo_translate (cr, - bounds.x1, - bounds.y1);
+ goo_canvas_render (pdata->canvas->priv->goocanvas, cr, &bounds, .8);
+ /*
+ g_print ("Scale %.2f, cairo's bounds: (%.2fx%.2f) => (%.2fx%.2f), canvas's bounds: (%.2fx%.2f) => (%.2fx%.2f)\n",
+ pdata->scale, bounds.x1, bounds.y1, bounds.x2, bounds.y2,
+ canvas_bounds.x1, canvas_bounds.y1, canvas_bounds.x2, canvas_bounds.y2);
+ */
+}
+
+typedef struct {
+ PrintPageData *pdata;
+ GtkSpinButton *zoom;
+ GtkSpinButton *h_npages;
+ GtkSpinButton *v_npages;
+} PrintCustomData;
+
+static void print_page_setup_cb (GtkWidget *button, PrintCustomData *cdata);
+static void print_h_npages_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata);
+static void print_v_npages_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata);
+static void print_zoom_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata);
+static void print_page_numbers_toggled_cb (GtkToggleButton *toggle, PrintCustomData *cdata);
+
+static GObject *
+print_create_custom_widget_cb (GtkPrintOperation *operation, PrintPageData *pdata)
+{
+ GtkWidget *vbox, *bbox, *button, *label, *hbox, *table, *entry;
+ PrintCustomData *cdata;
+
+ cdata = g_new0 (PrintCustomData, 1);
+ cdata->pdata = pdata;
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+
+ /* page size's adjustments */
+ bbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
+
+ button = gtk_button_new_with_label (_("Adjust page's size and orientation"));
+ g_signal_connect (button, "clicked", G_CALLBACK (print_page_setup_cb), cdata);
+ gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+
+ /* zoom control */
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), _("<b>Zoom</b>"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 10);
+
+ hbox = gtk_hbox_new (FALSE, 0); /* HIG */
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
+
+ label = gtk_label_new (_("Number of pages used:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ entry = gtk_spin_button_new_with_range (1., 100., 1.);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 0);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1, GTK_FILL, 0, 5, 0);
+ cdata->h_npages = (GtkSpinButton*) entry;
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (print_h_npages_value_changed_cb), cdata);
+
+ label = gtk_label_new (_("horizontally"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
+
+ entry = gtk_spin_button_new_with_range (1., 100., 1.);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 0);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2, GTK_FILL, 0, 5, 0);
+ cdata->v_npages = (GtkSpinButton*) entry;
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (print_v_npages_value_changed_cb), cdata);
+
+ label = gtk_label_new (_("vertically"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new (_("Zoom factor:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+
+ entry = gtk_spin_button_new_with_range (.1, 10., .05);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 2);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 2, 3, GTK_FILL, 0, 5, 0);
+ cdata->zoom = (GtkSpinButton*) entry;
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (print_zoom_value_changed_cb), cdata);
+
+ /* misc options */
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), _("<b>Page numbers</b>"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 10);
+
+ hbox = gtk_hbox_new (FALSE, 0); /* HIG */
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ bbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
+
+ button = gtk_check_button_new_with_label (_("Print page numbers"));
+ g_signal_connect (button, "toggled", G_CALLBACK (print_page_numbers_toggled_cb), cdata);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cdata->pdata->show_page_numbers);
+ gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
+
+ /* correct start state */
+ gtk_widget_show_all (vbox);
+ g_object_set_data_full (G_OBJECT (vbox), "cdata", cdata, g_free);
+
+ /* default zoom to 1 */
+ gtk_spin_button_set_value (cdata->zoom, 1.);
+
+ return G_OBJECT (vbox);
+}
+
+static void
+print_page_numbers_toggled_cb (GtkToggleButton *toggle, PrintCustomData *cdata)
+{
+ cdata->pdata->show_page_numbers = gtk_toggle_button_get_active (toggle);
+}
+
+static void
+print_page_setup_cb (GtkWidget *button, PrintCustomData *cdata)
+{
+ GtkPageSetup *setup;
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+
+ setup = cdata->pdata->page_setup;
+ cdata->pdata->page_setup = gtk_print_run_page_setup_dialog ((GtkWindow *) toplevel,
+ cdata->pdata->page_setup, cdata->pdata->settings);
+ if ((cdata->pdata->page_setup != setup) && (setup != page_setup))
+ g_object_unref (setup);
+
+ print_zoom_value_changed_cb (cdata->zoom, cdata);
+}
+
+static void
+print_h_npages_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata)
+{
+ gdouble page_width, page_height;
+ GooCanvasBounds bounds;
+ gdouble canvas_height;
+ gdouble zoom;
+ gint h_npages, v_npages;
+
+ h_npages = (gint) gtk_spin_button_get_value (entry);
+ page_width = gtk_page_setup_get_page_width (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+ page_height = gtk_page_setup_get_page_height (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (cdata->pdata->canvas->priv->goocanvas), &bounds);
+ zoom = (gdouble) h_npages * page_width / (bounds.x2 - bounds.x1);
+
+ canvas_height = (bounds.y2 - bounds.y1) * zoom;
+ v_npages = (gint) (canvas_height / page_height + 1.);
+
+ g_signal_handlers_block_by_func (cdata->zoom, G_CALLBACK (print_zoom_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->zoom, zoom);
+ g_signal_handlers_unblock_by_func (cdata->zoom, G_CALLBACK (print_zoom_value_changed_cb), cdata);
+ g_signal_handlers_block_by_func (cdata->v_npages, G_CALLBACK (print_v_npages_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->v_npages, v_npages);
+ g_signal_handlers_unblock_by_func (cdata->v_npages, G_CALLBACK (print_v_npages_value_changed_cb), cdata);
+
+ cdata->pdata->scale = zoom;
+ cdata->pdata->page_width = page_width / zoom;
+ cdata->pdata->page_height = page_height / zoom;
+ cdata->pdata->h_npages = h_npages;
+ cdata->pdata->v_npages = v_npages;
+
+ /*
+ g_print ("Pages: %d/%d Page:%.2f/%.2f\n", cdata->pdata->h_npages, cdata->pdata->v_npages,
+ cdata->pdata->page_width, cdata->pdata->page_height);
+ */
+}
+
+static void
+print_v_npages_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata)
+{
+ gdouble page_width, page_height;
+ GooCanvasBounds bounds;
+ gdouble canvas_width;
+ gdouble zoom;
+ gint h_npages, v_npages;
+
+ v_npages = (gint) gtk_spin_button_get_value (entry);
+ page_width = gtk_page_setup_get_page_width (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+ page_height = gtk_page_setup_get_page_height (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (cdata->pdata->canvas->priv->goocanvas), &bounds);
+ zoom = (gdouble) v_npages * page_height / (bounds.y2 - bounds.y1);
+
+ canvas_width = (bounds.x2 - bounds.x1) * zoom;
+ h_npages = (gint) (canvas_width / page_width + 1.);
+
+ g_signal_handlers_block_by_func (cdata->zoom, G_CALLBACK (print_zoom_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->zoom, zoom);
+ g_signal_handlers_unblock_by_func (cdata->zoom, G_CALLBACK (print_zoom_value_changed_cb), cdata);
+ g_signal_handlers_block_by_func (cdata->h_npages, G_CALLBACK (print_h_npages_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->h_npages, h_npages);
+ g_signal_handlers_unblock_by_func (cdata->h_npages, G_CALLBACK (print_h_npages_value_changed_cb), cdata);
+
+ cdata->pdata->scale = zoom;
+ cdata->pdata->page_width = page_width / zoom;
+ cdata->pdata->page_height = page_height / zoom;
+ cdata->pdata->h_npages = h_npages;
+ cdata->pdata->v_npages = v_npages;
+
+ /*
+ g_print ("Pages: %d/%d Page:%.2f/%.2f\n", cdata->pdata->h_npages, cdata->pdata->v_npages,
+ cdata->pdata->page_width, cdata->pdata->page_height);
+ */
+}
+
+static void
+print_zoom_value_changed_cb (GtkSpinButton *entry, PrintCustomData *cdata)
+{
+ gdouble page_width, page_height;
+ GooCanvasBounds bounds;
+ gdouble canvas_width, canvas_height;
+ gdouble zoom;
+ gint h_npages, v_npages;
+
+ zoom = gtk_spin_button_get_value (entry);
+ page_width = gtk_page_setup_get_page_width (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+ page_height = gtk_page_setup_get_page_height (cdata->pdata->page_setup, GTK_UNIT_POINTS);
+
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (cdata->pdata->canvas->priv->goocanvas), &bounds);
+ canvas_width = (bounds.x2 - bounds.x1) * zoom;
+ canvas_height = (bounds.y2 - bounds.y1) * zoom;
+ h_npages = (gint) (canvas_width / page_width + 1.);
+ v_npages = (gint) (canvas_height / page_height + 1.);
+
+ g_signal_handlers_block_by_func (cdata->h_npages, G_CALLBACK (print_h_npages_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->h_npages, h_npages);
+ g_signal_handlers_unblock_by_func (cdata->h_npages, G_CALLBACK (print_h_npages_value_changed_cb), cdata);
+ g_signal_handlers_block_by_func (cdata->v_npages, G_CALLBACK (print_v_npages_value_changed_cb), cdata);
+ gtk_spin_button_set_value (cdata->v_npages, v_npages);
+ g_signal_handlers_unblock_by_func (cdata->v_npages, G_CALLBACK (print_v_npages_value_changed_cb), cdata);
+
+ cdata->pdata->scale = zoom;
+ cdata->pdata->page_width = page_width / zoom;
+ cdata->pdata->page_height = page_height / zoom;
+ cdata->pdata->h_npages = h_npages;
+ cdata->pdata->v_npages = v_npages;
+
+ /*
+ g_print ("Pages: %d/%d Page:%.2f/%.2f\n", cdata->pdata->h_npages, cdata->pdata->v_npages,
+ cdata->pdata->page_width, cdata->pdata->page_height);
+ */
+}
+
diff --git a/tools/browser/canvas/browser-canvas-print.h b/tools/browser/canvas/browser-canvas-print.h
new file mode 100644
index 0000000..1fd39f8
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-print.h
@@ -0,0 +1,27 @@
+/* browser-canvas-print.h
+ *
+ * Copyright (C) 2007 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <goocanvas.h>
+
+G_BEGIN_DECLS
+
+void browser_canvas_print (BrowserCanvas *canvas);
+
+G_END_DECLS
diff --git a/tools/browser/canvas/browser-canvas-priv.h b/tools/browser/canvas/browser-canvas-priv.h
new file mode 100644
index 0000000..989cb31
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-priv.h
@@ -0,0 +1,36 @@
+/* browser-canvas.h
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_PRIV__
+#define __BROWSER_CANVAS_PRIV__
+
+#include <goocanvas.h>
+
+G_BEGIN_DECLS
+
+struct _BrowserCanvasPrivate
+{
+ GooCanvas *goocanvas;
+ GSList *items; /* BrowserCanvasItem objects, non ordered */
+};
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-table.c b/tools/browser/canvas/browser-canvas-table.c
new file mode 100644
index 0000000..7c2d05a
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-table.c
@@ -0,0 +1,500 @@
+/* browser-canvas-table.c
+ *
+ * Copyright (C) 2002 - 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "browser-canvas.h"
+#include "browser-canvas-table.h"
+#include "browser-canvas-column.h"
+#include <glib/gi18n-lib.h>
+
+static void browser_canvas_table_class_init (BrowserCanvasTableClass *class);
+static void browser_canvas_table_init (BrowserCanvasTable *drag);
+static void browser_canvas_table_dispose (GObject *object);
+static void browser_canvas_table_finalize (GObject *object);
+
+static void browser_canvas_table_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_table_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void browser_canvas_table_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info, guint time);
+
+enum
+{
+ PROP_0,
+ PROP_TABLE,
+ PROP_MENU_FUNC
+};
+
+struct _BrowserCanvasTablePrivate
+{
+ GdaMetaTable *table;
+
+ /* UI building information */
+ GSList *column_items; /* list of GooCanvasItem for the columns */
+ GSList *other_items; /* list of GooCanvasItem for other purposes */
+ gdouble *column_ypos; /* array for each column's Y position in this canvas group */
+ GtkWidget *(*popup_menu_func) (BrowserCanvasTable *ce);
+
+ /* presentation parameters */
+ gdouble x_text_space;
+ gdouble y_text_space;
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *table_parent_class = NULL;
+
+GType
+browser_canvas_table_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasTableClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_table_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasTable),
+ 0,
+ (GInstanceInitFunc) browser_canvas_table_init
+ };
+
+ type = g_type_register_static (TYPE_BROWSER_CANVAS_ITEM, "BrowserCanvasTable", &info, 0);
+ }
+
+ return type;
+}
+
+
+static void
+browser_canvas_table_class_init (BrowserCanvasTableClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (class);
+
+ table_parent_class = g_type_class_peek_parent (class);
+ iclass->drag_data_get = browser_canvas_table_drag_data_get;
+
+ object_class->dispose = browser_canvas_table_dispose;
+ object_class->finalize = browser_canvas_table_finalize;
+
+ /* Properties */
+ object_class->set_property = browser_canvas_table_set_property;
+ object_class->get_property = browser_canvas_table_get_property;
+
+ g_object_class_install_property
+ (object_class, PROP_TABLE,
+ g_param_spec_pointer ("table", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (object_class, PROP_MENU_FUNC,
+ g_param_spec_pointer ("popup_menu_func", "Popup menu function",
+ "Function to create a popup menu on each BrowserCanvasTable",
+ G_PARAM_WRITABLE));
+}
+
+static gboolean button_press_event_cb (BrowserCanvasTable *ce, GooCanvasItem *target_item, GdkEventButton *event,
+ gpointer unused_data);
+
+static void
+browser_canvas_table_init (BrowserCanvasTable *table)
+{
+ table->priv = g_new0 (BrowserCanvasTablePrivate, 1);
+ table->priv->table = NULL;
+ table->priv->column_ypos = NULL;
+ table->priv->popup_menu_func = NULL;
+
+ table->priv->x_text_space = 3.;
+ table->priv->y_text_space = 3.;
+
+ g_signal_connect (G_OBJECT (table), "button-press-event",
+ G_CALLBACK (button_press_event_cb), NULL);
+}
+
+static void clean_items (BrowserCanvasTable *ce);
+static void create_items (BrowserCanvasTable *ce);
+
+static void
+browser_canvas_table_dispose (GObject *object)
+{
+ BrowserCanvasTable *ce;
+
+ g_return_if_fail (IS_BROWSER_CANVAS_TABLE (object));
+
+ ce = BROWSER_CANVAS_TABLE (object);
+
+ /* REM: let the GooCanvas library destroy the items itself */
+ ce->priv->table = NULL;
+
+ /* for the parent class */
+ table_parent_class->dispose (object);
+}
+
+
+static void
+browser_canvas_table_finalize (GObject * object)
+{
+ BrowserCanvasTable *ce;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_TABLE (object));
+
+ ce = BROWSER_CANVAS_TABLE (object);
+ if (ce->priv) {
+ g_slist_free (ce->priv->column_items);
+ g_slist_free (ce->priv->other_items);
+ if (ce->priv->column_ypos)
+ g_free (ce->priv->column_ypos);
+
+ g_free (ce->priv);
+ ce->priv = NULL;
+ }
+
+ /* for the parent class */
+ table_parent_class->finalize (object);
+}
+
+static void
+browser_canvas_table_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasTable *ce = NULL;
+
+ ce = BROWSER_CANVAS_TABLE (object);
+
+ switch (param_id) {
+ case PROP_TABLE: {
+ GdaMetaTable *table;
+ table = g_value_get_pointer (value);
+ if (table && (table == ce->priv->table))
+ return;
+
+ if (ce->priv->table) {
+ ce->priv->table = NULL;
+ clean_items (ce);
+ }
+
+ if (table) {
+ ce->priv->table = (GdaMetaTable*) table;
+ create_items (ce);
+ }
+ break;
+ }
+ case PROP_MENU_FUNC:
+ ce->priv->popup_menu_func = (GtkWidget *(*) (BrowserCanvasTable *ce)) g_value_get_pointer (value);
+ break;
+ }
+}
+
+static void
+browser_canvas_table_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasTable *ce = NULL;
+
+ ce = BROWSER_CANVAS_TABLE (object);
+
+ switch (param_id) {
+ case PROP_TABLE:
+ g_value_set_pointer (value, ce->priv->table);
+ break;
+ }
+}
+
+/*
+ * destroy any existing GooCanvasItem obejcts
+ */
+static void
+clean_items (BrowserCanvasTable *ce)
+{
+ GSList *list;
+ /* destroy all the items in the group */
+ while (ce->priv->column_items)
+ g_object_unref (G_OBJECT (ce->priv->column_items->data));
+
+ for (list = ce->priv->other_items; list; list = list->next)
+ g_object_unref (G_OBJECT (list->data));
+ g_slist_free (ce->priv->other_items);
+ ce->priv->other_items = NULL;
+
+ /* free the columns positions */
+ if (ce->priv->column_ypos) {
+ g_free (ce->priv->column_ypos);
+ ce->priv->column_ypos = NULL;
+ }
+}
+
+/*
+ * create new GooCanvasItem objects
+ */
+static void
+create_items (BrowserCanvasTable *ce)
+{
+ GooCanvasItem *item, *frame, *title;
+ gdouble y, ysep;
+#define HEADER_Y_PAD 3.
+#define Y_PAD 0.
+#define X_PAD 3.
+#define RADIUS_X 5.
+#define RADIUS_Y 5.
+#define MIN_HEIGHT 70.
+ GooCanvasBounds border_bounds;
+ GooCanvasBounds bounds;
+ const gchar *cstr;
+ gchar *tmpstr = NULL;
+ GSList *columns, *list;
+ gint column_nb;
+ gdouble column_width;
+
+ clean_items (ce);
+ g_assert (ce->priv->table);
+
+ /* title */
+ cstr = GDA_META_DB_OBJECT (ce->priv->table)->obj_short_name;
+ if (cstr)
+ tmpstr = g_markup_printf_escaped ("<b>%s</b>", cstr);
+ else
+ tmpstr = g_strdup_printf ("<b>%s</b>", _("No name"));
+
+ y = RADIUS_Y;
+ title = goo_canvas_text_new (GOO_CANVAS_ITEM (ce), tmpstr,
+ RADIUS_X + X_PAD, y,
+ -1, GTK_ANCHOR_NORTH_WEST,
+ "font", "Sans 11",
+ "use-markup", TRUE, NULL);
+
+ g_free (tmpstr);
+ goo_canvas_item_get_bounds (title, &bounds);
+ border_bounds = bounds;
+ border_bounds.x1 = 0.;
+ border_bounds.y1 = 0.;
+ y += bounds.y2 - bounds.y1 + HEADER_Y_PAD;
+
+ /* separator's placeholder */
+ ysep = y;
+ y += HEADER_Y_PAD;
+
+ /* columns' vertical position */
+ columns = ce->priv->table->columns;
+ ce->priv->column_ypos = g_new0 (gdouble, g_slist_length (columns) + 1);
+
+ /* columns */
+ for (column_nb = 0, list = columns; list; list = list->next, column_nb++) {
+ ce->priv->column_ypos [column_nb] = y;
+ item = browser_canvas_column_new (GOO_CANVAS_ITEM (ce),
+ GDA_META_TABLE_COLUMN (list->data),
+ X_PAD, ce->priv->column_ypos [column_nb], NULL);
+ ce->priv->column_items = g_slist_append (ce->priv->column_items, item);
+ goo_canvas_item_get_bounds (item, &bounds);
+ border_bounds.x1 = MIN (border_bounds.x1, bounds.x1);
+ border_bounds.x2 = MAX (border_bounds.x2, bounds.x2);
+ border_bounds.y1 = MIN (border_bounds.y1, bounds.y1);
+ border_bounds.y2 = MAX (border_bounds.y2, bounds.y2);
+
+ y += bounds.y2 - bounds.y1 + Y_PAD;
+ }
+ if (!columns && (border_bounds.y2 - border_bounds.y1 < MIN_HEIGHT))
+ border_bounds.y2 += MIN_HEIGHT - (border_bounds.y2 - border_bounds.y1);
+
+ /* border */
+ column_width = border_bounds.x2 - border_bounds.x1;
+ border_bounds.y2 += RADIUS_Y;
+ border_bounds.x2 += RADIUS_X;
+ frame = goo_canvas_rect_new (GOO_CANVAS_ITEM (ce), border_bounds.x1, border_bounds.y1,
+ border_bounds.x2, border_bounds.y2,
+ "radius-x", RADIUS_X,
+ "radius-y", RADIUS_Y,
+ "fill-color", "#f8f8f8",
+ NULL);
+ ce->priv->other_items = g_slist_prepend (ce->priv->other_items, frame);
+
+ /* title's background */
+ gchar *cpath;
+ cpath = g_strdup_printf ("M %d %d H %d V %d H %d Z",
+ (gint) border_bounds.x1, (gint) border_bounds.y1,
+ (gint) border_bounds.x2, (gint) ysep,
+ (gint) border_bounds.x1);
+ item = goo_canvas_rect_new (GOO_CANVAS_ITEM (ce), border_bounds.x1, border_bounds.y1,
+ border_bounds.x2, ysep + RADIUS_X,
+ "clip_path", cpath,
+ "radius-x", RADIUS_X,
+ "radius-y", RADIUS_Y,
+ "fill-color", "#aaaaff",
+ NULL);
+ g_free (cpath);
+ goo_canvas_item_lower (item, NULL);
+
+ /* separator */
+ item = goo_canvas_polyline_new_line (GOO_CANVAS_ITEM (ce), border_bounds.x1, ysep, border_bounds.x2, ysep,
+ "close-path", FALSE,
+ "line-width", .7, NULL);
+ ce->priv->other_items = g_slist_prepend (ce->priv->other_items, item);
+
+ goo_canvas_item_lower (frame, NULL);
+
+ /* setting the columns' background width to be the same for all */
+ for (list = ce->priv->column_items; list; list = list->next)
+ g_object_set (G_OBJECT (list->data), "width", column_width, NULL);
+}
+
+static gboolean
+button_press_event_cb (BrowserCanvasTable *ce, GooCanvasItem *target_item, GdkEventButton *event,
+ gpointer unused_data)
+{
+ if (ce->priv->popup_menu_func) {
+ GtkWidget *menu;
+ menu = ce->priv->popup_menu_func (ce);
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ NULL, NULL, ((GdkEventButton *)event)->button,
+ ((GdkEventButton *)event)->time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * browser_canvas_table_get_column_item
+ * @ce: a #BrowserCanvasTable object
+ * @column: a #GdaMetaTableColumn object
+ *
+ * Get the #BrowserCanvasColumn object representing @column
+ * in @ce.
+ *
+ * Returns: the corresponding #BrowserCanvasColumn
+ */
+BrowserCanvasColumn *
+browser_canvas_table_get_column_item (BrowserCanvasTable *ce, GdaMetaTableColumn *column)
+{
+ gint pos;
+
+ g_return_val_if_fail (ce && IS_BROWSER_CANVAS_TABLE (ce), NULL);
+ g_return_val_if_fail (ce->priv, NULL);
+ g_return_val_if_fail (ce->priv->table, NULL);
+
+ pos = g_slist_index (ce->priv->table->columns, column);
+ g_return_val_if_fail (pos >= 0, NULL);
+
+ return g_slist_nth_data (ce->priv->column_items, pos);
+}
+
+
+/**
+ * browser_canvas_table_get_column_ypos
+ * @ce: a #BrowserCanvasTable object
+ * @column: a #GdaMetaTableColumn object
+ *
+ * Get the Y position of the middle of the #BrowserCanvasColumn object representing @column
+ * in @ce, in @ce's coordinates.
+ *
+ * Returns: the Y coordinate.
+ */
+gdouble
+browser_canvas_table_get_column_ypos (BrowserCanvasTable *ce, GdaMetaTableColumn *column)
+{
+ gint pos;
+
+ g_return_val_if_fail (ce && IS_BROWSER_CANVAS_TABLE (ce), 0.);
+ g_return_val_if_fail (ce->priv, 0.);
+ g_return_val_if_fail (ce->priv->table, 0.);
+ g_return_val_if_fail (ce->priv->column_ypos, 0.);
+
+ pos = g_slist_index (ce->priv->table->columns, column);
+ g_return_val_if_fail (pos >= 0, 0.);
+ return (0.75 * ce->priv->column_ypos[pos+1] + 0.25 * ce->priv->column_ypos[pos]);
+}
+
+
+/**
+ * browser_canvas_table_new
+ * @parent: the parent item, or NULL.
+ * @table: a #GdaMetaTable to display
+ * @x: the x coordinate
+ * @y: the y coordinate
+ * @...: optional pairs of property names and values, and a terminating NULL.
+ *
+ * Creates a new canvas item to display the @table table
+ *
+ * Returns: a new #GooCanvasItem object
+ */
+GooCanvasItem *
+browser_canvas_table_new (GooCanvasItem *parent, GdaMetaTable *table,
+ gdouble x, gdouble y, ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_TABLE, "allow-move", TRUE, NULL);
+
+ if (parent) {
+ goo_canvas_item_add_child (parent, item, -1);
+ g_object_unref (item);
+ }
+
+ g_object_set (item, "table", table, NULL);
+
+ va_start (var_args, y);
+ first_property = va_arg (var_args, char*);
+ if (first_property)
+ g_object_set_valist ((GObject*) item, first_property, var_args);
+ va_end (var_args);
+
+ goo_canvas_item_translate (item, x, y);
+
+ return item;
+}
+
+static void
+browser_canvas_table_drag_data_get (BrowserCanvasItem *citem, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info, guint time)
+{
+ BrowserCanvasTable *ctable;
+
+ ctable = BROWSER_CANVAS_TABLE (citem);
+ if (!ctable->priv->table)
+ return;
+
+ GdaMetaDbObject *dbo;
+ gchar *str, *tmp1, *tmp2, *tmp3;
+
+ dbo = GDA_META_DB_OBJECT (ctable->priv->table);
+ tmp1 = gda_rfc1738_encode (dbo->obj_schema);
+ tmp2 = gda_rfc1738_encode (dbo->obj_name);
+ tmp3 = gda_rfc1738_encode (dbo->obj_short_name);
+ str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s", tmp1, tmp2, tmp3);
+ g_free (tmp1);
+ g_free (tmp2);
+ g_free (tmp3);
+ gtk_selection_data_set (data, data->target, 8, str, strlen (str));
+ g_free (str);
+}
diff --git a/tools/browser/canvas/browser-canvas-table.h b/tools/browser/canvas/browser-canvas-table.h
new file mode 100644
index 0000000..7e90145
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-table.h
@@ -0,0 +1,66 @@
+/* browser-canvas-table.h
+ *
+ * Copyright (C) 2007 - 2008 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_TABLE__
+#define __BROWSER_CANVAS_TABLE__
+
+#include "browser-canvas-item.h"
+#include "browser-canvas-decl.h"
+#include <libgda/gda-meta-struct.h>
+
+G_BEGIN_DECLS
+
+/*
+ *
+ * "Drag item" GooCanvas item: a BrowserCanvasItem item which is used to represent
+ * an element being dragged, and destroys itself when the mouse button is released
+ *
+ */
+
+#define TYPE_BROWSER_CANVAS_TABLE (browser_canvas_table_get_type())
+#define BROWSER_CANVAS_TABLE(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_table_get_type(), BrowserCanvasTable)
+#define BROWSER_CANVAS_TABLE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_table_get_type (), BrowserCanvasTableClass)
+#define IS_BROWSER_CANVAS_TABLE(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_table_get_type ())
+
+
+/* struct for the object's data */
+struct _BrowserCanvasTable
+{
+ BrowserCanvasItem object;
+
+ BrowserCanvasTablePrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasTableClass
+{
+ BrowserCanvasItemClass parent_class;
+};
+
+/* generic widget's functions */
+GType browser_canvas_table_get_type (void) G_GNUC_CONST;
+GooCanvasItem *browser_canvas_table_new (GooCanvasItem *parent, GdaMetaTable *table,
+ gdouble x, gdouble y, ...);
+BrowserCanvasColumn *browser_canvas_table_get_column_item (BrowserCanvasTable *ce, GdaMetaTableColumn *column);
+gdouble browser_canvas_table_get_column_ypos (BrowserCanvasTable *ce, GdaMetaTableColumn *column);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-text.c b/tools/browser/canvas/browser-canvas-text.c
new file mode 100644
index 0000000..9e5dbba
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-text.c
@@ -0,0 +1,527 @@
+/* browser-canvas-text.c
+ *
+ * Copyright (C) 2002 - 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <libgda/libgda.h>
+#include "browser-canvas.h"
+#include "browser-canvas-text.h"
+
+static void browser_canvas_text_class_init (BrowserCanvasTextClass * class);
+static void browser_canvas_text_init (BrowserCanvasText *text);
+static void browser_canvas_text_dispose (GObject * object);
+static void browser_canvas_text_finalize (GObject * object);
+
+static void browser_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gboolean enter_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct);
+static gboolean leave_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct);
+
+enum
+{
+ PROP_0,
+ PROP_TEXT,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_HIGHLIGHT_COLOR,
+ PROP_UNDERLINE,
+ PROP_BOLD
+};
+
+struct _BrowserCanvasTextPrivate
+{
+ gchar *text;
+
+ /* properties */
+ gboolean underline;
+ gboolean bold;
+ gchar *highlight_color;
+
+ /* UI building information */
+ GooCanvasItem *bg_item;
+ GooCanvasItem *text_item;
+
+ /* animation */
+ guint anim_id;
+ guint current_anim_rgba;
+ guint end_anim_rgba;
+
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *text_parent_class = NULL;
+
+GType
+browser_canvas_text_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasTextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_text_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvasText),
+ 0,
+ (GInstanceInitFunc) browser_canvas_text_init
+ };
+
+ type = g_type_register_static (TYPE_BROWSER_CANVAS_ITEM, "BrowserCanvasText", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+browser_canvas_text_class_init (BrowserCanvasTextClass * class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ text_parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = browser_canvas_text_dispose;
+ object_class->finalize = browser_canvas_text_finalize;
+
+ /* Properties */
+ object_class->set_property = browser_canvas_text_set_property;
+ object_class->get_property = browser_canvas_text_get_property;
+
+ g_object_class_install_property
+ (object_class, PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL, 0., G_MAXDOUBLE, 0., G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL, 0., G_MAXDOUBLE, 0., G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_TEXT,
+ g_param_spec_string ("text", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (object_class, PROP_HIGHLIGHT_COLOR,
+ g_param_spec_string ("highlight_color", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (object_class, PROP_UNDERLINE,
+ g_param_spec_boolean ("text_underline", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (object_class, PROP_BOLD,
+ g_param_spec_boolean ("text_bold", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+browser_canvas_text_init (BrowserCanvasText *text)
+{
+ text->priv = g_new0 (BrowserCanvasTextPrivate, 1);
+ text->priv->text = NULL;
+ text->priv->highlight_color = g_strdup (BROWSER_CANVAS_ENTITY_COLOR);
+
+ g_signal_connect (G_OBJECT (text), "enter-notify-event",
+ G_CALLBACK (enter_notify_cb), text);
+ g_signal_connect (G_OBJECT (text), "leave-notify-event",
+ G_CALLBACK (leave_notify_cb), text);
+}
+
+static void
+browser_canvas_text_dispose (GObject *object)
+{
+ BrowserCanvasText *ct;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_TEXT (object));
+
+ ct = BROWSER_CANVAS_TEXT (object);
+
+ /* animation */
+ if (ct->priv->anim_id) {
+ g_source_remove (ct->priv->anim_id);
+ ct->priv->anim_id = 0;
+ }
+
+ /* for the parent class */
+ text_parent_class->dispose (object);
+}
+
+
+static void
+browser_canvas_text_finalize (GObject *object)
+{
+ BrowserCanvasText *ct;
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS_TEXT (object));
+
+ ct = BROWSER_CANVAS_TEXT (object);
+ if (ct->priv) {
+ g_free (ct->priv->text);
+
+ if (ct->priv->highlight_color)
+ g_free (ct->priv->highlight_color);
+
+ g_free (ct->priv);
+ ct->priv = NULL;
+ }
+
+ /* for the parent class */
+ text_parent_class->finalize (object);
+}
+
+static void clean_items (BrowserCanvasText *ct);
+static void create_items (BrowserCanvasText *ct);
+
+static void
+adjust_text_pango_attributes (BrowserCanvasText *ct)
+{
+ if (! ct->priv->text_item)
+ return;
+
+ if (ct->priv->bold || ct->priv->underline) {
+ gchar *str;
+ if (ct->priv->bold) {
+ if (ct->priv->underline)
+ str = g_strdup_printf ("<b><u>%s</u></b>", ct->priv->text);
+ else
+ str = g_strdup_printf ("<b>%s</b>", ct->priv->text);
+ }
+ else
+ str = g_strdup_printf ("<u>%s</u>", ct->priv->text);
+ g_object_set (G_OBJECT (ct->priv->text_item),
+ "text", str,
+ "use-markup", TRUE, NULL);
+ g_free (str);
+ }
+ else
+ g_object_set (G_OBJECT (ct->priv->text_item),
+ "text", ct->priv->text,
+ "use-markup", FALSE, NULL);
+}
+
+static void
+browser_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasText *ct = NULL;
+ const gchar *cstr = NULL;
+ gchar *str;
+ gdouble size = 0;
+ gboolean bool = FALSE;
+
+ ct = BROWSER_CANVAS_TEXT (object);
+
+ switch (param_id) {
+ case PROP_TEXT:
+ g_free (ct->priv->text);
+ ct->priv->text = NULL;
+ clean_items (ct);
+ ct->priv->text = g_strdup (g_value_get_string (value));
+ create_items (ct);
+ break;
+ case PROP_WIDTH:
+ size = g_value_get_double (value);
+ if (ct->priv->bg_item)
+ g_object_set (G_OBJECT (ct->priv->bg_item),
+ "width", size,
+ NULL);
+ break;
+ case PROP_HEIGHT:
+ size = g_value_get_double (value);
+ if (ct->priv->bg_item)
+ g_object_set (G_OBJECT (ct->priv->bg_item),
+ "height", size,
+ NULL);
+ break;
+ case PROP_HIGHLIGHT_COLOR:
+ cstr = g_value_get_string (value);
+ if (ct->priv->highlight_color) {
+ g_free (ct->priv->highlight_color);
+ ct->priv->highlight_color = NULL;
+ }
+ if (cstr)
+ ct->priv->highlight_color = g_strdup (cstr);
+ else
+ ct->priv->highlight_color = g_strdup (BROWSER_CANVAS_ENTITY_COLOR);
+ break;
+ case PROP_UNDERLINE:
+ bool = g_value_get_boolean (value);
+ ct->priv->underline = bool;
+ adjust_text_pango_attributes (ct);
+ if (ct->priv->text_item) {
+ if (bool) {
+ str = g_strdup_printf ("<u>%s</u>", ct->priv->text);
+ g_object_set (G_OBJECT (ct->priv->text_item),
+ "text", str,
+ "use-markup", TRUE, NULL);
+ g_free (str);
+ }
+ else
+ g_object_set (G_OBJECT (ct->priv->text_item),
+ "text", ct->priv->text,
+ "use-markup", FALSE, NULL);
+ }
+ case PROP_BOLD:
+ bool = g_value_get_boolean (value);
+ ct->priv->bold = bool;
+ adjust_text_pango_attributes (ct);
+ break;
+ }
+}
+
+static void
+browser_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasText *ct;
+
+ ct = BROWSER_CANVAS_TEXT (object);
+
+ switch (param_id) {
+ default:
+ g_warning ("No such property!");
+ break;
+ }
+}
+
+/*
+ * destroy any existing GooCanvasItem obejcts
+ */
+static void
+clean_items (BrowserCanvasText *ct)
+{
+ if (ct->priv->bg_item) {
+ goo_canvas_item_remove (GOO_CANVAS_ITEM (ct->priv->bg_item));
+ ct->priv->bg_item = NULL;
+ }
+ if (ct->priv->text_item) {
+ goo_canvas_item_remove (GOO_CANVAS_ITEM (ct->priv->text_item));
+ ct->priv->text_item = NULL;
+ }
+}
+
+/*
+ * create new GooCanvasItem objects
+ */
+static void
+create_items (BrowserCanvasText *ct)
+{
+ GooCanvasItem *item, *text;
+ GooCanvasBounds bounds;
+
+ g_object_set (G_OBJECT (ct),
+ "allow_move", FALSE,
+ NULL);
+
+ /* text: text's name */
+ text = goo_canvas_text_new (GOO_CANVAS_ITEM (ct), ct->priv->text,
+ 0., 0.,
+ -1, GTK_ANCHOR_NORTH_WEST,
+ "fill_color", "black",
+ "font", BROWSER_CANVAS_FONT,
+ "alignment", PANGO_ALIGN_RIGHT,
+ NULL);
+ ct->priv->text_item = text;
+
+ /* UI metrics */
+ goo_canvas_item_get_bounds (text, &bounds);
+
+ /* background */
+ item = goo_canvas_rect_new (GOO_CANVAS_ITEM (ct),
+ 0., 0.,
+ bounds.x2 - bounds.x1,
+ bounds.y2 - bounds.y1,
+ "fill_color", BROWSER_CANVAS_OBJ_BG_COLOR,
+ "radius-x", 2.,
+ "radius-y", 2.,
+ "stroke-pattern", NULL,
+ NULL);
+
+ ct->priv->bg_item = item;
+ goo_canvas_item_lower (item, NULL);
+
+ adjust_text_pango_attributes (ct);
+}
+
+static gboolean
+enter_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct)
+{
+ browser_canvas_text_set_highlight (ct, TRUE);
+ return FALSE;
+}
+
+static gboolean
+leave_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct)
+{
+ browser_canvas_text_set_highlight (ct, FALSE);
+ return FALSE;
+}
+
+static guint
+compute_step_value (current, end)
+{
+#define STEP 15
+ if (current < end)
+ return current + MIN (STEP, (end - current));
+ else if (current > end)
+ return current - MIN (STEP, (current - end));
+ else
+ return current;
+}
+
+static gboolean
+anim_cb (BrowserCanvasText *ct)
+{
+ guint current, end, value;
+ guint rgba = 0;
+
+ /* red */
+ current = (ct->priv->current_anim_rgba >> 24) & 0xFF;
+ end = (ct->priv->end_anim_rgba >> 24) & 0xFF;
+ value = compute_step_value (current, end) << 24;
+ rgba += value;
+
+ /* green */
+ current = (ct->priv->current_anim_rgba >> 16) & 0xFF;
+ end = (ct->priv->end_anim_rgba >> 16) & 0xFF;
+ value = compute_step_value (current, end) << 16;
+ rgba += value;
+
+ /* blue */
+ current = (ct->priv->current_anim_rgba >> 8) & 0xFF;
+ end = (ct->priv->end_anim_rgba >> 8) & 0xFF;
+ value = compute_step_value (current, end) << 8;
+ rgba += value;
+
+ /* alpha */
+ current = ct->priv->current_anim_rgba & 0xFF;
+ end = ct->priv->end_anim_rgba & 0xFF;
+ value = compute_step_value (current, end);
+ rgba += value;
+
+ if (rgba == ct->priv->end_anim_rgba) {
+ ct->priv->anim_id = 0;
+ return FALSE;
+ }
+ else {
+ g_object_set (G_OBJECT (ct->priv->bg_item), "fill_color_rgba", rgba, NULL);
+ ct->priv->current_anim_rgba = rgba;
+ return TRUE;
+ }
+}
+
+/**
+ * browser_canvas_text_set_highlight
+ * @ct: a #BrowserCanvasText object
+ * @highlight:
+ *
+ * Turns ON or OFF the highlighting of @ct
+ */
+void
+browser_canvas_text_set_highlight (BrowserCanvasText *ct, gboolean highlight)
+{
+ gchar *str_color;
+ GdkColor gdk_color;
+
+ g_return_if_fail (ct && IS_BROWSER_CANVAS_TEXT (ct));
+ g_return_if_fail (ct->priv);
+
+ if (! ct->priv->bg_item)
+ return;
+
+ if (ct->priv->anim_id) {
+ g_source_remove (ct->priv->anim_id);
+ ct->priv->anim_id = 0;
+ }
+
+ str_color = highlight ? ct->priv->highlight_color : BROWSER_CANVAS_OBJ_BG_COLOR;
+ if (gdk_color_parse (str_color, &gdk_color)) {
+ guint col;
+
+ col = ((guint) (gdk_color.red * 255. / 65535.0));
+ ct->priv->end_anim_rgba = col << 24;
+ col = ((guint) (gdk_color.green * 255. / 65535.0));
+ ct->priv->end_anim_rgba += col << 16;
+ col = ((guint) (gdk_color.blue * 255. / 65535.0));
+ ct->priv->end_anim_rgba += col << 8;
+ if (!ct->priv->current_anim_rgba)
+ ct->priv->current_anim_rgba = ct->priv->end_anim_rgba;
+
+ if (highlight)
+ ct->priv->end_anim_rgba += 255;
+ else
+ ct->priv->end_anim_rgba += 50;
+
+ ct->priv->anim_id = g_timeout_add (10, (GSourceFunc) anim_cb, ct);
+ }
+ else
+ g_object_set (G_OBJECT (ct->priv->bg_item), "fill_color", str_color, NULL);
+}
+
+/**
+ * browser_canvas_text_new
+ * @parent: the parent item, or NULL.
+ * @txt: text to display
+ * @x: the x coordinate of the text.
+ * @y: the y coordinate of the text.
+ * @...: optional pairs of property names and values, and a terminating NULL.
+ *
+ * Creates a new canvas item to display the @txt message
+ *
+ * Returns: a new #GooCanvasItem object
+ */
+GooCanvasItem *
+browser_canvas_text_new (GooCanvasItem *parent,
+ const gchar *txt,
+ gdouble x,
+ gdouble y,
+ ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_TEXT, NULL);
+
+ if (parent) {
+ goo_canvas_item_add_child (parent, item, -1);
+ g_object_unref (item);
+ }
+
+ va_start (var_args, y);
+ first_property = va_arg (var_args, char*);
+ if (first_property)
+ g_object_set_valist ((GObject*) item, first_property, var_args);
+ va_end (var_args);
+
+ g_object_set (item, "text", txt, NULL);
+ goo_canvas_item_translate (item, x, y);
+
+ return item;
+}
diff --git a/tools/browser/canvas/browser-canvas-text.h b/tools/browser/canvas/browser-canvas-text.h
new file mode 100644
index 0000000..4beba91
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-text.h
@@ -0,0 +1,71 @@
+/* browser-canvas-text.h
+ *
+ * Copyright (C) 2007 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS_TEXT__
+#define __BROWSER_CANVAS_TEXT__
+
+#include "browser-canvas-item.h"
+
+G_BEGIN_DECLS
+
+/*
+ *
+ * "Drag item" GooCanvas item: a BrowserCanvasItem item which is used to represent
+ * an element being dragged, and destroys itself when the mouse button is released
+ *
+ */
+
+#define TYPE_BROWSER_CANVAS_TEXT (browser_canvas_text_get_type())
+#define BROWSER_CANVAS_TEXT(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_text_get_type(), BrowserCanvasText)
+#define BROWSER_CANVAS_TEXT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_text_get_type (), BrowserCanvasTextClass)
+#define IS_BROWSER_CANVAS_TEXT(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_text_get_type ())
+
+
+typedef struct _BrowserCanvasText BrowserCanvasText;
+typedef struct _BrowserCanvasTextClass BrowserCanvasTextClass;
+typedef struct _BrowserCanvasTextPrivate BrowserCanvasTextPrivate;
+
+
+/* struct for the object's data */
+struct _BrowserCanvasText
+{
+ BrowserCanvasItem object;
+
+ BrowserCanvasTextPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasTextClass
+{
+ BrowserCanvasItemClass parent_class;
+};
+
+/* generic widget's functions */
+GType browser_canvas_text_get_type (void) G_GNUC_CONST;
+GooCanvasItem* browser_canvas_text_new (GooCanvasItem *parent,
+ const gchar *txt,
+ gdouble x,
+ gdouble y,
+ ...);
+void browser_canvas_text_set_highlight (BrowserCanvasText *ct, gboolean highlight);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/canvas/browser-canvas-utility.c b/tools/browser/canvas/browser-canvas-utility.c
new file mode 100644
index 0000000..4485b22
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-utility.c
@@ -0,0 +1,836 @@
+/* graph-utility.c
+ * Copyright (C) 2004 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "browser-canvas-utility.h"
+#include <math.h>
+#include <string.h>
+
+static gchar *points_to_path (GooCanvasPoints *points);
+
+static gboolean compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
+ gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
+ gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y);
+
+static void compute_text_marks_offsets (gdouble x1, gdouble y1, gdouble x2, gdouble y2,
+ gdouble *xoff, gdouble *yoff, GtkAnchorType *anchor_type);
+
+static GSList *browser_canvas_util_compute_handle_shapes (GooCanvasItem *parent, GSList *shapes, gint index,
+ gdouble x1, gdouble y1, gdouble x2, gdouble y2);
+
+static GSList *browser_canvas_canvas_shape_add_to_list (GSList *list, gchar *swallow_id, GooCanvasItem *item);
+static BrowserCanvasCanvasShape *browser_canvas_canvas_shape_find (GSList *list, const gchar *id);
+
+
+/*
+ * Computes the points' coordinates of the line going from
+ * @ref_pk_ent to @fk_ent (which are themselves rectangles)
+ *
+ * if @shapes is not NULL, then the shapes in the list are reused, and the ones which don't need
+ * to exist anymore are removed
+ *
+ * Returns a list of BrowserCanvasCanvasShapes structures
+ */
+GSList *
+browser_canvas_util_compute_anchor_shapes (GooCanvasItem *parent, GSList *shapes,
+ BrowserCanvasTable *fk_ent, BrowserCanvasTable *ref_pk_ent,
+ guint nb_anchors, guint ext, gboolean with_handle)
+{
+ GSList *retval = shapes;
+ guint i;
+ gdouble fx1, fy1, fx2, fy2; /* FK entity item (bounds) */
+ gdouble rx1, ry1, rx2, ry2; /* REF PK entity item (bounds) */
+
+ BrowserCanvasCanvasShape *shape;
+ gchar *id;
+
+ gdouble rcx, rcy; /* center of ref_pk entity item */
+ gdouble cx, cy;
+
+ gdouble rux, ruy; /* current ref_pk point for the arrow line */
+ gdouble dx, dy; /* increments to compute the new ref_pk point for the arrow line */
+ GooCanvasBounds bounds;
+
+ g_return_val_if_fail (nb_anchors > 0, NULL);
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (fk_ent), &bounds);
+ fx1 = bounds.x1;
+ fy1 = bounds.y1;
+ fx2 = bounds.x2;
+ fy2 = bounds.y2;
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ref_pk_ent), &bounds);
+ rx1 = bounds.x1;
+ ry1 = bounds.y1;
+ rx2 = bounds.x2;
+ ry2 = bounds.y2;
+
+ /* compute the cx, cy, dx and dy values */
+ rcx = (rx1 + rx2) / 2.;
+ rcy = (ry1 + ry2) / 2.;
+ cx = (fx1 + fx2) / 2.;
+ cy = (fy1 + fy2) / 2.;
+ rux = rcx;
+ ruy = rcy;
+ dx = 0;
+ dy = 0;
+
+ for (i = 0; i < nb_anchors; i++) {
+ /* TODO:
+ - detect tables overlapping
+ */
+ if ((rcx == cx) && (rcy == cy)) {
+ /* tables have the same center (includes case when they are equal) */
+ gdouble Dy, Dx;
+ GooCanvasPoints *ap, *points;
+ GooCanvasItem *item;
+
+ points = goo_canvas_points_new (4);
+ ap = goo_canvas_points_new (4);
+
+ Dy = (ry2 - ry1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
+ Dx = (rx2 - rx1) * (0.8 + 0.1 * i);
+ if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
+ cx, cy, cx + Dx, cy - Dy,
+ &(ap->coords[0]), &(ap->coords[1]),
+ &(ap->coords[2]), &(ap->coords[3])))
+ return retval;
+
+ if (ap->coords[0] > ap->coords[2]) {
+ points->coords[0] = ap->coords[0];
+ points->coords[1] = ap->coords[1];
+ }
+ else {
+ points->coords[0] = ap->coords[2];
+ points->coords[1] = ap->coords[3];
+ }
+
+ points->coords[2] = cx + Dx;
+ points->coords[3] = cy - Dy;
+
+ Dy = (fy2 - fy1) / 2. / (gdouble ) (nb_anchors + 1) * (gdouble) (i + 1);
+ Dx = (fx2 - fx1) * (0.8 + 0.1 * i);
+ points->coords[4] = cx + Dx;
+ points->coords[5] = cy + Dy;
+
+ if (! compute_intersect_rect_line (fx1, fy1, fx2, fy2,
+ cx, cy, cx + Dx, cy + Dy,
+ &(ap->coords[0]), &(ap->coords[1]),
+ &(ap->coords[2]), &(ap->coords[3])))
+ return retval;
+
+ if (ap->coords[0] > ap->coords[2]) {
+ points->coords[6] = ap->coords[0];
+ points->coords[7] = ap->coords[1];
+ }
+ else {
+ points->coords[6] = ap->coords[2];
+ points->coords[7] = ap->coords[3];
+ }
+
+ id = g_strdup_printf ("a%d", i);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item, "points", points, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_polyline_new_line (parent,
+ points->coords[0], points->coords [1],
+ points->coords[2], points->coords [3],
+ "close-path", FALSE,
+ "points", points, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ goo_canvas_points_unref (ap);
+
+ /* extension marks as text */
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
+ id = g_strdup_printf ("a%de1", i);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[2] + 5.,
+ "y", points->coords[3] - 5., NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[2] + 5.,
+ points->coords[3] - 5., -1,
+ GTK_ANCHOR_SOUTH, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
+ id = g_strdup_printf ("a%de2", i);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[4] + 5.,
+ "y", points->coords[5] + 5., NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[4] + 5.,
+ points->coords[5] + 5., -1,
+ GTK_ANCHOR_NORTH, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ /* handle in the middle */
+ if (with_handle)
+ retval = browser_canvas_util_compute_handle_shapes (parent, retval, i,
+ points->coords[2],
+ points->coords[3],
+ points->coords[4],
+ points->coords[5]);
+ goo_canvas_points_unref (points);
+ }
+ else {
+ GooCanvasPoints *ap, *points;
+ GooCanvasItem *item;
+
+ points = goo_canvas_points_new (2);
+ ap = goo_canvas_points_new (4);
+
+ if (nb_anchors > 1) {
+ if ((dx == 0) && (dy == 0)) {
+ /* compute perpendicular to D={(rcx, rcy), (cx, cy)} */
+ gdouble vx = (rcx - cx), vy = (rcy - cy);
+ gdouble tmp;
+
+ tmp = vx;
+ vx = vy;
+ vy = - tmp;
+
+ /* compute intersect of ref_pkey rectangle and D=[vx, vy] passing at (rcx, rcy) */
+ if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
+ rcx, rcy, rcx + vx, rcy + vy,
+ &(ap->coords[0]), &(ap->coords[1]),
+ &(ap->coords[2]), &(ap->coords[3])))
+ return retval;
+ dx = (ap->coords[2] - ap->coords[0]) / (gdouble) (nb_anchors + 1);
+ dy = (ap->coords[3] - ap->coords[1]) / (gdouble) (nb_anchors + 1);
+ rux = ap->coords[0];
+ ruy = ap->coords[1];
+ }
+
+ rux += dx;
+ ruy += dy;
+ }
+
+ /* compute the 4 intersection points */
+ if (! compute_intersect_rect_line (rx1, ry1, rx2, ry2,
+ rux, ruy, cx, cy,
+ &(ap->coords[0]), &(ap->coords[1]),
+ &(ap->coords[2]), &(ap->coords[3])))
+ return retval;
+ if (! compute_intersect_rect_line (fx1, fy1, fx2, fy2,
+ rux, ruy, cx, cy,
+ &(ap->coords[4]), &(ap->coords[5]),
+ &(ap->coords[6]), &(ap->coords[7])))
+ return retval;
+
+ /* choosing between point coords(0,1) and coords(2,3) */
+ if (((ap->coords[0] - ap->coords[4]) * (ap->coords[0] - ap->coords[4]) +
+ (ap->coords[1] - ap->coords[5]) * (ap->coords[1] - ap->coords[5])) <
+ ((ap->coords[2] - ap->coords[4]) * (ap->coords[2] - ap->coords[4]) +
+ (ap->coords[3] - ap->coords[5]) * (ap->coords[3] - ap->coords[5]))) {
+ points->coords[0] = ap->coords[0];
+ points->coords[1] = ap->coords[1];
+ }
+ else {
+ points->coords[0] = ap->coords[2];
+ points->coords[1] = ap->coords[3];
+ }
+
+ /* choosing between point coords(4,5) and coords(6,7) */
+ if (((points->coords[0] - ap->coords[4]) * (points->coords[0] - ap->coords[4]) +
+ (points->coords[1] - ap->coords[5]) * (points->coords[1] - ap->coords[5])) <
+ ((points->coords[0] - ap->coords[6]) * (points->coords[0] - ap->coords[6]) +
+ (points->coords[1] - ap->coords[7]) * (points->coords[1] - ap->coords[7]))) {
+ points->coords[2] = ap->coords[4];
+ points->coords[3] = ap->coords[5];
+ }
+ else {
+ points->coords[2] = ap->coords[6];
+ points->coords[3] = ap->coords[7];
+ }
+
+ id = g_strdup_printf ("a%d", i);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item, "points", points, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_polyline_new_line (parent,
+ points->coords[0], points->coords [1],
+ points->coords[2], points->coords [3],
+ "close-path", FALSE,
+ "points", points, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ goo_canvas_points_unref (ap);
+
+ /* extension marks as text */
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
+ gdouble mxoff = 0., myoff = 0.;
+ GtkAnchorType atype;
+
+ compute_text_marks_offsets (points->coords[0], points->coords[1],
+ points->coords[2], points->coords[3],
+ &mxoff, &myoff, &atype);
+ id = g_strdup_printf ("a%de1", i);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[2] + mxoff,
+ "y", points->coords[3] + myoff,
+ "anchor", atype, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[2] + mxoff,
+ points->coords[3] + myoff, -1,
+ atype, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
+ gdouble mxoff, myoff;
+ GtkAnchorType atype;
+
+ compute_text_marks_offsets (points->coords[2], points->coords[3],
+ points->coords[0], points->coords[1],
+ &mxoff, &myoff, &atype);
+
+ id = g_strdup_printf ("a%de2", i);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[0] + mxoff,
+ "y", points->coords[1] + myoff,
+ "anchor", atype, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[0] + mxoff,
+ points->coords[1] + myoff, -1,
+ atype, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ /* handle in the middle */
+ if (with_handle)
+ retval = browser_canvas_util_compute_handle_shapes (parent, retval, i,
+ points->coords[0],
+ points->coords[1],
+ points->coords[2],
+ points->coords[3]);
+ goo_canvas_points_unref (points);
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Computes the position offsets, relative to X2=(x2, y2) of a text to be written
+ * close to the X2 point, also computes the anchor type
+ */
+static void
+compute_text_marks_offsets (gdouble x1, gdouble y1, gdouble x2, gdouble y2,
+ gdouble *xoff, gdouble *yoff, GtkAnchorType *anchor_type)
+{
+ gdouble mxoff, myoff;
+ GtkAnchorType atype = GTK_ANCHOR_CENTER; /* FIXME */
+ gdouble sint, cost;
+ gdouble sina = 0.5;
+ gdouble cosa = 0.866025; /* sqrt(3)/2 */
+ gdouble hyp;
+ gdouble d = 15.;
+
+ hyp = sqrt ((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
+ sint = - (y2 - y1) / hyp;
+ cost = (x2 - x1) / hyp;
+
+ mxoff = -d * (sina * sint + cosa * cost);
+ myoff = -d * (sina * cost - cosa * sint);
+
+ if (xoff)
+ *xoff = mxoff;
+ if (yoff)
+ *yoff = myoff;
+ if (anchor_type)
+ *anchor_type = atype;
+}
+
+/*
+ * Computes the points of intersection between a rectangle (defined by the first 2 points)
+ * and a line (defined by the next 2 points).
+ *
+ * The result is returned in place of the line's point definition
+ *
+ * --------- ----- D1
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * --------- ---- D2
+ *
+ * | |
+ * | |
+ * D3 D4
+ *
+ * Returns: TRUE if the line crosses the rectangle, and FALSE if it misses it.
+ */
+static gboolean
+compute_intersect_rect_line (gdouble rectx1, gdouble recty1, gdouble rectx2, gdouble recty2,
+ gdouble P1x, gdouble P1y, gdouble P2x, gdouble P2y,
+ gdouble *R1x, gdouble *R1y, gdouble *R2x, gdouble *R2y)
+{
+ gboolean retval = FALSE;
+ gboolean rotated = FALSE;
+ gdouble a=0.; /* line slope y = a x + b */
+ gdouble b; /* line offset y = a x + b */
+ gdouble offset = 2.;
+
+ gdouble ptsx[4]; /* points' X coordinate: 0 for intersect with D1, 1 for D2,... */
+ gdouble ptsy[4]; /* points' Y coordinate */
+
+ if ((rectx1 == rectx2) && (recty1 == recty2))
+ return FALSE;
+ if ((rectx1 >= rectx2) || (recty1 >= recty2))
+ return FALSE;
+ if ((P1x == P2x) && (P1y == P2y))
+ return FALSE;
+
+ /* rotate the coordinates to invert X and Y to avoid rounding problems ? */
+ if (P1x != P2x)
+ a = (P1y - P2y) / (P1x - P2x);
+ if ((P1x == P2x) || (fabs (a) > 1)) {
+ gdouble tmp;
+ rotated = TRUE;
+ tmp = rectx1; rectx1 = recty1; recty1 = tmp;
+ tmp = rectx2; rectx2 = recty2; recty2 = tmp;
+ tmp = P1x; P1x = P1y; P1y = tmp;
+ tmp = P2x; P2x = P2y; P2y = tmp;
+ a = (P1y - P2y) / (P1x - P2x);
+ }
+
+ /* here we have (P1x != P2x), non vertical line */
+ b = P1y - a * P1x;
+
+ if (a == 0) {
+ /* horizontal line */
+
+ if ((b <= recty2) && (b >= recty1)) {
+ retval = TRUE;
+ *R1x = rectx1 - offset; *R1y = b;
+ *R2x = rectx2 + offset; *R2y = b;
+ }
+ }
+ else {
+ gdouble retx[2] = {0., 0.};
+ gdouble rety[2] = {0., 0.};
+ gint i = 0;
+
+ /* non horizontal and non vertical line */
+ /* D1 */
+ ptsy[0] = recty1 - offset;
+ ptsx[0] = (recty1 - b) / a;
+
+ /* D2 */
+ ptsy[1] = recty2 + offset;
+ ptsx[1] = (recty2 - b) / a;
+
+ /* D3 */
+ ptsx[2] = rectx1 - offset;
+ ptsy[2] = a * rectx1 + b;
+
+ /* D4 */
+ ptsx[3] = rectx2 + offset;
+ ptsy[3] = a * rectx2 + b;
+
+ if ((ptsx[0] >= rectx1) && (ptsx[0] <= rectx2)) {
+ retval = TRUE;
+ retx[i] = ptsx[0]; rety[i] = ptsy[0];
+ i ++;
+ }
+ if ((ptsx[1] >= rectx1) && (ptsx[1] <= rectx2)) {
+ retval = TRUE;
+ retx[i] = ptsx[1]; rety[i] = ptsy[1];
+ i ++;
+ }
+ if ((i<2) && (ptsy[2] >= recty1) && (ptsy[2] <= recty2)) {
+ retval = TRUE;
+ retx[i] = ptsx[2]; rety[i] = ptsy[2];
+ i ++;
+ }
+ if ((i<2) && (ptsy[3] >= recty1) && (ptsy[3] <= recty2)) {
+ retval = TRUE;
+ retx[i] = ptsx[3]; rety[i] = ptsy[3];
+ i++;
+ }
+
+ if (retval) {
+ g_assert (i == 2); /* wee need 2 points! */
+ *R1x = retx[0]; *R1y = rety[0];
+ *R2x = retx[1]; *R2y = rety[1];
+ }
+ }
+
+ if (retval && rotated) {
+ /* rotate it back */
+ gdouble tmp;
+
+ tmp = *R1x; *R1x = *R1y; *R1y = tmp;
+ tmp = *R2x; *R2x = *R2y; *R2y = tmp;
+ }
+
+ return retval;
+}
+
+/*
+ * Compute the anchor shapes to link field1 to field2 from ent1 to ent2
+ *
+ * if @shapes is not NULL, then the shapes in the list are reused, and the ones which don't need
+ * to exist anymore are removed
+ *
+ * Returns a list of BrowserCanvasCanvasShapes structures
+ */
+GSList *
+browser_canvas_util_compute_connect_shapes (GooCanvasItem *parent, GSList *shapes,
+ BrowserCanvasTable *ent1, GdaMetaTableColumn *field1,
+ BrowserCanvasTable *ent2, GdaMetaTableColumn *field2,
+ guint nb_connect, guint ext)
+{
+ GSList *retval = shapes;
+ GooCanvasItem *item;
+ GooCanvasPoints *points;
+ gdouble xl1, xr1, xl2, xr2, yt1, yt2; /* X boundings and Y top of ent1 and ent2 */
+ gdouble x1, x2; /* X positions of the lines extremities close to ent1 and ent2 */
+ gdouble x1offset, x2offset; /* offsets for the horizontal part of the lines */
+ double sq = 5.;
+ double eps = 0.5;
+ GooCanvasBounds bounds;
+
+ BrowserCanvasCanvasShape *shape;
+ gchar *id;
+
+ if (!field1 || !field2)
+ return browser_canvas_util_compute_anchor_shapes (parent, shapes, ent1, ent2, 1, ext, FALSE);
+
+ /* line made of 4 points */
+ points = goo_canvas_points_new (4);
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ent1), &bounds);
+ xl1 = bounds.x1;
+ yt1 = bounds.y1;
+ xr1 = bounds.x2;
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ent2), &bounds);
+ xl2 = bounds.x1;
+ yt2 = bounds.y1;
+ xr2 = bounds.x2;
+
+ if (xl2 > xr1) {
+ x1 = xr1 + eps;
+ x2 = xl2 - eps;
+ x1offset = 2 * sq;
+ x2offset = -2 * sq;
+ }
+ else {
+ if (xl1 >= xr2) {
+ x1 = xl1 - eps;
+ x2 = xr2 + eps;
+ x1offset = - 2 * sq;
+ x2offset = 2 * sq;
+ }
+ else {
+ if ((xl1 + xr1) < (xl2 + xr2)) {
+ x1 = xl1 - eps;
+ x2 = xl2 - eps;
+ x1offset = -2 * sq;
+ x2offset = -2 * sq;
+ }
+ else {
+ x1 = xr1 + eps;
+ x2 = xr2 + eps;
+ x1offset = 2 * sq;
+ x2offset = 2 * sq;
+ }
+ }
+ }
+
+ points->coords[0] = x1;
+ points->coords[1] = browser_canvas_table_get_column_ypos (ent1, field1) + yt1;
+
+ points->coords[2] = x1 + x1offset;
+ points->coords[3] = points->coords[1];
+
+ points->coords[4] = x2 + x2offset;
+ points->coords[5] = browser_canvas_table_get_column_ypos (ent2, field2) + yt2;
+
+ points->coords[6] = x2;
+ points->coords[7] = points->coords[5];
+
+ id = g_strdup_printf ("c%d", nb_connect);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item, "points", points, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_polyline_new_line (parent,
+ points->coords[0], points->coords [1],
+ points->coords[2], points->coords [3],
+ "close-path", FALSE,
+ "points", points, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+
+ /* extension marks as text */
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_1) {
+ gdouble mxoff = 0., myoff = 0.;
+ GtkAnchorType atype;
+
+ compute_text_marks_offsets (points->coords[4], points->coords[5],
+ points->coords[2], points->coords[3],
+ &mxoff, &myoff, &atype);
+
+ id = g_strdup_printf ("ce%d1", nb_connect);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[2] + mxoff,
+ "y", points->coords[3] + myoff,
+ "anchor", atype, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[2] + mxoff,
+ points->coords[3] + myoff, -1,
+ atype, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ if (ext & CANVAS_SHAPE_EXT_JOIN_OUTER_2) {
+ gdouble mxoff, myoff;
+ GtkAnchorType atype;
+
+ compute_text_marks_offsets (points->coords[2], points->coords[3],
+ points->coords[4], points->coords[5],
+ &mxoff, &myoff, &atype);
+
+ id = g_strdup_printf ("ce%d2", nb_connect);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item,
+ "x", points->coords[2] + mxoff,
+ "y", points->coords[3] + myoff,
+ "anchor", atype, NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_text_new (parent, "*",
+ points->coords[2] + mxoff,
+ points->coords[3] + myoff, -1,
+ atype, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+
+ /* handle in the middle */
+ /*retval = browser_canvas_util_compute_handle_shapes (parent, retval, nb_connect,
+ points->coords[2], points->coords[3],
+ points->coords[4], points->coords[5]);*/
+ goo_canvas_points_unref (points);
+
+ return retval;
+}
+
+/*
+ * computes a "handle" in the middle of the 2 points passed as argument*
+ *
+ * Warning: the obsolete shapes in @shapes are _not_ removed.
+ */
+GSList *
+browser_canvas_util_compute_handle_shapes (GooCanvasItem *parent, GSList *shapes, gint index,
+ gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+{
+ /* don't add anything... */
+ /*return shapes;*/
+
+ GSList *retval = shapes;
+ gdouble x, y, sq = 5.;
+ GooCanvasItem *item;
+ BrowserCanvasCanvasShape *shape;
+ gchar *id;
+
+ /* circle in the middle */
+ x = (x1 + x2) / 2.;
+ y = (y1 + y2) / 2.;
+
+ id = g_strdup_printf ("h%d", index);
+ shape = browser_canvas_canvas_shape_find (retval, id);
+ if (shape) {
+ g_object_set (shape->item,
+ "center-x", x, "center-y", y,
+ NULL);
+ shape->_used = TRUE;
+ g_free (id);
+ }
+ else {
+ item = goo_canvas_ellipse_new (parent, x, y, sq, sq,
+ "fill-color", "black",
+ "visibility", GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD,
+ "visibility-threshold", .9, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+
+ return retval;
+}
+
+static gchar *
+points_to_path (GooCanvasPoints *points)
+{
+ GString *string;
+ gchar *path;
+ gint i;
+
+ g_return_val_if_fail (points, NULL);
+ g_return_val_if_fail (points->num_points >= 2, NULL);
+
+ string = g_string_new ("");
+ g_string_append_printf (string, "M%d %d", (int) points->coords[0], (int) points->coords[1]);
+
+ for (i = 1; i < points->num_points; i++)
+ g_string_append_printf (string, " L%d %d",
+ (int) points->coords[2*i], (int) points->coords[2*i+1]);
+
+ path = string->str;
+ g_string_free (string, FALSE);
+ return path;
+}
+
+static GSList *
+browser_canvas_canvas_shape_add_to_list (GSList *list, gchar *swallow_id, GooCanvasItem *item)
+{
+ BrowserCanvasCanvasShape *shape = g_new (BrowserCanvasCanvasShape, 1);
+
+ g_assert (swallow_id);
+ g_assert (item);
+ shape->id = swallow_id;
+ shape->item = item;
+ shape->_used = TRUE;
+ shape->is_new = TRUE;
+
+ /*g_print ("Shape %p (%s: %s) added\n", item, swallow_id, G_OBJECT_TYPE_NAME (item));*/
+
+ return g_slist_append (list, shape);
+}
+
+static BrowserCanvasCanvasShape *
+browser_canvas_canvas_shape_find (GSList *list, const gchar *id)
+{
+ BrowserCanvasCanvasShape *shape = NULL;
+ GSList *l;
+
+ for (l = list; l && !shape; l = l->next)
+ if (!strcmp (((BrowserCanvasCanvasShape*) l->data)->id, id))
+ shape = (BrowserCanvasCanvasShape*) l->data;
+
+ /*g_print ("Looking for shape %s: %s\n", id, shape ? "Found" : "Not found");*/
+ return shape;
+}
+
+GSList *
+browser_canvas_canvas_shapes_remove_obsolete_shapes (GSList *list)
+{
+ GSList *l, *ret = list;
+
+ for (l = list; l; ) {
+ if (((BrowserCanvasCanvasShape*)(l->data))->_used) {
+ ((BrowserCanvasCanvasShape*)(l->data))->_used = FALSE;
+ l=l->next;
+ }
+ else {
+ GSList *tmp;
+ BrowserCanvasCanvasShape *shape = (BrowserCanvasCanvasShape*) l->data;
+
+ g_free (shape->id);
+ goo_canvas_item_remove (shape->item);
+ g_free (shape);
+
+ tmp = l->next;
+ ret = g_slist_delete_link (ret, l);
+ l = tmp;
+ }
+ }
+
+ return ret;
+}
+
+void
+browser_canvas_canvas_shapes_remove_all (GSList *list)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ BrowserCanvasCanvasShape *shape = (BrowserCanvasCanvasShape*) l->data;
+
+ g_free (shape->id);
+ goo_canvas_item_remove (shape->item);
+ g_free (shape);
+ }
+
+ g_slist_free (list);
+}
+
+void
+browser_canvas_canvas_shapes_dump (GSList *list)
+{
+ GSList *l;
+ g_print ("Canvas shapes...\n");
+ for (l = list; l; l = l->next)
+ g_print ("\tShape %s @%p (%s: %p) %s\n", BROWSER_CANVAS_CANVAS_SHAPE (l->data)->id,
+ BROWSER_CANVAS_CANVAS_SHAPE (l->data),
+ G_OBJECT_TYPE_NAME (BROWSER_CANVAS_CANVAS_SHAPE (l->data)->item),
+ BROWSER_CANVAS_CANVAS_SHAPE (l->data)->item,
+ BROWSER_CANVAS_CANVAS_SHAPE (l->data)->_used ? "Used": "Not used");
+}
diff --git a/tools/browser/canvas/browser-canvas-utility.h b/tools/browser/canvas/browser-canvas-utility.h
new file mode 100644
index 0000000..bc5d1d4
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-utility.h
@@ -0,0 +1,51 @@
+/* browser-canvas-utility.h
+ * Copyright (C) 2007 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <goocanvas.h>
+#include "browser-canvas-table.h"
+
+G_BEGIN_DECLS
+
+enum {
+ CANVAS_SHAPE_EXT_JOIN_OUTER_1 = 1 << 0,
+ CANVAS_SHAPE_EXT_JOIN_OUTER_2 = 1 << 1
+};
+
+typedef struct {
+ gchar *id;
+ GooCanvasItem *item;
+ gboolean _used;
+ gboolean is_new;
+} BrowserCanvasCanvasShape;
+
+GSList *browser_canvas_util_compute_anchor_shapes (GooCanvasItem *parent, GSList *shapes,
+ BrowserCanvasTable *fk_ent, BrowserCanvasTable *ref_pk_ent,
+ guint nb_anchors, guint ext, gboolean with_handle);
+GSList *browser_canvas_util_compute_connect_shapes (GooCanvasItem *parent, GSList *shapes,
+ BrowserCanvasTable *ent1, GdaMetaTableColumn *field1,
+ BrowserCanvasTable *ent2, GdaMetaTableColumn *field2,
+ guint nb_connect, guint ext);
+
+void browser_canvas_canvas_shapes_dump (GSList *list);
+void browser_canvas_canvas_shapes_remove_all (GSList *list);
+GSList *browser_canvas_canvas_shapes_remove_obsolete_shapes (GSList *list);
+
+#define BROWSER_CANVAS_CANVAS_SHAPE(x) ((BrowserCanvasCanvasShape *)(x))
+
+G_END_DECLS
diff --git a/tools/browser/canvas/browser-canvas.c b/tools/browser/canvas/browser-canvas.c
new file mode 100644
index 0000000..dfcae3d
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas.c
@@ -0,0 +1,934 @@
+/* browser-canvas.c
+ *
+ * Copyright (C) 2007 - 2008 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include "browser-canvas.h"
+#include "browser-canvas-priv.h"
+#include "browser-canvas-item.h"
+#include "browser-canvas-print.h"
+#include <libgda/libgda.h>
+#ifndef CANVAS_EXAMPLE
+#include "../support.h"
+#endif
+
+#define DEFAULT_SCALE .8
+#ifdef HAVE_GRAPHVIZ
+#include <stddef.h>
+#include <gvc.h>
+#ifndef ND_coord_i
+ #define ND_coord_i ND_coord
+#endif
+static GVC_t* gvc = NULL;
+#endif
+#include <cairo.h>
+#include <cairo-svg.h>
+#include <math.h>
+
+static void browser_canvas_class_init (BrowserCanvasClass *klass);
+static void browser_canvas_init (BrowserCanvas *canvas);
+static void browser_canvas_dispose (GObject *object);
+static void browser_canvas_finalize (GObject *object);
+
+static void browser_canvas_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void browser_canvas_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;
+
+enum
+{
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+};
+
+static gint canvas_signals[LAST_SIGNAL] = { };
+
+GType
+browser_canvas_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (BrowserCanvasClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) browser_canvas_class_init,
+ NULL,
+ NULL,
+ sizeof (BrowserCanvas),
+ 0,
+ (GInstanceInitFunc) browser_canvas_init
+ };
+
+ type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "BrowserCanvas", &info, 0);
+ }
+ return type;
+}
+
+static void
+browser_canvas_class_init (BrowserCanvasClass *klass)
+{
+ GtkWidgetClass *widget_class;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ widget_class = (GtkWidgetClass *) klass;
+
+ /* properties */
+ object_class->set_property = browser_canvas_set_property;
+ object_class->get_property = browser_canvas_get_property;
+
+ /* virtual functions */
+ klass->clean_canvas_items = NULL;
+ klass->build_context_menu = NULL;
+
+ object_class->dispose = browser_canvas_dispose;
+ object_class->finalize = browser_canvas_finalize;
+}
+
+static int canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static void drag_begin_cb (BrowserCanvas *canvas, GdkDragContext *drag_context, GooCanvas *gcanvas);
+static void drag_data_get_cb (BrowserCanvas *canvas, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info,
+ guint time, GooCanvas *gcanvas);
+static void drag_data_received_cb (BrowserCanvas *canvas, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time, GooCanvas *gcanvas);
+static gboolean idle_add_canvas_cb (BrowserCanvas *canvas);
+static void
+browser_canvas_init (BrowserCanvas *canvas)
+{
+ canvas->priv = g_new0 (BrowserCanvasPrivate, 1);
+
+ canvas->priv->goocanvas = GOO_CANVAS (goo_canvas_new ());
+ gtk_widget_show (GTK_WIDGET (canvas->priv->goocanvas));
+ g_object_set_data (G_OBJECT (canvas->priv->goocanvas), "browsercanvas", canvas);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (canvas),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (canvas), GTK_SHADOW_NONE);
+ g_idle_add ((GSourceFunc) idle_add_canvas_cb, canvas);
+ canvas->priv->items = NULL;
+
+ canvas->xmouse = 50.;
+ canvas->ymouse = 50.;
+
+ g_signal_connect (canvas, "event",
+ G_CALLBACK (canvas_event_cb), canvas->priv->goocanvas);
+ g_signal_connect (canvas, "drag-begin",
+ G_CALLBACK (drag_begin_cb), canvas->priv->goocanvas);
+ g_signal_connect (canvas, "drag-data-get",
+ G_CALLBACK (drag_data_get_cb), canvas->priv->goocanvas);
+ g_signal_connect (canvas, "drag-data-received",
+ G_CALLBACK (drag_data_received_cb), canvas->priv->goocanvas);
+ g_object_set (G_OBJECT (canvas->priv->goocanvas),
+ "automatic-bounds", TRUE,
+ "bounds-padding", 5.,
+ "bounds-from-origin", FALSE,
+ "anchor", GTK_ANCHOR_CENTER, NULL);
+
+ /* reseting the zoom */
+ goo_canvas_set_scale (canvas->priv->goocanvas, DEFAULT_SCALE);
+}
+
+static gboolean
+idle_add_canvas_cb (BrowserCanvas *canvas)
+{
+ gtk_container_add (GTK_CONTAINER (canvas), GTK_WIDGET (canvas->priv->goocanvas));
+ return FALSE;
+}
+
+static void
+drag_begin_cb (BrowserCanvas *canvas, GdkDragContext *drag_context, GooCanvas *gcanvas)
+{
+ BrowserCanvasItem *citem;
+
+ citem = g_object_get_data (G_OBJECT (canvas), "__drag_src_item");
+ if (citem) {
+ /*
+ gtk_drag_source_set_icon_pixbuf (GTK_WIDGET (canvas),
+ browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
+ */
+ }
+}
+
+static void
+drag_data_get_cb (BrowserCanvas *canvas, GdkDragContext *drag_context,
+ GtkSelectionData *data, guint info,
+ guint time, GooCanvas *gcanvas)
+{
+ BrowserCanvasItem *citem;
+
+ citem = g_object_get_data (G_OBJECT (canvas), "__drag_src_item");
+ if (citem) {
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (citem));
+ if (iclass->drag_data_get)
+ iclass->drag_data_get (citem, drag_context, data, info, time);
+ }
+}
+
+static void
+drag_data_received_cb (BrowserCanvas *canvas, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time, GooCanvas *gcanvas)
+{
+ GooCanvasItem *item;
+ item = goo_canvas_get_item_at (gcanvas, x, y, TRUE);
+ if (item) {
+ g_print ("Dragged into %s\n", G_OBJECT_TYPE_NAME (item));
+ gtk_drag_finish (context, TRUE, FALSE, time);
+ }
+ else {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ }
+}
+
+
+static void popup_zoom_in_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_zoom_out_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_zoom_fit_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_export_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_print_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static int
+canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas)
+{
+ gboolean done = TRUE;
+ GooCanvasItem *item;
+ BrowserCanvasClass *class = BROWSER_CANVAS_CLASS (G_OBJECT_GET_CLASS (canvas));
+ gdouble x, y;
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ x = ((GdkEventButton *) event)->x;
+ y = ((GdkEventButton *) event)->y;
+ goo_canvas_convert_from_pixels (gcanvas, &x, &y);
+ item = goo_canvas_get_item_at (gcanvas, x, y, TRUE);
+
+ if (!item) {
+ if ((((GdkEventButton *) event)->button == 3) && (class->build_context_menu)) {
+ GtkWidget *menu, *mitem;
+
+ canvas->xmouse = x;
+ canvas->ymouse = y;
+
+ /* extra menu items, if any */
+ menu = (class->build_context_menu) (canvas);
+
+ /* default menu items */
+ if (!menu)
+ menu = gtk_menu_new ();
+ else {
+ mitem = gtk_separator_menu_item_new ();
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+ }
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_IN, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_zoom_in_cb), canvas);
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_OUT, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_zoom_out_cb), canvas);
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_FIT, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_zoom_fit_cb), canvas);
+
+ mitem = gtk_separator_menu_item_new ();
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_append (menu, mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_export_cb), canvas);
+
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL);
+ gtk_widget_show (mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_print_cb), canvas);
+ gtk_menu_append (menu, mitem);
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ NULL, NULL, ((GdkEventButton *)event)->button,
+ ((GdkEventButton *)event)->time);
+ }
+ }
+ done = TRUE;
+ break;
+ default:
+ done = FALSE;
+ break;
+ }
+ return done;
+}
+
+static void
+popup_zoom_in_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) + .05);
+}
+
+static void
+popup_zoom_out_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) - .05);
+}
+
+static void
+popup_zoom_fit_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_fit_zoom_factor (canvas);
+}
+
+static void
+popup_export_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ GtkWidget *dlg;
+ gint result;
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (canvas));
+ GtkFileFilter *filter;
+
+#define MARGIN 5.
+
+ if (!GTK_WIDGET_TOPLEVEL (toplevel))
+ toplevel = NULL;
+
+ dlg = gtk_file_chooser_dialog_new (_("Save diagram as"), (GtkWindow*) toplevel,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("PNG Image"));
+ gtk_file_filter_add_mime_type (filter, "image/png");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), filter);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("SVG file"));
+ gtk_file_filter_add_mime_type (filter, "image/svg+xml");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), filter);
+
+ result = gtk_dialog_run (GTK_DIALOG (dlg));
+ if (result == GTK_RESPONSE_ACCEPT) {
+ gchar *filename;
+ gchar *lcfilename;
+ cairo_surface_t *surface = NULL;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
+ if (filename) {
+ GooCanvasBounds bounds;
+ gdouble width, height;
+ gchar *error = NULL;
+ enum {
+ OUT_UNKNOWN,
+ OUT_PNG,
+ OUT_SVG
+ } otype = OUT_UNKNOWN;
+
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (canvas->priv->goocanvas), &bounds);
+ width = (bounds.x2 - bounds.x1) + 2. * MARGIN;
+ height = (bounds.y2 - bounds.y1) + 2. * MARGIN;
+
+ lcfilename = g_ascii_strdown (filename, -1);
+ if (g_str_has_suffix (lcfilename, "png")) {
+ otype = OUT_PNG;
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ }
+ if (g_str_has_suffix (lcfilename, "svg")) {
+ cairo_status_t status;
+ otype = OUT_SVG;
+ surface = cairo_svg_surface_create (filename, width, height);
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ error = g_strdup_printf ("<b>%s</b>:\n%s",
+ _("Failed to create SVG file"),
+ cairo_status_to_string (status));
+ cairo_surface_destroy (surface);
+ surface = NULL;
+ }
+ }
+ if (otype == OUT_UNKNOWN)
+ error = g_strdup_printf ("<b>%s</b>",
+ _("File format to save to is not recognized."));
+
+ if (surface) {
+ cairo_t *cr;
+ cairo_status_t status;
+
+ cr = cairo_create (surface);
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_GRAY);
+ cairo_set_line_width (cr, goo_canvas_get_default_line_width (canvas->priv->goocanvas));
+ cairo_translate (cr, MARGIN - bounds.x1, MARGIN - bounds.y1);
+
+ goo_canvas_render (GOO_CANVAS (canvas), cr, NULL, 0.8);
+
+ cairo_show_page (cr);
+
+ switch (otype) {
+ case OUT_PNG:
+ status = cairo_surface_write_to_png (surface, filename);
+ if (status != CAIRO_STATUS_SUCCESS)
+ error = g_strdup_printf ("<b>%s</b>:\n%s",
+ _("Failed to create PNG file"),
+ cairo_status_to_string (status));
+ break;
+ default:
+ break;
+ }
+
+ cairo_surface_destroy (surface);
+ cairo_destroy (cr);
+ }
+
+ if (error) {
+ GtkWidget *errdlg;
+
+ errdlg = gtk_message_dialog_new_with_markup ((GtkWindow*) toplevel,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE, "%s", error);
+ g_free (error);
+ gtk_dialog_run (GTK_DIALOG (errdlg));
+ gtk_widget_destroy (errdlg);
+ }
+
+ g_free (filename);
+ g_free (lcfilename);
+ }
+ }
+ gtk_widget_destroy (dlg);
+}
+
+static void
+popup_print_cb (GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_print (canvas);
+}
+
+static void item_destroyed_cb (BrowserCanvasItem *item, BrowserCanvas *canvas);
+static void
+browser_canvas_dispose (GObject * object)
+{
+ BrowserCanvas *canvas;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS (object));
+
+ canvas = BROWSER_CANVAS (object);
+
+ /* get rid of the GooCanvasItems */
+ if (canvas->priv->items) {
+ GSList *list;
+ for (list = canvas->priv->items; list; list = list->next) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (item_destroyed_cb), canvas);
+ }
+ g_slist_free (canvas->priv->items);
+ canvas->priv->items = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->dispose (object);
+}
+
+/**
+ * browser_canvas_declare_item
+ * @canvas: a #BrowserCanvas widget
+ * @item: a #BrowserCanvasItem object
+ *
+ * Declares @item to be listed by @canvas as one of its items.
+ */
+void
+browser_canvas_declare_item (BrowserCanvas *canvas, BrowserCanvasItem *item)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+ g_return_if_fail (canvas->priv);
+ g_return_if_fail (IS_BROWSER_CANVAS_ITEM (item));
+
+ g_print ("%s (canvas=>%p, item=>%p)\n", __FUNCTION__, canvas, item);
+ if (g_slist_find (canvas->priv->items, item))
+ return;
+
+ canvas->priv->items = g_slist_prepend (canvas->priv->items, item);
+ g_signal_connect (G_OBJECT (item), "destroy",
+ G_CALLBACK (item_destroyed_cb), canvas);
+}
+
+static void
+item_destroyed_cb (BrowserCanvasItem *item, BrowserCanvas *canvas)
+{
+ g_print ("%s (canvas=>%p, item=>%p)\n", __FUNCTION__, canvas, item);
+ g_return_if_fail (g_slist_find (canvas->priv->items, item));
+ g_signal_handlers_disconnect_by_func (G_OBJECT (item), G_CALLBACK (item_destroyed_cb), canvas);
+ canvas->priv->items = g_slist_remove (canvas->priv->items, item);
+}
+
+
+static void
+browser_canvas_finalize (GObject *object)
+{
+ BrowserCanvas *canvas;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_BROWSER_CANVAS (object));
+ canvas = BROWSER_CANVAS (object);
+
+ if (canvas->priv) {
+ g_free (canvas->priv);
+ canvas->priv = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->finalize (object);
+}
+
+
+static void
+browser_canvas_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvas *canvas;
+
+ canvas = BROWSER_CANVAS (object);
+
+ switch (param_id) {
+ }
+}
+
+static void
+browser_canvas_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvas *canvas;
+
+ canvas = BROWSER_CANVAS (object);
+
+ switch (param_id) {
+ }
+}
+
+/**
+ * browser_canvas_set_zoom_factor
+ * @canvas: a #BrowserCanvas widget
+ * @n: the zoom factor
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that correspond
+ * to one canvas unit. A zoom factor of 1.0 is the default value; greater than 1.0 makes a zoom in
+ * and lower than 1.0 makes a zoom out.
+ */
+void
+browser_canvas_set_zoom_factor (BrowserCanvas *canvas, gdouble n)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+ g_return_if_fail (canvas->priv);
+
+ goo_canvas_set_scale (canvas->priv->goocanvas, n);
+}
+
+/**
+ * browser_canvas_get_zoom_factor
+ * @canvas: a #BrowserCanvas widget
+ *
+ * Get the current zooming factor of a canvas.
+ *
+ * Returns: the zooming factor.
+ */
+gdouble
+browser_canvas_get_zoom_factor (BrowserCanvas *canvas)
+{
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), 1.);
+ g_return_val_if_fail (canvas->priv, 1.);
+
+ return goo_canvas_get_scale (canvas->priv->goocanvas);
+}
+
+/**
+ * browser_canvas_fit_zoom_factor
+ * @canvas: a #BrowserCanvas widget
+ *
+ * Compute and set the correct zoom factor so that all the items on @canvas can be displayed
+ * at once.
+ *
+ * Returns: the new zooming factor.
+ */
+gdouble
+browser_canvas_fit_zoom_factor (BrowserCanvas *canvas)
+{
+ gdouble zoom, xall, yall;
+ GooCanvasBounds bounds;
+
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), 1.);
+ g_return_val_if_fail (canvas->priv, 1.);
+
+ xall = GTK_WIDGET (canvas)->allocation.width;
+ yall = GTK_WIDGET (canvas)->allocation.height;
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (goo_canvas_get_root_item (canvas->priv->goocanvas)),
+ &bounds);
+ bounds.y1 -= 6.; bounds.y2 += 6.;
+ bounds.x1 -= 6.; bounds.x2 += 6.;
+ zoom = yall / (bounds.y2 - bounds.y1);
+ if (xall / (bounds.x2 - bounds.x1) < zoom)
+ zoom = xall / (bounds.x2 - bounds.x1);
+
+ /* set a limit to the zoom */
+ if (zoom > DEFAULT_SCALE)
+ zoom = DEFAULT_SCALE;
+
+ browser_canvas_set_zoom_factor (canvas, zoom);
+
+ return zoom;
+}
+
+/**
+ * browser_canvas_center
+ * @canvas: a #BrowserCanvas widget
+ *
+ * Centers the display on the layout
+ */
+void
+browser_canvas_center (BrowserCanvas *canvas)
+{
+ /* remove top and left margins if we are running out of space */
+ if (canvas->priv->goocanvas->hadjustment && canvas->priv->goocanvas->vadjustment) {
+ gdouble hlow, hup, vlow, vup, hmargin, vmargin;
+ gdouble left, top, right, bottom;
+ GooCanvasBounds bounds;
+
+ goo_canvas_get_bounds (canvas->priv->goocanvas, &left, &top, &right, &bottom);
+ goo_canvas_item_get_bounds (goo_canvas_get_root_item (canvas->priv->goocanvas),
+ &bounds);
+
+ g_object_get (G_OBJECT (GOO_CANVAS (canvas->priv->goocanvas)->hadjustment),
+ "lower", &hlow, "upper", &hup, NULL);
+ g_object_get (G_OBJECT (GOO_CANVAS (canvas->priv->goocanvas)->vadjustment),
+ "lower", &vlow, "upper", &vup, NULL);
+
+ /*
+ g_print ("Canvas's bounds: %.2f,%.2f -> %.2f,%.2f\n", left, top, right, bottom);
+ g_print ("Root's bounds: %.2f,%.2f -> %.2f,%.2f\n", bounds.x1, bounds.y1, bounds.x2, bounds.y2);
+ g_print ("Xm: %.2f, Ym: %.2f\n", hup - hlow - (right - left), vup - vlow - (bottom - top));
+ */
+ hmargin = hup - hlow - (bounds.x2 - bounds.x1);
+ if (hmargin > 0)
+ left -= hmargin / 2. + (left - bounds.x1);
+ vmargin = vup - vlow - (bounds.y2 - bounds.y1);
+ if (vmargin > 0)
+ top -= vmargin / 2. + (top - bounds.y1);
+ if ((hmargin > 0) || (vmargin > 0)) {
+ goo_canvas_set_bounds (canvas->priv->goocanvas, left, top, right, bottom);
+ /*g_print ("Canvas's new bounds: %.2f,%.2f -> %.2f,%.2f\n", left, top, right, bottom);*/
+ goo_canvas_set_scale (canvas->priv->goocanvas, canvas->priv->goocanvas->scale);
+ }
+ }
+}
+
+/**
+ * browser_canvas_auto_layout_enabled
+ * @canvas: a #BrowserCanvas widget
+ *
+ * Tells if @canvas has the possibility to automatically adjust its layout
+ * using the GraphViz library.
+ *
+ * Returns: TRUE if @canvas can automatically adjust its layout
+ */
+gboolean
+browser_canvas_auto_layout_enabled (BrowserCanvas *canvas)
+{
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), FALSE);
+ g_return_val_if_fail (canvas->priv, FALSE);
+
+#ifdef HAVE_GRAPHVIZ
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+#ifdef HAVE_GRAPHVIZ
+typedef struct {
+ BrowserCanvas *canvas;
+ Agraph_t *graph;
+ GSList *nodes_list; /* list of NodeLayout structures */
+} GraphLayout;
+
+typedef struct {
+ BrowserCanvasItem *item; /* item to be moved */
+ Agnode_t *node;
+ gdouble start_x;
+ gdouble start_y;
+ gdouble end_x;
+ gdouble end_y;
+ gdouble width;
+ gdouble height;
+ gdouble dx;
+ gdouble dy;
+ gboolean stop;
+ gdouble cur_x;
+ gdouble cur_y;
+} NodeLayout;
+
+static gboolean canvas_animate_to (GraphLayout *gl);
+#endif
+
+/**
+ * browser_canvas_auto_layout
+ * @canvas: a #BrowserCanvas widget
+ *
+ * Re-organizes the layout of the @canvas' items using the GraphViz
+ * layout engine.
+ */
+void
+browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate, BrowserCanvasLayoutAlgorithm algorithm)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+ g_return_if_fail (canvas->priv);
+
+#define GV_SCALE 72.
+
+#ifndef HAVE_GRAPHVIZ
+ g_message ("GraphViz library support not compiled, cannot do graph layout...\n");
+ return FALSE;
+#else
+ GSList *list;
+ Agraph_t *graph;
+ GHashTable *nodes_hash; /* key = BrowserCanvasItem, value = Agnode_t *node */
+ GSList *nodes_list = NULL; /* list of NodeLayout structures */
+
+ if (!gvc)
+ gvc = gvContext ();
+
+ graph = agopen ("BrowserCanvasLayout", AGDIGRAPHSTRICT);
+ agnodeattr (graph, "shape", "box");
+ agnodeattr (graph, "height", ".1");
+ agnodeattr (graph, "width", ".1");
+ agnodeattr (graph, "fixedsize", "true");
+ agnodeattr (graph, "pack", "true");
+ agnodeattr (graph, "packmode", "node");
+
+ /* Graph nodes creation */
+ nodes_hash = g_hash_table_new (NULL, NULL);
+ for (list = canvas->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ Agnode_t *node;
+ gchar *tmp;
+ double val;
+ GooCanvasBounds bounds;
+ gboolean moving;
+ NodeLayout *nl;
+
+ g_object_get (G_OBJECT (item), "allow-move", &moving, NULL);
+ if (!moving)
+ continue;
+
+ nl = g_new0 (NodeLayout, 1);
+ nl->item = item;
+ nodes_list = g_slist_prepend (nodes_list, nl);
+
+ tmp = g_strdup_printf ("%p", item);
+ node = agnode (graph, tmp);
+ nl->node = node;
+ g_hash_table_insert (nodes_hash, item, node);
+
+ tmp = g_strdup_printf ("%p", node);
+ agset (node, "label", tmp);
+ g_free (tmp);
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds);
+ nl->width = bounds.x2 - bounds.x1;
+ nl->height = bounds.y2 - bounds.y1;
+ val = (bounds.y2 - bounds.y1) / GV_SCALE;
+ tmp = g_strdup_printf ("%.3f", val);
+ agset (node, "height", tmp);
+ g_free (tmp);
+ val = (bounds.x2 - bounds.x1) / GV_SCALE;
+ tmp = g_strdup_printf ("%.3f", val);
+ agset (node, "width", tmp);
+ g_free (tmp);
+
+ nl->start_x = bounds.x1;
+ nl->start_y = bounds.y1;
+ nl->cur_x = nl->start_x;
+ nl->cur_y = nl->start_y;
+
+ /*g_print ("Before: Node %p: HxW: %.3f %.3f\n", node, (bounds.y2 - bounds.y1) / GV_SCALE,
+ (bounds.x2 - bounds.x1) / GV_SCALE);*/
+ }
+ /* Graph edges creation */
+ for (list = canvas->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ BrowserCanvasItem *from, *to;
+ gboolean moving;
+
+ g_object_get (G_OBJECT (item), "allow-move", &moving, NULL);
+ if (moving)
+ continue;
+
+ browser_canvas_item_get_edge_nodes (item, &from, &to);
+ if (from && to) {
+ Agnode_t *from_node, *to_node;
+ from_node = (Agnode_t*) g_hash_table_lookup (nodes_hash, from);
+ to_node = (Agnode_t*) g_hash_table_lookup (nodes_hash, to);
+ if (from_node && to_node)
+ agedge (graph, from_node, to_node);
+ }
+ }
+
+ switch (algorithm) {
+ default:
+ case BROWSER_CANVAS_LAYOUT_DEFAULT:
+ gvLayout (gvc, graph, "dot");
+ break;
+ case BROWSER_CANVAS_LAYOUT_RADIAL:
+ gvLayout (gvc, graph, "circo");
+ break;
+ }
+ gvRender (gvc, graph, "dot", NULL);
+ /*gvRenderFilename (gvc, graph, "png", "out.png");*/
+ /*gvRender (gvc, graph, "dot", stdout);*/
+
+
+ for (list = nodes_list; list; list = list->next) {
+ NodeLayout *nl = (NodeLayout*) list->data;
+ nl->end_x = ND_coord_i (nl->node).x - (nl->width / 2.);
+ nl->end_y = - ND_coord_i (nl->node).y - (nl->height / 2.);
+ nl->dx = fabs (nl->end_x - nl->start_x);
+ nl->dy = fabs (nl->end_y - nl->start_y);
+ nl->stop = FALSE;
+ /*g_print ("After: Node %p: HxW: %.3f %.3f XxY = %d, %d\n", nl->node,
+ ND_height (nl->node), ND_width (nl->node),
+ ND_coord_i (nl->node).x, - ND_coord_i (nl->node).y);*/
+ if (!animate)
+ goo_canvas_item_translate (GOO_CANVAS_ITEM (nl->item), nl->end_x - nl->start_x,
+ nl->end_y - nl->start_y);
+ }
+
+ g_hash_table_destroy (nodes_hash);
+ gvFreeLayout (gvc, graph);
+
+ if (animate) {
+ GraphLayout *gl;
+ gl = g_new0 (GraphLayout, 1);
+ gl->canvas = canvas;
+ gl->graph = graph;
+ gl->nodes_list = nodes_list;
+ while (canvas_animate_to (gl));
+ }
+ else {
+ agclose (graph);
+ g_slist_foreach (nodes_list, (GFunc) g_free, NULL);
+ g_slist_free (nodes_list);
+ }
+
+#endif
+}
+
+#ifdef HAVE_GRAPHVIZ
+static gdouble
+compute_animation_inc (float start, float stop, float current)
+{
+ gdouble inc;
+#ifndef PI
+#define PI 3.14159265
+#endif
+#define STEPS 30.
+
+ if (stop == start)
+ return 0.;
+
+ inc = (stop - start) / STEPS;
+
+ return inc;
+}
+
+static gboolean
+canvas_animate_to (GraphLayout *gl)
+{
+ gboolean stop = TRUE;
+ GSList *list;
+
+#define EPSILON 1.
+ for (list = gl->nodes_list; list; list = list->next) {
+ NodeLayout *nl = (NodeLayout*) list->data;
+ if (!nl->stop) {
+ gdouble dx, dy, ndx, ndy;
+
+ dx = compute_animation_inc (nl->start_x, nl->end_x, nl->cur_x);
+ dy = compute_animation_inc (nl->start_y, nl->end_y, nl->cur_y);
+ ndx = fabs (nl->cur_x + dx - nl->end_x);
+ ndy = fabs (nl->cur_y + dy - nl->end_y);
+ nl->cur_x += dx;
+ nl->cur_y += dy;
+ browser_canvas_item_translate (nl->item, dx, dy);
+ if (((ndx <= EPSILON) || (ndx >= nl->dx)) &&
+ ((ndy <= EPSILON) || (ndy >= nl->dy)))
+ nl->stop = TRUE;
+ else {
+ stop = FALSE;
+ nl->dx = ndx;
+ nl->dy = ndy;
+ }
+ }
+ }
+
+ goo_canvas_request_update (GOO_CANVAS (gl->canvas->priv->goocanvas));
+ goo_canvas_update (GOO_CANVAS (gl->canvas->priv->goocanvas));
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+ if (stop) {
+ agclose (gl->graph);
+ g_slist_foreach (gl->nodes_list, (GFunc) g_free, NULL);
+ g_slist_free (gl->nodes_list);
+ g_free (gl);
+ }
+ return !stop;
+}
+#endif
+
+/**
+ * browser_canvas_serialize_items
+ */
+gchar *
+browser_canvas_serialize_items (BrowserCanvas *canvas)
+{
+ GSList *list;
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), NULL);
+
+ for (list = canvas->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ TO_IMPLEMENT;
+ }
+}
diff --git a/tools/browser/canvas/browser-canvas.h b/tools/browser/canvas/browser-canvas.h
new file mode 100644
index 0000000..97116e1
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas.h
@@ -0,0 +1,79 @@
+/* browser-canvas.h
+ *
+ * Copyright (C) 2007 - 2008 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __BROWSER_CANVAS__
+#define __BROWSER_CANVAS__
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "browser-canvas-decl.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_BROWSER_CANVAS (browser_canvas_get_type())
+#define BROWSER_CANVAS(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, browser_canvas_get_type(), BrowserCanvas)
+#define BROWSER_CANVAS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, browser_canvas_get_type (), BrowserCanvasClass)
+#define IS_BROWSER_CANVAS(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, browser_canvas_get_type ())
+
+typedef enum {
+ BROWSER_CANVAS_LAYOUT_DEFAULT,
+ BROWSER_CANVAS_LAYOUT_RADIAL
+} BrowserCanvasLayoutAlgorithm;
+
+/* struct for the object's data */
+struct _BrowserCanvas
+{
+ GtkScrolledWindow widget;
+
+ /* pointer position when a context menu was last opened */
+ gdouble xmouse;
+ gdouble ymouse;
+
+ /* private */
+ BrowserCanvasPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasClass
+{
+ GtkScrolledWindowClass parent_class;
+
+ /* virtual functions */
+ void (*clean_canvas_items) (BrowserCanvas *canvas); /* clean any extra structure, not the individual items */
+
+ GtkWidget *(*build_context_menu) (BrowserCanvas *canvas);
+};
+
+/* generic widget's functions */
+GType browser_canvas_get_type (void) G_GNUC_CONST;
+void browser_canvas_declare_item (BrowserCanvas *canvas, BrowserCanvasItem *item);
+
+void browser_canvas_set_zoom_factor (BrowserCanvas *canvas, gdouble n);
+gdouble browser_canvas_get_zoom_factor (BrowserCanvas *canvas);
+gdouble browser_canvas_fit_zoom_factor (BrowserCanvas *canvas);
+gboolean browser_canvas_auto_layout_enabled (BrowserCanvas *canvas);
+void browser_canvas_perform_auto_layout (BrowserCanvas *canvas, gboolean animate,
+ BrowserCanvasLayoutAlgorithm algorithm);
+void browser_canvas_center (BrowserCanvas *canvas);
+gchar *browser_canvas_serialize_items (BrowserCanvas *canvas);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/main.c b/tools/browser/main.c
index 0423b2b..c04992b 100644
--- a/tools/browser/main.c
+++ b/tools/browser/main.c
@@ -20,6 +20,7 @@
*/
#include <glib/gi18n-lib.h>
+#include <glib/gprintf.h>
#include <string.h>
#include <libgdaui/libgdaui.h>
#include "support.h"
@@ -29,14 +30,36 @@
#include "login-dialog.h"
#include "auth-dialog.h"
+/* options */
+gchar *perspective = NULL;
+
+static GOptionEntry entries[] = {
+ { "perspective", 'p', 0, G_OPTION_ARG_STRING, &perspective, "Perspective", "default perspective "
+ "to use when opening windows"},
+ { NULL }
+};
+
int
main (int argc, char *argv[])
{
+ GOptionContext *context;
+ GError *error = NULL;
gboolean have_loop = FALSE;
+ context = g_option_context_new (_("[DSN|connection string]..."));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_set_ignore_unknown_options (context, TRUE);
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_fprintf (stderr, "Can't parse arguments: %s\n", error->message);
+ return EXIT_FAILURE;
+ }
+ g_option_context_free (context);
+
gdaui_init ();
gtk_init (&argc, &argv);
+ browser_core_set_default_factory (perspective);
+
if (argc == 1) {
GError *error = NULL;
if (browser_connection_open (&error))
diff --git a/tools/browser/schema-browser/Makefile.am b/tools/browser/schema-browser/Makefile.am
index bcdc793..aea39bb 100644
--- a/tools/browser/schema-browser/Makefile.am
+++ b/tools/browser/schema-browser/Makefile.am
@@ -23,3 +23,9 @@ libperspective_la_SOURCES = \
table-columns.h \
mgr-columns.c \
mgr-columns.h
+
+if HAVE_GOOCANVAS
+libperspective_la_SOURCES += \
+ table-relations.c \
+ table-relations.h
+endif
diff --git a/tools/browser/schema-browser/favorite-selector.c b/tools/browser/schema-browser/favorite-selector.c
index 98bef55..0972b81 100644
--- a/tools/browser/schema-browser/favorite-selector.c
+++ b/tools/browser/schema-browser/favorite-selector.c
@@ -350,7 +350,8 @@ gboolean tree_store_drag_get_cb (GdauiTreeStore *store, const gchar *path, GtkSe
if (cvalue) {
const gchar *str;
str = g_value_get_string (cvalue);
- gtk_selection_data_set (selection_data, selection_data->target, 8, str, strlen (str));
+ gtk_selection_data_set (selection_data, selection_data->target, 8,
+ (guchar*) str, strlen (str));
return TRUE;
}
}
diff --git a/tools/browser/schema-browser/table-columns.c b/tools/browser/schema-browser/table-columns.c
index 87e597c..6dd920d 100644
--- a/tools/browser/schema-browser/table-columns.c
+++ b/tools/browser/schema-browser/table-columns.c
@@ -604,8 +604,6 @@ follow_if_link (GtkWidget *text_view, GtkTextIter *iter, TableColumns *tcolumns)
table_name,
table_short_name);
}
- else
- g_warning ("Missing information...\n");
}
if (tags)
diff --git a/tools/browser/schema-browser/table-info.c b/tools/browser/schema-browser/table-info.c
index ada59a6..6fc0610 100644
--- a/tools/browser/schema-browser/table-info.c
+++ b/tools/browser/schema-browser/table-info.c
@@ -28,6 +28,9 @@
#include "../support.h"
#include "../cc-gray-bar.h"
#include "table-columns.h"
+#ifdef HAVE_GOOCANVAS
+#include "table-relations.h"
+#endif
#include "schema-browser-perspective.h"
struct _TableInfoPrivate {
@@ -334,8 +337,21 @@ table_info_new (BrowserConnection *bcnc,
str = g_strdup_printf ("<small>%s</small>", _("Columns"));
gtk_label_set_markup (GTK_LABEL (label), str);
g_free (str);
+ gtk_widget_show (page);
gtk_notebook_append_page (GTK_NOTEBOOK (sub_nb), page, label);
}
+#ifdef HAVE_GOOCANVAS
+ page = table_relations_new (tinfo);
+ if (page) {
+ label = gtk_label_new ("");
+ str = g_strdup_printf ("<small>%s</small>", _("Relations"));
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_widget_show (page);
+ gtk_notebook_append_page (GTK_NOTEBOOK (sub_nb), page, label);
+ }
+#endif
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (sub_nb), 0);
/* show everything */
gtk_widget_show_all (top_nb);
diff --git a/tools/browser/schema-browser/table-relations.c b/tools/browser/schema-browser/table-relations.c
new file mode 100644
index 0000000..c04dd2f
--- /dev/null
+++ b/tools/browser/schema-browser/table-relations.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <libgda/gda-tree.h>
+#include "table-info.h"
+#include "table-relations.h"
+#include <libgdaui/gdaui-tree-store.h>
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "schema-browser-perspective.h"
+#include "../canvas/browser-canvas-db-relations.h"
+
+struct _TableRelationsPrivate {
+ BrowserConnection *bcnc;
+ TableInfo *tinfo;
+ GtkWidget *canvas;
+};
+
+static void table_relations_class_init (TableRelationsClass *klass);
+static void table_relations_init (TableRelations *trels, TableRelationsClass *klass);
+static void table_relations_dispose (GObject *object);
+
+static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableRelations *trels);
+
+enum {
+ SELECTION_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint table_relations_signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * TableRelations class implementation
+ */
+
+static void
+table_relations_class_init (TableRelationsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = table_relations_dispose;
+}
+
+
+static void
+table_relations_init (TableRelations *trels, TableRelationsClass *klass)
+{
+ trels->priv = g_new0 (TableRelationsPrivate, 1);
+}
+
+static void
+table_relations_dispose (GObject *object)
+{
+ TableRelations *trels = (TableRelations *) object;
+
+ /* free memory */
+ if (trels->priv) {
+ if (trels->priv->bcnc)
+ g_object_unref (trels->priv->bcnc);
+ g_free (trels->priv);
+ trels->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+table_relations_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo relations = {
+ sizeof (TableRelationsClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) table_relations_class_init,
+ NULL,
+ NULL,
+ sizeof (TableRelations),
+ 0,
+ (GInstanceInitFunc) table_relations_init
+ };
+ type = g_type_register_static (GTK_TYPE_VBOX, "TableRelations", &relations, 0);
+ }
+ return type;
+}
+
+
+static void
+meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableRelations *trels)
+{
+ GdaMetaDbObject *dbo;
+ GValue *tname, *tschema;
+
+ g_object_set (G_OBJECT (trels->priv->canvas), "meta-struct", mstruct, NULL);
+
+ g_value_set_string ((tschema = gda_value_new (G_TYPE_STRING)),
+ table_info_get_table_schema (trels->priv->tinfo));
+ g_value_set_string ((tname = gda_value_new (G_TYPE_STRING)),
+ table_info_get_table_name (trels->priv->tinfo));
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas), NULL,
+ tschema, tname);
+
+ dbo = gda_meta_struct_get_db_object (mstruct, NULL, tschema, tname);
+
+ if (dbo && (dbo->obj_type == GDA_META_DB_TABLE)) {
+ GdaMetaTable *table;
+ table = GDA_META_TABLE (dbo);
+
+ GSList *list;
+ for (list = table->reverse_fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fkey = (GdaMetaTableForeignKey*) list->data;
+ g_value_set_string (tname, fkey->meta_table->obj_name);
+ g_value_set_string (tschema, fkey->meta_table->obj_schema);
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas),
+ NULL,
+ tschema, tname);
+ }
+
+ for (list = table->fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fkey = (GdaMetaTableForeignKey*) list->data;
+ g_value_set_string (tname, fkey->depend_on->obj_name);
+ g_value_set_string (tschema, fkey->depend_on->obj_schema);
+ browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (trels->priv->canvas),
+ NULL,
+ tschema, tname);
+ }
+ }
+ gda_value_free (tschema);
+ gda_value_free (tname);
+
+ browser_canvas_perform_auto_layout (BROWSER_CANVAS (trels->priv->canvas), TRUE,
+ BROWSER_CANVAS_LAYOUT_DEFAULT);
+}
+
+/**
+ * table_relations_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+table_relations_new (TableInfo *tinfo)
+{
+ TableRelations *trels;
+
+ g_return_val_if_fail (IS_TABLE_INFO (tinfo), NULL);
+
+ trels = TABLE_RELATIONS (g_object_new (TABLE_RELATIONS_TYPE, NULL));
+
+ trels->priv->tinfo = tinfo;
+ trels->priv->bcnc = g_object_ref (table_info_get_connection (tinfo));
+ g_signal_connect (trels->priv->bcnc, "meta-changed",
+ G_CALLBACK (meta_changed_cb), trels);
+
+ /*
+ * Relations
+ */
+ trels->priv->canvas = browser_canvas_db_relations_new (NULL);
+ gtk_box_pack_start (GTK_BOX (trels), trels->priv->canvas, TRUE, TRUE, 0);
+ gtk_widget_show (trels);
+
+ /*
+ * initial update
+ */
+ GdaMetaStruct *mstruct;
+ mstruct = browser_connection_get_meta_struct (trels->priv->bcnc);
+ if (mstruct)
+ meta_changed_cb (trels->priv->bcnc, mstruct, trels);
+
+ return (GtkWidget*) trels;
+}
+
diff --git a/tools/browser/schema-browser/table-relations.h b/tools/browser/schema-browser/table-relations.h
new file mode 100644
index 0000000..7d66df4
--- /dev/null
+++ b/tools/browser/schema-browser/table-relations.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TABLE_RELATIONS_H__
+#define __TABLE_RELATIONS_H__
+
+
+#include "table-info.h"
+
+G_BEGIN_DECLS
+
+#define TABLE_RELATIONS_TYPE (table_relations_get_type())
+#define TABLE_RELATIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, TABLE_RELATIONS_TYPE, TableRelations))
+#define TABLE_RELATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, TABLE_RELATIONS_TYPE, TableRelationsClass))
+#define IS_TABLE_RELATIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, TABLE_RELATIONS_TYPE))
+#define IS_TABLE_RELATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TABLE_RELATIONS_TYPE))
+
+typedef struct _TableRelations TableRelations;
+typedef struct _TableRelationsClass TableRelationsClass;
+typedef struct _TableRelationsPrivate TableRelationsPrivate;
+
+struct _TableRelations {
+ GtkVBox parent;
+ TableRelationsPrivate *priv;
+};
+
+struct _TableRelationsClass {
+ GtkVBoxClass parent_class;
+};
+
+GType table_relations_get_type (void) G_GNUC_CONST;
+
+GtkWidget *table_relations_new (TableInfo *tinfo);
+
+G_END_DECLS
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]