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 "relations-diagram.h" 24 #include "../support.h" 25 #include "../gdaui-bar.h" 26 #include "../canvas/browser-canvas-db-relations.h" 27 #include <gdk/gdkkeysyms.h> 28 #include <libgda-ui/internal/popup-container.h> 29 #include "../browser-page.h" 30 #include "../browser-perspective.h" 31 #include "../browser-window.h" 32 #include "../data-manager/data-manager-perspective.h" 33 #include "../../tool-utils.h" 34 35 struct _RelationsDiagramPrivate { 36 BrowserConnection *bcnc; 37 gint fav_id; /* diagram's ID as a favorite, -1=>not a favorite */ 38 39 GdauiBar *header; 40 GtkWidget *canvas; 41 GtkWidget *save_button; 42 43 GtkWidget *popup_container; /* to enter canvas's name */ 44 GtkWidget *name_entry; 45 GtkWidget *real_save_button; 46 }; 47 48 static void relations_diagram_class_init (RelationsDiagramClass *klass); 49 static void relations_diagram_init (RelationsDiagram *diagram, RelationsDiagramClass *klass); 50 static void relations_diagram_dispose (GObject *object); 51 static void relations_diagram_set_property (GObject *object, 52 guint param_id, 53 const GValue *value, 54 GParamSpec *pspec); 55 static void relations_diagram_get_property (GObject *object, 56 guint param_id, 57 GValue *value, 58 GParamSpec *pspec); 59 60 /* BrowserPage interface */ 61 static void relations_diagram_page_init (BrowserPageIface *iface); 62 static GtkActionGroup *relations_diagram_page_get_actions_group (BrowserPage *page); 63 static const gchar *relations_diagram_page_get_actions_ui (BrowserPage *page); 64 static GtkWidget *relations_diagram_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button); 65 66 static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram); 67 static void favorites_changed_cb (BrowserConnection *bcnc, RelationsDiagram *diagram); 68 static void relations_diagram_set_fav_id (RelationsDiagram *diagram, gint fav_id, GError **error); 69 70 /* properties */ 71 enum { 72 PROP_0, 73 }; 74 75 static GObjectClass *parent_class = NULL; 76 77 78 /* 79 * RelationsDiagram class implementation 80 */ 81 82 static void 83 relations_diagram_class_init (RelationsDiagramClass *klass) 84 { 85 GObjectClass *object_class = G_OBJECT_CLASS (klass); 86 87 parent_class = g_type_class_peek_parent (klass); 88 89 /* Properties */ 90 object_class->set_property = relations_diagram_set_property; 91 object_class->get_property = relations_diagram_get_property; 92 93 object_class->dispose = relations_diagram_dispose; 94 } 95 96 static void 97 relations_diagram_page_init (BrowserPageIface *iface) 98 { 99 iface->i_get_actions_group = relations_diagram_page_get_actions_group; 100 iface->i_get_actions_ui = relations_diagram_page_get_actions_ui; 101 iface->i_get_tab_label = relations_diagram_page_get_tab_label; 102 } 103 104 static void 105 relations_diagram_init (RelationsDiagram *diagram, G_GNUC_UNUSED RelationsDiagramClass *klass) 106 { 107 diagram->priv = g_new0 (RelationsDiagramPrivate, 1); 108 diagram->priv->fav_id = -1; 109 diagram->priv->popup_container = NULL; 110 111 gtk_orientable_set_orientation (GTK_ORIENTABLE (diagram), GTK_ORIENTATION_VERTICAL); 112 } 113 114 static void 115 relations_diagram_dispose (GObject *object) 116 { 117 RelationsDiagram *diagram = (RelationsDiagram *) object; 118 119 /* free memory */ 120 if (diagram->priv) { 121 if (diagram->priv->bcnc) { 122 g_signal_handlers_disconnect_by_func (diagram->priv->bcnc, 123 G_CALLBACK (meta_changed_cb), diagram); 124 g_signal_handlers_disconnect_by_func (diagram->priv->bcnc, 125 G_CALLBACK (favorites_changed_cb), diagram); 126 g_object_unref (diagram->priv->bcnc); 127 } 128 129 if (diagram->priv->popup_container) 130 gtk_widget_destroy (diagram->priv->popup_container); 131 132 g_free (diagram->priv); 133 diagram->priv = NULL; 134 } 135 136 parent_class->dispose (object); 137 } 138 139 GType 140 relations_diagram_get_type (void) 141 { 142 static GType type = 0; 143 144 if (G_UNLIKELY (type == 0)) { 145 static const GTypeInfo info = { 146 sizeof (RelationsDiagramClass), 147 (GBaseInitFunc) NULL, 148 (GBaseFinalizeFunc) NULL, 149 (GClassInitFunc) relations_diagram_class_init, 150 NULL, 151 NULL, 152 sizeof (RelationsDiagram), 153 0, 154 (GInstanceInitFunc) relations_diagram_init, 155 0 156 }; 157 static GInterfaceInfo page_info = { 158 (GInterfaceInitFunc) relations_diagram_page_init, 159 NULL, 160 NULL 161 }; 162 163 type = g_type_register_static (GTK_TYPE_BOX, "RelationsDiagram", &info, 0); 164 g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info); 165 } 166 return type; 167 } 168 169 static void 170 relations_diagram_set_property (GObject *object, 171 guint param_id, 172 G_GNUC_UNUSED const GValue *value, 173 GParamSpec *pspec) 174 { 175 /*RelationsDiagram *diagram; 176 diagram = RELATIONS_DIAGRAM (object);*/ 177 switch (param_id) { 178 default: 179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 180 break; 181 } 182 } 183 184 static void 185 relations_diagram_get_property (GObject *object, 186 guint param_id, 187 G_GNUC_UNUSED GValue *value, 188 GParamSpec *pspec) 189 { 190 /*RelationsDiagram *diagram; 191 diagram = RELATIONS_DIAGRAM (object);*/ 192 switch (param_id) { 193 default: 194 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 195 break; 196 } 197 } 198 199 static void 200 meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram) 201 { 202 g_object_set (G_OBJECT (diagram->priv->canvas), "meta-struct", mstruct, NULL); 203 } 204 205 static void 206 favorites_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, RelationsDiagram *diagram) 207 { 208 if (diagram->priv->fav_id >= 0) 209 relations_diagram_set_fav_id (diagram, diagram->priv->fav_id, NULL); 210 } 211 212 /* 213 * POPUP 214 */ 215 static void 216 real_save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram) 217 { 218 gchar *str; 219 220 str = browser_canvas_serialize_items (BROWSER_CANVAS (diagram->priv->canvas)); 221 222 GError *lerror = NULL; 223 ToolsFavorites *bfav; 224 ToolsFavoritesAttributes fav; 225 226 memset (&fav, 0, sizeof (ToolsFavoritesAttributes)); 227 fav.id = diagram->priv->fav_id; 228 fav.type = GDA_TOOLS_FAVORITES_DIAGRAMS; 229 fav.name = gtk_editable_get_chars (GTK_EDITABLE (diagram->priv->name_entry), 0, -1); 230 if (!*fav.name) { 231 g_free (fav.name); 232 fav.name = g_strdup (_("Diagram")); 233 } 234 fav.contents = str; 235 236 gtk_widget_hide (diagram->priv->popup_container); 237 238 bfav = browser_connection_get_favorites (diagram->priv->bcnc); 239 if (! gda_tools_favorites_add (bfav, 0, &fav, ORDER_KEY_SCHEMA, G_MAXINT, &lerror)) { 240 browser_show_error ((GtkWindow*) gtk_widget_get_toplevel (button), 241 "<b>%s:</b>\n%s", 242 _("Could not save diagram"), 243 lerror && lerror->message ? lerror->message : _("No detail")); 244 if (lerror) 245 g_error_free (lerror); 246 } 247 248 relations_diagram_set_fav_id (diagram, fav.id, NULL); 249 250 g_free (fav.name); 251 g_free (str); 252 } 253 254 static void 255 save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram) 256 { 257 gchar *str; 258 259 if (!diagram->priv->popup_container) { 260 GtkWidget *window, *wid, *hbox; 261 262 window = popup_container_new (button); 263 diagram->priv->popup_container = window; 264 265 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 266 gtk_container_add (GTK_CONTAINER (window), hbox); 267 wid = gtk_label_new (""); 268 str = g_strdup_printf ("%s:", _("Canvas's name")); 269 gtk_label_set_markup (GTK_LABEL (wid), str); 270 g_free (str); 271 gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0); 272 273 wid = gtk_entry_new (); 274 gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5); 275 diagram->priv->name_entry = wid; 276 if (diagram->priv->fav_id > 0) { 277 ToolsFavoritesAttributes fav; 278 if (gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), 279 diagram->priv->fav_id, &fav, NULL)) { 280 gtk_entry_set_text (GTK_ENTRY (wid), fav.name); 281 gda_tools_favorites_reset_attributes (&fav); 282 } 283 } 284 285 g_signal_connect (wid, "activate", 286 G_CALLBACK (real_save_clicked_cb), diagram); 287 288 wid = gtk_button_new_with_label (_("Save")); 289 gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0); 290 g_signal_connect (wid, "clicked", 291 G_CALLBACK (real_save_clicked_cb), diagram); 292 diagram->priv->real_save_button = wid; 293 294 gtk_widget_show_all (hbox); 295 } 296 297 gtk_widget_show (diagram->priv->popup_container); 298 } 299 300 301 /** 302 * relations_diagram_new 303 * 304 * Returns: a new #GtkWidget 305 */ 306 GtkWidget * 307 relations_diagram_new (BrowserConnection *bcnc) 308 { 309 RelationsDiagram *diagram; 310 311 g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL); 312 313 diagram = RELATIONS_DIAGRAM (g_object_new (RELATIONS_DIAGRAM_TYPE, NULL)); 314 315 diagram->priv->bcnc = g_object_ref (bcnc); 316 g_signal_connect (diagram->priv->bcnc, "meta-changed", 317 G_CALLBACK (meta_changed_cb), diagram); 318 g_signal_connect (bcnc, "favorites-changed", 319 G_CALLBACK (favorites_changed_cb), diagram); 320 321 322 /* header */ 323 GtkWidget *hbox, *wid; 324 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 325 gtk_box_pack_start (GTK_BOX (diagram), hbox, FALSE, FALSE, 0); 326 327 GtkWidget *label; 328 gchar *str; 329 str = g_strdup_printf ("<b>%s</b>\n%s", _("Relations diagram"), _("Unsaved")); 330 label = gdaui_bar_new (str); 331 g_free (str); 332 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); 333 diagram->priv->header = GDAUI_BAR (label); 334 335 wid = gdaui_bar_add_button_from_stock (GDAUI_BAR (label), GTK_STOCK_SAVE); 336 diagram->priv->save_button = wid; 337 338 g_signal_connect (wid, "clicked", 339 G_CALLBACK (save_clicked_cb), diagram); 340 341 gtk_widget_show_all (hbox); 342 343 /* main contents */ 344 wid = browser_canvas_db_relations_new (NULL); 345 diagram->priv->canvas = wid; 346 gtk_box_pack_start (GTK_BOX (diagram), wid, TRUE, TRUE, 0); 347 gtk_widget_show_all (wid); 348 349 GdaMetaStruct *mstruct; 350 mstruct = browser_connection_get_meta_struct (diagram->priv->bcnc); 351 if (mstruct) 352 meta_changed_cb (diagram->priv->bcnc, mstruct, diagram); 353 354 return (GtkWidget*) diagram; 355 } 356 357 GtkWidget * 358 relations_diagram_new_with_fav_id (BrowserConnection *bcnc, gint fav_id, GError **error) 359 { 360 RelationsDiagram *diagram = NULL; 361 ToolsFavoritesAttributes fav; 362 xmlDocPtr doc = NULL; 363 364 if (! gda_tools_favorites_get (browser_connection_get_favorites (bcnc), 365 fav_id, &fav, error)) 366 return FALSE; 367 368 369 doc = xmlParseDoc (BAD_CAST fav.contents); 370 if (!doc) { 371 g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_INTERNAL_COMMAND_ERROR, 372 "%s", _("Error parsing favorite's contents")); 373 goto out; 374 } 375 376 /* create diagram */ 377 diagram = RELATIONS_DIAGRAM (relations_diagram_new (bcnc)); 378 if (!diagram) 379 goto out; 380 gchar *str, *tmp; 381 tmp = g_markup_printf_escaped (_("'%s' diagram"), fav.name); 382 str = g_strdup_printf ("<b>%s</b>\n%s", _("Relations diagram"), tmp); 383 g_free (tmp); 384 gdaui_bar_set_text (diagram->priv->header, str); 385 g_free (str); 386 diagram->priv->fav_id = fav_id; 387 relations_diagram_set_fav_id (diagram, fav_id, NULL); 388 389 /* fill the diagram */ 390 xmlNodePtr root, node; 391 root = xmlDocGetRootElement (doc); 392 if (!root) 393 goto out; 394 for (node = root->children; node; node = node->next) { 395 if (!strcmp ((gchar*) node->name, "table")) { 396 xmlChar *schema; 397 xmlChar *name; 398 schema = xmlGetProp (node, BAD_CAST "schema"); 399 name = xmlGetProp (node, BAD_CAST "name"); 400 if (schema && name) { 401 BrowserCanvasTable *table; 402 GValue *v1, *v2; 403 g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), (gchar*) schema); 404 g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), (gchar*) name); 405 xmlFree (schema); 406 xmlFree (name); 407 table = browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (diagram->priv->canvas), 408 NULL, v1, v2); 409 gda_value_free (v1); 410 gda_value_free (v2); 411 if (table) { 412 xmlChar *x, *y; 413 x = xmlGetProp (node, BAD_CAST "x"); 414 y = xmlGetProp (node, BAD_CAST "y"); 415 browser_canvas_translate_item (BROWSER_CANVAS (diagram->priv->canvas), 416 (BrowserCanvasItem*) table, 417 x ? g_ascii_strtod ((gchar*) x, NULL) : 0., 418 y ? g_ascii_strtod ((gchar*) y, NULL) : 0.); 419 if (x) 420 xmlFree (x); 421 if (y) 422 xmlFree (y); 423 } 424 } 425 else { 426 if (schema) 427 xmlFree (schema); 428 if (name) 429 xmlFree (name); 430 g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR, 431 "%s", _("Missing table attribute in favorite's contents")); 432 gtk_widget_destroy ((GtkWidget*) diagram); 433 diagram = NULL; 434 goto out; 435 } 436 } 437 } 438 439 out: 440 gda_tools_favorites_reset_attributes (&fav); 441 if (doc) 442 xmlFreeDoc (doc); 443 return (GtkWidget*) diagram; 444 } 445 446 /* 447 * relations_diagram_set_fav_id 448 * 449 * Sets the favorite ID of @diagram: ensure every displayed information is up to date 450 */ 451 static void 452 relations_diagram_set_fav_id (RelationsDiagram *diagram, gint fav_id, GError **error) 453 { 454 g_return_if_fail (IS_RELATIONS_DIAGRAM (diagram)); 455 ToolsFavoritesAttributes fav; 456 457 if ((fav_id >=0) && 458 gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), 459 fav_id, &fav, error)) { 460 gchar *str, *tmp; 461 tmp = g_markup_printf_escaped (_("'%s' diagram"), fav.name); 462 str = g_strdup_printf ("<b>%s</b>\n%s", _("Relations diagram"), tmp); 463 g_free (tmp); 464 gdaui_bar_set_text (diagram->priv->header, str); 465 g_free (str); 466 467 diagram->priv->fav_id = fav.id; 468 469 gda_tools_favorites_reset_attributes (&fav); 470 } 471 else { 472 gchar *str; 473 str = g_strdup_printf ("<b>%s</b>\n%s", _("Relations diagram"), _("Unsaved")); 474 gdaui_bar_set_text (diagram->priv->header, str); 475 g_free (str); 476 diagram->priv->fav_id = -1; 477 } 478 479 /* update notebook's tab label */ 480 BrowserPerspective *pers; 481 pers = browser_page_get_perspective (BROWSER_PAGE (diagram)); 482 if (pers) 483 browser_perspective_page_tab_label_change (pers, BROWSER_PAGE (diagram)); 484 } 485 486 /** 487 * relations_diagram_get_fav_id 488 * 489 */ 490 gint 491 relations_diagram_get_fav_id (RelationsDiagram *diagram) 492 { 493 g_return_val_if_fail (IS_RELATIONS_DIAGRAM (diagram), -1); 494 return diagram->priv->fav_id; 495 } 496 497 static void 498 action_view_contents_cb (G_GNUC_UNUSED GtkAction *action, RelationsDiagram *diagram) 499 { 500 gchar *str; 501 str = browser_canvas_db_relations_items_to_data_manager (BROWSER_CANVAS_DB_RELATIONS (diagram->priv->canvas)); 502 g_print ("%s\n", str); 503 504 if (str) { 505 BrowserWindow *bwin; 506 BrowserPerspective *pers; 507 bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) diagram); 508 pers = browser_window_change_perspective (bwin, _("Data manager")); 509 510 data_manager_perspective_new_tab (DATA_MANAGER_PERSPECTIVE (pers), str); 511 g_free (str); 512 } 513 } 514 515 516 static GtkActionEntry ui_actions[] = { 517 { "ViewContents", GTK_STOCK_EDIT, N_("_Contents"), NULL, N_("View contents"), 518 G_CALLBACK (action_view_contents_cb)}, 519 }; 520 static const gchar *ui_actions_info = 521 "<ui>" 522 " <menubar name='MenuBar'>" 523 " </menubar>" 524 " <toolbar name='ToolBar'>" 525 " <separator/>" 526 " <toolitem action='ViewContents'/>" 527 " </toolbar>" 528 "</ui>"; 529 530 static GtkActionGroup * 531 relations_diagram_page_get_actions_group (BrowserPage *page) 532 { 533 GtkActionGroup *agroup; 534 agroup = gtk_action_group_new ("SchemaBrowserRelationsDiagramActions"); 535 gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE); 536 gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page); 537 538 return agroup; 539 } 540 541 static const gchar * 542 relations_diagram_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page) 543 { 544 return ui_actions_info; 545 } 546 547 548 static GtkWidget * 549 relations_diagram_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button) 550 { 551 GtkWidget *wid; 552 RelationsDiagram *diagram; 553 gchar *tab_name = NULL; 554 GdkPixbuf *table_pixbuf; 555 556 diagram = RELATIONS_DIAGRAM (page); 557 if (diagram->priv->fav_id > 0) { 558 ToolsFavoritesAttributes fav; 559 if (gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), 560 diagram->priv->fav_id, &fav, NULL)) { 561 tab_name = g_strdup (fav.name); 562 gda_tools_favorites_reset_attributes (&fav); 563 } 564 } 565 if (!tab_name) 566 tab_name = g_strdup (_("Diagram")); 567 568 table_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_DIAGRAM); 569 wid = browser_make_tab_label_with_pixbuf (tab_name, 570 table_pixbuf, 571 out_close_button ? TRUE : FALSE, out_close_button); 572 g_free (tab_name); 573 return wid; 574 } 575