1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 
21 #include <gtk/gtk.h>
22 #include <libgda/libgda.h>
23 #include <glib/gi18n-lib.h>
24 #include "browser-canvas-priv.h"
25 #include "browser-canvas-db-relations.h"
26 #include "browser-canvas-table.h"
27 #include "browser-canvas-column.h"
28 #include "browser-canvas-fkey.h"
29 #include "../common/objects-cloud.h"
30 #include "../common/fk-declare.h"
31 #include <libgda-ui/internal/popup-container.h>
32 #include "../browser-window.h"
33 #include "../support.h"
34 
35 static void browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass *class);
36 static void browser_canvas_db_relations_init       (BrowserCanvasDbRelations *canvas);
37 static void browser_canvas_db_relations_dispose   (GObject *object);
38 
39 static void browser_canvas_db_relations_set_property (GObject *object,
40 						      guint param_id,
41 						      const GValue *value,
42 						      GParamSpec *pspec);
43 static void browser_canvas_db_relations_get_property (GObject *object,
44 						      guint param_id,
45 						      GValue *value,
46 						      GParamSpec *pspec);
47 
48 /* virtual functions */
49 static void       clean_canvas_items  (BrowserCanvas *canvas);
50 static GtkWidget *build_context_menu  (BrowserCanvas *canvas);
51 static GSList    *get_layout_items    (BrowserCanvas *canvas);
52 
53 /* get a pointer to the parents to be able to call their destructor */
54 static GObjectClass *parent_class = NULL;
55 
56 enum
57 {
58         PROP_0,
59 	PROP_META_STRUCT
60 };
61 
62 struct _BrowserCanvasDbRelationsPrivate
63 {
64 	GHashTable       *hash_tables; /* key = GdaMetaTable, value = BrowserCanvasMetaTable (and the reverse) */
65 	GHashTable       *hash_fkeys; /* key = GdaMetaTableForeignKey, value = BrowserCanvasFkey */
66 
67 	GdaMetaStruct    *mstruct;
68 	GooCanvasItem    *level_separator; /* all tables items will be above this item and FK lines below */
69 
70 	GtkWidget        *add_dialog;
71 	ObjectsCloud     *cloud;
72 };
73 
74 GType
browser_canvas_db_relations_get_type(void)75 browser_canvas_db_relations_get_type (void)
76 {
77 	static GType type = 0;
78 
79 	if (G_UNLIKELY (type == 0)) {
80 		static const GTypeInfo info = {
81 			sizeof (BrowserCanvasDbRelationsClass),
82 			(GBaseInitFunc) NULL,
83 			(GBaseFinalizeFunc) NULL,
84 			(GClassInitFunc) browser_canvas_db_relations_class_init,
85 			NULL,
86 			NULL,
87 			sizeof (BrowserCanvasDbRelations),
88 			0,
89 			(GInstanceInitFunc) browser_canvas_db_relations_init,
90 			0
91 		};
92 
93 		type = g_type_register_static (TYPE_BROWSER_CANVAS, "BrowserCanvasDbRelations", &info, 0);
94 	}
95 	return type;
96 }
97 
98 static void
browser_canvas_db_relations_init(BrowserCanvasDbRelations * canvas)99 browser_canvas_db_relations_init (BrowserCanvasDbRelations * canvas)
100 {
101 	canvas->priv = g_new0 (BrowserCanvasDbRelationsPrivate, 1);
102 	canvas->priv->hash_tables = g_hash_table_new (NULL, NULL);
103 	canvas->priv->hash_fkeys = g_hash_table_new (NULL, NULL);
104 	canvas->priv->mstruct = NULL;
105 }
106 
107 static void
browser_canvas_db_relations_class_init(BrowserCanvasDbRelationsClass * class)108 browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass * class)
109 {
110 	GObjectClass   *object_class = G_OBJECT_CLASS (class);
111 	parent_class = g_type_class_peek_parent (class);
112 
113 	/* BrowserCanvas virtual functions */
114 	BROWSER_CANVAS_CLASS (class)->clean_canvas_items = clean_canvas_items;
115 	BROWSER_CANVAS_CLASS (class)->build_context_menu = build_context_menu;
116 	BROWSER_CANVAS_CLASS (class)->get_layout_items = get_layout_items;
117 	object_class->dispose = browser_canvas_db_relations_dispose;
118 
119 	/* properties */
120 	object_class->set_property = browser_canvas_db_relations_set_property;
121         object_class->get_property = browser_canvas_db_relations_get_property;
122 	g_object_class_install_property (object_class, PROP_META_STRUCT,
123                                          g_param_spec_object ("meta-struct", "GdaMetaStruct", NULL,
124 							      GDA_TYPE_META_STRUCT,
125 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
126 }
127 
128 static void
browser_canvas_db_relations_dispose(GObject * object)129 browser_canvas_db_relations_dispose (GObject *object)
130 {
131 	BrowserCanvasDbRelations *canvas;
132 
133 	g_return_if_fail (object != NULL);
134 	g_return_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (object));
135 
136 	canvas = BROWSER_CANVAS_DB_RELATIONS (object);
137 
138 	if (canvas->priv) {
139 		clean_canvas_items (BROWSER_CANVAS (canvas));
140 		if (canvas->priv->mstruct)
141 			g_object_unref (canvas->priv->mstruct);
142 
143 		g_hash_table_destroy (canvas->priv->hash_tables);
144 		g_hash_table_destroy (canvas->priv->hash_fkeys);
145 
146 		if (canvas->priv->add_dialog)
147 			gtk_widget_destroy (canvas->priv->add_dialog);
148 
149 		g_free (canvas->priv);
150 		canvas->priv = NULL;
151 	}
152 
153 	/* for the parent class */
154 	parent_class->dispose (object);
155 }
156 
157 typedef struct {
158 	GdaMetaTable   *table;
159 	GooCanvasBounds bounds;
160 } PresentTable;
161 
162 static void
browser_canvas_db_relations_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)163 browser_canvas_db_relations_set_property (GObject *object,
164 					  guint param_id,
165 					  const GValue *value,
166 					  GParamSpec *pspec)
167 {
168 	BrowserCanvasDbRelations *canvas;
169 	BrowserCanvas *b_canvas;
170 
171         canvas = BROWSER_CANVAS_DB_RELATIONS (object);
172 	b_canvas = BROWSER_CANVAS (object);
173         if (canvas->priv) {
174                 switch (param_id) {
175 		case PROP_META_STRUCT: {
176 			GSList *present_tables = NULL;
177 			GdaMetaStruct *mstruct = g_value_get_object (value);
178 			GdaMetaStruct *old_mstruct;
179 			if (canvas->priv->mstruct == mstruct)
180 				break;
181 			if (mstruct)
182 				g_object_ref (mstruct);
183 
184 			if (canvas->priv->mstruct) {
185 				GSList *list;
186 				for (list = b_canvas->priv->items; list; list = list->next) {
187 					BrowserCanvasItem *item;
188 					GdaMetaTable *mtable;
189 					item = BROWSER_CANVAS_ITEM (list->data);
190 					mtable = g_hash_table_lookup (canvas->priv->hash_tables,
191 								      item);
192 					if (! mtable)
193 						continue;
194 
195 					PresentTable *pt;
196 					pt = g_new (PresentTable, 1);
197 					present_tables = g_slist_prepend (present_tables, pt);
198 					pt->table = mtable;
199 					goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item),
200 								    &(pt->bounds));
201 				}
202 			}
203 			old_mstruct = canvas->priv->mstruct;
204 			clean_canvas_items (BROWSER_CANVAS (canvas));
205 			canvas->priv->mstruct = mstruct;
206 			if (present_tables) {
207 				GSList *list;
208 				for (list = present_tables; list; list = list->next) {
209 					PresentTable *pt = (PresentTable*) list->data;
210 					GdaMetaDbObject *dbo = (GdaMetaDbObject*) pt->table;
211 					GValue *v1 = NULL, *v2 = NULL, *v3 = NULL;
212 					BrowserCanvasTable *ctable;
213 					GooCanvasBounds bounds;
214 
215 					if (dbo->obj_catalog)
216 						g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)),
217 								    dbo->obj_catalog);
218 					if (dbo->obj_schema)
219 						g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)),
220 								    dbo->obj_schema);
221 					if (dbo->obj_name)
222 						g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)),
223 								    dbo->obj_name);
224 
225 					ctable = browser_canvas_db_relations_add_table (canvas, v1, v2,
226 											v3);
227 					if (v1) gda_value_free (v1);
228 					if (v3) gda_value_free (v2);
229 					if (v2) gda_value_free (v3);
230 
231 					if (ctable) {
232 						goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable),
233 									    &bounds);
234 						browser_canvas_translate_item (BROWSER_CANVAS (canvas),
235 									       (BrowserCanvasItem*) ctable,
236 									       pt->bounds.x1 - bounds.x1,
237 									       pt->bounds.y1 - bounds.y1);
238 					}
239 					g_free (pt);
240 				}
241 				g_slist_free (present_tables);
242 				g_object_set (G_OBJECT (b_canvas->priv->goocanvas),
243 					      "automatic-bounds", TRUE, NULL);
244 			}
245 			if (old_mstruct)
246 				g_object_unref (old_mstruct);
247 
248 			if (canvas->priv->cloud)
249 				objects_cloud_set_meta_struct (canvas->priv->cloud,
250 							       canvas->priv->mstruct);
251 			break;
252 		}
253 		default:
254 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
255 			break;
256 		}
257 	}
258 }
259 
260 static void
browser_canvas_db_relations_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)261 browser_canvas_db_relations_get_property (GObject *object,
262 					  guint param_id,
263 					  GValue *value,
264 					  GParamSpec *pspec)
265 {
266 	BrowserCanvasDbRelations *canvas;
267 
268         canvas = BROWSER_CANVAS_DB_RELATIONS (object);
269         if (canvas->priv) {
270                 switch (param_id) {
271 		case PROP_META_STRUCT:
272 			g_value_set_object (value, canvas->priv->mstruct);
273 			break;
274 		default:
275 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
276 			break;
277 		}
278 	}
279 }
280 
281 static void
cloud_object_selected_cb(G_GNUC_UNUSED ObjectsCloud * ocloud,G_GNUC_UNUSED ObjectsCloudObjType sel_type,const gchar * sel_contents,BrowserCanvasDbRelations * dbrel)282 cloud_object_selected_cb (G_GNUC_UNUSED ObjectsCloud *ocloud, G_GNUC_UNUSED ObjectsCloudObjType sel_type,
283 			  const gchar *sel_contents, BrowserCanvasDbRelations *dbrel)
284 {
285 	GdaMetaTable *mtable;
286 	GValue *table_schema;
287 	GValue *table_name;
288 	GdaQuarkList *ql;
289 
290 	ql = gda_quark_list_new_from_string (sel_contents);
291 	g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)),
292 			    gda_quark_list_find (ql, "OBJ_SCHEMA"));
293 	g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)),
294 			    gda_quark_list_find (ql, "OBJ_NAME"));
295 	gda_quark_list_free (ql);
296 
297 #ifdef GDA_DEBUG_NO
298 	g_print ("Add %s.%s\n",
299 		 g_value_get_string (table_schema), g_value_get_string (table_name));
300 #endif
301 	mtable = (GdaMetaTable*) gda_meta_struct_complement (dbrel->priv->mstruct, GDA_META_DB_TABLE,
302 							     NULL, table_schema, table_name, NULL);
303 	if (mtable) {
304 		BrowserCanvasTable *ctable;
305 		GooCanvasBounds bounds;
306 		gdouble x, y;
307 
308 		x = BROWSER_CANVAS (dbrel)->xmouse;
309 		y = BROWSER_CANVAS (dbrel)->ymouse;
310 
311 		ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
312 		goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
313 		browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
314 					       x - bounds.x1, y - bounds.y1);
315 	}
316 	gda_value_free (table_schema);
317 	gda_value_free (table_name);
318 }
319 
320 /**
321  * browser_canvas_db_relations_new
322  * @mstruct: (allow-none): a #GdaMetaStruct object, or %NULL
323  *
324  * Creates a new canvas widget to display the relations between the database's tables.
325  *
326  * After the #BrowserCanvasDbRelations has been created, it is possible to display tables
327  * using browser_canvas_db_relations_add_table().
328  *
329  * Returns: a new #GtkWidget widget
330  */
331 GtkWidget *
browser_canvas_db_relations_new(GdaMetaStruct * mstruct)332 browser_canvas_db_relations_new (GdaMetaStruct *mstruct)
333 {
334 	BrowserCanvas *canvas;
335 	BrowserCanvasDbRelations *dbrels;
336 	GooCanvasItem *item;
337 	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
338 
339 	canvas = BROWSER_CANVAS (g_object_new (TYPE_BROWSER_CANVAS_DB_RELATIONS,
340 					       "meta-struct", mstruct, NULL));
341 	dbrels = BROWSER_CANVAS_DB_RELATIONS (canvas);
342 	item = goo_canvas_group_new (goo_canvas_get_root_item (canvas->priv->goocanvas), NULL);
343 	dbrels->priv->level_separator = item;
344 
345         return GTK_WIDGET (canvas);
346 }
347 
348 static void
clean_canvas_items(BrowserCanvas * canvas)349 clean_canvas_items (BrowserCanvas *canvas)
350 {
351 	BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
352 	GSList *clist, *list;
353 
354 	/* remove canvas item */
355 	clist = g_slist_copy (canvas->priv->items);
356 	for (list = clist; list; list = list->next)
357 		goo_canvas_item_remove (GOO_CANVAS_ITEM (list->data));
358 	g_slist_free (clist);
359 
360 	/* clean memory */
361 	g_hash_table_destroy (dbrel->priv->hash_tables);
362 	g_hash_table_destroy (dbrel->priv->hash_fkeys);
363 	dbrel->priv->hash_tables = g_hash_table_new (NULL, NULL);
364 	dbrel->priv->hash_fkeys = g_hash_table_new (NULL, NULL);
365 }
366 
367 static GtkWidget *canvas_entity_popup_func (BrowserCanvasTable *ce);
368 
369 static void popup_func_delete_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
370 static void popup_func_add_depend_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
371 static void popup_func_add_ref_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
372 static void popup_func_declare_fk_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
373 
374 static GtkWidget *
canvas_entity_popup_func(BrowserCanvasTable * ce)375 canvas_entity_popup_func (BrowserCanvasTable *ce)
376 {
377 	GtkWidget *menu, *entry;
378 
379 	menu = gtk_menu_new ();
380 	entry = gtk_menu_item_new_with_label (_("Remove from graph"));
381 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_delete_cb), ce);
382 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
383 	gtk_widget_show (entry);
384 	entry = gtk_menu_item_new_with_label (_("Add referenced tables to graph"));
385 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_depend_cb), ce);
386 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
387 	gtk_widget_show (entry);
388 	entry = gtk_menu_item_new_with_label (_("Add tables referencing this table to graph"));
389 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_ref_cb), ce);
390 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
391 	gtk_widget_show (entry);
392 
393 	entry = gtk_separator_menu_item_new ();
394 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
395 	gtk_widget_show (entry);
396 
397 	entry = gtk_menu_item_new_with_label (_("Declare foreign key for this table"));
398 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_declare_fk_cb), ce);
399 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
400 	gtk_widget_show (entry);
401 
402 	return menu;
403 }
404 
405 static void
popup_func_declare_fk_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasTable * ce)406 popup_func_declare_fk_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
407 {
408 	GtkWidget *dlg, *parent;
409 	GdaMetaStruct *mstruct;
410 	GdaMetaTable *mtable;
411 	gint response;
412 
413 	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (ce)));
414 	g_object_get (G_OBJECT (ce), "meta-struct", &mstruct, "table", &mtable, NULL);
415 	dlg = fk_declare_new ((GtkWindow *) parent, mstruct, mtable);
416 	response = gtk_dialog_run (GTK_DIALOG (dlg));
417 	if (response == GTK_RESPONSE_ACCEPT) {
418 		GError *error = NULL;
419 		if (! fk_declare_write (FK_DECLARE (dlg),
420 					BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
421 					&error)) {
422 			browser_show_error ((GtkWindow *) parent, _("Failed to declare foreign key: %s"),
423 					    error && error->message ? error->message : _("No detail"));
424 			g_clear_error (&error);
425 		}
426 		else if (BROWSER_IS_WINDOW (parent))
427 			browser_window_show_notice (BROWSER_WINDOW (parent),
428 						    GTK_MESSAGE_INFO, "fkdeclare",
429 						    _("Successfully declared foreign key"));
430 		else
431 			browser_show_message ((GtkWindow *) parent, "%s",
432 					      _("Successfully declared foreign key"));
433 	}
434 
435 	gtk_widget_destroy (dlg);
436 	g_object_unref (mstruct);
437 }
438 
439 static void
popup_func_delete_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasTable * ce)440 popup_func_delete_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
441 {
442 	GdaMetaTable *mtable;
443 	BrowserCanvasDbRelations *dbrel;
444 
445 	dbrel = BROWSER_CANVAS_DB_RELATIONS (browser_canvas_item_get_canvas (BROWSER_CANVAS_ITEM (ce)));
446 
447 	mtable = g_hash_table_lookup (dbrel->priv->hash_tables, ce);
448 	g_hash_table_remove (dbrel->priv->hash_tables, ce);
449 	g_hash_table_remove (dbrel->priv->hash_tables, mtable);
450 	goo_canvas_item_remove (GOO_CANVAS_ITEM (ce));
451 
452 	/* remove FK items */
453 	GSList *list;
454 	for (list = mtable->fk_list; list; list = list->next) {
455 		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
456 		GooCanvasItem *fk_item;
457 
458 		fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
459 		if (fk_item) {
460 			goo_canvas_item_remove (fk_item);
461 			g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
462 		}
463 	}
464 
465 	for (list = mtable->reverse_fk_list; list; list = list->next) {
466 		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
467 		GooCanvasItem *fk_item;
468 
469 		fk_item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
470 		if (fk_item) {
471 			goo_canvas_item_remove (fk_item);
472 			g_hash_table_remove (dbrel->priv->hash_fkeys, fk);
473 		}
474 	}
475 }
476 
477 static void
popup_func_add_depend_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasTable * ce)478 popup_func_add_depend_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
479 {
480 	BrowserCanvasDbRelations *dbrel;
481 	GdaMetaDbObject *dbo;
482 
483 	dbrel = BROWSER_CANVAS_DB_RELATIONS (browser_canvas_item_get_canvas (BROWSER_CANVAS_ITEM (ce)));
484 	dbo = g_hash_table_lookup (dbrel->priv->hash_tables, ce);
485 	if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE))
486 		return;
487 
488 	if (!dbrel->priv->mstruct)
489 		return;
490 
491 	GdaMetaTable *mtable = GDA_META_TABLE (dbo);
492 	GSList *list;
493 
494 	for (list = mtable->fk_list; list; list = list->next) {
495 		GdaMetaTableForeignKey *fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
496 		if (fk->depend_on->obj_type != GDA_META_DB_TABLE)
497 			continue;
498 		if (g_hash_table_lookup (dbrel->priv->hash_tables, fk->depend_on))
499 			continue;
500 
501 		GValue *v1, *v2, *v3;
502 		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_catalog);
503 		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_schema);
504 		g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), fk->depend_on->obj_name);
505 		browser_canvas_db_relations_add_table (dbrel, v1, v2, v3);
506 		gda_value_free (v1);
507 		gda_value_free (v2);
508 		gda_value_free (v3);
509 	}
510 }
511 
512 static void
popup_func_add_ref_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasTable * ce)513 popup_func_add_ref_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
514 {
515 	BrowserCanvasDbRelations *dbrel;
516 	GdaMetaDbObject *dbo;
517 
518 	dbrel = BROWSER_CANVAS_DB_RELATIONS (browser_canvas_item_get_canvas (BROWSER_CANVAS_ITEM (ce)));
519 	dbo = g_hash_table_lookup (dbrel->priv->hash_tables, ce);
520 	if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE))
521 		return;
522 
523 	if (!dbrel->priv->mstruct)
524 		return;
525 
526 	GSList *alldbo, *list;
527 
528 	alldbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
529 	for (list = alldbo; list; list = list->next) {
530 		GdaMetaDbObject *fkdbo = GDA_META_DB_OBJECT (list->data);
531 		if (fkdbo->obj_type != GDA_META_DB_TABLE)
532 			continue;
533 
534 		GSList *fklist;
535 		for (fklist = GDA_META_TABLE (fkdbo)->fk_list; fklist; fklist = fklist->next) {
536 			GdaMetaTableForeignKey *fk = GDA_META_TABLE_FOREIGN_KEY (fklist->data);
537 			if (fk->depend_on != dbo)
538 				continue;
539 			if (g_hash_table_lookup (dbrel->priv->hash_tables, fkdbo))
540 				continue;
541 
542 			GValue *v1, *v2, *v3;
543 			g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_catalog);
544 			g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_schema);
545 			g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), fkdbo->obj_name);
546 			browser_canvas_db_relations_add_table (dbrel, v1, v2, v3);
547 			gda_value_free (v1);
548 			gda_value_free (v2);
549 			gda_value_free (v3);
550 		}
551 	}
552 	g_slist_free (alldbo);
553 }
554 
555 static GSList *
complement_layout_items(BrowserCanvasDbRelations * dbrel,BrowserCanvasItem * current,GSList * elist)556 complement_layout_items (BrowserCanvasDbRelations *dbrel, BrowserCanvasItem *current, GSList *elist)
557 {
558 	GSList *items = elist;
559 	GdaMetaTable *mtable;
560 	mtable = g_hash_table_lookup (dbrel->priv->hash_tables, current);
561 	if (!mtable)
562 		return items;
563 
564 	GSList *list;
565 	for (list = mtable->fk_list; list; list = list->next) {
566 		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
567 		BrowserCanvasItem *item;
568 
569 		item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
570 		if (item && !g_slist_find (items, item)) {
571 			items = g_slist_prepend (items, item);
572 			items = complement_layout_items (dbrel, item, items);
573 		}
574 	}
575 
576 	for (list = mtable->reverse_fk_list; list; list = list->next) {
577 		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
578 		BrowserCanvasItem *item;
579 
580 		item = g_hash_table_lookup (dbrel->priv->hash_fkeys, fk);
581 		if (item && !g_slist_find (items, item)) {
582 			items = g_slist_prepend (items, item);
583 			items = complement_layout_items (dbrel, item, items);
584 		}
585 	}
586 
587 	return items;
588 }
589 
590 static GSList *
get_layout_items(BrowserCanvas * canvas)591 get_layout_items (BrowserCanvas *canvas)
592 {
593 	GSList *items = NULL;
594 	BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
595 
596 	if (!canvas->priv->current_selected_item)
597 		return g_slist_copy (canvas->priv->items);
598 
599 	GdaMetaTable *mtable;
600 	mtable = g_hash_table_lookup (dbrel->priv->hash_tables, canvas->priv->current_selected_item);
601 	if (!mtable)
602 		return g_slist_copy (canvas->priv->items);
603 
604 	items = g_slist_prepend (NULL, canvas->priv->current_selected_item);
605 	items = complement_layout_items (dbrel, canvas->priv->current_selected_item, items);
606 
607 	/* add non related items */
608 	GSList *list;
609 	for (list = canvas->priv->items; list; list = list->next) {
610 		if (!g_slist_find (items, list->data))
611 			items = g_slist_prepend (items, list->data);
612 	}
613 
614 	return g_slist_reverse (items);
615 }
616 static gint dbo_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2);
617 static void popup_add_table_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *canvas);
618 static void table_menu_item_activated_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel);
619 static void popup_add_all_tables_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel);
620 static GtkWidget *
build_context_menu(BrowserCanvas * canvas)621 build_context_menu (BrowserCanvas *canvas)
622 {
623 	GtkWidget *menu, *submitem, *submenu, *mitem;
624 	BrowserCanvasDbRelations *dbrel = BROWSER_CANVAS_DB_RELATIONS (canvas);
625 	GSList *list, *all_dbo;
626 
627 	if (!dbrel->priv->mstruct)
628 		return NULL;
629 
630 	menu = gtk_menu_new ();
631 	/* entry to display a window with tables in it */
632 	submitem = gtk_menu_item_new_with_label (_("Add tables"));
633 	gtk_widget_show (submitem);
634 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), submitem);
635 	g_signal_connect (G_OBJECT (submitem), "activate", G_CALLBACK (popup_add_table_cb), canvas);
636 
637 	/* entry to display sub menus */
638 	submitem = gtk_menu_item_new_with_label (_("Add one table"));
639 	gtk_widget_show (submitem);
640 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), submitem);
641 	submenu = gtk_menu_new ();
642 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (submitem), submenu);
643 
644 	/* build sub menu */
645 	GHashTable *schemas = NULL; /* key = schema name, value = a #GtkMenu as parent */
646 	GSList *added_schemas = NULL;
647 	schemas = g_hash_table_new (g_str_hash, g_str_equal);
648 	all_dbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
649 	all_dbo = g_slist_sort (all_dbo, (GCompareFunc) dbo_sort_func);
650 
651 	for (list = all_dbo; list; list = list->next) {
652 		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
653 		GtkWidget *img;
654 		if (dbo->obj_type != GDA_META_DB_TABLE)
655 			continue;
656 		if (g_hash_table_lookup (dbrel->priv->hash_tables, dbo))
657 			/* table already present on canvas */
658 			continue;
659 
660 		if (strcmp (dbo->obj_short_name, dbo->obj_full_name)) {
661 			mitem = gtk_image_menu_item_new_with_label (dbo->obj_short_name);
662 			img = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
663 			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
664 			g_object_set_data (G_OBJECT (mitem), "dbtable", GDA_META_TABLE (dbo));
665 			gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
666 			g_signal_connect (mitem, "activate",
667 					  G_CALLBACK (table_menu_item_activated_cb), dbrel);
668 		}
669 
670 		GtkWidget *schema_menu;
671 		schema_menu = g_hash_table_lookup (schemas, dbo->obj_schema);
672 		if (!schema_menu) {
673 			mitem = gtk_image_menu_item_new_with_label (dbo->obj_schema);
674 			img = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_SCHEMA));
675 			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
676 			gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
677 
678 			schema_menu = gtk_menu_new ();
679 			g_object_set_data (G_OBJECT (schema_menu), "dbo", dbo);
680 			gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), schema_menu);
681 			g_hash_table_insert (schemas, dbo->obj_schema, schema_menu);
682 			added_schemas = g_slist_prepend (added_schemas, schema_menu);
683 		}
684 
685 		mitem = gtk_image_menu_item_new_with_label (dbo->obj_short_name);
686 		img = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
687 		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mitem), img);
688 		g_object_set_data (G_OBJECT (mitem), "dbtable", GDA_META_TABLE (dbo));
689 		gtk_menu_shell_prepend (GTK_MENU_SHELL (schema_menu), mitem);
690 		g_signal_connect (mitem, "activate",
691 				  G_CALLBACK (table_menu_item_activated_cb), dbrel);
692 	}
693 	g_slist_free (all_dbo);
694 	g_hash_table_destroy (schemas);
695 
696 	/* entry to add ALL tables */
697 	mitem = gtk_separator_menu_item_new ();
698 	gtk_widget_show (mitem);
699 	gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
700 
701 	mitem = gtk_menu_item_new_with_label (_("Add all tables"));
702 	gtk_widget_show (mitem);
703 	gtk_menu_shell_prepend (GTK_MENU_SHELL (submenu), mitem);
704 	g_signal_connect (G_OBJECT (mitem), "activate", G_CALLBACK (popup_add_all_tables_cb), dbrel);
705 
706 	/* entry below each schema sub menu to add all tables in schema */
707 	for (list = added_schemas; list; list = list->next) {
708 		GdaMetaDbObject *dbo;
709 		dbo = g_object_get_data (G_OBJECT (list->data), "dbo");
710 		g_assert (dbo);
711 
712 		mitem = gtk_separator_menu_item_new ();
713 		gtk_widget_show (mitem);
714 		gtk_menu_shell_prepend (GTK_MENU_SHELL (list->data), mitem);
715 
716 		mitem = gtk_menu_item_new_with_label (_("Add all tables in schema"));
717 		gtk_widget_show (mitem);
718 		gtk_menu_shell_prepend (GTK_MENU_SHELL (list->data), mitem);
719 		g_object_set_data_full (G_OBJECT (mitem), "schema", g_strdup (dbo->obj_schema),
720 					g_free);
721 		g_signal_connect (G_OBJECT (mitem), "activate",
722 				  G_CALLBACK (popup_add_all_tables_cb), dbrel);
723 	}
724 	g_slist_free (added_schemas);
725 
726 	gtk_widget_show_all (submenu);
727 
728 	return menu;
729 }
730 
731 static gint
dbo_sort_func(GdaMetaDbObject * dbo1,GdaMetaDbObject * dbo2)732 dbo_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2)
733 {
734 	const gchar *n1, *n2;
735 	g_assert (dbo1);
736 	g_assert (dbo2);
737 	if (dbo1->obj_name[0] ==  '"')
738 		n1 = dbo1->obj_name + 1;
739 	else
740 		n1 = dbo1->obj_name;
741 	if (dbo2->obj_name[0] ==  '"')
742 		n2 = dbo2->obj_name + 1;
743 	else
744 		n2 = dbo2->obj_name;
745 	return strcmp (n2, n1);
746 }
747 
748 static void
popup_add_all_tables_cb(GtkMenuItem * mitem,BrowserCanvasDbRelations * dbrel)749 popup_add_all_tables_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
750 {
751 	GSList *all_dbo, *list;
752 	const gchar *schema;
753 	schema = g_object_get_data (G_OBJECT (mitem), "schema");
754 	all_dbo = gda_meta_struct_get_all_db_objects (dbrel->priv->mstruct);
755 	for (list = all_dbo; list; list = list->next) {
756 		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
757 		if (dbo->obj_type != GDA_META_DB_TABLE)
758 			continue;
759 		if (g_hash_table_lookup (dbrel->priv->hash_tables, dbo))
760 			/* table already present on canvas */
761 			continue;
762 		if (schema && strcmp (schema, dbo->obj_schema))
763 			continue;
764 
765 		GValue *table_schema;
766 		GValue *table_name;
767 		BrowserCanvasTable *ctable;
768 		GooCanvasBounds bounds;
769 		gdouble x, y;
770 		g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
771 		g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
772 
773 		x = BROWSER_CANVAS (dbrel)->xmouse;
774 		y = BROWSER_CANVAS (dbrel)->ymouse;
775 
776 		ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
777 		goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
778 		browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
779 					       x - bounds.x1, y - bounds.y1);
780 		gda_value_free (table_schema);
781 		gda_value_free (table_name);
782 	}
783 	g_slist_free (all_dbo);
784 }
785 
786 static void
table_menu_item_activated_cb(GtkMenuItem * mitem,BrowserCanvasDbRelations * dbrel)787 table_menu_item_activated_cb (GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrel)
788 {
789 	GValue *table_schema;
790 	GValue *table_name;
791 	GdaMetaTable *mtable;
792 	GdaMetaDbObject *dbo;
793 	BrowserCanvasTable *ctable;
794 	GooCanvasBounds bounds;
795 	gdouble x, y;
796 
797 	mtable = g_object_get_data (G_OBJECT (mitem), "dbtable");
798 	if (! mtable)
799 		return;
800 
801 	dbo = GDA_META_DB_OBJECT (mtable);
802 	g_value_set_string ((table_schema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
803 	g_value_set_string ((table_name = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
804 
805 	x = BROWSER_CANVAS (dbrel)->xmouse;
806 	y = BROWSER_CANVAS (dbrel)->ymouse;
807 
808 	ctable = browser_canvas_db_relations_add_table (dbrel, NULL, table_schema, table_name);
809 	goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (ctable), &bounds);
810 	browser_canvas_item_translate (BROWSER_CANVAS_ITEM (ctable),
811 				       x - bounds.x1, y - bounds.y1);
812 	gda_value_free (table_schema);
813 	gda_value_free (table_name);
814 }
815 
816 static gboolean
add_dialog_delete_event(GtkWidget * dialog,G_GNUC_UNUSED GdkEvent * event,G_GNUC_UNUSED gpointer data)817 add_dialog_delete_event (GtkWidget *dialog, G_GNUC_UNUSED GdkEvent *event, G_GNUC_UNUSED gpointer data)
818 {
819 	gtk_widget_hide (dialog);
820 	return TRUE;
821 }
822 
823 static void
popup_add_table_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasDbRelations * dbrels)824 popup_add_table_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasDbRelations *dbrels)
825 {
826 	if (! dbrels->priv->add_dialog) {
827 		GtkWidget *vbox, *cloud, *find, *dcontents;
828 		dbrels->priv->add_dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
829 		gtk_window_set_title (GTK_WINDOW (dbrels->priv->add_dialog),
830 				      _("Select tables to add to diagram"));
831 		gtk_window_set_transient_for (GTK_WINDOW (dbrels->priv->add_dialog),
832 					      (GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) dbrels));
833 		g_signal_connect (dbrels->priv->add_dialog, "delete-event",
834 				  G_CALLBACK (add_dialog_delete_event), NULL);
835 		gtk_window_set_default_size (GTK_WINDOW (dbrels->priv->add_dialog), 430, 400);
836 
837 		g_object_set_data (G_OBJECT (dbrels->priv->add_dialog), "__canvas", dbrels);
838 
839 		vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
840 		gtk_container_add (GTK_CONTAINER (dbrels->priv->add_dialog), vbox);
841 
842 		cloud = objects_cloud_new (dbrels->priv->mstruct, OBJECTS_CLOUD_TYPE_TABLE);
843 		dbrels->priv->cloud = OBJECTS_CLOUD (cloud);
844 		gtk_widget_set_size_request (GTK_WIDGET (cloud), 200, 300);
845 		g_signal_connect (cloud, "selected",
846 				  G_CALLBACK (cloud_object_selected_cb), dbrels);
847 		gtk_box_pack_start (GTK_BOX (vbox), cloud, TRUE, TRUE, 0);
848 
849 		find = objects_cloud_create_filter (OBJECTS_CLOUD (cloud));
850 		gtk_box_pack_start (GTK_BOX (vbox), find, FALSE, FALSE, 0);
851 
852 		gtk_widget_show_all (vbox);
853 	}
854 
855 	gtk_widget_show (dbrels->priv->add_dialog);
856 }
857 
858 /**
859  * browser_canvas_db_relations_get_table_item
860  * @canvas:
861  * @table:
862  *
863  * Returns:
864  */
865 BrowserCanvasTable *
browser_canvas_db_relations_get_table_item(BrowserCanvasDbRelations * canvas,GdaMetaTable * table)866 browser_canvas_db_relations_get_table_item  (BrowserCanvasDbRelations *canvas, GdaMetaTable *table)
867 {
868 	BrowserCanvasTable *table_item;
869 	g_return_val_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas), NULL);
870 	g_return_val_if_fail (canvas->priv, NULL);
871 
872 	table_item = g_hash_table_lookup (canvas->priv->hash_tables, table);
873 	return BROWSER_CANVAS_TABLE (table_item);
874 }
875 
876 /**
877  * browser_canvas_db_relations_add_table
878  * @canvas: a #BrowserCanvasDbRelations canvas
879  * @table_catalog: (allow-none): the catalog in which the table is, or %NULL
880  * @table_schema: (allow-none): the schema in which the table is, or %NULL
881  * @table_name: the table's name
882  *
883  * Add a table to @canvas.
884  *
885  * Returns: (transfer none): the corresponding canvas item, or %NULL if the table was not found.
886  */
887 BrowserCanvasTable *
browser_canvas_db_relations_add_table(BrowserCanvasDbRelations * canvas,const GValue * table_catalog,const GValue * table_schema,const GValue * table_name)888 browser_canvas_db_relations_add_table  (BrowserCanvasDbRelations *canvas,
889 					const GValue *table_catalog, const GValue *table_schema,
890 					const GValue *table_name)
891 {
892 	g_return_val_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas), NULL);
893 
894 	GdaMetaTable *mtable;
895 	GooCanvas *goocanvas;
896 	GError *lerror = NULL;
897 
898 	if (!canvas->priv->mstruct)
899 		return NULL;
900 
901 	goocanvas = BROWSER_CANVAS (canvas)->priv->goocanvas;
902 	mtable = (GdaMetaTable *) gda_meta_struct_complement (canvas->priv->mstruct, GDA_META_DB_TABLE,
903 							      table_catalog, table_schema, table_name, &lerror);
904 	if (mtable) {
905 		gdouble x = 0, y = 0;
906 		GooCanvasItem *table_item;
907 
908 		table_item = g_hash_table_lookup (canvas->priv->hash_tables, mtable);
909 		if (table_item)
910 			return BROWSER_CANVAS_TABLE (table_item);
911 
912 		table_item = browser_canvas_table_new (goo_canvas_get_root_item (goocanvas),
913 						       canvas->priv->mstruct, mtable, x, y, NULL);
914 		g_hash_table_insert (canvas->priv->hash_tables, mtable, table_item);
915 		g_hash_table_insert (canvas->priv->hash_tables, table_item, mtable);
916 		g_object_set (G_OBJECT (table_item),
917 			      "popup_menu_func", canvas_entity_popup_func, NULL);
918 		browser_canvas_declare_item (BROWSER_CANVAS (canvas),
919 					     BROWSER_CANVAS_ITEM (table_item));
920 		goo_canvas_item_raise (GOO_CANVAS_ITEM (table_item), canvas->priv->level_separator);
921 
922 		/* if there are some FK links, then also add them */
923 		GSList *list;
924 		for (list = mtable->fk_list; list; list = list->next) {
925 			GooCanvasItem *ref_table_item;
926 			GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
927 			ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->depend_on);
928 			if (ref_table_item) {
929 				GooCanvasItem *fk_item;
930 				fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
931 				if (!fk_item) {
932 					fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas),
933 									   canvas->priv->mstruct, fk, NULL);
934 					browser_canvas_declare_item (BROWSER_CANVAS (canvas),
935 								     BROWSER_CANVAS_ITEM (fk_item));
936 
937 					g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
938 					goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
939 							       canvas->priv->level_separator);
940 				}
941 			}
942 		}
943 		for (list = mtable->reverse_fk_list; list; list = list->next) {
944 			GooCanvasItem *ref_table_item;
945 			GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
946 			ref_table_item = g_hash_table_lookup (canvas->priv->hash_tables, fk->meta_table);
947 			if (ref_table_item) {
948 				GooCanvasItem *fk_item;
949 				fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
950 				if (!fk_item) {
951 					fk_item = browser_canvas_fkey_new (goo_canvas_get_root_item (goocanvas),
952 									   canvas->priv->mstruct, fk, NULL);
953 					browser_canvas_declare_item (BROWSER_CANVAS (canvas),
954 								     BROWSER_CANVAS_ITEM (fk_item));
955 
956 					g_hash_table_insert (canvas->priv->hash_fkeys, fk, fk_item);
957 					goo_canvas_item_lower (GOO_CANVAS_ITEM (fk_item),
958 							       canvas->priv->level_separator);
959 				}
960 			}
961 		}
962 
963 		return BROWSER_CANVAS_TABLE (table_item);
964 	}
965 	else {
966 		g_print ("WARNING: %s\n", lerror && lerror->message ? lerror->message : "No detail");
967 		g_clear_error (&lerror);
968 		return NULL;
969 	}
970 }
971 
972 /**
973  * browser_canvas_db_relations_select_table
974  */
975 void
browser_canvas_db_relations_select_table(BrowserCanvasDbRelations * canvas,BrowserCanvasTable * table)976 browser_canvas_db_relations_select_table (BrowserCanvasDbRelations *canvas,
977 					  BrowserCanvasTable *table)
978 {
979 	g_return_if_fail (IS_BROWSER_CANVAS_DB_RELATIONS (canvas));
980 	g_return_if_fail (!table || IS_BROWSER_CANVAS_ITEM (table));
981 
982 	browser_canvas_item_toggle_select (BROWSER_CANVAS (canvas), (BrowserCanvasItem*) table);
983 }
984 
985 /**
986  * browser_canvas_db_relations_items_to_data_manager
987  */
988 gchar *
browser_canvas_db_relations_items_to_data_manager(BrowserCanvasDbRelations * canvas)989 browser_canvas_db_relations_items_to_data_manager (BrowserCanvasDbRelations *canvas)
990 {
991 	gchar *retval = NULL;
992 	GSList *list;
993 	xmlDocPtr doc;
994 	xmlNodePtr topnode;
995 
996 	g_return_val_if_fail (IS_BROWSER_CANVAS (canvas), NULL);
997 
998 	/* create XML doc and root node */
999 	doc = xmlNewDoc (BAD_CAST "1.0");
1000 	topnode = xmlNewDocNode (doc, NULL, BAD_CAST "data", NULL);
1001         xmlDocSetRootElement (doc, topnode);
1002 
1003 	/* actually serialize all the items which can be serialized */
1004 	for (list = BROWSER_CANVAS (canvas)->priv->items; list; list = list->next) {
1005                 BrowserCanvasItem *item = BROWSER_CANVAS_ITEM (list->data);
1006 		GdaMetaTable *mtable;
1007 
1008 		mtable = g_hash_table_lookup (canvas->priv->hash_tables, item);
1009 		if (mtable) {
1010 			xmlNodePtr node;
1011 			node = xmlNewChild (topnode, NULL, BAD_CAST "table", NULL);
1012 			xmlSetProp (node, BAD_CAST "name",
1013 				    BAD_CAST GDA_META_DB_OBJECT (mtable)->obj_short_name);
1014 
1015 			GSList *fklist;
1016 			for (fklist = mtable->fk_list; fklist; fklist = fklist->next) {
1017 				GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) fklist->data;
1018 				GooCanvasItem *fk_item;
1019 
1020 				fk_item = g_hash_table_lookup (canvas->priv->hash_fkeys, fk);
1021 				if (fk_item) {
1022 					node = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
1023 					xmlSetProp (node, BAD_CAST "foreign_key_table",
1024 						    BAD_CAST fk->depend_on->obj_short_name);
1025 				}
1026 			}
1027 
1028 		}
1029 	}
1030 
1031 	/* create buffer from XML tree */
1032 	xmlChar *xstr = NULL;
1033 	xmlDocDumpFormatMemory (doc, &xstr, NULL, 1);
1034 	if (xstr) {
1035 		retval = g_strdup ((gchar *) xstr);
1036 		xmlFree (xstr);
1037 	}
1038 	xmlFreeDoc (doc);
1039 
1040 	return retval;
1041 }
1042