1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
4 *
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8 *
9 * Nautilus-Actions is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * Nautilus-Actions is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Nautilus-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * Authors:
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <glib/gi18n.h>
35
36 #include <core/na-io-provider.h>
37 #include <core/na-iprefs.h>
38
39 #include "nact-application.h"
40 #include "nact-main-statusbar.h"
41 #include "nact-main-toolbar.h"
42 #include "nact-main-tab.h"
43 #include "nact-menubar-priv.h"
44 #include "nact-sort-buttons.h"
45 #include "nact-tree-view.h"
46
47 /* private class data
48 */
49 struct _NactMenubarClassPrivate {
50 void *empty; /* so that gcc -pedantic is happy */
51 };
52
53 /* private instance data is externalized in nact-menubar-priv.h
54 * in order to be avaible to all nact-menubar-derived files.
55 */
56
57 static const GtkActionEntry entries[] = {
58
59 { "FileMenu", NULL, N_( "_File" ) },
60 { "EditMenu", NULL, N_( "_Edit" ) },
61 { "ViewMenu", NULL, N_( "_View" ) },
62 { "ViewToolbarMenu", NULL, N_( "_Toolbars" ) },
63 { "ToolsMenu", NULL, N_( "_Tools" ) },
64 { "MaintainerMenu", NULL, N_( "_Maintainer" ) },
65 { "HelpMenu", NULL, N_( "_Help" ) },
66 { "NotebookLabelMenu", NULL, N_( "Notebook _tabs" ) },
67
68 { "NewMenuItem", NULL, N_( "New _menu" ), NULL,
69 /* i18n: tooltip displayed in the status bar when selecting the 'New menu' item */
70 N_( "Insert a new menu at the current position" ),
71 G_CALLBACK( nact_menubar_file_on_new_menu ) },
72 { "NewActionItem", GTK_STOCK_NEW, N_( "_New action" ), NULL,
73 /* i18n: tooltip displayed in the status bar when selecting the 'New action' item */
74 N_( "Define a new action" ),
75 G_CALLBACK( nact_menubar_file_on_new_action ) },
76 { "NewProfileItem", NULL, N_( "New _profile" ), NULL,
77 /* i18n: tooltip displayed in the status bar when selecting the 'New profile' item */
78 N_( "Define a new profile attached to the current action" ),
79 G_CALLBACK( nact_menubar_file_on_new_profile ) },
80 { "SaveItem", GTK_STOCK_SAVE, NULL, NULL,
81 /* i18n: tooltip displayed in the status bar when selecting 'Save' item */
82 N_( "Record all the modified actions. Invalid actions will be silently ignored" ),
83 G_CALLBACK( nact_menubar_file_on_save ) },
84 { "QuitItem", GTK_STOCK_QUIT, NULL, NULL,
85 /* i18n: tooltip displayed in the status bar when selecting 'Quit' item */
86 N_( "Quit the application" ),
87 G_CALLBACK( nact_menubar_file_on_quit ) },
88 { "CutItem" , GTK_STOCK_CUT, NULL, NULL,
89 /* i18n: tooltip displayed in the status bar when selecting the Cut item */
90 N_( "Cut the selected item(s) to the clipboard" ),
91 G_CALLBACK( nact_menubar_edit_on_cut ) },
92 { "CopyItem" , GTK_STOCK_COPY, NULL, NULL,
93 /* i18n: tooltip displayed in the status bar when selecting the Copy item */
94 N_( "Copy the selected item(s) to the clipboard" ),
95 G_CALLBACK( nact_menubar_edit_on_copy ) },
96 { "PasteItem" , GTK_STOCK_PASTE, NULL, NULL,
97 /* i18n: tooltip displayed in the status bar when selecting the Paste item */
98 N_( "Insert the content of the clipboard just before the current position" ),
99 G_CALLBACK( nact_menubar_edit_on_paste ) },
100 { "PasteIntoItem" , NULL, N_( "Paste _into" ), "<Shift><Ctrl>V",
101 /* i18n: tooltip displayed in the status bar when selecting the Paste Into item */
102 N_( "Insert the content of the clipboard as first child of the current item" ),
103 G_CALLBACK( nact_menubar_edit_on_paste_into ) },
104 { "DuplicateItem" , NULL, N_( "D_uplicate" ), "",
105 /* i18n: tooltip displayed in the status bar when selecting the Duplicate item */
106 N_( "Duplicate the selected item(s)" ),
107 G_CALLBACK( nact_menubar_edit_on_duplicate ) },
108 { "DeleteItem", GTK_STOCK_DELETE, NULL, "Delete",
109 /* i18n: tooltip displayed in the status bar when selecting the Delete item */
110 N_( "Delete the selected item(s)" ),
111 G_CALLBACK( nact_menubar_edit_on_delete ) },
112 { "ReloadActionsItem", GTK_STOCK_REFRESH, N_( "_Reload the items" ), "F5",
113 /* i18n: tooltip displayed in the status bar when selecting the 'Reload' item */
114 N_( "Cancel your current modifications and reload the initial list of menus and actions" ),
115 G_CALLBACK( nact_menubar_edit_on_reload ) },
116 { "PreferencesItem", GTK_STOCK_PREFERENCES, NULL, NULL,
117 /* i18n: tooltip displayed in the status bar when selecting the 'Preferences' item */
118 N_( "Edit your preferences" ),
119 G_CALLBACK( nact_menubar_edit_on_prefererences ) },
120 { "ExpandAllItem" , NULL, N_( "_Expand all" ), NULL,
121 /* i18n: tooltip displayed in the status bar when selecting the Expand all item */
122 N_( "Entirely expand the items hierarchy" ),
123 G_CALLBACK( nact_menubar_view_on_expand_all ) },
124 { "CollapseAllItem" , NULL, N_( "_Collapse all" ), NULL,
125 /* i18n: tooltip displayed in the status bar when selecting the Collapse all item */
126 N_( "Entirely collapse the items hierarchy" ),
127 G_CALLBACK( nact_menubar_view_on_collapse_all ) },
128
129 { "ImportItem" , GTK_STOCK_CONVERT, N_( "_Import assistant..." ), "",
130 /* i18n: tooltip displayed in the status bar when selecting the Import item */
131 N_( "Import one or more actions from external files into your configuration" ),
132 G_CALLBACK( nact_menubar_tools_on_import ) },
133 { "ExportItem", NULL, N_( "E_xport assistant..." ), NULL,
134 /* i18n: tooltip displayed in the status bar when selecting the Export item */
135 N_( "Export one or more actions from your configuration to external files" ),
136 G_CALLBACK( nact_menubar_tools_on_export ) },
137
138 { "DumpSelectionItem", NULL, N_( "_Dump the selection" ), NULL,
139 /* i18n: tooltip displayed in the status bar when selecting the Dump selection item */
140 N_( "Recursively dump selected items" ),
141 G_CALLBACK( nact_menubar_maintainer_on_dump_selection ) },
142 { "BriefTreeStoreDumpItem", NULL, N_( "_Brief tree store dump" ), NULL,
143 /* i18n: tooltip displayed in the status bar when selecting the BriefTreeStoreDump item */
144 N_( "Briefly dump the tree store" ),
145 G_CALLBACK( nact_menubar_maintainer_on_brief_tree_store_dump ) },
146 { "ListModifiedItems", NULL, N_( "_List modified items" ), NULL,
147 /* i18n: tooltip displayed in the status bar when selecting the ListModifiedItems item */
148 N_( "List the modified items" ),
149 G_CALLBACK( nact_menubar_maintainer_on_list_modified_items ) },
150 { "DumpClipboard", NULL, N_( "_Dump the clipboard" ), NULL,
151 /* i18n: tooltip displayed in the status bar when selecting the DumpClipboard item */
152 N_( "Dump the content of the clipboard object" ),
153 G_CALLBACK( nact_menubar_maintainer_on_dump_clipboard ) },
154 { "FunctionTest", NULL, "_Test a function", NULL,
155 "Test a function (see nact-menubar-maintainer.c",
156 G_CALLBACK( nact_menubar_maintainer_on_test_function ) },
157
158 { "HelpItem" , GTK_STOCK_HELP, N_( "Contents" ), "F1",
159 /* i18n: tooltip displayed in the status bar when selecting the Help item */
160 N_( "Display help about this program" ),
161 G_CALLBACK( nact_menubar_help_on_help ) },
162 { "AboutItem", GTK_STOCK_ABOUT, NULL, NULL,
163 /* i18n: tooltip displayed in the status bar when selecting the About item */
164 N_( "Display informations about this program" ),
165 G_CALLBACK( nact_menubar_help_on_about ) },
166 };
167
168 static const GtkToggleActionEntry toolbar_entries[] = {
169
170 { "ViewFileToolbarItem", NULL, N_( "_File" ), NULL,
171 /* i18n: tooltip displayed in the status bar when selecting the 'View File toolbar' item */
172 N_( "Display the File toolbar" ),
173 G_CALLBACK( nact_menubar_view_on_toolbar_file ), FALSE },
174 { "ViewEditToolbarItem", NULL, N_( "_Edit" ), NULL,
175 /* i18n: tooltip displayed in the status bar when selecting the 'View Edit toolbar' item */
176 N_( "Display the Edit toolbar" ),
177 G_CALLBACK( nact_menubar_view_on_toolbar_edit ), FALSE },
178 { "ViewToolsToolbarItem", NULL, N_( "_Tools" ), NULL,
179 /* i18n: tooltip displayed in the status bar when selecting 'View Tools toolbar' item */
180 N_( "Display the Tools toolbar" ),
181 G_CALLBACK( nact_menubar_view_on_toolbar_tools ), FALSE },
182 { "ViewHelpToolbarItem", NULL, N_( "_Help" ), NULL,
183 /* i18n: tooltip displayed in the status bar when selecting 'View Help toolbar' item */
184 N_( "Display the Help toolbar" ),
185 G_CALLBACK( nact_menubar_view_on_toolbar_help ), FALSE },
186 };
187
188 static const GtkRadioActionEntry tabs_pos_entries[] = {
189
190 { "TabsPosLeftItem", NULL, N_( "On the _left" ), NULL,
191 /* i18n: tooltip displayed in the status bar when selecting the 'Set tabs position on the left' item */
192 N_( "Display the notebook tabs on the left side" ),
193 GTK_POS_LEFT },
194 { "TabsPosRightItem", NULL, N_( "On the _right" ), NULL,
195 /* i18n: tooltip displayed in the status bar when selecting the 'Set tabs position on the right' item */
196 N_( "Display the notebook tabs on the right side" ),
197 GTK_POS_RIGHT },
198 { "TabsPosTopItem", NULL, N_( "On the _top" ), NULL,
199 /* i18n: tooltip displayed in the status bar when selecting 'Set tabs position on the top' item */
200 N_( "Display the notebook tabs on the top side" ),
201 GTK_POS_TOP },
202 { "TabsPosBottomItem", NULL, N_( "On the _bottom" ), NULL,
203 /* i18n: tooltip displayed in the status bar when selecting 'Set tabs position on the bottom' item */
204 N_( "Display the notebook tabs on the bottom side" ),
205 GTK_POS_BOTTOM },
206 };
207
208 #define MENUBAR_PROP_STATUS_CONTEXT "menubar-status-context"
209 #define MENUBAR_PROP_MAIN_STATUS_CONTEXT "menubar-main-status-context"
210
211 /* signals
212 */
213 enum {
214 UPDATE_SENSITIVITIES,
215 LAST_SIGNAL
216 };
217
218 static const gchar *st_ui_menubar_actions = PKGUIDIR "/nautilus-actions-config-tool.actions";
219 static const gchar *st_ui_maintainer_actions = PKGUIDIR "/nautilus-actions-maintainer.actions";
220
221 static gint st_signals[ LAST_SIGNAL ] = { 0 };
222 static GObjectClass *st_parent_class = NULL;
223
224 static GType register_type( void );
225 static void class_init( NactMenubarClass *klass );
226 static void instance_init( GTypeInstance *instance, gpointer klass );
227 static void instance_dispose( GObject *application );
228 static void instance_finalize( GObject *application );
229
230 static void on_base_initialize_window( BaseWindow *window, gpointer user_data );
231 static void on_ui_manager_proxy_connect( GtkUIManager *ui_manager, GtkAction *action, GtkWidget *proxy, BaseWindow *window );
232 static void on_menu_item_selected( GtkMenuItem *proxy, BaseWindow *window );
233 static void on_menu_item_deselected( GtkMenuItem *proxy, BaseWindow *window );
234
235 static void on_open_context_menu( BaseWindow *window, GdkEventButton *event, const gchar *popup, gpointer user_data );
236 static void on_popup_selection_done( GtkMenuShell *menushell, BaseWindow *window );
237 static void on_tree_view_count_changed( BaseWindow *window, gboolean reset, gint menus, gint actions, gint profiles );
238 static void on_tree_view_focus_in( BaseWindow *window, gpointer user_data );
239 static void on_tree_view_focus_out( BaseWindow *window, gpointer user_data );
240 static void on_tree_view_modified_status_changed( BaseWindow *window, gboolean is_modified, gpointer user_data );
241 static void on_tree_view_selection_changed( BaseWindow *window, GList *selected, gpointer user_data );
242
243 static void on_update_sensitivities( NactMenubar *bar, BaseWindow *window );
244
245 GType
nact_menubar_get_type(void)246 nact_menubar_get_type( void )
247 {
248 static GType type = 0;
249
250 if( !type ){
251 type = register_type();
252 }
253
254 return( type );
255 }
256
257 static GType
register_type(void)258 register_type( void )
259 {
260 static const gchar *thisfn = "nact_menubar_register_type";
261 GType type;
262
263 static GTypeInfo info = {
264 sizeof( NactMenubarClass ),
265 ( GBaseInitFunc ) NULL,
266 ( GBaseFinalizeFunc ) NULL,
267 ( GClassInitFunc ) class_init,
268 NULL,
269 NULL,
270 sizeof( NactMenubar ),
271 0,
272 ( GInstanceInitFunc ) instance_init
273 };
274
275 g_debug( "%s", thisfn );
276
277 type = g_type_register_static( G_TYPE_OBJECT, "NactMenubar", &info, 0 );
278
279 return( type );
280 }
281
282 static void
class_init(NactMenubarClass * klass)283 class_init( NactMenubarClass *klass )
284 {
285 static const gchar *thisfn = "nact_menubar_class_init";
286 GObjectClass *object_class;
287
288 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
289
290 st_parent_class = g_type_class_peek_parent( klass );
291
292 object_class = G_OBJECT_CLASS( klass );
293 object_class->dispose = instance_dispose;
294 object_class->finalize = instance_finalize;
295
296 /**
297 * NactMenubar::menubar-signal-update-sensitivities
298 *
299 * This signal is emitted by the NactMenubar object on itself when
300 * menu items sensitivities have to be refreshed.
301 *
302 * Signal arg.: None
303 *
304 * Handler prototype:
305 * void ( *handler )( NactMenubar *bar, gpointer user_data );
306 */
307 st_signals[ UPDATE_SENSITIVITIES ] = g_signal_new(
308 MENUBAR_SIGNAL_UPDATE_SENSITIVITIES,
309 NACT_TYPE_MENUBAR,
310 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
311 0, /* no default handler */
312 NULL,
313 NULL,
314 g_cclosure_marshal_VOID__VOID,
315 G_TYPE_NONE,
316 0 );
317
318 klass->private = g_new0( NactMenubarClassPrivate, 1 );
319 }
320
321 static void
instance_init(GTypeInstance * instance,gpointer klass)322 instance_init( GTypeInstance *instance, gpointer klass )
323 {
324 static const gchar *thisfn = "nact_menubar_instance_init";
325 NactMenubar *self;
326
327 g_return_if_fail( NACT_IS_MENUBAR( instance ));
328
329 g_debug( "%s: instance=%p (%s), klass=%p",
330 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
331
332 self = NACT_MENUBAR( instance );
333
334 self->private = g_new0( NactMenubarPrivate, 1 );
335
336 self->private->dispose_has_run = FALSE;
337 }
338
339 static void
instance_dispose(GObject * object)340 instance_dispose( GObject *object )
341 {
342 static const gchar *thisfn = "nact_menubar_instance_dispose";
343 NactMenubarPrivate *priv;
344
345 g_return_if_fail( NACT_IS_MENUBAR( object ));
346
347 priv = NACT_MENUBAR( object )->private;
348
349 if( !priv->dispose_has_run ){
350
351 g_debug( "%s: object=%p (%s)",
352 thisfn,
353 ( void * ) object, G_OBJECT_TYPE_NAME( object ));
354
355 priv->dispose_has_run = TRUE;
356
357 base_window_signal_disconnect(
358 priv->window,
359 priv->update_sensitivities_handler_id );
360
361 g_object_unref( priv->action_group );
362 g_object_unref( priv->notebook_group );
363 g_object_unref( priv->ui_manager );
364 g_object_unref( priv->sort_buttons );
365
366 if( priv->selected_items ){
367 g_list_free( priv->selected_items );
368 }
369
370 /* chain up to the parent class */
371 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
372 G_OBJECT_CLASS( st_parent_class )->dispose( object );
373 }
374 }
375 }
376
377 static void
instance_finalize(GObject * instance)378 instance_finalize( GObject *instance )
379 {
380 static const gchar *thisfn = "nact_menubar_instance_finalize";
381 NactMenubar *self;
382
383 g_return_if_fail( NACT_IS_MENUBAR( instance ));
384
385 g_debug( "%s: instance=%p (%s)", thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
386
387 self = NACT_MENUBAR( instance );
388
389 g_free( self->private );
390
391 /* chain call to parent class */
392 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
393 G_OBJECT_CLASS( st_parent_class )->finalize( instance );
394 }
395 }
396
397 /**
398 * nact_menubar_new:
399 * @window: the window which embeds the menubar, usually the #NactMainWindow.
400 *
401 * The created menubar attachs itself to the @window; it also connect a weak
402 * reference to this same @window, thus automatically g_object_unref() -ing
403 * itself at @window finalization time.
404 *
405 * The menubar also takes advantage of #BaseWindow messages to initialize
406 * its Gtk widgets.
407 *
408 * Returns: a new #NactMenubar object.
409 */
410 NactMenubar *
nact_menubar_new(BaseWindow * window)411 nact_menubar_new( BaseWindow *window )
412 {
413 NactMenubar *bar;
414
415 g_return_val_if_fail( BASE_IS_WINDOW( window ), NULL );
416
417 bar = g_object_new( NACT_TYPE_MENUBAR, NULL );
418
419 bar->private->window = window;
420 bar->private->sort_buttons = nact_sort_buttons_new( window );
421
422 base_window_signal_connect(
423 window,
424 G_OBJECT( window ),
425 BASE_SIGNAL_INITIALIZE_WINDOW,
426 G_CALLBACK( on_base_initialize_window ));
427
428 g_object_set_data( G_OBJECT( window ), WINDOW_DATA_MENUBAR, bar );
429
430 return( bar );
431 }
432
433 static void
on_base_initialize_window(BaseWindow * window,gpointer user_data)434 on_base_initialize_window( BaseWindow *window, gpointer user_data )
435 {
436 static const gchar *thisfn = "nact_menubar_on_base_initialize_window";
437 GError *error;
438 guint merge_id;
439 GtkAccelGroup *accel_group;
440 GtkWidget *menubar, *vbox;
441 GtkWindow *toplevel;
442 gboolean has_maintainer_menu;
443 NactApplication *application;
444 guint tabs_pos;
445
446 BAR_WINDOW_VOID( window );
447
448 if( !bar->private->dispose_has_run ){
449
450 g_debug( "%s: window=%p (%s), user_data=%p",
451 thisfn,
452 ( void * ) window, G_OBJECT_TYPE_NAME( window ),
453 ( void * ) user_data );
454
455 /* create the menubar:
456 * - create action group, and insert list of actions in it
457 * - create the ui manager, and insert action group in it
458 * - merge inserted actions group with ui xml definition
459 * - install accelerators in the main window
460 * - pack menu bar in the main window
461 *
462 * "disconnect-proxy" signal is never triggered.
463 */
464 bar->private->ui_manager = gtk_ui_manager_new();
465 g_debug( "%s: ui_manager=%p", thisfn, ( void * ) bar->private->ui_manager );
466
467 base_window_signal_connect(
468 window,
469 G_OBJECT( bar->private->ui_manager ),
470 "connect-proxy",
471 G_CALLBACK( on_ui_manager_proxy_connect ));
472
473 tabs_pos = na_iprefs_get_tabs_pos( NULL );
474 bar->private->notebook_group = gtk_action_group_new( "NotebookActions" );
475 g_debug( "%s: notebook_group=%p", thisfn, ( void * ) bar->private->notebook_group );
476 gtk_action_group_set_translation_domain( bar->private-> notebook_group, GETTEXT_PACKAGE );
477 gtk_action_group_add_radio_actions( bar->private->notebook_group, tabs_pos_entries, G_N_ELEMENTS( tabs_pos_entries ), tabs_pos, G_CALLBACK( nact_menubar_view_on_tabs_pos_changed ), window );
478 gtk_ui_manager_insert_action_group( bar->private->ui_manager, bar->private->notebook_group, 0 );
479
480 bar->private->action_group = gtk_action_group_new( "MenubarActions" );
481 g_debug( "%s: action_group=%p", thisfn, ( void * ) bar->private->action_group );
482 gtk_action_group_set_translation_domain( bar->private->action_group, GETTEXT_PACKAGE );
483 gtk_action_group_add_actions( bar->private->action_group, entries, G_N_ELEMENTS( entries ), window );
484 gtk_action_group_add_toggle_actions( bar->private->action_group, toolbar_entries, G_N_ELEMENTS( toolbar_entries ), window );
485 gtk_ui_manager_insert_action_group( bar->private->ui_manager, bar->private->action_group, 0 );
486
487 error = NULL;
488 merge_id = gtk_ui_manager_add_ui_from_file( bar->private->ui_manager, st_ui_menubar_actions, &error );
489 if( merge_id == 0 || error ){
490 g_warning( "%s: error=%s", thisfn, error->message );
491 g_error_free( error );
492 }
493
494 has_maintainer_menu = FALSE;
495 #ifdef NA_MAINTAINER_MODE
496 has_maintainer_menu = TRUE;
497 #endif
498 if( has_maintainer_menu ){
499 error = NULL;
500 merge_id = gtk_ui_manager_add_ui_from_file( bar->private->ui_manager, st_ui_maintainer_actions, &error );
501 if( merge_id == 0 || error ){
502 g_warning( "%s: error=%s", thisfn, error->message );
503 g_error_free( error );
504 }
505 }
506
507 toplevel = base_window_get_gtk_toplevel( window );
508 accel_group = gtk_ui_manager_get_accel_group( bar->private->ui_manager );
509 gtk_window_add_accel_group( toplevel, accel_group );
510
511 menubar = gtk_ui_manager_get_widget( bar->private->ui_manager, "/ui/MainMenubar" );
512 vbox = base_window_get_widget( window, "MenubarVBox" );
513 gtk_box_pack_start( GTK_BOX( vbox ), menubar, FALSE, FALSE, 0 );
514
515 /* this creates a submenu in the toolbar */
516 /*gtk_container_add( GTK_CONTAINER( vbox ), toolbar );*/
517
518 /* initialize the private data
519 */
520 application = NACT_APPLICATION( base_window_get_application( bar->private->window ));
521 bar->private->updater = nact_application_get_updater( application );
522 bar->private->is_level_zero_writable = na_updater_is_level_zero_writable( bar->private->updater );
523 bar->private->has_writable_providers =
524 ( na_io_provider_find_writable_io_provider( NA_PIVOT( bar->private->updater )) != NULL );
525
526 g_debug( "%s: na_updater_is_level_zero_writable=%s, na_io_provider_find_writable_io_provider=%s",
527 thisfn,
528 bar->private->is_level_zero_writable ? "True":"False",
529 bar->private->has_writable_providers ? "True":"False" );
530
531 /* connect to all signal which may have an influence on the menu
532 * items sensitivity
533 */
534 base_window_signal_connect(
535 window,
536 G_OBJECT( window ),
537 MAIN_SIGNAL_CONTEXT_MENU,
538 G_CALLBACK( on_open_context_menu ));
539
540 base_window_signal_connect(
541 window,
542 G_OBJECT( window ),
543 TREE_SIGNAL_COUNT_CHANGED,
544 G_CALLBACK( on_tree_view_count_changed ));
545
546 base_window_signal_connect(
547 window,
548 G_OBJECT( window ),
549 TREE_SIGNAL_FOCUS_IN,
550 G_CALLBACK( on_tree_view_focus_in ));
551
552 base_window_signal_connect(
553 window,
554 G_OBJECT( window ),
555 TREE_SIGNAL_FOCUS_OUT,
556 G_CALLBACK( on_tree_view_focus_out ));
557
558 base_window_signal_connect(
559 window,
560 G_OBJECT( window ),
561 TREE_SIGNAL_MODIFIED_STATUS_CHANGED,
562 G_CALLBACK( on_tree_view_modified_status_changed ));
563
564 base_window_signal_connect(
565 window,
566 G_OBJECT( window ),
567 MAIN_SIGNAL_SELECTION_CHANGED,
568 G_CALLBACK( on_tree_view_selection_changed ));
569
570 bar->private->update_sensitivities_handler_id =
571 base_window_signal_connect(
572 window,
573 G_OBJECT( bar ),
574 MENUBAR_SIGNAL_UPDATE_SENSITIVITIES,
575 G_CALLBACK( on_update_sensitivities ));
576
577 nact_menubar_file_initialize( bar );
578 nact_main_toolbar_init( window, bar->private->action_group );
579 }
580 }
581
582 /*
583 * action: GtkAction, GtkToggleAction
584 * proxy: GtkImageMenuItem, GtkCheckMenuItem, GtkToolButton
585 */
586 static void
on_ui_manager_proxy_connect(GtkUIManager * ui_manager,GtkAction * action,GtkWidget * proxy,BaseWindow * window)587 on_ui_manager_proxy_connect( GtkUIManager *ui_manager, GtkAction *action, GtkWidget *proxy, BaseWindow *window )
588 {
589 static const gchar *thisfn = "nact_menubar_on_ui_manager_proxy_connect";
590
591 g_debug( "%s: ui_manager=%p (%s), action=%p (%s), proxy=%p (%s), window=%p (%s)",
592 thisfn,
593 ( void * ) ui_manager, G_OBJECT_TYPE_NAME( ui_manager ),
594 ( void * ) action, G_OBJECT_TYPE_NAME( action ),
595 ( void * ) proxy, G_OBJECT_TYPE_NAME( proxy ),
596 ( void * ) window, G_OBJECT_TYPE_NAME( window ));
597
598 if( GTK_IS_MENU_ITEM( proxy )){
599
600 base_window_signal_connect(
601 window,
602 G_OBJECT( proxy ),
603 "select",
604 G_CALLBACK( on_menu_item_selected ));
605
606 base_window_signal_connect(
607 window,
608 G_OBJECT( proxy ),
609 "deselect",
610 G_CALLBACK( on_menu_item_deselected ));
611 }
612 }
613
614 /*
615 * gtk_activatable_get_related_action() and gtk_action_get_tooltip()
616 * are only available starting with Gtk 2.16
617 */
618 static void
on_menu_item_selected(GtkMenuItem * proxy,BaseWindow * window)619 on_menu_item_selected( GtkMenuItem *proxy, BaseWindow *window )
620 {
621 GtkAction *action;
622 const gchar *tooltip;
623
624 /*g_debug( "nact_menubar_on_menu_item_selected: proxy=%p (%s), window=%p (%s)",
625 ( void * ) proxy, G_OBJECT_TYPE_NAME( proxy ),
626 ( void * ) window, G_OBJECT_TYPE_NAME( window ));*/
627
628 tooltip = NULL;
629 action = gtk_activatable_get_related_action( GTK_ACTIVATABLE( proxy ));
630
631 if( action ){
632 tooltip = gtk_action_get_tooltip( action );
633
634 if( tooltip ){
635 nact_main_statusbar_display_status( NACT_MAIN_WINDOW( window ), MENUBAR_PROP_STATUS_CONTEXT, tooltip );
636 }
637 }
638 }
639
640 static void
on_menu_item_deselected(GtkMenuItem * proxy,BaseWindow * window)641 on_menu_item_deselected( GtkMenuItem *proxy, BaseWindow *window )
642 {
643 nact_main_statusbar_hide_status( NACT_MAIN_WINDOW( window ), MENUBAR_PROP_STATUS_CONTEXT );
644 }
645
646 /*
647 * Opens a popup menu.
648 */
649 static void
on_open_context_menu(BaseWindow * window,GdkEventButton * event,const gchar * popup,gpointer user_data)650 on_open_context_menu( BaseWindow *window, GdkEventButton *event, const gchar *popup, gpointer user_data )
651 {
652 static const gchar *thisfn = "nact_menubar_on_open_context_menu";
653 GtkWidget *menu;
654
655 BAR_WINDOW_VOID( window );
656
657 menu = gtk_ui_manager_get_widget( bar->private->ui_manager, popup );
658 if( menu ){
659 bar->private->popup_handler =
660 g_signal_connect(
661 menu,
662 "selection-done",
663 G_CALLBACK( on_popup_selection_done ),
664 window );
665 if( event ){
666 gtk_menu_popup( GTK_MENU( menu ), NULL, NULL, NULL, NULL, event->button, event->time );
667 } else {
668 gtk_menu_popup( GTK_MENU( menu ), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
669 }
670 } else {
671 g_warning( "%s: menu not found: %s", thisfn, popup );
672 }
673 }
674
675 static void
on_popup_selection_done(GtkMenuShell * menushell,BaseWindow * window)676 on_popup_selection_done(GtkMenuShell *menushell, BaseWindow *window )
677 {
678 static const gchar *thisfn = "nact_menubar_on_popup_selection_done";
679
680 BAR_WINDOW_VOID( window );
681
682 g_debug( "%s", thisfn );
683
684 g_signal_handler_disconnect( menushell, bar->private->popup_handler );
685 bar->private->popup_handler = ( gulong ) 0;
686 }
687
688 /*
689 * when the tree view is refilled, update our internal counters so
690 * that we are knowing if we have some exportables
691 */
692 static void
on_tree_view_count_changed(BaseWindow * window,gboolean reset,gint menus,gint actions,gint profiles)693 on_tree_view_count_changed( BaseWindow *window, gboolean reset, gint menus, gint actions, gint profiles )
694 {
695 static const gchar *thisfn = "nact_menubar_on_tree_view_count_changed";
696 gchar *status;
697
698 BAR_WINDOW_VOID( window );
699
700 g_debug( "%s: window=%p, reset=%s, menus=%d, actions=%d, profiles=%d",
701 thisfn, ( void * ) window, reset ? "True":"False", menus, actions, profiles );
702
703 if( reset ){
704 bar->private->count_menus = menus;
705 bar->private->count_actions = actions;
706 bar->private->count_profiles = profiles;
707
708 } else {
709 bar->private->count_menus += menus;
710 bar->private->count_actions += actions;
711 bar->private->count_profiles += profiles;
712 }
713
714 bar->private->have_exportables = ( bar->private->count_menus + bar->private->count_actions > 0 );
715
716 /* i18n: note the space at the beginning of the sentence */
717 status = g_strdup_printf(
718 _( " %d menu(s), %d action(s), %d profile(s) are currently loaded" ),
719 bar->private->count_menus, bar->private->count_actions, bar->private->count_profiles );
720 nact_main_statusbar_display_status( NACT_MAIN_WINDOW( window ), MENUBAR_PROP_MAIN_STATUS_CONTEXT, status );
721 g_free( status );
722
723 g_signal_emit_by_name( bar, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES );
724 }
725
726 static void
on_tree_view_focus_in(BaseWindow * window,gpointer user_data)727 on_tree_view_focus_in( BaseWindow *window, gpointer user_data )
728 {
729 BAR_WINDOW_VOID( window );
730
731 g_debug( "nact_menubar_on_tree_view_focus_in" );
732
733 bar->private->treeview_has_focus = TRUE;
734 g_signal_emit_by_name( bar, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES );
735 }
736
737 static void
on_tree_view_focus_out(BaseWindow * window,gpointer user_data)738 on_tree_view_focus_out( BaseWindow *window, gpointer user_data )
739 {
740 BAR_WINDOW_VOID( window );
741
742 g_debug( "nact_menubar_on_tree_view_focus_out" );
743
744 bar->private->treeview_has_focus = FALSE;
745 g_signal_emit_by_name( bar, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES );
746 }
747
748 /*
749 * the count of modified NAObjectItem has changed
750 */
751 static void
on_tree_view_modified_status_changed(BaseWindow * window,gboolean is_modified,gpointer user_data)752 on_tree_view_modified_status_changed( BaseWindow *window, gboolean is_modified, gpointer user_data )
753 {
754 static const gchar *thisfn = "nact_menubar_on_tree_view_modified_status_changed";
755
756 g_debug( "%s: window=%p, is_modified=%s, user_data=%p",
757 thisfn, ( void * ) window, is_modified ? "True":"False", ( void * ) user_data );
758
759 BAR_WINDOW_VOID( window );
760
761 if( !bar->private->dispose_has_run ){
762
763 bar->private->is_tree_modified = is_modified;
764 g_signal_emit_by_name( bar, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES );
765 }
766 }
767
768 /*
769 * when the selection changes in the tree view, see what is selected
770 *
771 * It happens that this function is triggered after all tabs have already
772 * dealt with the MAIN_SIGNAL_SELECTION_CHANGED signal
773 *
774 * We are trying to precompute here all indicators which are needed to
775 * make actions sensitive. As a multiple selection may have multiple
776 * sort of indicators, we assure here that at least one item will be a
777 * valid candidate to the target action, the action taking care itself
778 * of applying to valid candidates, and rejecting the others.
779 */
780 static void
on_tree_view_selection_changed(BaseWindow * window,GList * selected,gpointer user_data)781 on_tree_view_selection_changed( BaseWindow *window, GList *selected, gpointer user_data )
782 {
783 static const gchar *thisfn = "nact_menubar_on_tree_view_selection_changed";
784 NAObject *first;
785 NAObject *selected_action;
786 NAObject *row, *parent;
787 GList *is;
788
789 BAR_WINDOW_VOID( window );
790
791 g_debug( "%s: selected_items=%p (count=%d)", thisfn, ( void * ) selected, g_list_length( selected ));
792
793 /* count the items
794 */
795 bar->private->count_selected = g_list_length( selected );
796
797 if( selected ){
798 na_object_item_count_items( selected, &bar->private->selected_menus, &bar->private->selected_actions, &bar->private->selected_profiles, FALSE );
799 g_debug( "%s: selected_menus=%d, selected_actions=%d, selected_profiles=%d",
800 thisfn,
801 bar->private->selected_menus, bar->private->selected_actions, bar->private->selected_profiles );
802 }
803
804 /* take a ref of the list of selected items
805 */
806 if( bar->private->selected_items ){
807 g_list_free( bar->private->selected_items );
808 }
809 bar->private->selected_items = g_list_copy( selected );
810
811 /* check if the parent of the first selected item is writable
812 * (File: New menu/New action)
813 * (Edit: Paste menu or action)
814 */
815 first = NULL;
816 if( selected ){
817 first = ( NAObject *) selected->data;
818 if( NA_IS_OBJECT_PROFILE( first )){
819 first = NA_OBJECT( na_object_get_parent( first ));
820 }
821 first = ( NAObject * ) na_object_get_parent( first );
822 }
823 if( first ){
824 bar->private->is_parent_writable = na_object_is_finally_writable( first, NULL );
825 g_debug( "%s: parent of first selected is not null: is_parent_writable=%s",
826 thisfn, bar->private->is_parent_writable ? "True":"False" );
827 } else {
828 bar->private->is_parent_writable = bar->private->is_level_zero_writable;
829 g_debug( "%s: first selected is at level zero: is_level_zero_writable=%s",
830 thisfn, bar->private->is_level_zero_writable ? "True":"False" );
831 }
832
833 /* check is only an action is selected, or only profile(s) of a same action
834 * (File: New profile)
835 * (Edit: Paste a profile)
836 */
837 bar->private->enable_new_profile = TRUE;
838 selected_action = NULL;
839 for( is = selected ; is ; is = is->next ){
840
841 if( NA_IS_OBJECT_MENU( is->data )){
842 bar->private->enable_new_profile = FALSE;
843 break;
844
845 } else if( NA_IS_OBJECT_ACTION( is->data )){
846 if( !selected_action ){
847 selected_action = NA_OBJECT( is->data );
848 } else {
849 bar->private->enable_new_profile = FALSE;
850 break;
851 }
852
853 } else if( NA_IS_OBJECT_PROFILE( is->data )){
854 first = NA_OBJECT( na_object_get_parent( is->data ));
855 if( !selected_action ){
856 selected_action = first;
857 } else if( selected_action != first ){
858 bar->private->enable_new_profile = FALSE;
859 break;
860 }
861 }
862 }
863 if( selected_action ){
864 bar->private->is_action_writable = na_object_is_finally_writable( selected_action, NULL );
865 } else {
866 bar->private->enable_new_profile = FALSE;
867 }
868
869 /* check that selection is not empty and that each selected item is writable
870 * and that all parents are writable
871 * if some selection is at level zero, then it must be writable
872 * (Edit: Cut/Delete)
873 */
874 if( selected ){
875 bar->private->are_parents_writable = TRUE;
876 bar->private->are_items_writable = TRUE;
877 for( is = selected ; is ; is = is->next ){
878 row = ( NAObject * ) is->data;
879 if( NA_IS_OBJECT_PROFILE( row )){
880 row = NA_OBJECT( na_object_get_parent( row ));
881 }
882 gchar *label = na_object_get_label( row );
883 gboolean writable = na_object_is_finally_writable( row, NULL );
884 g_debug( "%s: label=%s, writable=%s", thisfn, label, writable ? "True":"False" );
885 g_free( label );
886 bar->private->are_items_writable &= writable;
887 parent = ( NAObject * ) na_object_get_parent( row );
888 if( parent ){
889 bar->private->are_parents_writable &= na_object_is_finally_writable( parent, NULL );
890 } else {
891 bar->private->are_parents_writable &= bar->private->is_level_zero_writable;
892 }
893 }
894 }
895
896 g_signal_emit_by_name( bar, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES );
897 }
898
899 static void
on_update_sensitivities(NactMenubar * bar,BaseWindow * window)900 on_update_sensitivities( NactMenubar *bar, BaseWindow *window )
901 {
902 static const gchar *thisfn = "nact_menubar_on_update_sensitivities";
903
904 g_debug( "%s: bar=%p, window=%p", thisfn, ( void * ) bar, ( void * ) window );
905
906 nact_menubar_file_on_update_sensitivities( bar );
907 nact_menubar_edit_on_update_sensitivities( bar );
908 nact_menubar_view_on_update_sensitivities( bar );
909 nact_menubar_tools_on_update_sensitivities( bar );
910 nact_menubar_maintainer_on_update_sensitivities( bar );
911 nact_menubar_help_on_update_sensitivities( bar );
912 }
913
914 /**
915 * nact_menubar_enable_item:
916 * @bar: this #NactMenubar instance.
917 * @name: the name of the item in a menu.
918 * @enabled: whether this item should be enabled or not.
919 *
920 * Enable/disable an item in an menu.
921 */
922 void
nact_menubar_enable_item(const NactMenubar * bar,const gchar * name,gboolean enabled)923 nact_menubar_enable_item( const NactMenubar *bar, const gchar *name, gboolean enabled )
924 {
925 GtkAction *action;
926
927 if( !bar->private->dispose_has_run ){
928
929 action = gtk_action_group_get_action( bar->private->action_group, name );
930 gtk_action_set_sensitive( action, enabled );
931 }
932 }
933
934 /**
935 * nact_menubar_save_items:
936 * @window: the #BaseWindow
937 */
938 void
nact_menubar_save_items(BaseWindow * window)939 nact_menubar_save_items( BaseWindow *window )
940 {
941 nact_menubar_file_save_items( window );
942 }
943