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 <string.h>
22 #include <glib/gi18n-lib.h>
23 #include "schema-browser-perspective.h"
24 #include "favorite-selector.h"
25 #include "objects-index.h"
26 #include "../browser-window.h"
27 #include "table-info.h"
28 #include "../support.h"
29 #include "../browser-page.h"
30 #ifdef HAVE_GOOCANVAS
31 #include "relations-diagram.h"
32 #endif
33 #include <libgda/gda-debug-macros.h>
34
35 /*
36 * Main static functions
37 */
38 static void schema_browser_perspective_class_init (SchemaBrowserPerspectiveClass *klass);
39 static void schema_browser_perspective_init (SchemaBrowserPerspective *stmt);
40 static void schema_browser_perspective_dispose (GObject *object);
41
42 /* BrowserPerspective interface */
43 static void schema_browser_perspective_perspective_init (BrowserPerspectiveIface *iface);
44 static BrowserWindow *schema_browser_perspective_get_window (BrowserPerspective *perspective);
45 static GtkActionGroup *schema_browser_perspective_get_actions_group (BrowserPerspective *perspective);
46 static const gchar *schema_browser_perspective_get_actions_ui (BrowserPerspective *perspective);
47 static void schema_browser_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page);
48 static void schema_browser_perspective_get_current_customization (BrowserPerspective *perspective,
49 GtkActionGroup **out_agroup,
50 const gchar **out_ui);
51
52 /* get a pointer to the parents to be able to call their destructor */
53 static GObjectClass *parent_class = NULL;
54
55 struct _SchemaBrowserPerspectivePrivate {
56 GtkWidget *notebook;
57 GtkWidget *favorites;
58 gboolean favorites_shown;
59 BrowserWindow *bwin;
60 };
61
62 GType
schema_browser_perspective_get_type(void)63 schema_browser_perspective_get_type (void)
64 {
65 static GType type = 0;
66
67 if (G_UNLIKELY (type == 0)) {
68 static GMutex registering;
69 static const GTypeInfo info = {
70 sizeof (SchemaBrowserPerspectiveClass),
71 (GBaseInitFunc) NULL,
72 (GBaseFinalizeFunc) NULL,
73 (GClassInitFunc) schema_browser_perspective_class_init,
74 NULL,
75 NULL,
76 sizeof (SchemaBrowserPerspective),
77 0,
78 (GInstanceInitFunc) schema_browser_perspective_init,
79 0
80 };
81
82 static GInterfaceInfo perspective_info = {
83 (GInterfaceInitFunc) schema_browser_perspective_perspective_init,
84 NULL,
85 NULL
86 };
87
88 g_mutex_lock (®istering);
89 if (type == 0) {
90 type = g_type_register_static (GTK_TYPE_BOX, "SchemaBrowserPerspective", &info, 0);
91 g_type_add_interface_static (type, BROWSER_PERSPECTIVE_TYPE, &perspective_info);
92 }
93 g_mutex_unlock (®istering);
94 }
95 return type;
96 }
97
98 static void
schema_browser_perspective_class_init(SchemaBrowserPerspectiveClass * klass)99 schema_browser_perspective_class_init (SchemaBrowserPerspectiveClass * klass)
100 {
101 GObjectClass *object_class = G_OBJECT_CLASS (klass);
102 parent_class = g_type_class_peek_parent (klass);
103
104 object_class->dispose = schema_browser_perspective_dispose;
105 }
106
107 static void
schema_browser_perspective_perspective_init(BrowserPerspectiveIface * iface)108 schema_browser_perspective_perspective_init (BrowserPerspectiveIface *iface)
109 {
110 iface->i_get_window = schema_browser_perspective_get_window;
111 iface->i_get_actions_group = schema_browser_perspective_get_actions_group;
112 iface->i_get_actions_ui = schema_browser_perspective_get_actions_ui;
113 iface->i_page_tab_label_change = schema_browser_perspective_page_tab_label_change;
114 iface->i_get_current_customization = schema_browser_perspective_get_current_customization;
115 }
116
117
118 static void
schema_browser_perspective_init(SchemaBrowserPerspective * perspective)119 schema_browser_perspective_init (SchemaBrowserPerspective *perspective)
120 {
121 perspective->priv = g_new0 (SchemaBrowserPerspectivePrivate, 1);
122 perspective->priv->favorites_shown = TRUE;
123
124 gtk_orientable_set_orientation (GTK_ORIENTABLE (perspective), GTK_ORIENTATION_VERTICAL);
125 }
126
127 static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, ToolsFavoritesType fav_type,
128 const gchar *selection, SchemaBrowserPerspective *bpers);
129 static void objects_index_selection_changed_cb (GtkWidget *widget, ToolsFavoritesType fav_type,
130 const gchar *selection, SchemaBrowserPerspective *bpers);
131 /**
132 * schema_browser_perspective_new
133 *
134 * Creates new #BrowserPerspective widget which
135 */
136 BrowserPerspective *
schema_browser_perspective_new(BrowserWindow * bwin)137 schema_browser_perspective_new (BrowserWindow *bwin)
138 {
139 BrowserConnection *bcnc;
140 BrowserPerspective *bpers;
141 SchemaBrowserPerspective *perspective;
142 gboolean fav_supported;
143
144 bpers = (BrowserPerspective*) g_object_new (TYPE_SCHEMA_BROWSER_PERSPECTIVE, NULL);
145 perspective = (SchemaBrowserPerspective*) bpers;
146 bcnc = browser_window_get_connection (bwin);
147 fav_supported = browser_connection_get_favorites (bcnc) ? TRUE : FALSE;
148 perspective->priv->bwin = bwin;
149
150 /* contents */
151 GtkWidget *paned, *wid, *nb;
152 paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
153 if (fav_supported) {
154 wid = favorite_selector_new (bcnc);
155 g_signal_connect (wid, "selection-changed",
156 G_CALLBACK (fav_selection_changed_cb), bpers);
157 gtk_paned_add1 (GTK_PANED (paned), wid);
158 gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
159 perspective->priv->favorites = wid;
160 }
161
162 nb = gtk_notebook_new ();
163 perspective->priv->notebook = nb;
164 gtk_paned_add2 (GTK_PANED (paned), nb);
165 gtk_notebook_set_scrollable (GTK_NOTEBOOK (nb), TRUE);
166 gtk_notebook_popup_enable (GTK_NOTEBOOK (nb));
167
168 wid = objects_index_new (bcnc);
169 g_signal_connect (wid, "selection-changed",
170 G_CALLBACK (objects_index_selection_changed_cb), bpers);
171 gtk_notebook_append_page (GTK_NOTEBOOK (nb), wid,
172 browser_make_tab_label_with_stock (_("Index"), GTK_STOCK_ABOUT, FALSE,
173 NULL));
174 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (nb), wid, TRUE);
175 gtk_notebook_set_group_name (GTK_NOTEBOOK (nb), "schema-browser");
176
177 gtk_notebook_set_menu_label (GTK_NOTEBOOK (nb), wid,
178 browser_make_tab_label_with_stock (_("Index"), GTK_STOCK_ABOUT, FALSE,
179 NULL));
180 gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
181 gtk_widget_show_all (paned);
182
183 if (perspective->priv->favorites && !perspective->priv->favorites_shown)
184 gtk_widget_hide (perspective->priv->favorites);
185
186 browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
187
188 return bpers;
189 }
190
191 static void
close_button_clicked_cb(G_GNUC_UNUSED GtkWidget * wid,GtkWidget * page_widget)192 close_button_clicked_cb (G_GNUC_UNUSED GtkWidget *wid, GtkWidget *page_widget)
193 {
194 gtk_widget_destroy (page_widget);
195 }
196
197 static void
objects_index_selection_changed_cb(GtkWidget * widget,ToolsFavoritesType fav_type,const gchar * selection,SchemaBrowserPerspective * bpers)198 objects_index_selection_changed_cb (GtkWidget *widget, ToolsFavoritesType fav_type,
199 const gchar *selection, SchemaBrowserPerspective *bpers)
200 {
201 fav_selection_changed_cb (widget, -1, fav_type, selection, bpers);
202 }
203
204
205 static void
fav_selection_changed_cb(G_GNUC_UNUSED GtkWidget * widget,gint fav_id,ToolsFavoritesType fav_type,const gchar * selection,SchemaBrowserPerspective * bpers)206 fav_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gint fav_id, ToolsFavoritesType fav_type,
207 const gchar *selection, SchemaBrowserPerspective *bpers)
208 {
209 if (fav_type == GDA_TOOLS_FAVORITES_TABLES) {
210 GdaQuarkList *ql;
211 const gchar *type;
212 const gchar *schema = NULL, *table = NULL, *short_name = NULL;
213
214 ql = gda_quark_list_new_from_string (selection);
215 if (ql) {
216 type = gda_quark_list_find (ql, "OBJ_TYPE");
217 schema = gda_quark_list_find (ql, "OBJ_SCHEMA");
218 table = gda_quark_list_find (ql, "OBJ_NAME");
219 short_name = gda_quark_list_find (ql, "OBJ_SHORT_NAME");
220 }
221
222 if (!type || !schema || !table) {
223 if (ql)
224 gda_quark_list_free (ql);
225 return;
226 }
227
228 if (!strcmp (type, "table")) {
229 schema_browser_perspective_display_table_info (bpers, schema, table, short_name);
230 }
231 else {
232 gint ntabs, i;
233 ntabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (bpers->priv->notebook));
234 for (i = 0; i < ntabs; i++) {
235 GtkWidget *child;
236 child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
237 if (IS_TABLE_INFO (child)) {
238 if (!strcmp (schema, table_info_get_table_schema (TABLE_INFO (child))) &&
239 !strcmp (table, table_info_get_table_name (TABLE_INFO (child)))) {
240 gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
241 return;
242 }
243 }
244 }
245
246 g_warning ("Non handled favorite type: %s", type);
247 TO_IMPLEMENT;
248 }
249
250 if (ql)
251 gda_quark_list_free (ql);
252 }
253 else if (fav_type == GDA_TOOLS_FAVORITES_DIAGRAMS) {
254 #ifdef HAVE_GOOCANVAS
255 schema_browser_perspective_display_diagram (bpers, fav_id);
256 #else
257 g_warning ("Can't display diagram because canvas not compiled.");
258 #endif
259 }
260 #ifdef GDA_DEBUG_NO
261 g_print ("Reacted to selection fav_id=>%d type=>%u, contents=>%s\n", fav_id, fav_type, selection);
262 #endif
263 }
264
265 static void
schema_browser_perspective_dispose(GObject * object)266 schema_browser_perspective_dispose (GObject *object)
267 {
268 SchemaBrowserPerspective *perspective;
269
270 g_return_if_fail (object != NULL);
271 g_return_if_fail (IS_SCHEMA_BROWSER_PERSPECTIVE (object));
272
273 perspective = SCHEMA_BROWSER_PERSPECTIVE (object);
274 if (perspective->priv) {
275 browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
276 g_free (perspective->priv);
277 perspective->priv = NULL;
278 }
279
280 /* parent class */
281 parent_class->dispose (object);
282 }
283
284 #ifdef HAVE_GOOCANVAS
285 static void
action_create_diagram_cb(G_GNUC_UNUSED GtkAction * action,SchemaBrowserPerspective * bpers)286 action_create_diagram_cb (G_GNUC_UNUSED GtkAction *action, SchemaBrowserPerspective *bpers)
287 {
288 schema_browser_perspective_display_diagram (bpers, -1);
289 }
290 #endif
291
292 static void
favorites_toggle_cb(GtkToggleAction * action,BrowserPerspective * bpers)293 favorites_toggle_cb (GtkToggleAction *action, BrowserPerspective *bpers)
294 {
295 SchemaBrowserPerspective *perspective;
296 perspective = SCHEMA_BROWSER_PERSPECTIVE (bpers);
297 if (! perspective->priv->favorites)
298 return;
299
300 perspective->priv->favorites_shown = gtk_toggle_action_get_active (action);
301 if (perspective->priv->favorites_shown)
302 gtk_widget_show (perspective->priv->favorites);
303 else
304 gtk_widget_hide (perspective->priv->favorites);
305 }
306
307 static const GtkToggleActionEntry ui_toggle_actions [] =
308 {
309 { "SchemaToolsFavoritesShow", NULL, N_("_Show Favorites"), "F9", N_("Show or hide favorites"), G_CALLBACK (favorites_toggle_cb), FALSE }
310 };
311
312 static GtkActionEntry ui_actions[] = {
313 #ifdef HAVE_GOOCANVAS
314 { "Schema", NULL, N_("_Schema"), NULL, N_("Schema"), NULL },
315 { "NewDiagram", GTK_STOCK_ADD, N_("_New Diagram"), NULL, N_("Create a new diagram"),
316 G_CALLBACK (action_create_diagram_cb)},
317 #endif
318 };
319
320 static const gchar *ui_actions_info =
321 "<ui>"
322 " <menubar name='MenuBar'>"
323 " <menu name='Display' action='Display'>"
324 " <menuitem name='SchemaToolsFavoritesShow' action='SchemaToolsFavoritesShow'/>"
325 " </menu>"
326 #ifdef HAVE_GOOCANVAS
327 " <placeholder name='MenuExtension'>"
328 " <menu name='Schema' action='Schema'>"
329 " <menuitem name='NewDiagram' action= 'NewDiagram'/>"
330 " </menu>"
331 " </placeholder>"
332 #endif
333 " </menubar>"
334 "</ui>";
335
336 static GtkActionGroup *
schema_browser_perspective_get_actions_group(BrowserPerspective * perspective)337 schema_browser_perspective_get_actions_group (BrowserPerspective *perspective)
338 {
339 SchemaBrowserPerspective *bpers;
340 GtkActionGroup *agroup;
341 bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
342 agroup = gtk_action_group_new ("SchemaBrowserActions");
343 gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
344
345 gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
346
347 gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions,
348 G_N_ELEMENTS (ui_toggle_actions),
349 bpers);
350 GtkAction *action;
351 action = gtk_action_group_get_action (agroup, "SchemaToolsFavoritesShow");
352 if (bpers->priv->favorites)
353 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
354 bpers->priv->favorites_shown);
355 else
356 gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
357
358 return agroup;
359 }
360
361 static const gchar *
schema_browser_perspective_get_actions_ui(G_GNUC_UNUSED BrowserPerspective * bpers)362 schema_browser_perspective_get_actions_ui (G_GNUC_UNUSED BrowserPerspective *bpers)
363 {
364 return ui_actions_info;
365 }
366
367 static void
schema_browser_perspective_page_tab_label_change(BrowserPerspective * perspective,BrowserPage * page)368 schema_browser_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page)
369 {
370 SchemaBrowserPerspective *bpers;
371 GtkWidget *tab_label;
372 GtkWidget *close_btn;
373
374 bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
375 tab_label = browser_page_get_tab_label (page, &close_btn);
376 if (tab_label) {
377 gtk_notebook_set_tab_label (GTK_NOTEBOOK (bpers->priv->notebook),
378 GTK_WIDGET (page), tab_label);
379 g_signal_connect (close_btn, "clicked",
380 G_CALLBACK (close_button_clicked_cb), page);
381
382 tab_label = browser_page_get_tab_label (page, NULL);
383 gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook),
384 GTK_WIDGET (page), tab_label);
385 }
386 }
387
388 #ifdef HAVE_GOOCANVAS
389 /**
390 * schema_browser_perspective_display_diagram
391 *
392 */
393 void
schema_browser_perspective_display_diagram(SchemaBrowserPerspective * bpers,gint fav_id)394 schema_browser_perspective_display_diagram (SchemaBrowserPerspective *bpers, gint fav_id)
395 {
396 GtkWidget *diagram = NULL;
397
398 if (fav_id >= 0) {
399 gint ntabs, i;
400
401 ntabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (bpers->priv->notebook));
402 for (i = 0; i < ntabs; i++) {
403 GtkWidget *child;
404 child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
405 if (IS_RELATIONS_DIAGRAM (child)) {
406 if (relations_diagram_get_fav_id (RELATIONS_DIAGRAM (child)) == fav_id) {
407 gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
408 return;
409 }
410 }
411 }
412
413 GError *error = NULL;
414 diagram = relations_diagram_new_with_fav_id (browser_window_get_connection (bpers->priv->bwin),
415 fav_id, &error);
416 if (! diagram) {
417 browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) bpers),
418 error && error->message ? error->message :
419 _("Could not load diagram"));
420 g_clear_error (&error);
421 }
422 }
423 else
424 diagram = relations_diagram_new (browser_window_get_connection (bpers->priv->bwin));
425
426 if (diagram) {
427 GtkWidget *close_btn;
428 GtkWidget *tab_label;
429 gint i;
430
431 tab_label = browser_page_get_tab_label (BROWSER_PAGE (diagram), &close_btn);
432 i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), diagram, tab_label);
433 g_signal_connect (close_btn, "clicked",
434 G_CALLBACK (close_button_clicked_cb), diagram);
435
436 gtk_widget_show (diagram);
437
438 tab_label = browser_page_get_tab_label (BROWSER_PAGE (diagram), NULL);
439 gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook), diagram, tab_label);
440
441 gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
442 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
443 TRUE);
444 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (bpers->priv->notebook), diagram,
445 TRUE);
446 }
447 }
448 #endif
449
450
451 /**
452 * schema_browser_perspective_display_table_info
453 *
454 * Display (and create if necessary) a new page for the table's properties
455 */
456 void
schema_browser_perspective_display_table_info(SchemaBrowserPerspective * bpers,const gchar * table_schema,const gchar * table_name,G_GNUC_UNUSED const gchar * table_short_name)457 schema_browser_perspective_display_table_info (SchemaBrowserPerspective *bpers,
458 const gchar *table_schema,
459 const gchar *table_name,
460 G_GNUC_UNUSED const gchar *table_short_name)
461 {
462 g_return_if_fail (IS_SCHEMA_BROWSER_PERSPECTIVE (bpers));
463
464 gint ntabs, i;
465 ntabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (bpers->priv->notebook));
466 for (i = 0; i < ntabs; i++) {
467 GtkWidget *child;
468 child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
469 if (IS_TABLE_INFO (child)) {
470 if (!strcmp (table_schema, table_info_get_table_schema (TABLE_INFO (child))) &&
471 !strcmp (table_name, table_info_get_table_name (TABLE_INFO (child)))) {
472 gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
473 return;
474 }
475 }
476 }
477
478 GtkWidget *ti;
479 ti = table_info_new (browser_window_get_connection (bpers->priv->bwin), table_schema, table_name);
480 if (ti) {
481 GtkWidget *close_btn;
482 GtkWidget *tab_label;
483 gint i;
484
485 tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), &close_btn);
486 i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
487 g_signal_connect (close_btn, "clicked",
488 G_CALLBACK (close_button_clicked_cb), ti);
489
490 gtk_widget_show (ti);
491
492 tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), NULL);
493 gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
494
495 gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
496 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
497 TRUE);
498 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
499 TRUE);
500 }
501 }
502
503 static void
schema_browser_perspective_get_current_customization(BrowserPerspective * perspective,GtkActionGroup ** out_agroup,const gchar ** out_ui)504 schema_browser_perspective_get_current_customization (BrowserPerspective *perspective,
505 GtkActionGroup **out_agroup,
506 const gchar **out_ui)
507 {
508 SchemaBrowserPerspective *bpers;
509 GtkWidget *page_contents;
510
511 bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
512 page_contents = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook),
513 gtk_notebook_get_current_page (GTK_NOTEBOOK (bpers->priv->notebook)));
514 if (IS_BROWSER_PAGE (page_contents)) {
515 *out_agroup = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
516 *out_ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
517 }
518 }
519
520 static BrowserWindow *
schema_browser_perspective_get_window(BrowserPerspective * perspective)521 schema_browser_perspective_get_window (BrowserPerspective *perspective)
522 {
523 SchemaBrowserPerspective *bpers;
524 bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
525 return bpers->priv->bwin;
526 }
527