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 <math.h>
23 #include <libgda/libgda.h>
24 #include <glib/gi18n-lib.h>
25 #include "browser-canvas.h"
26 #include "browser-canvas-fkey.h"
27 #include "browser-canvas-table.h"
28 #include "browser-canvas-text.h"
29 #include "browser-canvas-utility.h"
30 #include "browser-canvas-db-relations.h"
31 #include "../../tool-utils.h"
32 #include "../support.h"
33 #include "../browser-window.h"
34 #include "../common/fk-declare.h"
35 
36 static void browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class);
37 static void browser_canvas_fkey_init       (BrowserCanvasFkey * cc);
38 static void browser_canvas_fkey_dispose    (GObject *object);
39 static void browser_canvas_fkey_finalize   (GObject *object);
40 
41 static void browser_canvas_fkey_set_property (GObject *object,
42 					    guint param_id,
43 					    const GValue *value,
44 					    GParamSpec *pspec);
45 static void browser_canvas_fkey_get_property (GObject *object,
46 					    guint param_id,
47 					    GValue *value,
48 					    GParamSpec *pspec);
49 
50 static void browser_canvas_fkey_get_edge_nodes (BrowserCanvasItem *citem,
51 					      BrowserCanvasItem **from, BrowserCanvasItem **to);
52 
53 static void clean_items (BrowserCanvasFkey *cc);
54 static void create_items (BrowserCanvasFkey *cc);
55 static void update_items (BrowserCanvasFkey *cc);
56 
57 enum
58 {
59 	PROP_0,
60 	PROP_META_STRUCT,
61 	PROP_FK_CONSTRAINT
62 };
63 
64 struct _BrowserCanvasFkeyPrivate
65 {
66 	GdaMetaStruct          *mstruct;
67 	GdaMetaTableForeignKey *fk;
68 	BrowserCanvasTable     *fk_table_item;
69 	BrowserCanvasTable     *ref_pk_table_item;
70 	GSList                 *shapes; /* list of BrowserCanvasCanvasShape structures */
71 };
72 
73 /* get a pointer to the parents to be able to call their destructor */
74 static GObjectClass *parent_class = NULL;
75 static GooCanvasLineDash *dash = NULL, *no_dash = NULL;
76 
77 GType
browser_canvas_fkey_get_type(void)78 browser_canvas_fkey_get_type (void)
79 {
80 	static GType type = 0;
81 
82         if (G_UNLIKELY (type == 0)) {
83 		static const GTypeInfo info = {
84 			sizeof (BrowserCanvasFkeyClass),
85 			(GBaseInitFunc) NULL,
86 			(GBaseFinalizeFunc) NULL,
87 			(GClassInitFunc) browser_canvas_fkey_class_init,
88 			NULL,
89 			NULL,
90 			sizeof (BrowserCanvasFkey),
91 			0,
92 			(GInstanceInitFunc) browser_canvas_fkey_init,
93 			0
94 		};
95 
96 		type = g_type_register_static (TYPE_BROWSER_CANVAS_ITEM, "BrowserCanvasFkey", &info, 0);
97 	}
98 
99 	return type;
100 }
101 
102 static void
browser_canvas_fkey_class_init(BrowserCanvasFkeyClass * class)103 browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class)
104 {
105 	GObjectClass   *object_class = G_OBJECT_CLASS (class);
106 
107 	parent_class = g_type_class_peek_parent (class);
108 
109 	BROWSER_CANVAS_ITEM_CLASS (class)->get_edge_nodes = browser_canvas_fkey_get_edge_nodes;
110 
111 	object_class->dispose = browser_canvas_fkey_dispose;
112 	object_class->finalize = browser_canvas_fkey_finalize;
113 
114 	/* Properties */
115 	object_class->set_property = browser_canvas_fkey_set_property;
116 	object_class->get_property = browser_canvas_fkey_get_property;
117 
118 	g_object_class_install_property
119                 (object_class, PROP_META_STRUCT,
120                  g_param_spec_object ("meta-struct", NULL, NULL,
121 				      GDA_TYPE_META_STRUCT,
122 				      (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
123 	g_object_class_install_property (object_class, PROP_FK_CONSTRAINT,
124 					 g_param_spec_pointer ("fk_constraint", "FK constraint",
125 							       NULL,
126 							       G_PARAM_WRITABLE));
127 
128 	dash = goo_canvas_line_dash_new (2, 5., 1.5);
129 	no_dash = goo_canvas_line_dash_new (0);
130 }
131 
132 static void
browser_canvas_fkey_init(BrowserCanvasFkey * cc)133 browser_canvas_fkey_init (BrowserCanvasFkey *cc)
134 {
135 	cc->priv = g_new0 (BrowserCanvasFkeyPrivate, 1);
136 	cc->priv->mstruct = NULL;
137 	cc->priv->fk = NULL;
138 	cc->priv->fk_table_item = NULL;
139 	cc->priv->ref_pk_table_item = NULL;
140 	cc->priv->shapes = NULL;
141 }
142 
143 static void
fk_table_item_weak_ref_lost(BrowserCanvasFkey * cc,G_GNUC_UNUSED BrowserCanvasTable * old_table_item)144 fk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, G_GNUC_UNUSED BrowserCanvasTable *old_table_item)
145 {
146 	cc->priv->fk_table_item = NULL;
147 }
148 
149 static void
ref_pk_table_item_weak_ref_lost(BrowserCanvasFkey * cc,G_GNUC_UNUSED BrowserCanvasTable * old_table_item)150 ref_pk_table_item_weak_ref_lost (BrowserCanvasFkey *cc, G_GNUC_UNUSED BrowserCanvasTable *old_table_item)
151 {
152 	cc->priv->ref_pk_table_item = NULL;
153 }
154 
155 
156 static void
browser_canvas_fkey_dispose(GObject * object)157 browser_canvas_fkey_dispose (GObject *object)
158 {
159 	BrowserCanvasFkey *cc;
160 	g_return_if_fail (object != NULL);
161 	g_return_if_fail (IS_BROWSER_CANVAS_FKEY (object));
162 
163 	cc = BROWSER_CANVAS_FKEY (object);
164 
165 	clean_items (cc);
166 	if (cc->priv->mstruct) {
167 		g_object_unref (cc->priv->mstruct);
168 		cc->priv->mstruct = NULL;
169 	}
170 	cc->priv->fk = NULL;
171 	if (cc->priv->fk_table_item) {
172 		g_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
173 				     (GWeakNotify) fk_table_item_weak_ref_lost, cc);
174 		cc->priv->fk_table_item = NULL;
175 	}
176 	if (cc->priv->ref_pk_table_item) {
177 		g_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
178 				     (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
179 		cc->priv->ref_pk_table_item = NULL;
180 	}
181 
182 	/* for the parent class */
183 	parent_class->dispose (object);
184 }
185 
186 
187 static void
browser_canvas_fkey_finalize(GObject * object)188 browser_canvas_fkey_finalize (GObject *object)
189 {
190 	BrowserCanvasFkey *cc;
191 	g_return_if_fail (object != NULL);
192 	g_return_if_fail (IS_BROWSER_CANVAS_FKEY (object));
193 
194 	cc = BROWSER_CANVAS_FKEY (object);
195 	if (cc->priv) {
196 		g_slist_free (cc->priv->shapes);
197 		g_free (cc->priv);
198 		cc->priv = NULL;
199 	}
200 
201 	/* for the parent class */
202 	parent_class->finalize (object);
203 }
204 
205 static void
browser_canvas_fkey_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)206 browser_canvas_fkey_set_property (GObject *object,
207 				  guint param_id,
208 				  const GValue *value,
209 				  GParamSpec *pspec)
210 {
211 	BrowserCanvasFkey *cc;
212 
213 	cc = BROWSER_CANVAS_FKEY (object);
214 
215 	switch (param_id) {
216 	case PROP_META_STRUCT:
217 		cc->priv->mstruct = g_value_dup_object (value);
218 		break;
219 	case PROP_FK_CONSTRAINT:
220 		if (cc->priv->fk != g_value_get_pointer (value)) {
221 			cc->priv->fk = g_value_get_pointer (value);
222 			clean_items (cc);
223 			create_items (cc);
224 		}
225 		break;
226 	default:
227 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
228 		break;
229 	}
230 }
231 
232 static void
browser_canvas_fkey_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)233 browser_canvas_fkey_get_property (GObject *object,
234 				  guint param_id,
235 				  GValue *value,
236 				  GParamSpec *pspec)
237 {
238 	BrowserCanvasFkey *cc;
239 
240 	cc = BROWSER_CANVAS_FKEY (object);
241 
242 	switch (param_id) {
243 	case PROP_META_STRUCT:
244 		g_value_set_object (value, cc->priv->mstruct);
245 		break;
246 	default:
247 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
248 		break;
249 	}
250 }
251 
252 static void
browser_canvas_fkey_get_edge_nodes(BrowserCanvasItem * citem,BrowserCanvasItem ** from,BrowserCanvasItem ** to)253 browser_canvas_fkey_get_edge_nodes (BrowserCanvasItem *citem,
254 					  BrowserCanvasItem **from, BrowserCanvasItem **to)
255 {
256 	BrowserCanvasFkey *cc;
257 
258 	cc = BROWSER_CANVAS_FKEY (citem);
259 
260 	if (from)
261 		*from = (BrowserCanvasItem*) cc->priv->fk_table_item;
262 	if (to)
263 		*to = (BrowserCanvasItem*) cc->priv->ref_pk_table_item;
264 }
265 
266 static gboolean single_item_enter_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
267 						   GdkEventCrossing *event, BrowserCanvasFkey *cc);
268 static gboolean single_item_leave_notify_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
269 						   GdkEventCrossing *event, BrowserCanvasFkey *cc);
270 static gboolean single_item_button_press_event_cb (GooCanvasItem *ci, GooCanvasItem *target_item,
271 						   GdkEventButton *event, BrowserCanvasFkey *cc);
272 static void table_item_moved_cb (GooCanvasItem *table, BrowserCanvasFkey *cc);
273 
274 /*
275  * destroy any existing GooCanvasItem objects
276  */
277 static void
clean_items(BrowserCanvasFkey * cc)278 clean_items (BrowserCanvasFkey *cc)
279 {
280 	if (cc->priv->fk_table_item) {
281 		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->fk_table_item),
282 						      G_CALLBACK (table_item_moved_cb), cc);
283 		g_object_weak_unref (G_OBJECT (cc->priv->fk_table_item),
284 				     (GWeakNotify) fk_table_item_weak_ref_lost, cc);
285 		cc->priv->fk_table_item = NULL;
286 	}
287 
288 	if (cc->priv->ref_pk_table_item) {
289 		g_signal_handlers_disconnect_by_func (G_OBJECT (cc->priv->ref_pk_table_item),
290 						      G_CALLBACK (table_item_moved_cb), cc);
291 		g_object_weak_unref (G_OBJECT (cc->priv->ref_pk_table_item),
292 				     (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
293 		cc->priv->ref_pk_table_item = NULL;
294 	}
295 
296 	/* remove all the GooCanvasItem objects */
297 	browser_canvas_canvas_shapes_remove_all (cc->priv->shapes);
298 	cc->priv->shapes = NULL;
299 }
300 
301 /*
302  * create new GooCanvasItem objects
303  */
304 static void
create_items(BrowserCanvasFkey * cc)305 create_items (BrowserCanvasFkey *cc)
306 {
307 	GSList *list, *canvas_shapes;
308 	BrowserCanvasTable *table_item;
309 	BrowserCanvas *canvas =  g_object_get_data (G_OBJECT (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (cc))),
310 						    "browsercanvas");
311 
312 	g_assert (cc->priv->fk);
313 
314 	/* Analyse FK constraint */
315 	table_item = browser_canvas_db_relations_get_table_item (BROWSER_CANVAS_DB_RELATIONS (canvas),
316 								 GDA_META_TABLE (cc->priv->fk->meta_table));
317 	cc->priv->fk_table_item = table_item;
318 	g_return_if_fail (table_item);
319 	g_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) fk_table_item_weak_ref_lost, cc);
320 
321 	g_signal_connect (G_OBJECT (table_item), "moving",
322 			  G_CALLBACK (table_item_moved_cb), cc);
323 	g_signal_connect (G_OBJECT (table_item), "moved",
324 			  G_CALLBACK (table_item_moved_cb), cc);
325 
326 	table_item = browser_canvas_db_relations_get_table_item (BROWSER_CANVAS_DB_RELATIONS (canvas),
327 								 GDA_META_TABLE (cc->priv->fk->depend_on));
328 	cc->priv->ref_pk_table_item = table_item;
329 	g_return_if_fail (table_item);
330 
331 	g_object_weak_ref (G_OBJECT (table_item), (GWeakNotify) ref_pk_table_item_weak_ref_lost, cc);
332 	g_signal_connect (G_OBJECT (table_item), "moving",
333 			  G_CALLBACK (table_item_moved_cb), cc);
334 	g_signal_connect (G_OBJECT (table_item), "moved",
335 			  G_CALLBACK (table_item_moved_cb), cc);
336 
337 	/* actual line(s) */
338 	g_assert (!cc->priv->shapes);
339 	canvas_shapes = browser_canvas_util_compute_anchor_shapes (GOO_CANVAS_ITEM (cc), NULL,
340 								   cc->priv->fk_table_item,
341 								   cc->priv->ref_pk_table_item,
342 								   /*MAX (cc->priv->fk->cols_nb, 1)*/ 1,
343 								   0, TRUE);
344 
345 	cc->priv->shapes = browser_canvas_canvas_shapes_remove_obsolete_shapes (canvas_shapes);
346 	for (list = canvas_shapes; list; list = list->next) {
347 		GooCanvasItem *item = BROWSER_CANVAS_CANVAS_SHAPE (list->data)->item;
348 		gchar *color = "black";
349 		g_object_set (G_OBJECT (item),
350 			      "stroke-color", color,
351 			      "line-dash", GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ? dash : no_dash,
352 			      NULL);
353 
354 		if (G_OBJECT_TYPE (item) == GOO_TYPE_CANVAS_POLYLINE) {
355 			g_object_set (G_OBJECT (item),
356 				      "start-arrow", TRUE,
357 				      "arrow-tip-length", 4.,
358 				      "arrow-length", 5.,
359 				      "arrow-width", 4.,
360 				      NULL);
361 		}
362 		else if (G_OBJECT_TYPE (item) == GOO_TYPE_CANVAS_ELLIPSE)
363 			g_object_set (G_OBJECT (item),
364 				      "fill-color", color,
365 				      NULL);
366 
367 		g_object_set_data (G_OBJECT (item), "fkcons", cc->priv->fk);
368 		g_signal_connect (G_OBJECT (item), "enter-notify-event",
369 				  G_CALLBACK (single_item_enter_notify_event_cb), cc);
370 		g_signal_connect (G_OBJECT (item), "leave-notify-event",
371 				  G_CALLBACK (single_item_leave_notify_event_cb), cc);
372 		g_signal_connect (G_OBJECT (item), "button-press-event",
373 				  G_CALLBACK (single_item_button_press_event_cb), cc);
374 
375 	}
376 }
377 
378 /*
379  * update GooCanvasItem objects
380  */
381 static void
update_items(BrowserCanvasFkey * cc)382 update_items (BrowserCanvasFkey *cc)
383 {
384 	cc->priv->shapes = browser_canvas_util_compute_anchor_shapes (GOO_CANVAS_ITEM (cc), cc->priv->shapes,
385 								      cc->priv->fk_table_item,
386 								      cc->priv->ref_pk_table_item,
387 								      /*MAX (cc->priv->fk->cols_nb, 1)*/ 1,
388 								      0, TRUE);
389 	cc->priv->shapes = browser_canvas_canvas_shapes_remove_obsolete_shapes (cc->priv->shapes);
390 }
391 
392 /*
393  * item is for a single FK constraint
394  */
395 static gboolean
single_item_enter_notify_event_cb(GooCanvasItem * ci,G_GNUC_UNUSED GooCanvasItem * target_item,G_GNUC_UNUSED GdkEventCrossing * event,BrowserCanvasFkey * cc)396 single_item_enter_notify_event_cb (GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
397 				   G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasFkey *cc)
398 {
399 	gint i;
400 
401 	for (i = 0; i < cc->priv->fk->cols_nb; i++) {
402 		GdaMetaTableColumn *tcol;
403 		BrowserCanvasColumn *column;
404 
405 		/* fk column */
406 		tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->meta_table)->columns,
407 					 cc->priv->fk->fk_cols_array[i] - 1);
408 
409 		column = browser_canvas_table_get_column_item (cc->priv->fk_table_item, tcol);
410 		browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), TRUE);
411 
412 		/* ref pk column */
413 		tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->depend_on)->columns,
414 					 cc->priv->fk->ref_pk_cols_array[i] - 1);
415 
416 		column = browser_canvas_table_get_column_item (cc->priv->ref_pk_table_item, tcol);
417 		browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), TRUE);
418 
419 		gchar *str;
420 		str = g_strdup_printf ("%s '%s'\n%s: %s\n%s: %s",
421 				       GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ?
422 				       _("Declared foreign key") : _("Foreign key"),
423 				       cc->priv->fk->fk_name,
424 				       _("Policy on UPDATE"),
425 				       gda_tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY (cc->priv->fk)),
426 				       _("Policy on DELETE"),
427 				       gda_tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY (cc->priv->fk)));
428 		gtk_widget_set_tooltip_text (GTK_WIDGET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (ci))),
429 					     str);
430 		g_free (str);
431 	}
432 
433 	return FALSE;
434 }
435 
436 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)437 single_item_leave_notify_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
438 				   G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasFkey *cc)
439 {
440 	gint i;
441 
442 	for (i = 0; i < cc->priv->fk->cols_nb; i++) {
443 		GdaMetaTableColumn *tcol;
444 		BrowserCanvasColumn *column;
445 
446 		/* fk column */
447 		tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->meta_table)->columns,
448 					 cc->priv->fk->fk_cols_array[i] - 1);
449 
450 		column = browser_canvas_table_get_column_item (cc->priv->fk_table_item, tcol);
451 		browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), FALSE);
452 
453 		/* ref pk column */
454 		tcol = g_slist_nth_data (GDA_META_TABLE (cc->priv->fk->depend_on)->columns,
455 					 cc->priv->fk->ref_pk_cols_array[i] - 1);
456 
457 		column = browser_canvas_table_get_column_item (cc->priv->ref_pk_table_item, tcol);
458 		browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), FALSE);
459 	}
460 
461 	return FALSE;
462 }
463 
464 static void
delete_declared_fk_cb(G_GNUC_UNUSED GtkMenuItem * mitem,BrowserCanvasFkey * cc)465 delete_declared_fk_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasFkey *cc)
466 {
467 	GError *error = NULL;
468 	GtkWidget *parent;
469 	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (cc)));
470 	if (! fk_declare_undeclare (cc->priv->mstruct,
471 				    BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
472 				    cc->priv->fk, &error)) {
473 		browser_show_error ((GtkWindow *) parent, _("Failed to undeclare foreign key: %s"),
474 				    error && error->message ? error->message : _("No detail"));
475 		g_clear_error (&error);
476 	}
477 	else if (BROWSER_IS_WINDOW (parent))
478 		browser_window_show_notice (BROWSER_WINDOW (parent),
479 					    GTK_MESSAGE_INFO, "fkdeclare",
480 					    _("Successfully undeclared foreign key"));
481 	else
482 		browser_show_message ((GtkWindow *) parent, "%s",
483 				      _("Successfully undeclared foreign key"));
484 }
485 
486 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)487 single_item_button_press_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
488 				   G_GNUC_UNUSED GdkEventButton *event, BrowserCanvasFkey *cc)
489 {
490 	GdaMetaTableForeignKey *fk = g_object_get_data (G_OBJECT (ci), "fkcons");
491 	if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk)) {
492 		GtkWidget *menu, *entry;
493 
494 		menu = gtk_menu_new ();
495 		entry = gtk_menu_item_new_with_label (_("Remove this declared foreign key"));
496 		g_object_set_data (G_OBJECT (entry), "fkcons", fk);
497 		g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (delete_declared_fk_cb), cc);
498 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
499 		gtk_widget_show (entry);
500 		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
501 				NULL, NULL, ((GdkEventButton *)event)->button,
502 				((GdkEventButton *)event)->time);
503 		return TRUE;
504 	}
505 	else
506 		return FALSE;
507 }
508 
509 static void
table_item_moved_cb(G_GNUC_UNUSED GooCanvasItem * table,BrowserCanvasFkey * cc)510 table_item_moved_cb (G_GNUC_UNUSED GooCanvasItem *table, BrowserCanvasFkey *cc)
511 {
512 	update_items (cc);
513 }
514 
515 /**
516  * browser_canvas_fkey_new
517  * @parent: the parent item, or NULL.
518  * @fkcons: the #GdaMetaTableForeignKey to represent
519  * @...: optional pairs of property names and values, and a terminating NULL.
520  *
521  * Creates a new canvas item to represent the @fkcons FK constraint
522  *
523  * Returns: a new #GooCanvasItem object
524  */
525 GooCanvasItem *
browser_canvas_fkey_new(GooCanvasItem * parent,GdaMetaStruct * mstruct,GdaMetaTableForeignKey * fkcons,...)526 browser_canvas_fkey_new (GooCanvasItem *parent, GdaMetaStruct *mstruct, GdaMetaTableForeignKey *fkcons, ...)
527 {
528 	GooCanvasItem *item;
529 	const char *first_property;
530 	va_list var_args;
531 
532 	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
533 
534 	item = g_object_new (TYPE_BROWSER_CANVAS_FKEY, "meta-struct", mstruct, NULL);
535 
536 	if (parent) {
537 		goo_canvas_item_add_child (parent, item, -1);
538 		g_object_unref (item);
539 	}
540 
541 	g_object_set (item, "fk_constraint", fkcons, NULL);
542 
543 	va_start (var_args, fkcons);
544 	first_property = va_arg (var_args, char*);
545 	if (first_property)
546 		g_object_set_valist ((GObject*) item, first_property, var_args);
547 	va_end (var_args);
548 
549 	return item;
550 }
551