1 /*
2  * Copyright (C) 2009 - 2011 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 "browser-window.h"
24 #include <libgda/binreloc/gda-binreloc.h>
25 #include "login-dialog.h"
26 #include "browser-core.h"
27 #include "support.h"
28 #include "browser-perspective.h"
29 #include "browser-connection.h"
30 #include "browser-connections-list.h"
31 #include "browser-spinner.h"
32 #include "browser-stock-icons.h"
33 #include "connection-binding-properties.h"
34 #include <gdk/gdkkeysyms.h>
35 
36 /*
37  * structure representing a 'tab' in a window
38  *
39  * a 'page' is a set of widgets created by a single BrowserPerspectiveFactory object, for a connection
40  * Each is owned by a BrowserWindow
41  */
42 typedef struct {
43         BrowserWindow      *bwin; /* pointer to window the tab is in, no ref held here */
44         BrowserPerspectiveFactory *factory;
45         gint                page_number; /* in reference to bwin->perspectives_nb */
46         BrowserPerspective  *perspective_widget;
47 
48 	GtkActionGroup      *customized_actions;
49 	guint                customized_merge_id;
50 	gchar               *customized_ui;
51 } PerspectiveData;
52 #define PERSPECTIVE_DATA(x) ((PerspectiveData*)(x))
53 static PerspectiveData *perspective_data_new (BrowserWindow *bwin, BrowserPerspectiveFactory *factory);
54 static void         perspective_data_free (PerspectiveData *pers);
55 
56 /*
57  * Main static functions
58  */
59 static void browser_window_class_init (BrowserWindowClass *klass);
60 static void browser_window_init (BrowserWindow *bwin);
61 static void browser_window_dispose (GObject *object);
62 
63 static gboolean window_state_event (GtkWidget *widget, GdkEventWindowState *event);
64 static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event);
65 static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, BrowserWindow *bwin);
66 
67 static void connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin);
68 static void connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin);
69 
70 static void transaction_status_changed_cb (BrowserConnection *bcnc, BrowserWindow *bwin);
71 
72 
73 enum {
74         FULLSCREEN_CHANGED,
75         LAST_SIGNAL
76 };
77 
78 static gint browser_window_signals[LAST_SIGNAL] = { 0 };
79 
80 
81 /* get a pointer to the parents to be able to call their destructor */
82 static GObjectClass  *parent_class = NULL;
83 
84 struct _BrowserWindowPrivate {
85 	BrowserConnection *bcnc;
86 	GtkNotebook       *perspectives_nb; /* notebook used to switch between tabs, for the selector part */
87         GSList            *perspectives; /* list of PerspectiveData pointers, owned here */
88 	PerspectiveData   *current_perspective;
89 	guint              ui_manager_merge_id; /* for current perspective */
90 
91 	GtkWidget         *menubar;
92 	GtkWidget         *toolbar;
93 	gboolean           toolbar_shown;
94 	gboolean           cursor_in_toolbar;
95 	GtkWidget         *spinner;
96 	guint              spinner_timer;
97 	GtkUIManager      *ui_manager;
98 	GtkActionGroup    *agroup;
99 	GtkActionGroup    *perspectives_actions;
100 	gboolean           updating_transaction_status;
101 
102 	GtkToolbarStyle    toolbar_style;
103 	GtkActionGroup    *cnc_agroup; /* one GtkAction for each BrowserConnection */
104 	gulong             cnc_added_sigid;
105 	gulong             cnc_removed_sigid;
106 
107 	GtkWidget         *notif_box;
108 	GSList            *notif_widgets;
109 
110 	GtkWidget         *statusbar;
111 	guint              cnc_statusbar_context;
112 
113 	gboolean           fullscreen;
114 	gulong             fullscreen_motion_sig_id;
115 	guint              fullscreen_timer_id;
116 };
117 
118 GType
browser_window_get_type(void)119 browser_window_get_type (void)
120 {
121 	static GType type = 0;
122 
123 	if (G_UNLIKELY (type == 0)) {
124 		static GMutex registering;
125 		static const GTypeInfo info = {
126 			sizeof (BrowserWindowClass),
127 			(GBaseInitFunc) NULL,
128 			(GBaseFinalizeFunc) NULL,
129 			(GClassInitFunc) browser_window_class_init,
130 			NULL,
131 			NULL,
132 			sizeof (BrowserWindow),
133 			0,
134 			(GInstanceInitFunc) browser_window_init,
135 			0
136 		};
137 
138 
139 		g_mutex_lock (&registering);
140 		if (type == 0)
141 			type = g_type_register_static (GTK_TYPE_WINDOW, "BrowserWindow", &info, 0);
142 		g_mutex_unlock (&registering);
143 	}
144 	return type;
145 }
146 
147 static void
browser_window_class_init(BrowserWindowClass * klass)148 browser_window_class_init (BrowserWindowClass *klass)
149 {
150 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
151 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
152 
153 	widget_class->window_state_event = window_state_event;
154 	widget_class->key_press_event = key_press_event;
155 	parent_class = g_type_class_peek_parent (klass);
156 
157 	browser_window_signals[FULLSCREEN_CHANGED] =
158                 g_signal_new ("fullscreen_changed",
159                               G_TYPE_FROM_CLASS (object_class),
160                               G_SIGNAL_RUN_LAST,
161                               G_STRUCT_OFFSET (BrowserWindowClass, fullscreen_changed),
162                               NULL, NULL,
163                               g_cclosure_marshal_VOID__BOOLEAN,
164                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
165 
166 	object_class->dispose = browser_window_dispose;
167 	klass->fullscreen_changed = NULL;
168 }
169 
170 static void
browser_window_init(BrowserWindow * bwin)171 browser_window_init (BrowserWindow *bwin)
172 {
173 	bwin->priv = g_new0 (BrowserWindowPrivate, 1);
174 	bwin->priv->bcnc = NULL;
175 	bwin->priv->perspectives_nb = NULL;
176 	bwin->priv->perspectives = NULL;
177 	bwin->priv->cnc_agroup = NULL;
178 	bwin->priv->cnc_added_sigid = 0;
179 	bwin->priv->cnc_removed_sigid = 0;
180 	bwin->priv->updating_transaction_status = FALSE;
181 	bwin->priv->fullscreen = FALSE;
182 	bwin->priv->fullscreen_motion_sig_id = 0;
183 	bwin->priv->fullscreen_timer_id = 0;
184 	bwin->priv->spinner_timer = 0;
185 }
186 
187 static void
browser_window_dispose(GObject * object)188 browser_window_dispose (GObject *object)
189 {
190 	BrowserWindow *bwin;
191 
192 	g_return_if_fail (object != NULL);
193 	g_return_if_fail (BROWSER_IS_WINDOW (object));
194 
195 	bwin = BROWSER_WINDOW (object);
196 	if (bwin->priv) {
197 		GSList *connections, *list;
198 
199 		if (bwin->priv->spinner_timer) {
200 			g_source_remove (bwin->priv->spinner_timer);
201 			bwin->priv->spinner_timer = 0;
202 		}
203 		connections = browser_core_get_connections ();
204 		for (list = connections; list; list = list->next)
205 			connection_removed_cb (browser_core_get(), BROWSER_CONNECTION (list->data), bwin);
206 		g_slist_free (connections);
207 
208 		if (bwin->priv->fullscreen_timer_id)
209 			g_source_remove (bwin->priv->fullscreen_timer_id);
210 
211 		if (bwin->priv->fullscreen_motion_sig_id)
212 			g_signal_handler_disconnect (bwin, bwin->priv->fullscreen_motion_sig_id);
213 
214 		if (bwin->priv->cnc_added_sigid > 0)
215 			g_signal_handler_disconnect (browser_core_get (), bwin->priv->cnc_added_sigid);
216 		if (bwin->priv->cnc_removed_sigid > 0)
217 			g_signal_handler_disconnect (browser_core_get (), bwin->priv->cnc_removed_sigid);
218 		if (bwin->priv->ui_manager)
219 			g_object_unref (bwin->priv->ui_manager);
220 		if (bwin->priv->cnc_agroup)
221 			g_object_unref (bwin->priv->cnc_agroup);
222 
223 		if (bwin->priv->bcnc) {
224 			g_signal_handlers_disconnect_by_func (bwin->priv->bcnc,
225 							      G_CALLBACK (transaction_status_changed_cb),
226 							      bwin);
227 			g_object_unref (bwin->priv->bcnc);
228 		}
229 		if (bwin->priv->perspectives) {
230 			g_slist_foreach (bwin->priv->perspectives, (GFunc) perspective_data_free, NULL);
231 			g_slist_free (bwin->priv->perspectives);
232 		}
233 		if (bwin->priv->perspectives_nb)
234 			g_object_unref (bwin->priv->perspectives_nb);
235 
236 		if (bwin->priv->notif_widgets)
237 			g_slist_free (bwin->priv->notif_widgets);
238 		g_free (bwin->priv);
239 		bwin->priv = NULL;
240 	}
241 
242 	/* parent class */
243 	parent_class->dispose (object);
244 }
245 
246 
247 static gboolean
delete_event(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEvent * event,BrowserWindow * bwin)248 delete_event (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, BrowserWindow *bwin)
249 {
250 	browser_core_close_window (bwin);
251         return TRUE;
252 }
253 
254 static void transaction_begin_cb (GtkAction *action, BrowserWindow *bwin);
255 static void transaction_commit_cb (GtkAction *action, BrowserWindow *bwin);
256 static void transaction_rollback_cb (GtkAction *action, BrowserWindow *bwin);
257 static void quit_cb (GtkAction *action, BrowserWindow *bwin);
258 static void about_cb (GtkAction *action, BrowserWindow *bwin);
259 #ifdef HAVE_GDU
260 static void manual_cb (GtkAction *action, BrowserWindow *bwin);
261 #endif
262 static void window_close_cb (GtkAction *action, BrowserWindow *bwin);
263 static void window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin);
264 static void window_new_cb (GtkAction *action, BrowserWindow *bwin);
265 static void window_new_with_cnc_cb (GtkAction *action, BrowserWindow *bwin);
266 static void connection_properties_cb (GtkAction *action, BrowserWindow *bwin);
267 static void connection_close_cb (GtkAction *action, BrowserWindow *bwin);
268 static void connection_open_cb (GtkAction *action, BrowserWindow *bwin);
269 static void connection_bind_cb (GtkAction *action, BrowserWindow *bwin);
270 static void connection_list_cb (GtkAction *action, BrowserWindow *bwin);
271 static void connection_meta_update_cb (GtkAction *action, BrowserWindow *bwin);
272 static void perspective_toggle_cb (GtkRadioAction *action, GtkRadioAction *current, BrowserWindow *bwin);
273 
274 static const GtkToggleActionEntry ui_toggle_actions [] =
275 {
276         { "WindowFullScreen", GTK_STOCK_FULLSCREEN, N_("_Fullscreen"), "F11", N_("Use the whole screen"), G_CALLBACK (window_fullscreen_cb), FALSE}
277 };
278 
279 static const GtkActionEntry ui_actions[] = {
280         { "Connection", NULL, N_("_Connection"), NULL, N_("Connection"), NULL },
281         { "ConnectionOpen", GTK_STOCK_CONNECT, N_("_Connect"), NULL, N_("Open a connection"), G_CALLBACK (connection_open_cb)},
282         { "ConnectionBind", NULL, N_("_Bind Connection"), "<control>I", N_("Use connection to create\n"
283 						    "a new binding connection to access data\n"
284 						    "from multiple databases at once"), G_CALLBACK (connection_bind_cb)},
285         { "ConnectionProps", GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Connection properties"), G_CALLBACK (connection_properties_cb)},
286         { "ConnectionList", NULL, N_("_Connections List"), NULL, N_("Connections list"), G_CALLBACK (connection_list_cb)},
287         { "ConnectionMetaSync", GTK_STOCK_REFRESH, N_("_Fetch Meta Data"), NULL, N_("Fetch meta data"), G_CALLBACK (connection_meta_update_cb)},
288         { "ConnectionClose", GTK_STOCK_CLOSE, N_("_Close Connection"), NULL, N_("Close this connection"), G_CALLBACK (connection_close_cb)},
289         { "Quit", GTK_STOCK_QUIT, N_("_Quit"), NULL, N_("Quit"), G_CALLBACK (quit_cb)},
290         { "Edit", NULL, N_("_Edit"), NULL, N_("Edit"), NULL },
291         { "Display", NULL, N_("_Display"), NULL, N_("Display"), NULL },
292         { "Perspective", NULL, N_("_Perspective"), NULL, N_("Perspective"), NULL },
293         { "Window", NULL, N_("_Window"), NULL, N_("Window"), NULL },
294         { "WindowNew", STOCK_NEW_WINDOW, N_("_New Window"), "<control>N", N_("Open a new window for current connection"), G_CALLBACK (window_new_cb)},
295         { "WindowNewOthers", NULL, N_("New Window for _Connection"), NULL, N_("Open a new window for a connection"), NULL},
296         { "WindowClose", GTK_STOCK_CLOSE, N_("_Close"), NULL, N_("Close this window"), G_CALLBACK (window_close_cb)},
297         { "Help", NULL, N_("_Help"), NULL, N_("Help"), NULL },
298         { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), G_CALLBACK (about_cb) },
299 #ifdef HAVE_GDU
300         { "HelpManual", GTK_STOCK_HELP, N_("_Manual"), "F1", N_("Manual"), G_CALLBACK (manual_cb) },
301 #endif
302 	{ "TransactionBegin", BROWSER_STOCK_BEGIN, N_("Begin"), NULL, N_("Begin a new transaction"),
303           G_CALLBACK (transaction_begin_cb)},
304         { "TransactionCommit", BROWSER_STOCK_COMMIT, N_("Commit"), NULL, N_("Commit current transaction"),
305           G_CALLBACK (transaction_commit_cb)},
306         { "TransactionRollback", BROWSER_STOCK_ROLLBACK, N_("Rollback"), NULL, N_("Rollback current transaction"),
307           G_CALLBACK (transaction_rollback_cb)},
308 };
309 
310 static const gchar *ui_actions_info =
311         "<ui>"
312         "  <menubar name='MenuBar'>"
313         "    <menu name='Connection' action='Connection'>"
314         "      <menuitem name='ConnectionOpen' action= 'ConnectionOpen'/>"
315         "      <menuitem name='ConnectionList' action= 'ConnectionList'/>"
316         "      <menuitem name='ConnectionMetaSync' action= 'ConnectionMetaSync'/>"
317         "      <separator/>"
318         "      <menuitem name='ConnectionProps' action= 'ConnectionProps'/>"
319         "      <menuitem name='ConnectionBind' action= 'ConnectionBind'/>"
320         "      <menuitem name='ConnectionClose' action= 'ConnectionClose'/>"
321         "      <separator/>"
322 	"      <menuitem name='TransactionBegin' action= 'TransactionBegin'/>"
323         "      <menuitem name='TransactionCommit' action= 'TransactionCommit'/>"
324         "      <menuitem name='TransactionRollback' action= 'TransactionRollback'/>"
325         "      <separator/>"
326         "      <menuitem name='Quit' action= 'Quit'/>"
327         "      <separator/>"
328         "    </menu>"
329         "    <menu name='Edit' action='Edit'>"
330         "    </menu>"
331         "    <menu name='Display' action='Display'>"
332         "    </menu>"
333         "    <menu name='Perspective' action='Perspective'>"
334         "      <placeholder name='PersList'/>"
335         "    </menu>"
336         "    <menu name='Window' action='Window'>"
337         "      <menuitem name='WindowFullScreen' action= 'WindowFullScreen'/>"
338         "      <separator/>"
339         "      <menuitem name='WindowNew' action= 'WindowNew'/>"
340         "      <menu name='WindowNewOthers' action='WindowNewOthers'>"
341         "          <placeholder name='CncList'/>"
342         "      </menu>"
343         "      <separator/>"
344         "      <menuitem name='WindowClose' action= 'WindowClose'/>"
345         "    </menu>"
346 	"    <placeholder name='MenuExtension'/>"
347         "    <menu name='Help' action='Help'>"
348 #ifdef HAVE_GDU
349         "      <menuitem name='HelpManual' action= 'HelpManual'/>"
350 #endif
351         "      <menuitem name='HelpAbout' action= 'HelpAbout'/>"
352         "    </menu>"
353         "  </menubar>"
354         "  <toolbar name='ToolBar'>"
355         "    <toolitem action='WindowClose'/>"
356         "    <toolitem action='WindowFullScreen'/>"
357         "    <toolitem action='TransactionBegin'/>"
358         "    <toolitem action='TransactionCommit'/>"
359         "    <toolitem action='TransactionRollback'/>"
360         "  </toolbar>"
361         "</ui>";
362 
363 /**
364  * browser_window_new
365  * @bcnc: a #BrowserConnection
366  * @factory: a #BrowserPerspectiveFactory, may be %NULL
367  *
368  * Creates a new #BrowserWindow window for the @bcnc connection, and displays it.
369  * If @factory is not %NULL, then the new window will show the perspective corresponding
370  * to @factory. If it's %NULL, then the default #BrowserPerspectiveFactory will be used,
371  * see browser_core_get_default_factory().
372  *
373  * Don't forget to call browser_core_take_window() to have the new window correctly
374  * managed by the browser. Similarly, to close the window, use browser_core_close_window()
375  * and not simply gtk_widget_destroy().
376  *
377  * Returns: the new object
378  */
379 BrowserWindow*
browser_window_new(BrowserConnection * bcnc,BrowserPerspectiveFactory * factory)380 browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
381 {
382 	BrowserWindow *bwin;
383 
384 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
385 
386 	bwin = BROWSER_WINDOW (g_object_new (BROWSER_TYPE_WINDOW, NULL));
387 	bwin->priv->bcnc = g_object_ref (bcnc);
388 	g_signal_connect (bcnc, "transaction-status-changed",
389 			  G_CALLBACK (transaction_status_changed_cb), bwin);
390 
391 	gchar *tmp;
392 	tmp = browser_connection_get_long_name (bcnc);
393 	gtk_window_set_title (GTK_WINDOW (bwin), tmp);
394 	g_free (tmp);
395 
396 	gtk_window_set_default_size ((GtkWindow*) bwin, 900, 650);
397 	g_signal_connect (G_OBJECT (bwin), "delete-event",
398                           G_CALLBACK (delete_event), bwin);
399 	/* icon */
400 	GdkPixbuf *icon;
401         gchar *path;
402         path = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser.png", NULL);
403         icon = gdk_pixbuf_new_from_file (path, NULL);
404         g_free (path);
405         if (icon) {
406                 gtk_window_set_icon (GTK_WINDOW (bwin), icon);
407                 g_object_unref (icon);
408         }
409 
410 	/* main VBox */
411 	GtkWidget *vbox;
412 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
413         gtk_container_add (GTK_CONTAINER (bwin), vbox);
414         gtk_widget_show (vbox);
415 
416 	/* menu */
417 	GtkWidget *menubar;
418         GtkWidget *toolbar;
419         GtkUIManager *ui;
420 	GtkActionGroup *group;
421 
422         group = gtk_action_group_new ("Actions");
423 	gtk_action_group_set_translation_domain (group, GETTEXT_PACKAGE);
424 
425 	bwin->priv->agroup = group;
426         gtk_action_group_add_actions (group, ui_actions, G_N_ELEMENTS (ui_actions), bwin);
427 	gtk_action_group_add_toggle_actions (group, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions), bwin);
428 	if (browser_connection_is_virtual (bwin->priv->bcnc)) {
429 		GtkAction *action;
430 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionBegin");
431 		gtk_action_set_visible (action, FALSE);
432 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionCommit");
433 		gtk_action_set_visible (action, FALSE);
434 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionRollback");
435 		gtk_action_set_visible (action, FALSE);
436 	}
437 	transaction_status_changed_cb (bwin->priv->bcnc, bwin);
438 
439         ui = gtk_ui_manager_new ();
440         gtk_ui_manager_insert_action_group (ui, group, 0);
441         gtk_ui_manager_add_ui_from_string (ui, ui_actions_info, -1, NULL);
442 	bwin->priv->ui_manager = ui;
443 
444 	GtkAccelGroup *accel_group;
445         accel_group = gtk_ui_manager_get_accel_group (ui);
446 	gtk_window_add_accel_group (GTK_WINDOW (bwin), accel_group);
447 
448         menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
449 	bwin->priv->menubar = menubar;
450 #ifdef HAVE_MAC_INTEGRATION
451 	gtk_osxapplication_set_menu_bar (theApp, GTK_MENU_SHELL (menubar));
452 #else
453         gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
454         gtk_widget_show (menubar);
455 #endif
456 
457         toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar");
458 	bwin->priv->toolbar = toolbar;
459 	bwin->priv->toolbar_shown = TRUE;
460         gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0);
461 	gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
462         gtk_widget_show (toolbar);
463 	bwin->priv->toolbar_style = gtk_toolbar_get_style (GTK_TOOLBAR (toolbar));
464 
465 	bwin->priv->notif_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
466 	gtk_box_pack_start (GTK_BOX (vbox), bwin->priv->notif_box, FALSE, FALSE, 0);
467         gtk_widget_show (bwin->priv->notif_box);
468 	bwin->priv->notif_widgets = NULL;
469 
470 	GtkToolItem *ti;
471 	GtkWidget *spinner, *svbox, *align;
472 
473 	ti = gtk_separator_tool_item_new ();
474 	gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (ti), FALSE);
475 	gtk_tool_item_set_expand (ti, TRUE);
476 	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), ti, -1);
477         gtk_widget_show (GTK_WIDGET (ti));
478 
479 	spinner = browser_spinner_new ();
480 	browser_spinner_set_size ((BrowserSpinner*) spinner, GTK_ICON_SIZE_SMALL_TOOLBAR);
481 
482 	svbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
483 	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
484 	gtk_container_add (GTK_CONTAINER (align), spinner);
485 	gtk_box_pack_start (GTK_BOX (svbox), align, TRUE, TRUE, 0);
486 
487 	ti = gtk_tool_item_new  ();
488 	gtk_container_add (GTK_CONTAINER (ti), svbox);
489 	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), ti, -1);
490         gtk_widget_show_all (GTK_WIDGET (ti));
491 	gtk_widget_hide (GTK_WIDGET (spinner));
492 	bwin->priv->spinner = spinner;
493 
494 	/* statusbar */
495 	bwin->priv->statusbar = gtk_statusbar_new ();
496 
497 	GSList *connections, *list;
498 	bwin->priv->cnc_agroup = gtk_action_group_new ("CncActions");
499 	gtk_action_group_set_translation_domain (bwin->priv->cnc_agroup, GETTEXT_PACKAGE);
500 
501 	connections = browser_core_get_connections ();
502 	for (list = connections; list; list = list->next)
503 		connection_added_cb (browser_core_get(), BROWSER_CONNECTION (list->data), bwin);
504 	g_slist_free (connections);
505 
506 	gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, bwin->priv->cnc_agroup, 0);
507 	bwin->priv->cnc_added_sigid = g_signal_connect (browser_core_get (), "connection-added",
508 							G_CALLBACK (connection_added_cb), bwin);
509 	bwin->priv->cnc_removed_sigid = g_signal_connect (browser_core_get (), "connection-removed",
510 							  G_CALLBACK (connection_removed_cb), bwin);
511 
512 
513 	/* create a PerspectiveData */
514 	PerspectiveData *pers;
515 	if (! factory && browser_connection_is_ldap (bcnc))
516 		factory = browser_core_get_factory (_("LDAP browser"));
517 	pers = perspective_data_new (bwin, factory);
518 	bwin->priv->perspectives = g_slist_prepend (bwin->priv->perspectives, pers);
519 	GtkActionGroup *actions;
520 	actions = browser_perspective_get_actions_group (BROWSER_PERSPECTIVE (pers->perspective_widget));
521 	if (actions) {
522 		gtk_action_group_set_translation_domain (actions, GETTEXT_PACKAGE);
523 		gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, actions, 0);
524 		g_object_unref (actions);
525 	}
526 	const gchar *ui_info;
527 	ui_info = browser_perspective_get_actions_ui (BROWSER_PERSPECTIVE (pers->perspective_widget));
528 	if (ui_info)
529 		bwin->priv->ui_manager_merge_id = gtk_ui_manager_add_ui_from_string (bwin->priv->ui_manager,
530 										     ui_info, -1, NULL);
531 	bwin->priv->current_perspective = pers;
532 	browser_perspective_get_current_customization (BROWSER_PERSPECTIVE (pers->perspective_widget),
533 						       &actions, &ui_info);
534 	browser_window_customize_perspective_ui (bwin, BROWSER_PERSPECTIVE (pers->perspective_widget),
535 						 actions, ui_info);
536 	if (actions)
537 		g_object_unref (actions);
538 
539 	/* insert perspective into window */
540         bwin->priv->perspectives_nb = (GtkNotebook*) gtk_notebook_new ();
541         g_object_ref (bwin->priv->perspectives_nb);
542         gtk_notebook_set_show_tabs (bwin->priv->perspectives_nb, FALSE);
543 	gtk_box_pack_start (GTK_BOX (vbox), (GtkWidget*) bwin->priv->perspectives_nb,
544 			    TRUE, TRUE, 0);
545 
546 	pers->page_number = gtk_notebook_append_page (bwin->priv->perspectives_nb,
547 						      GTK_WIDGET (pers->perspective_widget), NULL);
548         gtk_widget_show_all ((GtkWidget*) bwin->priv->perspectives_nb);
549 	gtk_widget_grab_focus (GTK_WIDGET (pers->perspective_widget));
550 
551 	/* build the perspectives menu */
552 	GtkActionGroup *agroup;
553 	const GSList *plist;
554 	GSList *radio_group = NULL;
555 	guint mid;
556 
557 	mid = gtk_ui_manager_new_merge_id (bwin->priv->ui_manager);
558 	agroup = gtk_action_group_new ("Perspectives");
559 	gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
560 
561 	gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, agroup, 0);
562 	bwin->priv->perspectives_actions = agroup;
563 	g_object_unref (agroup);
564 
565 	GtkAction *active_action = NULL;
566 	for (plist = browser_core_get_factories (); plist; plist = plist->next) {
567 		GtkAction *action;
568 		const gchar *name;
569 
570 		name = BROWSER_PERSPECTIVE_FACTORY (plist->data)->perspective_name;
571 		if (!strcmp (name, _("LDAP browser")) && !browser_connection_is_ldap (bcnc))
572 			continue;
573 
574 		action = GTK_ACTION (gtk_radio_action_new (name, name, NULL, NULL, FALSE));
575 
576 		if (!active_action &&
577 		    ((factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == factory)) ||
578 		     (!factory && (BROWSER_PERSPECTIVE_FACTORY (plist->data) == browser_core_get_default_factory ()))))
579 			active_action = action;
580 		if (BROWSER_PERSPECTIVE_FACTORY (plist->data)->menu_shortcut)
581 			gtk_action_group_add_action_with_accel (agroup, action,
582 								BROWSER_PERSPECTIVE_FACTORY (plist->data)->menu_shortcut);
583 		else
584 			gtk_action_group_add_action (agroup, action);
585 
586 		gtk_radio_action_set_group (GTK_RADIO_ACTION (action), radio_group);
587 		radio_group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action));
588 
589 		g_object_set_data (G_OBJECT (action), "pers", plist->data);
590 		g_signal_connect (action, "changed",
591 				  G_CALLBACK (perspective_toggle_cb), bwin);
592 
593 		g_object_unref (action);
594 
595 		gtk_ui_manager_add_ui (bwin->priv->ui_manager, mid,  "/MenuBar/Perspective/PersList",
596 				       name, name,
597 				       GTK_UI_MANAGER_AUTO, FALSE);
598 	}
599 
600 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (active_action), TRUE);
601 
602 	gtk_box_pack_start (GTK_BOX (vbox), bwin->priv->statusbar, FALSE, FALSE, 0);
603         gtk_widget_show (bwin->priv->statusbar);
604 	bwin->priv->cnc_statusbar_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar),
605 									  "cncbusy");
606 
607         gtk_widget_show (GTK_WIDGET (bwin));
608 
609 	gtk_widget_set_can_focus ((GtkWidget* )pers->perspective_widget, TRUE);
610 	gtk_widget_grab_focus ((GtkWidget* )pers->perspective_widget);
611 
612 	return bwin;
613 }
614 
615 static void
perspective_toggle_cb(GtkRadioAction * action,GtkRadioAction * current,BrowserWindow * bwin)616 perspective_toggle_cb (GtkRadioAction *action, GtkRadioAction *current, BrowserWindow *bwin)
617 {
618 	BrowserPerspectiveFactory *pf;
619 	GSList *list;
620 	PerspectiveData *pers;
621 	if (action != current)
622 		return;
623 
624 	pf = BROWSER_PERSPECTIVE_FACTORY (g_object_get_data (G_OBJECT (action), "pers"));
625 	g_assert (pf);
626 
627 	/* current perspective's cleanups */
628 	if (bwin->priv->current_perspective) {
629 		pers = bwin->priv->current_perspective;
630 		if (pers->customized_merge_id) {
631 			gtk_ui_manager_remove_ui (bwin->priv->ui_manager, pers->customized_merge_id);
632 			pers->customized_merge_id = 0;
633 		}
634 		bwin->priv->current_perspective = NULL;
635 	}
636 
637 	/* check if perspective already exists */
638 	for (list = bwin->priv->perspectives, pers = NULL; list; list = list->next) {
639 		if (PERSPECTIVE_DATA (list->data)->factory == pf) {
640 			pers = PERSPECTIVE_DATA (list->data);
641 			break;
642 		}
643 	}
644 
645 	if (!pers) {
646 		pers = perspective_data_new (bwin, pf);
647 		bwin->priv->perspectives = g_slist_prepend (bwin->priv->perspectives, pers);
648 		pers->page_number = gtk_notebook_append_page (bwin->priv->perspectives_nb,
649 							      GTK_WIDGET (pers->perspective_widget), NULL);
650 		gtk_widget_show (GTK_WIDGET (pers->perspective_widget));
651 
652 		GtkActionGroup *actions;
653 		actions = browser_perspective_get_actions_group (BROWSER_PERSPECTIVE (pers->perspective_widget));
654 		if (actions) {
655 			gtk_action_group_set_translation_domain (actions, GETTEXT_PACKAGE);
656 			gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, actions, 0);
657 			g_object_unref (actions);
658 		}
659 	}
660 
661 	gtk_notebook_set_current_page (bwin->priv->perspectives_nb, pers->page_number);
662 
663 	/* menus and toolbar handling */
664 	if (bwin->priv->ui_manager_merge_id > 0) {
665 		gtk_ui_manager_remove_ui (bwin->priv->ui_manager, bwin->priv->ui_manager_merge_id);
666 		bwin->priv->ui_manager_merge_id = 0;
667 	}
668 
669 	const gchar *ui_info;
670 	ui_info = browser_perspective_get_actions_ui (BROWSER_PERSPECTIVE (pers->perspective_widget));
671 	if (ui_info)
672 		bwin->priv->ui_manager_merge_id = gtk_ui_manager_add_ui_from_string (bwin->priv->ui_manager,
673 										     ui_info, -1, NULL);
674 
675 	/* current perspective's customizations */
676 	bwin->priv->current_perspective = pers;
677 
678 	GtkActionGroup *actions;
679 	browser_perspective_get_current_customization (BROWSER_PERSPECTIVE (pers->perspective_widget),
680 						       &actions, &ui_info);
681 	browser_window_customize_perspective_ui (bwin, BROWSER_PERSPECTIVE (pers->perspective_widget),
682 						 actions, ui_info);
683 	if (actions)
684 		g_object_unref (actions);
685 }
686 
687 static gboolean
spinner_stop_timer(BrowserWindow * bwin)688 spinner_stop_timer (BrowserWindow *bwin)
689 {
690 	browser_spinner_stop (BROWSER_SPINNER (bwin->priv->spinner));
691 	bwin->priv->spinner_timer = 0;
692 	return FALSE; /* remove timer */
693 }
694 
695 static void
connection_busy_cb(BrowserConnection * bcnc,gboolean is_busy,gchar * reason,BrowserWindow * bwin)696 connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, BrowserWindow *bwin)
697 {
698 	GtkAction *action;
699 	if (bcnc == bwin->priv->bcnc) {
700 		/* @bcbc is @bwin's own connection */
701 		if (bwin->priv->spinner_timer) {
702 			g_source_remove (bwin->priv->spinner_timer);
703 			bwin->priv->spinner_timer = 0;
704 		}
705 		if (is_busy) {
706 			browser_spinner_start (BROWSER_SPINNER (bwin->priv->spinner));
707 			gtk_widget_set_tooltip_text (bwin->priv->spinner, reason);
708 			gtk_statusbar_push (GTK_STATUSBAR (bwin->priv->statusbar),
709 					    bwin->priv->cnc_statusbar_context,
710 					    reason);
711 		}
712 		else {
713 			bwin->priv->spinner_timer = g_timeout_add (300,
714 						    (GSourceFunc) spinner_stop_timer, bwin);
715 			gtk_widget_set_tooltip_text (bwin->priv->spinner, NULL);
716 			gtk_statusbar_pop (GTK_STATUSBAR (bwin->priv->statusbar),
717 					   bwin->priv->cnc_statusbar_context);
718 		}
719 
720 		gboolean bsens = FALSE, csens = FALSE;
721 		if (!is_busy) {
722 			if (browser_connection_get_transaction_status (bcnc))
723 				csens = TRUE;
724 			else
725 				bsens = TRUE;
726 		}
727 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionBegin");
728 		gtk_action_set_sensitive (action, bsens);
729 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionCommit");
730 		gtk_action_set_sensitive (action, csens);
731 		action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionRollback");
732 		gtk_action_set_sensitive (action, csens);
733 
734 		action = gtk_action_group_get_action (bwin->priv->agroup, "WindowNew");
735 		gtk_action_set_sensitive (action, !is_busy);
736 		action = gtk_action_group_get_action (bwin->priv->agroup, "ConnectionMetaSync");
737 		gtk_action_set_sensitive (action, !is_busy);
738 	}
739 
740 	gchar *cncname;
741 	cncname = browser_connection_get_long_name (bcnc);
742 	action = gtk_action_group_get_action (bwin->priv->cnc_agroup, cncname);
743 	g_free (cncname);
744 	if (action)
745 		gtk_action_set_sensitive (action, !is_busy);
746 }
747 
748 /* update @bwin->priv->cnc_agroup and @bwin->priv->ui_manager */
749 static void
connection_added_cb(G_GNUC_UNUSED BrowserCore * bcore,BrowserConnection * bcnc,BrowserWindow * bwin)750 connection_added_cb (G_GNUC_UNUSED BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin)
751 {
752 	GtkAction *action;
753 	gchar *cncname;
754 	guint mid;
755 
756 	mid = gtk_ui_manager_new_merge_id (bwin->priv->ui_manager);
757 	cncname = browser_connection_get_long_name (bcnc);
758 	action = gtk_action_new (cncname, cncname, NULL, NULL);
759 	gtk_action_group_add_action (bwin->priv->cnc_agroup, action);
760 	guint *amid = g_new (guint, 1);
761 	*amid = mid;
762 	g_object_set_data_full (G_OBJECT (action), "mid", amid, g_free);
763 
764 	gtk_ui_manager_add_ui (bwin->priv->ui_manager, mid, "/MenuBar/Window/WindowNewOthers/CncList",
765 			       cncname, cncname,
766 			       GTK_UI_MANAGER_AUTO, FALSE);
767 	g_free (cncname);
768 	g_signal_connect (action, "activate",
769 			  G_CALLBACK (window_new_with_cnc_cb), bwin);
770 	g_object_set_data (G_OBJECT (action), "bcnc", bcnc);
771 	gtk_action_set_sensitive (action, ! browser_connection_is_busy (bcnc, NULL));
772 	g_object_unref (action);
773 
774 	gchar *reason = NULL;
775 	if (browser_connection_is_busy (bcnc, &reason)) {
776 		connection_busy_cb (bcnc, TRUE, reason, bwin);
777 		g_free (reason);
778 	}
779 	g_signal_connect (bcnc, "busy",
780 			  G_CALLBACK (connection_busy_cb), bwin);
781 }
782 
783 /* update @bwin->priv->cnc_agroup and @bwin->priv->ui_manager */
784 static void
connection_removed_cb(G_GNUC_UNUSED BrowserCore * bcore,BrowserConnection * bcnc,BrowserWindow * bwin)785 connection_removed_cb (G_GNUC_UNUSED BrowserCore *bcore, BrowserConnection *bcnc, BrowserWindow *bwin)
786 {
787 	GtkAction *action;
788 	gchar *path;
789 	gchar *cncname;
790 	guint *mid;
791 
792 	cncname = browser_connection_get_long_name (bcnc);
793 	path = g_strdup_printf ("/MenuBar/Window/WindowNewOthers/CncList/%s", cncname);
794 	g_free (cncname);
795 	action = gtk_ui_manager_get_action (bwin->priv->ui_manager, path);
796 	g_free (path);
797 	g_assert (action);
798 
799 	mid = g_object_get_data (G_OBJECT (action), "mid");
800 	g_assert (mid);
801 
802 	gtk_ui_manager_remove_ui (bwin->priv->ui_manager, *mid);
803 	gtk_action_group_remove_action (bwin->priv->cnc_agroup, action);
804 
805 	g_signal_handlers_disconnect_by_func (bcnc,
806 					      G_CALLBACK (connection_busy_cb), bwin);
807 }
808 
809 static void
connection_close_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)810 connection_close_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
811 {
812 	/* confirmation dialog */
813 	GtkWidget *dialog;
814 	char *str;
815 	BrowserConnection *bcnc;
816 	bcnc = browser_window_get_connection (bwin);
817 
818 	str = g_strdup_printf (_("Do you want to close the '%s' connection?"),
819 			       browser_connection_get_name (bcnc));
820 	dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (bwin), GTK_DIALOG_MODAL,
821 						     GTK_MESSAGE_QUESTION,
822 						     GTK_BUTTONS_YES_NO,
823 						     "<b>%s</b>", str);
824 	g_free (str);
825 
826 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) {
827 		/* actual connection closing */
828 		browser_core_close_connection (bcnc);
829 
830 		/* list all the windows using bwin's connection */
831 		GSList *list, *windows, *bwin_list = NULL;
832 
833 		windows = browser_core_get_windows ();
834 		for (list = windows; list; list = list->next) {
835 			if (browser_window_get_connection (BROWSER_WINDOW (list->data)) == bcnc)
836 				bwin_list = g_slist_prepend (bwin_list, list->data);
837 		}
838 		g_slist_free (windows);
839 
840 		for (list = bwin_list; list; list = list->next)
841 			delete_event (NULL, NULL, BROWSER_WINDOW (list->data));
842 		g_slist_free (bwin_list);
843 	}
844 	gtk_widget_destroy (dialog);
845 }
846 
847 static void
quit_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)848 quit_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
849 {
850 	/* confirmation dialog */
851 	GtkWidget *dialog;
852 	GSList *connections;
853 
854 	connections = browser_core_get_connections ();
855 	if (connections && connections->next)
856 		dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (bwin), GTK_DIALOG_MODAL,
857 							     GTK_MESSAGE_QUESTION,
858 							     GTK_BUTTONS_YES_NO,
859 							     "<b>%s</b>\n<small>%s</small>",
860 							     _("Do you want to quit the application?"),
861 							     _("all the connections will be closed."));
862 	else
863 		dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (bwin), GTK_DIALOG_MODAL,
864 							     GTK_MESSAGE_QUESTION,
865 							     GTK_BUTTONS_YES_NO,
866 							     "<b>%s</b>\n<small>%s</small>",
867 							     _("Do you want to quit the application?"),
868 							     _("the connection will be closed."));
869 
870 
871 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) {
872 		/* actual connection closing */
873 		GSList *list;
874 		for (list = connections; list; list = list->next)
875 			browser_core_close_connection (BROWSER_CONNECTION (list->data));
876 
877 		/* list all the windows using bwin's connection */
878 		GSList *windows;
879 		windows = browser_core_get_windows ();
880 		for (list = windows; list; list = list->next)
881 			delete_event (NULL, NULL, BROWSER_WINDOW (list->data));
882 		g_slist_free (windows);
883 	}
884 	g_slist_free (connections);
885 	gtk_widget_destroy (dialog);
886 }
887 
888 static void
transaction_status_changed_cb(BrowserConnection * bcnc,BrowserWindow * bwin)889 transaction_status_changed_cb (BrowserConnection *bcnc, BrowserWindow *bwin)
890 {
891 	if (!bwin->priv->agroup)
892 		return;
893 
894 	GtkAction *action;
895 	gboolean trans_started;
896 
897 	trans_started = browser_connection_get_transaction_status (bcnc) ? TRUE : FALSE;
898 	bwin->priv->updating_transaction_status = TRUE;
899 
900 	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionBegin");
901 	gtk_action_set_sensitive (action, !trans_started);
902 
903 	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionCommit");
904 	gtk_action_set_sensitive (action, trans_started);
905 
906 	action = gtk_action_group_get_action (bwin->priv->agroup, "TransactionRollback");
907 	gtk_action_set_sensitive (action, trans_started);
908 
909 	bwin->priv->updating_transaction_status = FALSE;
910 }
911 
912 static void
transaction_begin_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)913 transaction_begin_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
914 {
915 	if (!bwin->priv->updating_transaction_status) {
916 		GError *error = NULL;
917 		if (! browser_connection_begin (bwin->priv->bcnc, &error)) {
918 			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
919 					    _("Error starting transaction: %s"),
920 					    error && error->message ? error->message : _("No detail"));
921 			g_clear_error (&error);
922 		}
923 	}
924 }
925 
926 static void
transaction_commit_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)927 transaction_commit_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
928 {
929 	if (!bwin->priv->updating_transaction_status) {
930 		GError *error = NULL;
931 		if (! browser_connection_commit (bwin->priv->bcnc, &error)) {
932 			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
933 					    _("Error committing transaction: %s"),
934 					    error && error->message ? error->message : _("No detail"));
935 			g_clear_error (&error);
936 		}
937 	}
938 }
939 
940 static void
transaction_rollback_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)941 transaction_rollback_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
942 {
943 	if (!bwin->priv->updating_transaction_status) {
944 		GError *error = NULL;
945 		if (! browser_connection_rollback (bwin->priv->bcnc, &error)) {
946 			browser_show_error (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) bwin)),
947 					    _("Error rolling back transaction: %s"),
948 					    error && error->message ? error->message : _("No detail"));
949 			g_clear_error (&error);
950 		}
951 	}
952 }
953 
954 
955 static void
window_close_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)956 window_close_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
957 {
958 	delete_event (NULL, NULL, bwin);
959 }
960 
961 static gboolean
toolbar_hide_timeout_cb(BrowserWindow * bwin)962 toolbar_hide_timeout_cb (BrowserWindow *bwin)
963 {
964 	if (!bwin->priv->cursor_in_toolbar) {
965 		gtk_widget_hide (bwin->priv->toolbar);
966 		gtk_widget_hide (bwin->priv->menubar);
967 		bwin->priv->toolbar_shown = FALSE;
968 
969 		/* remove timer */
970 		bwin->priv->fullscreen_timer_id = 0;
971 		return FALSE;
972 	}
973 	else
974 		/* keep timer */
975 		return TRUE;
976 }
977 
978 #define BWIN_WINDOW_FULLSCREEN_POPUP_THRESHOLD 15
979 #define BWIN_WINDOW_FULLSCREEN_POPUP_TIMER 5
980 
981 static gboolean
fullscreen_motion_notify_cb(GtkWidget * widget,GdkEventMotion * event,G_GNUC_UNUSED gpointer user_data)982 fullscreen_motion_notify_cb (GtkWidget *widget, GdkEventMotion *event, G_GNUC_UNUSED gpointer user_data)
983 {
984 	BrowserWindow *bwin = BROWSER_WINDOW (widget);
985 	if (gtk_widget_get_window (widget) != event->window)
986 		return FALSE;
987 
988 	if (event->y < BWIN_WINDOW_FULLSCREEN_POPUP_THRESHOLD) {
989 		gtk_widget_show (bwin->priv->toolbar);
990 		gtk_widget_show (bwin->priv->menubar);
991 		bwin->priv->toolbar_shown = TRUE;
992 	}
993 
994 	if (bwin->priv->toolbar_shown) {
995 		/* reset toolbar hiding timer */
996 		if (bwin->priv->fullscreen_timer_id)
997 			g_source_remove (bwin->priv->fullscreen_timer_id);
998 		bwin->priv->fullscreen_timer_id = g_timeout_add_seconds (BWIN_WINDOW_FULLSCREEN_POPUP_TIMER,
999 									 (GSourceFunc) toolbar_hide_timeout_cb,
1000 									 bwin);
1001 	}
1002 	return FALSE;
1003 }
1004 
1005 gboolean
toolbar_enter_notify_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventCrossing * event,BrowserWindow * bwin)1006 toolbar_enter_notify_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEventCrossing *event,
1007 			 BrowserWindow *bwin)
1008 {
1009 	bwin->priv->cursor_in_toolbar = TRUE;
1010 	return FALSE;
1011 }
1012 
1013 gboolean
toolbar_leave_notify_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventCrossing * event,BrowserWindow * bwin)1014 toolbar_leave_notify_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEventCrossing *event, BrowserWindow *bwin)
1015 {
1016 	bwin->priv->cursor_in_toolbar = FALSE;
1017 	return FALSE;
1018 }
1019 
1020 static void
window_fullscreen_cb(GtkToggleAction * action,BrowserWindow * bwin)1021 window_fullscreen_cb (GtkToggleAction *action, BrowserWindow *bwin)
1022 {
1023 	if (gtk_toggle_action_get_active (action)) {
1024 		gtk_window_fullscreen (GTK_WINDOW (bwin));
1025 		browser_window_show_notice_printf (bwin, GTK_MESSAGE_INFO,
1026 						   "fullscreen-esc",
1027 						   "%s", _("Hit the Escape key to leave the fullscreen mode"));
1028 		gtk_widget_hide (bwin->priv->toolbar);
1029 		gtk_widget_hide (bwin->priv->menubar);
1030 		bwin->priv->toolbar_shown = FALSE;
1031 		bwin->priv->fullscreen_motion_sig_id = g_signal_connect (bwin, "motion-notify-event",
1032 									 G_CALLBACK (fullscreen_motion_notify_cb),
1033 									 NULL);
1034 		g_signal_connect (bwin->priv->toolbar, "enter-notify-event",
1035 				  G_CALLBACK (toolbar_enter_notify_cb), bwin);
1036 		g_signal_connect (bwin->priv->toolbar, "leave-notify-event",
1037 				  G_CALLBACK (toolbar_leave_notify_cb), bwin);
1038 		g_signal_connect (bwin->priv->menubar, "enter-notify-event",
1039 				  G_CALLBACK (toolbar_enter_notify_cb), bwin);
1040 		g_signal_connect (bwin->priv->menubar, "leave-notify-event",
1041 				  G_CALLBACK (toolbar_leave_notify_cb), bwin);
1042 	}
1043 	else {
1044 		gtk_window_unfullscreen (GTK_WINDOW (bwin));
1045 		g_signal_handler_disconnect (bwin, bwin->priv->fullscreen_motion_sig_id);
1046 		bwin->priv->fullscreen_motion_sig_id = 0;
1047 		g_signal_handlers_disconnect_by_func (bwin->priv->toolbar,
1048 						      G_CALLBACK (toolbar_enter_notify_cb), bwin);
1049 		g_signal_handlers_disconnect_by_func (bwin->priv->toolbar,
1050 						      G_CALLBACK (toolbar_leave_notify_cb), bwin);
1051 		g_signal_handlers_disconnect_by_func (bwin->priv->menubar,
1052 						      G_CALLBACK (toolbar_enter_notify_cb), bwin);
1053 		g_signal_handlers_disconnect_by_func (bwin->priv->menubar,
1054 						      G_CALLBACK (toolbar_leave_notify_cb), bwin);
1055 
1056 		gtk_widget_show (bwin->priv->toolbar);
1057 		gtk_widget_show (bwin->priv->menubar);
1058 		bwin->priv->toolbar_shown = TRUE;
1059 
1060 		if (bwin->priv->fullscreen_timer_id) {
1061 			g_source_remove (bwin->priv->fullscreen_timer_id);
1062 			bwin->priv->fullscreen_timer_id = 0;
1063 		}
1064 	}
1065 }
1066 
1067 static gboolean
key_press_event(GtkWidget * widget,GdkEventKey * event)1068 key_press_event (GtkWidget *widget, GdkEventKey *event)
1069 {
1070 	if ((event->keyval == GDK_KEY_Escape) &&
1071 	    browser_window_is_fullscreen (BROWSER_WINDOW (widget))) {
1072 		browser_window_set_fullscreen (BROWSER_WINDOW (widget), FALSE);
1073 		return TRUE;
1074 	}
1075 
1076 	/* parent class */
1077 	return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1078 }
1079 
1080 static gboolean
window_state_event(GtkWidget * widget,GdkEventWindowState * event)1081 window_state_event (GtkWidget *widget, GdkEventWindowState *event)
1082 {
1083 	BrowserWindow *bwin = BROWSER_WINDOW (widget);
1084 	gboolean (* window_state_event) (GtkWidget *, GdkEventWindowState *);
1085 	window_state_event = GTK_WIDGET_CLASS (parent_class)->window_state_event;
1086 
1087 	/* calling parent's method */
1088 	if (window_state_event)
1089                 window_state_event (widget, event);
1090 
1091 	if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1092                 gboolean fullscreen;
1093 		GtkWidget *wid;
1094 
1095 		wid = gtk_ui_manager_get_widget (bwin->priv->ui_manager, "/ToolBar");
1096 
1097                 fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1098 		bwin->priv->fullscreen = fullscreen;
1099 		if (fullscreen) {
1100 			gtk_toolbar_set_style (GTK_TOOLBAR (wid), GTK_TOOLBAR_ICONS);
1101 			browser_spinner_set_size (BROWSER_SPINNER (bwin->priv->spinner),
1102 						  GTK_ICON_SIZE_LARGE_TOOLBAR);
1103 		}
1104 		else {
1105 			gtk_toolbar_set_style (GTK_TOOLBAR (wid), bwin->priv->toolbar_style);
1106 			browser_spinner_set_size (BROWSER_SPINNER (bwin->priv->spinner),
1107 						  GTK_ICON_SIZE_SMALL_TOOLBAR);
1108 		}
1109 
1110 		wid = gtk_ui_manager_get_widget (bwin->priv->ui_manager, "/MenuBar");
1111 		if (fullscreen)
1112 			gtk_widget_hide (wid);
1113 		else
1114 			gtk_widget_show (wid);
1115 
1116 		g_signal_emit (G_OBJECT (bwin), browser_window_signals[FULLSCREEN_CHANGED], 0, fullscreen);
1117         }
1118 	return FALSE;
1119 }
1120 
1121 static void
window_new_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1122 window_new_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1123 {
1124 	BrowserWindow *nbwin;
1125 	BrowserConnection *bcnc;
1126 	bcnc = browser_window_get_connection (bwin);
1127 	nbwin = browser_window_new (bcnc, NULL);
1128 
1129 	browser_core_take_window (nbwin);
1130 }
1131 
1132 static void
window_new_with_cnc_cb(GtkAction * action,G_GNUC_UNUSED BrowserWindow * bwin)1133 window_new_with_cnc_cb (GtkAction *action, G_GNUC_UNUSED BrowserWindow *bwin)
1134 {
1135 	BrowserWindow *nbwin;
1136 	BrowserConnection *bcnc;
1137 	bcnc = g_object_get_data (G_OBJECT (action), "bcnc");
1138 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
1139 
1140 	nbwin = browser_window_new (bcnc, NULL);
1141 
1142 	browser_core_take_window (nbwin);
1143 }
1144 
1145 static void
connection_open_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED BrowserWindow * bwin)1146 connection_open_cb (G_GNUC_UNUSED GtkAction *action, G_GNUC_UNUSED BrowserWindow *bwin)
1147 {
1148 	browser_connection_open (NULL);
1149 }
1150 
1151 static void
connection_properties_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1152 connection_properties_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1153 {
1154 	if (BROWSER_IS_VIRTUAL_CONNECTION (bwin->priv->bcnc)) {
1155 		GtkWidget *win;
1156 		BrowserVirtualConnectionSpecs *specs;
1157 		gint res;
1158 
1159 		g_object_get (G_OBJECT (bwin->priv->bcnc), "specs", &specs, NULL);
1160 		win = connection_binding_properties_new_edit (specs); /* @specs is not modified */
1161 		gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (bwin));
1162 		gtk_widget_show (win);
1163 
1164 		res = gtk_dialog_run (GTK_DIALOG (win));
1165 		gtk_widget_hide (win);
1166 		if (res == GTK_RESPONSE_OK) {
1167 			GError *error = NULL;
1168 			if (!browser_virtual_connection_modify_specs (BROWSER_VIRTUAL_CONNECTION (bwin->priv->bcnc),
1169 								      connection_binding_properties_get_specs (CONNECTION_BINDING_PROPERTIES (win)),
1170 								      &error)) {
1171 				browser_show_error ((GtkWindow*) bwin,
1172 						    _("Error updating bound connection: %s"),
1173 						    error && error->message ? error->message : _("No detail"));
1174 				g_clear_error (&error);
1175 			}
1176 
1177 			/* update meta store */
1178 			browser_connection_update_meta_data (bwin->priv->bcnc);
1179 		}
1180 		gtk_widget_destroy (win);
1181 	}
1182 	else {
1183 		browser_connections_list_show (bwin->priv->bcnc);
1184 	}
1185 }
1186 
1187 static void
connection_bind_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1188 connection_bind_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1189 {
1190 	GtkWidget *win;
1191 	gint res;
1192 
1193 	win = connection_binding_properties_new_create (bwin->priv->bcnc);
1194 	gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (bwin));
1195 	gtk_widget_show (win);
1196 
1197 	res = gtk_dialog_run (GTK_DIALOG (win));
1198 	gtk_widget_hide (win);
1199 	if (res == GTK_RESPONSE_OK) {
1200 		BrowserConnection *bcnc;
1201 		GError *error = NULL;
1202 		bcnc = browser_virtual_connection_new (connection_binding_properties_get_specs
1203 						       (CONNECTION_BINDING_PROPERTIES (win)), &error);
1204 		if (bcnc) {
1205 			BrowserWindow *bwin;
1206 			bwin = browser_window_new (bcnc, NULL);
1207 
1208 			browser_core_take_window (bwin);
1209 			browser_core_take_connection (bcnc);
1210 		}
1211 		else {
1212 			browser_show_error ((GtkWindow*) bwin,
1213 					    _("Could not open binding connection: %s"),
1214 					    error && error->message ? error->message : _("No detail"));
1215 			g_clear_error (&error);
1216 		}
1217 	}
1218 	gtk_widget_destroy (win);
1219 }
1220 
1221 static void
connection_list_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1222 connection_list_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1223 {
1224 	browser_connections_list_show (bwin->priv->bcnc);
1225 }
1226 
1227 static void
connection_meta_update_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1228 connection_meta_update_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1229 {
1230 	browser_connection_update_meta_data (bwin->priv->bcnc);
1231 }
1232 
1233 static void
about_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1234 about_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1235 {
1236 	GdkPixbuf *icon;
1237         GtkWidget *dialog;
1238         const gchar *authors[] = {
1239                 "Vivien Malerba <malerba@gnome-db.org> (current maintainer)",
1240                 NULL
1241         };
1242         const gchar *documenters[] = {
1243                 NULL
1244         };
1245         const gchar *translator_credits = "";
1246 
1247         gchar *path;
1248         path = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser.png", NULL);
1249         icon = gdk_pixbuf_new_from_file (path, NULL);
1250         g_free (path);
1251 
1252         dialog = gtk_about_dialog_new ();
1253 	gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG (dialog), _("Database browser"));
1254         gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (dialog), PACKAGE_VERSION);
1255         gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG (dialog), "(C) 2009 - 2011 GNOME Foundation");
1256 	gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (dialog), _("Database access services for the GNOME Desktop"));
1257         gtk_about_dialog_set_license (GTK_ABOUT_DIALOG (dialog), "GNU General Public License");
1258         gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog), "http://www.gnome-db.org");
1259         gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG (dialog), authors);
1260         gtk_about_dialog_set_documenters (GTK_ABOUT_DIALOG (dialog), documenters);
1261         gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG (dialog), translator_credits);
1262         gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG (dialog), icon);
1263         g_signal_connect (G_OBJECT (dialog), "response",
1264                           G_CALLBACK (gtk_widget_destroy),
1265                           dialog);
1266         gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (bwin));
1267         gtk_widget_show (dialog);
1268 }
1269 
1270 #ifdef HAVE_GDU
1271 void
manual_cb(G_GNUC_UNUSED GtkAction * action,BrowserWindow * bwin)1272 manual_cb (G_GNUC_UNUSED GtkAction *action, BrowserWindow *bwin)
1273 {
1274 	browser_show_help (GTK_WINDOW (bwin), NULL);
1275 }
1276 #endif
1277 
1278 
1279 /**
1280  * browser_window_get_connection
1281  * @bwin: a #BrowserWindow
1282  *
1283  * Returns: the #BrowserConnection used in @bwin
1284  */
1285 BrowserConnection *
browser_window_get_connection(BrowserWindow * bwin)1286 browser_window_get_connection (BrowserWindow *bwin)
1287 {
1288 	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), NULL);
1289 	return bwin->priv->bcnc;
1290 }
1291 
1292 
1293 /*
1294  * perspective_data_new
1295  * @bwin: a #BrowserWindow in which the perspective will be
1296  * @factory: (allow-none): a #BrowserPerspectiveFactory, or %NULL
1297  *
1298  * Creates a new #PerspectiveData structure, it increases @bcnc's reference count.
1299  *
1300  * Returns: a new #PerspectiveData
1301  */
1302 static PerspectiveData *
perspective_data_new(BrowserWindow * bwin,BrowserPerspectiveFactory * factory)1303 perspective_data_new (BrowserWindow *bwin, BrowserPerspectiveFactory *factory)
1304 {
1305         PerspectiveData *pers;
1306 
1307         pers = g_new0 (PerspectiveData, 1);
1308         pers->bwin = NULL;
1309         pers->factory = factory;
1310         if (!pers->factory)
1311                 pers->factory = browser_core_get_default_factory ();
1312         pers->page_number = -1;
1313         g_assert (pers->factory);
1314 	pers->perspective_widget = g_object_ref (pers->factory->perspective_create (bwin));
1315 
1316         return pers;
1317 }
1318 
1319 /*
1320  * perspective_data_free
1321  */
1322 static void
perspective_data_free(PerspectiveData * pers)1323 perspective_data_free (PerspectiveData *pers)
1324 {
1325         if (pers->perspective_widget)
1326                 g_object_unref (pers->perspective_widget);
1327 	if (pers->customized_actions)
1328 		g_object_unref (pers->customized_actions);
1329 	g_free (pers->customized_ui);
1330         g_free (pers);
1331 }
1332 
1333 typedef struct {
1334 	BrowserWindow *bwin;
1335 	guint cid; /* statusbar context id */
1336 	guint msgid; /* statusbar message id */
1337 } StatusData;
1338 
1339 static gboolean
status_auto_pop_timeout(StatusData * sd)1340 status_auto_pop_timeout (StatusData *sd)
1341 {
1342 	if (sd->bwin) {
1343 		g_object_remove_weak_pointer (G_OBJECT (sd->bwin), (gpointer*) &(sd->bwin));
1344 		gtk_statusbar_remove (GTK_STATUSBAR (sd->bwin->priv->statusbar), sd->cid, sd->msgid);
1345 	}
1346 	g_free (sd);
1347 
1348 	return FALSE; /* remove source */
1349 }
1350 
1351 /**
1352  * browser_window_push_status
1353  * @bwin: a #BrowserWindow
1354  * @context: textual description of what context the new message is being used in
1355  * @text: textual message
1356  * @auto_clear: %TRUE if the message has to disappear after a while
1357  *
1358  * Pushes a new message onto @bwin's statusbar's stack.
1359  *
1360  * Returns: the message ID, see gtk_statusbar_push(), or %0 if @auto_clear is %TRUE
1361  */
1362 guint
browser_window_push_status(BrowserWindow * bwin,const gchar * context,const gchar * text,gboolean auto_clear)1363 browser_window_push_status (BrowserWindow *bwin, const gchar *context, const gchar *text, gboolean auto_clear)
1364 {
1365 	guint cid;
1366 	guint retval;
1367 
1368 	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), 0);
1369 	g_return_val_if_fail (context, 0);
1370 	g_return_val_if_fail (text, 0);
1371 
1372 	cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar), context);
1373 	retval = gtk_statusbar_push (GTK_STATUSBAR (bwin->priv->statusbar), cid, text);
1374 	if (auto_clear) {
1375 		StatusData *sd;
1376 		sd = g_new0 (StatusData, 1);
1377 		sd->bwin = bwin;
1378 		g_object_add_weak_pointer (G_OBJECT (sd->bwin), (gpointer*) &(sd->bwin));
1379 		sd->cid = cid;
1380 		sd->msgid = retval;
1381 		g_timeout_add_seconds (5, (GSourceFunc) status_auto_pop_timeout, sd);
1382 		return 0;
1383 	}
1384 	else
1385 		return retval;
1386 }
1387 
1388 /**
1389  * browser_window_pop_status
1390  * @bwin: a #BrowserWindow
1391  * @context: textual description of what context the message is being used in
1392  *
1393  * Removes the first message in the @bwin's statusbar's stack with the given context.
1394  */
1395 void
browser_window_pop_status(BrowserWindow * bwin,const gchar * context)1396 browser_window_pop_status (BrowserWindow *bwin, const gchar *context)
1397 {
1398 	guint cid;
1399 
1400 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
1401 	g_return_if_fail (context);
1402 
1403 	cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar), context);
1404 	gtk_statusbar_pop (GTK_STATUSBAR (bwin->priv->statusbar), cid);
1405 }
1406 
1407 /**
1408  * browser_window_show_notice_printf
1409  * @bwin: a #BrowserWindow
1410  * @context: textual description of what context the message is being used in
1411  * @format: the text to display
1412  *
1413  * Make @bwin display a notice
1414  */
1415 void
browser_window_show_notice_printf(BrowserWindow * bwin,GtkMessageType type,const gchar * context,const gchar * format,...)1416 browser_window_show_notice_printf (BrowserWindow *bwin, GtkMessageType type, const gchar *context,
1417 				   const gchar *format, ...)
1418 {
1419 	va_list args;
1420         gchar sz[2048];
1421 
1422 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
1423 
1424         /* build the message string */
1425         va_start (args, format);
1426         vsnprintf (sz, sizeof (sz), format, args);
1427         va_end (args);
1428 	browser_window_show_notice (bwin, type, context, sz);
1429 }
1430 
1431 
1432 static void
info_bar_response_cb(GtkInfoBar * ibar,G_GNUC_UNUSED gint response,BrowserWindow * bwin)1433 info_bar_response_cb (GtkInfoBar *ibar, G_GNUC_UNUSED gint response, BrowserWindow *bwin)
1434 {
1435 	bwin->priv->notif_widgets = g_slist_remove (bwin->priv->notif_widgets, ibar);
1436 	gtk_widget_destroy ((GtkWidget*) ibar);
1437 }
1438 
1439 /* hash table to remain which context notices have to be hidden: key=context, value=GINT_TO_POINTER (1) */
1440 static GHashTable *hidden_contexts = NULL;
1441 static void
hidden_contexts_foreach_save(const gchar * context,G_GNUC_UNUSED gint value,xmlNodePtr root)1442 hidden_contexts_foreach_save (const gchar *context, G_GNUC_UNUSED gint value, xmlNodePtr root)
1443 {
1444 	xmlNewChild (root, NULL, BAD_CAST "hide-notice", BAD_CAST context);
1445 }
1446 
1447 static void
hide_notice_toggled_cb(GtkToggleButton * toggle,gchar * context)1448 hide_notice_toggled_cb (GtkToggleButton *toggle, gchar *context)
1449 {
1450 	g_assert (hidden_contexts);
1451 	if (gtk_toggle_button_get_active (toggle))
1452 		g_hash_table_insert (hidden_contexts, g_strdup (context), GINT_TO_POINTER (TRUE));
1453 	else
1454 		g_hash_table_remove (hidden_contexts, context);
1455 
1456 	/* save config */
1457 	xmlDocPtr doc;
1458 	xmlNodePtr root;
1459 	xmlChar *xml_contents;
1460 	gint size;
1461 	doc = xmlNewDoc (BAD_CAST "1.0");
1462 	root = xmlNewNode (NULL, BAD_CAST "gda-browser-preferences");
1463 	xmlDocSetRootElement (doc, root);
1464 	g_hash_table_foreach (hidden_contexts, (GHFunc) hidden_contexts_foreach_save, root);
1465 	xmlDocDumpFormatMemory (doc, &xml_contents, &size, 1);
1466 	xmlFreeDoc (doc);
1467 
1468 	/* save to disk */
1469 	gchar *confdir, *conffile = NULL;
1470 	GError *lerror = NULL;
1471 	confdir = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir(), "gda-browser", NULL);
1472 	if (!g_file_test (confdir, G_FILE_TEST_EXISTS)) {
1473 		if (g_mkdir_with_parents (confdir, 0700)) {
1474 			g_warning ("Can't create configuration directory '%s' to save preferences.",
1475 				   confdir);
1476 			goto out;
1477 		}
1478 	}
1479 	conffile = g_build_filename (confdir, "preferences.xml", NULL);
1480 	if (! g_file_set_contents (conffile, (gchar *) xml_contents, size, &lerror)) {
1481 		g_warning ("Can't save preferences file '%s': %s", conffile,
1482 			   lerror && lerror->message ? lerror->message : _("No detail"));
1483 		g_clear_error (&lerror);
1484 	}
1485 
1486  out:
1487 	xmlFree (xml_contents);
1488 	g_free (confdir);
1489 	g_free (conffile);
1490 }
1491 
1492 static void
load_preferences(void)1493 load_preferences (void)
1494 {
1495 	/* load preferences */
1496 	xmlDocPtr doc;
1497 	xmlNodePtr root, node;
1498 	gchar *conffile;
1499 	conffile = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir(),
1500 				 "gda-browser", "preferences.xml", NULL);
1501 	if (!g_file_test (conffile, G_FILE_TEST_EXISTS)) {
1502 		g_free (conffile);
1503 		return;
1504 	}
1505 
1506 	doc = xmlParseFile (conffile);
1507 	if (!doc) {
1508 		g_warning ("Error loading preferences from file '%s'", conffile);
1509 		g_free (conffile);
1510 		return;
1511 	}
1512 	g_free (conffile);
1513 
1514 	root = xmlDocGetRootElement (doc);
1515 	for (node = root->children; node; node = node->next) {
1516 		xmlChar *contents;
1517 		if (strcmp ((gchar *) node->name, "hide-notice"))
1518 			continue;
1519 		contents = xmlNodeGetContent (node);
1520 		if (contents) {
1521 			g_hash_table_insert (hidden_contexts, g_strdup ((gchar*) contents),
1522 					     GINT_TO_POINTER (TRUE));
1523 			xmlFree (contents);
1524 		}
1525 	}
1526 	xmlFreeDoc (doc);
1527 }
1528 
1529 
1530 /**
1531  * browser_window_show_notice
1532  * @bwin: a #BrowserWindow
1533  * @context: textual description of what context the message is being used in
1534  * @text: the information's text
1535  *
1536  * Makes @bwin display a notice
1537  */
1538 void
browser_window_show_notice(BrowserWindow * bwin,GtkMessageType type,const gchar * context,const gchar * text)1539 browser_window_show_notice (BrowserWindow *bwin, GtkMessageType type, const gchar *context, const gchar *text)
1540 {
1541 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
1542 	gboolean hide = FALSE;
1543 
1544 	if ((type != GTK_MESSAGE_ERROR) && context) {
1545 		if (!hidden_contexts) {
1546 			hidden_contexts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1547 			load_preferences ();
1548 		}
1549 		hide = GPOINTER_TO_INT (g_hash_table_lookup (hidden_contexts, context));
1550 	}
1551 
1552 	if (hide) {
1553 		gchar *ptr, *tmp;
1554 		tmp = g_strdup (text);
1555 		for (ptr = tmp; *ptr && (*ptr != '\n'); ptr++);
1556 		if (*ptr) {
1557 			*ptr = '.'; ptr++;
1558 			*ptr = '.'; ptr++;
1559 			*ptr = '.'; ptr++;
1560 			*ptr = 0;
1561 		}
1562 		browser_window_push_status (bwin, "SupportNotice", tmp, TRUE);
1563 		g_free (tmp);
1564 	}
1565 	else {
1566 		if (bwin->priv->notif_widgets) {
1567 			GSList *list;
1568 			for (list = bwin->priv->notif_widgets; list; list = list->next) {
1569 				const gchar *c1, *t1;
1570 				c1 = g_object_get_data (G_OBJECT (list->data), "context");
1571 				t1 = g_object_get_data (G_OBJECT (list->data), "text");
1572 				if (((c1 && context && !strcmp (c1, context)) || (!c1 && !context)) &&
1573 				    ((t1 && text && !strcmp (t1, text)) || (!t1 && !text)))
1574 					break;
1575 			}
1576 			if (list) {
1577 				GtkWidget *wid;
1578 				wid = GTK_WIDGET (list->data);
1579 				g_object_ref ((GObject*) wid);
1580 				gtk_container_remove (GTK_CONTAINER (bwin->priv->notif_box), wid);
1581 				gtk_box_pack_end (GTK_BOX (bwin->priv->notif_box), wid, TRUE, TRUE, 0);
1582 				g_object_unref ((GObject*) wid);
1583 				return;
1584 			}
1585 		}
1586 
1587 		GtkWidget *cb = NULL;
1588 		if (context && (type == GTK_MESSAGE_INFO)) {
1589 			cb = gtk_check_button_new_with_label (_("Don't show this message again"));
1590 			g_signal_connect_data (cb, "toggled",
1591 					       G_CALLBACK (hide_notice_toggled_cb), g_strdup (context),
1592 					       (GClosureNotify) g_free, 0);
1593 		}
1594 
1595 		/* use a GtkInfoBar */
1596 		GtkWidget *ibar, *content_area, *label;
1597 
1598 		ibar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, 1, NULL);
1599 		if (context)
1600 			g_object_set_data_full (G_OBJECT (ibar), "context", g_strdup (context), g_free);
1601 		if (text)
1602 			g_object_set_data_full (G_OBJECT (ibar), "text", g_strdup (text), g_free);
1603 		gtk_info_bar_set_message_type (GTK_INFO_BAR (ibar), type);
1604 		label = gtk_label_new ("");
1605 		gtk_label_set_markup (GTK_LABEL (label), text);
1606 		gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1607 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
1608 		content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (ibar));
1609 		if (cb) {
1610 			GtkWidget *box;
1611 			box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1612 			gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
1613 			gtk_box_pack_start (GTK_BOX (box), cb, FALSE, FALSE, 0);
1614 			gtk_container_add (GTK_CONTAINER (content_area), box);
1615 			gtk_widget_show_all (box);
1616 		}
1617 		else {
1618 			gtk_container_add (GTK_CONTAINER (content_area), label);
1619 			gtk_widget_show (label);
1620 		}
1621 		g_signal_connect (ibar, "response",
1622 				  G_CALLBACK (info_bar_response_cb), bwin);
1623 		gtk_box_pack_end (GTK_BOX (bwin->priv->notif_box), ibar, TRUE, TRUE, 0);
1624 		bwin->priv->notif_widgets = g_slist_append (bwin->priv->notif_widgets, ibar);
1625 		if (g_slist_length (bwin->priv->notif_widgets) > 2) {
1626 			gtk_widget_destroy (GTK_WIDGET (bwin->priv->notif_widgets->data));
1627 			bwin->priv->notif_widgets = g_slist_delete_link (bwin->priv->notif_widgets,
1628 									 bwin->priv->notif_widgets);
1629 		}
1630 		gtk_widget_show (ibar);
1631 	}
1632 }
1633 
1634 
1635 /**
1636  * browser_window_customize_perspective_ui
1637  * @bwin: a #BrowserWindow
1638  * @bpers: the #BrowserPerspective concerned
1639  * @actions_group: (allow-none): a #GtkActionGroup object, or %NULL
1640  * @ui_info: (allow-none): a merge UI string, or %NULL. See gtk_ui_manager_add_ui_from_string()
1641  *
1642  * Customizes a UI specific to the @bpers perspective. Any
1643  * previous customization is removed, replaced by the new requested one.
1644  *
1645  * If @actions_group is %NULL then any it simply removes the customization.
1646  */
1647 void
browser_window_customize_perspective_ui(BrowserWindow * bwin,BrowserPerspective * bpers,GtkActionGroup * actions_group,const gchar * ui_info)1648 browser_window_customize_perspective_ui (BrowserWindow *bwin, BrowserPerspective *bpers,
1649 					 GtkActionGroup *actions_group,
1650 					 const gchar *ui_info)
1651 {
1652 	PerspectiveData *pdata = NULL;
1653 	GSList *list;
1654 
1655 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
1656 	g_return_if_fail (IS_BROWSER_PERSPECTIVE (bpers));
1657 
1658 	for (list = bwin->priv->perspectives; list; list = list->next) {
1659 		if (PERSPECTIVE_DATA (list->data)->perspective_widget == bpers) {
1660 			pdata = PERSPECTIVE_DATA (list->data);
1661 			break;
1662 		}
1663 	}
1664 	if (! pdata)
1665 		return;
1666 
1667 	/* cleanups */
1668 	if (pdata->customized_merge_id) {
1669 		gtk_ui_manager_remove_ui (bwin->priv->ui_manager, pdata->customized_merge_id);
1670 		pdata->customized_merge_id = 0;
1671 	}
1672 	if (pdata->customized_actions) {
1673 		gtk_ui_manager_remove_action_group (bwin->priv->ui_manager, pdata->customized_actions);
1674 		g_object_unref (pdata->customized_actions);
1675 		pdata->customized_actions = NULL;
1676 	}
1677 	g_free (pdata->customized_ui);
1678 	pdata->customized_ui = NULL;
1679 	gtk_ui_manager_ensure_update (bwin->priv->ui_manager);
1680 
1681 	if (actions_group) {
1682 		g_return_if_fail (GTK_IS_ACTION_GROUP (actions_group));
1683 		gtk_action_group_set_translation_domain (actions_group, GETTEXT_PACKAGE);
1684 		gtk_ui_manager_insert_action_group (bwin->priv->ui_manager, actions_group, 0);
1685 		pdata->customized_actions = g_object_ref (actions_group);
1686 	}
1687 	if (ui_info) {
1688 		pdata->customized_ui = g_strdup (ui_info);
1689 		pdata->customized_merge_id = gtk_ui_manager_add_ui_from_string (bwin->priv->ui_manager,
1690 										pdata->customized_ui,
1691 										-1, NULL);
1692 	}
1693 }
1694 
1695 /**
1696  * browser_window_change_perspective
1697  * @bwin: a #BrowserWindow
1698  * @perspective: the name of the perspective to change to
1699  *
1700  * Make @bwin switch to the perspective named @perspective
1701  *
1702  * Returns: a pointer to the #BrowserPerspective, or %NULL if not found
1703  */
1704 BrowserPerspective *
browser_window_change_perspective(BrowserWindow * bwin,const gchar * perspective)1705 browser_window_change_perspective (BrowserWindow *bwin, const gchar *perspective)
1706 {
1707 	BrowserPerspectiveFactory *bpf;
1708 	BrowserPerspective *bpers = NULL;
1709 	PerspectiveData *current_pdata;
1710 
1711 	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), NULL);
1712 	g_return_val_if_fail (perspective, NULL);
1713 
1714 	current_pdata = bwin->priv->current_perspective;
1715 
1716 	bpf = browser_core_get_factory (perspective);
1717 	if (!bpf)
1718 		return NULL;
1719 	GList *actions, *list;
1720 	actions = gtk_action_group_list_actions (bwin->priv->perspectives_actions);
1721 	for (list = actions; list; list = list->next) {
1722 		GtkAction *action = (GtkAction *) list->data;
1723 		BrowserPerspectiveFactory *pf;
1724 		pf = BROWSER_PERSPECTIVE_FACTORY (g_object_get_data (G_OBJECT (action), "pers"));
1725 		if (pf == bpf) {
1726 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1727 			PerspectiveData *pdata = bwin->priv->current_perspective;
1728 			if (pdata && ! g_ascii_strcasecmp (pdata->factory->perspective_name, perspective))
1729 				bpers = pdata->perspective_widget;
1730 			break;
1731 		}
1732 	}
1733 	g_list_free (actions);
1734 
1735 	gchar *tmp;
1736 	tmp = g_markup_printf_escaped (_("The current perspective has changed to the '%s' perspective, you "
1737 					 "can switch back to previous perspective through the "
1738 					 "'Perspective/%s' menu, or using the '%s' shortcut"),
1739 				       bwin->priv->current_perspective->factory->perspective_name,
1740 				       current_pdata->factory->perspective_name,
1741 				       current_pdata->factory->menu_shortcut);
1742 
1743 
1744 	browser_window_show_notice (bwin, GTK_MESSAGE_INFO, "Perspective change", tmp);
1745 	g_free (tmp);
1746 
1747 	return bpers;
1748 }
1749 
1750 /**
1751  * browser_window_is_fullscreen
1752  * @bwin: a #BrowserWindow
1753  *
1754  * Returns: %TRUE if @bwin is fullscreen
1755  */
1756 gboolean
browser_window_is_fullscreen(BrowserWindow * bwin)1757 browser_window_is_fullscreen (BrowserWindow *bwin)
1758 {
1759 	g_return_val_if_fail (BROWSER_IS_WINDOW (bwin), FALSE);
1760 	return bwin->priv->fullscreen;
1761 }
1762 
1763 /**
1764  * browser_window_set_fullscreen
1765  * @bwin: a #BrowserWindow
1766  * @fullscreen:
1767  *
1768  * Requires @bwin to be fullscreen if @fullscreen is %TRUE
1769  */
1770 void
browser_window_set_fullscreen(BrowserWindow * bwin,gboolean fullscreen)1771 browser_window_set_fullscreen (BrowserWindow *bwin, gboolean fullscreen)
1772 {
1773 	GtkAction *action;
1774 	g_return_if_fail (BROWSER_IS_WINDOW (bwin));
1775 
1776 	action = gtk_action_group_get_action (bwin->priv->agroup, "WindowFullScreen");
1777 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), fullscreen);
1778 }
1779