1 /*
2  * Copyright (C) 2009 - 2012 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 <glib/gi18n-lib.h>
22 #include <string.h>
23 #include "table-info.h"
24 #include "../dnd.h"
25 #include "../support.h"
26 #include "../gdaui-bar.h"
27 #include "table-columns.h"
28 #include "table-preferences.h"
29 #ifdef HAVE_GOOCANVAS
30 #include "table-relations.h"
31 #endif
32 #include "schema-browser-perspective.h"
33 #include "../browser-page.h"
34 #include "../browser-stock-icons.h"
35 #include "../browser-window.h"
36 #include "../data-manager/data-manager-perspective.h"
37 #include <libgda-ui/gdaui-enums.h>
38 #include <libgda-ui/gdaui-basic-form.h>
39 #include <libgda-ui/internal/popup-container.h>
40 #include <libgda/gda-data-model-extra.h>
41 #include "../common/fk-declare.h"
42 #include <libgda/gda-debug-macros.h>
43 
44 struct _TableInfoPrivate {
45 	BrowserConnection *bcnc;
46 
47 	gchar *schema;
48 	gchar *table_name;
49 	gchar *table_short_name;
50 
51 	GdauiBar *header;
52 	GtkWidget *contents; /* notebook with pageO <=> @unknown_table_notice, page1 <=> @pages */
53 	GtkWidget *unknown_table_notice;
54 	GtkWidget *pages; /* notebook to store individual pages */
55 
56 	GtkWidget *insert_popup;
57 	GHashTable *insert_columns_hash; /* key = column index as a pointer, value = GdaHolder in the
58 					 * params used in the INSERT statement */
59 };
60 
61 static void table_info_class_init (TableInfoClass *klass);
62 static void table_info_init       (TableInfo *tinfo, TableInfoClass *klass);
63 static void table_info_dispose   (GObject *object);
64 static void table_info_set_property (GObject *object,
65 				     guint param_id,
66 				     const GValue *value,
67 				     GParamSpec *pspec);
68 static void table_info_get_property (GObject *object,
69 				     guint param_id,
70 				     GValue *value,
71 				     GParamSpec *pspec);
72 /* BrowserPage interface */
73 static void                 table_info_page_init (BrowserPageIface *iface);
74 static GtkActionGroup      *table_info_page_get_actions_group (BrowserPage *page);
75 static const gchar         *table_info_page_get_actions_ui (BrowserPage *page);
76 static GtkWidget           *table_info_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
77 
78 static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableInfo *tinfo);
79 
80 /* properties */
81 enum {
82         PROP_0,
83 };
84 
85 static GObjectClass *parent_class = NULL;
86 
87 
88 /*
89  * TableInfo class implementation
90  */
91 
92 static void
table_info_class_init(TableInfoClass * klass)93 table_info_class_init (TableInfoClass *klass)
94 {
95 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
96 
97 	parent_class = g_type_class_peek_parent (klass);
98 
99 	/* Properties */
100         object_class->set_property = table_info_set_property;
101         object_class->get_property = table_info_get_property;
102 
103 	object_class->dispose = table_info_dispose;
104 }
105 
106 static void
table_info_page_init(BrowserPageIface * iface)107 table_info_page_init (BrowserPageIface *iface)
108 {
109 	iface->i_get_actions_group = table_info_page_get_actions_group;
110 	iface->i_get_actions_ui = table_info_page_get_actions_ui;
111 	iface->i_get_tab_label = table_info_page_get_tab_label;
112 }
113 
114 static void
table_info_init(TableInfo * tinfo,G_GNUC_UNUSED TableInfoClass * klass)115 table_info_init (TableInfo *tinfo, G_GNUC_UNUSED TableInfoClass *klass)
116 {
117 	tinfo->priv = g_new0 (TableInfoPrivate, 1);
118 
119 	gtk_orientable_set_orientation (GTK_ORIENTABLE (tinfo), GTK_ORIENTATION_VERTICAL);
120 }
121 
122 static void
table_info_dispose(GObject * object)123 table_info_dispose (GObject *object)
124 {
125 	TableInfo *tinfo = (TableInfo *) object;
126 
127 	/* free memory */
128 	if (tinfo->priv) {
129 		if (tinfo->priv->insert_columns_hash)
130 			g_hash_table_destroy (tinfo->priv->insert_columns_hash);
131 		if (tinfo->priv->insert_popup)
132 			gtk_widget_destroy (tinfo->priv->insert_popup);
133 		g_free (tinfo->priv->schema);
134 		g_free (tinfo->priv->table_name);
135 		g_free (tinfo->priv->table_short_name);
136 		if (tinfo->priv->bcnc) {
137 			g_signal_handlers_disconnect_by_func (tinfo->priv->bcnc,
138 							      G_CALLBACK (meta_changed_cb), tinfo);
139 			g_object_unref (tinfo->priv->bcnc);
140 		}
141 
142 		g_free (tinfo->priv);
143 		tinfo->priv = NULL;
144 	}
145 
146 	parent_class->dispose (object);
147 }
148 
149 GType
table_info_get_type(void)150 table_info_get_type (void)
151 {
152 	static GType type = 0;
153 
154 	if (G_UNLIKELY (type == 0)) {
155 		static const GTypeInfo info = {
156 			sizeof (TableInfoClass),
157 			(GBaseInitFunc) NULL,
158 			(GBaseFinalizeFunc) NULL,
159 			(GClassInitFunc) table_info_class_init,
160 			NULL,
161 			NULL,
162 			sizeof (TableInfo),
163 			0,
164 			(GInstanceInitFunc) table_info_init,
165 			0
166 		};
167 
168 		static GInterfaceInfo page_info = {
169                         (GInterfaceInitFunc) table_info_page_init,
170 			NULL,
171                         NULL
172                 };
173 
174 		type = g_type_register_static (GTK_TYPE_BOX, "TableInfo", &info, 0);
175 		g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info);
176 	}
177 	return type;
178 }
179 
180 static void
table_info_set_property(GObject * object,guint param_id,G_GNUC_UNUSED const GValue * value,GParamSpec * pspec)181 table_info_set_property (GObject *object,
182 			 guint param_id,
183 			 G_GNUC_UNUSED const GValue *value,
184 			 GParamSpec *pspec)
185 {
186 	switch (param_id) {
187 	default:
188 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
189 		break;
190 	}
191 }
192 
193 static void
table_info_get_property(GObject * object,guint param_id,G_GNUC_UNUSED GValue * value,GParamSpec * pspec)194 table_info_get_property (GObject *object,
195 			 guint param_id,
196 			 G_GNUC_UNUSED GValue *value,
197 			 GParamSpec *pspec)
198 {
199 	switch (param_id) {
200 	default:
201 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
202 		break;
203 	}
204 }
205 
206 static void
display_table_not_found_error(TableInfo * tinfo,gboolean is_error)207 display_table_not_found_error (TableInfo *tinfo, gboolean is_error)
208 {
209 	if (is_error)
210 		gtk_notebook_set_current_page (GTK_NOTEBOOK (tinfo->priv->contents), 0);
211 	else
212 		gtk_notebook_set_current_page (GTK_NOTEBOOK (tinfo->priv->contents), 1);
213 }
214 
215 static gchar *
table_info_to_selection(TableInfo * tinfo)216 table_info_to_selection (TableInfo *tinfo)
217 {
218 	GString *string;
219 	gchar *tmp;
220 	string = g_string_new ("OBJ_TYPE=table");
221 	tmp = gda_rfc1738_encode (tinfo->priv->schema);
222 	g_string_append_printf (string, ";OBJ_SCHEMA=%s", tmp);
223 	g_free (tmp);
224 	tmp = gda_rfc1738_encode (tinfo->priv->table_name);
225 	g_string_append_printf (string, ";OBJ_NAME=%s", tmp);
226 	g_free (tmp);
227 	tmp = gda_rfc1738_encode (tinfo->priv->table_short_name);
228 	g_string_append_printf (string, ";OBJ_SHORT_NAME=%s", tmp);
229 	g_free (tmp);
230 	return g_string_free (string, FALSE);
231 }
232 
233 static void
source_drag_data_get_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkDragContext * context,GtkSelectionData * selection_data,guint info,G_GNUC_UNUSED guint time,TableInfo * tinfo)234 source_drag_data_get_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkDragContext *context,
235 			 GtkSelectionData *selection_data,
236 			 guint info, G_GNUC_UNUSED guint time, TableInfo *tinfo)
237 {
238 	switch (info) {
239 	case TARGET_KEY_VALUE: {
240 		gchar *str;
241 		str = table_info_to_selection (tinfo);
242 		gtk_selection_data_set (selection_data,
243 					gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
244 					strlen (str));
245 		g_free (str);
246 		break;
247 	}
248 	default:
249 	case TARGET_PLAIN: {
250 		gchar *str;
251 		str = g_strdup_printf ("%s.%s", tinfo->priv->schema, tinfo->priv->table_name);
252 		gtk_selection_data_set_text (selection_data, str, -1);
253 		g_free (str);
254 		break;
255 	}
256 	case TARGET_ROOTWIN:
257 		TO_IMPLEMENT; /* dropping on the Root Window => create a file */
258 		break;
259 	}
260 }
261 
262 static void
meta_changed_cb(G_GNUC_UNUSED BrowserConnection * bcnc,GdaMetaStruct * mstruct,TableInfo * tinfo)263 meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableInfo *tinfo)
264 {
265 	GdaMetaDbObject *dbo;
266 	GValue *schema_v = NULL, *name_v;
267 
268 	if (tinfo->priv->insert_columns_hash) {
269 		g_hash_table_destroy (tinfo->priv->insert_columns_hash);
270 		tinfo->priv->insert_columns_hash = NULL;
271 	}
272 	if (tinfo->priv->insert_popup) {
273 		gtk_widget_destroy (tinfo->priv->insert_popup);
274 		tinfo->priv->insert_popup = NULL;
275 	}
276 
277 	g_value_set_string ((schema_v = gda_value_new (G_TYPE_STRING)), tinfo->priv->schema);
278 	g_value_set_string ((name_v = gda_value_new (G_TYPE_STRING)), tinfo->priv->table_name);
279 	dbo = gda_meta_struct_get_db_object (mstruct, NULL, schema_v, name_v);
280 	if (schema_v)
281 		gda_value_free (schema_v);
282 	gda_value_free (name_v);
283 
284 	if (tinfo->priv->table_short_name) {
285 		g_free (tinfo->priv->table_short_name);
286 		tinfo->priv->table_short_name = NULL;
287 
288 		gtk_drag_source_unset ((GtkWidget *) tinfo->priv->header);
289 		g_signal_handlers_disconnect_by_func (tinfo->priv->header,
290 						      G_CALLBACK (source_drag_data_get_cb), tinfo);
291 	}
292 	if (dbo) {
293 		tinfo->priv->table_short_name = g_strdup (dbo->obj_short_name);
294 		gtk_drag_source_set ((GtkWidget *) tinfo->priv->header, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
295 				     dbo_table, G_N_ELEMENTS (dbo_table), GDK_ACTION_COPY);
296 		gtk_drag_source_set_icon_pixbuf ((GtkWidget *) tinfo->priv->header,
297 						 browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
298 		g_signal_connect (tinfo->priv->header, "drag-data-get",
299 				  G_CALLBACK (source_drag_data_get_cb), tinfo);
300 	}
301 
302 	display_table_not_found_error (tinfo, dbo ? FALSE : TRUE);
303 }
304 
305 /**
306  * table_info_new
307  *
308  * Returns: a new #GtkWidget
309  */
310 GtkWidget *
table_info_new(BrowserConnection * bcnc,const gchar * schema,const gchar * table)311 table_info_new (BrowserConnection *bcnc,
312 		const gchar *schema, const gchar *table)
313 {
314 	TableInfo *tinfo;
315 
316 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
317 	g_return_val_if_fail (schema, NULL);
318 	g_return_val_if_fail (table, NULL);
319 
320 	tinfo = TABLE_INFO (g_object_new (TABLE_INFO_TYPE, NULL));
321 
322 	tinfo->priv->bcnc = g_object_ref (bcnc);
323 	g_signal_connect (tinfo->priv->bcnc, "meta-changed",
324 			  G_CALLBACK (meta_changed_cb), tinfo);
325 	tinfo->priv->schema = g_strdup (schema);
326 	tinfo->priv->table_name = g_strdup (table);
327 
328 	/* header */
329         GtkWidget *label;
330 	gchar *str, *tmp;
331 
332 	/* To translators: "In schema" refers to the database schema an object is in */
333 	tmp = g_strdup_printf (_("In schema '%s'"), schema);
334 	str = g_strdup_printf ("<b>%s</b>\n%s", table, tmp);
335 	g_free (tmp);
336 	label = gdaui_bar_new (str);
337 	g_free (str);
338         gtk_box_pack_start (GTK_BOX (tinfo), label, FALSE, FALSE, 0);
339         gtk_widget_show (label);
340 	tinfo->priv->header = GDAUI_BAR (label);
341 
342 	/* main contents */
343 	GtkWidget *top_nb;
344 	top_nb = gtk_notebook_new ();
345 	tinfo->priv->contents = top_nb;
346 	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (top_nb), GTK_POS_BOTTOM);
347 	gtk_notebook_set_show_border (GTK_NOTEBOOK (top_nb), FALSE);
348 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (top_nb), FALSE);
349 	gtk_box_pack_start (GTK_BOX (tinfo), top_nb, TRUE, TRUE, 0);
350 
351 	/* "table not found" error page */
352 	GtkWidget *image, *hbox;
353 
354 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
355 	gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (""), TRUE, TRUE, 0);
356 	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
357 	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 10);
358 	label = gtk_label_new (_("Table not found. If you think this is an error,\n"
359 				 "please refresh the meta data from the database\n"
360 				 "(menu Connection/Fetch meta data)."));
361 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
362 	gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (""), TRUE, TRUE, 0);
363 
364 	gtk_notebook_append_page (GTK_NOTEBOOK (top_nb), hbox, NULL);
365 	tinfo->priv->unknown_table_notice = label;
366 
367 	/* notebook for the pages */
368 	GtkWidget *sub_nb;
369 	sub_nb = gtk_notebook_new ();
370 	tinfo->priv->pages = sub_nb;
371 	gtk_notebook_append_page (GTK_NOTEBOOK (top_nb), sub_nb, NULL);
372 	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (sub_nb), GTK_POS_BOTTOM);
373 
374 	/* append pages */
375 	GtkWidget *page;
376 	page = table_columns_new (tinfo);
377 	if (page) {
378 		label = gtk_label_new ("");
379 		str = g_strdup_printf ("<small>%s</small>", _("Columns"));
380 		gtk_label_set_markup (GTK_LABEL (label), str);
381 		g_free (str);
382 		gtk_widget_show (page);
383 		gtk_notebook_append_page (GTK_NOTEBOOK (sub_nb), page, label);
384 	}
385 #ifdef HAVE_GOOCANVAS
386 	page = table_relations_new (tinfo);
387 	if (page) {
388 		label = gtk_label_new ("");
389 		str = g_strdup_printf ("<small>%s</small>", _("Relations"));
390 		gtk_label_set_markup (GTK_LABEL (label), str);
391 		g_free (str);
392 		gtk_widget_show (page);
393 		gtk_notebook_append_page (GTK_NOTEBOOK (sub_nb), page, label);
394 	}
395 #endif
396 	page = table_preferences_new (tinfo);
397 	if (page) {
398 		label = gtk_label_new ("");
399 		str = g_strdup_printf ("<small>%s</small>", _("Preferences"));
400 		gtk_label_set_markup (GTK_LABEL (label), str);
401 		g_free (str);
402 		gtk_widget_show (page);
403 		gtk_notebook_append_page (GTK_NOTEBOOK (sub_nb), page, label);
404 	}
405 	gtk_notebook_set_current_page (GTK_NOTEBOOK (sub_nb), 0);
406 
407 	/* show everything */
408         gtk_widget_show_all (top_nb);
409 	display_table_not_found_error (tinfo, TRUE);
410 
411 	GdaMetaStruct *mstruct;
412 	mstruct = browser_connection_get_meta_struct (tinfo->priv->bcnc);
413 	if (mstruct)
414 		meta_changed_cb (tinfo->priv->bcnc, mstruct, tinfo);
415 
416 	return (GtkWidget*) tinfo;
417 }
418 
419 /**
420  * table_info_get_table_schema
421  */
422 const gchar *
table_info_get_table_schema(TableInfo * tinfo)423 table_info_get_table_schema (TableInfo *tinfo)
424 {
425 	g_return_val_if_fail (IS_TABLE_INFO (tinfo), NULL);
426 	return tinfo->priv->schema;
427 }
428 
429 /**
430  * table_info_get_table_name
431  */
432 const gchar *
table_info_get_table_name(TableInfo * tinfo)433 table_info_get_table_name (TableInfo *tinfo)
434 {
435 	g_return_val_if_fail (IS_TABLE_INFO (tinfo), NULL);
436 	return tinfo->priv->table_name;
437 }
438 
439 /**
440  * table_info_get_connection
441  */
442 BrowserConnection *
table_info_get_connection(TableInfo * tinfo)443 table_info_get_connection (TableInfo *tinfo)
444 {
445 	g_return_val_if_fail (IS_TABLE_INFO (tinfo), NULL);
446 	return tinfo->priv->bcnc;
447 }
448 
449 /*
450  * UI actions
451  */
452 static void
action_add_to_fav_cb(G_GNUC_UNUSED GtkAction * action,TableInfo * tinfo)453 action_add_to_fav_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
454 {
455 	ToolsFavorites *bfav;
456         ToolsFavoritesAttributes fav;
457         GError *error = NULL;
458 
459         memset (&fav, 0, sizeof (ToolsFavoritesAttributes));
460         fav.id = -1;
461         fav.type = GDA_TOOLS_FAVORITES_TABLES;
462         fav.name = NULL;
463         fav.descr = NULL;
464         fav.contents = table_info_to_selection (tinfo);
465 
466         bfav = browser_connection_get_favorites (tinfo->priv->bcnc);
467         if (! gda_tools_favorites_add (bfav, 0, &fav, ORDER_KEY_SCHEMA, G_MAXINT, &error)) {
468                 browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tinfo),
469                                     _("Could not add favorite: %s"),
470                                     error && error->message ? error->message : _("No detail"));
471                 if (error)
472                         g_error_free (error);
473         }
474 	g_free (fav.contents);
475 }
476 
477 static void
action_view_contents_cb(G_GNUC_UNUSED GtkAction * action,TableInfo * tinfo)478 action_view_contents_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
479 {
480 	if (! tinfo->priv->table_short_name)
481 		return;
482 
483 	BrowserWindow *bwin;
484 	BrowserPerspective *pers;
485 	bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) tinfo);
486 	pers = browser_window_change_perspective (bwin, _("Data manager"));
487 
488 	xmlDocPtr doc;
489 	xmlNodePtr node, topnode;
490 	xmlChar *contents;
491 	int size;
492 	doc = xmlNewDoc (BAD_CAST "1.0");
493 	topnode = xmlNewDocNode (doc, NULL, BAD_CAST "data", NULL);
494 	xmlDocSetRootElement (doc, topnode);
495 	node = xmlNewChild (topnode, NULL, BAD_CAST "table", NULL);
496 	xmlSetProp (node, BAD_CAST "name", BAD_CAST tinfo->priv->table_short_name);
497 	xmlDocDumpFormatMemory (doc, &contents, &size, 1);
498 	xmlFreeDoc (doc);
499 
500 	data_manager_perspective_new_tab (DATA_MANAGER_PERSPECTIVE (pers), (gchar*) contents);
501 	xmlFree (contents);
502 }
503 
504 static void
insert_form_params_changed_cb(GdauiBasicForm * form,G_GNUC_UNUSED GdaHolder * param,G_GNUC_UNUSED gboolean is_user_modif,GtkWidget * popup)505 insert_form_params_changed_cb (GdauiBasicForm *form, G_GNUC_UNUSED GdaHolder *param,
506 			       G_GNUC_UNUSED gboolean is_user_modif, GtkWidget *popup)
507 {
508 	/* if all params are valid => authorize the execute button */
509 	gtk_dialog_set_response_sensitive (GTK_DIALOG (popup), GTK_RESPONSE_ACCEPT,
510 					   gdaui_basic_form_is_valid (form));
511 }
512 
statement_executed_cb(G_GNUC_UNUSED BrowserConnection * bcnc,G_GNUC_UNUSED guint exec_id,G_GNUC_UNUSED GObject * out_result,G_GNUC_UNUSED GdaSet * out_last_inserted_row,GError * error,TableInfo * tinfo)513 static void statement_executed_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
514 				   G_GNUC_UNUSED guint exec_id,
515 				   G_GNUC_UNUSED GObject *out_result,
516 				   G_GNUC_UNUSED GdaSet *out_last_inserted_row, GError *error,
517 				   TableInfo *tinfo)
518 {
519 	if (error)
520 		browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tinfo)),
521 				    _("Error executing query:\n%s"),
522 				    error->message ?
523 				    error->message : _("No detail"));
524 	else
525 		browser_window_show_notice_printf (BROWSER_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tinfo)),
526 						   GTK_MESSAGE_INFO,
527 						   "DataInsertQuery",
528 						   "%s", _("Data successfully inserted"));
529 }
530 
531 static void
insert_response_cb(GtkWidget * dialog,gint response_id,TableInfo * tinfo)532 insert_response_cb (GtkWidget *dialog, gint response_id, TableInfo *tinfo)
533 {
534 	if (response_id == GTK_RESPONSE_ACCEPT) {
535 		GdaStatement *stmt;
536 		GdaSet *params;
537 		GError *lerror = NULL;
538 
539 		stmt = g_object_get_data (G_OBJECT (dialog), "stmt");
540 		params = g_object_get_data (G_OBJECT (dialog), "params");
541 
542 		if (! browser_connection_execute_statement_cb (tinfo->priv->bcnc,
543 							       stmt, params,
544 							       GDA_STATEMENT_MODEL_RANDOM_ACCESS,
545 							       FALSE,
546 							       (BrowserConnectionExecuteCallback) statement_executed_cb,
547 							       tinfo, &lerror)) {
548 			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tinfo)),
549 					    _("Error executing query: %s"),
550 					    lerror && lerror->message ? lerror->message : _("No detail"));
551 			g_clear_error (&lerror);
552 		}
553 		gtk_widget_hide (dialog);
554 	}
555 #ifdef HAVE_GDU
556 	else if (response_id == GTK_RESPONSE_HELP) {
557 		browser_show_help ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tinfo),
558 				   "table-insert-data");
559 	}
560 #endif
561 	else
562 		gtk_widget_hide (dialog);
563 }
564 
565 typedef struct {
566 	gint          cols_nb;
567 	gint         *fk_cols_array;
568 	GdaSet       *insert_params;
569 	GHashTable   *chash;
570 	GdaStatement *stmt;
571 	GdaDataModel *model;
572 	gboolean      model_rerunning;
573 } FKBindData;
574 
575 static void
fk_bind_select_executed_cb(G_GNUC_UNUSED BrowserConnection * bcnc,G_GNUC_UNUSED guint exec_id,GObject * out_result,G_GNUC_UNUSED GdaSet * out_last_inserted_row,G_GNUC_UNUSED GError * error,FKBindData * fkdata)576 fk_bind_select_executed_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
577 			    G_GNUC_UNUSED guint exec_id,
578 			    GObject *out_result,
579 			    G_GNUC_UNUSED GdaSet *out_last_inserted_row, G_GNUC_UNUSED GError *error,
580 			    FKBindData *fkdata)
581 {
582 	gint i;
583 	GdaDataModel *model;
584 	if (! out_result)
585 		return;
586 
587 	if (fkdata->model)
588 		g_object_unref (fkdata->model);
589 
590 	model = GDA_DATA_MODEL (out_result);
591 	for (i = 0; i < fkdata->cols_nb; i++) {
592 		GdaHolder *h;
593 		GdaSetSource *source;
594 		h = g_hash_table_lookup (fkdata->chash,
595 					 GINT_TO_POINTER (fkdata->fk_cols_array [i] - 1));
596 		source = gda_set_get_source (fkdata->insert_params, h);
597 		if (source && gda_holder_get_source_model (h, NULL)) {
598 			gda_set_replace_source_model (fkdata->insert_params, source,
599 						      model);
600 			/* break now as gda_set_replace_source_model() does the job of replacing
601 			 * the data model for all the holders which share the same data model */
602 			break;
603 		}
604 		else {
605 			gboolean bound;
606 			bound = gda_holder_set_source_model (h, model, i, NULL);
607 #ifdef GDA_DEBUG_NO
608 			if (bound)
609 				g_print ("Bound holder [%s] to column %d for model %p\n", gda_holder_get_id (h), i, model);
610 			else
611 				g_print ("Could not bind holder [%s] to column %d\n", gda_holder_get_id (h), i);
612 #endif
613 			if (!bound) {
614 				/* There was an error => unbind all the parameters */
615 				for (i = 0; i < fkdata->cols_nb; i++) {
616 					h = g_hash_table_lookup (fkdata->chash,
617 								 GINT_TO_POINTER (fkdata->fk_cols_array [i] - 1));
618 					gda_holder_set_source_model (h, NULL, 0, NULL);
619 				}
620 				break;
621 			}
622 		}
623 	}
624 	fkdata->model = g_object_ref (out_result);
625 	fkdata->model_rerunning = FALSE;
626 }
627 
628 static void
fkdata_list_free(GSList * fkdata_list)629 fkdata_list_free (GSList *fkdata_list)
630 {
631 	GSList *list;
632 	for (list = fkdata_list; list; list = list->next) {
633 		FKBindData *fkdata = (FKBindData*) list->data;
634 		g_free (fkdata->fk_cols_array);
635 		g_object_unref (fkdata->insert_params);
636 		g_object_unref (fkdata->stmt);
637 		if (fkdata->model)
638 			g_object_unref (fkdata->model);
639 		g_free (fkdata);
640 	}
641 	g_slist_free (fkdata_list);
642 }
643 
644 static void
action_insert_cb(G_GNUC_UNUSED GtkAction * action,TableInfo * tinfo)645 action_insert_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
646 {
647 	/* init */
648 	if (! tinfo->priv->table_short_name)
649 		return;
650 
651 	if (tinfo->priv->insert_popup) {
652 		gtk_widget_show (tinfo->priv->insert_popup);
653 		GSList *fkdata_list;
654 
655 		for (fkdata_list = g_object_get_data (G_OBJECT (tinfo->priv->insert_popup), "fkdata_list");
656 		     fkdata_list; fkdata_list = fkdata_list->next) {
657 			FKBindData *fkdata = (FKBindData *) fkdata_list->data;
658 			if (fkdata->model && !fkdata->model_rerunning) {
659 				if (browser_connection_execute_statement_cb (tinfo->priv->bcnc,
660 									     fkdata->stmt, NULL,
661 									     GDA_STATEMENT_MODEL_RANDOM_ACCESS,
662 									     FALSE,
663 									     (BrowserConnectionExecuteCallback) fk_bind_select_executed_cb,
664 									     fkdata, NULL) != 0)
665 					fkdata->model_rerunning = TRUE;
666 			}
667 		}
668 		return;
669 	}
670 
671 	BrowserWindow *bwin;
672 	GdaMetaStruct *mstruct;
673 	bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) tinfo);
674 	mstruct = browser_connection_get_meta_struct (tinfo->priv->bcnc);
675 	if (!mstruct) {
676 		browser_show_error (GTK_WINDOW (bwin), _("Meta data not yet available"));
677 		return;
678 	}
679 
680 	/* get table's information */
681 	GdaMetaDbObject *dbo;
682 	GValue *v1, *v2;
683 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)),
684 			    tinfo->priv->schema);
685 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)),
686 			    tinfo->priv->table_name);
687 	dbo = gda_meta_struct_complement (mstruct,
688 					  GDA_META_DB_TABLE, NULL, v1, v2, NULL);
689 	gda_value_free (v1);
690 	gda_value_free (v2);
691 
692 	if (! dbo) {
693 		browser_show_error (GTK_WINDOW (bwin), _("Can't find information about table"));
694 		return;
695 	}
696 
697 	/* build statement */
698 	GdaSqlBuilder *b;
699 	GSList *list;
700 	GdaMetaTable *mtable;
701 
702 	b = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
703 	gda_sql_builder_set_table (b, tinfo->priv->table_short_name);
704 	mtable = GDA_META_TABLE (dbo);
705 	for (list = mtable->columns; list; list = list->next) {
706 		GdaMetaTableColumn *col = (GdaMetaTableColumn*) list->data;
707 		gda_sql_builder_add_field_value_id (b,
708 						    gda_sql_builder_add_id (b, col->column_name),
709 						    gda_sql_builder_add_param (b, col->column_name,
710 									       col->gtype, col->nullok));
711 	}
712 	GdaStatement *stmt;
713 	stmt = gda_sql_builder_get_statement (b, NULL);
714 	g_object_unref (b);
715 #ifdef GDA_DEBUG_NO
716 	gchar *sql;
717 	sql = gda_statement_to_sql (stmt, NULL, NULL);
718 	g_print ("[%s]\n", sql);
719 	g_free (sql);
720 #endif
721 
722 	/* handle user preferences */
723 	GdaSet *params;
724 	if (! gda_statement_get_parameters (stmt, &params, NULL)) {
725 		gchar *sql;
726 		sql = gda_statement_to_sql (stmt, NULL, NULL);
727 
728 		browser_show_error (GTK_WINDOW (bwin),
729 				    _("Internal error while building INSERT statement:\n%s"), sql);
730 		g_free (sql);
731 		g_object_unref (stmt);
732 		return;
733 	}
734 	GSList *fkdata_list = NULL;
735 	gint nthcol;
736 	if (mtable->fk_list)
737 		tinfo->priv->insert_columns_hash = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
738 
739 	for (nthcol = 0, list = mtable->columns; list; nthcol++, list = list->next) {
740 		GdaMetaTableColumn *col = (GdaMetaTableColumn*) list->data;
741 		gchar *plugin;
742 		const GValue *autoinc;
743 		GdaHolder *holder;
744 
745 		plugin = browser_connection_get_table_column_attribute (tinfo->priv->bcnc,
746 									mtable,	col,
747 									BROWSER_CONNECTION_COLUMN_PLUGIN,
748 									NULL);
749 		holder = gda_set_get_holder (params, col->column_name);
750 		if (!holder)
751 			continue;
752 
753 		autoinc = gda_meta_table_column_get_attribute (col, GDA_ATTRIBUTE_AUTO_INCREMENT);
754 		if (tinfo->priv->insert_columns_hash)
755 			g_hash_table_insert (tinfo->priv->insert_columns_hash, GINT_TO_POINTER (nthcol), g_object_ref (holder));
756 
757 		if (!plugin && !col->default_value && !autoinc)
758 			continue;
759 		if (plugin) {
760 			GValue *value;
761 			value = gda_value_new_from_string (plugin, G_TYPE_STRING);
762 			gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
763 			gda_value_free (value);
764 		}
765 
766 		if (col->default_value) {
767 			GValue *dv;
768 			//g_value_set_string ((dv = gda_value_new (G_TYPE_STRING)), col->default_value);
769 			dv = gda_value_new_null ();
770 			gda_holder_set_default_value (holder, dv);
771 			gda_value_free (dv);
772 			gda_holder_set_value_to_default (holder);
773 
774 			gchar *tmp;
775 			tmp = g_strdup_printf (_("Default value: '%s'"), col->default_value);
776 			g_object_set (holder, "description", tmp, NULL);
777 			g_free (tmp);
778 		}
779 		else if (autoinc) {
780 			GValue *dv;
781 			g_value_set_string ((dv = gda_value_new (G_TYPE_STRING)), "");
782 			gda_holder_set_default_value (holder, dv);
783 			gda_value_free (dv);
784 			gda_holder_set_value_to_default (holder);
785 			g_object_set (holder, "description", _("Default value: auto incremented value"), NULL);
786 		}
787 
788 		g_free (plugin);
789 	}
790 
791 	/* analyse FK list to propose values to choose from */
792 	for (list = mtable->fk_list; list; list = list->next) {
793 		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
794 		if (fk->depend_on == dbo) /* don't link to itself */
795 			continue;
796 		else if (fk->depend_on->obj_type != GDA_META_DB_TABLE)
797 			continue;
798 		GdaMetaDbObject *rdbo = (GdaMetaDbObject*) fk->depend_on;
799 		GdaDataModel *cmodel;
800 		GValue *schema_v, *name_v, *catalog_v;
801 		GError *lerror = NULL;
802 
803 		g_value_set_string ((catalog_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_catalog);
804 		g_value_set_string ((schema_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_schema);
805 		g_value_set_string ((name_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_name);
806 		dbo = gda_meta_struct_get_db_object (mstruct, NULL, schema_v, name_v);
807 
808 		cmodel = gda_meta_store_extract (browser_connection_get_meta_store (tinfo->priv->bcnc),
809 						"SELECT tc.constraint_name, k.column_name FROM _key_column_usage k INNER JOIN _table_constraints tc ON (k.table_catalog=tc.table_catalog AND k.table_schema=tc.table_schema AND k.table_name=tc.table_name AND k.constraint_name=tc.constraint_name) WHERE tc.constraint_type='UNIQUE' AND k.table_catalog = ##catalog::string AND k.table_schema = ##schema::string AND k.table_name = ##tname::string ORDER by k.ordinal_position", &lerror,
810 						"catalog", catalog_v,
811 						"schema", schema_v,
812 						"tname", name_v, NULL);
813 
814 		gda_value_free (catalog_v);
815 		gda_value_free (schema_v);
816 		gda_value_free (name_v);
817 
818 		GdaSqlBuilder *b = NULL;
819 		if (cmodel) {
820 			gint nrows, i;
821 			nrows = gda_data_model_get_n_rows (cmodel);
822 			b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
823 			gda_sql_builder_select_add_target_id (b,
824 							      gda_sql_builder_add_id (b, rdbo->obj_short_name),
825 							      NULL);
826 			/* add REF PK fields */
827 			for (i = 0; i < fk->cols_nb; i++) {
828 				gda_sql_builder_select_add_field (b, fk->ref_pk_names_array [i], NULL, NULL);
829 			}
830 
831 			/* add UNIQUE fields */
832 			for (i = 0; i < nrows; i++) {
833 				const GValue *cvalue;
834 				cvalue = gda_data_model_get_value_at (cmodel, 1, i, NULL);
835 				if (!cvalue || (G_VALUE_TYPE (cvalue) != G_TYPE_STRING))
836 					break;
837 				gda_sql_builder_select_add_field (b, g_value_get_string (cvalue), NULL, NULL);
838 				gda_sql_builder_select_order_by (b,
839 								 gda_sql_builder_add_id (b, g_value_get_string (cvalue)),
840 								 TRUE, NULL);
841 			}
842 			if (i < nrows) {
843 				/* error  */
844 				g_object_unref (b);
845 				b = NULL;
846 			}
847 			/*gda_data_model_dump (cmodel, NULL);*/
848 			g_object_unref (cmodel);
849 		}
850 		else {
851 			g_warning ("Can't get list of unique constraints for table '%s': %s",
852 				   rdbo->obj_short_name,
853 				   lerror && lerror->message ? lerror->message : _("No detail"));
854 			g_clear_error (&lerror);
855 		}
856 
857 		if (b) {
858 			GdaStatement *stmt;
859 			stmt = gda_sql_builder_get_statement (b, NULL);
860 			if (stmt) {
861 #ifdef GDA_DEBUG_NO
862 				gchar *sql;
863 				sql = gda_statement_to_sql (stmt, NULL, NULL);
864 				g_print ("UNIQUE SELECT [%s]\n", sql);
865 				g_free (sql);
866 #endif
867 
868 				FKBindData *fkdata;
869 				guint eid;
870 				fkdata = g_new0 (FKBindData, 1);
871 				fkdata->cols_nb = fk->cols_nb;
872 				fkdata->fk_cols_array = g_new (gint, fk->cols_nb);
873 				memcpy (fkdata->fk_cols_array, fk->fk_cols_array, sizeof (gint) * fk->cols_nb);
874 				fkdata->chash = tinfo->priv->insert_columns_hash;
875 				fkdata->stmt = stmt;
876 				eid = browser_connection_execute_statement_cb (tinfo->priv->bcnc, stmt, NULL,
877 									       GDA_STATEMENT_MODEL_RANDOM_ACCESS,
878 									       FALSE,
879 									       (BrowserConnectionExecuteCallback) fk_bind_select_executed_cb,
880 									       fkdata, NULL);
881 				if (! eid) {
882 					g_free (fkdata->fk_cols_array);
883 					g_object_unref (fkdata->stmt);
884 					g_free (fkdata);
885 				}
886 				fkdata->insert_params = g_object_ref (params);
887 
888 				/* attach the kfdata to @popup to be able to re-run the SELECT
889 				 * everytime the window is shown */
890 				fkdata_list = g_slist_prepend (fkdata_list, fkdata);
891 			}
892 
893 			g_object_unref (b);
894 		}
895 	}
896 
897 	/* create popup */
898 	GtkWidget *popup;
899 	popup = gtk_dialog_new_with_buttons (_("Values to insert into table"), GTK_WINDOW (bwin),
900 					     0,
901 #ifdef HAVE_GDU
902 					     GTK_STOCK_HELP, GTK_RESPONSE_HELP,
903 #endif
904 					     GTK_STOCK_EXECUTE, GTK_RESPONSE_ACCEPT,
905 					     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
906 					     NULL);
907 	tinfo->priv->insert_popup = popup;
908 	g_object_set_data_full (G_OBJECT (popup), "stmt", stmt, g_object_unref);
909 	g_object_set_data_full (G_OBJECT (popup), "params", params, g_object_unref);
910 	if (fkdata_list)
911 		g_object_set_data_full (G_OBJECT (popup), "fkdata_list",
912 					fkdata_list, (GDestroyNotify) fkdata_list_free);
913 
914 	g_signal_connect (popup, "close",
915 			  G_CALLBACK (gtk_widget_hide), NULL);
916 	g_signal_connect (popup, "response",
917 			  G_CALLBACK (insert_response_cb), tinfo);
918 
919 	GtkWidget *label, *form;
920 	gchar *str;
921 	label = gtk_label_new ("");
922 	str = g_strdup_printf ("<b>%s</b>:\n<small>%s</small>",
923 			       _("Values to insert into table"),
924 			       _("assign values to the following variables"));
925 	gtk_label_set_markup (GTK_LABEL (label), str);
926 	g_free (str);
927 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (popup))),
928 			    label, FALSE, FALSE, 0);
929 
930 	form = gdaui_basic_form_new (params);
931 	g_object_set ((GObject*) form, "show-actions", TRUE, NULL);
932 	g_signal_connect (form, "holder-changed",
933 			  G_CALLBACK (insert_form_params_changed_cb), popup);
934 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (popup))),
935 			    form, TRUE, TRUE, 5);
936 
937 	gtk_dialog_set_response_sensitive (GTK_DIALOG (popup), GTK_RESPONSE_ACCEPT,
938 					   gdaui_basic_form_is_valid (GDAUI_BASIC_FORM (form)));
939 
940 	gtk_widget_show_all (popup);
941 }
942 
943 static void
action_declarefk_cb(G_GNUC_UNUSED GtkAction * action,TableInfo * tinfo)944 action_declarefk_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
945 {
946 	GtkWidget *dlg, *parent;
947 	GdaMetaStruct *mstruct;
948 	GdaMetaDbObject *dbo;
949 	gint response;
950 	GValue *v1, *v2;
951 
952 	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) tinfo);
953 	mstruct = browser_connection_get_meta_struct (tinfo->priv->bcnc);
954 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), tinfo->priv->schema);
955 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), tinfo->priv->table_name);
956 	dbo = gda_meta_struct_get_db_object (mstruct, NULL, v1, v2);
957 	gda_value_free (v1);
958 	gda_value_free (v2);
959 	if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE)) {
960 		browser_show_error ((GtkWindow *) parent, _("Can't find information about table '%s'"),
961 				    tinfo->priv->table_short_name);
962 		return;
963 	}
964 
965 	dlg = fk_declare_new ((GtkWindow *) parent, mstruct, GDA_META_TABLE (dbo));
966 	response = gtk_dialog_run (GTK_DIALOG (dlg));
967 	if (response == GTK_RESPONSE_ACCEPT) {
968 		GError *error = NULL;
969 		if (! fk_declare_write (FK_DECLARE (dlg),
970 					BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
971 					&error)) {
972 			browser_show_error ((GtkWindow *) parent, _("Failed to declare foreign key: %s"),
973 					    error && error->message ? error->message : _("No detail"));
974 			g_clear_error (&error);
975 		}
976 		else if (BROWSER_IS_WINDOW (parent))
977 			browser_window_show_notice (BROWSER_WINDOW (parent),
978 						    GTK_MESSAGE_INFO, "fkdeclare",
979 						    _("Successfully declared foreign key"));
980 		else
981 			browser_show_message ((GtkWindow *) parent, "%s",
982 					      _("Successfully declared foreign key"));
983 	}
984 
985 	gtk_widget_destroy (dlg);
986 }
987 
988 static GtkActionEntry ui_actions[] = {
989 	{ "Table", NULL, N_("_Table"), NULL, N_("Table"), NULL },
990 	{ "AddToFav", STOCK_ADD_BOOKMARK, N_("Add to _Favorites"), NULL, N_("Add table to favorites"),
991 	  G_CALLBACK (action_add_to_fav_cb)},
992 	{ "ViewContents", GTK_STOCK_EDIT, N_("_Contents"), NULL, N_("View table's contents"),
993 	  G_CALLBACK (action_view_contents_cb)},
994 	{ "InsertData", GTK_STOCK_ADD, N_("_Insert Data"), NULL, N_("Insert data into table"),
995 	  G_CALLBACK (action_insert_cb)},
996 	{ "KfDeclare", NULL, N_("_Declare Foreign Key"), NULL, N_("Declare a foreign key for table"),
997 	  G_CALLBACK (action_declarefk_cb)},
998 };
999 static const gchar *ui_actions_info =
1000 	"<ui>"
1001 	"  <menubar name='MenuBar'>"
1002 	"    <placeholder name='MenuExtension'>"
1003         "      <menu name='Table' action='Table'>"
1004         "        <menuitem name='AddToFav' action= 'AddToFav'/>"
1005         "        <menuitem name='InsertData' action= 'InsertData'/>"
1006         "        <menuitem name='ViewContents' action= 'ViewContents'/>"
1007         "        <separator/>"
1008         "        <menuitem name='KfDeclare' action= 'KfDeclare'/>"
1009         "      </menu>"
1010         "    </placeholder>"
1011 	"  </menubar>"
1012 	"  <toolbar name='ToolBar'>"
1013 	"    <separator/>"
1014 	"    <toolitem action='AddToFav'/>"
1015 	"    <toolitem action='ViewContents'/>"
1016 	"    <toolitem action='InsertData'/>"
1017 	"  </toolbar>"
1018 	"</ui>";
1019 
1020 static GtkActionGroup *
table_info_page_get_actions_group(BrowserPage * page)1021 table_info_page_get_actions_group (BrowserPage *page)
1022 {
1023 	GtkActionGroup *agroup;
1024 	agroup = gtk_action_group_new ("SchemaBrowserTableInfoActions");
1025 	gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
1026 	gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page);
1027 
1028 	return agroup;
1029 }
1030 
1031 static const gchar *
table_info_page_get_actions_ui(G_GNUC_UNUSED BrowserPage * page)1032 table_info_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
1033 {
1034 	return ui_actions_info;
1035 }
1036 
1037 static GtkWidget *
table_info_page_get_tab_label(BrowserPage * page,GtkWidget ** out_close_button)1038 table_info_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
1039 {
1040 	TableInfo *tinfo;
1041 	const gchar *tab_name;
1042 	GdkPixbuf *table_pixbuf;
1043 
1044 	tinfo = TABLE_INFO (page);
1045 	table_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
1046 	tab_name = tinfo->priv->table_short_name ? tinfo->priv->table_short_name : tinfo->priv->table_name;
1047 	return browser_make_tab_label_with_pixbuf (tab_name,
1048 						   table_pixbuf,
1049 						   out_close_button ? TRUE : FALSE, out_close_button);
1050 }
1051