3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program 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.
14 * This Program 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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
35 #include <glib/gi18n.h>
38 #include <api/na-core-utils.h>
39 #include <api/na-timeout.h>
41 #include <core/na-io-provider.h>
42 #include <core/na-iprefs.h>
44 #include "nact-application.h"
45 #include "nact-main-statusbar.h"
46 #include "nact-main-tab.h"
47 #include "nact-menubar-priv.h"
48 #include "nact-tree-ieditable.h"
50 static NATimeout st_autosave_prefs_timeout
= { 0 };
51 static guint st_event_autosave
= 0;
53 static gchar
*st_save_error
= N_( "Save error" );
54 static gchar
*st_save_warning
= N_( "Some items may not have been saved" );
55 static gchar
*st_level_zero_write
= N_( "Unable to rewrite the level-zero items list" );
56 static gchar
*st_delete_error
= N_( "Some items have not be deleted" );
58 static gboolean
save_item( BaseWindow
*window
, NAUpdater
*updater
, NAObjectItem
*item
, GSList
**messages
);
59 static void install_autosave( NactMenubar
*bar
);
60 static void on_autosave_prefs_changed( const gchar
*group
, const gchar
*key
, gconstpointer new_value
, gpointer user_data
);
61 static void on_autosave_prefs_timeout( NactMenubar
*bar
);
62 static gboolean
autosave_callback( NactMenubar
*bar
);
63 static void autosave_destroyed( NactMenubar
*bar
);
66 * nact_menubar_file_initialize:
67 * @bar: this #NactMenubar object.
70 nact_menubar_file_initialize( NactMenubar
*bar
)
72 install_autosave( bar
);
76 * nact_menubar_file_on_update_sensitivities:
77 * @bar: this #NactMenubar object.
79 * Update sensitivity of items of the File menu.
82 nact_menubar_file_on_update_sensitivities( const NactMenubar
*bar
)
84 gboolean new_item_enabled
;
86 /* new menu / new action
87 * new item will be inserted just before the beginning of selection
88 * parent of the first selected row must be writable
89 * we must have at least one writable provider
91 new_item_enabled
= bar
->private->is_parent_writable
&& bar
->private->has_writable_providers
;
92 nact_menubar_enable_item( bar
, "NewMenuItem", new_item_enabled
);
93 nact_menubar_enable_item( bar
, "NewActionItem", new_item_enabled
);
95 /* new profile enabled if selection is relative to only one writable action
96 * i.e. contains profile(s) of the same action, or only contains one action
97 * action must be writable
99 nact_menubar_enable_item( bar
, "NewProfileItem",
100 bar
->private->enable_new_profile
&& bar
->private->is_action_writable
);
102 /* save enabled if at least one item has been modified
103 * or level-zero has been resorted and is writable
105 nact_menubar_enable_item( bar
, "SaveItem", ( bar
->private->is_tree_modified
));
107 /* quit always enabled */
111 * nact_menubar_file_on_new_menu:
112 * @gtk_action: the #GtkAction action.
113 * @window: the #BaseWindow main window.
115 * Triggers File / New menu item.
118 nact_menubar_file_on_new_menu( GtkAction
*gtk_action
, BaseWindow
*window
)
121 NactApplication
*application
;
123 NactTreeView
*items_view
;
126 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
127 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
129 menu
= na_object_menu_new_with_defaults();
130 na_object_check_status( menu
);
131 application
= NACT_APPLICATION( base_window_get_application( window
));
132 updater
= nact_application_get_updater( application
);
133 na_updater_check_item_writability_status( updater
, NA_OBJECT_ITEM( menu
));
134 items
= g_list_prepend( NULL
, menu
);
135 items_view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
136 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view
), items
, NULL
);
137 na_object_free_items( items
);
141 * nact_menubar_file_on_new_action:
142 * @gtk_action: the #GtkAction action.
143 * @window: the #BaseWindow main window.
145 * Triggers File / New action item.
148 nact_menubar_file_on_new_action( GtkAction
*gtk_action
, BaseWindow
*window
)
150 NAObjectAction
*action
;
151 NactApplication
*application
;
153 NactTreeView
*items_view
;
156 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
157 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
159 action
= na_object_action_new_with_defaults();
160 na_object_check_status( action
);
161 application
= NACT_APPLICATION( base_window_get_application( window
));
162 updater
= nact_application_get_updater( application
);
163 na_updater_check_item_writability_status( updater
, NA_OBJECT_ITEM( action
));
164 items
= g_list_prepend( NULL
, action
);
165 items_view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
166 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view
), items
, NULL
);
167 na_object_free_items( items
);
171 * nact_menubar_file_on_new_profile:
172 * @gtk_action: the #GtkAction action.
173 * @window: the #BaseWindow main window.
175 * Triggers File / New profile item.
178 nact_menubar_file_on_new_profile( GtkAction
*gtk_action
, BaseWindow
*window
)
180 NAObjectAction
*action
;
181 NAObjectProfile
*profile
;
182 NactTreeView
*items_view
;
186 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
187 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
191 MAIN_PROP_ITEM
, &action
,
194 profile
= na_object_profile_new_with_defaults();
195 na_object_set_label( profile
, _( "New profile" ));
197 name
= na_object_action_get_new_profile_name( action
);
198 na_object_set_id( profile
, name
);
201 /*na_object_attach_profile( action, profile );*/
203 na_object_check_status( profile
);
205 items
= g_list_prepend( NULL
, profile
);
206 items_view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
207 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view
), items
, NULL
);
208 na_object_free_items( items
);
212 * nact_menubar_file_on_save:
213 * @gtk_action: the #GtkAction action.
214 * @window: the #BaseWindow main window.
216 * Triggers File /Save item.
218 * Saving is not only saving modified items, but also saving hierarchy
219 * (and order if alpha order is not set).
221 * This is the same function that nact_menubar_file_save_items(), just with
222 * different arguments.
225 nact_menubar_file_on_save( GtkAction
*gtk_action
, BaseWindow
*window
)
227 static const gchar
*thisfn
= "nact_menubar_file_on_save";
229 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
230 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
231 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
233 nact_menubar_file_save_items( window
);
237 * nact_menubar_file_save_items:
238 * @gtk_action: the #GtkAction action.
239 * @window: the #BaseWindow main window.
242 * This is the same function that nact_menubar_file_on_save(), just
243 * with different arguments.
246 * - rewrite the level-zero items list
247 * - delete the items which are marked to be deleted
248 * - rewrite (i.e. delete/write) updated items
250 * The difficulty here is that some sort of pseudo-transactionnal process
253 * - if the level-zero items list cannot be updated, then an error message
254 * is displayed, and we abort the whole processus
256 * - if some items cannot be actually deleted, then an error message is
257 * displayed, and the whole processus is aborted;
259 * a/ items which have not been deleted must be restored (maybe marked
260 * as deleted ?) -> so these items are modified
261 * b/ the level-zero list must be updated with these restored items
264 * - idem if some items cannot be actually rewritten...
267 nact_menubar_file_save_items( BaseWindow
*window
)
269 static const gchar
*thisfn
= "nact_menubar_file_save_items";
270 NactTreeView
*items_view
;
273 NAObjectItem
*duplicate
;
277 BAR_WINDOW_VOID( window
);
279 g_debug( "%s: window=%p", thisfn
, ( void * ) window
);
281 /* always write the level zero list of items as the first save phase
282 * and reset the corresponding modification flag
284 items_view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
285 items
= nact_tree_view_get_items( items_view
);
286 na_object_dump_tree( items
);
289 if( nact_tree_ieditable_is_level_zero_modified( NACT_TREE_IEDITABLE( items_view
))){
290 if( !na_iprefs_write_level_zero( items
, &messages
)){
291 if( g_slist_length( messages
)){
292 msg
= na_core_utils_slist_join_at_end( messages
, "\n" );
294 msg
= g_strdup( gettext( st_level_zero_write
));
296 base_window_display_error_dlg( window
, gettext( st_save_error
), msg
);
298 na_core_utils_slist_free( messages
);
302 g_signal_emit_by_name( window
, TREE_SIGNAL_LEVEL_ZERO_CHANGED
, FALSE
);
305 /* remove deleted items
306 * so that new actions with same id do not risk to be deleted later
307 * not deleted items are reinserted in the tree
309 if( !nact_tree_ieditable_remove_deleted( NACT_TREE_IEDITABLE( items_view
), &messages
)){
310 if( g_slist_length( messages
)){
311 msg
= na_core_utils_slist_join_at_end( messages
, "\n" );
313 msg
= g_strdup( gettext( st_delete_error
));
315 base_window_display_error_dlg( window
, gettext( st_save_error
), msg
);
317 na_core_utils_slist_free( messages
);
320 na_object_free_items( items
);
321 items
= nact_tree_view_get_items( items_view
);
324 /* recursively save the modified items
325 * check is useless here if item was not modified, but not very costly;
326 * above all, it is less costly to check the status here, than to check
327 * recursively each and every modified item
332 for( it
= items
; it
; it
= it
->next
){
333 save_item( window
, bar
->private->updater
, NA_OBJECT_ITEM( it
->data
), &messages
);
334 duplicate
= NA_OBJECT_ITEM( na_object_duplicate( it
->data
));
335 na_object_reset_origin( it
->data
, duplicate
);
336 na_object_check_status( it
->data
);
337 new_pivot
= g_list_prepend( new_pivot
, duplicate
);
340 if( g_slist_length( messages
)){
341 msg
= na_core_utils_slist_join_at_end( messages
, "\n" );
342 base_window_display_error_dlg( window
, gettext( st_save_warning
), msg
);
344 na_core_utils_slist_free( messages
);
347 na_pivot_set_new_items( NA_PIVOT( bar
->private->updater
), g_list_reverse( new_pivot
));
348 na_object_free_items( items
);
349 nact_main_window_block_reload( NACT_MAIN_WINDOW( window
));
350 g_signal_emit_by_name( window
, TREE_SIGNAL_MODIFIED_STATUS_CHANGED
, FALSE
);
354 * iterates here on each and every NAObjectItem row stored in the tree
357 save_item( BaseWindow
*window
, NAUpdater
*updater
, NAObjectItem
*item
, GSList
**messages
)
359 static const gchar
*thisfn
= "nact_menubar_file_save_item";
361 NAIOProvider
*provider_before
;
362 NAIOProvider
*provider_after
;
363 GList
*subitems
, *it
;
367 g_return_val_if_fail( NACT_IS_MAIN_WINDOW( window
), FALSE
);
368 g_return_val_if_fail( NA_IS_UPDATER( updater
), FALSE
);
369 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), FALSE
);
373 if( NA_IS_OBJECT_MENU( item
)){
374 subitems
= na_object_get_items( item
);
375 for( it
= subitems
; it
; it
= it
->next
){
376 ret
&= save_item( window
, updater
, NA_OBJECT_ITEM( it
->data
), messages
);
380 provider_before
= na_object_get_provider( item
);
382 if( na_object_is_modified( item
)){
383 label
= na_object_get_label( item
);
384 g_debug( "%s: saving %p (%s) '%s'", thisfn
, ( void * ) item
, G_OBJECT_TYPE_NAME( item
), label
);
387 save_ret
= na_updater_write_item( updater
, item
, messages
);
388 ret
= ( save_ret
== NA_IIO_PROVIDER_CODE_OK
);
391 if( NA_IS_OBJECT_ACTION( item
)){
392 na_object_reset_last_allocated( item
);
395 provider_after
= na_object_get_provider( item
);
396 if( provider_after
!= provider_before
){
397 g_signal_emit_by_name( window
, MAIN_SIGNAL_ITEM_UPDATED
, item
, MAIN_DATA_PROVIDER
);
406 * nact_menubar_file_on_quit:
407 * @gtk_action: the #GtkAction action.
408 * @window: the #BaseWindow main window.
410 * Triggers the File / Quit item.
413 nact_menubar_file_on_quit( GtkAction
*gtk_action
, BaseWindow
*window
)
415 static const gchar
*thisfn
= "nact_menubar_file_on_quit";
417 g_debug( "%s: item=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
418 g_return_if_fail( GTK_IS_ACTION( gtk_action
) || gtk_action
== NULL
);
420 nact_main_window_quit( NACT_MAIN_WINDOW( window
));
424 * nact_menubar_file_install_autosave:
425 * @bar: this #NactMenubar instance.
427 * Setup the autosave feature and initialize its monitoring.
430 install_autosave( NactMenubar
*bar
)
432 st_autosave_prefs_timeout
.timeout
= 100;
433 st_autosave_prefs_timeout
.handler
= ( NATimeoutFunc
) on_autosave_prefs_timeout
;
434 st_autosave_prefs_timeout
.user_data
= bar
;
436 na_settings_register_key_callback( NA_IPREFS_MAIN_SAVE_AUTO
, G_CALLBACK( on_autosave_prefs_changed
), NULL
);
437 na_settings_register_key_callback( NA_IPREFS_MAIN_SAVE_PERIOD
, G_CALLBACK( on_autosave_prefs_changed
), NULL
);
439 on_autosave_prefs_timeout( bar
);
443 on_autosave_prefs_changed( const gchar
*group
, const gchar
*key
, gconstpointer new_value
, gpointer user_data
)
445 na_timeout_event( &st_autosave_prefs_timeout
);
449 on_autosave_prefs_timeout( NactMenubar
*bar
)
451 static const gchar
*thisfn
= "nact_menubar_file_on_autosave_prefs_timeout";
452 gboolean autosave_on
;
453 guint autosave_period
;
455 g_return_if_fail( NACT_IS_MENUBAR( bar
));
457 autosave_on
= na_settings_get_boolean( NA_IPREFS_MAIN_SAVE_AUTO
, NULL
, NULL
);
458 autosave_period
= na_settings_get_uint( NA_IPREFS_MAIN_SAVE_PERIOD
, NULL
, NULL
);
460 if( st_event_autosave
){
461 if( !g_source_remove( st_event_autosave
)){
462 g_warning( "%s: unable to remove autosave event source", thisfn
);
464 st_event_autosave
= 0;
468 st_event_autosave
= g_timeout_add_seconds_full(
470 autosave_period
* 60,
471 ( GSourceFunc
) autosave_callback
,
473 ( GDestroyNotify
) autosave_destroyed
);
478 autosave_callback( NactMenubar
*bar
)
480 const gchar
*context
= "autosave-context";
481 g_debug( "nact_menubar_file_autosave_callback" );
483 nact_main_statusbar_display_status( NACT_MAIN_WINDOW( bar
->private->window
), context
, _( "Automatically saving pending modifications..." ));
484 nact_menubar_file_save_items( bar
->private->window
);
485 nact_main_statusbar_hide_status( NACT_MAIN_WINDOW( bar
->private->window
), context
);
491 autosave_destroyed( NactMenubar
*bar
)
493 g_debug( "nact_menubar_file_autosave_destroyed" );