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, ¶ms, 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