[libgda] Tools: re-added canvas part
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Tools: re-added canvas part
- Date: Sun, 30 Nov 2014 22:06:19 +0000 (UTC)
commit 4a3c92c5e7a91687a4aef79786544c769fe51335
Author: Vivien Malerba <malerba gnome-db org>
Date: Sun Nov 30 22:34:34 2014 +0100
Tools: re-added canvas part
configure.ac | 1 +
tools/Makefile.am | 5 +
tools/browser/Makefile.am | 4 +
tools/browser/canvas/Makefile.am | 47 +
tools/browser/canvas/browser-canvas-column.c | 330 ++++++
tools/browser/canvas/browser-canvas-column.h | 64 ++
tools/browser/canvas/browser-canvas-db-relations.c | 1042 +++++++++++++++++
tools/browser/canvas/browser-canvas-db-relations.h | 68 ++
tools/browser/canvas/browser-canvas-decl.h | 44 +
tools/browser/canvas/browser-canvas-fkey.c | 550 +++++++++
tools/browser/canvas/browser-canvas-fkey.h | 63 +
tools/browser/canvas/browser-canvas-item.c | 411 +++++++
tools/browser/canvas/browser-canvas-item.h | 70 ++
tools/browser/canvas/browser-canvas-print.c | 457 ++++++++
tools/browser/canvas/browser-canvas-print.h | 29 +
tools/browser/canvas/browser-canvas-priv.h | 39 +
tools/browser/canvas/browser-canvas-table.c | 603 ++++++++++
tools/browser/canvas/browser-canvas-table.h | 66 ++
tools/browser/canvas/browser-canvas-text.c | 528 +++++++++
tools/browser/canvas/browser-canvas-text.h | 69 ++
tools/browser/canvas/browser-canvas-utility.c | 747 ++++++++++++
tools/browser/canvas/browser-canvas-utility.h | 50 +
tools/browser/canvas/browser-canvas.c | 1183 ++++++++++++++++++++
tools/browser/canvas/browser-canvas.h | 87 ++
tools/browser/schema-browser/Makefile.am | 1 +
tools/browser/schema-browser/relations-diagram.c | 15 +-
tools/browser/schema-browser/table-relations.c | 3 +-
27 files changed, 6567 insertions(+), 9 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e7c10b5..5b34cb9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1019,6 +1019,7 @@ tools/Makefile
tools/gda-sql-6.0.1:tools/gda-sql.1.in
tools/base/Makefile
tools/browser/Makefile
+tools/browser/canvas/Makefile
tools/browser/dummy-perspective/Makefile
tools/browser/schema-browser/Makefile
tools/browser/query-exec/Makefile
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 0fa1c96..d873d26 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -58,6 +58,10 @@ else
EXTRALDFLAGS=
endif
+if HAVE_GOOCANVAS
+CANVAS_LDADD=browser/canvas/libcanvas.la -lm
+endif
+
libbrowser_la_SOURCES=
libbrowser_la_LIBADD = \
@@ -66,6 +70,7 @@ libbrowser_la_LIBADD = \
browser/libbrowsercore.la \
$(ldap_lib) \
browser/schema-browser/libperspective.la \
+ $(CANVAS_LDADD) \
browser/dummy-perspective/libperspective.la \
browser/query-exec/libperspective.la \
browser/data-manager/libperspective.la
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 7853b46..6b672e3 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -4,6 +4,9 @@ SUBDIRS = dummy-perspective schema-browser query-exec data-manager
if LDAP
SUBDIRS += ldap-browser
endif
+if HAVE_GOOCANVAS
+SUBDIRS += canvas
+endif
SUBDIRS += help
AM_CPPFLAGS = \
@@ -93,3 +96,4 @@ EXTRA_DIST = \
CLEANFILES = \
$(appdata_DATA)
+
diff --git a/tools/browser/canvas/Makefile.am b/tools/browser/canvas/Makefile.am
new file mode 100644
index 0000000..3dfc69b
--- /dev/null
+++ b/tools/browser/canvas/Makefile.am
@@ -0,0 +1,47 @@
+noinst_LTLIBRARIES = libcanvas.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/tools \
+ -I$(top_srcdir)/tools/browser \
+ -I$(top_srcdir)/tools/base \
+ -I$(top_builddir) \
+ -I$(top_builddir)/libgda \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
+ $(COREDEPS_CFLAGS) \
+ $(COREDEPS_WFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GOOCANVAS_CFLAGS) \
+ $(GRAPHVIZ_CFLAGS) \
+ $(MAC_INTEGRATION_CFLAGS)
+
+libcanvas_la_SOURCES = \
+ browser-canvas.c \
+ browser-canvas.h \
+ browser-canvas-priv.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_LIBADD = \
+ $(top_builddir)/libgda/libgda-6.0.la \
+ $(COREDEPS_LIBS) \
+ $(GTK_LIBS) \
+ $(GOOCANVAS_LIBS) \
+ $(GRAPHVIZ_LIBS) \
+ $(MAC_INTEGRATION_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..f6ff9f3
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-column.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_META_STRUCT,
+ PROP_COLUMN,
+};
+
+struct _BrowserCanvasColumnPrivate
+{
+ GdaMetaStruct *mstruct;
+ 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,
+ 0
+ };
+
+ 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_META_STRUCT,
+ g_param_spec_object ("meta-struct", NULL, NULL,
+ GDA_TYPE_META_STRUCT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+ 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->mstruct = NULL;
+ 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->mstruct)
+ g_object_unref (cf->priv->mstruct);
+ 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_META_STRUCT:
+ cf->priv->mstruct = g_value_dup_object (value);
+ break;
+ case PROP_COLUMN:
+ g_return_if_fail (cf->priv->mstruct);
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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_META_STRUCT:
+ g_value_set_object (value, cf->priv->mstruct);
+ break;
+ case PROP_COLUMN:
+ g_value_set_pointer (value, cf->priv->column);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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: (skip)
+ * @parent: (allow-none): the parent item, or %NULL
+ * @mstruct: the #GdaMetaStruct @column is from
+ * @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, GdaMetaStruct *mstruct, GdaMetaTableColumn *column,
+ gdouble x, gdouble y, ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_COLUMN, "meta-struct", mstruct, 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 (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, G_GNUC_UNUSED GdkDragContext *drag_context,
+ GtkSelectionData *data, G_GNUC_UNUSED guint info,
+ G_GNUC_UNUSED 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, gtk_selection_data_get_target (data), 8, (guchar*) 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..c01c769
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-column.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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,
+ GdaMetaStruct *mstruct, 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..249bcaa
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ * Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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"
+#include "../objects-cloud.h"
+#include "../fk-declare.h"
+#include <libgda-ui/internal/popup-container.h>
+#include "../browser-window.h"
+#include "../ui-support.h"
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+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);
+static GSList *get_layout_items (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 */
+
+ GdaMetaStruct *mstruct;
+ GooCanvasItem *level_separator; /* all tables items will be above this item and FK lines below */
+
+ GtkWidget *add_dialog;
+ ObjectsCloud *cloud;
+};
+
+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,
+ 0
+ };
+
+ 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;
+ BROWSER_CANVAS_CLASS (class)->get_layout_items = get_layout_items;
+ 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);
+
+ g_hash_table_destroy (canvas->priv->hash_tables);
+ g_hash_table_destroy (canvas->priv->hash_fkeys);
+
+ if (canvas->priv->add_dialog)
+ gtk_widget_destroy (canvas->priv->add_dialog);
+
+ g_free (canvas->priv);
+ canvas->priv = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->dispose (object);
+}
+
+typedef struct {
+ GdaMetaTable *table;
+ GooCanvasBounds bounds;
+} PresentTable;
+
+static void
+browser_canvas_db_relations_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BrowserCanvasDbRelations *canvas;
+ BrowserCanvas *b_canvas;
+
+ canvas = BROWSER_CANVAS_DB_RELATIONS (object);
+ b_canvas = BROWSER_CANVAS (object);
+ if (canvas->priv) {
+ switch (param_id) {
+ case PROP_META_STRUCT: {
+ GSList *present_tables = NULL;
+ GdaMetaStruct *mstruct = g_value_get_object (value);
+ GdaMetaStruct *old_mstruct;
+ if (canvas->priv->mstruct == mstruct)
+ break;
+ if (mstruct)
+ g_object_ref (mstruct);
+
+ if (canvas->priv->mstruct) {
+ GSList *list;
+ for (list = b_canvas->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item;
+ GdaMetaTable *mtable;
+ item = BROWSER_CANVAS_ITEM (list->data);
+ mtable = g_hash_table_lookup (canvas->priv->hash_tables,
+ item);
+ if (! mtable)
+ continue;
+
+ PresentTable *pt;
+ pt = g_new (PresentTable, 1);
+ present_tables = g_slist_prepend (present_tables, pt);
+ pt->table = mtable;
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item),
+ &(pt->bounds));
+ }
+ }
+ old_mstruct = canvas->priv->mstruct;
+ clean_canvas_items (BROWSER_CANVAS (canvas));
+ canvas->priv->mstruct = mstruct;
+ if (present_tables) {
+ GSList *list;
+ for (list = present_tables; list; list = list->next) {
+ PresentTable *pt = (PresentTable*) list->data;
+ GdaMetaDbObject *dbo = (GdaMetaDbObject*) pt->table;
+ GValue *v1 = NULL, *v2 = NULL, *v3 = NULL;
+ BrowserCanvasTable *ctable;
+ GooCanvasBounds bounds;
+
+ if (dbo->obj_catalog)
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)),
+ dbo->obj_catalog);
+ if (dbo->obj_schema)
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)),
+ dbo->obj_schema);
+ if (dbo->obj_name)
+ g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)),
+ dbo->obj_name);
+
+ ctable = browser_canvas_db_relations_add_table (canvas, v1, v2,
+ v3);
+ if (v1) gda_value_free (v1);
+ if (v3) gda_value_free (v2);
+ if (v2) gda_value_free (v3);
+
+ if (ctable) {
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable),
+ &bounds);
+ browser_canvas_translate_item (BROWSER_CANVAS (canvas),
+ (BrowserCanvasItem*) ctable,
+ pt->bounds.x1 - bounds.x1,
+ pt->bounds.y1 - bounds.y1);
+ }
+ g_free (pt);
+ }
+ g_slist_free (present_tables);
+ g_object_set (G_OBJECT (b_canvas->priv->goocanvas),
+ "automatic-bounds", TRUE, NULL);
+ }
+ if (old_mstruct)
+ g_object_unref (old_mstruct);
+
+ if (canvas->priv->cloud)
+ objects_cloud_set_meta_struct (canvas->priv->cloud,
+ canvas->priv->mstruct);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+cloud_object_selected_cb (G_GNUC_UNUSED ObjectsCloud *ocloud, G_GNUC_UNUSED ObjectsCloudObjType sel_type,
+ const gchar *sel_contents, BrowserCanvasDbRelations *dbrel)
+{
+ GdaMetaTable *mtable;
+ GValue *table_schema;
+ GValue *table_name;
+ GdaQuarkList *ql;
+
+ ql = gda_quark_list_new_from_string (sel_contents);
+ g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)),
+ gda_quark_list_find (ql, "OBJ_SCHEMA"));
+ g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)),
+ gda_quark_list_find (ql, "OBJ_NAME"));
+ gda_quark_list_free (ql);
+
+#ifdef GDA_DEBUG_NO
+ g_print ("Add %s.%s\n",
+ g_value_get_string (table_schema), g_value_get_string (table_name));
+#endif
+ mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
+ NULL, table_schema, table_name, NULL);
+ if (mtable) {
+ BrowserCanvasTable *ctable;
+ GooCanvasBounds bounds;
+ gdouble x, y;
+
+ x = BROWSER_CANVAS (dbrel)->xmouse;
+ y = BROWSER_CANVAS (dbrel)->ymouse;
+
+ ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
+ browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
+ x - bounds.x1, y - bounds.y1);
+ }
+ gda_value_free (table_schema);
+ gda_value_free (table_name);
+}
+
+/**
+ * browser_canvas_db_relations_new
+ * @mstruct: (allow-none): 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)
+{
+ BrowserCanvas *canvas;
+ BrowserCanvasDbRelations *dbrels;
+ GooCanvasItem *item;
+ g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
+
+ canvas = BROWSER_CANVAS (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS,
+ "meta-struct", mstruct, NULL));
+ dbrels = BROWSER_CANVAS_DB_RELATIONS (canvas);
+ item = goo_canvas_group_new (goo_canvas_get_root_item (canvas->priv->goocanvas), NULL);
+ dbrels->priv->level_separator = item;
+
+ return GTK_WIDGET (canvas);
+}
+
+static void
+clean_canvas_items (BrowserCanvas *canvas)
+{
+ BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+ GSList *clist, *list;
+
+ /* remove canvas item */
+ clist = g_slist_copy (canvas->priv->items);
+ for (list = clist; list; list = list->next)
+ goo_canvas_item_remove (GOO_CANVAS_ITEM (list->data));
+ g_slist_free (clist);
+
+ /* 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);
+}
+
+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 void popup_func_add_ref_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
+static void popup_func_declare_fk_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 from graph"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_delete_cb), ce);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+ entry = gtk_menu_item_new_with_label (_("Add referenced tables to graph"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_depend_cb), ce);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+ entry = gtk_menu_item_new_with_label (_("Add tables referencing this table to graph"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_ref_cb), ce);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+
+ entry = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+
+ entry = gtk_menu_item_new_with_label (_("Declare foreign key for this table"));
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_declare_fk_cb), ce);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+
+ return menu;
+}
+
+static void
+popup_func_declare_fk_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
+{
+ GtkWidget *dlg, *parent;
+ GdaMetaStruct *mstruct;
+ GdaMetaTable *mtable;
+ gint response;
+
+ parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas
(GOO_CANVAS_ITEM (ce)));
+ g_object_get (G_OBJECT (ce), "meta-struct", &mstruct, "table", &mtable, NULL);
+ dlg = fk_declare_new ((GtkWindow *) parent, mstruct, mtable);
+ response = gtk_dialog_run (GTK_DIALOG (dlg));
+ if (response == GTK_RESPONSE_ACCEPT) {
+ GError *error = NULL;
+ if (! fk_declare_write (FK_DECLARE (dlg),
+ BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+ &error)) {
+ ui_show_error ((GtkWindow *) parent, _("Failed to declare foreign key: %s"),
+ error && error->message ? error->message : _("No detail"));
+ g_clear_error (&error);
+ }
+ else if (BROWSER_IS_WINDOW (parent))
+ browser_window_show_notice (BROWSER_WINDOW (parent),
+ GTK_MESSAGE_INFO, "fkdeclare",
+ _("Successfully declared foreign key"));
+ else
+ ui_show_message ((GtkWindow *) parent, "%s",
+ _("Successfully declared foreign key"));
+ }
+
+ gtk_widget_destroy (dlg);
+ g_object_unref (mstruct);
+}
+
+static void
+popup_func_delete_cb (G_GNUC_UNUSED 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));
+
+ /* remove FK items */
+ GSList *list;
+ for (list = mtable->fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ GooCanvasItem *fk_item;
+
+ fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+ if (fk_item) {
+ goo_canvas_item_remove (fk_item);
+ g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
+ }
+ }
+
+ for (list = mtable->reverse_fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ GooCanvasItem *fk_item;
+
+ fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+ if (fk_item) {
+ goo_canvas_item_remove (fk_item);
+ g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
+ }
+ }
+}
+
+static void
+popup_func_add_depend_cb (G_GNUC_UNUSED 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;
+
+ 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;
+
+ 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);
+ browser_canvas_db_relations_add_table (dbrel, v1, v2, v3);
+ gda_value_free (v1);
+ gda_value_free (v2);
+ gda_value_free (v3);
+ }
+}
+
+static void
+popup_func_add_ref_cb (G_GNUC_UNUSED 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;
+
+ GSList *alldbo, *list;
+
+ alldbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
+ for (list = alldbo; list; list = list->next) {
+ GdaMetaDbObject *fkdbo = GDA_META_DB_OBJECT (list->data);
+ if (fkdbo->obj_type != GDA_META_DB_TABLE)
+ continue;
+
+ GSList *fklist;
+ for (fklist = GDA_META_TABLE (fkdbo)->fk_list; fklist; fklist = fklist->next) {
+ GdaMetaTableForeignKey *fk = GDA_META_TABLE_FOREIGN_KEY (fklist->data);
+ if (fk->depend_on != dbo)
+ continue;
+ if (g_hash_table_lookup (dbrel->priv->hash_tables, fkdbo))
+ continue;
+
+ GValue *v1, *v2, *v3;
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_catalog);
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_schema);
+ g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_name);
+ browser_canvas_db_relations_add_table (dbrel, v1, v2, v3);
+ gda_value_free (v1);
+ gda_value_free (v2);
+ gda_value_free (v3);
+ }
+ }
+ g_slist_free (alldbo);
+}
+
+static GSList *
+complement_layout_items (BrowserCanvasDbRelations *dbrel, BrowserCanvasItem *current, GSList *elist)
+{
+ GSList *items = elist;
+ GdaMetaTable *mtable;
+ mtable = g_hash_table_lookup (dbrel->priv->hash_tables, current);
+ if (!mtable)
+ return items;
+
+ GSList *list;
+ for (list = mtable->fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ BrowserCanvasItem *item;
+
+ item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+ if (item && !g_slist_find (items, item)) {
+ items = g_slist_prepend (items, item);
+ items = complement_layout_items (dbrel, item, items);
+ }
+ }
+
+ for (list = mtable->reverse_fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ BrowserCanvasItem *item;
+
+ item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
+ if (item && !g_slist_find (items, item)) {
+ items = g_slist_prepend (items, item);
+ items = complement_layout_items (dbrel, item, items);
+ }
+ }
+
+ return items;
+}
+
+static GSList *
+get_layout_items (BrowserCanvas *canvas)
+{
+ GSList *items = NULL;
+ BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+
+ if (!canvas->priv->current_selected_item)
+ return g_slist_copy (canvas->priv->items);
+
+ GdaMetaTable *mtable;
+ mtable = g_hash_table_lookup (dbrel->priv->hash_tables, canvas->priv->current_selected_item);
+ if (!mtable)
+ return g_slist_copy (canvas->priv->items);
+
+ items = g_slist_prepend (NULL, canvas->priv->current_selected_item);
+ items = complement_layout_items (dbrel, canvas->priv->current_selected_item, items);
+
+ /* add non related items */
+ GSList *list;
+ for (list = canvas->priv->items; list; list = list->next) {
+ if (!g_slist_find (items, list->data))
+ items = g_slist_prepend (items, list->data);
+ }
+
+ return g_slist_reverse (items);
+}
+static gint dbo_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2);
+static void popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *canvas);
+static void table_menu_item_activated_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel);
+static void popup_add_all_tables_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel);
+static GtkWidget *
+build_context_menu (BrowserCanvas *canvas)
+{
+ GtkWidget *menu, *submitem, *submenu, *mitem;
+ BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
+ GSList *list, *all_dbo;
+
+ if (!dbrel->priv->mstruct)
+ return NULL;
+
+ menu = gtk_menu_new ();
+ /* entry to display a window with tables in it */
+ submitem = gtk_menu_item_new_with_label (_("Add tables"));
+ gtk_widget_show (submitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), submitem);
+ g_signal_connect (G_OBJECT (submitem), "activate", G_CALLBACK (popup_add_table_cb), canvas);
+
+ /* entry to display sub menus */
+ submitem = gtk_menu_item_new_with_label (_("Add one table"));
+ gtk_widget_show (submitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), submitem);
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (submitem), submenu);
+
+ /* build sub menu */
+ GHashTable *schemas = NULL; /* key = schema name, value = a #GtkMenu as parent */
+ GSList *added_schemas = NULL;
+ schemas = g_hash_table_new (g_str_hash, g_str_equal);
+ all_dbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
+ all_dbo = g_slist_sort (all_dbo, (GCompareFunc) dbo_sort_func);
+
+ for (list = all_dbo; list; list = list->next) {
+ GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+ GtkWidget *img;
+ if (dbo->obj_type != GDA_META_DB_TABLE)
+ continue;
+ if (g_hash_table_lookup (dbrel->priv->hash_tables, dbo))
+ /* table already present on canvas */
+ continue;
+
+ if (strcmp (dbo->obj_short_name, dbo->obj_full_name)) {
+ mitem = gtk_image_menu_item_new_with_label (dbo->obj_short_name);
+ img = gtk_image_new_from_pixbuf (ui_get_pixbuf_icon (UI_ICON_TABLE));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
+ g_object_set_data (G_OBJECT (mitem), "dbtable", GDA_META_TABLE (dbo));
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (table_menu_item_activated_cb), dbrel);
+ }
+
+ GtkWidget *schema_menu;
+ schema_menu = g_hash_table_lookup (schemas, dbo->obj_schema);
+ if (!schema_menu) {
+ mitem = gtk_image_menu_item_new_with_label (dbo->obj_schema);
+ img = gtk_image_new_from_pixbuf (ui_get_pixbuf_icon (UI_ICON_SCHEMA));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
+
+ schema_menu = gtk_menu_new ();
+ g_object_set_data (G_OBJECT (schema_menu), "dbo", dbo);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), schema_menu);
+ g_hash_table_insert (schemas, dbo->obj_schema, schema_menu);
+ added_schemas = g_slist_prepend (added_schemas, schema_menu);
+ }
+
+ mitem = gtk_image_menu_item_new_with_label (dbo->obj_short_name);
+ img = gtk_image_new_from_pixbuf (ui_get_pixbuf_icon (UI_ICON_TABLE));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
+ g_object_set_data (G_OBJECT (mitem), "dbtable", GDA_META_TABLE (dbo));
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (schema_menu), mitem);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (table_menu_item_activated_cb), dbrel);
+ }
+ g_slist_free (all_dbo);
+ g_hash_table_destroy (schemas);
+
+ /* entry to add ALL tables */
+ mitem = gtk_separator_menu_item_new ();
+ gtk_widget_show (mitem);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
+
+ mitem = gtk_menu_item_new_with_label (_("Add all tables"));
+ gtk_widget_show (mitem);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_add_all_tables_cb), dbrel);
+
+ /* entry below each schema sub menu to add all tables in schema */
+ for (list = added_schemas; list; list = list->next) {
+ GdaMetaDbObject *dbo;
+ dbo = g_object_get_data (G_OBJECT (list->data), "dbo");
+ g_assert (dbo);
+
+ mitem = gtk_separator_menu_item_new ();
+ gtk_widget_show (mitem);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (list->data), mitem);
+
+ mitem = gtk_menu_item_new_with_label (_("Add all tables in schema"));
+ gtk_widget_show (mitem);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (list->data), mitem);
+ g_object_set_data_full (G_OBJECT (mitem), "schema", g_strdup (dbo->obj_schema),
+ g_free);
+ g_signal_connect (G_OBJECT (mitem), "activate",
+ G_CALLBACK (popup_add_all_tables_cb), dbrel);
+ }
+ g_slist_free (added_schemas);
+
+ gtk_widget_show_all (submenu);
+
+ return menu;
+}
+
+static gint
+dbo_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2)
+{
+ const gchar *n1, *n2;
+ g_assert (dbo1);
+ g_assert (dbo2);
+ if (dbo1->obj_name[0] == '"')
+ n1 = dbo1->obj_name + 1;
+ else
+ n1 = dbo1->obj_name;
+ if (dbo2->obj_name[0] == '"')
+ n2 = dbo2->obj_name + 1;
+ else
+ n2 = dbo2->obj_name;
+ return strcmp (n2, n1);
+}
+
+static void
+popup_add_all_tables_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
+{
+ GSList *all_dbo, *list;
+ const gchar *schema;
+ schema = g_object_get_data (G_OBJECT (mitem), "schema");
+ all_dbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
+ for (list = all_dbo; list; list = list->next) {
+ GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+ if (dbo->obj_type != GDA_META_DB_TABLE)
+ continue;
+ if (g_hash_table_lookup (dbrel->priv->hash_tables, dbo))
+ /* table already present on canvas */
+ continue;
+ if (schema && strcmp (schema, dbo->obj_schema))
+ continue;
+
+ GValue *table_schema;
+ GValue *table_name;
+ BrowserCanvasTable *ctable;
+ GooCanvasBounds bounds;
+ gdouble x, y;
+ g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
+ g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+
+ x = BROWSER_CANVAS (dbrel)->xmouse;
+ y = BROWSER_CANVAS (dbrel)->ymouse;
+
+ ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
+ browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
+ x - bounds.x1, y - bounds.y1);
+ gda_value_free (table_schema);
+ gda_value_free (table_name);
+ }
+ g_slist_free (all_dbo);
+}
+
+static void
+table_menu_item_activated_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
+{
+ GValue *table_schema;
+ GValue *table_name;
+ GdaMetaTable *mtable;
+ GdaMetaDbObject *dbo;
+ BrowserCanvasTable *ctable;
+ GooCanvasBounds bounds;
+ gdouble x, y;
+
+ mtable = g_object_get_data (G_OBJECT (mitem), "dbtable");
+ if (! mtable)
+ return;
+
+ dbo = GDA_META_DB_OBJECT (mtable);
+ g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
+ g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+
+ x = BROWSER_CANVAS (dbrel)->xmouse;
+ y = BROWSER_CANVAS (dbrel)->ymouse;
+
+ ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
+ browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
+ x - bounds.x1, y - bounds.y1);
+ gda_value_free (table_schema);
+ gda_value_free (table_name);
+}
+
+static gboolean
+add_dialog_delete_event (GtkWidget *dialog, G_GNUC_UNUSED GdkEvent *event, G_GNUC_UNUSED gpointer data)
+{
+ gtk_widget_hide (dialog);
+ return TRUE;
+}
+
+static void
+popup_add_table_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrels)
+{
+ if (! dbrels->priv->add_dialog) {
+ GtkWidget *vbox, *cloud, *find, *dcontents;
+ dbrels->priv->add_dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (dbrels->priv->add_dialog),
+ _("Select tables to add to diagram"));
+ gtk_window_set_transient_for (GTK_WINDOW (dbrels->priv->add_dialog),
+ (GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) dbrels));
+ g_signal_connect (dbrels->priv->add_dialog, "delete-event",
+ G_CALLBACK (add_dialog_delete_event), NULL);
+ gtk_window_set_default_size (GTK_WINDOW (dbrels->priv->add_dialog), 430, 400);
+
+ g_object_set_data (G_OBJECT (dbrels->priv->add_dialog), "__canvas", dbrels);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (dbrels->priv->add_dialog), vbox);
+
+ cloud = objects_cloud_new (dbrels->priv->mstruct, OBJECTS_CLOUD_TYPE_TABLE);
+ dbrels->priv->cloud = OBJECTS_CLOUD (cloud);
+ gtk_widget_set_size_request (GTK_WIDGET (cloud), 200, 300);
+ g_signal_connect (cloud, "selected",
+ G_CALLBACK (cloud_object_selected_cb), dbrels);
+ gtk_box_pack_start (GTK_BOX (vbox), cloud, TRUE, TRUE, 0);
+
+ find = objects_cloud_create_filter (OBJECTS_CLOUD (cloud));
+ gtk_box_pack_start (GTK_BOX (vbox), find, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (vbox);
+ }
+
+ gtk_widget_show (dbrels->priv->add_dialog);
+}
+
+/**
+ * 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: (allow-none): the catalog in which the table is, or %NULL
+ * @table_schema: (allow-none): the schema in which the table is, or %NULL
+ * @table_name: the table's name
+ *
+ * Add a table to @canvas.
+ *
+ * Returns: (transfer none): 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);
+
+ GdaMetaTable *mtable;
+ GooCanvas *goocanvas;
+ GError *lerror = NULL;
+
+ 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,
&lerror);
+ if (mtable) {
+ gdouble x = 0, y = 0;
+ GooCanvasItem *table_item;
+
+ table_item = g_hash_table_lookup (canvas->priv->hash_tables, mtable);
+ if (table_item)
+ return BROWSER_CANVAS_TABLE (table_item);
+
+ table_item = browser_canvas_table_new (goo_canvas_get_root_item (goocanvas),
+ canvas->priv->mstruct, 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);
+ 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));
+ goo_canvas_item_raise (GOO_CANVAS_ITEM (table_item), canvas->priv->level_separator);
+
+ /* 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 = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
+ if (!fk_item) {
+ fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item
(goocanvas),
+ canvas->priv->mstruct, 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);
+ goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
+ canvas->priv->level_separator);
+ }
+ }
+ }
+ 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 = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
+ if (!fk_item) {
+ fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item
(goocanvas),
+ canvas->priv->mstruct, 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);
+ goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
+ canvas->priv->level_separator);
+ }
+ }
+ }
+
+ return BROWSER_CANVAS_TABLE (table_item);
+ }
+ else {
+ g_print ("WARNING: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+ g_clear_error (&lerror);
+ return NULL;
+ }
+}
+
+/**
+ * browser_canvas_db_relations_select_table
+ */
+void
+browser_canvas_db_relations_select_table (BrowserCanvasDbRelations *canvas,
+ BrowserCanvasTable *table)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas));
+ g_return_if_fail (!table || IS_BROWSER_CANVAS_ITEM (table));
+
+ browser_canvas_item_toggle_select (BROWSER_CANVAS (canvas), (BrowserCanvasItem*) table);
+}
+
+/**
+ * browser_canvas_db_relations_items_to_data_manager
+ */
+gchar *
+browser_canvas_db_relations_items_to_data_manager (BrowserCanvasDbRelations *canvas)
+{
+ gchar *retval = NULL;
+ GSList *list;
+ xmlDocPtr doc;
+ xmlNodePtr topnode;
+
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), NULL);
+
+ /* create XML doc and root node */
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ topnode = xmlNewDocNode (doc, NULL, BAD_CAST "data", NULL);
+ xmlDocSetRootElement (doc, topnode);
+
+ /* actually serialize all the items which can be serialized */
+ for (list = BROWSER_CANVAS (canvas)->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ GdaMetaTable *mtable;
+
+ mtable = g_hash_table_lookup (canvas->priv->hash_tables, item);
+ if (mtable) {
+ xmlNodePtr node;
+ node = xmlNewChild (topnode, NULL, BAD_CAST "table", NULL);
+ xmlSetProp (node, BAD_CAST "name",
+ BAD_CAST GDA_META_DB_OBJECT (mtable)->obj_short_name);
+
+ GSList *fklist;
+ for (fklist = mtable->fk_list; fklist; fklist = fklist->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) fklist->data;
+ GooCanvasItem *fk_item;
+
+ fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
+ if (fk_item) {
+ node = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
+ xmlSetProp (node, BAD_CAST "foreign_key_table",
+ BAD_CAST fk->depend_on->obj_short_name);
+ }
+ }
+
+ }
+ }
+
+ /* create buffer from XML tree */
+ xmlChar *xstr = NULL;
+ xmlDocDumpFormatMemory (doc, &xstr, NULL, 1);
+ if (xstr) {
+ retval = g_strdup ((gchar *) xstr);
+ xmlFree (xstr);
+ }
+ xmlFreeDoc (doc);
+
+ return retval;
+}
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..59c4267
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-db-relations.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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);
+void browser_canvas_db_relations_select_table (BrowserCanvasDbRelations *canvas,
+ BrowserCanvasTable *table);
+gchar *browser_canvas_db_relations_items_to_data_manager (BrowserCanvasDbRelations *canvas);
+
+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..3aaa395
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-decl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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..48a4b66
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-fkey.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ * Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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"
+#include "../ui-support.h"
+#include "../browser-window.h"
+#include "../fk-declare.h"
+#include "../common/t-utils.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_META_STRUCT,
+ PROP_FK_CONSTRAINT
+};
+
+struct _BrowserCanvasFkeyPrivate
+{
+ GdaMetaStruct *mstruct;
+ 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;
+static GooCanvasLineDash *dash = NULL, *no_dash = 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,
+ 0
+ };
+
+ 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_META_STRUCT,
+ g_param_spec_object ("meta-struct", NULL, NULL,
+ GDA_TYPE_META_STRUCT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+ g_object_class_install_property (object_class, PROP_FK_CONSTRAINT,
+ g_param_spec_pointer ("fk_constraint", "FK constraint",
+ NULL,
+ G_PARAM_WRITABLE));
+
+ dash = goo_canvas_line_dash_new (2, 5., 1.5);
+ no_dash = goo_canvas_line_dash_new (0);
+}
+
+static void
+browser_canvas_fkey_init (BrowserCanvasFkey *cc)
+{
+ cc->priv = g_new0 (BrowserCanvasFkeyPrivate, 1);
+ cc->priv->mstruct = NULL;
+ cc->priv->fk = NULL;
+ cc->priv->fk_table_item = NULL;
+ cc->priv->ref_pk_table_item = NULL;
+ cc->priv->shapes = NULL;
+}
+
+static void
+fk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, G_GNUC_UNUSED BrowserCanvasTable *old_table_item)
+{
+ cc->priv->fk_table_item = NULL;
+}
+
+static void
+ref_pk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, G_GNUC_UNUSED BrowserCanvasTable *old_table_item)
+{
+ cc->priv->ref_pk_table_item = 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);
+ if (cc->priv->mstruct) {
+ g_object_unref (cc->priv->mstruct);
+ cc->priv->mstruct = NULL;
+ }
+ cc->priv->fk = NULL;
+ if (cc->priv->fk_table_item) {
+ g_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
+ (GWeakNotify) fk_table_item_weak_ref_lost, cc);
+ cc->priv->fk_table_item = NULL;
+ }
+ if (cc->priv->ref_pk_table_item) {
+ g_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
+ (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
+ 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_META_STRUCT:
+ cc->priv->mstruct = g_value_dup_object (value);
+ break;
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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) {
+ case PROP_META_STRUCT:
+ g_value_set_object (value, cc->priv->mstruct);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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);
+
+/*
+ * 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_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
+ (GWeakNotify) fk_table_item_weak_ref_lost, 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_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
+ (GWeakNotify) ref_pk_table_item_weak_ref_lost, 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_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) fk_table_item_weak_ref_lost, cc);
+
+ 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);
+
+ 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_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
+ 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);
+
+ /* 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,
+ "line-dash", GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ? dash :
no_dash,
+ 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);
+}
+
+/*
+ * item is for a single FK constraint
+ */
+static gboolean
+single_item_enter_notify_event_cb (GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED 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);
+
+ gchar *str;
+ str = g_strdup_printf ("%s '%s'\n%s: %s\n%s: %s",
+ GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ?
+ _("Declared foreign key") : _("Foreign key"),
+ cc->priv->fk->fk_name,
+ _("Policy on UPDATE"),
+ t_utils_fk_policy_to_string
(GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY (cc->priv->fk)),
+ _("Policy on DELETE"),
+ t_utils_fk_policy_to_string
(GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY (cc->priv->fk)));
+ gtk_widget_set_tooltip_text (GTK_WIDGET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (ci))),
+ str);
+ g_free (str);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+single_item_leave_notify_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED 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 void
+delete_declared_fk_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasFkey *cc)
+{
+ GError *error = NULL;
+ GtkWidget *parent;
+ parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas
(GOO_CANVAS_ITEM (cc)));
+ if (! fk_declare_undeclare (cc->priv->mstruct,
+ BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+ cc->priv->fk, &error)) {
+ ui_show_error ((GtkWindow *) parent, _("Failed to undeclare foreign key: %s"),
+ error && error->message ? error->message : _("No detail"));
+ g_clear_error (&error);
+ }
+ else if (BROWSER_IS_WINDOW (parent))
+ browser_window_show_notice (BROWSER_WINDOW (parent),
+ GTK_MESSAGE_INFO, "fkdeclare",
+ _("Successfully undeclared foreign key"));
+ else
+ ui_show_message ((GtkWindow *) parent, "%s",
+ _("Successfully undeclared foreign key"));
+}
+
+static gboolean
+single_item_button_press_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED GdkEventButton *event, BrowserCanvasFkey *cc)
+{
+ GdaMetaTableForeignKey *fk = g_object_get_data (G_OBJECT (ci), "fkcons");
+ if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk)) {
+ GtkWidget *menu, *entry;
+
+ menu = gtk_menu_new ();
+ entry = gtk_menu_item_new_with_label (_("Remove this declared foreign key"));
+ g_object_set_data (G_OBJECT (entry), "fkcons", fk);
+ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (delete_declared_fk_cb), cc);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+ gtk_widget_show (entry);
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ NULL, NULL, ((GdkEventButton *)event)->button,
+ ((GdkEventButton *)event)->time);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static void
+table_item_moved_cb (G_GNUC_UNUSED GooCanvasItem *table, BrowserCanvasFkey *cc)
+{
+ update_items (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, GdaMetaStruct *mstruct, GdaMetaTableForeignKey *fkcons, ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_FKEY, "meta-struct", mstruct, 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..c4318f0
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-fkey.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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,
+ GdaMetaStruct *mstruct, 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..e50182e
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-item.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "browser-canvas-item.h"
+#include "browser-canvas.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;
+ gboolean allow_select;
+
+ gchar *tooltip_text;
+};
+
+enum
+{
+ MOVED,
+ MOVING,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ALLOW_MOVE,
+ PROP_ALLOW_SELECT,
+ PROP_TOOLTIP_TEXT
+};
+
+static gint browser_canvas_item_signals[LAST_SIGNAL] = { 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,
+ 0
+ };
+ 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);
+
+ class->moved = NULL;
+ class->moving = 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, FALSE, (G_PARAM_READABLE |
G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (object_class, PROP_ALLOW_SELECT,
+ g_param_spec_boolean ("allow-select", NULL, NULL, FALSE, (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) {
+ 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_ALLOW_SELECT:
+ citem->priv->allow_select = 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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_ALLOW_SELECT:
+ g_value_set_boolean (value, citem->priv->allow_select);
+ 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: (allow-none): a place to store the FROM part of the edge, or %NULL
+ * @to: (allow-none): 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, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED GdkEventCrossing *event, G_GNUC_UNUSED 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, G_GNUC_UNUSED GooCanvasItem *target_item,
+ GdkEventButton *event, G_GNUC_UNUSED 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_with_coordinates (GTK_WIDGET (canvas),
+ target_list,
+
GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT,
+ event->button, (GdkEvent*) event,
+ -1, -1);
+ 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_select && (event->state & GDK_CONTROL_MASK)) {
+ browser_canvas_item_toggle_select (browser_canvas_item_get_canvas (citem),
citem);
+ done = TRUE;
+ }
+ 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;
+ goo_canvas_pointer_grab (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (citem)),
+ GOO_CANVAS_ITEM (citem),
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ NULL, event->time);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return done;
+}
+
+static gboolean
+button_release_event (BrowserCanvasItem *citem, G_GNUC_UNUSED GooCanvasItem *target_item,
+ GdkEventButton *event, G_GNUC_UNUSED gpointer data)
+{
+ if (citem->priv->allow_move) {
+ citem->priv->moving = FALSE;
+ goo_canvas_pointer_ungrab (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (citem)),
+ GOO_CANVAS_ITEM (citem), event->time);
+#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, G_GNUC_UNUSED GooCanvasItem *target_item,
+ GdkEventMotion *event, G_GNUC_UNUSED 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..891dfcc
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-item.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BROWSER_CANVAS_ITEM__
+#define __BROWSER_CANVAS_ITEM__
+
+#include <goocanvas.h>
+#include "browser-canvas-decl.h"
+#include <libxml/tree.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);
+
+ /* 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);
+ void (*set_selected) (BrowserCanvasItem *citem, gboolean selected);
+
+ /* serialization and de-serialization virtual methods (don't need to be implemented) */
+ xmlNodePtr (*serialize) (BrowserCanvasItem *citem);
+};
+
+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..610feae
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-print.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ * Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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, G_GNUC_UNUSED 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 (G_GNUC_UNUSED GtkPrintOperation *operation, G_GNUC_UNUSED GtkPrintContext *context,
+ G_GNUC_UNUSED PrintPageData *pdata)
+{
+
+}
+
+static void
+print_draw_page (G_GNUC_UNUSED 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 (G_GNUC_UNUSED GtkPrintOperation *operation, PrintPageData *pdata)
+{
+ GtkWidget *vbox, *bbox, *button, *label, *hbox, *grid, *entry;
+ PrintCustomData *cdata;
+
+ cdata = g_new0 (PrintCustomData, 1);
+ cdata->pdata = pdata;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+
+ /* page size's adjustments */
+ bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ 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_box_new (GTK_ORIENTATION_HORIZONTAL, 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);
+
+ grid = gtk_grid_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), grid, TRUE, TRUE, 0);
+
+ label = gtk_label_new (_("Number of pages used:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+
+ entry = gtk_spin_button_new_with_range (1., 100., 1.);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 0);
+ gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 1, 1);
+ 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_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
+
+ entry = gtk_spin_button_new_with_range (1., 100., 1.);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 0);
+ gtk_grid_attach (GTK_GRID (grid), entry, 1, 1, 1, 1);
+ 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_grid_attach (GTK_GRID (grid), label, 2, 1, 1, 1);
+
+ label = gtk_label_new (_("Zoom factor:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
+
+ entry = gtk_spin_button_new_with_range (.1, 10., .05);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (entry), 2);
+ gtk_grid_attach (GTK_GRID (grid), entry, 1, 2, 1, 1);
+ 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_box_new (GTK_ORIENTATION_HORIZONTAL, 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_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ 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..444ab1f
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-print.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2001 - 2002 Gonzalo Paniagua Javier <gonzalo gnome-db org>
+ * Copyright (C) 2001 - 2002 Rodrigo Moya <rodrigo gnome-db org>
+ * Copyright (C) 2003 Danilo Schoeneberg <dj starfire-programming net>
+ * Copyright (C) 2003 Laurent Sansonetti <lrz gnome org>
+ * Copyright (C) 2005 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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..41681e5
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-priv.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BROWSER_CANVAS_PRIV__
+#define __BROWSER_CANVAS_PRIV__
+
+#include <goocanvas.h>
+#include "browser-canvas-decl.h"
+
+G_BEGIN_DECLS
+
+struct _BrowserCanvasPrivate
+{
+ GooCanvas *goocanvas;
+ GSList *items; /* BrowserCanvasItem objects, non ordered */
+
+ gboolean canvas_moving;
+
+ BrowserCanvasItem *current_selected_item;
+};
+
+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..77b8c7a
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-table.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "browser-canvas.h"
+#include "browser-canvas-priv.h"
+#include "browser-canvas-table.h"
+#include "browser-canvas-column.h"
+#include <glib/gi18n-lib.h>
+#include <string.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);
+static void browser_canvas_table_set_selected (BrowserCanvasItem *citem, gboolean selected);
+
+static xmlNodePtr browser_canvas_table_serialize (BrowserCanvasItem *citem);
+
+enum
+{
+ PROP_0,
+ PROP_META_STRUCT,
+ PROP_TABLE,
+ PROP_MENU_FUNC
+};
+
+struct _BrowserCanvasTablePrivate
+{
+ GdaMetaStruct *mstruct;
+ 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);
+
+ GooCanvasItem *selection_mark;
+};
+
+/* 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,
+ 0
+ };
+
+ 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;
+ iclass->set_selected = browser_canvas_table_set_selected;
+ iclass->serialize = browser_canvas_table_serialize;
+
+ 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_META_STRUCT,
+ g_param_spec_object ("meta-struct", NULL, NULL,
+ GDA_TYPE_META_STRUCT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+ 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->mstruct = NULL;
+ table->priv->table = NULL;
+ table->priv->column_ypos = NULL;
+ table->priv->popup_menu_func = NULL;
+
+ table->priv->selection_mark = NULL;
+
+ 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;
+ if (ce->priv->mstruct) {
+ g_object_unref (ce->priv->mstruct);
+ ce->priv->mstruct = 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_META_STRUCT:
+ ce->priv->mstruct = g_value_dup_object (value);
+ break;
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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_META_STRUCT:
+ g_value_set_object (value, ce->priv->mstruct);
+ break;
+ case PROP_TABLE:
+ g_value_set_pointer (value, ce->priv->table);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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.
+#define SELECTION_SIZE 4.
+ 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, GOO_CANVAS_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),
+ ce->priv->mstruct,
+ 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);
+
+ ce->priv->selection_mark = goo_canvas_rect_new (GOO_CANVAS_ITEM (ce), border_bounds.x1 -
SELECTION_SIZE,
+ border_bounds.y1 - SELECTION_SIZE,
+ border_bounds.x2 + 2 * SELECTION_SIZE,
+ border_bounds.y2 + 2 * SELECTION_SIZE,
+ "radius-x", RADIUS_X,
+ "radius-y", RADIUS_Y,
+ "fill-color", "#11d155",//"#ffea08",
+ "stroke-color", "#11d155",//"#ffea08",
+ NULL);
+ g_object_set (G_OBJECT (ce->priv->selection_mark), "visibility", GOO_CANVAS_ITEM_HIDDEN, NULL);
+
+ /* 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);
+ goo_canvas_item_lower (ce->priv->selection_mark, 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, G_GNUC_UNUSED GooCanvasItem *target_item,
+ GdkEventButton *event,
+ G_GNUC_UNUSED gpointer data)
+{
+ if ((event->button == 3) && 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, GdaMetaStruct *mstruct, GdaMetaTable *table,
+ gdouble x, gdouble y, ...)
+{
+ GooCanvasItem *item;
+ const char *first_property;
+ va_list var_args;
+
+ g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+
+ item = g_object_new (TYPE_BROWSER_CANVAS_TABLE, "meta-struct", mstruct,
+ "allow-move", TRUE,
+ "allow-select", 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, G_GNUC_UNUSED GdkDragContext *drag_context,
+ GtkSelectionData *data, G_GNUC_UNUSED guint info,
+ G_GNUC_UNUSED 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, gtk_selection_data_get_target (data), 8, (guchar*) str, strlen (str));
+ g_free (str);
+}
+
+static void
+browser_canvas_table_set_selected (BrowserCanvasItem *citem, gboolean selected)
+{
+ g_object_set (G_OBJECT (BROWSER_CANVAS_TABLE (citem)->priv->selection_mark),
+ "visibility", selected ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_HIDDEN, NULL);
+}
+
+static xmlNodePtr
+browser_canvas_table_serialize (BrowserCanvasItem *citem)
+{
+ BrowserCanvasTable *ctable;
+
+ ctable = BROWSER_CANVAS_TABLE (citem);
+ if (!ctable->priv->table)
+ return NULL;
+
+ GdaMetaDbObject *dbo;
+ xmlNodePtr node;
+ GooCanvasBounds bounds;
+ gchar *str;
+
+ dbo = GDA_META_DB_OBJECT (ctable->priv->table);
+ node = xmlNewNode (NULL, BAD_CAST "table");
+ xmlSetProp (node, BAD_CAST "schema", BAD_CAST (dbo->obj_schema));
+ xmlSetProp (node, BAD_CAST "name", BAD_CAST (dbo->obj_name));
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (citem), &bounds);
+ str = g_strdup_printf ("%.1f", bounds.x1);
+ xmlSetProp (node, BAD_CAST "x", BAD_CAST str);
+ g_free (str);
+ str = g_strdup_printf ("%.1f", bounds.y1);
+ xmlSetProp (node, BAD_CAST "y", BAD_CAST str);
+ g_free (str);
+
+ return node;
+}
+
+/**
+ * browser_canvas_table_get_anchor_bounds
+ *
+ * Get the bounds to be used to compute anchors, ie. without the selection mark or any other
+ * artefact not part of the table's rectangle.
+ */
+void
+browser_canvas_table_get_anchor_bounds (BrowserCanvasTable *ce, GooCanvasBounds *bounds)
+{
+ g_return_if_fail (IS_BROWSER_CANVAS_TABLE (ce));
+ g_return_if_fail (bounds);
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ce), bounds);
+ bounds->x1 += SELECTION_SIZE;
+ bounds->y1 += SELECTION_SIZE;
+ bounds->x2 -= SELECTION_SIZE;
+ bounds->y2 -= SELECTION_SIZE;
+}
diff --git a/tools/browser/canvas/browser-canvas-table.h b/tools/browser/canvas/browser-canvas-table.h
new file mode 100644
index 0000000..5d59daf
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-table.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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,
+ GdaMetaStruct *mstruct, 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);
+void browser_canvas_table_get_anchor_bounds (BrowserCanvasTable *ce, GooCanvasBounds
*bounds);
+
+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..644bca9
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-text.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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,
+ 0
+ };
+
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+browser_canvas_text_get_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ GParamSpec *pspec)
+{
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ 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, GOO_CANVAS_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 (G_GNUC_UNUSED GooCanvasItem *item, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasText *ct)
+{
+ browser_canvas_text_set_highlight (ct, TRUE);
+ return FALSE;
+}
+
+static gboolean
+leave_notify_cb (G_GNUC_UNUSED GooCanvasItem *item, G_GNUC_UNUSED GooCanvasItem *target_item,
+ G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasText *ct)
+{
+ browser_canvas_text_set_highlight (ct, FALSE);
+ return FALSE;
+}
+
+static guint
+compute_step_value (guint current, guint end)
+{
+ const guint 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..e38bd13
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-text.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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..0e7edb7
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-utility.c
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "browser-canvas-utility.h"
+#include <math.h>
+#include <string.h>
+
+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, GooCanvasAnchorType *anchor_type);
+
+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);
+
+ browser_canvas_table_get_anchor_bounds (fk_ent, &bounds);
+ fx1 = bounds.x1;
+ fy1 = bounds.y1;
+ fx2 = bounds.x2;
+ fy2 = bounds.y2;
+ browser_canvas_table_get_anchor_bounds (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,
+ GOO_CANVAS_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,
+ GOO_CANVAS_ANCHOR_NORTH, NULL);
+ retval = browser_canvas_canvas_shape_add_to_list (retval, id, item);
+ }
+ }
+
+ 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.;
+ GooCanvasAnchorType 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;
+ GooCanvasAnchorType 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);
+ }
+ }
+
+ 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, GooCanvasAnchorType *anchor_type)
+{
+ gdouble mxoff, myoff;
+ GooCanvasAnchorType atype = GOO_CANVAS_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);
+ browser_canvas_table_get_anchor_bounds (ent1, &bounds);
+ xl1 = bounds.x1;
+ yt1 = bounds.y1;
+ xr1 = bounds.x2;
+ browser_canvas_table_get_anchor_bounds (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.;
+ GooCanvasAnchorType 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;
+ GooCanvasAnchorType 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);
+ }
+ }
+
+ goo_canvas_points_unref (points);
+
+ return retval;
+}
+
+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..1eee72a
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas-utility.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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..7ac57bf
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas.c
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2010 David King <davidk openismus com>
+ * Copyright (C) 2010 - 2011 Murray Cumming <murrayc murrayc com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
+#include <libgda-ui/libgda-ui.h>
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#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);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+enum
+{
+ ITEM_SELECTED,
+ LAST_SIGNAL
+};
+
+static gint canvas_signals[LAST_SIGNAL] = { 0 };
+
+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,
+ 0
+ };
+
+ type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "BrowserCanvas", &info, 0);
+ }
+ return type;
+}
+
+static void
+browser_canvas_class_init (BrowserCanvasClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* signals */
+ canvas_signals[ITEM_SELECTED] =
+ g_signal_new ("item-selected",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (BrowserCanvasClass, item_selected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ TYPE_BROWSER_CANVAS_ITEM);
+
+ /* 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 gboolean canvas_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static gboolean motion_notify_event_cb (BrowserCanvas *canvas, GdkEvent *event, GooCanvas *gcanvas);
+static gboolean canvas_scroll_event_cb (GooCanvas *gcanvas, GdkEvent *event, BrowserCanvas *canvas);
+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->priv->current_selected_item = NULL;
+
+ canvas->xmouse = 50.;
+ canvas->ymouse = 50.;
+
+ g_signal_connect (canvas, "event",
+ G_CALLBACK (canvas_event_cb), canvas->priv->goocanvas);
+ g_signal_connect (canvas->priv->goocanvas, "scroll-event",
+ G_CALLBACK (canvas_scroll_event_cb), canvas);
+ g_signal_connect (canvas, "motion-notify-event",
+ G_CALLBACK (motion_notify_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", GOO_CANVAS_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, G_GNUC_UNUSED GdkDragContext *drag_context,
+ G_GNUC_UNUSED 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, G_GNUC_UNUSED 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 (G_GNUC_UNUSED BrowserCanvas *canvas, GdkDragContext *context,
+ gint x, gint y, G_GNUC_UNUSED GtkSelectionData *data,
+ G_GNUC_UNUSED 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 gboolean
+canvas_scroll_event_cb (G_GNUC_UNUSED GooCanvas *gcanvas, GdkEvent *event, BrowserCanvas *canvas)
+{
+ gboolean done = TRUE;
+ GdkEventScroll *ev = (GdkEventScroll *) event;
+
+ switch (event->type) {
+ case GDK_SCROLL:
+ if (ev->state & GDK_SHIFT_MASK) {
+ if (ev->direction == GDK_SCROLL_UP)
+ browser_canvas_scale_layout (canvas, 1.05);
+ else
+ browser_canvas_scale_layout (canvas, .95);
+ }
+ else {
+ if (ev->direction == GDK_SCROLL_UP)
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor
(canvas) + .03);
+ else if (ev->direction == GDK_SCROLL_DOWN)
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor
(canvas) - .03);
+ }
+ done = TRUE;
+ break;
+ default:
+ done = FALSE;
+ break;
+ }
+ return done;
+}
+
+static GdkCursor *hand_cursor = NULL;
+
+static gboolean
+motion_notify_event_cb (BrowserCanvas *canvas, GdkEvent *event, G_GNUC_UNUSED GooCanvas *gcanvas)
+{
+ gboolean done = TRUE;
+
+ switch (event->type) {
+ case GDK_MOTION_NOTIFY:
+ if (((GdkEventMotion*) event)->state & GDK_BUTTON1_MASK) {
+ if (canvas->priv->canvas_moving) {
+ GtkAdjustment *ha, *va;
+ gdouble x, y;
+ gdouble upper, lower, page_size;
+ ha = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (canvas));
+ va = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (canvas));
+
+ upper = gtk_adjustment_get_upper (ha);
+ lower = gtk_adjustment_get_lower (ha);
+ page_size = gtk_adjustment_get_page_size (ha);
+ x = gtk_adjustment_get_value (ha);
+ x = CLAMP (x + canvas->xmouse - ((GdkEventMotion*) event)->x,
+ lower, upper - page_size);
+ gtk_adjustment_set_value (ha, x);
+
+ upper = gtk_adjustment_get_upper (va);
+ lower = gtk_adjustment_get_lower (va);
+ page_size = gtk_adjustment_get_page_size (va);
+ y = gtk_adjustment_get_value (va);
+ y = CLAMP (y + canvas->ymouse - ((GdkEventMotion*) event)->y,
+ lower, upper - page_size);
+ gtk_adjustment_set_value (va, y);
+ }
+ else {
+ canvas->xmouse = ((GdkEventMotion*) event)->x;
+ canvas->ymouse = ((GdkEventMotion*) event)->y;
+ canvas->priv->canvas_moving = TRUE;
+ if (! hand_cursor)
+ hand_cursor = gdk_cursor_new (GDK_HAND2);
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (canvas)),
+ hand_cursor);
+ }
+ }
+ done = TRUE;
+ break;
+ default:
+ done = FALSE;
+ break;
+ }
+ return done;
+}
+
+#ifdef HAVE_GRAPHVIZ
+static void popup_layout_default_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+static void popup_layout_radial_cb (GtkMenuItem *mitem, BrowserCanvas *canvas);
+#endif
+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 gboolean
+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_shell_append (GTK_MENU_SHELL (menu), mitem);
+ }
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_IN, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (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_shell_append (GTK_MENU_SHELL (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_shell_append (GTK_MENU_SHELL (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_shell_append (GTK_MENU_SHELL (menu), mitem);
+
+#ifdef HAVE_GRAPHVIZ
+ mitem = gtk_menu_item_new_with_label (_("Linear layout"));
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK
(popup_layout_default_cb), canvas);
+
+ mitem = gtk_menu_item_new_with_label (_("Radial layout"));
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+ g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK
(popup_layout_radial_cb), canvas);
+
+ mitem = gtk_separator_menu_item_new ();
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+#endif
+
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (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_shell_append (GTK_MENU_SHELL (menu), mitem);
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ NULL, NULL, ((GdkEventButton *)event)->button,
+ ((GdkEventButton *)event)->time);
+ }
+ }
+ done = TRUE;
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (canvas->priv->canvas_moving) {
+ canvas->priv->canvas_moving = FALSE;
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (canvas)), NULL);
+ }
+ break;
+ case GDK_2BUTTON_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) {
+ GooCanvasItem *bitem;
+ for (bitem = item; bitem; bitem = goo_canvas_item_get_parent (bitem)) {
+ if (IS_BROWSER_CANVAS_ITEM (bitem)) {
+ gboolean allow_select;
+ g_object_get (G_OBJECT (bitem), "allow-select", &allow_select, NULL);
+ if (allow_select) {
+ browser_canvas_item_toggle_select (canvas,
BROWSER_CANVAS_ITEM (bitem));
+ break;
+ }
+ }
+ }
+ }
+ else
+ browser_canvas_fit_zoom_factor (canvas);
+ done = TRUE;
+ break;
+ default:
+ done = FALSE;
+ break;
+ }
+ return done;
+}
+
+#ifdef HAVE_GRAPHVIZ
+static void
+popup_layout_default_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_perform_auto_layout (canvas, TRUE, BROWSER_CANVAS_LAYOUT_DEFAULT);
+}
+
+static void
+popup_layout_radial_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_perform_auto_layout (canvas, TRUE, BROWSER_CANVAS_LAYOUT_RADIAL);
+}
+#endif
+
+static void
+popup_zoom_in_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) + .05);
+}
+
+static void
+popup_zoom_out_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_set_zoom_factor (canvas, browser_canvas_get_zoom_factor (canvas) - .05);
+}
+
+static void
+popup_zoom_fit_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_fit_zoom_factor (canvas);
+}
+
+static void
+popup_export_cb (G_GNUC_UNUSED 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_is_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);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
+ gdaui_get_default_path ());
+ 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;
+
+ gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)));
+ 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 (canvas->priv->goocanvas, 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 ((GtkWindow*) toplevel,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE, NULL);
+ gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (errdlg), 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 (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvas *canvas)
+{
+ browser_canvas_print (canvas);
+}
+
+
+static void
+weak_ref_lost (BrowserCanvas *canvas, BrowserCanvasItem *old_item)
+{
+ canvas->priv->items = g_slist_remove (canvas->priv->items, old_item);
+ if (canvas->priv->current_selected_item == old_item) {
+ canvas->priv->current_selected_item = NULL;
+ g_signal_emit (canvas, canvas_signals [ITEM_SELECTED], 0, NULL);
+ }
+}
+
+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_object_weak_unref (G_OBJECT (list->data), (GWeakNotify) weak_ref_lost, 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_object_weak_ref (G_OBJECT (item), (GWeakNotify) weak_ref_lost, canvas);
+}
+
+
+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);
+}
+
+/**
+ * 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);
+
+ if (n < 0.01)
+ n = 0.01;
+ else if (n > 1.)
+ n = 1.;
+ 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.);
+
+ GtkAllocation alloc;
+ gtk_widget_get_allocation (GTK_WIDGET (canvas), &alloc);
+ xall = alloc.width;
+ yall = alloc.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;
+#else
+ BrowserCanvasClass *class = BROWSER_CANVAS_CLASS (G_OBJECT_GET_CLASS (canvas));
+ GSList *list, *layout_items;
+ Agraph_t *graph;
+ GHashTable *nodes_hash; /* key = BrowserCanvasItem, value = Agnode_t *node */
+ GSList *nodes_list = NULL; /* list of NodeLayout structures */
+
+ if (!gvc)
+ gvc = gvContext ();
+
+#ifdef GRAPHVIZ_NEW_API
+ graph = agopen ("BrowserCanvasLayout", Agdirected, NULL);
+ agset (graph, "shape", "box");
+ agset (graph, "height", ".1");
+ agset (graph, "width", ".1");
+ agset (graph, "fixedsize", "true");
+ agset (graph, "pack", "true");
+ agset (graph, "packmode", "node");
+#else
+ graph = agopen ("BrowserCanvasLayout", AGRAPH);
+ agnodeattr (graph, "shape", "box");
+ agnodeattr (graph, "height", ".1");
+ agnodeattr (graph, "width", ".1");
+ agnodeattr (graph, "fixedsize", "true");
+ agnodeattr (graph, "pack", "true");
+ agnodeattr (graph, "packmode", "node");
+#endif
+
+
+ if (class->get_layout_items)
+ layout_items = class->get_layout_items (canvas);
+ else
+ layout_items = canvas->priv->items;
+
+ /* Graph nodes creation */
+ nodes_hash = g_hash_table_new (NULL, NULL);
+ for (list = layout_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);
+#ifdef GRAPHVIZ_NEW_API
+ node = agnode (graph, tmp, 0);
+#else
+ node = agnode (graph, tmp);
+#endif
+ 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 = layout_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) {
+#ifdef GRAPHVIZ_NEW_API
+ agedge (graph, from_node, to_node, "", 0);
+#else
+ agedge (graph, from_node, to_node);
+#endif
+ }
+ }
+ }
+
+ if (layout_items != canvas->priv->items)
+ g_slist_free (layout_items);
+
+ 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, G_GNUC_UNUSED 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_scale_layout
+ */
+void
+browser_canvas_scale_layout (BrowserCanvas *canvas, gdouble scale)
+{
+ GSList *list;
+ GooCanvasBounds ref_bounds;
+ gdouble refx, refy;
+
+ g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+ if (!canvas->priv->items)
+ return;
+
+ goo_canvas_get_bounds (canvas->priv->goocanvas, &ref_bounds.x1, &ref_bounds.y1,
+ &ref_bounds.x2, &ref_bounds.y2);
+ refx = (ref_bounds.x2 - ref_bounds.x1) / 2.;
+ refy = (ref_bounds.y2 - ref_bounds.y1) / 2.;
+ for (list = canvas->priv->items; list; list = list->next) {
+ gboolean can_move;
+ g_object_get ((GObject*) list->data, "allow-move", &can_move, NULL);
+ if (can_move) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ GooCanvasBounds bounds;
+ gdouble tx, ty;
+
+ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds);
+ tx = (scale - 1.) * (bounds.x1 - refx);
+ ty = (scale - 1.) * (bounds.y1 - refy);
+ browser_canvas_item_translate (item, tx, ty);
+ }
+ }
+}
+
+/**
+ * browser_canvas_serialize_items
+ */
+gchar *
+browser_canvas_serialize_items (BrowserCanvas *canvas)
+{
+ gchar *retval = NULL;
+ GSList *list;
+ xmlDocPtr doc;
+ xmlNodePtr topnode;
+
+ g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), NULL);
+
+ /* create XML doc and root node */
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ topnode = xmlNewDocNode (doc, NULL, BAD_CAST "canvas", NULL);
+ xmlDocSetRootElement (doc, topnode);
+
+ /* actually serialize all the items which can be serialized */
+ for (list = canvas->priv->items; list; list = list->next) {
+ BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
+ BrowserCanvasItemClass *iclass = (BrowserCanvasItemClass*) G_OBJECT_GET_CLASS (item);
+ if (iclass->serialize) {
+ xmlNodePtr node;
+ node = iclass->serialize (item);
+ if (node)
+ xmlAddChild (topnode, node);
+ }
+ }
+
+ /* create buffer from XML tree */
+ xmlChar *xstr = NULL;
+ xmlDocDumpMemory (doc, &xstr, NULL);
+ if (xstr) {
+ retval = g_strdup ((gchar *) xstr);
+ xmlFree (xstr);
+ }
+ xmlFreeDoc (doc);
+
+ return retval;
+}
+
+/**
+ * browser_canvas_item_toggle_select
+ */
+void
+browser_canvas_item_toggle_select (BrowserCanvas *canvas, BrowserCanvasItem *item)
+{
+ gboolean do_select = TRUE;
+ g_return_if_fail (IS_BROWSER_CANVAS (canvas));
+ g_return_if_fail (!item || IS_BROWSER_CANVAS_ITEM (item));
+
+ if (canvas->priv->current_selected_item == item) {
+ /* deselect item */
+ do_select = FALSE;
+ }
+
+ if (canvas->priv->current_selected_item) {
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS
(canvas->priv->current_selected_item));
+ if (iclass->set_selected)
+ iclass->set_selected (canvas->priv->current_selected_item, FALSE);
+ canvas->priv->current_selected_item = NULL;
+ }
+
+
+ if (do_select && item) {
+ BrowserCanvasItemClass *iclass = BROWSER_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item));
+ if (iclass->set_selected)
+ iclass->set_selected (item, TRUE);
+ canvas->priv->current_selected_item = item;
+ }
+ g_signal_emit (canvas, canvas_signals [ITEM_SELECTED], 0, item);
+}
+
+void
+browser_canvas_translate_item (G_GNUC_UNUSED BrowserCanvas *canvas, BrowserCanvasItem *item,
+ gdouble dx, gdouble dy)
+{
+ browser_canvas_item_translate (item, dx, dy);
+}
diff --git a/tools/browser/canvas/browser-canvas.h b/tools/browser/canvas/browser-canvas.h
new file mode 100644
index 0000000..2b0049a
--- /dev/null
+++ b/tools/browser/canvas/browser-canvas.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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, or while moving around the canvas */
+ gdouble xmouse;
+ gdouble ymouse;
+
+ /* private */
+ BrowserCanvasPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _BrowserCanvasClass
+{
+ GtkScrolledWindowClass parent_class;
+
+ /* signals */
+ void (*item_selected) (BrowserCanvas *canvas, BrowserCanvasItem *item);
+
+ /* virtual functions */
+ void (*clean_canvas_items) (BrowserCanvas *canvas); /* clean any extra structure, not the
individual items */
+ GSList *(*get_layout_items) (BrowserCanvas *canvas);
+
+ 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);
+void browser_canvas_scale_layout (BrowserCanvas *canvas, gdouble scale);
+
+gchar *browser_canvas_serialize_items (BrowserCanvas *canvas);
+
+void browser_canvas_item_toggle_select (BrowserCanvas *canvas, BrowserCanvasItem *item);
+void browser_canvas_translate_item (BrowserCanvas *canvas, BrowserCanvasItem *item,
+ gdouble dx, gdouble dy);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/schema-browser/Makefile.am b/tools/browser/schema-browser/Makefile.am
index 326f24e..ebad86e 100644
--- a/tools/browser/schema-browser/Makefile.am
+++ b/tools/browser/schema-browser/Makefile.am
@@ -10,6 +10,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/tools/common \
-I$(top_srcdir)/tools/browser \
-I$(top_builddir) \
+ -I$(top_builddir)/libgda \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_srcdir)/libgda/sqlite \
diff --git a/tools/browser/schema-browser/relations-diagram.c
b/tools/browser/schema-browser/relations-diagram.c
index e28d3b6..3adfe9c 100644
--- a/tools/browser/schema-browser/relations-diagram.c
+++ b/tools/browser/schema-browser/relations-diagram.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 - 2012 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2010 David King <davidk openismus com>
* Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
*
@@ -20,8 +20,8 @@
#include <glib/gi18n-lib.h>
#include <string.h>
+#include "t-app.h"
#include "relations-diagram.h"
-#include "../support.h"
#include "../gdaui-bar.h"
#include "../canvas/browser-canvas-db-relations.h"
#include <gdk/gdkkeysyms.h>
@@ -30,7 +30,8 @@
#include "../browser-perspective.h"
#include "../browser-window.h"
#include "../data-manager/data-manager-perspective.h"
-#include "../../base-tool-utils.h"
+#include "../ui-support.h"
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
struct _RelationsDiagramPrivate {
TConnection *tcnc;
@@ -332,7 +333,7 @@ relations_diagram_new (TConnection *tcnc)
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
diagram->priv->header = GDAUI_BAR (label);
- wid = gdaui_bar_add_button_from_stock (GDAUI_BAR (label), GTK_STOCK_SAVE);
+ wid = gdaui_bar_add_button_from_icon_name (GDAUI_BAR (label), "document-save");
diagram->priv->save_button = wid;
g_signal_connect (wid, "clicked",
@@ -566,9 +567,9 @@ relations_diagram_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_b
tab_name = g_strdup (_("Diagram"));
table_pixbuf = ui_get_pixbuf_icon (UI_ICON_DIAGRAM);
- wid = browser_make_tab_label_with_pixbuf (tab_name,
- table_pixbuf,
- out_close_button ? TRUE : FALSE, out_close_button);
+ wid = ui_make_tab_label_with_pixbuf (tab_name,
+ table_pixbuf,
+ out_close_button ? TRUE : FALSE, out_close_button);
g_free (tab_name);
return wid;
}
diff --git a/tools/browser/schema-browser/table-relations.c b/tools/browser/schema-browser/table-relations.c
index 3dc1b1b..f0147fe 100644
--- a/tools/browser/schema-browser/table-relations.c
+++ b/tools/browser/schema-browser/table-relations.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 - 2011 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2009 - 2014 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2010 David King <davidk openismus com>
* Copyright (C) 2011 Murray Cumming <murrayc murrayc com>
*
@@ -26,7 +26,6 @@
#include "table-info.h"
#include "table-relations.h"
#include <libgda-ui/gdaui-tree-store.h>
-#include "../support.h"
#include "../gdaui-bar.h"
#include "schema-browser-perspective.h"
#include "../canvas/browser-canvas-db-relations.h"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]