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 RC2_ofb64_encrypt(const unsigned char * in,unsigned char * out,long length,RC2_KEY * schedule,unsigned char * ivec,int * num)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 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 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 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 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 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 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 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 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 * 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 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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 * 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 * 1032 table_info_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page) 1033 { 1034 return ui_actions_info; 1035 } 1036 1037 static GtkWidget * 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