[nautilus-actions] New 'Paste into' menu item



commit 1e5abb37ae8e289e456608b58b6dec7a9a77b798
Author: Pierre Wieser <pwieser trychlos org>
Date:   Sun Oct 4 21:35:28 2009 +0200

    New 'Paste into' menu item
    
    Also have rewrote the code which manages action sensitivities.
    Also rewrote the insertion code in the treeview.

 ChangeLog                                     |   52 ++++
 Makefile.am                                   |    1 -
 configure.ac                                  |    5 +-
 src/common/na-object-api.h                    |    2 +-
 src/common/na-object-item-fn.h                |    2 +
 src/common/na-object-item.c                   |   49 +++-
 src/nact/.gitignore                           |    2 +
 src/nact/Makefile.am                          |   18 +-
 src/nact/nact-iactions-list.c                 |  403 +++++++++++++++++--------
 src/nact/nact-iactions-list.h                 |   15 +-
 src/nact/nact-main-menubar.c                  |  355 +++++++++++++++-------
 src/nact/nact-main-menubar.h                  |    4 +-
 src/nact/nact-main-window.c                   |   38 ++-
 src/nact/nact-main-window.h                   |    2 +
 src/nact/nact-marshal.list                    |    1 +
 src/nact/nact-tree-model.c                    |  383 +++++++-----------------
 src/nact/nact-tree-model.h                    |    4 +-
 src/nact/nautilus-actions-config-tool.actions |    1 +
 18 files changed, 817 insertions(+), 520 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 11d48bb..79b59c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,55 @@
+2009-10-04 Pierre Wieser <pwieser trychlos org>
+
+	Use some stored indicators to compute the action sensitivities
+	rather that all recomputing each time.
+	Rewrite insertion code in the treeview.
+
+	* Makefile.am:
+	* configure.ac: No more compile with --enable-iso-c not -pedantic
+	options as GLib-generated marshaling code is not compliant.
+
+	* src/common/na-object-api.h:
+	* src/common/na-object-item-fn.h:
+	* src/common/na-object-item.c (na_object_insert_item):
+	Now accepts a null object as sibling position.
+	(na_object_item_count_items): New function.
+
+	* src/nact/nact-marshal.c:
+	* src/nact/nact-marshal.h: New files.
+
+	* src/nact/Makefile.am: Updated accordingly.
+
+	* src/nact/nact-iactions-list.c:
+	Now maintains a count of menus, actions and profiles items.
+	Send a signal when the count is changed.
+	Rewrite insertion in the treeview.
+
+	* src/nact/nact-iactions-list.c:
+	* src/nact/nact-iactions-list.h
+	(nact_iactions_list_has_exportable): Removed function.
+	(nact_iactions_list_insert_into): New function.
+
+	* src/nact/nact-main-menubar.c:
+	* src/nact/nact-main-menubar.h:
+	Maintains its own counters to compute the actions sensitivities.
+	Define new menu item 'Paste into'.
+	nact_main_menubar_refresh_actions_sensitivity: Removed function.
+	nact_main_menubar_dispose: New function.
+
+	* src/nact/nact-main-window.c:
+	* src/nact/nact-main-window.h:
+	Connect to the iactions-list-selection-changed signal rather than
+	implement the corresponding virtual function.
+	Define new 'main-window-update-sensitivities' signal.
+
+	* src/nact/nact-tree-model.c:
+	* src/nact/nact-tree-model.h:
+	Rewrite insertion code in the tree store.
+	nact_tree_model_insert_into, nact_tree_model_object_at_path: New
+	functions.
+
+	* src/nact/nautilus-actions-config-tool.actions: new 'Paste into' item.
+
 2009-10-02 Pierre Wieser <pwieser trychlos org>
 
 	Centralize plugin routines in a dedicated libna-runtime library.
diff --git a/Makefile.am b/Makefile.am
index 32b4216..fa24eaf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,7 +45,6 @@ DISTCHECK_CONFIGURE_FLAGS = \
 # do not try to install schemas when making distcheck
 DISTCHECK_CONFIGURE_FLAGS += \
 	--disable-schemas-install	\
-	--enable-iso-c				\
 	$(NULL)
 
 EXTRA_DIST = \
diff --git a/configure.ac b/configure.ac
index de2a35f..bc96794 100644
--- a/configure.ac
+++ b/configure.ac
@@ -80,13 +80,16 @@ AC_PROG_MAKE_SET
 # Gnome stuff
 GNOME_COMMON_INIT
 GNOME_MAINTAINER_MODE_DEFINES
-NA_GNOME_COMPILE_WARNINGS([error],[-ansi -pedantic -Wno-overlength-strings])
+NA_GNOME_COMPILE_WARNINGS([error],[-ansi -Wno-overlength-strings])
 AC_SUBST([AM_CPPFLAGS],["${AM_CPPFLAGS} ${DISABLE_DEPRECATED}"])
 # unique 1.0.8 doesn't satisfy this
 #AC_SUBST([AM_CPPFLAGS],["${AM_CPPFLAGS} \
 #	-DG_DISABLE_SINGLE_INCLUDES -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES -DGTK_DISABLE_SINGLE_INCLUDES"])
 AC_SUBST([AM_CFLAGS],["${AM_CFLAGS} ${WARN_CFLAGS}"])
 
+# GLib marshaling
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+
 # libtool
 AM_PROG_LIBTOOL
 
diff --git a/src/common/na-object-api.h b/src/common/na-object-api.h
index cbebf29..c5e2a20 100644
--- a/src/common/na-object-api.h
+++ b/src/common/na-object-api.h
@@ -70,7 +70,7 @@ G_BEGIN_DECLS
 /* NAObjectItem
  */
 
-#define na_object_insert_item( object, item, before ) na_object_item_insert_item( NA_OBJECT_ITEM( object ), NA_OBJECT( item ), NA_OBJECT( before ))
+#define na_object_insert_item( object, item, before ) na_object_item_insert_item( NA_OBJECT_ITEM( object ), NA_OBJECT( item ), ( NAObject * ) before )
 #define na_object_remove_item( object, item )		na_object_item_remove_item( NA_OBJECT_ITEM( object ), NA_OBJECT( item ))
 
 G_END_DECLS
diff --git a/src/common/na-object-item-fn.h b/src/common/na-object-item-fn.h
index 548251c..7a13d0a 100644
--- a/src/common/na-object-item-fn.h
+++ b/src/common/na-object-item-fn.h
@@ -54,6 +54,8 @@ GdkPixbuf     *na_object_item_get_pixbuf( const NAObjectItem *item, GtkWidget *w
 void           na_object_item_insert_item( NAObjectItem *item, const NAObject *object, const NAObject *before );
 void           na_object_item_remove_item( NAObjectItem *item, const NAObject *object );
 
+void           na_object_item_count_items( GList *items, gint *menus, gint *actions, gint *profiles, gboolean recurse );
+
 G_END_DECLS
 
 #endif /* __NA_COMMON_OBJECT_ITEM_FN_H__ */
diff --git a/src/common/na-object-item.c b/src/common/na-object-item.c
index c4aba9e..f4d503e 100644
--- a/src/common/na-object-item.c
+++ b/src/common/na-object-item.c
@@ -108,12 +108,18 @@ na_object_item_insert_item( NAObjectItem *item, const NAObject *object, const NA
 
 	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 	g_return_if_fail( NA_IS_OBJECT( object ));
-	g_return_if_fail( NA_IS_OBJECT( before ));
+	g_return_if_fail( !before || NA_IS_OBJECT( before ));
 
 	if( !item->private->dispose_has_run ){
 
 		if( !g_list_find( item->private->items, ( gpointer ) object )){
-			before_list = g_list_find( item->private->items, ( gconstpointer ) before );
+
+			before_list = NULL;
+
+			if( before ){
+				before_list = g_list_find( item->private->items, ( gconstpointer ) before );
+			}
+
 			if( before_list ){
 				item->private->items = g_list_insert_before( item->private->items, before_list, ( gpointer ) object );
 			} else {
@@ -153,3 +159,42 @@ na_object_item_remove_item( NAObjectItem *item, const NAObject *object )
 		}
 	}
 }
+
+/**
+ * na_object_item_count_items:
+ * @items: a list if #NAObject to be counted.
+ * @menus: will be set to the count of menus.
+ * @actions: will be set to the count of actions.
+ * @profiles: will be set to the count of profiles.
+ * @recurse: whether to recursively count all items, or only those in
+ *  level zero of the list.
+ *
+ * Count the numbers of items if the provided list.
+ *
+ * As this function is recursive, the counters should be initialized by
+ * the caller before calling it.
+ */
+void
+na_object_item_count_items( GList *items, gint *menus, gint *actions, gint *profiles, gboolean recurse )
+{
+	GList *it;
+
+	for( it = items ; it ; it = it->next ){
+
+		/*g_debug( "na_object_item_count_items: item is %s", G_OBJECT_TYPE_NAME( it->data ));*/
+		if( NA_IS_OBJECT_MENU( it->data )){
+			*menus += 1;
+		} else if( NA_IS_OBJECT_ACTION( it->data )){
+			*actions += 1;
+		} else if( NA_IS_OBJECT_PROFILE( it->data )){
+			*profiles += 1;
+		}
+
+		if( recurse ){
+			if( NA_IS_OBJECT_ITEM( it->data )){
+				na_object_item_count_items(
+						NA_OBJECT_ITEM( it->data )->private->items, menus, actions, profiles, recurse );
+			}
+		}
+	}
+}
diff --git a/src/nact/.gitignore b/src/nact/.gitignore
index 582b638..b6d766d 100644
--- a/src/nact/.gitignore
+++ b/src/nact/.gitignore
@@ -1,3 +1,5 @@
+nact-marshal.c
+nact-marshal.h
 nact.desktop
 nautilus-actions-config
 nautilus-actions-config-tool
diff --git a/src/nact/Makefile.am b/src/nact/Makefile.am
index f6809a6..eb5024a 100644
--- a/src/nact/Makefile.am
+++ b/src/nact/Makefile.am
@@ -38,6 +38,11 @@ AM_CPPFLAGS += \
 	$(NAUTILUS_ACTIONS_CFLAGS)							\
 	$(NULL)
 
+BUILT_SOURCES = \
+	nact-marshal.c										\
+	nact-marshal.h										\
+	$(NULL)
+
 nautilus_actions_config_tool_SOURCES = \
 	base-application.c									\
 	base-application.h									\
@@ -89,8 +94,16 @@ nautilus_actions_config_tool_SOURCES = \
 	nact-window.h										\
 	nact-xml-reader.c									\
 	nact-xml-reader.h									\
+	$(BUILT_SOURCES)									\
 	$(NULL)
 
+nact-marshal.h: nact-marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=nact_marshal > $@
+
+nact-marshal.c: nact-marshal.list $(GLIB_GENMARSHAL)
+	echo "#include \"nact-marshal.h\"" > $@ && \
+	$(GLIB_GENMARSHAL) $< --body --prefix=nact_marshal >> $@
+
 nautilus_actions_config_tool_LDADD = \
 	$(top_builddir)/src/common/libna-common.la			\
 	$(top_builddir)/src/runtime/libna-runtime.la		\
@@ -114,11 +127,14 @@ applicationsdir = $(datadir)/applications
 applications_DATA = $(applications_files)
 
 CLEANFILES = \
-	$(applications_files)
+	$(applications_files)								\
+	$(BUILT_SOURCES)									\
+	$(NULL)
 
 EXTRA_DIST = \
 	$(applications_in_files)							\
 	$(pkgdata_DATA)										\
+	nact-marshal.list									\
 	$(NULL)
 
 uninstall-hook:
diff --git a/src/nact/nact-iactions-list.c b/src/nact/nact-iactions-list.c
index 3a3c820..0586106 100644
--- a/src/nact/nact-iactions-list.c
+++ b/src/nact/nact-iactions-list.c
@@ -39,6 +39,7 @@
 #include <common/na-iprefs.h>
 
 #include "nact-application.h"
+#include "nact-marshal.h"
 #include "nact-main-tab.h"
 #include "nact-tree-model.h"
 #include "nact-window.h"
@@ -53,6 +54,7 @@ struct NactIActionsListInterfacePrivate {
 /* signals
  */
 enum {
+	LIST_COUNT_UPDATED,
 	SELECTION_CHANGED,
 	LAST_SIGNAL
 };
@@ -72,6 +74,15 @@ enum {
 	TOGGLE_EXPAND
 };
 
+/* storing the counters
+ */
+typedef struct {
+	gint menus;
+	gint actions;
+	gint profiles;
+}
+	CountersStruct;
+
 /* when iterating through a selection
  */
 typedef struct {
@@ -102,6 +113,7 @@ typedef struct {
  */
 #define SELECTION_CHANGED_SIGNAL_MODE	"nact-iactions-list-selection-changed-signal-mode"
 #define MANAGEMENT_MODE					"nact-iactions-list-management-mode"
+#define LIST_COUNTERS					"nact-iactions-list-counters"
 
 static gint         st_signals[ LAST_SIGNAL ] = { 0 };
 static gboolean     st_initialized = FALSE;
@@ -112,8 +124,13 @@ static void         interface_base_init( NactIActionsListInterface *klass );
 static void         interface_base_finalize( NactIActionsListInterface *klass );
 
 static void         free_items_callback( NactIActionsList *instance, GList *items );
-static GtkTreePath *do_insert_items( GtkTreeView *treeview, GtkTreeModel *model, GList *items, GtkTreePath *path, gint level, GList **parents, gboolean inside );
-static GList       *do_insert_items_add_parent( GList *parents, GtkTreeView *treeview, GtkTreeModel *model, NAObject *parent );
+static void         decremente_counters( NactIActionsList *instance, GList *items );
+static GtkTreePath *get_selection_first_path( GtkTreeView *treeview );
+static void         do_insert_items( GtkTreeView *treeview, GtkTreeModel *model, GList *items, GtkTreePath *path, GList **parents );
+static NAObject    *do_insert_into_first( GtkTreeView *treeview, GtkTreeModel *model, GList *items, GtkTreePath *insert_path, GtkTreePath **new_path );
+static GtkTreePath *do_insert_into_second( GtkTreeView *treeview, GtkTreeModel *model, NAObject *object, GtkTreePath *insert_path, NAObject **parent );
+static void         incremente_counters( NactIActionsList *instance, GList *items );
+static void         update_parents_edition_status( GList *parents, GList *items );
 
 static gchar       *v_get_treeview_name( NactIActionsList *instance );
 
@@ -124,7 +141,6 @@ static void         filter_selection_iter( GtkTreeModel *model, GtkTreePath *pat
 static GtkTreeView *get_actions_list_treeview( NactIActionsList *instance );
 static gboolean     get_item_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, IdToObjectIter *ito );
 static gboolean     get_items_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, GList **items );
-static gboolean     has_exportable_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, gboolean *has_exportable );
 static gboolean     has_modified_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, gboolean *has_modified );
 static gboolean     have_dnd_mode( NactIActionsList *instance );
 static gboolean     have_filter_selection_mode( NactIActionsList *instance );
@@ -142,6 +158,7 @@ static void         on_tab_updatable_item_updated( NactIActionsList *instance, N
 static void         on_iactions_list_selection_changed( NactIActionsList *instance, GSList *selected_items );
 static void         select_first_row( NactIActionsList *instance );
 static void         select_row_at_path( NactIActionsList *instance, GtkTreeView *treeview, GtkTreeModel *model, GtkTreePath *path );
+static void         send_list_count_updated_signal( NactIActionsList *instance, CountersStruct *cs );
 static void         set_selection_changed_mode( NactIActionsList *instance, gboolean authorized );
 static void         toggle_collapse( NactIActionsList *instance );
 static gboolean     toggle_collapse_iter( NactIActionsList *instance, GtkTreeView *treeview, GtkTreeModel *model, GtkTreeIter *iter, NAObject *object, gpointer user_data );
@@ -197,6 +214,29 @@ interface_base_init( NactIActionsListInterface *klass )
 		klass->private = g_new0( NactIActionsListInterfacePrivate, 1 );
 
 		/**
+		 * nact-iactions-list-count-updated:
+		 *
+		 * This signal is emitted byIActionsList to its implementor when
+		 * it has been asked to refill the list.
+		 *
+		 * It sends as arguments to the connected handlers the total
+		 * count of menus, actions and profiles in the stored list.
+		 */
+		st_signals[ LIST_COUNT_UPDATED ] = g_signal_new(
+				IACTIONS_LIST_SIGNAL_LIST_COUNT_UPDATED,
+				G_TYPE_OBJECT,
+				G_SIGNAL_RUN_LAST,
+				0,						/* no default handler */
+				NULL,
+				NULL,
+				nact_marshal_VOID__INT_INT_INT,
+				G_TYPE_NONE,
+				3,
+				G_TYPE_INT,
+				G_TYPE_INT,
+				G_TYPE_INT );
+
+		/**
 		 * nact-iactions-list-selection-changed:
 		 *
 		 * This signal is emitted byIActionsList to its implementor,
@@ -329,6 +369,7 @@ nact_iactions_list_runtime_init_toplevel( NactIActionsList *instance, GList *ite
 	gboolean have_filter_selection;
 	gboolean is_proxy;
 	GtkTreeSelection *selection;
+	CountersStruct *cs;
 
 	g_debug( "%s: instance=%p, items=%p (%d items)",
 			thisfn, ( void * ) instance, ( void * ) items, g_list_length( items ));
@@ -348,6 +389,9 @@ nact_iactions_list_runtime_init_toplevel( NactIActionsList *instance, GList *ite
 
 		nact_tree_model_runtime_init( model, have_dnd );
 
+		cs = g_new0( CountersStruct, 1 );
+		g_object_set_data( G_OBJECT( instance ), LIST_COUNTERS, cs );
+
 		/* set up selection control */
 		base_window_signal_connect(
 				BASE_WINDOW( instance ),
@@ -383,8 +427,8 @@ nact_iactions_list_runtime_init_toplevel( NactIActionsList *instance, GList *ite
 				TAB_UPDATABLE_SIGNAL_ITEM_UPDATED,
 				G_CALLBACK( on_tab_updatable_item_updated ));
 
-		/* records NactIActionsList as a proxy for edition status modification
-		 */
+		/* records NactIActionsList as a proxy for edition status
+		 * modification */
 		is_proxy = is_iduplicable_proxy( instance );
 		if( is_proxy ){
 			na_iduplicable_register_consumer( G_OBJECT( instance ));
@@ -435,6 +479,7 @@ nact_iactions_list_dispose( NactIActionsList *instance )
 	static const gchar *thisfn = "nact_iactions_list_dispose";
 	GtkTreeView *treeview;
 	NactTreeModel *model;
+	CountersStruct *cs;
 
 	g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
 	g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
@@ -445,6 +490,9 @@ nact_iactions_list_dispose( NactIActionsList *instance )
 		model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
 
 		nact_tree_model_dispose( model );
+
+		cs = ( CountersStruct * ) g_object_get_data( G_OBJECT( instance ), LIST_COUNTERS );
+		g_free( cs );
 	}
 }
 
@@ -497,6 +545,8 @@ nact_iactions_list_delete( NactIActionsList *instance, GList *items )
 
 		set_selection_changed_mode( instance, FALSE );
 
+		decremente_counters( instance, items );
+
 		for( it = items ; it ; it = it->next ){
 			if( path ){
 				gtk_tree_path_free( path );
@@ -514,6 +564,25 @@ nact_iactions_list_delete( NactIActionsList *instance, GList *items )
 	}
 }
 
+static void
+decremente_counters( NactIActionsList *instance, GList *items )
+{
+	gint menus, actions, profiles;
+	CountersStruct *cs;
+
+	menus = 0;
+	actions = 0;
+	profiles = 0;
+	na_object_item_count_items( items, &menus, &actions, &profiles, TRUE );
+
+	cs = ( CountersStruct * ) g_object_get_data( G_OBJECT( instance ), LIST_COUNTERS );
+	cs->menus -= menus;
+	cs->actions -= actions;
+	cs->profiles -= profiles;
+
+	send_list_count_updated_signal( instance, cs );
+}
+
 /**
  * nact_iactions_list_display_order_change:
  * @instance: this #NactIActionsList implementation.
@@ -575,6 +644,7 @@ nact_iactions_list_fill( NactIActionsList *instance, GList *items )
 	GtkTreeView *treeview;
 	NactTreeModel *model;
 	gboolean only_actions;
+	CountersStruct *cs;
 
 	g_debug( "%s: instance=%p, items=%p", thisfn, ( void * ) instance, ( void * ) items );
 	g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
@@ -589,6 +659,13 @@ nact_iactions_list_fill( NactIActionsList *instance, GList *items )
 		nact_tree_model_fill( model, items, only_actions );
 		set_selection_changed_mode( instance, TRUE );
 
+		cs = ( CountersStruct * ) g_object_get_data( G_OBJECT( instance ), LIST_COUNTERS );
+		cs->menus = 0;
+		cs->actions = 0;
+		cs->profiles = 0;
+		na_object_item_count_items( items, &cs->menus, &cs->actions, &cs->profiles, TRUE );
+		send_list_count_updated_signal( instance, cs );
+
 		select_first_row( instance );
 	}
 }
@@ -732,18 +809,15 @@ nact_iactions_list_get_selected_items( NactIActionsList *instance )
 }
 
 /**
- * nact_iactions_list_has_exportable:
- *
- * Returns: %TRUE if there is at least one action in the list, %FALSE
- * else.
+ * nact_iactions_list_has_modified_items:
+ * @window: this #NactIActionsList instance.
  *
- * This is used to see if we can enable or not the "Export" item in the
- * menubar (as of 1.12.1, we only export actions).
+ * Returns: %TRUE if at least there is one modified item in the list.
  */
 gboolean
-nact_iactions_list_has_exportable( NactIActionsList *instance )
+nact_iactions_list_has_modified_items( NactIActionsList *instance )
 {
-	gboolean has_exportable = FALSE;
+	gboolean has_modified = FALSE;
 	GtkTreeView *treeview;
 	NactTreeModel *model;
 
@@ -753,36 +827,53 @@ nact_iactions_list_has_exportable( NactIActionsList *instance )
 
 		treeview = get_actions_list_treeview( instance );
 		model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
-		nact_tree_model_iter( model, ( FnIterOnStore ) has_exportable_iter, &has_exportable );
+
+		nact_tree_model_iter( model, ( FnIterOnStore ) has_modified_iter, &has_modified );
 	}
 
-	return( has_exportable );
+	return( has_modified );
 }
 
 /**
- * nact_iactions_list_has_modified_items:
- * @window: this #NactIActionsList instance.
+ * nact_iactions_list_insert_at_path:
+ * @instance: this #NactIActionsList instance.
+ * @items: a list of items to be inserted (e.g. from a paste).
+ * @path: insertion path.
  *
- * Returns: %TRUE if at least there is one modified item in the list.
+ * Inserts the provided @items list in the treeview.
+ *
+ * This function takes care of repositionning a new selection if
+ * possible, and refilter the display model.
  */
-gboolean
-nact_iactions_list_has_modified_items( NactIActionsList *instance )
+void
+nact_iactions_list_insert_at_path( NactIActionsList *instance, GList *items, GtkTreePath *insert_path )
 {
-	gboolean has_modified = FALSE;
+	static const gchar *thisfn = "nact_iactions_list_insert_at_path";
 	GtkTreeView *treeview;
-	NactTreeModel *model;
+	GtkTreeModel *model;
+	GList *parents;
 
-	g_return_val_if_fail( NACT_IS_IACTIONS_LIST( instance ), FALSE );
+	g_debug( "%s: instance=%p, items=%p (%d items)",
+			thisfn, ( void * ) instance, ( void * ) items, g_list_length( items ));
+	g_return_if_fail( NACT_IS_IACTIONS_LIST( instance ));
+	g_return_if_fail( NACT_IS_WINDOW( instance ));
 
 	if( st_initialized && !st_finalized ){
 
 		treeview = get_actions_list_treeview( instance );
-		model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
+		model = gtk_tree_view_get_model( treeview );
+		g_return_if_fail( NACT_IS_TREE_MODEL( model ));
 
-		nact_tree_model_iter( model, ( FnIterOnStore ) has_modified_iter, &has_modified );
-	}
+		do_insert_items( treeview, model, items, insert_path, &parents );
 
-	return( has_modified );
+		incremente_counters( instance, items );
+
+		update_parents_edition_status( parents, items );
+
+		gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( model ));
+
+		select_row_at_path( instance, treeview, model, insert_path );
+	}
 }
 
 /**
@@ -797,11 +888,11 @@ nact_iactions_list_has_modified_items( NactIActionsList *instance )
  * The provided @items list is supposed to be homogeneous, i.e. referes
  * to only profiles, or only actions or menus.
  *
- * If the @items list contains only profiles, they can only be inserted
- * into an action, and the profiles will eventually be renumbered.
+ * If the @items list contains profiles, they can only be inserted
+ * into an action, or before another profile.
  *
  * If new item is a #NAActionMenu or a #NAAction, it will be inserted
- * before the current action or inside the current menu.
+ * before the current action or menu.
  *
  * This function takes care of repositionning a new selection if
  * possible, and refilter the display model.
@@ -812,9 +903,8 @@ nact_iactions_list_insert_items( NactIActionsList *instance, GList *items, NAObj
 	static const gchar *thisfn = "nact_iactions_list_insert_items";
 	GtkTreeView *treeview;
 	GtkTreeModel *model;
-	GtkTreeSelection *selection;
-	GList *list_selected;
 	GtkTreePath *insert_path;
+	NAObject *object;
 
 	g_debug( "%s: instance=%p, items=%p (%d items)",
 			thisfn, ( void * ) instance, ( void * ) items, g_list_length( items ));
@@ -832,42 +922,54 @@ nact_iactions_list_insert_items( NactIActionsList *instance, GList *items, NAObj
 			insert_path = object_to_path( instance, NACT_TREE_MODEL( model ), sibling );
 
 		} else {
-			selection = gtk_tree_view_get_selection( treeview );
-			list_selected = gtk_tree_selection_get_selected_rows( selection, NULL );
-			if( g_list_length( list_selected )){
-				insert_path = gtk_tree_path_copy(( GtkTreePath * ) list_selected->data );
+			insert_path = get_selection_first_path( treeview );
+
+			/* as a particular case, insert profiles into current action */
+			object = nact_tree_model_object_at_path( NACT_TREE_MODEL( model ), insert_path );
+			/*g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
+			g_debug( "%s: items->data is %s", thisfn, G_OBJECT_TYPE_NAME( items->data ));*/
+			if( NA_IS_OBJECT_ACTION( object ) &&
+				NA_IS_OBJECT_PROFILE( items->data )){
+
+				nact_iactions_list_insert_into( instance, items );
+				gtk_tree_path_free( insert_path );
+				return;
 			}
-			g_list_foreach( list_selected, ( GFunc ) gtk_tree_path_free, NULL );
-			g_list_free( list_selected );
 		}
 
-		nact_iactions_list_insert_at_path( instance, items, insert_path, TRUE );
-
+		nact_iactions_list_insert_at_path( instance, items, insert_path );
 		gtk_tree_path_free( insert_path );
 	}
 }
 
 /**
- * nact_iactions_list_insert_at_path:
+ * nact_iactions_list_insert_into:
  * @instance: this #NactIActionsList instance.
  * @items: a list of items to be inserted (e.g. from a paste).
- * @path: insertion path.
- * @inside: %TRUE if the item may be inserted inside of the give path.
  *
- * Inserts the provided @items list in the treeview.
+ * Inserts the provided @items list as first childs of the current item.
  *
- * This function takes care of repositionning a new selection if
- * possible, and refilter the display model.
+ * The provided @items list is supposed to be homogeneous, i.e. referes
+ * to only profiles, or only actions or menus.
+ *
+ * If the @items list contains only profiles, they can only be inserted
+ * into an action, and the profiles will eventually be renumbered.
+ *
+ * If new item is a #NAActionMenu or a #NAAction, it will be inserted
+ * as first childs of the current menu.
+ *
+ * This function takes care of repositionning a new selection as the
+ * last inserted item, and refilter the display model.
  */
 void
-nact_iactions_list_insert_at_path( NactIActionsList *instance, GList *items, GtkTreePath *insert_path, gboolean inside )
+nact_iactions_list_insert_into( NactIActionsList *instance, GList *items )
 {
-	static const gchar *thisfn = "nact_iactions_list_insert_at_path";
+	static const gchar *thisfn = "nact_iactions_list_insert_into";
 	GtkTreeView *treeview;
 	GtkTreeModel *model;
-	GtkTreePath *last_path = NULL;
-	GList *parents = NULL;
-	GList *it;
+	GtkTreePath *insert_path;
+	GtkTreePath *new_path;
+	NAObject *parent;
 
 	g_debug( "%s: instance=%p, items=%p (%d items)",
 			thisfn, ( void * ) instance, ( void * ) items, g_list_length( items ));
@@ -876,97 +978,161 @@ nact_iactions_list_insert_at_path( NactIActionsList *instance, GList *items, Gtk
 
 	if( st_initialized && !st_finalized ){
 
+		insert_path = NULL;
 		treeview = get_actions_list_treeview( instance );
 		model = gtk_tree_view_get_model( treeview );
-		g_return_if_fail( NACT_IS_TREE_MODEL( model ));
+		insert_path = get_selection_first_path( treeview );
 
-		last_path = do_insert_items( treeview, model, items, insert_path, 0, &parents, inside );
+		incremente_counters( instance, items );
 
-		for( it = parents ; it ; it = it->next ){
-			na_object_check_edition_status( it->data );
-		}
+		parent = do_insert_into_first( treeview, model, items, insert_path, &new_path );
+
+		na_object_check_edition_status( parent );
 
 		gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( model ));
-		select_row_at_path( instance, treeview, model, last_path );
 
-		gtk_tree_path_free( last_path );
+		select_row_at_path( instance, treeview, model, new_path );
+
+		gtk_tree_path_free( new_path );
+		gtk_tree_path_free( insert_path );
 	}
 }
 
 static GtkTreePath *
-do_insert_items( GtkTreeView *treeview,
-					GtkTreeModel *model,
-					GList *items,
-					GtkTreePath *path,
-					gint level,
-					GList **parents,
-					gboolean inside )
+get_selection_first_path( GtkTreeView *treeview )
 {
-	static const gchar *thisfn = "nact_iactions_list_do_insert_items";
-	guint nb_profiles, nb_actions, nb_menus;
+	GtkTreeSelection *selection;
+	GList *list_selected;
+	GtkTreePath *path;
+
+	path = NULL;
+	selection = gtk_tree_view_get_selection( treeview );
+	list_selected = gtk_tree_selection_get_selected_rows( selection, NULL );
+
+	if( g_list_length( list_selected )){
+		path = gtk_tree_path_copy(( GtkTreePath * ) list_selected->data );
+
+	} else {
+		path = gtk_tree_path_new_from_string( "0" );
+	}
+
+	g_list_foreach( list_selected, ( GFunc ) gtk_tree_path_free, NULL );
+	g_list_free( list_selected );
+
+	return( path );
+}
+
+static void
+do_insert_items( GtkTreeView *treeview, GtkTreeModel *model, GList *items, GtkTreePath *insert_path, GList **list_parents )
+{
+	/*static const gchar *thisfn = "nact_iactions_list_do_insert_items";*/
+	GList *reversed;
 	GList *it;
 	GList *subitems;
-	gchar *inserted_path_str;
-	GtkTreePath *inserted_path;
 	NAObject *obj_parent;
-	GtkTreePath *returned_path;
 
-	returned_path = NULL;
+	obj_parent = NULL;
+	if( list_parents ){
+		*list_parents = NULL;
+	}
 
-	nact_window_count_level_zero_items( items, &nb_actions, &nb_profiles, &nb_menus );
+	reversed = g_list_reverse( items );
 
-	g_debug( "%s: level=%d, actions=%d, profiles=%d, menus=%d", thisfn, level, nb_actions, nb_profiles, nb_menus );
+	for( it = reversed ; it ; it = it->next ){
 
-	if( nb_actions || nb_profiles || nb_menus ){
-		g_return_val_if_fail(( nb_profiles && !( nb_actions + nb_menus )) || ( !nb_profiles && ( nb_actions + nb_menus )), NULL );
-		/*g_return_if_fail(( nb_profiles && ( NA_IS_OBJECT_ACTION( obj_selected ) || NA_IS_OBJECT_PROFILE( obj_selected ))) || !nb_profiles );*/
+		nact_tree_model_insert( NACT_TREE_MODEL( model ), NA_OBJECT( it->data ), insert_path, &obj_parent );
 
-		for( it = items ; it ; it = it->next ){
+		if( list_parents && obj_parent ){
+			if( !g_list_find( *list_parents, obj_parent )){
+				*list_parents = g_list_prepend( *list_parents, obj_parent );
+			}
+		}
 
-			/* note that returned iter may have became invalid after conversion
-			 * from store to filter_model, and ran through filter_visible function
-			 * we so cannot rely on it if object is a profile inserted at level > 0
-			 */
-			inserted_path_str = nact_tree_model_insert(
-					NACT_TREE_MODEL( model ), NA_OBJECT( it->data ), path, &obj_parent, inside );
-			g_debug( "%s: inserted_path=%s", thisfn, inserted_path_str );
+		/* recursively insert subitems
+		 */
+		if( NA_IS_OBJECT_ITEM( it->data ) && na_object_get_items_count( it->data )){
 
-			inserted_path = gtk_tree_path_new_from_string( inserted_path_str );
+			subitems = na_object_get_items( it->data );
+			do_insert_into_first( treeview, model, subitems, insert_path, NULL );
+			na_object_free_items( subitems );
+		}
+	}
 
-			if( level == 0 ){
-				gtk_tree_view_expand_to_path( treeview, inserted_path );
-				gtk_tree_path_free( returned_path );
-				returned_path = gtk_tree_path_copy( inserted_path );
-			}
+	/*g_list_free( reversed );*/
+}
 
-			*parents = do_insert_items_add_parent( *parents, treeview, model, obj_parent );
+static NAObject *
+do_insert_into_first( GtkTreeView *treeview, GtkTreeModel *model, GList *items, GtkTreePath *insert_path, GtkTreePath **new_path )
+{
+	GList *last;
+	NAObject *parent;
+	GtkTreePath *inserted_path;
 
-			/* recursively insert subitems
-			 */
-			if( NA_IS_OBJECT_ITEM( it->data )){
-				subitems = na_object_get_items( it->data );
-				do_insert_items( treeview, model, subitems, inserted_path, level+1, parents, inside );
-				na_object_free_items( subitems );
-			}
+	parent = NULL;
+	last = g_list_last( items );
+	items = g_list_remove_link( items, last );
 
-			gtk_tree_path_free( inserted_path );
-			g_free( inserted_path_str );
-		}
+	inserted_path = do_insert_into_second( treeview, model, NA_OBJECT( last->data ), insert_path, &parent );
+	do_insert_items( treeview, model, items, inserted_path, NULL );
+
+	if( new_path ){
+		*new_path = gtk_tree_path_copy( inserted_path );
 	}
 
-	return( level ? NULL : returned_path );
+	gtk_tree_path_free( inserted_path );
+
+	return( parent );
+}
+
+static GtkTreePath *
+do_insert_into_second( GtkTreeView *treeview, GtkTreeModel *model, NAObject *object, GtkTreePath *insert_path, NAObject **parent )
+{
+	/*static const gchar *thisfn = "nact_iactions_list_do_insert_into";*/
+	GtkTreePath *new_path;
+
+	new_path = nact_tree_model_insert_into( NACT_TREE_MODEL( model ), object, insert_path, parent );
+
+	gtk_tree_view_expand_to_path( treeview, new_path );
+
+	return( new_path );
 }
 
-static GList *
-do_insert_items_add_parent( GList *parents, GtkTreeView *treeview, GtkTreeModel *model, NAObject *parent )
+static void
+incremente_counters( NactIActionsList *instance, GList *items )
 {
-	g_return_val_if_fail( parent, NULL );
+	gint menus, actions, profiles;
+	CountersStruct *cs;
+
+	menus = 0;
+	actions = 0;
+	profiles = 0;
+	na_object_item_count_items( items, &menus, &actions, &profiles, TRUE );
+	/*g_debug( "incremente_counters: counted: menus=%d, actions=%d, profiles=%d", menus, actions, profiles );*/
+
+	cs = ( CountersStruct * ) g_object_get_data( G_OBJECT( instance ), LIST_COUNTERS );
+	/*g_debug( "incremente_counters: cs before: menus=%d, actions=%d, profiles=%d", cs->menus, cs->actions, cs->profiles );*/
+	cs->menus += menus;
+	cs->actions += actions;
+	cs->profiles += profiles;
+	/*g_debug( "incremente_counters: cs after: menus=%d, actions=%d, profiles=%d", cs->menus, cs->actions, cs->profiles );*/
+
+	send_list_count_updated_signal( instance, cs );
+}
 
-	if( !g_list_find( parents, parent )){
-		parents = g_list_prepend( parents, parent );
+static void
+update_parents_edition_status( GList *parents, GList *items )
+{
+	GList *it;
+
+	/*if( !parents || !g_list_length( parents )){
+		parents = g_list_copy( items );
+	}*/
+
+	for( it = parents ; it ; it = it->next ){
+		na_object_check_edition_status( it->data );
 	}
 
-	return( parents );
+	g_list_free( parents );
 }
 
 /**
@@ -1315,18 +1481,6 @@ get_items_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, GList
 	return( FALSE );
 }
 
-static gboolean
-has_exportable_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, gboolean *has_exportable )
-{
-	if( NA_IS_OBJECT_ACTION( object )){
-		*has_exportable = TRUE;
-		return( TRUE );
-	}
-
-	/* don't stop iteration while not found or not at end */
-	return( FALSE );
-}
-
 /*
  * stop as soon as we have found a modified item
  */
@@ -1647,6 +1801,15 @@ select_row_at_path( NactIActionsList *instance, GtkTreeView *treeview, GtkTreeMo
 	}
 }
 
+/*
+ * send a 'fill' signal with count of items
+ */
+static void
+send_list_count_updated_signal( NactIActionsList *instance, CountersStruct *cs )
+{
+	g_signal_emit_by_name( instance, IACTIONS_LIST_SIGNAL_LIST_COUNT_UPDATED, cs->menus, cs->actions, cs->profiles );
+}
+
 static void
 set_selection_changed_mode( NactIActionsList *instance, gboolean authorized )
 {
diff --git a/src/nact/nact-iactions-list.h b/src/nact/nact-iactions-list.h
index ffb68b9..558596e 100644
--- a/src/nact/nact-iactions-list.h
+++ b/src/nact/nact-iactions-list.h
@@ -38,6 +38,16 @@
  *
  * This same interface is used in the main window (edition mode, default),
  * and in the export assistant (export mode).
+ *
+ * Counting rows
+ *
+ *   Counting rows is needed to maintain action sensitivities in the
+ *   menubar : at least 'Tools\Export' menu item depends of the content
+ *   of the IActionsList.
+ *   Rows are first counted when the treeview is primarily filled, or
+ *   refilled on demand.
+ *   Counters are then incremented in nact_iactions_list_insert() and
+ *   nact_iactions_list_delete() functions.
  */
 
 #include <gtk/gtk.h>
@@ -93,6 +103,7 @@ typedef struct {
 
 /* signals
  */
+#define IACTIONS_LIST_SIGNAL_LIST_COUNT_UPDATED			"nact-iactions-list-count-updated"
 #define IACTIONS_LIST_SIGNAL_SELECTION_CHANGED			"nact-iactions-list-selection-changed"
 
 /* management modes
@@ -120,10 +131,10 @@ NAObject *nact_iactions_list_get_item( NactIActionsList *instance, const gchar *
 GList    *nact_iactions_list_get_items( NactIActionsList *instance );
 gint      nact_iactions_list_get_management_mode( NactIActionsList *instance );
 GList    *nact_iactions_list_get_selected_items( NactIActionsList *instance );
-gboolean  nact_iactions_list_has_exportable( NactIActionsList *instance );
 gboolean  nact_iactions_list_has_modified_items( NactIActionsList *instance );
+void      nact_iactions_list_insert_at_path( NactIActionsList *instance, GList *items, GtkTreePath *path );
 void      nact_iactions_list_insert_items( NactIActionsList *instance, GList *items, NAObject *sibling );
-void      nact_iactions_list_insert_at_path( NactIActionsList *instance, GList *items, GtkTreePath *path, gboolean inside );
+void      nact_iactions_list_insert_into( NactIActionsList *instance, GList *items );
 gboolean  nact_iactions_list_is_expanded( NactIActionsList *instance, const NAObject *item );
 void      nact_iactions_list_set_management_mode( NactIActionsList *instance, gint mode );
 void      nact_iactions_list_toggle_collapse( NactIActionsList *instance, const NAObject *item );
diff --git a/src/nact/nact-main-menubar.c b/src/nact/nact-main-menubar.c
index 9144b9e..451f301 100644
--- a/src/nact/nact-main-menubar.c
+++ b/src/nact/nact-main-menubar.c
@@ -68,7 +68,29 @@
 #define MENUBAR_PROP_ITEM_ACTION		"nact-menubar-item-action"
 #endif
 
-static void     on_tab_updatable_selection_changed( NactMainWindow *window, gint count_selected );
+/* this structure is updated each time the user interacts in the
+ * interface ; it is then used to update action sensitivities
+ */
+typedef struct {
+	gint     selected_menus;
+	gint     selected_actions;
+	gint     selected_profiles;
+	gint     clipboard_menus;
+	gint     clipboard_actions;
+	gint     clipboard_profiles;
+	gint     list_menus;
+	gint     list_actions;
+	gint     list_profiles;
+	gboolean is_modified;
+	gboolean have_exportables;
+}
+	MenubarIndicatorsStruct;
+
+#define MENUBAR_PROP_INDICATORS			"nact-menubar-indicators"
+
+static void     on_iactions_list_count_updated( NactMainWindow *window, gint menus, gint actions, gint profiles );
+static void     on_iactions_list_selection_changed( NactMainWindow *window, GList *selected );
+static void     on_update_sensitivities( NactMainWindow *window, gpointer user_data );
 
 static void     on_new_menu_activated( GtkAction *action, NactMainWindow *window );
 static void     on_new_action_activated( GtkAction *action, NactMainWindow *window );
@@ -81,8 +103,10 @@ static void     on_quit_activated( GtkAction *action, NactMainWindow *window );
 static void     on_cut_activated( GtkAction *action, NactMainWindow *window );
 static void     on_copy_activated( GtkAction *action, NactMainWindow *window );
 static void     on_paste_activated( GtkAction *action, NactMainWindow *window );
+static void     on_paste_into_activated( GtkAction *action, NactMainWindow *window );
 static void     on_duplicate_activated( GtkAction *action, NactMainWindow *window );
 static void     on_delete_activated( GtkAction *action, NactMainWindow *window );
+static void     update_clipboard_counters( NactMainWindow *window );
 static void     on_reload_activated( GtkAction *action, NactMainWindow *window );
 static void     on_preferences_activated( GtkAction *action, NactMainWindow *window );
 
@@ -102,7 +126,6 @@ static void     on_menu_item_selected( GtkMenuItem *proxy, NactMainWindow *windo
 static void     on_menu_item_deselected( GtkMenuItem *proxy, NactMainWindow *window );
 static void     on_proxy_connect( GtkActionGroup *action_group, GtkAction *action, GtkWidget *proxy, NactMainWindow *window );
 static void     on_proxy_disconnect( GtkActionGroup *action_group, GtkAction *action, GtkWidget *proxy, NactMainWindow *window );
-static void     refresh_actions_sensitivity_with_count( NactMainWindow *window, gint count_selected );
 
 static const GtkActionEntry entries[] = {
 
@@ -142,8 +165,12 @@ static const GtkActionEntry entries[] = {
 				G_CALLBACK( on_copy_activated ) },
 		{ "PasteItem" , GTK_STOCK_PASTE, NULL, NULL,
 				/* i18n: tooltip displayed in the status bar when selecting the Paste item */
-				N_( "Insert the content of the clipboard at the current position" ),
+				N_( "Insert the content of the clipboard just before the current position" ),
 				G_CALLBACK( on_paste_activated ) },
+		{ "PasteIntoItem" , NULL, N_( "Paste _into" ), "<Shift><Ctrl>V",
+				/* i18n: tooltip displayed in the status bar when selecting the Paste Into item */
+				N_( "Insert the content of the clipboard as first child of the current item" ),
+				G_CALLBACK( on_paste_into_activated ) },
 		{ "DuplicateItem" , NULL, N_( "D_uplicate" ), "",
 				/* i18n: tooltip displayed in the status bar when selecting the Duplicate item */
 				N_( "Duplicate the selected item(s)" ),
@@ -191,6 +218,8 @@ static const GtkActionEntry entries[] = {
  * @window: the #NactMainWindow to which the menubar is attached.
  *
  * Creates the menubar.
+ * Connects to all possible signals which may have an impact on action
+ * sensitivities.
  */
 void
 nact_main_menubar_runtime_init( NactMainWindow *window )
@@ -203,6 +232,7 @@ nact_main_menubar_runtime_init( NactMainWindow *window )
 	GtkAccelGroup *accel_group;
 	GtkWidget *menubar, *vbox;
 	GtkWindow *toplevel;
+	MenubarIndicatorsStruct *mis;
 
 	g_debug( "%s: window=%p", thisfn, ( void * ) window );
 
@@ -260,50 +290,171 @@ nact_main_menubar_runtime_init( NactMainWindow *window )
 	base_window_signal_connect(
 			BASE_WINDOW( window ),
 			G_OBJECT( window ),
-			TAB_UPDATABLE_SIGNAL_SELECTION_CHANGED,
-			G_CALLBACK( on_tab_updatable_selection_changed ));
+			IACTIONS_LIST_SIGNAL_LIST_COUNT_UPDATED,
+			G_CALLBACK( on_iactions_list_count_updated ));
+
+	base_window_signal_connect(
+			BASE_WINDOW( window ),
+			G_OBJECT( window ),
+			IACTIONS_LIST_SIGNAL_SELECTION_CHANGED,
+			G_CALLBACK( on_iactions_list_selection_changed ));
+
+	base_window_signal_connect(
+			BASE_WINDOW( window ),
+			G_OBJECT( window ),
+			MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES,
+			G_CALLBACK( on_update_sensitivities ));
+
+	mis = g_new0( MenubarIndicatorsStruct, 1 );
+	g_object_set_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS, mis );
 }
 
 /**
- * nact_main_menubar_refresh_actions_sensitivity:
- *
- * Sensitivity of items (whether they are activable of not) in the
- * menubar should be recomputed each time aaction in the user interface
- * may lead to a change in one of the menu actions.
- *
- * This consists in :
- * - when there is a change in the current selection in IActionsList
- *   (new profile, cut, copy, duplicate, delete)
- * - when there is a change in the content of IActionsList
- *   (export)
- * - when there is a change in the content of the clipboard
- *   (paste)
- *
- * Note that the same actions are also used as toolbar actions ; we so
- * cannot rely on just recomputing the sensitivity e.g. when about to
- * open a popup menu in the menubar. Sensitivity of the actions need
- * to be updated as soon as the global context is changed.
+ * nact_main_menubar_dispose:
+ * @window: this #NactMainWindow window.
  *
+ * Release internally allocated resources.
  */
 void
-nact_main_menubar_refresh_actions_sensitivity( NactMainWindow *window )
+nact_main_menubar_dispose( NactMainWindow *window )
+{
+	static const gchar *thisfn = "nact_main_menubar_dispose";
+	MenubarIndicatorsStruct *mis;
+
+	g_debug( "%s: window=%p", thisfn, ( void * ) window );
+	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
+
+	mis = ( MenubarIndicatorsStruct * ) g_object_get_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS );
+	g_free( mis );
+}
+
+/*
+ * when the IActionsList is refilled, update our internal counters so
+ * that we are knowing if we have some exportables
+ */
+static void
+on_iactions_list_count_updated( NactMainWindow *window, gint menus, gint actions, gint profiles )
 {
-	GList *selected;
-	guint count;
+	MenubarIndicatorsStruct *mis;
+
+	g_debug( "nact_main_menubar_on_iactions_list_count_updated: menus=%u, actions=%u, profiles=%u", menus, actions, profiles );
+	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
 
-	g_return_if_fail( NACT_MAIN_WINDOW( window ));
-	g_return_if_fail( NACT_IS_IACTIONS_LIST( window ));
+	mis = ( MenubarIndicatorsStruct * ) g_object_get_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS );
+	mis->list_menus = menus;
+	mis->list_actions = actions;
+	mis->list_profiles = profiles;
+	mis->have_exportables = ( mis->list_actions > 0 );
 
-	selected = nact_iactions_list_get_selected_items( NACT_IACTIONS_LIST( window ));
-	count = g_list_length( selected );
-	na_object_free_items( selected );
-	refresh_actions_sensitivity_with_count( window, count );
+	g_signal_emit_by_name( window, MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES, NULL );
 }
 
+/*
+ * when the selection changes in IActionsList, see what is selected
+ */
 static void
-on_tab_updatable_selection_changed( NactMainWindow *window, gint count_selected )
+on_iactions_list_selection_changed( NactMainWindow *window, GList *selected )
 {
-	refresh_actions_sensitivity_with_count( window, count_selected );
+	MenubarIndicatorsStruct *mis;
+
+	g_debug( "nact_main_menubar_on_iactions_list_selection_changed: selected=%p (%d)",
+			( void * ) selected, g_list_length( selected ));
+	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
+
+	mis = ( MenubarIndicatorsStruct * ) g_object_get_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS );
+	mis->selected_menus = 0;
+	mis->selected_actions = 0;
+	mis->selected_profiles = 0;
+	na_object_item_count_items( selected, &mis->selected_menus, &mis->selected_actions, &mis->selected_profiles, FALSE );
+	g_debug( "nact_main_menubar_on_iactions_list_selection_changed: menus=%d, actions=%d, profiles=%d",
+			mis->selected_menus, mis->selected_actions, mis->selected_profiles );
+
+	g_signal_emit_by_name( window, MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES, NULL );
+}
+
+static void
+on_update_sensitivities( NactMainWindow *window, gpointer user_data )
+{
+	static const gchar *thisfn = "nact_main_menubar_on_update_sensitivities";
+	MenubarIndicatorsStruct *mis;
+	NAObject *item;
+	NAObject *profile;
+	gboolean has_modified;
+	gint count_list;
+	gint count_selected;
+	gboolean paste_enabled;
+	gboolean paste_into_enabled;
+	gboolean clipboard_is_empty;
+
+	g_debug( "%s: window=%p", thisfn, ( void * ) window );
+	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
+
+	mis = ( MenubarIndicatorsStruct * ) g_object_get_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS );
+
+	g_object_get(
+			G_OBJECT( window ),
+			TAB_UPDATABLE_PROP_EDITED_ACTION, &item,
+			TAB_UPDATABLE_PROP_EDITED_PROFILE, &profile,
+			NULL );
+	g_return_if_fail( !item || NA_IS_OBJECT_ITEM( item ));
+	g_return_if_fail( !profile || NA_IS_OBJECT_PROFILE( profile ));
+
+	has_modified = nact_main_window_has_modified_items( window );
+	count_list = mis->list_menus + mis->list_actions + mis->list_profiles;
+	count_selected = mis->selected_menus + mis->selected_actions + mis->selected_profiles;
+
+	clipboard_is_empty = ( mis->clipboard_menus + mis->clipboard_actions + mis->clipboard_profiles == 0 );
+
+	paste_enabled = FALSE;
+	if( !clipboard_is_empty ){
+		if( mis->clipboard_profiles ){
+			paste_enabled = profile && NA_IS_OBJECT_PROFILE( profile );
+		} else {
+			paste_enabled = ( item != NULL );
+		}
+	}
+
+	paste_into_enabled = FALSE;
+	if( !clipboard_is_empty ){
+		if( mis->clipboard_profiles ){
+			paste_into_enabled = item && NA_IS_OBJECT_ACTION( item );
+		} else {
+			paste_into_enabled = item && NA_IS_OBJECT_MENU( item );
+		}
+	}
+
+	/* new menu always enabled */
+	/* new action always enabled */
+	/* new profile enabled if selection is relative to only one action */
+	enable_item( window, "NewProfileItem", item != NULL && !NA_IS_OBJECT_MENU( item ));
+	/* save enabled if at least one item has been modified */
+	enable_item( window, "SaveItem", has_modified );
+	/* quit always enabled */
+	/* cut/copy enabled if selection not empty */
+	enable_item( window, "CutItem", count_selected > 0 );
+	enable_item( window, "CopyItem", count_selected > 0 );
+	/* paste enabled if
+	 * - clipboard contains only profiles, and current selection is a profile
+	 * - clipboard contains actions or menus, and current selection is a menu or an action */
+	enable_item( window, "PasteItem", count_selected <= 1 && paste_enabled );
+	/* paste into enabled if
+	 * - clipboard has profiles and current item is an action
+	 * - or current item is a menu */
+	enable_item( window, "PasteIntoItem", count_selected <= 1 && paste_into_enabled );
+	/* duplicate/delete enabled if selection not empty */
+	enable_item( window, "DuplicateItem", count_selected > 0 );
+	enable_item( window, "DeleteItem", count_selected > 0 );
+	/* reload items always enabled */
+	/* preferences always enabled */
+	/* expand all/collapse all requires at least one item in the list */
+	enable_item( window, "ExpandAllItem", count_list > 0 );
+	enable_item( window, "CollapseAllItem", count_list > 0 );
+	/* import item always enabled */
+	/* export item enabled if IActionsList store contains actions */
+	enable_item( window, "ExportItem", mis->have_exportables );
+	/* TODO: help temporarily disabled */
+	enable_item( window, "HelpItem", FALSE );
+	/* about always enabled */
 }
 
 static void
@@ -370,7 +521,7 @@ on_new_profile_activated( GtkAction *gtk_action, NactMainWindow *window )
  * saving is not only saving modified items, but also saving hierarchy
  * (and order if alpha order is not set)
  *
- * note that we only go down in the hierarchy is parent is valid and not
+ * note that we only go down in the hierarchy if parent is valid and not
  * modified (or has been successfully saved)
  */
 static void
@@ -400,7 +551,7 @@ on_save_activated( GtkAction *gtk_action, NactMainWindow *window )
 
 	/* required as selection has not changed
 	 */
-	nact_main_menubar_refresh_actions_sensitivity( window );
+	g_signal_emit_by_name( window, MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES, NULL );
 
 	/* get ride of notification messages of IOProviders
 	 */
@@ -504,6 +655,7 @@ on_cut_activated( GtkAction *gtk_action, NactMainWindow *window )
 	nact_main_window_move_to_deleted( window, items );
 	clipboard = nact_main_window_get_clipboard( window );
 	nact_clipboard_primary_set( clipboard, items, FALSE );
+	update_clipboard_counters( window );
 	nact_iactions_list_delete( NACT_IACTIONS_LIST( window ), items );
 
 	/* do not unref selected items as the ref has been moved to main_deleted
@@ -531,12 +683,15 @@ on_copy_activated( GtkAction *gtk_action, NactMainWindow *window )
 	items = nact_iactions_list_get_selected_items( NACT_IACTIONS_LIST( window ));
 	clipboard = nact_main_window_get_clipboard( window );
 	nact_clipboard_primary_set( clipboard, items, TRUE );
+	update_clipboard_counters( window );
 	na_object_free_items( items );
-	nact_main_menubar_refresh_actions_sensitivity( window );
+
+	g_signal_emit_by_name( window, MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES, NULL );
 }
 
 /*
- * pastes the current content of the clipboard
+ * pastes the current content of the clipboard at the current position
+ * (same path, same level)
  * - (menu) get from clipboard a copy of installed items
  *          the clipboard will return a new copy
  *          and renumber its own data for allowing a new paste
@@ -573,6 +728,44 @@ on_paste_activated( GtkAction *gtk_action, NactMainWindow *window )
 }
 
 /*
+ * pastes the current content of the clipboard as the first child of
+ * currently selected item
+ * - (menu) get from clipboard a copy of installed items
+ *          the clipboard will return a new copy
+ *          and renumber its own data for allowing a new paste
+ * - (tree) insert new items, the tree store will ref them
+ *          attaching each item to its parent
+ *          recursively checking edition status of the topmost parent
+ *          selecting the first item at end
+ * - (menu) unreffing the copy got from clipboard
+ */
+static void
+on_paste_into_activated( GtkAction *gtk_action, NactMainWindow *window )
+{
+	GList *items, *it;
+	NactClipboard *clipboard;
+	NAObjectAction *action = NULL;
+
+	clipboard = nact_main_window_get_clipboard( window );
+	items = nact_clipboard_primary_get( clipboard );
+
+	/* if pasted items are profiles, then setup the action
+	 */
+	for( it = items ; it ; it = it->next ){
+		if( NA_IS_OBJECT_PROFILE( it->data )){
+			if( !action ){
+				g_object_get( G_OBJECT( window ), TAB_UPDATABLE_PROP_EDITED_ACTION, &action, NULL );
+				g_return_if_fail( NA_IS_OBJECT_ACTION( action ));
+			}
+			na_object_profile_set_action( NA_OBJECT_PROFILE( it->data ), action );
+		}
+	}
+
+	nact_iactions_list_insert_into( NACT_IACTIONS_LIST( window ), items );
+	na_object_free_items( items );
+}
+
+/*
  * duplicate is just as paste, with the difference that content comes
  * from the current selection, instead of coming from the clipboard
  *
@@ -653,6 +846,27 @@ on_delete_activated( GtkAction *gtk_action, NactMainWindow *window )
 	/*g_list_free( items );*/
 }
 
+/*
+ * as we are coming from cut or copy to clipboard, report selection
+ * counters to clipboard ones
+ */
+static void
+update_clipboard_counters( NactMainWindow *window )
+{
+	MenubarIndicatorsStruct *mis;
+
+	mis = ( MenubarIndicatorsStruct * ) g_object_get_data( G_OBJECT( window ), MENUBAR_PROP_INDICATORS );
+
+	mis->clipboard_menus = mis->selected_menus;
+	mis->clipboard_actions = mis->selected_actions;
+	mis->clipboard_profiles = mis->selected_profiles;
+
+	g_debug( "nact_main_menubar_update_clipboard_counters: menus=%d, actions=%d, profiles=%d",
+			mis->clipboard_menus, mis->clipboard_actions, mis->clipboard_profiles );
+
+	g_signal_emit_by_name( window, MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES, NULL );
+}
+
 static void
 on_reload_activated( GtkAction *gtk_action, NactMainWindow *window )
 {
@@ -819,68 +1033,3 @@ on_proxy_disconnect( GtkActionGroup *action_group, GtkAction *action, GtkWidget
 {
 	/* signal handlers will be automagically disconnected on NactWindow::dispose */
 }
-
-static void
-refresh_actions_sensitivity_with_count( NactMainWindow *window, gint count_selected )
-{
-	static const gchar *thisfn = "nact_main_menubar_refresh_actions_sensitivity_with_count";
-	NAObjectItem *item;
-	NAObjectProfile *profile;
-	gboolean has_exportable;
-	gboolean has_modified;
-	guint nb_actions, nb_profiles, nb_menus;
-	gboolean paste_enabled;
-	NactClipboard *clipboard;
-
-	g_debug( "%s: window=%p, count_selected=%d", thisfn, ( void * ) window, count_selected );
-	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
-
-	g_object_get(
-			G_OBJECT( window ),
-			TAB_UPDATABLE_PROP_EDITED_ACTION, &item,
-			TAB_UPDATABLE_PROP_EDITED_PROFILE, &profile,
-			NULL );
-	g_debug( "%s: item=%p (%s), profile=%p", thisfn,
-			( void * ) item, item ? G_OBJECT_TYPE_NAME( item ) : "(nil)", ( void * ) profile );
-
-	has_exportable = nact_iactions_list_has_exportable( NACT_IACTIONS_LIST( window ));
-	g_debug( "%s: has_exportable=%s", thisfn, has_exportable ? "True":"False" );
-	has_modified = nact_main_window_has_modified_items( window );
-	g_debug( "%s: has_modified=%s", thisfn, has_modified ? "True":"False" );
-
-	paste_enabled = FALSE;
-	clipboard = nact_main_window_get_clipboard( window );
-	nact_clipboard_primary_counts( clipboard, &nb_actions, &nb_profiles, &nb_menus );
-	g_debug( "%s: actions=%d, profiles=%d, menus=%d", thisfn, nb_actions, nb_profiles, nb_menus );
-	if( nb_profiles ){
-		paste_enabled = NA_IS_OBJECT_ACTION( item );
-	} else {
-		paste_enabled = ( nb_actions + nb_menus > 0 );
-	}
-
-	/* new menu always enabled */
-	/* new action always enabled */
-	/* new profile enabled if selection is relative to only one action */
-	enable_item( window, "NewProfileItem", item != NULL && !NA_IS_OBJECT_MENU( item ));
-	/* save enabled if at least one item has been modified */
-	enable_item( window, "SaveItem", has_modified );
-	/* quit always enabled */
-	/* cut/copy enabled if selection not empty */
-	enable_item( window, "CutItem", count_selected > 0 );
-	enable_item( window, "CopyItem", count_selected > 0 );
-	/* paste enabled if
-	 * - clipboard contains only profiles, and current selection is action or profile
-	 * - clipboard contains actions or menus */
-	enable_item( window, "PasteItem", paste_enabled );
-	/* duplicate/delete enabled if selection not empty */
-	enable_item( window, "DuplicateItem", count_selected > 0 );
-	enable_item( window, "DeleteItem", count_selected > 0 );
-	/* reload items always enabled */
-	/* preferences always enabled */
-	/* import item always enabled */
-	/* export item enabled if IActionsList store contains actions */
-	enable_item( window, "ExportItem", has_exportable );
-	/* TODO: help temporarily disabled */
-	enable_item( window, "HelpItem", FALSE );
-	/* about always enabled */
-}
diff --git a/src/nact/nact-main-menubar.h b/src/nact/nact-main-menubar.h
index 7916a74..d5815fd 100644
--- a/src/nact/nact-main-menubar.h
+++ b/src/nact/nact-main-menubar.h
@@ -37,14 +37,12 @@
  * @include: nact/nact-main-menubar.h
  */
 
-#include <gtk/gtk.h>
-
 #include "nact-main-window.h"
 
 G_BEGIN_DECLS
 
 void  nact_main_menubar_runtime_init( NactMainWindow *window );
-void  nact_main_menubar_refresh_actions_sensitivity( NactMainWindow *window );
+void  nact_main_menubar_dispose( NactMainWindow *window );
 
 G_END_DECLS
 
diff --git a/src/nact/nact-main-window.c b/src/nact/nact-main-window.c
index 0539ce2..7a6edb8 100644
--- a/src/nact/nact-main-window.c
+++ b/src/nact/nact-main-window.c
@@ -111,6 +111,7 @@ enum {
 enum {
 	SELECTION_CHANGED,
 	ITEM_UPDATED,
+	UPDATE_SENSITIVITIES,
 	LAST_SIGNAL
 };
 
@@ -141,7 +142,7 @@ static void     on_base_initial_load_toplevel( NactMainWindow *window, gpointer
 static void     on_base_runtime_init_toplevel( NactMainWindow *window, gpointer user_data );
 static void     on_base_all_widgets_showed( NactMainWindow *window, gpointer user_data );
 
-static void     iactions_list_selection_changed( NactIActionsList *instance, GSList *selected_items );
+static void     on_iactions_list_selection_changed( NactIActionsList *instance, GSList *selected_items );
 static void     set_current_object_item( NactMainWindow *window, GSList *selected_items );
 static void     set_current_profile( NactMainWindow *window, gboolean set_action, GSList *selected_items );
 static gchar   *iactions_list_get_treeview_name( NactIActionsList *instance );
@@ -343,6 +344,24 @@ class_init( NactMainWindowClass *klass )
 			G_TYPE_NONE,
 			1,
 			G_TYPE_POINTER );
+
+	/**
+	 * main-window-update-sensitivities:
+	 *
+	 * This signal is emitted each time a user interaction may led the
+	 * action sensitivities to be updated.
+	 */
+	st_signals[ UPDATE_SENSITIVITIES ] = g_signal_new(
+			MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES,
+			G_TYPE_OBJECT,
+			G_SIGNAL_RUN_LAST,
+			0,					/* no default handler */
+			NULL,
+			NULL,
+			g_cclosure_marshal_VOID__POINTER,
+			G_TYPE_NONE,
+			1,
+			G_TYPE_POINTER );
 }
 
 static void
@@ -352,7 +371,7 @@ iactions_list_iface_init( NactIActionsListInterface *iface )
 
 	g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
 
-	iface->selection_changed = iactions_list_selection_changed;
+	iface->selection_changed = NULL;
 	iface->get_treeview_name = iactions_list_get_treeview_name;
 }
 
@@ -542,6 +561,7 @@ instance_dispose( GObject *window )
 		nact_icommand_tab_dispose( NACT_ICOMMAND_TAB( window ));
 		nact_iconditions_tab_dispose( NACT_ICONDITIONS_TAB( window ));
 		nact_iadvanced_tab_dispose( NACT_IADVANCED_TAB( window ));
+		nact_main_menubar_dispose( self );
 
 		/* chain up to the parent class */
 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
@@ -890,6 +910,12 @@ on_base_runtime_init_toplevel( NactMainWindow *window, gpointer user_data )
 
 	if( !window->private->dispose_has_run ){
 
+		base_window_signal_connect(
+				BASE_WINDOW( window ),
+				G_OBJECT( window ),
+				IACTIONS_LIST_SIGNAL_SELECTION_CHANGED,
+				G_CALLBACK( on_iactions_list_selection_changed ));
+
 		application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
 		pivot = nact_application_get_pivot( application );
 		tree = na_pivot_get_items( pivot );
@@ -899,11 +925,11 @@ on_base_runtime_init_toplevel( NactMainWindow *window, gpointer user_data )
 		nact_icommand_tab_runtime_init_toplevel( NACT_ICOMMAND_TAB( window ));
 		nact_iconditions_tab_runtime_init_toplevel( NACT_ICONDITIONS_TAB( window ));
 		nact_iadvanced_tab_runtime_init_toplevel( NACT_IADVANCED_TAB( window ));
+		nact_main_menubar_runtime_init( window );
 
 		/* fill the IActionsList at last so that all signals are connected
 		 */
 		nact_iactions_list_runtime_init_toplevel( NACT_IACTIONS_LIST( window ), tree );
-		nact_main_menubar_runtime_init( window );
 
 		/* forces a no-selection when the list is initially empty
 		 */
@@ -933,8 +959,6 @@ on_base_all_widgets_showed( NactMainWindow *window, gpointer user_data )
 		nact_icommand_tab_all_widgets_showed( NACT_ICOMMAND_TAB( window ));
 		nact_iconditions_tab_all_widgets_showed( NACT_ICONDITIONS_TAB( window ));
 		nact_iadvanced_tab_all_widgets_showed( NACT_IADVANCED_TAB( window ));
-
-		nact_main_menubar_refresh_actions_sensitivity( window );
 	}
 }
 
@@ -944,9 +968,9 @@ on_base_all_widgets_showed( NactMainWindow *window, gpointer user_data )
  * @selected_items: the currently selected items in ActionsList
  */
 static void
-iactions_list_selection_changed( NactIActionsList *instance, GSList *selected_items )
+on_iactions_list_selection_changed( NactIActionsList *instance, GSList *selected_items )
 {
-	static const gchar *thisfn = "nact_main_window_iactions_list_selection_changed";
+	static const gchar *thisfn = "nact_main_window_on_iactions_list_selection_changed";
 	NactMainWindow *window;
 	NAObject *object;
 	gint count;
diff --git a/src/nact/nact-main-window.h b/src/nact/nact-main-window.h
index 829013d..660b143 100644
--- a/src/nact/nact-main-window.h
+++ b/src/nact/nact-main-window.h
@@ -69,6 +69,8 @@ typedef struct {
 }
 	NactMainWindowClass;
 
+#define MAIN_WINDOW_SIGNAL_UPDATE_ACTION_SENSITIVITIES	"main-window-update-sensitivities"
+
 GType           nact_main_window_get_type( void );
 
 NactMainWindow *nact_main_window_new( BaseApplication *application );
diff --git a/src/nact/nact-marshal.list b/src/nact/nact-marshal.list
new file mode 100644
index 0000000..11ef833
--- /dev/null
+++ b/src/nact/nact-marshal.list
@@ -0,0 +1 @@
+VOID:INT,INT,INT
diff --git a/src/nact/nact-tree-model.c b/src/nact/nact-tree-model.c
index 1761dad..79e65ad 100644
--- a/src/nact/nact-tree-model.c
+++ b/src/nact/nact-tree-model.c
@@ -168,13 +168,6 @@ static void           instance_finalize( GObject *application );
 static NactTreeModel *tree_model_new( BaseWindow *window, GtkTreeView *treeview );
 
 static void           fill_tree_store( GtkTreeStore *model, GtkTreeView *treeview, GList *items, gboolean only_actions, GtkTreeIter *parent );
-
-static void           insert_get_iters_action( GtkTreeModel *model, const NAObject *select_object, GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object, gboolean inside );
-static void           insert_get_iters_profile( GtkTreeModel *model, const NAObject *select_object, GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object, gboolean inside );
-static void           insert_get_iters_menu( GtkTreeModel *model, const NAObject *select_object, GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object, gboolean inside );
-static void           insert_before_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
-static void           insert_before_parent_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
-static void           insert_as_last_child_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
 static void           remove_if_exists( NactTreeModel *model, GtkTreeModel *store, const NAObject *object );
 
 static GList         *add_parent( GList *parents, GtkTreeModel *store, GtkTreeIter *obj_iter );
@@ -733,316 +726,121 @@ fill_tree_store( GtkTreeStore *model, GtkTreeView *treeview,
  * nact_tree_model_insert:
  * @model: this #NactTreeModel instance.
  * @object: a #NAObject-derived object to be inserted.
- * @path: the #GtkTreePath of the beginning of the current selection,
- * or NULL.
- * @obj_parent: set to the parent or the object itself.
- * @inside: %TRUE if the item may be inserted inside of the given path.
+ * @path: the #GtkTreePath which specifies the insertion path.
+ * @parent: set to the parent or the object itself.
  *
  * Insert a new row at the given position.
  *
- * Returns: the path string of the inserted row as a newly allocated
- * string. The returned path should be g_free() by the caller.
- *
- * +--------------------+----------------------+----------------------+----------------------+
- * | inserted object -> |        action        |        profile       |         menu         |
- * +--------------------+----------------------+----------------------+----------------------+
- * | currently selected |                      |                      |                      |
- * |      |             |                      |                      |                      |
- * |      v             |                      |                      |                      |
- * |    (nil)           |    insert_before     |          n/a         |    insert_before     |
- * |                    |                      |                      |                      |
- * |   action           |    insert_before     | insert_as_last_child |    insert_before     |
- * |                    |                      |                      |                      |
- * |   profile          | insert_before_parent |    insert_before     | insert_before_parent |
- * |                    |                      |                      |                      |
- * |    menu            |    insert_before     |          n/a         |    insert_before     |
- * |                    | insert_as_last_child |                      | insert_as_last_child |
- * +-----------------------------------------------------------------------------------------+
+ * Gtk API uses to returns iter ; but at least when inserting a new
+ * profile in an action, we may have store_iter_path="0:1" (good), but
+ * iter_path="0:0" (bad) - so we'd rather return a string path.
  *
- * insert_before       : parent=NULL     , sibling_from_path (or null if path was null)
- * insert_before_parent: parent=NULL     , sibling_from_parent_path
- * insert_as_last_child: parent_from_path, sibling=NULL
- *
- * Gtk API uses to returns iter ; but at least when inserting a new profile in an action, we
- * may have store_iter_path="0:1" (good), but iter_path="0:0" (bad) - so we return rather a
- * string path
+ * Note that we do not return anything here as the insertion path at
+ * the beginning of the function is always valid when the function
+ * returns; it points now to the newly inserted row.
  */
-gchar *
-nact_tree_model_insert( NactTreeModel *model,
-						const NAObject *object,
-						GtkTreePath *path,
-						NAObject **obj_parent,
-						gboolean inside )
+void
+nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent )
 {
-	static const gchar *thisfn = "nact_tree_model_insert";
-	gchar *path_str = NULL;
+	/*static const gchar *thisfn = "nact_tree_model_insert";*/
 	GtkTreeModel *store;
-	GtkTreeIter select_iter;
-	NAObject *select_object;
+	GtkTreeIter iter;
 	GtkTreeIter parent_iter;
+	GtkTreePath *parent_path;
+	NAObject *parent_obj;
+	gboolean has_parent;
 	GtkTreeIter sibling_iter;
-	GtkTreeIter store_iter;
-	gboolean has_parent_iter;
-	gboolean has_sibling_iter;
-
-	path_str = path ? gtk_tree_path_to_string( path ) : NULL;
-	g_debug( "%s: model=%p, object=%p (%s), path=%p (%s)",
-			thisfn, ( void * ) model,
-			( void * ) object, G_OBJECT_TYPE_NAME( object ),
-			( void * ) path, path_str );
-	g_free( path_str );
+	NAObject *sibling_obj;
+	gboolean has_sibling;
 
-	g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
-	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
+	g_return_if_fail( NACT_IS_TREE_MODEL( model ));
+	g_return_if_fail( NA_IS_OBJECT( object ));
 
 	if( !model->private->dispose_has_run ){
 
 		store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
-		has_parent_iter = FALSE;
-		has_sibling_iter = FALSE;
-		*obj_parent = NA_OBJECT( object );
+		has_parent = FALSE;
+		parent_obj = NULL;
+		sibling_obj = NULL;
 
 		remove_if_exists( model, store, object );
 
-		if( path ){
-			if( !gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &select_iter, path )){
-				return( NULL );
-			}
+		/* may be FALSE when store is empty */
+		has_sibling = gtk_tree_model_get_iter( store, &sibling_iter, path );
+		if( has_sibling ){
+			gtk_tree_model_get( store, &sibling_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &sibling_obj, -1 );
+			g_object_unref( sibling_obj );
+		}
 
-			gtk_tree_model_get( GTK_TREE_MODEL( model ), &select_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &select_object, -1 );
+		if( gtk_tree_path_get_depth( path ) > 1 ){
 
-			g_return_val_if_fail( select_object, NULL );
-			g_return_val_if_fail( NA_IS_OBJECT( select_object ), NULL );
+			has_parent = TRUE;
+			parent_path = gtk_tree_path_copy( path );
+			gtk_tree_path_up( parent_path );
+			gtk_tree_model_get_iter( store, &parent_iter, parent_path );
+			gtk_tree_path_free( parent_path );
 
-			if( NA_IS_OBJECT_ACTION( object )){
-				insert_get_iters_action( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent, inside );
-			}
+			gtk_tree_model_get( store, &parent_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &parent_obj, -1 );
+			g_object_unref( parent_obj );
 
-			if( NA_IS_OBJECT_PROFILE( object )){
-				insert_get_iters_profile( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent, inside );
+			if( parent && !*parent ){
+				*parent = parent_obj;
 			}
 
-			if( NA_IS_OBJECT_MENU( object )){
-				insert_get_iters_menu( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent, inside );
+			if( has_sibling ){
+				na_object_insert_item( parent_obj, object, sibling_obj );
+			} else {
+				na_object_append_item( parent_obj, object );
 			}
-
-			g_object_unref( select_object );
-
-		} else {
-			g_return_val_if_fail( NA_IS_OBJECT_ITEM( object ), NULL );
 		}
 
-		gtk_tree_store_insert_before( GTK_TREE_STORE( store ), &store_iter, has_parent_iter ? &parent_iter : NULL, has_sibling_iter ? &sibling_iter : NULL );
-		gtk_tree_store_set( GTK_TREE_STORE( store ), &store_iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
-		display_item( GTK_TREE_STORE( store ), model->private->treeview, &store_iter, object );
-
-		path_str = gtk_tree_model_get_string_from_iter( store, &store_iter );
+		gtk_tree_store_insert_before(
+				GTK_TREE_STORE( store ), &iter,
+				has_parent ? &parent_iter : NULL,
+				has_sibling ? &sibling_iter : NULL );
+		gtk_tree_store_set( GTK_TREE_STORE( store ), &iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
+		display_item( GTK_TREE_STORE( store ), model->private->treeview, &iter, object );
 	}
-
-	return( path_str );
 }
 
-/*
- * inserts an action
- */
-static void
-insert_get_iters_action( GtkTreeModel *model,
-							const NAObject *select_object,
-							GtkTreePath *select_path,
-							const NAObject *object,
-							GtkTreeIter *parent_iter,
-							gboolean *has_parent_iter,
-							GtkTreeIter *sibling_iter,
-							gboolean *has_sibling_iter,
-							NAObject **parent_object,
-							gboolean inside )
-{
-	gint count;
-
-	g_return_if_fail( NA_IS_OBJECT_ACTION( object ));
-
-	/* (insert_before)
-	 * insert action before selected action
-	 * if selected action has a parent :
-	 * - set parent_object to parent of selected action
-	 * - add object to subitems of parent of selected action
-	 */
-	if( NA_IS_OBJECT_ACTION( select_object )){
-		insert_before_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	/* (insert_before_parent)
-	 * insert action before parent of selected profile
-	 * if parent of selected profile has itself a parent :
-	 * - set parent_object to parent of parent of selected profile
-	 * - add object to subitems of parent of parent of selected profile
-	 */
-	if( NA_IS_OBJECT_PROFILE( select_object )){
-		insert_before_parent_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	/* insert_as_last_child if there is no child at all
-	 * insert_before else
-	 */
-	if( NA_IS_OBJECT_MENU( select_object )){
-		count = na_object_get_items_count( select_object );
-		if( count == 0 && inside ){
-			insert_as_last_child_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-		} else {
-			insert_before_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-		}
-	}
-}
-
-/*
- * insert a profile
- */
-static void
-insert_get_iters_profile( GtkTreeModel *model,
-		const NAObject *select_object,
-		GtkTreePath *select_path,
-		const NAObject *object,
-		GtkTreeIter *parent_iter,
-		gboolean *has_parent_iter,
-		GtkTreeIter *sibling_iter,
-		gboolean *has_sibling_iter,
-		NAObject **parent_object,
-		gboolean inside )
-{
-	g_return_if_fail( NA_IS_OBJECT_PROFILE( object ));
-
-	if( NA_IS_OBJECT_ACTION( select_object )){
-		insert_as_last_child_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	if( NA_IS_OBJECT_PROFILE( select_object )){
-		insert_before_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	if( NA_IS_OBJECT_MENU( select_object )){
-		g_return_if_reached();
-	}
-}
-
-/*
- * insert a menu
- */
-static void
-insert_get_iters_menu( GtkTreeModel *model,
-		const NAObject *select_object,
-		GtkTreePath *select_path,
-		const NAObject *object,
-		GtkTreeIter *parent_iter,
-		gboolean *has_parent_iter,
-		GtkTreeIter *sibling_iter,
-		gboolean *has_sibling_iter,
-		NAObject **parent_object,
-		gboolean inside )
-{
-	gint count;
-
-	g_return_if_fail( NA_IS_OBJECT_MENU( object ));
-
-	if( NA_IS_OBJECT_ACTION( select_object )){
-		insert_before_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	if( NA_IS_OBJECT_PROFILE( select_object )){
-		insert_before_parent_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-	}
-
-	/* insert_as_last_child if there is no child at all
-	 * insert_before else
-	 */
-	if( NA_IS_OBJECT_MENU( select_object )){
-		count = na_object_get_items_count( select_object );
-		if( count == 0 && inside ){
-			insert_as_last_child_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-		} else {
-			insert_before_get_iters( model, select_path, object, parent_iter, has_parent_iter, sibling_iter, has_sibling_iter, parent_object );
-		}
-	}
-}
-
-/*
- * insert an action or a menu when there is no current selection
- * insert an action or a menu when the selection is an action
- * insert a profile before a profile
- */
-static void
-insert_before_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object )
+GtkTreePath *
+nact_tree_model_insert_into( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent )
 {
-	GtkTreePath *path;
+	static const gchar *thisfn = "nact_tree_model_insert_into";
+	GtkTreeModel *store;
 	GtkTreeIter iter;
-	NAObject *sibling_obj;
+	GtkTreeIter parent_iter;
+	GtkTreePath *new_path;
+	gchar *path_str;
 
-	g_debug( "nact_tree_model_insert_before_get_iters" );
+	g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
+	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
 
-	gtk_tree_model_get_iter( model, sibling_iter, select_path );
-	gtk_tree_model_get( model, sibling_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &sibling_obj, -1 );
-	*has_sibling_iter = TRUE;
+	new_path = NULL;
 
-	if( gtk_tree_path_get_depth( select_path ) > 1 ){
-		path = gtk_tree_path_copy( select_path );
-		gtk_tree_path_up( path );
-		gtk_tree_model_get_iter( model, &iter, path );
-		gtk_tree_model_get( model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, parent_object, -1 );
-		g_return_if_fail( NA_IS_OBJECT_ITEM( *parent_object ));
-		na_object_insert_item( *parent_object, object, sibling_obj );
-		g_object_unref( *parent_object );
-		gtk_tree_path_free( path );
-	}
+	if( !model->private->dispose_has_run ){
 
-	g_object_unref( sibling_obj );
-}
+		store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
 
-/*
- * insert an action or a menu when the selection is a profile
- */
-static void
-insert_before_parent_get_iters( GtkTreeModel *model, GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object )
-{
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	NAObject *sibling_obj;
+		if( !gtk_tree_model_get_iter( store, &parent_iter, path )){
+			path_str = gtk_tree_path_to_string( path );
+			g_warning( "%s: unable to get iter at path %s", thisfn, path_str );
+			g_free( path_str );
+			return( NULL );
+		}
+		gtk_tree_model_get( store, &parent_iter, IACTIONS_LIST_NAOBJECT_COLUMN, parent, -1 );
+		g_object_unref( *parent );
 
-	g_debug( "nact_tree_model_insert_before_parent_get_iters" );
+		na_object_insert_item( *parent, object, NULL );
 
-	path = gtk_tree_path_copy( select_path );
-	gtk_tree_path_up( path );
-	gtk_tree_model_get_iter( model, sibling_iter, path );
-	gtk_tree_model_get( model, sibling_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &sibling_obj, -1 );
-	*has_sibling_iter = TRUE;
+		gtk_tree_store_insert_after( GTK_TREE_STORE( store ), &iter, &parent_iter, NULL );
+		gtk_tree_store_set( GTK_TREE_STORE( store ), &iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
+		display_item( GTK_TREE_STORE( store ), model->private->treeview, &iter, object );
 
-	if( gtk_tree_path_get_depth( path ) > 1 ){
-		gtk_tree_path_up( path );
-		gtk_tree_model_get_iter( model, &iter, path );
-		gtk_tree_model_get( model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, parent_object, -1 );
-		g_return_if_fail( NA_IS_OBJECT_ITEM( *parent_object ));
-		na_object_insert_item( *parent_object, object, sibling_obj );
-		g_object_unref( *parent_object );
+		new_path = gtk_tree_model_get_path( store, &iter );
 	}
 
-	g_object_unref( sibling_obj );
-	gtk_tree_path_free( path );
-}
-
-/*
- * insert an action or a menu when the selection is a menu
- * insert a profile when the selection is an action
- */
-static void
-insert_as_last_child_get_iters( GtkTreeModel *model, GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object )
-{
-	g_debug( "nact_tree_model_insert_as_last_child_get_iters" );
-
-	gtk_tree_model_get_iter( model, parent_iter, select_path );
-	*has_parent_iter = TRUE;
-
-	gtk_tree_model_get( model, parent_iter, IACTIONS_LIST_NAOBJECT_COLUMN, parent_object, -1 );
-	g_return_if_fail( NA_IS_OBJECT_ITEM( *parent_object ));
-	na_object_append_item( *parent_object, object );
-	g_object_unref( *parent_object );
+	return( new_path );
 }
 
 /*
@@ -1075,6 +873,35 @@ nact_tree_model_iter( NactTreeModel *model, FnIterOnStore fn, gpointer user_data
 }
 
 /**
+ * nact_tree_model_object_at_path:
+ * @model: this #NactTreeModel instance.
+ * @path: the #GtkTreePath to be searched.
+ *
+ * Returns: the #NAObject at the given @path if any, or NULL.
+ */
+NAObject *
+nact_tree_model_object_at_path( NactTreeModel *model, GtkTreePath *path )
+{
+	NAObject *object;
+	GtkTreeModel *store;
+	GtkTreeIter iter;
+
+	g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
+
+	object = NULL;
+
+	if( !model->private->dispose_has_run ){
+
+		store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
+		gtk_tree_model_get_iter( store, &iter, path );
+		gtk_tree_model_get( store, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
+		g_object_unref( object );
+	}
+
+	return( object );
+}
+
+/**
  * nact_tree_model_remove:
  * @model: this #NactTreeModel instance.
  * @object: the #NAObject to be deleted.
@@ -1630,7 +1457,7 @@ idrag_dest_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, Gt
 					}
 				}
 				object_list = g_list_reverse( object_list );
-				nact_iactions_list_insert_at_path( NACT_IACTIONS_LIST( main_window ), object_list, dest, FALSE );
+				nact_iactions_list_insert_at_path( NACT_IACTIONS_LIST( main_window ), object_list, dest );
 
 				if( !copy_data ){
 					nact_iactions_list_delete( NACT_IACTIONS_LIST( main_window ), object_list );
@@ -1675,7 +1502,7 @@ idrag_dest_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, Gt
 					} else {
 						g_return_val_if_fail( NA_IS_OBJECT_ACTION( action ), FALSE );
 						object_list = g_list_prepend( NULL, action );
-						nact_iactions_list_insert_at_path( NACT_IACTIONS_LIST( main_window ), object_list, dest, FALSE );
+						nact_iactions_list_insert_at_path( NACT_IACTIONS_LIST( main_window ), object_list, dest );
 						g_list_free( object_list );
 					}
 
diff --git a/src/nact/nact-tree-model.h b/src/nact/nact-tree-model.h
index 781e918..9ea00ed 100644
--- a/src/nact/nact-tree-model.h
+++ b/src/nact/nact-tree-model.h
@@ -97,8 +97,10 @@ void         nact_tree_model_display( NactTreeModel *model, NAObject *object );
 void         nact_tree_model_display_order_change( NactTreeModel *model, gint order_mode );
 void         nact_tree_model_dump( NactTreeModel *model );
 void         nact_tree_model_fill( NactTreeModel *model, GList *items, gboolean only_actions);
-gchar       *nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent, gboolean inside );
+void         nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent );
+GtkTreePath *nact_tree_model_insert_into( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent );
 void         nact_tree_model_iter( NactTreeModel *model, FnIterOnStore fn, gpointer user_data );
+NAObject    *nact_tree_model_object_at_path( NactTreeModel *model, GtkTreePath *path );
 GtkTreePath *nact_tree_model_remove( NactTreeModel *model, NAObject *object );
 
 G_END_DECLS
diff --git a/src/nact/nautilus-actions-config-tool.actions b/src/nact/nautilus-actions-config-tool.actions
index 2ef3596..4641274 100644
--- a/src/nact/nautilus-actions-config-tool.actions
+++ b/src/nact/nautilus-actions-config-tool.actions
@@ -13,6 +13,7 @@
             <menuitem action="CutItem" />
             <menuitem action="CopyItem" />
             <menuitem action="PasteItem" />
+            <menuitem action="PasteIntoItem" />
             <menuitem action="DuplicateItem" />
             <menuitem action="DeleteItem" />
             <separator />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]