Convert NASettings to a private singleton
[nautilus-actions.git] / src / nact / nact-menubar-file.c
blob007a44cfaf74168e707d47ad0d00f5be6cc16573
1 /*
2 * Nautilus-Actions
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.
24 * Authors:
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)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <glib/gi18n.h>
36 #include <libintl.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.
69 void
70 nact_menubar_file_initialize( NactMenubar *bar )
72 install_autosave( bar );
75 /**
76 * nact_menubar_file_on_update_sensitivities:
77 * @bar: this #NactMenubar object.
79 * Update sensitivity of items of the File menu.
81 void
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.
117 void
118 nact_menubar_file_on_new_menu( GtkAction *gtk_action, BaseWindow *window )
120 NAObjectMenu *menu;
121 NactApplication *application;
122 NAUpdater *updater;
123 NactTreeView *items_view;
124 GList *items;
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.
147 void
148 nact_menubar_file_on_new_action( GtkAction *gtk_action, BaseWindow *window )
150 NAObjectAction *action;
151 NactApplication *application;
152 NAUpdater *updater;
153 NactTreeView *items_view;
154 GList *items;
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.
177 void
178 nact_menubar_file_on_new_profile( GtkAction *gtk_action, BaseWindow *window )
180 NAObjectAction *action;
181 NAObjectProfile *profile;
182 NactTreeView *items_view;
183 gchar *name;
184 GList *items;
186 g_return_if_fail( GTK_IS_ACTION( gtk_action ));
187 g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
189 g_object_get(
190 G_OBJECT( window ),
191 MAIN_PROP_ITEM, &action,
192 NULL );
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 );
199 g_free( 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.
224 void
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.
241 * Save items.
242 * This is the same function that nact_menubar_file_on_save(), just
243 * with different arguments.
245 * Synopsis:
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
251 * must be setup:
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;
258 * plus:
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
262 * and reset modified
264 * - idem if some items cannot be actually rewritten...
266 void
267 nact_menubar_file_save_items( BaseWindow *window )
269 static const gchar *thisfn = "nact_menubar_file_save_items";
270 NactTreeView *items_view;
271 GList *items, *it;
272 GList *new_pivot;
273 NAObjectItem *duplicate;
274 GSList *messages;
275 gchar *msg;
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 );
287 messages = NULL;
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" );
293 } else {
294 msg = g_strdup( gettext( st_level_zero_write ));
296 base_window_display_error_dlg( window, gettext( st_save_error ), msg );
297 g_free( msg );
298 na_core_utils_slist_free( messages );
301 } else {
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" );
312 } else {
313 msg = g_strdup( gettext( st_delete_error ));
315 base_window_display_error_dlg( window, gettext( st_save_error ), msg );
316 g_free( msg );
317 na_core_utils_slist_free( messages );
319 } else {
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
329 new_pivot = NULL;
330 messages = NULL;
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 );
343 g_free( 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
356 static gboolean
357 save_item( BaseWindow *window, NAUpdater *updater, NAObjectItem *item, GSList **messages )
359 static const gchar *thisfn = "nact_menubar_file_save_item";
360 gboolean ret;
361 NAIOProvider *provider_before;
362 NAIOProvider *provider_after;
363 GList *subitems, *it;
364 gchar *label;
365 guint save_ret;
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 );
371 ret = TRUE;
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 );
385 g_free( label );
387 save_ret = na_updater_write_item( updater, item, messages );
388 ret = ( save_ret == NA_IIO_PROVIDER_CODE_OK );
390 if( ret ){
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 );
402 return( ret );
406 * nact_menubar_file_on_quit:
407 * @gtk_action: the #GtkAction action.
408 * @window: the #BaseWindow main window.
410 * Triggers the File / Quit item.
412 void
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.
429 static void
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 );
442 static void
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 );
448 static void
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;
467 if( autosave_on ){
468 st_event_autosave = g_timeout_add_seconds_full(
469 G_PRIORITY_DEFAULT,
470 autosave_period * 60,
471 ( GSourceFunc ) autosave_callback,
472 bar,
473 ( GDestroyNotify ) autosave_destroyed );
477 static gboolean
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 );
487 return( TRUE );
490 static void
491 autosave_destroyed( NactMenubar *bar )
493 g_debug( "nact_menubar_file_autosave_destroyed" );