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>
37 #include <api/na-core-utils.h>
39 #include <core/na-io-provider.h>
40 #include <core/na-iprefs.h>
42 #include "nact-application.h"
43 #include "nact-main-tab.h"
44 #include "nact-menubar-priv.h"
45 #include "nact-preferences-editor.h"
46 #include "nact-tree-ieditable.h"
48 static GList
*prepare_for_paste( BaseWindow
*window
);
49 static GList
*get_deletables( NAUpdater
*updater
, GList
*tree
, GSList
**not_deletable
);
50 static GSList
*get_deletables_rec( NAUpdater
*updater
, GList
*tree
);
51 static gchar
*add_non_deletable_msg( const NAObjectItem
*item
, gint reason
);
52 static void update_clipboard_counters( BaseWindow
*window
);
55 * nact_menubar_edit_on_update_sensitivities:
56 * @bar: this #NactMenubar object.
58 * Update sensitivity of items of the Edit menu.
60 * Each action (cut, copy, delete, etc.) takes itself care of whether it
61 * can safely apply to all the selection or not. The action can at least
62 * assume that at least one item will be a valid candidate (this is the
66 nact_menubar_edit_on_update_sensitivities( const NactMenubar
*bar
)
69 gboolean copy_enabled
;
70 gboolean paste_enabled
;
71 gboolean paste_into_enabled
;
72 gboolean duplicate_enabled
;
73 gboolean delete_enabled
;
74 NAObject
*parent_item
;
75 NAObject
*selected_action
;
76 NAObject
*selected_item
;
77 gboolean is_clipboard_empty
;
79 is_clipboard_empty
= ( bar
->private->clipboard_menus
+ bar
->private->clipboard_actions
+ bar
->private->clipboard_profiles
== 0 );
81 /* cut requires a non-empty selection
82 * and that the selection is writable (can be modified, i.e. is not read-only)
83 * and that all parents are writable (as implies a delete operation)
85 duplicate_enabled
= bar
->private->treeview_has_focus
|| bar
->private->popup_handler
;
86 duplicate_enabled
&= bar
->private->count_selected
> 0;
87 duplicate_enabled
&= bar
->private->are_parents_writable
;
88 cut_enabled
= duplicate_enabled
;
89 cut_enabled
&= bar
->private->are_items_writable
;
90 nact_menubar_enable_item( bar
, "CutItem", cut_enabled
);
92 /* copy only requires a non-empty selection */
93 copy_enabled
= bar
->private->treeview_has_focus
|| bar
->private->popup_handler
;
94 copy_enabled
&= bar
->private->count_selected
> 0;
95 nact_menubar_enable_item( bar
, "CopyItem", copy_enabled
);
98 * - clipboard is not empty
99 * - current selection is not multiple
100 * - if clipboard contains only profiles,
101 * then current selection must be a profile or an action
102 * and the action must be writable
103 * - if clipboard contains actions or menus,
104 * then current selection (if any) must be a menu or an action
105 * and its parent must be writable
107 paste_enabled
= bar
->private->treeview_has_focus
|| bar
->private->popup_handler
;
108 paste_enabled
&= !is_clipboard_empty
;
109 paste_enabled
&= bar
->private->count_selected
<= 1;
110 if( bar
->private->clipboard_profiles
){
111 paste_enabled
&= bar
->private->count_selected
== 1;
112 paste_enabled
&= bar
->private->is_action_writable
;
114 paste_enabled
&= bar
->private->has_writable_providers
;
115 if( bar
->private->count_selected
){
116 paste_enabled
&= bar
->private->is_parent_writable
;
118 paste_enabled
&= bar
->private->is_level_zero_writable
;
121 nact_menubar_enable_item( bar
, "PasteItem", paste_enabled
);
123 /* paste into enabled if
124 * - clipboard is not empty
125 * - current selection is not multiple
126 * - if clipboard contains only profiles,
127 * then current selection must be an action
128 * and the action must be writable
129 * - if clipboard contains actions or menus,
130 * then current selection (if any) must be a menu
131 * and its parent must be writable
133 paste_into_enabled
= bar
->private->treeview_has_focus
|| bar
->private->popup_handler
;
134 paste_into_enabled
&= !is_clipboard_empty
;
135 paste_into_enabled
&= bar
->private->count_selected
<= 1;
136 if( bar
->private->clipboard_profiles
){
137 paste_into_enabled
&= bar
->private->count_selected
== 1;
138 if( paste_into_enabled
){
139 selected_action
= NA_OBJECT( bar
->private->selected_items
->data
);
140 paste_into_enabled
&= NA_IS_OBJECT_ACTION( selected_action
);
141 paste_into_enabled
&= na_object_is_finally_writable( selected_action
, NULL
);
144 paste_into_enabled
&= bar
->private->has_writable_providers
;
145 if( bar
->private->count_selected
){
146 selected_item
= NA_OBJECT( bar
->private->selected_items
->data
);
147 paste_into_enabled
&= NA_IS_OBJECT_MENU( selected_item
);
148 if( paste_into_enabled
){
149 parent_item
= ( NAObject
* ) na_object_get_parent( selected_item
);
150 paste_into_enabled
&= parent_item
151 ? na_object_is_finally_writable( parent_item
, NULL
)
152 : bar
->private->is_level_zero_writable
;
155 paste_into_enabled
&= bar
->private->is_level_zero_writable
;
158 nact_menubar_enable_item( bar
, "PasteIntoItem", paste_into_enabled
);
160 /* duplicate items will be duplicated besides each one
161 * selection must be non-empty
162 * each parent must be writable
164 nact_menubar_enable_item( bar
, "DuplicateItem", duplicate_enabled
);
166 /* delete is same that cut
167 * but items themselves must be writable (because physically deleted)
168 * this will be checked on delete activated
170 delete_enabled
= cut_enabled
;
171 nact_menubar_enable_item( bar
, "DeleteItem", delete_enabled
);
173 /* reload items always enabled */
175 /* preferences always enabled */
179 * nact_menubar_edit_on_cut:
180 * @gtk_action: the #GtkAction action.
181 * @window: the #BaseWindow main window.
183 * Cut objects are installed both in the clipboard and in the deleted list.
184 * Parent pointer is reset to %NULL.
185 * Old parent status is re-checked by the tree model delete operation.
186 * When pasting later these cut objects:
187 * - the first time, we paste the very same object, removing it from the
188 * deleted list, attaching it to a new parent
189 * the object itself is not modified, but the parent is.
190 * - the following times, we paste a copy of this object with a new identifier
192 * cuts the visible selection
193 * - (tree) get new refs on selected items
194 * - (main) add selected items to main list of deleted,
195 * moving newref from list_from_tree to main_list_of_deleted
196 * - (menu) install in clipboard a copy of selected objects
197 * - (tree) remove selected items, unreffing objects
200 nact_menubar_edit_on_cut( GtkAction
*gtk_action
, BaseWindow
*window
)
202 static const gchar
*thisfn
= "nact_menubar_edit_on_cut";
204 NactClipboard
*clipboard
;
206 GSList
*non_deletables
;
209 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
210 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
212 BAR_WINDOW_VOID( window
);
214 items
= na_object_copyref_items( bar
->private->selected_items
);
215 non_deletables
= NULL
;
216 to_delete
= get_deletables( bar
->private->updater
, items
, &non_deletables
);
218 if( non_deletables
){
219 gchar
*second
= na_core_utils_slist_join_at_end( non_deletables
, "\n" );
220 base_window_display_error_dlg(
221 BASE_WINDOW( window
),
222 _( "Not all items have been cut as following ones are not modifiable:" ),
225 na_core_utils_slist_free( non_deletables
);
229 clipboard
= nact_main_window_get_clipboard( NACT_MAIN_WINDOW( window
));
230 nact_clipboard_primary_set( clipboard
, to_delete
, CLIPBOARD_MODE_CUT
);
231 update_clipboard_counters( window
);
232 view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
233 nact_tree_ieditable_delete( NACT_TREE_IEDITABLE( view
), to_delete
, TREE_OPE_DELETE
);
236 na_object_free_items( items
);
240 * nact_menubar_edit_on_copy:
241 * @gtk_action: the #GtkAction action.
242 * @window: the #BaseWindow main window.
244 * copies the visible selection
245 * - (tree) get new refs on selected items
246 * - (menu) install in clipboard a copy of selected objects
247 * renumbering actions/menus id to ensure unicity at paste time
248 * - (menu) release refs on selected items
249 * - (menu) refresh actions sensitivy (as selection doesn't change)
252 nact_menubar_edit_on_copy( GtkAction
*gtk_action
, BaseWindow
*window
)
254 static const gchar
*thisfn
= "nact_menubar_edit_on_copy";
256 NactClipboard
*clipboard
;
258 BAR_WINDOW_VOID( window
);
260 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
261 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
262 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
264 items
= na_object_copyref_items( bar
->private->selected_items
);
265 clipboard
= nact_main_window_get_clipboard( NACT_MAIN_WINDOW( window
));
266 nact_clipboard_primary_set( clipboard
, items
, CLIPBOARD_MODE_COPY
);
267 update_clipboard_counters( window
);
268 na_object_free_items( items
);
270 g_signal_emit_by_name( bar
, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES
);
274 * nact_menubar_edit_on_paste:
275 * @gtk_action: the #GtkAction action.
276 * @window: the #BaseWindow main window.
278 * pastes the current content of the clipboard at the current position
279 * (same path, same level)
280 * - (menu) get from clipboard a copy of installed items
281 * the clipboard will return a new copy
282 * and renumber its own data for allowing a new paste
283 * - (tree) insert new items, the tree store will ref them
284 * attaching each item to its parent
285 * recursively checking edition status of the topmost parent
286 * selecting the first item at end
287 * - (menu) unreffing the copy got from clipboard
290 nact_menubar_edit_on_paste( GtkAction
*gtk_action
, BaseWindow
*window
)
292 static const gchar
*thisfn
= "nact_menubar_edit_on_paste";
296 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
298 items
= prepare_for_paste( window
);
301 view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
302 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( view
), items
, NULL
);
303 na_object_free_items( items
);
308 * nact_menubar_edit_on_paste_into:
309 * @gtk_action: the #GtkAction action.
310 * @window: the #BaseWindow main window.
312 * pastes the current content of the clipboard as the first child of
313 * currently selected item
314 * - (menu) get from clipboard a copy of installed items
315 * the clipboard will return a new copy
316 * and renumber its own data for allowing a new paste
317 * - (tree) insert new items, the tree store will ref them
318 * attaching each item to its parent
319 * recursively checking edition status of the topmost parent
320 * selecting the first item at end
321 * - (menu) unreffing the copy got from clipboard
324 nact_menubar_edit_on_paste_into( GtkAction
*gtk_action
, BaseWindow
*window
)
326 static const gchar
*thisfn
= "nact_menubar_edit_on_paste_into";
330 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
332 items
= prepare_for_paste( window
);
334 view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
335 nact_tree_ieditable_insert_into( NACT_TREE_IEDITABLE( view
), items
);
336 na_object_free_items( items
);
341 prepare_for_paste( BaseWindow
*window
)
343 static const gchar
*thisfn
= "nact_menubar_edit_prepare_for_paste";
345 NactClipboard
*clipboard
;
346 NAObjectAction
*action
;
350 BAR_WINDOW_VALUE( window
, NULL
);
352 clipboard
= nact_main_window_get_clipboard( NACT_MAIN_WINDOW( window
));
353 items
= nact_clipboard_primary_get( clipboard
, &renumber
);
356 /* if pasted items are profiles, then setup the target action
358 for( it
= items
; it
; it
= it
->next
){
360 if( NA_IS_OBJECT_PROFILE( it
->data
)){
362 g_object_get( G_OBJECT( window
), MAIN_PROP_ITEM
, &action
, NULL
);
363 g_return_val_if_fail( NA_IS_OBJECT_ACTION( action
), NULL
);
367 relabel
= na_updater_should_pasted_be_relabeled( bar
->private->updater
, NA_OBJECT( it
->data
));
368 na_object_prepare_for_paste( it
->data
, relabel
, renumber
, action
);
369 na_object_check_status( it
->data
);
372 g_debug( "%s: action=%p (%s)",
373 thisfn
, ( void * ) action
, action
? G_OBJECT_TYPE_NAME( action
): "(null)" );
379 * nact_menubar_edit_on_duplicate:
380 * @gtk_action: the #GtkAction action.
381 * @window: the #BaseWindow main window.
383 * duplicate is just as paste, with the difference that content comes
384 * from the current selection, instead of coming from the clipboard
386 * this is nonetheless a bit more complicated because when we duplicate
387 * some items (e.g. a multiple selection), we expect to see the new
388 * items just besides the original ones...
391 nact_menubar_edit_on_duplicate( GtkAction
*gtk_action
, BaseWindow
*window
)
393 static const gchar
*thisfn
= "nact_menubar_edit_on_duplicate";
394 NAObjectAction
*action
;
401 BAR_WINDOW_VOID( window
);
403 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
404 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
405 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
407 items
= na_object_copyref_items( bar
->private->selected_items
);
409 for( it
= items
; it
; it
= it
->next
){
410 obj
= NA_OBJECT( na_object_duplicate( it
->data
));
413 /* duplicating a profile
414 * as we insert in sibling mode, the parent doesn't change
416 if( NA_IS_OBJECT_PROFILE( obj
)){
417 action
= NA_OBJECT_ACTION( na_object_get_parent( it
->data
));
420 relabel
= na_updater_should_pasted_be_relabeled( bar
->private->updater
, obj
);
421 na_object_prepare_for_paste( obj
, relabel
, TRUE
, action
);
422 na_object_set_origin( obj
, NULL
);
423 na_object_check_status( obj
);
424 dup
= g_list_prepend( NULL
, obj
);
425 view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
426 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( view
), dup
, it
->data
);
427 na_object_free_items( dup
);
430 na_object_free_items( items
);
434 * nact_menubar_edit_on_delete:
435 * @gtk_action: the #GtkAction action.
436 * @window: the #BaseWindow main window.
438 * deletes the visible selection
439 * - (tree) get new refs on selected items
440 * - (tree) remove selected items, unreffing objects
441 * - (main) add selected items to main list of deleted,
442 * moving newref from list_from_tree to main_list_of_deleted
443 * - (tree) select next row (if any, or previous if any, or none)
445 * note that we get from selection a list of trees, but we don't have
446 * yet ensured that each element of this tree is actually deletable
447 * each branch of this list must be recursively deletable in order
448 * this branch itself be deleted
451 nact_menubar_edit_on_delete( GtkAction
*gtk_action
, BaseWindow
*window
)
453 static const gchar
*thisfn
= "nact_menubar_edit_on_delete";
456 GSList
*non_deletables
;
459 BAR_WINDOW_VOID( window
);
461 g_debug( "%s: gtk_action=%p, window=%p", thisfn
, ( void * ) gtk_action
, ( void * ) window
);
462 g_return_if_fail( GTK_IS_ACTION( gtk_action
));
463 g_return_if_fail( NACT_IS_MAIN_WINDOW( window
));
465 items
= na_object_copyref_items( bar
->private->selected_items
);
466 non_deletables
= NULL
;
467 to_delete
= get_deletables( bar
->private->updater
, items
, &non_deletables
);
469 if( non_deletables
){
470 gchar
*second
= na_core_utils_slist_join_at_end( non_deletables
, "\n" );
471 base_window_display_error_dlg(
472 BASE_WINDOW( window
),
473 _( "Not all items have been deleted as following ones are not modifiable:" ),
476 na_core_utils_slist_free( non_deletables
);
480 view
= nact_main_window_get_items_view( NACT_MAIN_WINDOW( window
));
481 nact_tree_ieditable_delete( NACT_TREE_IEDITABLE( view
), to_delete
, TREE_OPE_DELETE
);
484 na_object_free_items( items
);
488 get_deletables( NAUpdater
*updater
, GList
*selected
, GSList
**non_deletables
)
493 GSList
*sub_deletables
;
497 for( it
= selected
; it
; it
= it
->next
){
499 if( !na_updater_is_item_writable( updater
, NA_OBJECT_ITEM( it
->data
), &reason
)){
500 *non_deletables
= g_slist_prepend(
501 *non_deletables
, add_non_deletable_msg( NA_OBJECT_ITEM( it
->data
), reason
));
505 if( NA_IS_OBJECT_MENU( it
->data
)){
506 subitems
= na_object_get_items( it
->data
);
507 sub_deletables
= get_deletables_rec( updater
, subitems
);
509 if( sub_deletables
){
510 *non_deletables
= g_slist_concat( *non_deletables
, sub_deletables
);
515 to_delete
= g_list_prepend( to_delete
, na_object_ref( it
->data
));
522 get_deletables_rec( NAUpdater
*updater
, GList
*tree
)
530 for( it
= tree
; it
; it
= it
->next
){
532 if( !na_updater_is_item_writable( updater
, NA_OBJECT_ITEM( it
->data
), &reason
)){
533 msgs
= g_slist_prepend(
534 msgs
, add_non_deletable_msg( NA_OBJECT_ITEM( it
->data
), reason
));
538 if( NA_IS_OBJECT_MENU( it
->data
)){
539 subitems
= na_object_get_items( it
->data
);
540 msgs
= g_slist_concat( msgs
, get_deletables_rec( updater
, subitems
));
548 add_non_deletable_msg( const NAObjectItem
*item
, gint reason
)
554 label
= na_object_get_label( item
);
555 reason_str
= na_io_provider_get_readonly_tooltip( reason
);
557 msg
= g_strdup_printf( "%s: %s", label
, reason_str
);
559 g_free( reason_str
);
566 * as we are coming from cut or copy to clipboard, report selection
567 * counters to clipboard ones
570 update_clipboard_counters( BaseWindow
*window
)
572 BAR_WINDOW_VOID( window
);
574 bar
->private->clipboard_menus
= bar
->private->selected_menus
;
575 bar
->private->clipboard_actions
= bar
->private->selected_actions
;
576 bar
->private->clipboard_profiles
= bar
->private->selected_profiles
;
578 g_debug( "nact_menubar_update_clipboard_counters: menus=%d, actions=%d, profiles=%d",
579 bar
->private->clipboard_menus
, bar
->private->clipboard_actions
, bar
->private->clipboard_profiles
);
581 g_signal_emit_by_name( bar
, MENUBAR_SIGNAL_UPDATE_SENSITIVITIES
);
585 * nact_menubar_edit_on_reload:
586 * @gtk_action: the #GtkAction action.
587 * @window: the #BaseWindow main window.
589 * Reload items from I/O storage subsystems.
592 nact_menubar_edit_on_reload( GtkAction
*gtk_action
, BaseWindow
*window
)
594 nact_main_window_reload( NACT_MAIN_WINDOW( window
));
598 * nact_menubar_edit_on_preferences:
599 * @gtk_action: the #GtkAction action.
600 * @window: the #BaseWindow main window.
605 nact_menubar_edit_on_prefererences( GtkAction
*gtk_action
, BaseWindow
*window
)
607 nact_preferences_editor_run( window
);