Update copyright message
[nautilus-actions.git] / src / nact / nact-iactions-list.c
blob0c2a74e59b50215d8efe4c8b8c54bc99a214b5ca
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 <api/na-object-api.h>
37 #include "base-window.h"
38 #include "base-keysyms.h"
39 #include "nact-main-menubar.h"
40 #include "nact-main-tab.h"
41 #include "nact-marshal.h"
42 #include "nact-tree-model.h"
43 #include "nact-iactions-list.h"
44 #include "nact-iactions-list-priv.h"
46 /* private interface data
48 struct NactIActionsListInterfacePrivate {
49 void *empty; /* so that gcc -pedantic is happy */
52 /* when iterating through a selection
54 typedef struct {
55 gboolean has_menu_or_action;
57 SelectionIter;
59 /* signals
61 enum {
62 LIST_COUNT_UPDATED,
63 SELECTION_CHANGED,
64 FOCUS_IN,
65 FOCUS_OUT,
66 COLUMN_EDITED,
67 STATUS_CHANGED,
68 LAST_SIGNAL
71 static gint st_signals[ LAST_SIGNAL ] = { 0 };
72 gboolean st_iactions_list_initialized = FALSE;
73 gboolean st_iactions_list_finalized = FALSE;
75 static GType register_type( void );
76 static void interface_base_init( NactIActionsListInterface *klass );
77 static void interface_base_finalize( NactIActionsListInterface *klass );
79 static void free_items_callback( NactIActionsList *instance, GList *items );
80 static void free_column_edited_callback( NactIActionsList *instance, NAObject *object, gchar *text, gint column );
82 static gboolean are_profiles_displayed( NactIActionsList *instance, IActionsListInstanceData *ialid );
83 static void display_label( GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, NactIActionsList *instance );
84 static gboolean filter_selection( GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, NactIActionsList *instance );
85 static gboolean filter_selection_is_homogeneous( GtkTreeSelection *selection, NAObject *object );
86 static void filter_selection_iter( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, SelectionIter *str );
87 static gboolean filter_selection_has_menu_or_action( GtkTreeSelection *selection );
88 static gboolean filter_selection_is_implicitely_selected( NAObject *object );
89 static void filter_selection_set_implicitely_selected_childs( NAObject *object, gboolean select );
90 static gboolean have_dnd_mode( NactIActionsList *instance, IActionsListInstanceData *ialid );
91 static gboolean have_filter_selection_mode( NactIActionsList *instance, IActionsListInstanceData *ialid );
92 static void inline_edition( NactIActionsList *instance );
93 static gboolean is_iduplicable_proxy( NactIActionsList *instance, IActionsListInstanceData *ialid );
94 static gboolean on_button_press_event( GtkWidget *widget, GdkEventButton *event, NactIActionsList *instance );
95 static void on_edition_status_changed( NactIActionsList *instance, NAIDuplicable *object );
96 static gboolean on_focus_in( GtkWidget *widget, GdkEventFocus *event, NactIActionsList *instance );
97 static gboolean on_focus_out( GtkWidget *widget, GdkEventFocus *event, NactIActionsList *instance );
98 static gboolean on_key_pressed_event( GtkWidget *widget, GdkEventKey *event, NactIActionsList *instance );
99 static void on_label_edited( GtkCellRendererText *renderer, const gchar *path, const gchar *text, NactIActionsList *instance );
100 static void on_tab_updatable_item_updated( NactIActionsList *instance, NAObject *object, gboolean force_display );
101 static void open_popup( NactIActionsList *instance, GdkEventButton *event );
103 GType
104 nact_iactions_list_get_type( void )
106 static GType iface_type = 0;
108 if( !iface_type ){
109 iface_type = register_type();
112 return( iface_type );
115 static GType
116 register_type( void )
118 static const gchar *thisfn = "nact_iactions_list_register_type";
119 GType type;
121 static const GTypeInfo info = {
122 sizeof( NactIActionsListInterface ),
123 ( GBaseInitFunc ) interface_base_init,
124 ( GBaseFinalizeFunc ) interface_base_finalize,
125 NULL,
126 NULL,
127 NULL,
130 NULL
133 g_debug( "%s", thisfn );
135 type = g_type_register_static( G_TYPE_INTERFACE, "NactIActionsList", &info, 0 );
137 g_type_interface_add_prerequisite( type, BASE_WINDOW_TYPE );
139 return( type );
142 static void
143 interface_base_init( NactIActionsListInterface *klass )
145 static const gchar *thisfn = "nact_iactions_list_interface_base_init";
147 if( !st_iactions_list_initialized ){
149 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
151 klass->private = g_new0( NactIActionsListInterfacePrivate, 1 );
154 * nact-iactions-list-count-updated:
156 * This signal is emitted byIActionsList to its implementor when
157 * it has been asked to refill the list.
159 * It sends as arguments to the connected handlers the total
160 * count of menus, actions and profiles in the stored list.
162 st_signals[ LIST_COUNT_UPDATED ] = g_signal_new(
163 IACTIONS_LIST_SIGNAL_LIST_COUNT_UPDATED,
164 G_TYPE_OBJECT,
165 G_SIGNAL_RUN_LAST,
166 0, /* no default handler */
167 NULL,
168 NULL,
169 nact_marshal_VOID__INT_INT_INT,
170 G_TYPE_NONE,
172 G_TYPE_INT,
173 G_TYPE_INT,
174 G_TYPE_INT );
177 * nact-iactions-list-selection-changed:
179 * This signal is emitted byIActionsList to its implementor,
180 * in response to the "changed" Gtk signal, each time the
181 * selection has changed in the treeview.
183 * It is not just a proxy, as we add a list of currently selected
184 * objects as user_data (see #nact_iactions_list_on_treeview_selection_changed()).
186 * Note that IActionsList is itself connected to this signal,
187 * in order to convert the signal to an interface API
188 * (see #on_iactions_list_selection_changed()).
190 * The main window is typically the only interested. It will
191 * setup current item and profiles, before emitting another
192 * signal targeting the notebook tabs
193 * (see. MAIN_WINDOW_SIGNAL_SELECTION_CHANGED signal).
195 st_signals[ SELECTION_CHANGED ] = g_signal_new_class_handler(
196 IACTIONS_LIST_SIGNAL_SELECTION_CHANGED,
197 G_TYPE_OBJECT,
198 G_SIGNAL_RUN_CLEANUP,
199 G_CALLBACK( free_items_callback ),
200 NULL,
201 NULL,
202 g_cclosure_marshal_VOID__POINTER,
203 G_TYPE_NONE,
205 G_TYPE_POINTER );
208 * nact-iactions-list-focus-in:
210 * This signal is emitted byIActionsList when it gains the focus.
211 * In particular, edition menu is disabled outside of the treeview.
213 st_signals[ FOCUS_IN ] = g_signal_new(
214 IACTIONS_LIST_SIGNAL_FOCUS_IN,
215 G_TYPE_OBJECT,
216 G_SIGNAL_RUN_LAST,
218 NULL,
219 NULL,
220 g_cclosure_marshal_VOID__POINTER,
221 G_TYPE_NONE,
223 G_TYPE_POINTER );
226 * nact-iactions-list-focus-out:
228 * This signal is emitted byIActionsList when it looses the focus.
229 * In particular, edition menu is disabled outside of the treeview.
231 st_signals[ FOCUS_OUT ] = g_signal_new(
232 IACTIONS_LIST_SIGNAL_FOCUS_OUT,
233 G_TYPE_OBJECT,
234 G_SIGNAL_RUN_LAST,
236 NULL,
237 NULL,
238 g_cclosure_marshal_VOID__POINTER,
239 G_TYPE_NONE,
241 G_TYPE_POINTER );
244 * nact-iactions-list-column-edited:
246 * This signal is emitted byIActionsList when there has been an
247 * inline edition in one of the columns.
248 * The edition tabs should updates their own entries.
250 st_signals[ COLUMN_EDITED ] = g_signal_new_class_handler(
251 IACTIONS_LIST_SIGNAL_COLUMN_EDITED,
252 G_TYPE_OBJECT,
253 G_SIGNAL_RUN_CLEANUP,
254 G_CALLBACK( free_column_edited_callback ),
255 NULL,
256 NULL,
257 nact_marshal_VOID__POINTER_POINTER_INT,
258 G_TYPE_NONE,
260 G_TYPE_POINTER,
261 G_TYPE_POINTER,
262 G_TYPE_INT );
265 * nact-iactions-list-status-changed:
267 * This signal is emitted byIActionsList to its implementor
268 * when the status of an item has changed.
270 st_signals[ STATUS_CHANGED ] = g_signal_new(
271 IACTIONS_LIST_SIGNAL_STATUS_CHANGED,
272 G_TYPE_OBJECT,
273 G_SIGNAL_RUN_LAST,
274 0, /* no default handler */
275 NULL,
276 NULL,
277 g_cclosure_marshal_VOID__POINTER,
278 G_TYPE_NONE,
280 G_TYPE_POINTER );
282 st_iactions_list_initialized = TRUE;
286 static void
287 free_items_callback( NactIActionsList *instance, GList *items )
289 g_debug( "nact_iactions_list_free_items_callback: selection=%p (%d items)",
290 ( void * ) items, g_list_length( items ));
292 na_object_unref_selected_items( items );
295 static void
296 free_column_edited_callback( NactIActionsList *instance, NAObject *object, gchar *text, gint column )
298 static const gchar *thisfn = "nact_iactions_list_free_column_edited_callback";
300 g_debug( "%s: instance=%p, object=%p (%s), text=%s, column=%d",
301 thisfn, ( void * ) instance, ( void * ) object, G_OBJECT_TYPE_NAME( object ), text, column );
303 g_free( text );
306 static void
307 interface_base_finalize( NactIActionsListInterface *klass )
309 static const gchar *thisfn = "nact_iactions_list_interface_base_finalize";
311 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
313 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
315 st_iactions_list_finalized = TRUE;
317 g_free( klass->private );
322 * nact_iactions_list_initial_load_toplevel:
323 * @instance: this #NactIActionsList *instance.
325 * Allocates and initializes the ActionsList widget.
327 * GtkTreeView is created with NactTreeModel model
328 * NactTreeModel
329 * implements EggTreeMultiDragSourceIface
330 * is derived from GtkTreeModelFilter
331 * GtkTreeModelFilter is built on top of GtkTreeStore
333 * Please note that management mode for the list should have been set
334 * before calling this function.
336 void
337 nact_iactions_list_initial_load_toplevel( NactIActionsList *instance )
339 static const gchar *thisfn = "nact_iactions_list_initial_load_toplevel";
340 GtkWidget *label;
341 GtkTreeView *treeview;
342 GtkTreeViewColumn *column;
343 GtkCellRenderer *renderer;
344 IActionsListInstanceData *ialid;
346 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
347 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
349 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
351 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
352 ialid = nact_iactions_list_priv_get_instance_data( instance );
353 ialid->selection_changed_allowed = FALSE;
355 /* associates the ActionsList to the label */
356 label = base_window_get_widget( BASE_WINDOW( instance ), "ActionsListLabel" );
357 gtk_label_set_mnemonic_widget( GTK_LABEL( label ), GTK_WIDGET( treeview ));
359 nact_tree_model_initial_load( BASE_WINDOW( instance ), treeview );
360 gtk_tree_view_set_enable_tree_lines( treeview, TRUE );
362 /* create visible columns on the tree view
364 /* icon: no header */
365 column = gtk_tree_view_column_new_with_attributes(
366 "icon",
367 gtk_cell_renderer_pixbuf_new(),
368 "pixbuf", IACTIONS_LIST_ICON_COLUMN,
369 NULL );
370 gtk_tree_view_append_column( treeview, column );
372 renderer = gtk_cell_renderer_text_new();
373 column = gtk_tree_view_column_new_with_attributes(
374 "label",
375 renderer,
376 "text", IACTIONS_LIST_LABEL_COLUMN,
377 NULL );
378 gtk_tree_view_column_set_sort_column_id( column, IACTIONS_LIST_LABEL_COLUMN );
379 gtk_tree_view_column_set_cell_data_func(
380 column, renderer, ( GtkTreeCellDataFunc ) display_label, instance, NULL );
381 gtk_tree_view_append_column( treeview, column );
386 * nact_iactions_list_runtime_init_toplevel:
387 * @window: this #NactIActionsList *instance.
388 * @items: list of #NAObject actions and menus as provided by #NAPivot.
390 * Allocates and initializes the ActionsList widget.
392 void
393 nact_iactions_list_runtime_init_toplevel( NactIActionsList *instance, GList *items )
395 static const gchar *thisfn = "nact_iactions_list_runtime_init_toplevel";
396 GtkTreeView *treeview;
397 NactTreeModel *model;
398 gboolean have_dnd;
399 gboolean have_filter_selection;
400 gboolean is_proxy;
401 GtkTreeSelection *selection;
402 IActionsListInstanceData *ialid;
403 GtkTreeViewColumn *column;
404 GList *renderers;
406 g_debug( "%s: instance=%p, items=%p (%d items)",
407 thisfn, ( void * ) instance, ( void * ) items, g_list_length( items ));
408 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
410 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
412 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
413 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
415 ialid = nact_iactions_list_priv_get_instance_data( instance );
416 ialid->selection_changed_allowed = FALSE;
417 have_dnd = have_dnd_mode( instance, ialid );
418 have_filter_selection = have_filter_selection_mode( instance, ialid );
420 if( have_filter_selection ){
421 selection = gtk_tree_view_get_selection( treeview );
422 gtk_tree_selection_set_select_function( selection, ( GtkTreeSelectionFunc ) filter_selection, instance, NULL );
425 nact_tree_model_runtime_init( model, have_dnd );
427 /* set up selection control */
428 base_window_signal_connect(
429 BASE_WINDOW( instance ),
430 G_OBJECT( gtk_tree_view_get_selection( treeview )),
431 "changed",
432 G_CALLBACK( nact_iactions_list_on_treeview_selection_changed ));
434 /* catch press 'Enter' */
435 base_window_signal_connect(
436 BASE_WINDOW( instance ),
437 G_OBJECT( treeview ),
438 "key-press-event",
439 G_CALLBACK( on_key_pressed_event ));
441 /* catch double-click */
442 base_window_signal_connect(
443 BASE_WINDOW( instance ),
444 G_OBJECT( treeview ),
445 "button-press-event",
446 G_CALLBACK( on_button_press_event ));
448 /* updates the treeview display when an item is modified */
449 ialid->tab_updated_handler = base_window_signal_connect(
450 BASE_WINDOW( instance ),
451 G_OBJECT( instance ),
452 TAB_UPDATABLE_SIGNAL_ITEM_UPDATED,
453 G_CALLBACK( on_tab_updatable_item_updated ));
455 /* enable/disable edit menu item accelerators depending of
456 * which widget has the focus */
457 base_window_signal_connect(
458 BASE_WINDOW( instance ),
459 G_OBJECT( treeview ),
460 "focus-in-event",
461 G_CALLBACK( on_focus_in ));
463 base_window_signal_connect(
464 BASE_WINDOW( instance ),
465 G_OBJECT( treeview ),
466 "focus-out-event",
467 G_CALLBACK( on_focus_out ));
469 /* label edition: inform the corresponding tab */
470 column = gtk_tree_view_get_column( treeview, IACTIONS_LIST_LABEL_COLUMN );
471 renderers = gtk_cell_layout_get_cells( GTK_CELL_LAYOUT( column ));
472 base_window_signal_connect(
473 BASE_WINDOW( instance ),
474 G_OBJECT( renderers->data ),
475 "edited",
476 G_CALLBACK( on_label_edited ));
478 /* records NactIActionsList as a proxy for edition status
479 * modification */
480 is_proxy = is_iduplicable_proxy( instance, ialid );
481 if( is_proxy ){
482 na_iduplicable_register_consumer( G_OBJECT( instance ));
484 base_window_signal_connect(
485 BASE_WINDOW( instance ),
486 G_OBJECT( instance ),
487 NA_IDUPLICABLE_SIGNAL_STATUS_CHANGED,
488 G_CALLBACK( on_edition_status_changed ));
491 /* fill the model after having connected the signals
492 * so that callbacks are triggered at last
494 nact_iactions_list_fill( instance, items );
496 /* force the treeview to have the focus at start
498 gtk_widget_grab_focus( GTK_WIDGET( treeview ));
503 * nact_iactions_list_all_widgets_showed:
504 * @window: this #NactIActionsList *instance.
506 void
507 nact_iactions_list_all_widgets_showed( NactIActionsList *instance )
509 static const gchar *thisfn = "nact_iactions_list_all_widgets_showed";
511 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
512 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
514 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
516 nact_iactions_list_bis_select_first_row( instance );
521 * nact_iactions_list_dispose:
522 * @window: this #NactIActionsList *instance.
524 void
525 nact_iactions_list_dispose( NactIActionsList *instance )
527 static const gchar *thisfn = "nact_iactions_list_dispose";
528 GtkTreeView *treeview;
529 NactTreeModel *model;
530 IActionsListInstanceData *ialid;
532 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
533 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
535 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
537 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
538 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
540 ialid = nact_iactions_list_priv_get_instance_data( instance );
541 g_list_free( ialid->modified_items );
542 ialid->modified_items = NULL;
544 ialid->selection_changed_allowed = FALSE;
545 nact_tree_model_dispose( model );
547 g_free( ialid );
552 * nact_iactions_list_brief_tree_dump:
553 * @instance: this #NactIActionsList implementation.
555 * Brief dump of the tree store content.
557 void
558 nact_iactions_list_brief_tree_dump( NactIActionsList *instance )
560 GtkTreeView *treeview;
561 NactTreeModel *model;
563 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
565 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
567 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
568 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
569 nact_tree_model_dump( model );
574 * nact_iactions_list_collapse_all:
575 * @instance: this #NactIActionsList implementation.
577 * Collapse all the tree hierarchy.
579 void
580 nact_iactions_list_collapse_all( NactIActionsList *instance )
582 static const gchar *thisfn = "nact_iactions_list_collapse_all";
583 GtkTreeView *treeview;
585 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
586 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
588 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
590 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
591 gtk_tree_view_collapse_all( treeview );
596 * nact_iactions_list_display_order_change:
597 * @instance: this #NactIActionsList implementation.
598 * @order_mode: the new order mode.
600 * Setup the new order mode.
602 void
603 nact_iactions_list_display_order_change( NactIActionsList *instance, gint order_mode )
605 GtkTreeView *treeview;
606 NactTreeModel *model;
608 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
610 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
612 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
613 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
614 nact_tree_model_display_order_change( model, order_mode );
619 * nact_iactions_list_expand_all:
620 * @instance: this #NactIActionsList implementation.
622 * Expand all the tree hierarchy.
624 void
625 nact_iactions_list_expand_all( NactIActionsList *instance )
627 static const gchar *thisfn = "nact_iactions_list_expand_all";
628 GtkTreeView *treeview;
630 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
631 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
633 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
635 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
636 gtk_tree_view_expand_all( treeview );
641 * nact_iactions_list_fill:
642 * @instance: this #NactIActionsList instance.
644 * Fill the listbox with the provided list of items.
646 * Menus are expanded, profiles are not.
647 * The selection is reset to the first line of the tree, if there is one.
649 void
650 nact_iactions_list_fill( NactIActionsList *instance, GList *items )
652 static const gchar *thisfn = "nact_iactions_list_fill";
653 GtkTreeView *treeview;
654 NactTreeModel *model;
655 gboolean profiles_are_displayed;
656 IActionsListInstanceData *ialid;
658 g_debug( "%s: instance=%p, items=%p", thisfn, ( void * ) instance, ( void * ) items );
659 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
661 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
663 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
664 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
666 nact_iactions_list_bis_clear_selection( instance, treeview );
668 ialid = nact_iactions_list_priv_get_instance_data( instance );
669 profiles_are_displayed = are_profiles_displayed( instance, ialid );
671 ialid->selection_changed_allowed = FALSE;
672 nact_tree_model_fill( model, items, profiles_are_displayed );
673 ialid->selection_changed_allowed = TRUE;
675 g_list_free( ialid->modified_items );
676 ialid->modified_items = NULL;
678 g_signal_emit_by_name(
679 instance,
680 MAIN_WINDOW_SIGNAL_LEVEL_ZERO_ORDER_CHANGED,
681 GINT_TO_POINTER( FALSE ));
683 ialid->menus = 0;
684 ialid->actions = 0;
685 ialid->profiles = 0;
687 if( profiles_are_displayed ){
688 na_object_item_count_items( items, &ialid->menus, &ialid->actions, &ialid->profiles, TRUE );
689 nact_iactions_list_priv_send_list_count_updated_signal( instance, ialid );
695 * nact_iactions_list_get_management_mode:
696 * @instance: this #NactIActionsList instance.
698 * Returns: the current management mode of the list.
700 gint
701 nact_iactions_list_get_management_mode( NactIActionsList *instance )
703 gint mode = 0;
704 IActionsListInstanceData *ialid;
706 g_return_val_if_fail( NACT_IS_IACTIONS_LIST( instance ), 0 );
708 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
710 ialid = nact_iactions_list_priv_get_instance_data( instance );
711 mode = ialid->management_mode;
714 return( mode );
718 * nact_iactions_list_has_modified_items:
719 * @window: this #NactIActionsList instance.
721 * Returns: %TRUE if at least there is one modified item in the list.
723 gboolean
724 nact_iactions_list_has_modified_items( NactIActionsList *instance )
726 gboolean has_modified = FALSE;
727 /*GtkTreeView *treeview;
728 NactTreeModel *model;*/
729 IActionsListInstanceData *ialid;
731 g_return_val_if_fail( NACT_IS_IACTIONS_LIST( instance ), FALSE );
733 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
735 ialid = nact_iactions_list_priv_get_instance_data( instance );
736 has_modified = ( g_list_length( ialid->modified_items ) > 0 );
738 /*treeview = get_actions_list_treeview( instance );
739 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
740 nact_tree_model_iter( model, ( FnIterOnStore ) has_modified_iter, &has_modified );*/
743 return( has_modified );
747 * nact_iactions_list_on_treeview_selection_changed:
748 * @selection: current selection.
749 * @instance: this instance of the #NactIActionsList interface.
751 * This is our handler for "changed" signal emitted by the treeview.
752 * The handler is inhibited while filling the list (usually only at
753 * runtime init), and while deleting a selection.
755 void
756 nact_iactions_list_on_treeview_selection_changed( GtkTreeSelection *selection, NactIActionsList *instance )
758 GList *selected_items;
759 IActionsListInstanceData *ialid;
761 ialid = nact_iactions_list_priv_get_instance_data( instance );
762 if( ialid->selection_changed_allowed ){
764 g_debug( "nact_iactions_list_on_treeview_selection_changed" );
765 g_signal_handler_block( instance, ialid->tab_updated_handler );
767 selected_items = nact_iactions_list_bis_get_selected_items( instance );
768 g_debug( "nact_iactions_list_on_treeview_selection_changed: selection=%p (%d items)", ( void * ) selected_items, g_list_length( selected_items ));
769 g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_SELECTION_CHANGED, selected_items );
771 g_signal_handler_unblock( instance, ialid->tab_updated_handler );
773 /* selection list is freed in cleanup handler for the signal */
778 * nact_iactions_list_remove_rec:
779 * @list: list of modified objects.
780 * @object: the object to be removed from the list.
782 * When removing from modified list an object which is no more modified,
783 * then all subitems of the object have also to be removed
785 * Returns: the updated list.
787 GList *
788 nact_iactions_list_remove_rec( GList *list, NAObject *object )
790 GList *subitems, *it;
792 if( NA_IS_OBJECT_ITEM( object )){
793 subitems = na_object_get_items( object );
794 for( it = subitems ; it ; it = it->next ){
795 list = nact_iactions_list_remove_rec( list, it->data );
799 list = g_list_remove( list, object );
801 return( list );
805 * nact_iactions_list_set_management_mode:
806 * @instance: this #NactIActionsList instance.
807 * @mode: management mode.
809 * Set the management mode for this @instance.
811 * For the two known modes (edition mode, export mode), we also allow
812 * multiple selection in the list.
814 void
815 nact_iactions_list_set_management_mode( NactIActionsList *instance, gint mode )
817 GtkTreeView *treeview;
818 GtkTreeSelection *selection;
819 gboolean multiple;
820 IActionsListInstanceData *ialid;
822 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
824 if( st_iactions_list_initialized && !st_iactions_list_finalized ){
826 ialid = nact_iactions_list_priv_get_instance_data( instance );
827 ialid->management_mode = mode;
829 multiple = ( mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION ||
830 mode == IACTIONS_LIST_MANAGEMENT_MODE_EXPORT );
832 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
833 selection = gtk_tree_view_get_selection( treeview );
834 gtk_tree_selection_set_mode( selection, multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE );
838 static gboolean
839 are_profiles_displayed( NactIActionsList *instance, IActionsListInstanceData *ialid )
841 gboolean display;
843 display = ( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION );
845 return( display );
849 * item modified: italic
850 * item not saveable (invalid): red
852 static void
853 display_label( GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, NactIActionsList *instance )
855 NAObject *object;
856 gchar *label;
857 gboolean modified = FALSE;
858 gboolean valid = TRUE;
859 IActionsListInstanceData *ialid;
860 NAObjectItem *item;
861 gboolean writable_item;
863 gtk_tree_model_get( model, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
864 g_object_unref( object );
865 g_return_if_fail( NA_IS_OBJECT( object ));
867 ialid = nact_iactions_list_priv_get_instance_data( instance );
868 label = na_object_get_label( object );
869 g_object_set( cell, "style-set", FALSE, NULL );
870 g_object_set( cell, "foreground-set", FALSE, NULL );
871 /*g_debug( "nact_iactions_list_display_label: %s %s", G_OBJECT_TYPE_NAME( object ), label );*/
873 if( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION ){
875 modified = na_object_is_modified( object );
876 valid = na_object_is_valid( object );
877 item = NA_IS_OBJECT_PROFILE( object ) ? na_object_get_parent( object ) : NA_OBJECT_ITEM( object );
878 writable_item = nact_window_is_item_writable( NACT_WINDOW( instance ), item, NULL );
880 if( modified ){
881 g_object_set( cell, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL );
884 if( !valid ){
885 g_object_set( cell, "foreground", "Red", "foreground-set", TRUE, NULL );
888 g_object_set( cell, "editable", writable_item, NULL );
891 g_object_set( cell, "text", label, NULL );
892 g_free( label );
896 * rationale: it is very difficult to copy anything in the clipboard,
897 * and to hope that this will be easily copyable anywhere after.
898 * We know how to insert profiles, or how to insert actions or menus,
899 * but not how nor where to insert e.g. a mix selection.
901 * So a selection must first be homogeneous, i.e. it only contains
902 * explicitely selected profiles _or_ menus or actions (and their childs).
904 * To simplify the selection management while letting the user actually
905 * select almost anything, we are doing following assumptions:
906 * - when the user selects one row, all childs are also automatically
907 * selected ; visible childs are setup so that they are known as
908 * 'indirectly' selected
909 * - when a row is set as 'indirectly' selected, user cannot select
910 * nor unselect it (sort of readonly or mandatory implied selection)
911 * while the parent stays itself selected
913 static gboolean
914 filter_selection( GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, NactIActionsList *instance )
916 static const gchar *thisfn = "nact_iactions_list_filter_selection";
917 GList *selected_paths;
918 GtkTreeIter iter;
919 NAObject *object;
921 gtk_tree_model_get_iter( model, &iter, path );
922 gtk_tree_model_get( model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
923 g_return_val_if_fail( object, FALSE );
924 g_return_val_if_fail( NA_IS_OBJECT_ID( object ), FALSE );
925 g_object_unref( object );
927 /* if there is not yet any selection, then anything is allowed
929 selected_paths = gtk_tree_selection_get_selected_rows( selection, NULL );
930 if( !selected_paths || !g_list_length( selected_paths )){
931 /*g_debug( "%s: current selection is empty: allowing this one", thisfn );*/
932 filter_selection_set_implicitely_selected_childs( object, !path_currently_selected );
933 return( TRUE );
936 /* if the object at the row is already 'implicitely' selected, i.e.
937 * selected because of the selection of one of its parents, then
938 * nothing is allowed
940 if( filter_selection_is_implicitely_selected( object )){
941 g_debug( "%s: implicitely selected item: selection not allowed", thisfn );
942 return( FALSE );
945 /* object at the row is not 'implicitely' selected: we may so select
946 * or unselect it while the selection stays homogeneous
947 * (rather we set its childs to the corresponding implied status)
949 if( path_currently_selected ||
950 filter_selection_is_homogeneous( selection, object )){
952 filter_selection_set_implicitely_selected_childs( object, !path_currently_selected );
954 return( TRUE );
958 * does the selection stay homogeneous when adding this object ?
960 static gboolean
961 filter_selection_is_homogeneous( GtkTreeSelection *selection, NAObject *object )
963 gboolean homogeneous;
965 if( filter_selection_has_menu_or_action( selection )){
966 homogeneous = !NA_IS_OBJECT_PROFILE( object );
967 } else {
968 homogeneous = NA_IS_OBJECT_PROFILE( object );
971 return( homogeneous );
974 static gboolean
975 filter_selection_has_menu_or_action( GtkTreeSelection *selection )
977 gboolean has_menu_or_action;
978 SelectionIter *str;
980 has_menu_or_action = FALSE;
981 str = g_new0( SelectionIter, 1 );
982 str->has_menu_or_action = has_menu_or_action;
983 gtk_tree_selection_selected_foreach( selection, ( GtkTreeSelectionForeachFunc ) filter_selection_iter, str );
984 has_menu_or_action = str->has_menu_or_action;
985 g_free( str );
987 return( has_menu_or_action );
990 static void
991 filter_selection_iter( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, SelectionIter *str )
993 NAObject *object;
995 gtk_tree_model_get( model, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
997 if( NA_IS_OBJECT_ITEM( object )){
998 str->has_menu_or_action = TRUE;
1001 g_object_unref( object );
1004 static gboolean
1005 filter_selection_is_implicitely_selected( NAObject *object )
1007 gboolean selected;
1009 selected = ( gboolean ) GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( object), "nact-implicit-selection" ));
1011 return( selected );
1015 * the object is being selected (resp. unselected)
1016 * recursively set the 'implicit selection' flag for all its childs
1018 static void
1019 filter_selection_set_implicitely_selected_childs( NAObject *object, gboolean select )
1021 GList *childs, *ic;
1023 if( NA_IS_OBJECT_ITEM( object )){
1024 childs = na_object_get_items( object );
1025 for( ic = childs ; ic ; ic = ic->next ){
1026 g_object_set_data( G_OBJECT( ic->data ), "nact-implicit-selection", GUINT_TO_POINTER(( guint ) select ));
1027 filter_selection_set_implicitely_selected_childs( NA_OBJECT( ic->data ), select );
1032 static gboolean
1033 have_dnd_mode( NactIActionsList *instance, IActionsListInstanceData *ialid )
1035 gboolean have_dnd;
1037 have_dnd = ( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION );
1039 return( have_dnd );
1042 static gboolean
1043 have_filter_selection_mode( NactIActionsList *instance, IActionsListInstanceData *ialid )
1045 gboolean have_filter;
1047 have_filter = ( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION );
1049 return( have_filter );
1053 * triggered by 'F2' key
1054 * only in edition mode
1056 static void
1057 inline_edition( NactIActionsList *instance )
1059 static const gchar *thisfn = "nact_iactions_list_inline_edition";
1060 IActionsListInstanceData *ialid;
1061 GtkTreeView *treeview;
1062 GtkTreeSelection *selection;
1063 GList *listrows;
1064 GtkTreePath *path;
1065 GtkTreeViewColumn *column;
1067 g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
1068 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
1070 ialid = nact_iactions_list_priv_get_instance_data( instance );
1071 if( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION ){
1073 ialid->selection_changed_allowed = FALSE;
1075 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
1076 selection = gtk_tree_view_get_selection( treeview );
1077 listrows = gtk_tree_selection_get_selected_rows( selection, NULL );
1079 if( g_list_length( listrows ) == 1 ){
1080 path = ( GtkTreePath * ) listrows->data;
1081 column = gtk_tree_view_get_column( treeview, IACTIONS_LIST_LABEL_COLUMN );
1082 gtk_tree_view_set_cursor( treeview, path, column, TRUE );
1085 g_list_foreach( listrows, ( GFunc ) gtk_tree_path_free, NULL );
1086 g_list_free( listrows );
1088 ialid->selection_changed_allowed = TRUE;
1092 static gboolean
1093 is_iduplicable_proxy( NactIActionsList *instance, IActionsListInstanceData *ialid )
1095 gboolean is_proxy;
1097 is_proxy = ( ialid->management_mode == IACTIONS_LIST_MANAGEMENT_MODE_EDITION );
1098 g_debug( "nact_iactions_list_is_iduplicable_proxy: is_proxy=%s", is_proxy ? "True":"False" );
1100 return( is_proxy );
1103 static gboolean
1104 on_button_press_event( GtkWidget *widget, GdkEventButton *event, NactIActionsList *instance )
1106 /*static const gchar *thisfn = "nact_iactions_list_v_on_button_pres_event";
1107 g_debug( "%s: widget=%p, event=%p, user_data=%p", thisfn, widget, event, user_data );*/
1109 gboolean stop = FALSE;
1111 /* double-click of left button */
1112 if( event->type == GDK_2BUTTON_PRESS && event->button == 1 ){
1113 nact_iactions_list_bis_toggle_collapse( instance );
1114 stop = TRUE;
1117 /* single click on right button */
1118 if( event->type == GDK_BUTTON_PRESS && event->button == 3 ){
1119 open_popup( instance, event );
1120 stop = TRUE;
1123 return( stop );
1126 static void
1127 on_edition_status_changed( NactIActionsList *instance, NAIDuplicable *object )
1129 GtkTreeView *treeview;
1130 NactTreeModel *model;
1131 IActionsListInstanceData *ialid;
1133 ialid = nact_iactions_list_priv_get_instance_data( instance );
1135 g_debug( "nact_iactions_list_on_edition_status_changed: instance=%p, object=%p (%s)",
1136 ( void * ) instance,
1137 ( void * ) object, G_OBJECT_TYPE_NAME( object ));
1139 g_return_if_fail( NA_IS_OBJECT( object ));
1141 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
1142 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
1143 nact_tree_model_display( model, NA_OBJECT( object ));
1145 if( na_object_is_modified( object )){
1146 if( !g_list_find( ialid->modified_items, object )){
1147 ialid->modified_items = g_list_prepend( ialid->modified_items, object );
1149 } else {
1150 ialid->modified_items = nact_iactions_list_remove_rec( ialid->modified_items, NA_OBJECT( object ));
1153 /* do not send status-changed signal while filling the tree
1155 if( ialid->selection_changed_allowed ){
1156 g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_STATUS_CHANGED, NULL );
1161 * focus is monitored to avoid an accelerator being pressed while on a tab
1162 * triggers an unwaited operation on the list
1163 * e.g. when editing an entry field on the tab, pressing Del should _not_
1164 * delete current row in the list !
1166 static gboolean
1167 on_focus_in( GtkWidget *widget, GdkEventFocus *event, NactIActionsList *instance )
1169 /*static const gchar *thisfn = "nact_iactions_list_on_focus_in";*/
1170 gboolean stop = FALSE;
1172 /*g_debug( "%s: widget=%p, event=%p, instance=%p", thisfn, ( void * ) widget, ( void * ) event, ( void * ) instance );*/
1173 g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_FOCUS_IN, instance );
1175 return( stop );
1178 static gboolean
1179 on_focus_out( GtkWidget *widget, GdkEventFocus *event, NactIActionsList *instance )
1181 /*static const gchar *thisfn = "nact_iactions_list_on_focus_out";*/
1182 gboolean stop = FALSE;
1184 /*g_debug( "%s: widget=%p, event=%p, instance=%p", thisfn, ( void * ) widget, ( void * ) event, ( void * ) instance );*/
1185 g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_FOCUS_OUT, instance );
1187 return( stop );
1190 static gboolean
1191 on_key_pressed_event( GtkWidget *widget, GdkEventKey *event, NactIActionsList *instance )
1193 /*static const gchar *thisfn = "nact_iactions_list_v_on_key_pressed_event";
1194 g_debug( "%s: widget=%p, event=%p, user_data=%p", thisfn, widget, event, user_data );*/
1195 gboolean stop = FALSE;
1197 if( event->keyval == NACT_KEY_Return || event->keyval == NACT_KEY_KP_Enter ){
1198 nact_iactions_list_bis_toggle_collapse( instance );
1199 stop = TRUE;
1202 if( event->keyval == NACT_KEY_F2 ){
1203 inline_edition( instance );
1204 stop = TRUE;
1207 if( event->keyval == NACT_KEY_Right ){
1208 nact_iactions_list_bis_expand_to_first_child( instance );
1209 stop = TRUE;
1212 if( event->keyval == NACT_KEY_Left ){
1213 nact_iactions_list_bis_collapse_to_parent( instance );
1214 stop = TRUE;
1217 return( stop );
1221 * path: path of the edited row, as a string
1222 * text: new text
1224 * - inform tabs so that they can update their fields
1225 * data = object_at_row + new_label
1226 * this will trigger set the object content, and other updates
1228 static void
1229 on_label_edited( GtkCellRendererText *renderer, const gchar *path_str, const gchar *text, NactIActionsList *instance )
1231 GtkTreeView *treeview;
1232 NactTreeModel *model;
1233 NAObject *object;
1234 GtkTreePath *path;
1235 gchar *new_text;
1237 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
1238 model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
1239 path = gtk_tree_path_new_from_string( path_str );
1240 object = nact_tree_model_object_at_path( model, path );
1241 new_text = g_strdup( text );
1243 g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_COLUMN_EDITED, object, new_text, IACTIONS_LIST_LABEL_COLUMN );
1247 * an item has been updated in one of the tabs
1248 * update the treeview to reflects its new edition status
1250 static void
1251 on_tab_updatable_item_updated( NactIActionsList *instance, NAObject *object, gboolean force_display )
1253 static const gchar *thisfn = "nact_iactions_list_on_tab_updatable_item_updated";
1254 GtkTreeView *treeview;
1255 GtkTreeModel *model;
1257 g_debug( "%s: instance=%p, object=%p (%s), force_display=%s", thisfn,
1258 ( void * ) instance, ( void * ) object, G_OBJECT_TYPE_NAME( object ),
1259 force_display ? "True":"False" );
1260 g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
1261 g_return_if_fail( NA_IS_OBJECT( object ));
1262 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
1264 if( object ){
1265 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
1266 model = gtk_tree_view_get_model( treeview );
1267 if( !na_object_check_status_up( object ) && force_display ){
1268 on_edition_status_changed( instance, NA_IDUPLICABLE( object ));
1273 static void
1274 open_popup( NactIActionsList *instance, GdkEventButton *event )
1276 GtkTreeView *treeview;
1277 GtkTreeModel *model;
1278 GtkTreePath *path;
1280 treeview = nact_iactions_list_priv_get_actions_list_treeview( instance );
1281 if( gtk_tree_view_get_path_at_pos( treeview, event->x, event->y, &path, NULL, NULL, NULL )){
1282 model = gtk_tree_view_get_model( treeview );
1283 nact_iactions_list_bis_select_row_at_path( instance, treeview, model, path );
1284 gtk_tree_path_free( path );
1285 nact_main_menubar_open_popup( NACT_MAIN_WINDOW( instance ), event );