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