/* * Copyright (C) 2009 - 2012 Vivien Malerba * Copyright (C) 2010 David King * Copyright (C) 2011 Murray Cumming * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "relations-diagram.h" #include "../support.h" #include "../gdaui-bar.h" #include "../canvas/browser-canvas-db-relations.h" #include #include #include "../browser-page.h" #include "../browser-perspective.h" #include "../browser-window.h" #include "../data-manager/data-manager-perspective.h" #include "../../tool-utils.h" struct _RelationsDiagramPrivate { BrowserConnection *bcnc; gint fav_id; /* diagram's ID as a favorite, -1=>not a favorite */ GdauiBar *header; GtkWidget *canvas; GtkWidget *save_button; GtkWidget *popup_container; /* to enter canvas's name */ GtkWidget *name_entry; GtkWidget *real_save_button; }; static void relations_diagram_class_init (RelationsDiagramClass *klass); static void relations_diagram_init (RelationsDiagram *diagram, RelationsDiagramClass *klass); static void relations_diagram_dispose (GObject *object); static void relations_diagram_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void relations_diagram_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); /* BrowserPage interface */ static void relations_diagram_page_init (BrowserPageIface *iface); static GtkActionGroup *relations_diagram_page_get_actions_group (BrowserPage *page); static const gchar *relations_diagram_page_get_actions_ui (BrowserPage *page); static GtkWidget *relations_diagram_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button); static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram); static void favorites_changed_cb (BrowserConnection *bcnc, RelationsDiagram *diagram); static void relations_diagram_set_fav_id (RelationsDiagram *diagram, gint fav_id, GError **error); /* properties */ enum { PROP_0, }; static GObjectClass *parent_class = NULL; /* * RelationsDiagram class implementation */ static void relations_diagram_class_init (RelationsDiagramClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); /* Properties */ object_class->set_property = relations_diagram_set_property; object_class->get_property = relations_diagram_get_property; object_class->dispose = relations_diagram_dispose; } static void relations_diagram_page_init (BrowserPageIface *iface) { iface->i_get_actions_group = relations_diagram_page_get_actions_group; iface->i_get_actions_ui = relations_diagram_page_get_actions_ui; iface->i_get_tab_label = relations_diagram_page_get_tab_label; } static void relations_diagram_init (RelationsDiagram *diagram, G_GNUC_UNUSED RelationsDiagramClass *klass) { diagram->priv = g_new0 (RelationsDiagramPrivate, 1); diagram->priv->fav_id = -1; diagram->priv->popup_container = NULL; gtk_orientable_set_orientation (GTK_ORIENTABLE (diagram), GTK_ORIENTATION_VERTICAL); } static void relations_diagram_dispose (GObject *object) { RelationsDiagram *diagram = (RelationsDiagram *) object; /* free memory */ if (diagram->priv) { if (diagram->priv->bcnc) { g_signal_handlers_disconnect_by_func (diagram->priv->bcnc, G_CALLBACK (meta_changed_cb), diagram); g_signal_handlers_disconnect_by_func (diagram->priv->bcnc, G_CALLBACK (favorites_changed_cb), diagram); g_object_unref (diagram->priv->bcnc); } if (diagram->priv->popup_container) gtk_widget_destroy (diagram->priv->popup_container); g_free (diagram->priv); diagram->priv = NULL; } parent_class->dispose (object); } GType relations_diagram_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GTypeInfo info = { sizeof (RelationsDiagramClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) relations_diagram_class_init, NULL, NULL, sizeof (RelationsDiagram), 0, (GInstanceInitFunc) relations_diagram_init, 0 }; static GInterfaceInfo page_info = { (GInterfaceInitFunc) relations_diagram_page_init, NULL, NULL }; type = g_type_register_static (GTK_TYPE_BOX, "RelationsDiagram", &info, 0); g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info); } return type; } static void relations_diagram_set_property (GObject *object, guint param_id, G_GNUC_UNUSED const GValue *value, GParamSpec *pspec) { /*RelationsDiagram *diagram; diagram = RELATIONS_DIAGRAM (object);*/ switch (param_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void relations_diagram_get_property (GObject *object, guint param_id, G_GNUC_UNUSED GValue *value, GParamSpec *pspec) { /*RelationsDiagram *diagram; diagram = RELATIONS_DIAGRAM (object);*/ switch (param_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct, RelationsDiagram *diagram) { g_object_set (G_OBJECT (diagram->priv->canvas), "meta-struct", mstruct, NULL); } static void favorites_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, RelationsDiagram *diagram) { if (diagram->priv->fav_id >= 0) relations_diagram_set_fav_id (diagram, diagram->priv->fav_id, NULL); } /* * POPUP */ static void real_save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram) { gchar *str; str = browser_canvas_serialize_items (BROWSER_CANVAS (diagram->priv->canvas)); GError *lerror = NULL; ToolsFavorites *bfav; ToolsFavoritesAttributes fav; memset (&fav, 0, sizeof (ToolsFavoritesAttributes)); fav.id = diagram->priv->fav_id; fav.type = GDA_TOOLS_FAVORITES_DIAGRAMS; fav.name = gtk_editable_get_chars (GTK_EDITABLE (diagram->priv->name_entry), 0, -1); if (!*fav.name) { g_free (fav.name); fav.name = g_strdup (_("Diagram")); } fav.contents = str; gtk_widget_hide (diagram->priv->popup_container); bfav = browser_connection_get_favorites (diagram->priv->bcnc); if (! gda_tools_favorites_add (bfav, 0, &fav, ORDER_KEY_SCHEMA, G_MAXINT, &lerror)) { browser_show_error ((GtkWindow*) gtk_widget_get_toplevel (button), "%s:\n%s", _("Could not save diagram"), lerror && lerror->message ? lerror->message : _("No detail")); if (lerror) g_error_free (lerror); } relations_diagram_set_fav_id (diagram, fav.id, NULL); g_free (fav.name); g_free (str); } static void save_clicked_cb (GtkWidget *button, RelationsDiagram *diagram) { gchar *str; if (!diagram->priv->popup_container) { GtkWidget *window, *wid, *hbox; window = popup_container_new (button); diagram->priv->popup_container = window; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_add (GTK_CONTAINER (window), hbox); wid = gtk_label_new (""); str = g_strdup_printf ("%s:", _("Canvas's name")); gtk_label_set_markup (GTK_LABEL (wid), str); g_free (str); gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0); wid = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 5); diagram->priv->name_entry = wid; if (diagram->priv->fav_id > 0) { ToolsFavoritesAttributes fav; if (gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), diagram->priv->fav_id, &fav, NULL)) { gtk_entry_set_text (GTK_ENTRY (wid), fav.name); gda_tools_favorites_reset_attributes (&fav); } } g_signal_connect (wid, "activate", G_CALLBACK (real_save_clicked_cb), diagram); wid = gtk_button_new_with_label (_("Save")); gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0); g_signal_connect (wid, "clicked", G_CALLBACK (real_save_clicked_cb), diagram); diagram->priv->real_save_button = wid; gtk_widget_show_all (hbox); } gtk_widget_show (diagram->priv->popup_container); } /** * relations_diagram_new * * Returns: a new #GtkWidget */ GtkWidget * relations_diagram_new (BrowserConnection *bcnc) { RelationsDiagram *diagram; g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL); diagram = RELATIONS_DIAGRAM (g_object_new (RELATIONS_DIAGRAM_TYPE, NULL)); diagram->priv->bcnc = g_object_ref (bcnc); g_signal_connect (diagram->priv->bcnc, "meta-changed", G_CALLBACK (meta_changed_cb), diagram); g_signal_connect (bcnc, "favorites-changed", G_CALLBACK (favorites_changed_cb), diagram); /* header */ GtkWidget *hbox, *wid; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (diagram), hbox, FALSE, FALSE, 0); GtkWidget *label; gchar *str; str = g_strdup_printf ("%s\n%s", _("Relations diagram"), _("Unsaved")); label = gdaui_bar_new (str); g_free (str); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); diagram->priv->header = GDAUI_BAR (label); wid = gdaui_bar_add_button_from_stock (GDAUI_BAR (label), GTK_STOCK_SAVE); diagram->priv->save_button = wid; g_signal_connect (wid, "clicked", G_CALLBACK (save_clicked_cb), diagram); gtk_widget_show_all (hbox); /* main contents */ wid = browser_canvas_db_relations_new (NULL); diagram->priv->canvas = wid; gtk_box_pack_start (GTK_BOX (diagram), wid, TRUE, TRUE, 0); gtk_widget_show_all (wid); GdaMetaStruct *mstruct; mstruct = browser_connection_get_meta_struct (diagram->priv->bcnc); if (mstruct) meta_changed_cb (diagram->priv->bcnc, mstruct, diagram); return (GtkWidget*) diagram; } GtkWidget * relations_diagram_new_with_fav_id (BrowserConnection *bcnc, gint fav_id, GError **error) { RelationsDiagram *diagram = NULL; ToolsFavoritesAttributes fav; xmlDocPtr doc = NULL; if (! gda_tools_favorites_get (browser_connection_get_favorites (bcnc), fav_id, &fav, error)) return FALSE; doc = xmlParseDoc (BAD_CAST fav.contents); if (!doc) { g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_INTERNAL_COMMAND_ERROR, "%s", _("Error parsing favorite's contents")); goto out; } /* create diagram */ diagram = RELATIONS_DIAGRAM (relations_diagram_new (bcnc)); if (!diagram) goto out; gchar *str, *tmp; tmp = g_markup_printf_escaped (_("'%s' diagram"), fav.name); str = g_strdup_printf ("%s\n%s", _("Relations diagram"), tmp); g_free (tmp); gdaui_bar_set_text (diagram->priv->header, str); g_free (str); diagram->priv->fav_id = fav_id; relations_diagram_set_fav_id (diagram, fav_id, NULL); /* fill the diagram */ xmlNodePtr root, node; root = xmlDocGetRootElement (doc); if (!root) goto out; for (node = root->children; node; node = node->next) { if (!strcmp ((gchar*) node->name, "table")) { xmlChar *schema; xmlChar *name; schema = xmlGetProp (node, BAD_CAST "schema"); name = xmlGetProp (node, BAD_CAST "name"); if (schema && name) { BrowserCanvasTable *table; GValue *v1, *v2; g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), (gchar*) schema); g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), (gchar*) name); xmlFree (schema); xmlFree (name); table = browser_canvas_db_relations_add_table (BROWSER_CANVAS_DB_RELATIONS (diagram->priv->canvas), NULL, v1, v2); gda_value_free (v1); gda_value_free (v2); if (table) { xmlChar *x, *y; x = xmlGetProp (node, BAD_CAST "x"); y = xmlGetProp (node, BAD_CAST "y"); browser_canvas_translate_item (BROWSER_CANVAS (diagram->priv->canvas), (BrowserCanvasItem*) table, x ? g_ascii_strtod ((gchar*) x, NULL) : 0., y ? g_ascii_strtod ((gchar*) y, NULL) : 0.); if (x) xmlFree (x); if (y) xmlFree (y); } } else { if (schema) xmlFree (schema); if (name) xmlFree (name); g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR, "%s", _("Missing table attribute in favorite's contents")); gtk_widget_destroy ((GtkWidget*) diagram); diagram = NULL; goto out; } } } out: gda_tools_favorites_reset_attributes (&fav); if (doc) xmlFreeDoc (doc); return (GtkWidget*) diagram; } /* * relations_diagram_set_fav_id * * Sets the favorite ID of @diagram: ensure every displayed information is up to date */ static void relations_diagram_set_fav_id (RelationsDiagram *diagram, gint fav_id, GError **error) { g_return_if_fail (IS_RELATIONS_DIAGRAM (diagram)); ToolsFavoritesAttributes fav; if ((fav_id >=0) && gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), fav_id, &fav, error)) { gchar *str, *tmp; tmp = g_markup_printf_escaped (_("'%s' diagram"), fav.name); str = g_strdup_printf ("%s\n%s", _("Relations diagram"), tmp); g_free (tmp); gdaui_bar_set_text (diagram->priv->header, str); g_free (str); diagram->priv->fav_id = fav.id; gda_tools_favorites_reset_attributes (&fav); } else { gchar *str; str = g_strdup_printf ("%s\n%s", _("Relations diagram"), _("Unsaved")); gdaui_bar_set_text (diagram->priv->header, str); g_free (str); diagram->priv->fav_id = -1; } /* update notebook's tab label */ BrowserPerspective *pers; pers = browser_page_get_perspective (BROWSER_PAGE (diagram)); if (pers) browser_perspective_page_tab_label_change (pers, BROWSER_PAGE (diagram)); } /** * relations_diagram_get_fav_id * */ gint relations_diagram_get_fav_id (RelationsDiagram *diagram) { g_return_val_if_fail (IS_RELATIONS_DIAGRAM (diagram), -1); return diagram->priv->fav_id; } static void action_view_contents_cb (G_GNUC_UNUSED GtkAction *action, RelationsDiagram *diagram) { gchar *str; str = browser_canvas_db_relations_items_to_data_manager (BROWSER_CANVAS_DB_RELATIONS (diagram->priv->canvas)); g_print ("%s\n", str); if (str) { BrowserWindow *bwin; BrowserPerspective *pers; bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) diagram); pers = browser_window_change_perspective (bwin, _("Data manager")); data_manager_perspective_new_tab (DATA_MANAGER_PERSPECTIVE (pers), str); g_free (str); } } static GtkActionEntry ui_actions[] = { { "ViewContents", GTK_STOCK_EDIT, N_("_Contents"), NULL, N_("View contents"), G_CALLBACK (action_view_contents_cb)}, }; static const gchar *ui_actions_info = "" " " " " " " " " " " " " ""; static GtkActionGroup * relations_diagram_page_get_actions_group (BrowserPage *page) { GtkActionGroup *agroup; agroup = gtk_action_group_new ("SchemaBrowserRelationsDiagramActions"); gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE); gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page); return agroup; } static const gchar * relations_diagram_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page) { return ui_actions_info; } static GtkWidget * relations_diagram_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button) { GtkWidget *wid; RelationsDiagram *diagram; gchar *tab_name = NULL; GdkPixbuf *table_pixbuf; diagram = RELATIONS_DIAGRAM (page); if (diagram->priv->fav_id > 0) { ToolsFavoritesAttributes fav; if (gda_tools_favorites_get (browser_connection_get_favorites (diagram->priv->bcnc), diagram->priv->fav_id, &fav, NULL)) { tab_name = g_strdup (fav.name); gda_tools_favorites_reset_attributes (&fav); } } if (!tab_name) tab_name = g_strdup (_("Diagram")); table_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_DIAGRAM); wid = browser_make_tab_label_with_pixbuf (tab_name, table_pixbuf, out_close_button ? TRUE : FALSE, out_close_button); g_free (tab_name); return wid; }