1 /*
2  * Copyright (C) 2011 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include <glib/gi18n-lib.h>
20 #include <string.h>
21 #include <sql-parser/gda-sql-parser.h>
22 #include "fk-declare.h"
23 #include "../support.h"
24 #include "../../tool-utils.h"
25 
26 /*
actions_sort_func(ToolsFavoriteAction * act1,ToolsFavoriteAction * act2)27  * Main static functions
28  */
29 static void fk_declare_class_init (FkDeclareClass * class);
30 static void fk_declare_init (FkDeclare *declare);
31 static void fk_declare_dispose (GObject *object);
32 
33 enum {
34 	MODEL_COLUMNS_COLUMN_PIXBUF,
35 	MODEL_COLUMNS_COLUMN_STRING,
36 	MODEL_COLUMNS_COLUMN_META_COLUMN,
37 	MODEL_COLUMNS_COLUMN_LAST
38 };
39 
40 enum {
41 	MODEL_TABLES_COLUMN_PIXBUF,
42 	MODEL_TABLES_COLUMN_STRING,
gda_tools_favorites_get_actions(ToolsFavorites * bfav,BrowserConnection * bcnc,GdaSet * set)43 	MODEL_TABLES_COLUMN_META_TABLE,
44 	MODEL_TABLES_COLUMN_LAST
45 };
46 static GtkTreeModel *create_tables_model (GdaMetaStruct *mstruct);
47 static void update_reference_column_choices (FkDeclare *dec);
48 static void update_dialog_response_sensitiveness (FkDeclare *dec);
49 
50 /* get a pointer to the parents to be able to call their destructor */
51 static GObjectClass  *parent_class = NULL;
52 
53 typedef struct {
54 	GtkWidget *checkbox; /* column name chackbox */
55 	GtkComboBox *cbox; /* referenced column combo box */
56 	GdaMetaTableColumn *column;
57 } Assoc;
58 
59 struct _FkDeclarePrivate
60 {
61 	GdaMetaStruct *mstruct;
62 	GdaMetaTable *mtable;
63 	GtkWidget *fk_name;
64 	GtkComboBox *ref_table_cbox;
65 	gint n_cols; /* length (@mtable->columns) */
66 	Assoc *associations; /* size is @n_cols */
67 
68 	gboolean dialog_sensitive;
69 };
70 
71 GType
72 fk_declare_get_type (void)
73 {
74 	static GType type = 0;
75 
76 	if (G_UNLIKELY (type == 0)) {
77 		static GMutex registering;
78 		static const GTypeInfo info = {
79 			sizeof (FkDeclareClass),
80 			(GBaseInitFunc) NULL,
81 			(GBaseFinalizeFunc) NULL,
82 			(GClassInitFunc) fk_declare_class_init,
83 			NULL,
84 			NULL,
85 			sizeof (FkDeclare),
86 			0,
87 			(GInstanceInitFunc) fk_declare_init,
88 			0
89 		};
90 
91 		g_mutex_lock (&registering);
92 		if (type == 0)
93 			type = g_type_register_static (GTK_TYPE_DIALOG, "FkDeclare", &info, 0);
94 		g_mutex_unlock (&registering);
95 	}
96 
97 	return type;
98 }
99 
100 
101 static void
102 fk_declare_class_init (FkDeclareClass *class)
103 {
104 	GObjectClass *object_class = G_OBJECT_CLASS (class);
105 
106 	parent_class = g_type_class_peek_parent (class);
107 
108 	/* virtual functions */
109 	object_class->dispose = fk_declare_dispose;
110 }
111 
112 #ifdef HAVE_GDU
113 static void
114 help_clicked_cb (GtkButton *button, G_GNUC_UNUSED FkDeclare *declare)
115 {
116 	browser_show_help ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) button),
117 			   "declared-fk");
118 	g_signal_stop_emission_by_name (button, "clicked");
119 }
120 #endif
121 
122 static void
123 fk_declare_init (FkDeclare *declare)
124 {
125 	declare->priv = g_new0 (FkDeclarePrivate, 1);
126 	declare->priv->dialog_sensitive = FALSE;
127 
128 	gtk_dialog_add_buttons (GTK_DIALOG (declare),
129 				GTK_STOCK_ADD,
130 				GTK_RESPONSE_ACCEPT,
131 				GTK_STOCK_CANCEL,
132 				GTK_RESPONSE_REJECT,
133 				NULL);
134 #ifdef HAVE_GDU
135 	GtkWidget *help_btn;
136 	help_btn = gtk_button_new_from_stock (GTK_STOCK_HELP);
137 	g_signal_connect (help_btn, "clicked",
138 			  G_CALLBACK (help_clicked_cb), declare);
139 	gtk_widget_show (help_btn);
140 	gtk_dialog_add_action_widget (GTK_DIALOG (declare), help_btn, GTK_RESPONSE_HELP);
141 #endif
142 
143 	gtk_dialog_set_response_sensitive (GTK_DIALOG (declare), GTK_RESPONSE_ACCEPT, FALSE);
144 }
145 
146 static void
gda_tools_favorites_free_action(ToolsFavoriteAction * action)147 fk_declare_dispose (GObject *object)
148 {
149 	FkDeclare *declare;
150 	declare = FK_DECLARE (object);
151 	if (declare->priv) {
152 		g_free (declare->priv->associations);
153 		if (declare->priv->mstruct)
154 			g_object_unref (declare->priv->mstruct);
155 
156 		g_free (declare->priv);
157 		declare->priv = NULL;
158 	}
159 
160 	/* parent class */
161         parent_class->dispose (object);
162 }
163 
164 static void is_node_sensitive (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model,
165 			       GtkTreeIter *iter, gpointer data);
gda_tools_favorites_free_actions_list(GSList * actions_list)166 static void fk_name_changed_cb (GtkWidget *entry, FkDeclare *decl);
167 static void table_selection_changed_cb (GtkComboBox *cbox, FkDeclare *decl);
168 static void column_toggled_cb (GtkToggleButton *toggle, FkDeclare *decl);
169 static void column_selection_changed_cb (GtkComboBox *cbox, FkDeclare *decl);
170 
171 static void
172 create_internal_layout (FkDeclare *decl)
173 {
174 	GtkWidget *label, *grid, *cbox, *entry;
175 	char *markup, *str;
176 	GtkWidget *dcontents;
177 	gint i;
178 	GSList *list;
179 
180 	dcontents = gtk_dialog_get_content_area (GTK_DIALOG (decl));
181 	gtk_box_set_spacing (GTK_BOX (dcontents), 5);
182 
183 	/* label */
184 	label = gtk_label_new ("");
185 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
186 	str = g_strdup_printf (_("Declare a foreign key for table '%s'"),
187 			       GDA_META_DB_OBJECT (decl->priv->mtable)->obj_short_name);
188 	markup = g_markup_printf_escaped ("<big><b>%s:</b></big>\n%s", str,
189 					  _("define which table is references, which columns are "
190 					    "part of the foreign key, "
191 					    "and which column each one references"));
192 	g_free (str);
193 
194 	gtk_label_set_markup (GTK_LABEL (label), markup);
195 	g_free (markup);
196 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
197 	gtk_box_pack_start (GTK_BOX (dcontents), label, FALSE, FALSE, 0);
198 	gtk_widget_show_all (label);
199 
200 	/* GtkTable to hold contents */
201 	grid = gtk_grid_new ();
202 	gtk_box_pack_start (GTK_BOX (dcontents), grid, TRUE, TRUE, 0);
203 	gtk_grid_set_column_spacing (GTK_GRID (grid), 5);
204 	gtk_grid_set_row_spacing (GTK_GRID (grid), 5);
205 
206 	/* FK name */
207 	gfloat yalign;
208 	label = gtk_label_new (_("Foreign key name:"));
209 	gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
210 	gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
211 	gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
212 	entry = gtk_entry_new ();
213 	decl->priv->fk_name = entry;
214 	gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 1, 1);
215 	g_signal_connect (entry, "changed",
216 			  G_CALLBACK (fk_name_changed_cb), decl);
217 
218 	/* table to reference */
219 	label = gtk_label_new (_("Referenced table:"));
220 	gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
221 	gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
222 	gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
223 
224 	GtkTreeModel *model;
225 	GtkCellRenderer *renderer;
226 	model = create_tables_model (decl->priv->mstruct);
227 	cbox = gtk_combo_box_new_with_model (model);
228 	decl->priv->ref_table_cbox = GTK_COMBO_BOX (cbox);
229 	g_signal_connect (cbox, "changed",
230 			  G_CALLBACK (table_selection_changed_cb), decl);
231 	g_object_unref (G_OBJECT (model));
232 	renderer = gtk_cell_renderer_pixbuf_new ();
233 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, FALSE);
234 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
235 					"pixbuf", MODEL_TABLES_COLUMN_PIXBUF,
236 					NULL);
237 	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cbox),
238 					    renderer,
239 					    is_node_sensitive,
240 					    NULL, NULL);
241 
242 	renderer = gtk_cell_renderer_text_new ();
243 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, TRUE);
244 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
245 					"text", MODEL_TABLES_COLUMN_STRING,
246 					NULL);
247 	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cbox),
248 					    renderer,
249 					    is_node_sensitive,
250 					    NULL, NULL);
251 	gtk_grid_attach (GTK_GRID (grid), cbox, 1, 1, 1, 1);
252 
253 	/* more labels */
254 	label = gtk_label_new ("");
255 	markup = g_strdup_printf ("<b>%s:</b>", _("Columns"));
256 	gtk_label_set_markup (GTK_LABEL (label), markup);
257 	g_free (markup);
258 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
259 	gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
260 
261 	label = gtk_label_new ("");
262 	markup = g_strdup_printf ("<b>%s:</b>", _("Referenced column"));
263 	gtk_label_set_markup (GTK_LABEL (label), markup);
264 	g_free (markup);
265 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
266 	gtk_grid_attach (GTK_GRID (grid), label, 1, 2, 1, 1);
267 
268 	/* columns */
269 	decl->priv->n_cols = g_slist_length (decl->priv->mtable->columns);
270 	decl->priv->associations = g_new0 (Assoc, decl->priv->n_cols);
271 	for (i = 3, list = decl->priv->mtable->columns; list; i++, list = list->next) {
272 		GtkCellRenderer *renderer;
273 		GdaMetaTableColumn *column = (GdaMetaTableColumn*) list->data;
274 		Assoc *assoc = &(decl->priv->associations [i-3]);
275 		assoc->column = column;
276 
277 		label = gtk_check_button_new_with_label (column->column_name);
278 		gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
279 		assoc->checkbox = label;
280 		g_signal_connect (label, "toggled",
281 				  G_CALLBACK (column_toggled_cb), decl);
282 
283 		cbox = gtk_combo_box_new ();
284 		g_object_set_data (G_OBJECT (label), "cbox", cbox);
285 		renderer = gtk_cell_renderer_pixbuf_new ();
286 		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, FALSE);
287 		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
288 						"pixbuf", MODEL_COLUMNS_COLUMN_PIXBUF,
289 						NULL);
290 		renderer = gtk_cell_renderer_text_new ();
291 		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, TRUE);
292 		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
293 						"text", MODEL_COLUMNS_COLUMN_STRING,
294 						NULL);
295 		gtk_grid_attach (GTK_GRID (grid), cbox, 1, i, 1, 1);
296 		assoc->cbox = GTK_COMBO_BOX (cbox);
297 		g_signal_connect (cbox, "changed",
298 				  G_CALLBACK (column_selection_changed_cb), decl);
299 		gtk_widget_set_sensitive (cbox, FALSE);
300 	}
301 	gtk_widget_show_all (grid);
302 }
303 
304 static void
305 is_node_sensitive (G_GNUC_UNUSED GtkCellLayout *cell_layout,
306 		   GtkCellRenderer *cell, GtkTreeModel *tree_model,
307 		   GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
308 {
309 	gboolean sensitive;
310 	sensitive = !gtk_tree_model_iter_has_child (tree_model, iter);
311 	g_object_set (cell, "sensitive", sensitive, NULL);
312 }
313 
314 static void
315 fk_name_changed_cb (G_GNUC_UNUSED GtkWidget *entry, FkDeclare *decl)
316 {
317 	update_dialog_response_sensitiveness (decl);
318 }
319 
320 static void
321 table_selection_changed_cb (G_GNUC_UNUSED GtkComboBox *cbox, FkDeclare *decl)
322 {
323 	update_reference_column_choices (decl);
324 	update_dialog_response_sensitiveness (decl);
325 }
326 
327 static void
328 column_toggled_cb (GtkToggleButton *toggle, FkDeclare *decl)
329 {
330 	GtkWidget *cbox;
331 	cbox = g_object_get_data (G_OBJECT (toggle), "cbox");
332 	gtk_widget_set_sensitive (cbox, gtk_toggle_button_get_active (toggle));
333 	update_dialog_response_sensitiveness (decl);
334 }
335 
336 static void
337 column_selection_changed_cb (G_GNUC_UNUSED GtkComboBox *cbox, FkDeclare *decl)
338 {
339 	update_dialog_response_sensitiveness (decl);
340 }
341 
342 static void
343 update_reference_column_choices (FkDeclare *decl)
344 {
345 	gint i;
346 	GtkTreeIter iter;
347 	GdaMetaTable *mtable = NULL;
348 
349 	if (gtk_combo_box_get_active_iter (decl->priv->ref_table_cbox, &iter))
350 		gtk_tree_model_get (gtk_combo_box_get_model (decl->priv->ref_table_cbox), &iter,
351 				    MODEL_TABLES_COLUMN_META_TABLE, &mtable, -1);
352 
353 	for (i = 0; i < decl->priv->n_cols; i++) {
354 		Assoc *assoc = &(decl->priv->associations [i]);
355 		GtkListStore *lstore;
356 		lstore = (GtkListStore*) gtk_combo_box_get_model (assoc->cbox);
357 		if (! lstore) {
358 			lstore = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
359 			gtk_combo_box_set_model (assoc->cbox, GTK_TREE_MODEL (lstore));
360 			g_object_unref (lstore);
361 		}
362 		else
363 			gtk_list_store_clear (lstore);
364 
365 		if (!mtable)
366 			continue;
367 
368 		/* add columns */
369 		GSList *list;
370 		for (list = mtable->columns; list; list = list->next) {
371 			GdaMetaTableColumn *col = (GdaMetaTableColumn*) list->data;
372 			GdkPixbuf *pix;
373 			gtk_list_store_append (lstore, &iter);
374 			pix = browser_get_pixbuf_icon (BROWSER_ICON_COLUMN);
375 			gtk_list_store_set (lstore, &iter,
376 					    MODEL_COLUMNS_COLUMN_PIXBUF, pix,
377 					    MODEL_COLUMNS_COLUMN_STRING, col->column_name,
378 					    MODEL_COLUMNS_COLUMN_META_COLUMN, col, -1);
379 		}
380 	}
381 }
382 
383 /**
384  * fk_declare_new
385  */
386 GtkWidget  *
387 fk_declare_new (GtkWindow *parent, GdaMetaStruct *mstruct, GdaMetaTable *table)
388 {
389 	GtkWidget *wid;
390 	FkDeclare *decl;
391 	gchar *str;
392 
393 	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
394 	g_return_val_if_fail (table, NULL);
395 	g_return_val_if_fail (GDA_META_DB_OBJECT (table)->obj_type == GDA_META_DB_TABLE, NULL);
396 	g_return_val_if_fail (table->columns, NULL);
397 
398 	str = g_strdup_printf (_("Declare a foreign key for table '%s'"),
399 			       GDA_META_DB_OBJECT (table)->obj_short_name);
400 	wid = (GtkWidget*) g_object_new (FK_DECLARE_TYPE, "title", str,
401 					 "transient-for", parent,
402 					 "border-width", 10, NULL);
403 	g_free (str);
404 
405 	decl = FK_DECLARE (wid);
406 	decl->priv->mstruct = g_object_ref ((GObject*) mstruct);
407 	decl->priv->mtable = table;
408 
409 	create_internal_layout (decl);
410 
411 	return wid;
412 }
413 
414 static gint
415 dbo_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2)
416 {
417 	const gchar *n1, *n2;
418 	g_assert (dbo1);
419 	g_assert (dbo2);
420 	if (dbo1->obj_name[0] ==  '"')
421 		n1 = dbo1->obj_name + 1;
422 	else
423 		n1 = dbo1->obj_name;
424 	if (dbo2->obj_name[0] ==  '"')
425 		n2 = dbo2->obj_name + 1;
426 	else
427 		n2 = dbo2->obj_name;
428 	return strcmp (n2, n1);
429 }
430 
431 static GtkTreeModel *
432 create_tables_model (GdaMetaStruct *mstruct)
433 {
434 	GtkTreeStore *tstore;
435 	GSList *all_dbo, *list;
436 	GHashTable *schemas = NULL; /* key = schema name, value = a #GtkTreeRowReference as parent */
437 
438 	tstore = gtk_tree_store_new (MODEL_TABLES_COLUMN_LAST, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
439 	schemas = g_hash_table_new_full (g_str_hash, g_str_equal,
440 					 NULL, (GDestroyNotify) gtk_tree_row_reference_free);
441 	all_dbo = gda_meta_struct_get_all_db_objects (mstruct);
442 	all_dbo = g_slist_sort (all_dbo, (GCompareFunc) dbo_sort_func);
443 	for (list = all_dbo; list; list = list->next) {
444 		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
445 		GtkTreeIter iter;
446 		GdkPixbuf *pix;
447 		if (dbo->obj_type != GDA_META_DB_TABLE)
448 			continue;
449 
450 		if (strcmp (dbo->obj_short_name, dbo->obj_full_name)) {
451 			gtk_tree_store_prepend (tstore, &iter, NULL);
452 			pix = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
453 			gtk_tree_store_set (tstore, &iter,
454 					    MODEL_TABLES_COLUMN_PIXBUF, pix,
455 					    MODEL_TABLES_COLUMN_STRING, dbo->obj_short_name,
456 					    MODEL_TABLES_COLUMN_META_TABLE, GDA_META_TABLE (dbo), -1);
457 		}
458 
459 		GtkTreePath *path;
460 		GtkTreeRowReference *rref;
461 		GtkTreeIter parent;
462 		rref = g_hash_table_lookup (schemas, dbo->obj_schema);
463 		if (!rref) {
464 			GtkTreeIter iter;
465 			GtkTreePath *path;
466 			GdkPixbuf *pix;
467 			gtk_tree_store_append (tstore, &iter, NULL);
468 			pix = browser_get_pixbuf_icon (BROWSER_ICON_SCHEMA);
469 			gtk_tree_store_set (tstore, &iter,
470 					    MODEL_TABLES_COLUMN_PIXBUF, pix,
471 					    MODEL_TABLES_COLUMN_STRING, dbo->obj_schema,
472 					    MODEL_TABLES_COLUMN_META_TABLE, NULL, -1);
473 			path = gtk_tree_model_get_path ((GtkTreeModel*) tstore, &iter);
474 			rref = gtk_tree_row_reference_new ((GtkTreeModel*) tstore, path);
475 			gtk_tree_path_free (path);
476 			g_hash_table_insert (schemas, dbo->obj_schema, rref);
477 		}
478 
479 		path = gtk_tree_row_reference_get_path (rref);
480 		g_assert (gtk_tree_model_get_iter ((GtkTreeModel*) tstore, &parent, path));
481 		gtk_tree_path_free (path);
482 		gtk_tree_store_prepend (tstore, &iter, &parent);
483 		pix = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
484 		gtk_tree_store_set (tstore, &iter,
485 				    MODEL_TABLES_COLUMN_PIXBUF, pix,
486 				    MODEL_TABLES_COLUMN_STRING, dbo->obj_short_name,
487 				    MODEL_TABLES_COLUMN_META_TABLE, GDA_META_TABLE (dbo), -1);
488 	}
489 	g_slist_free (all_dbo);
490 	g_hash_table_destroy (schemas);
491 
492 	return GTK_TREE_MODEL (tstore);
493 }
494 
495 /*
496  * Sets the dialog's sensitiveness for the GTK_RESPONSE_ACCEPT response
497  *
498  * It is sensitive if:
499  *  - the FK is named
500  *  - a reference table is selected
501  *  - at least one column is checked
502  *  - for each checked column, there is a selected reference column
503  */
504 static void
505 update_dialog_response_sensitiveness (FkDeclare *decl)
506 {
507 	gboolean sensitive = FALSE;
508 	gint i;
509 	gboolean onechecked = FALSE;
510 	const gchar *fkname;
511 
512 	fkname = gtk_entry_get_text (GTK_ENTRY (decl->priv->fk_name));
513 	if (!fkname || !*fkname)
514 		goto out;
515 
516 	if (gtk_combo_box_get_active (decl->priv->ref_table_cbox) == -1)
517 		goto out;
518 
519 	sensitive = TRUE;
520 	for (i = 0; i < decl->priv->n_cols; i++) {
521 		Assoc *assoc = &(decl->priv->associations [i]);
522 		if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (assoc->checkbox)))
523 			continue;
524 
525 		onechecked = TRUE;
526 		if (gtk_combo_box_get_active (assoc->cbox) == -1)
527 			sensitive = FALSE;
528 	}
529 
530 	if (! onechecked) {
531 		sensitive = FALSE;
532 		goto out;
533 	}
534 
535  out:
536 	decl->priv->dialog_sensitive = sensitive;
537 	gtk_dialog_set_response_sensitive (GTK_DIALOG (decl), GTK_RESPONSE_ACCEPT, sensitive);
538 }
539 
540 /**
541  * fk_declare_write
542  * @decl: a #FkDeclare widget
543  * @bwin: (allow-none): a #BrowserWindow, or %NULL
544  * @error: a place to store errors or %NULL
545  *
546  * Actually declares the new foreign key in the meta store
547  *
548  * Returns: %TRUE if no error occurred
549  */
550 gboolean
551 fk_declare_write (FkDeclare *decl, BrowserWindow *bwin, GError **error)
552 {
553 	gboolean retval = FALSE;
554 
555 	g_return_val_if_fail (IS_FK_DECLARE (decl), FALSE);
556 	g_return_val_if_fail (!bwin || BROWSER_IS_WINDOW (bwin), FALSE);
557 
558 	if (! decl->priv->dialog_sensitive) {
559 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_COMMAND_ARGUMENTS_ERROR,
560 			     "%s", _("Missing information to declare foreign key"));
561 		return FALSE;
562 	}
563 
564 	GdaMetaTable *mtable = NULL;
565 	GtkTreeIter iter;
566 	g_assert (gtk_combo_box_get_active_iter (decl->priv->ref_table_cbox, &iter));
567 	gtk_tree_model_get (gtk_combo_box_get_model (decl->priv->ref_table_cbox), &iter,
568 			    MODEL_TABLES_COLUMN_META_TABLE, &mtable, -1);
569 
570 	GdaMetaStore *mstore;
571 	gchar **colnames, **ref_colnames;
572 	colnames = g_new0 (gchar *, decl->priv->n_cols);
573 	ref_colnames = g_new0 (gchar *, decl->priv->n_cols);
574 	gint i, j;
575 	for (i = 0, j = 0; i < decl->priv->n_cols; i++) {
576 		Assoc *assoc = &(decl->priv->associations [i]);
577 		if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (assoc->checkbox)))
578 			continue;
579 		colnames [j] = assoc->column->column_name;
580 		g_assert (gtk_combo_box_get_active_iter (assoc->cbox, &iter));
581 		GdaMetaTableColumn *ref_column;
582 		gtk_tree_model_get (gtk_combo_box_get_model (assoc->cbox), &iter,
583 				    MODEL_TABLES_COLUMN_META_TABLE, &ref_column, -1);
584 		g_assert (ref_column);
585 		ref_colnames [j] = ref_column->column_name;
586 		j++;
587 	}
588 
589 	g_object_get (G_OBJECT (decl->priv->mstruct), "meta-store", &mstore, NULL);
590 	retval = gda_meta_store_declare_foreign_key (mstore, NULL,
591 						     gtk_entry_get_text (GTK_ENTRY (decl->priv->fk_name)),
592 						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_catalog,
593 						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_schema,
594 						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_name,
595 						     GDA_META_DB_OBJECT (mtable)->obj_catalog,
596 						     GDA_META_DB_OBJECT (mtable)->obj_schema,
597 						     GDA_META_DB_OBJECT (mtable)->obj_name,
598 						     j, colnames, ref_colnames, error);
599 	g_free (colnames);
600 	g_free (ref_colnames);
601 
602 	if (retval && bwin) {
603 		BrowserConnection *bcnc;
604 		bcnc = browser_window_get_connection (bwin);
605 		browser_connection_meta_data_changed (bcnc);
606 	}
607 
608 	g_object_unref (mstore);
609 	return retval;
610 }
611 
612 /**
613  * fk_declare_undeclare:
614  * @mstruct: the #GdaMEtaStruct to delete the FK from
615  * @bwin: (allow-none): a #BrowserWindow, or %NULL
616  * @decl_fk: the #GdaMetaTableForeignKey fk to delete
617  * @error: a place to store errors, or %NULL
618  *
619  * Deletes a declared FK.
620  *
621  * Returns: %TRUE if no error occurred
622  */
623 gboolean
624 fk_declare_undeclare (GdaMetaStruct *mstruct, BrowserWindow *bwin, GdaMetaTableForeignKey *decl_fk,
625 		      GError **error)
626 {
627 	gboolean retval = FALSE;
628 	GdaMetaStore *mstore;
629 
630 	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE);
631 	g_return_val_if_fail (!bwin || BROWSER_IS_WINDOW (bwin), FALSE);
632 	g_return_val_if_fail (decl_fk, FALSE);
633 	if (!decl_fk->meta_table ||
634 	    !decl_fk->meta_table->obj_catalog ||
635 	    !decl_fk->meta_table->obj_schema ||
636 	    !decl_fk->meta_table->obj_name ||
637 	    !decl_fk->depend_on ||
638 	    !decl_fk->depend_on->obj_catalog ||
639 	    !decl_fk->depend_on->obj_schema ||
640 	    !decl_fk->depend_on->obj_name) {
641 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_COMMAND_ARGUMENTS_ERROR,
642 			     "%s", _("Missing information to undeclare foreign key"));
643 		return FALSE;
644 	}
645 
646 	g_object_get (G_OBJECT (mstruct), "meta-store", &mstore, NULL);
647 	retval = gda_meta_store_undeclare_foreign_key (mstore, NULL,
648 						       decl_fk->fk_name,
649 						       decl_fk->meta_table->obj_catalog,
650 						       decl_fk->meta_table->obj_schema,
651 						       decl_fk->meta_table->obj_name,
652 						       decl_fk->depend_on->obj_catalog,
653 						       decl_fk->depend_on->obj_schema,
654 						       decl_fk->depend_on->obj_name,
655 						       error);
656 	if (retval && bwin) {
657 		BrowserConnection *bcnc;
658 		bcnc = browser_window_get_connection (bwin);
659 		browser_connection_meta_data_changed (bcnc);
660 	}
661 
662 	g_object_unref (mstore);
663 	return retval;
664 }
665