[nautilus-actions] Fix edition status and saving in the UI



commit 87282ae355aa4882dc0371caa89888daadb648cc
Author: Pierre Wieser <pwieser trychlos org>
Date:   Tue Sep 22 22:54:36 2009 +0200

    Fix edition status and saving in the UI

 ChangeLog                                |   56 +++
 TODO                                     |    2 +
 configure.ac                             |    1 +
 src/common/na-iduplicable.c              |   76 ++--
 src/common/na-iduplicable.h              |    7 +-
 src/common/na-iio-provider.c             |   44 +--
 src/common/na-object-action.c            |   13 +-
 src/common/na-object-action.h            |    8 +-
 src/common/na-object-api.h               |    7 +-
 src/common/na-object-fn.h                |   26 +-
 src/common/na-object-id.c                |    9 +-
 src/common/na-object-item-fn.h           |    4 +-
 src/common/na-object-item.c              |  115 ++++--
 src/common/na-object-menu.c              |    1 +
 src/common/na-object-profile.c           |    8 +
 src/common/na-object.c                   |  753 +++++++++++++++++-------------
 src/common/na-pivot.c                    |  133 +++---
 src/common/na-pivot.h                    |    4 +-
 src/nact/nact-assistant-import.c         |   19 +-
 src/nact/nact-assistant-import.h         |    4 +-
 src/nact/nact-clipboard.c                |    2 +-
 src/nact/nact-iactions-list.c            |   86 +++-
 src/nact/nact-iactions-list.h            |    1 +
 src/nact/nact-main-menubar.c             |  178 +++++--
 src/nact/nact-main-window.c              |   42 +-
 src/nact/nact-tree-model.c               |   29 +-
 src/nact/nact-window.c                   |   11 +-
 src/nact/nautilus-actions-config-tool.ui |    2 -
 28 files changed, 1018 insertions(+), 623 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index d7cdbd7..5231847 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,59 @@
+2009-09-22 Pierre Wieser <pwieser trychlos org>
+
+	* configure.ac: Add a comment about GtkActivatable.
+
+	* src/common/na-iduplicable.c:
+	* src/common/na-iduplicable.h:
+	Reorganize the code and add comments about recursivity inside.
+
+	* src/common/na-iio-provider.c:
+	Replace local dump_hierarchy() with call to na_object_dump_tree().
+	na_iio_provider_write_item(): Make debug trace more verbose.
+
+	* src/common/na-object-action.c:
+	* src/common/na-object-action.h:
+	Output debug traces depending on NAIDuplicable flag.
+	Fix equality check if a profile is modified.
+
+	* src/common/na-object-api.h (na_object_dump_norec,
+	na_object_dump_tree, na_object_rewind_origin): New functions.
+	na_object_insert_item(): Add a sibling pointer.
+
+	* src/common/na-object.c:
+	* src/common/na-object-fn.h:
+	Reorganize the code and add comments about recursivity inside.
+
+	* src/common/na-object-id.c:
+	* src/common/na-object-menu.c:
+	* src/common/na-object-profile.c: Make debug trace more verbose.
+
+	* src/common/na-object-item.c:
+	* src/common/na-object-item-fn.h (na_object_item_insert_item):
+	Add a sibling pointer.
+	na_object_item_free_items(): Only unref if object is always valid.
+	object_are_equal(): Position is a factor of equality.
+
+	* src/common/na-pivot.c:
+	* src/common/na-pivot.h (na_pivot_get_item): New function.
+
+	* src/nact/nact-assistant-import.c:
+	Actually imports the actions into the tree store before
+	confirming the operation was done.
+
+	* nact/nact-iactions-list.c:
+	* nact/nact-iactions-list.h (nact_iactions_list_get_item):
+	New function.
+
+	* src/nact/nact-main-menubar.c:
+	Do not use GtkActivatable if version <= 2.16.
+	Rewrite save items process.
+
+	* src/nact/nact-main-window.c (nact_main_window_action_exists):
+	Restore the function, searching in NAPivot and in tree store.
+
+	* src/nact/nact-tree-model.c:
+	Now inserts the new row before a given sibling object.
+
 2009-09-21 Pierre Wieser <pwieser trychlos org>
 
 	* src/common/na-gconf-provider.c (write_item_menu):
diff --git a/TODO b/TODO
index fc37ca8..458bc45 100644
--- a/TODO
+++ b/TODO
@@ -50,3 +50,5 @@
   cf. commit 5ee6938486cd82f1274969506873df950348dd61 and before
   function nact-tree-model.c::add_parent
   recreate a small code which reproduces this bug and open in bugzilla
+
+- lot of work to do on gconf editor
diff --git a/configure.ac b/configure.ac
index a40bf26..cd60585 100644
--- a/configure.ac
+++ b/configure.ac
@@ -111,6 +111,7 @@ AM_GCONF_SOURCE_2
 # Gtk  >= 2.12: GtkBuilder
 #
 # [nact-assistant] remove work-around for #589745 when Gtk+ >= 2.18
+# [nact-main-menubar] GtkActivatable only available starting with Gtk+ 2.16
 #
 # [configure.ac, nautilus-actions.c] remove test for
 # nautilus_menu_provider_emit_items_updated_signal() when Gnome >= 2.16
diff --git a/src/common/na-iduplicable.c b/src/common/na-iduplicable.c
index 6a24264..8f0e994 100644
--- a/src/common/na-iduplicable.c
+++ b/src/common/na-iduplicable.c
@@ -254,43 +254,6 @@ na_iduplicable_dump( const NAIDuplicable *object )
 }
 
 /**
- * na_iduplicable_duplicate:
- * @object: the #NAIDuplicable object to be duplicated.
- *
- * Exactly duplicates a #NAIDuplicable-implemented object.
- * Properties %NA_IDUPLICABLE_PROP_ORIGIN, %PROP_IDUPLICABLE_ISMODIFIED
- * and %PROP_IDUPLICABLE_ISVALID are initialized to their default
- * values.
- *
- * As %PROP_IDUPLICABLE_ISVALID property is set to %TRUE without any
- * further check, this suppose that only valid objects are duplicated.
- *
- * Returns: a new #NAIDuplicable.
- */
-NAIDuplicable *
-na_iduplicable_duplicate( const NAIDuplicable *object )
-{
-	/*static const gchar *thisfn = "na_iduplicable_duplicate";*/
-	NAIDuplicable *dup = NULL;
-
-	/*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
-
-	g_return_val_if_fail( st_initialized && !st_finalized, NULL );
-	g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
-
-	dup = v_new( object );
-
-	if( dup ){
-		v_copy( dup, object );
-		set_origin( dup, object );
-		set_modified( dup, FALSE );
-		set_valid( dup, TRUE );
-	}
-
-	return( dup );
-}
-
-/**
  * na_iduplicable_check_edition_status:
  * @object: the #NAIDuplicable object to be checked.
  *
@@ -307,7 +270,7 @@ na_iduplicable_duplicate( const NAIDuplicable *object )
  * That is, the modification and validity status are only set on the
  * specified object.
  * Nonetheless, a derived class may perfectly implement a recursive
- * check on childs, if any. See, e.g. #NAObjectItem implementation.
+ * check on childs, if any. See, e.g. #NAObject implementation.
  */
 void
 na_iduplicable_check_edition_status( const NAIDuplicable *object )
@@ -347,6 +310,43 @@ na_iduplicable_check_edition_status( const NAIDuplicable *object )
 }
 
 /**
+ * na_iduplicable_duplicate:
+ * @object: the #NAIDuplicable object to be duplicated.
+ *
+ * Exactly duplicates a #NAIDuplicable-implemented object.
+ * Properties %NA_IDUPLICABLE_PROP_ORIGIN, %PROP_IDUPLICABLE_ISMODIFIED
+ * and %PROP_IDUPLICABLE_ISVALID are initialized to their default
+ * values.
+ *
+ * As %PROP_IDUPLICABLE_ISVALID property is set to %TRUE without any
+ * further check, this suppose that only valid objects are duplicated.
+ *
+ * Returns: a new #NAIDuplicable.
+ */
+NAIDuplicable *
+na_iduplicable_duplicate( const NAIDuplicable *object )
+{
+	/*static const gchar *thisfn = "na_iduplicable_duplicate";*/
+	NAIDuplicable *dup = NULL;
+
+	/*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
+
+	g_return_val_if_fail( st_initialized && !st_finalized, NULL );
+	g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
+
+	dup = v_new( object );
+
+	if( dup ){
+		v_copy( dup, object );
+		set_origin( dup, object );
+		set_modified( dup, FALSE );
+		set_valid( dup, TRUE );
+	}
+
+	return( dup );
+}
+
+/**
  * na_iduplicable_is_modified:
  * @object: the #NAIDuplicable object whose status is to be returned.
  *
diff --git a/src/common/na-iduplicable.h b/src/common/na-iduplicable.h
index a565f52..7260a13 100644
--- a/src/common/na-iduplicable.h
+++ b/src/common/na-iduplicable.h
@@ -145,21 +145,18 @@ typedef struct {
 #define NA_IDUPLICABLE_SIGNAL_MODIFIED_CHANGED		"na-iduplicable-modified-changed"
 #define NA_IDUPLICABLE_SIGNAL_VALID_CHANGED			"na-iduplicable-valid-changed"
 
-#define NA_IDUPLICABLE_EDITION_STATUS_DEBUG			0
+#define NA_IDUPLICABLE_EDITION_STATUS_DEBUG			1
 
 GType          na_iduplicable_get_type( void );
 
 void           na_iduplicable_init( NAIDuplicable *object );
 void           na_iduplicable_dump( const NAIDuplicable *object );
-
-NAIDuplicable *na_iduplicable_duplicate( const NAIDuplicable *object );
-
 void           na_iduplicable_check_edition_status( const NAIDuplicable *object );
+NAIDuplicable *na_iduplicable_duplicate( const NAIDuplicable *object );
 
 gboolean       na_iduplicable_is_modified( const NAIDuplicable *object );
 gboolean       na_iduplicable_is_valid( const NAIDuplicable *object );
 NAIDuplicable *na_iduplicable_get_origin( const NAIDuplicable *object );
-
 void           na_iduplicable_set_origin( NAIDuplicable *object, const NAIDuplicable *origin );
 
 void           na_iduplicable_register_consumer( GObject *consumer );
diff --git a/src/common/na-iio-provider.c b/src/common/na-iio-provider.c
index 4e59c29..c78df49 100644
--- a/src/common/na-iio-provider.c
+++ b/src/common/na-iio-provider.c
@@ -55,7 +55,6 @@ static void     interface_base_finalize( NAIIOProviderInterface *klass );
 static GList   *build_hierarchy( GList *tree, GSList *level_zero, gboolean list_if_empty );
 static gint     search_item( const NAObject *obj, const gchar *uuid );
 static GList   *get_merged_items_list( const NAPivot *pivot, GSList *providers );
-static void     dump_hierarchy( GList *tree, gint level );
 
 static guint    try_write_item( const NAIIOProvider *instance, NAObject *item, gchar **message );
 
@@ -180,7 +179,7 @@ na_iio_provider_get_items_tree( const NAPivot *pivot )
 		hierarchy = sort_tree( pivot, hierarchy );
 	}
 
-	dump_hierarchy( hierarchy, 0 );
+	na_object_dump_tree( hierarchy );
 
 	return( hierarchy );
 }
@@ -280,35 +279,6 @@ get_merged_items_list( const NAPivot *pivot, GSList *providers )
 	return( merged );
 }
 
-static void
-dump_hierarchy( GList *tree, gint level )
-{
-	GString *prefix;
-	gint i;
-	GList *subitems, *it;
-	gchar *id;
-
-	prefix = g_string_new( "" );
-	for( i = 0 ; i < level ; ++i ){
-		g_string_append_printf( prefix, "  " );
-	}
-
-	for( it = tree ; it ; it = it->next ){
-		id = na_object_get_id( it->data );
-		g_debug( "nact_iio_provider_dump_hierarchy: %s%p (%s) %s",
-				prefix->str, ( void * ) it->data, G_OBJECT_TYPE_NAME( it->data ), id );
-		g_free( id );
-
-		if( NA_IS_OBJECT_ITEM( it->data )){
-			subitems = na_object_get_items( it->data );
-			dump_hierarchy( subitems, level+1 );
-			na_object_free_items( subitems );
-		}
-	}
-
-	g_string_free( prefix, TRUE );
-}
-
 /**
  * na_iio_provider_write_item:
  * @pivot: the #NAPivot object which owns the list of registered I/O
@@ -331,8 +301,10 @@ na_iio_provider_write_item( const NAPivot *pivot, NAObject *item, gchar **messag
 	NAIIOProvider *bad_instance;
 	GSList *providers, *ip;
 
-	g_debug( "%s: pivot=%p, item=%p, message=%p",
-			thisfn, ( void * ) pivot, ( void * ) item, ( void * ) message );
+	g_debug( "%s: pivot=%p (%s), item=%p (%s), message=%p", thisfn,
+			( void * ) pivot, G_OBJECT_TYPE_NAME( pivot ),
+			( void * ) item, G_OBJECT_TYPE_NAME( item ),
+			( void * ) message );
 
 	g_return_val_if_fail( st_initialized && !st_finalized, NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail(( NA_IS_PIVOT( pivot ) || !pivot ), NA_IIO_PROVIDER_PROGRAM_ERROR );
@@ -424,8 +396,10 @@ na_iio_provider_delete_item( const NAPivot *pivot, const NAObject *item, gchar *
 	guint ret;
 	NAIIOProvider *instance;
 
-	g_debug( "%s: pivot=%p, item=%p, message=%p",
-			thisfn, ( void * ) pivot, ( void * ) item, ( void * ) message );
+	g_debug( "%s: pivot=%p (%s), item=%p (%s), message=%p", thisfn,
+			( void * ) pivot, G_OBJECT_TYPE_NAME( pivot ),
+			( void * ) item, G_OBJECT_TYPE_NAME( item ),
+			( void * ) message );
 
 	g_return_val_if_fail( st_initialized && !st_finalized, NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NA_IIO_PROVIDER_PROGRAM_ERROR );
diff --git a/src/common/na-object-action.c b/src/common/na-object-action.c
index d19f7ce..e52c2c1 100644
--- a/src/common/na-object-action.c
+++ b/src/common/na-object-action.c
@@ -34,6 +34,7 @@
 
 #include <string.h>
 
+#include "na-iduplicable.h"
 #include "na-object-api.h"
 #include "na-object-action.h"
 #include "na-object-profile.h"
@@ -551,7 +552,7 @@ object_copy( NAObject *target, const NAObject *source )
  * note 1: version is not localized (see configure.ac)
  *
  * note 2: when checking for equality of profiles, we know that NAObjectItem
- * has already checked their edition status, and assert that profiles lists
+ * has already checked their edition status, and that the two profiles lists
  * were the sames ; we so only report the modification status to the action
  */
 static gboolean
@@ -577,7 +578,7 @@ object_are_equal( const NAObject *a, const NAObject *b )
 
 	if( equal ){
 		profiles = na_object_get_items( a );
-		for( ip = profiles ; ip ; ip = ip->next ){
+		for( ip = profiles ; ip && equal ; ip = ip->next ){
 			id = na_object_get_id( ip->data );
 			profile = NA_OBJECT_PROFILE( na_object_get_item( b, id ));
 			equal = !na_object_is_modified( profile );
@@ -593,7 +594,13 @@ object_are_equal( const NAObject *a, const NAObject *b )
 		na_object_free_items( profiles );
 	}
 
-	/*g_debug( "na_object_action_are_equal: %s", equal ? "True":"False" );*/
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_action_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
+			( void * ) a, G_OBJECT_TYPE_NAME( a ),
+			( void * ) b, G_OBJECT_TYPE_NAME( b ),
+			equal ? "True":"False" );
+#endif
+
 	return( equal );
 }
 
diff --git a/src/common/na-object-action.h b/src/common/na-object-action.h
index 1ecb8c8..c6c6dc7 100644
--- a/src/common/na-object-action.h
+++ b/src/common/na-object-action.h
@@ -39,7 +39,7 @@
  * This is the class which maintains data and properties of an Nautilus
  * action.
  *
- * Note about the UUID:
+ * Note about the UUID :
  *
  * The uuid is only required when writing the action to GConf in order
  * to ensure unicity of subdirectories.
@@ -49,6 +49,12 @@
  * Note that a user may import an action, translate it and then
  * reexport it : we so may have two different actions with the same
  * uuid. The user has so to modify the UUID before import.
+ *
+ * Note about edition status :
+ *
+ * As a particular rule of #NAObjectItem derived class, an action is
+ * considered modified as soon as any of its profiles has been modified
+ * itself (because they are saved as a whole).
  */
 
 #include <glib/gi18n.h>
diff --git a/src/common/na-object-api.h b/src/common/na-object-api.h
index 9a20125..29fdd3d 100644
--- a/src/common/na-object-api.h
+++ b/src/common/na-object-api.h
@@ -46,9 +46,11 @@ G_BEGIN_DECLS
 /* NAObject
  */
 #define na_object_dump( object )					na_object_object_dump( NA_OBJECT( object ))
+#define na_object_dump_norec( object )				na_object_object_dump_norec( NA_OBJECT( object ))
+#define na_object_dump_tree( tree )					na_object_object_dump_tree( tree )
 #define na_object_get_clipboard_id( object )		na_object_object_get_clipboard_id( NA_OBJECT( object ))
 #define na_object_ref( object )						na_object_object_ref( NA_OBJECT( object ))
-#define na_object_copy( target, source )			na_object_object_copy( NA_OBJECT( target ), NA_OBJECT( source ))
+#define na_object_rewind_origin( target, source )	na_object_object_rewind_origin( NA_OBJECT( target ), NA_OBJECT( source ))
 
 /* NAIDuplicable
  */
@@ -59,7 +61,6 @@ G_BEGIN_DECLS
 
 #define na_object_get_origin( object )				na_object_iduplicable_get_origin( NA_OBJECT( object ))
 #define na_object_set_origin( object, origin )		na_object_iduplicable_set_origin( NA_OBJECT( object ), NA_OBJECT( origin ))
-#define na_object_set_origin_rec( object, origin )	na_object_iduplicable_set_origin_recurse( NA_OBJECT( object ), NA_OBJECT( origin ))
 
 /* NAObjectId
  */
@@ -89,7 +90,7 @@ G_BEGIN_DECLS
 #define na_object_set_items( object, list )			na_object_item_set_items( NA_OBJECT_ITEM( object ), list )
 
 #define na_object_append_item( object, item )		na_object_item_append_item( NA_OBJECT_ITEM( object ), NA_OBJECT( item ))
-#define na_object_insert_item( object, item )		na_object_item_insert_item( NA_OBJECT_ITEM( object ), NA_OBJECT( item ))
+#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_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-fn.h b/src/common/na-object-fn.h
index fd3d580..923a9f2 100644
--- a/src/common/na-object-fn.h
+++ b/src/common/na-object-fn.h
@@ -46,28 +46,28 @@
 
 G_BEGIN_DECLS
 
-/* NAObject
- */
-void      na_object_object_dump( const NAObject *object );
-gchar    *na_object_object_get_clipboard_id( const NAObject *object );
-NAObject *na_object_object_ref( const NAObject *object );
-void      na_object_object_copy( NAObject *target, const NAObject *source );
-
-GList    *na_object_get_hierarchy( const NAObject *object );
-void      na_object_free_hierarchy( GList *hierarchy );
-
 /* NAIDuplicable
  */
+void      na_object_iduplicable_check_edition_status( const NAObject *object );
 NAObject *na_object_iduplicable_duplicate( const NAObject *object );
 
-void      na_object_iduplicable_check_edition_status( const NAObject *object );
 gboolean  na_object_iduplicable_are_equal( const NAObject *a, const NAObject *b );
 gboolean  na_object_iduplicable_is_modified( const NAObject *object );
 gboolean  na_object_iduplicable_is_valid( const NAObject *object );
-
 NAObject *na_object_iduplicable_get_origin( const NAObject *object );
 void      na_object_iduplicable_set_origin( NAObject *object, const NAObject *origin );
-void      na_object_iduplicable_set_origin_recurse( NAObject *object, const NAObject *origin );
+
+/* NAObject
+ */
+void      na_object_object_dump( const NAObject *object );
+void      na_object_object_dump_norec( const NAObject *object );
+void      na_object_object_dump_tree( GList *tree );
+gchar    *na_object_object_get_clipboard_id( const NAObject *object );
+NAObject *na_object_object_ref( const NAObject *object );
+void      na_object_object_rewind_origin( NAObject *target, const NAObject *source );
+
+GList    *na_object_get_hierarchy( const NAObject *object );
+void      na_object_free_hierarchy( GList *hierarchy );
 
 G_END_DECLS
 
diff --git a/src/common/na-object-id.c b/src/common/na-object-id.c
index 5793ffe..f7a896b 100644
--- a/src/common/na-object-id.c
+++ b/src/common/na-object-id.c
@@ -34,6 +34,7 @@
 
 #include <string.h>
 
+#include "na-iduplicable.h"
 #include "na-object-fn.h"
 #include "na-object-id-class.h"
 #include "na-object-id-fn.h"
@@ -432,7 +433,13 @@ object_are_equal( const NAObject *a, const NAObject *b )
 		}
 	}
 
-	/*g_debug( "na_object_id_are_equal: %s", equal ? "True":"False" );*/
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_id_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
+			( void * ) a, G_OBJECT_TYPE_NAME( a ),
+			( void * ) b, G_OBJECT_TYPE_NAME( b ),
+			equal ? "True":"False" );
+#endif
+
 	return( equal );
 }
 
diff --git a/src/common/na-object-item-fn.h b/src/common/na-object-item-fn.h
index 108a0dc..dc1d4cd 100644
--- a/src/common/na-object-item-fn.h
+++ b/src/common/na-object-item-fn.h
@@ -78,8 +78,8 @@ void           na_object_item_set_provider( NAObjectItem *item, const NAIIOProvi
 void           na_object_item_set_items( NAObjectItem *item, GList *items );
 
 void           na_object_item_append_item( NAObjectItem *item, const NAObject *object );
-void           na_object_item_insert_item( NAObjectItem *item, const NAObject *object );
-void           na_object_item_remove_item( NAObjectItem *item, NAObject *object );
+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 );
 
 G_END_DECLS
 
diff --git a/src/common/na-object-item.c b/src/common/na-object-item.c
index 89880e9..fd1546e 100644
--- a/src/common/na-object-item.c
+++ b/src/common/na-object-item.c
@@ -35,6 +35,7 @@
 #include <string.h>
 #include <uuid/uuid.h>
 
+#include "na-iduplicable.h"
 #include "na-object-api.h"
 #include "na-object-item-class.h"
 #include "na-object-item-fn.h"
@@ -562,7 +563,14 @@ na_object_item_get_items_count( const NAObjectItem *item )
 void
 na_object_item_free_items( GList *items )
 {
-	g_list_foreach( items, ( GFunc ) g_object_unref, NULL );
+	GList *it;
+
+	for( it = items ; it ; it = it->next ){
+		if( G_IS_OBJECT( it->data )){
+			g_object_unref( it->data );
+		}
+	}
+
 	g_list_free( items );
 }
 
@@ -714,50 +722,60 @@ na_object_item_append_item( NAObjectItem *item, const NAObject *object )
 /**
  * na_object_item_insert_item:
  * @item: the #NAObjectItem to which add the subitem.
- * @object: a #NAObject to be inserted at the list of subitems.
+ * @object: a #NAObject to be inserted in the list of subitems.
+ * @before: the #NAObject before which the @object should be inserted.
  *
- * Inserts a new @object at the beginning of the list of subitems of
- * @item.
+ * Inserts a new @object in the list of subitems of @item.
  *
  * We add a reference on provided @object.
  */
 void
-na_object_item_insert_item( NAObjectItem *item, const NAObject *object )
+na_object_item_insert_item( NAObjectItem *item, const NAObject *object, const NAObject *before )
 {
+	GList *before_list;
+
 	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 	g_return_if_fail( !item->private->dispose_has_run );
 	g_return_if_fail( NA_IS_OBJECT( object ));
+	g_return_if_fail( NA_IS_OBJECT( before ));
 
 	if( !g_list_find( item->private->items, ( gpointer ) object )){
-		item->private->items = g_list_prepend( item->private->items, g_object_ref(( gpointer ) object ));
+		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, g_object_ref(( gpointer ) object ));
+		} else {
+			item->private->items = g_list_prepend( item->private->items, g_object_ref(( gpointer ) object ));
+		}
 	}
 }
 
 /**
  * na_object_item_remove_item:
- * @item: the #NAObjectItem item from which the subitems must be removed.
- * @object: a #NAObject object to be removed from list of subitems.
+ * @item: the #NAObjectItem from which the subitem must be removed.
+ * @object: a #NAObject to be removed from the list of subitems.
  *
- * Removes a subitem from the list of subitems.
+ * Removes an @object from the list of subitems of @item.
  *
- * We also decrement the reference count on removed subitem.
+ * We decrement the reference count on @object.
  */
 void
-na_object_item_remove_item( NAObjectItem *item, NAObject *object )
+na_object_item_remove_item( NAObjectItem *item, const NAObject *object )
 {
 	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 	g_return_if_fail( !item->private->dispose_has_run );
 	g_return_if_fail( NA_IS_OBJECT( object ));
 
-	item->private->items = g_list_remove( item->private->items, ( gconstpointer ) object );
-	g_object_unref( object );
+	if( g_list_find( item->private->items, ( gconstpointer ) object )){
+		item->private->items = g_list_remove( item->private->items, ( gconstpointer ) object );
+		g_object_unref(( gpointer ) object );
+	}
 }
 
 static void
 object_dump( const NAObject *item )
 {
 	static const gchar *thisfn = "na_object_item_object_dump";
-	GList *it;
+	/*GList *it;*/
 
 	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 	g_return_if_fail( !NA_OBJECT_ITEM( item )->private->dispose_has_run );
@@ -773,9 +791,13 @@ object_dump( const NAObject *item )
 			NA_OBJECT_ITEM( item )->private->items ? g_list_length( NA_OBJECT_ITEM( item )->private->items ) : 0,
 			( void * ) NA_OBJECT_ITEM( item )->private->items );
 
-	for( it = NA_OBJECT_ITEM( item )->private->items ; it ; it = it->next ){
+	/* do not recurse here, as this is actually dealt with by
+	 * na_object_dump() api ;
+	 * else, we would have the action being dumped after its childs
+	 */
+	/*for( it = NA_OBJECT_ITEM( item )->private->items ; it ; it = it->next ){
 		na_object_dump( it->data );
-	}
+	}*/
 }
 
 static void
@@ -832,21 +854,30 @@ object_copy( NAObject *target, const NAObject *source )
  *
  * note 1: The provider is not considered as pertinent here
  *
- * note 2: NAObjectItem recursively checks for equality in subitems.
- * Nonetheless, the modification status of subitems doesn't have any
+ * note 2: as a particular case, this function is not recursive
+ * because the equality test will stop as soon as it fails, and we so
+ * cannot be sure to even come here.
+ *
+ * The recursivity of na_object_check_edition_status() is directly
+ * dealt with by the main entry api function.
+ *
+ * More, the modification status of subitems doesn't have any
  * impact on this object itself, provided that subitems lists are
  * themselves identical
  *
- * note 3: Only NAObjectAction is modified that one of the profiles are
- * modified (because they are saved as a whole)
+ * note 3: #NAObjectAction is considered as modified when at least one
+ * of the profiles is itself modified (because they are saved as a
+ * whole). See #NAObjectAction.
  */
 static gboolean
 object_are_equal( const NAObject *a, const NAObject *b )
 {
 	gboolean equal = TRUE;
 	GList *it;
-	NAObject *first_obj, *second_obj;
 	gchar *first_id, *second_id;
+	NAObject *first_obj, *second_obj;
+	gint first_pos, second_pos;
+	GList *second_list;
 
 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( a ), FALSE );
 	g_return_val_if_fail( !NA_OBJECT_ITEM( a )->private->dispose_has_run, FALSE );
@@ -870,37 +901,49 @@ object_are_equal( const NAObject *a, const NAObject *b )
 
 	if( equal ){
 		for( it = NA_OBJECT_ITEM( a )->private->items ; it && equal ; it = it->next ){
-			first_obj = NA_OBJECT( it->data );
-			first_id = na_object_get_id( first_obj );
-			second_obj = ( NAObject * ) na_object_get_item( b, first_id );
-			g_free( first_id );
+			first_id = na_object_get_id( it->data );
+			second_obj = na_object_get_item( b, first_id );
 			if( second_obj ){
-				na_object_check_edition_status( second_obj );
+				first_pos = g_list_position( NA_OBJECT_ITEM( a )->private->items, it );
+				second_list = g_list_find( NA_OBJECT_ITEM( b )->private->items, second_obj );
+				second_pos = g_list_position( NA_OBJECT_ITEM( b )->private->items, second_list );
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+				g_debug( "na_object_item_object_are_equal: first_pos=%u, second_pos=%u", first_pos, second_pos );
+#endif
+				if( first_pos != second_pos ){
+					equal = FALSE;
+				}
 			} else {
 #if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
-				g_debug( "na_object_item_are_equal: object=%p (%s), equal=False", ( void * ) b, G_OBJECT_TYPE_NAME( b ));
+					g_debug( "na_object_item_object_are_equal: id=%s not found in b", first_id );
 #endif
 				equal = FALSE;
 			}
+			g_free( first_id );
 		}
 	}
 
 	if( equal ){
 		for( it = NA_OBJECT_ITEM( b )->private->items ; it && equal ; it = it->next ){
-			second_obj = NA_OBJECT( it->data );
-			second_id = na_object_get_id( second_obj );
-			first_obj = ( NAObject * ) na_object_get_item( a, second_id );
-			g_free( second_id );
+			second_id = na_object_get_id( it->data );
+			first_obj = na_object_get_item( a, second_id );
 			if( !first_obj ){
 #if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
-				g_debug( "na_object_item_are_equal: object=%p (%s), equal=False", ( void * ) b, G_OBJECT_TYPE_NAME( b ));
+					g_debug( "na_object_item_object_are_equal: id=%s not found in a", second_id );
 #endif
 				equal = FALSE;
 			}
+			g_free( second_id );
 		}
 	}
 
-	/*g_debug( "na_object_item_are_equal: %s", equal ? "True":"False" );*/
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_item_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
+			( void * ) a, G_OBJECT_TYPE_NAME( a ),
+			( void * ) b, G_OBJECT_TYPE_NAME( b ),
+			equal ? "True":"False" );
+#endif
+
 	return( equal );
 }
 
@@ -911,16 +954,16 @@ static gboolean
 object_is_valid( const NAObject *object )
 {
 	gboolean valid = TRUE;
-	GList *it;
+	/*GList *it;*/
 
 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( object ), FALSE );
 	g_return_val_if_fail( !NA_OBJECT_ITEM( object )->private->dispose_has_run, FALSE );
 
-	if( valid ){
+	/*if( valid ){
 		for( it = NA_OBJECT_ITEM( object )->private->items ; it && valid ; it = it->next ){
 			valid = na_object_is_valid( it->data );
 		}
-	}
+	}*/
 
 	return( valid );
 }
diff --git a/src/common/na-object-menu.c b/src/common/na-object-menu.c
index 1079ae4..9999929 100644
--- a/src/common/na-object-menu.c
+++ b/src/common/na-object-menu.c
@@ -32,6 +32,7 @@
 #include <config.h>
 #endif
 
+#include "na-iduplicable.h"
 #include "na-object-api.h"
 #include "na-object-action.h"
 #include "na-object-menu.h"
diff --git a/src/common/na-object-profile.c b/src/common/na-object-profile.c
index 60234dc..d7aacce 100644
--- a/src/common/na-object-profile.c
+++ b/src/common/na-object-profile.c
@@ -36,6 +36,7 @@
 
 #include <libnautilus-extension/nautilus-file-info.h>
 
+#include "na-iduplicable.h"
 #include "na-object-api.h"
 #include "na-object-action.h"
 #include "na-object-profile.h"
@@ -1551,6 +1552,13 @@ object_are_equal( const NAObject *a, const NAObject *b )
 				na_utils_string_lists_are_equal( first->private->schemes, second->private->schemes );
 	}
 
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_profile_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
+			( void * ) a, G_OBJECT_TYPE_NAME( a ),
+			( void * ) b, G_OBJECT_TYPE_NAME( b ),
+			equal ? "True":"False" );
+#endif
+
 	return( equal );
 }
 
diff --git a/src/common/na-object.c b/src/common/na-object.c
index 165da3e..643f9eb 100644
--- a/src/common/na-object.c
+++ b/src/common/na-object.c
@@ -34,6 +34,7 @@
 
 #include <string.h>
 
+#include "na-object-api.h"
 #include "na-object-class.h"
 #include "na-object-fn.h"
 #include "na-iduplicable.h"
@@ -60,31 +61,26 @@ static void           instance_constructed( GObject *object );
 static void           instance_dispose( GObject *object );
 static void           instance_finalize( GObject *object );
 
-static void           dump_hierarchy( const NAObject *object );
-static void           do_dump( const NAObject *object );
-
-static gchar         *most_derived_clipboard_id( const NAObject *object );
-
-static void           ref_hierarchy( const NAObject *object );
-
 static NAIDuplicable *iduplicable_new( const NAIDuplicable *object );
-static NAObject      *most_derived_new( const NAObject *object );
-
 static void           iduplicable_copy( NAIDuplicable *target, const NAIDuplicable *source );
-static void           copy_hierarchy( NAObject *target, const NAObject *source );
-
 static gboolean       iduplicable_are_equal( const NAIDuplicable *a, const NAIDuplicable *b );
-static gboolean       are_equal_hierarchy( const NAObject *a, const NAObject *b );
-static gboolean       do_are_equal( const NAObject *a, const NAObject *b );
-
 static gboolean       iduplicable_is_valid( const NAIDuplicable *object );
-static gboolean       is_valid_hierarchy( const NAObject *object );
-static gboolean       do_is_valid( const NAObject *object );
-
-static void           do_copy( NAObject *target, const NAObject *source );
 
 static GList         *v_get_childs( const NAObject *object );
+
+static gboolean       are_equal_hierarchy( const NAObject *a, const NAObject *b );
+static void           copy_hierarchy( NAObject *target, const NAObject *source );
+static gboolean       do_are_equal( const NAObject *a, const NAObject *b );
+static void           do_copy( NAObject *target, const NAObject *source );
+static void           do_dump( const NAObject *object );
+static gboolean       do_is_valid( const NAObject *object );
+static void           dump_hierarchy( const NAObject *object );
+static void           dump_tree( GList *tree, gint level );
+static gboolean       is_valid_hierarchy( const NAObject *object );
+static gchar         *most_derived_clipboard_id( const NAObject *object );
 static GList         *most_derived_get_childs( const NAObject *object );
+static NAObject      *most_derived_new( const NAObject *object );
+static void           ref_hierarchy( const NAObject *object );
 
 GType
 na_object_get_type( void )
@@ -241,246 +237,331 @@ instance_finalize( GObject *object )
 }
 
 /**
- * na_object_object_dump:
- * @object: the #NAObject-derived object to be dumped.
+ * na_object_iduplicable_check_edition_status:
+ * @object: the #NAObject object to be checked.
  *
- * Dumps via g_debug the actual content of the object.
+ * Recursively checks for the edition status of @object and its childs
+ * (if any).
+ *
+ * Internally set some properties which may be requested later. This
+ * two-steps check-request let us optimize some work in the UI.
+ *
+ * na_object_check_edition_status( object )
+ *  +- na_iduplicable_check_edition_status( object )
+ *      +- get_origin( object )
+ *      +- modified_status = v_are_equal( origin, object ) -> interface are_equal()
+ *      +- valid_status = v_is_valid( object )             -> interface is_valid()
+ *
+ * Note that the recursivity is managed here, so that we can be sure
+ * that edition status of childs is actually checked.
  */
 void
-na_object_object_dump( const NAObject *object )
+na_object_iduplicable_check_edition_status( const NAObject *object )
 {
+	GList *childs, *ic;
+
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_iduplicable_check_edition_status: object=%p (%s)",
+			( void * ) object, G_OBJECT_TYPE_NAME( object ));
+#endif
 	g_return_if_fail( NA_IS_OBJECT( object ));
 	g_return_if_fail( !object->private->dispose_has_run );
 
-	dump_hierarchy( object );
+	childs = v_get_childs( object );
+	for( ic = childs ; ic ; ic = ic->next ){
+		na_object_iduplicable_check_edition_status( NA_OBJECT( ic->data ));
+	}
+
+	na_iduplicable_check_edition_status( NA_IDUPLICABLE( object ));
 }
 
-static void
-dump_hierarchy( const NAObject *object )
+/**
+ * na_object_iduplicable_duplicate:
+ * @object: the #NAObject object to be dumped.
+ *
+ * Exactly duplicates a #NAObject-derived object.
+ *
+ * Returns: the new #NAObject.
+ *
+ *   na_object_duplicate( origin )
+ *   +- na_object_iduplicable_duplicate( origin )
+ *      +- na_iduplicable_duplicate( origin )
+ *         +- dup = v_new( object )
+ *         |  +- interface->new( object)
+ *         |     +- iduplicable_new( object )
+ *         |        +- most_derived_new( object )
+ *         |           +- object_new( ... ) from a derived class
+ *         +- v_copy( dup, origin )
+ *         |  +- interface->copy( dup, origin )
+ *         |     +- iduplicable_copy( target, source )
+ *         |        +- copy_hierarchy( target, source )
+ *         |           +- object_copy( ... ) from each successive derived class
+ *         +- set_origin( dup, origin )
+ *         +- set_modified( dup, FALSE )
+ *         +- set_valid( dup, FALSE )
+ *
+ * Though the interface api is not recursive per se, the implementation
+ * is ; i.e. duplicating a #NAObjectItem also duplicates the whole tree
+ * inside.
+ */
+NAObject *
+na_object_iduplicable_duplicate( const NAObject *object )
 {
-	GList *hierarchy, *ih;
+	NAIDuplicable *duplicate;
 
-	hierarchy = na_object_get_hierarchy( object );
+	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
+	g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
+	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
 
-	for( ih = hierarchy ; ih ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->dump ){
-			NA_OBJECT_CLASS( ih->data )->dump( object );
-		}
-	}
+	duplicate = na_iduplicable_duplicate( NA_IDUPLICABLE( object ));
 
-	na_object_free_hierarchy( hierarchy );
+	/*g_debug( "na_object_iduplicable_duplicate: object=%p (%s), duplicate=%p (%s)",
+			( void * ) object, G_OBJECT_TYPE_NAME( object ),
+			( void * ) duplicate, duplicate ? G_OBJECT_TYPE_NAME( duplicate ) : "" );*/
+
+	return( NA_OBJECT( duplicate ));
 }
 
-static void
-do_dump( const NAObject *object )
+/**
+ * na_object_iduplicable_are_equal:
+ * @a: a first #NAObject object.
+ * @b: a second #NAObject object to be compared to the first one.
+ *
+ * Compares the two #NAObject objects.
+ *
+ * At least when it finds that @a and @b are equal, each derived
+ * class should call its parent class to give it an opportunity to
+ * detect a difference.
+ *
+ * Returns: %TRUE if @a and @b are identical, %FALSE else.
+ */
+gboolean
+na_object_iduplicable_are_equal( const NAObject *a, const NAObject *b )
 {
-	static const char *thisfn = "na_object_do_dump";
-
-	g_debug( "%s: object=%p", thisfn, ( void * ) object );
+	g_return_val_if_fail( NA_IS_OBJECT( a ), FALSE );
+	g_return_val_if_fail( !a->private->dispose_has_run, FALSE );
+	g_return_val_if_fail( NA_IS_OBJECT( b ), FALSE );
+	g_return_val_if_fail( !b->private->dispose_has_run, FALSE );
 
-	na_iduplicable_dump( NA_IDUPLICABLE( object ));
+	return( are_equal_hierarchy( a, b ));
 }
 
 /**
- * na_object_object_get_clipboard_id:
- * @object: the #NAObject-derived object for which we will get a id.
+ * na_object_iduplicable_is_modified:
+ * @object: the #NAObject object whose status is requested.
  *
- * Returns: a newly allocated string which contains an id for the
- * #NAobject. This id is suitable for the internal clipboard.
+ * Returns the current modification status of @object.
  *
- * The returned string should be g_free() by the caller.
+ * This suppose that @object has been previously duplicated in order
+ * to get benefits provided by the IDuplicable interface.
+ *
+ * This suppose also that the edition status of @object has previously
+ * been checked via na_object_check_edited_status().
+ *
+ * Returns: %TRUE is the provided object has been modified regarding to
+ * the original one, %FALSE else.
  */
-gchar *
-na_object_object_get_clipboard_id( const NAObject *object )
+gboolean
+na_object_iduplicable_is_modified( const NAObject *object )
 {
-	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
-	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
+	g_return_val_if_fail( NA_IS_OBJECT( object ), FALSE );
+	g_return_val_if_fail( !object->private->dispose_has_run, FALSE );
 
-	return( most_derived_clipboard_id( object ));
+	return( na_iduplicable_is_modified( NA_IDUPLICABLE( object )));
 }
 
-static gchar *
-most_derived_clipboard_id( const NAObject *object )
+/**
+ * na_object_iduplicable_is_valid:
+ * @object: the #NAObject object whose validity is to be checked.
+ *
+ * Gets the validity status of @object.
+ *
+ * Returns: %TRUE is @object is valid, %FALSE else.
+ */
+gboolean
+na_object_iduplicable_is_valid( const NAObject *object )
 {
-	gchar *clipboard_id;
-	GList *hierarchy, *ih;
-	gboolean found;
-
-	found = FALSE;
-	clipboard_id = NULL;
-	hierarchy = g_list_reverse( na_object_get_hierarchy( object ));
-
-	for( ih = hierarchy ; ih && !found ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->get_clipboard_id ){
-			clipboard_id = NA_OBJECT_CLASS( ih->data )->get_clipboard_id( object );
-			found = TRUE;
-		}
-	}
-
-	na_object_free_hierarchy( hierarchy );
+	g_return_val_if_fail( NA_IS_OBJECT( object ), FALSE );
+	g_return_val_if_fail( !object->private->dispose_has_run, FALSE );
 
-	return( clipboard_id );
+	return( na_iduplicable_is_valid( NA_IDUPLICABLE( object )));
 }
 
 /**
- * TODO: get ride of this
- * na_object_object_ref:
- * @object: the #NAObject-derived object to be reffed.
+ * na_object_iduplicable_get_origin:
+ * @object: the #NAObject object whose status is requested.
  *
- * Returns: a ref on the #NAobject.
+ * Returns the original object which was at the origin of @object.
  *
- * If the object has childs, then it should also have reffed them.
+ * Returns: a #NAObject, or NULL.
+ *
+ * Do not use here NA_OBJECT macro as it may return a (valid) NULL value
  */
 NAObject *
-na_object_object_ref( const NAObject *object )
+na_object_iduplicable_get_origin( const NAObject *object )
 {
 	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
 	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
 
-	ref_hierarchy( object );
-
-	return( g_object_ref(( gpointer ) object ));
+	/*return( NA_OBJECT( na_iduplicable_get_origin( NA_IDUPLICABLE( object ))));*/
+	return(( NAObject * ) na_iduplicable_get_origin( NA_IDUPLICABLE( object )));
 }
 
-static void
-ref_hierarchy( const NAObject *object )
+/**
+ * na_object_iduplicable_set_origin:
+ * @object: the #NAObject object whose origin is to be set.
+ * @origin: a #NAObject which will be set as the new origin of @object.
+ *
+ * Sets the new origin of @object, and of all its childs.
+ *
+ * Be warned: but recursively reinitializing the origin to NULL, this
+ * function may cause difficult to solve issues.
+ */
+void
+na_object_iduplicable_set_origin( NAObject *object, const NAObject *origin )
 {
-	GList *hierarchy, *ih;
+	GList *childs, *ic;
 
-	hierarchy = na_object_get_hierarchy( object );
+	g_return_if_fail( NA_IS_OBJECT( object ));
+	g_return_if_fail( !object->private->dispose_has_run );
+	g_return_if_fail( NA_IS_OBJECT( origin ) || !origin );
+	g_return_if_fail( !origin || !origin->private->dispose_has_run );
 
-	for( ih = hierarchy ; ih ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->ref ){
-			NA_OBJECT_CLASS( ih->data )->ref( object );
-		}
-	}
+	na_iduplicable_set_origin( NA_IDUPLICABLE( object ), NA_IDUPLICABLE( origin ));
 
-	na_object_free_hierarchy( hierarchy );
+	childs = v_get_childs( object );
+	for( ic = childs ; ic ; ic = ic->next ){
+		na_object_iduplicable_set_origin( NA_OBJECT( ic->data ), origin );
+	}
 }
 
 /**
- * na_object_iduplicable_duplicate:
- * @object: the #NAObject object to be dumped.
- *
- * Exactly duplicates a #NAObject-derived object.
+ * na_object_object_dump:
+ * @object: the #NAObject-derived object to be dumped.
  *
- * Returns: the new #NAObject.
+ * Dumps via g_debug the actual content of the object.
  *
- *     na_object_duplicate( origin )
- *      +- na_iduplicable_duplicate( origin )
- *      |   +- dup = duplicate( origin )
- *      |   |   +- dup = v_new( object ) -> interface new()
- *      |   |   +- v_copy( dup, origin ) -> interface copy()
- *      |   |
- *      |   +- set_origin( dup, origin )
- *      |   +- set_modified( dup, FALSE )
- *      |   +- set_valid( dup, FALSE )
- *      |
- *      +- na_object_check_edited_status
+ * The recursivity is dealt with here. If we let #NAObjectItem do this,
+ * the dump of #NAObjectItem-derived object will be splitted, childs
+ * being inserted inside.
  */
-NAObject *
-na_object_iduplicable_duplicate( const NAObject *object )
+void
+na_object_object_dump( const NAObject *object )
 {
-	NAIDuplicable *duplicate;
+	GList *childs, *ic;
 
-	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
-	g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
-	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
+	g_return_if_fail( NA_IS_OBJECT( object ));
+	g_return_if_fail( !object->private->dispose_has_run );
 
-	duplicate = na_iduplicable_duplicate( NA_IDUPLICABLE( object ));
+	na_object_object_dump_norec( object );
 
-	/*g_debug( "na_object_iduplicable_duplicate: object is %s at %p, duplicate is %s at %p",
-			G_OBJECT_TYPE_NAME( object ), ( void * ) object,
-			duplicate ? G_OBJECT_TYPE_NAME( duplicate ) : "", ( void * ) duplicate );*/
+	childs = v_get_childs( object );
+	for( ic = childs ; ic ; ic = ic->next ){
+		na_object_object_dump( NA_OBJECT( ic->data ));
+	}
+}
 
-	/*if( duplicate ){
-		na_iduplicable_check_edition_status( duplicate );
-	}*/
+/**
+ * na_object_object_dump_norec:
+ * @object: the #NAObject-derived object to be dumped.
+ *
+ * Dumps via g_debug the actual content of the object.
+ *
+ * This function is not recursive.
+ */
+void
+na_object_object_dump_norec( const NAObject *object )
+{
+	g_return_if_fail( NA_IS_OBJECT( object ));
+	g_return_if_fail( !object->private->dispose_has_run );
 
-	return( NA_OBJECT( duplicate ));
+	dump_hierarchy( object );
 }
 
-static NAIDuplicable *
-iduplicable_new( const NAIDuplicable *object )
+/**
+ * na_object_object_dump_tree:
+ * @tree: a hierarchical list of #NAObject-derived objects.
+ *
+ * Outputs a brief, hierarchical dump of the provided list.
+ */
+void
+na_object_object_dump_tree( GList *tree )
 {
-	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
-	g_return_val_if_fail( !NA_OBJECT( object )->private->dispose_has_run, NULL );
-
-	return( NA_IDUPLICABLE( most_derived_new( NA_OBJECT( object ))));
+	dump_tree( tree, 0 );
 }
 
-static NAObject *
-most_derived_new( const NAObject *object )
+/**
+ * na_object_object_get_clipboard_id:
+ * @object: the #NAObject-derived object for which we will get a id.
+ *
+ * Returns: a newly allocated string which contains an id for the
+ * #NAobject. This id is suitable for the internal clipboard.
+ *
+ * The returned string should be g_free() by the caller.
+ */
+gchar *
+na_object_object_get_clipboard_id( const NAObject *object )
 {
-	NAObject *new_object;
-	GList *hierarchy, *ih;
-	gboolean found;
+	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
+	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
 
-	found = FALSE;
-	new_object = NULL;
-	hierarchy = g_list_reverse( na_object_get_hierarchy( object ));
+	return( most_derived_clipboard_id( object ));
+}
 
-	for( ih = hierarchy ; ih && !found ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->new ){
-			new_object = NA_OBJECT_CLASS( ih->data )->new( object );
-			found = TRUE;
-		}
-	}
+/**
+ * TODO: get ride of this
+ * na_object_object_ref:
+ * @object: the #NAObject-derived object to be reffed.
+ *
+ * Returns: a ref on the #NAobject.
+ *
+ * If the object has childs, then it should also have reffed them.
+ */
+NAObject *
+na_object_object_ref( const NAObject *object )
+{
+	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
+	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
 
-	na_object_free_hierarchy( hierarchy );
+	ref_hierarchy( object );
 
-	return( new_object );
+	return( g_object_ref(( gpointer ) object ));
 }
 
 /**
- * na_object_object_copy:
- * @target: the #NAObject-derived object which will receive data.
- * @source: the #NAObject-derived object which will provide data.
+ * na_object_object_rewind_origin:
+ * @target: must be a duplication of @source.
+ * @source: a #NAObject-derived object.
+ *
+ * Recursively rewind origin between @source to @target, so that
+ * @target appear as the origin of @source.
  *
- * Copies data and properties from @source to @target.
+ * The origin of @target itself is set to NULL.
+ *
+ * This only works if @target has just been duplicated from @source,
+ * and thus we do not have to check if childs lists are equal.
  */
 void
-na_object_object_copy( NAObject *target, const NAObject *source )
+na_object_object_rewind_origin( NAObject *target, const NAObject *source )
 {
+	GList *childs, *ic;
+	NAObject *origin;
+
 	g_return_if_fail( NA_IS_OBJECT( target ));
 	g_return_if_fail( !target->private->dispose_has_run );
 	g_return_if_fail( NA_IS_OBJECT( source ));
 	g_return_if_fail( !source->private->dispose_has_run );
 
-	copy_hierarchy( target, source );
-}
-
-static void
-iduplicable_copy( NAIDuplicable *target, const NAIDuplicable *source )
-{
-	g_return_if_fail( NA_IS_OBJECT( target ));
-	g_return_if_fail( !NA_OBJECT( target )->private->dispose_has_run );
-	g_return_if_fail( NA_IS_OBJECT( source ));
-	g_return_if_fail( !NA_OBJECT( source )->private->dispose_has_run );
-
-	copy_hierarchy( NA_OBJECT( target ), NA_OBJECT( source ));
-}
-
-static void
-copy_hierarchy( NAObject *target, const NAObject *source )
-{
-	GList *hierarchy, *ih;
-
-	hierarchy = na_object_get_hierarchy( source );
-
-	for( ih = hierarchy ; ih ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->copy ){
-			NA_OBJECT_CLASS( ih->data )->copy( target, source );
-		}
+	childs = v_get_childs( target );
+	for( ic = childs ; ic ; ic = ic->next ){
+		origin = na_object_get_origin( ic->data );
+		na_object_rewind_origin( ic->data, origin );
 	}
 
-	na_object_free_hierarchy( hierarchy );
-}
-
-static void
-do_copy( NAObject *target, const NAObject *source )
-{
-	/* nothing to do here */
+	origin = na_object_get_origin( target );
+	g_return_if_fail( origin == source );
+	na_iduplicable_set_origin( NA_IDUPLICABLE( source ), NA_IDUPLICABLE( target ));
+	na_iduplicable_set_origin( NA_IDUPLICABLE( target ), NULL );
 }
 
 /**
@@ -516,28 +597,24 @@ na_object_free_hierarchy( GList *hierarchy )
 	g_list_free( hierarchy );
 }
 
-/**
- * na_object_iduplicable_are_equal:
- * @a: a first #NAObject object.
- * @b: a second #NAObject object to be compared to the first one.
- *
- * Compares the two #NAObject objects.
- *
- * At least when it finds that @a and @b are equal, each derived
- * class should call its parent class to give it an opportunity to
- * detect a difference.
- *
- * Returns: %TRUE if @a and @b are identical, %FALSE else.
- */
-gboolean
-na_object_iduplicable_are_equal( const NAObject *a, const NAObject *b )
+static NAIDuplicable *
+iduplicable_new( const NAIDuplicable *object )
 {
-	g_return_val_if_fail( NA_IS_OBJECT( a ), FALSE );
-	g_return_val_if_fail( !a->private->dispose_has_run, FALSE );
-	g_return_val_if_fail( NA_IS_OBJECT( b ), FALSE );
-	g_return_val_if_fail( !b->private->dispose_has_run, FALSE );
+	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
+	g_return_val_if_fail( !NA_OBJECT( object )->private->dispose_has_run, NULL );
 
-	return( are_equal_hierarchy( a, b ));
+	return( NA_IDUPLICABLE( most_derived_new( NA_OBJECT( object ))));
+}
+
+static void
+iduplicable_copy( NAIDuplicable *target, const NAIDuplicable *source )
+{
+	g_return_if_fail( NA_IS_OBJECT( target ));
+	g_return_if_fail( !NA_OBJECT( target )->private->dispose_has_run );
+	g_return_if_fail( NA_IS_OBJECT( source ));
+	g_return_if_fail( !NA_OBJECT( source )->private->dispose_has_run );
+
+	copy_hierarchy( NA_OBJECT( target ), NA_OBJECT( source ));
 }
 
 static gboolean
@@ -552,6 +629,18 @@ iduplicable_are_equal( const NAIDuplicable *a, const NAIDuplicable *b )
 }
 
 static gboolean
+iduplicable_is_valid( const NAIDuplicable *object )
+{
+	return( is_valid_hierarchy( NA_OBJECT( object )));
+}
+
+static GList *
+v_get_childs( const NAObject *object ){
+
+	return( most_derived_get_childs( object ));
+}
+
+static gboolean
 are_equal_hierarchy( const NAObject *a, const NAObject *b )
 {
 	gboolean are_equal;
@@ -571,42 +660,56 @@ are_equal_hierarchy( const NAObject *a, const NAObject *b )
 	return( are_equal );
 }
 
+static void
+copy_hierarchy( NAObject *target, const NAObject *source )
+{
+	GList *hierarchy, *ih;
+
+	hierarchy = na_object_get_hierarchy( source );
+
+	for( ih = hierarchy ; ih ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->copy ){
+			NA_OBJECT_CLASS( ih->data )->copy( target, source );
+		}
+	}
+
+	na_object_free_hierarchy( hierarchy );
+}
+
 static gboolean
 do_are_equal( const NAObject *a, const NAObject *b )
 {
-	/*g_debug( "na_object_do_are_equal: a=%s at %p, b=%s at %p",
-			G_OBJECT_TYPE_NAME( a ), ( void * ) a, G_OBJECT_TYPE_NAME( b ), ( void * ) b );*/
+	gboolean are_equal;
 
 	/* as there is no data in NAObject, they are considered here as
 	 * equal is both null or both not null
 	 */
-	return(( a && b ) || ( !a && !b ));
+	are_equal = ( a && b ) || ( !a && !b );
+
+#if NA_IDUPLICABLE_EDITION_STATUS_DEBUG
+	g_debug( "na_object_do_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
+			( void * ) a, G_OBJECT_TYPE_NAME( a ),
+			( void * ) b, G_OBJECT_TYPE_NAME( b ),
+			are_equal ? "True":"False" );
+#endif
+
+	return( are_equal );
 }
 
-static gboolean
-iduplicable_is_valid( const NAIDuplicable *object )
+static void
+do_copy( NAObject *target, const NAObject *source )
 {
-	return( is_valid_hierarchy( NA_OBJECT( object )));
+	/* nothing to do here */
 }
 
-static gboolean
-is_valid_hierarchy( const NAObject *object )
+static void
+do_dump( const NAObject *object )
 {
-	gboolean is_valid;
-	GList *hierarchy, *ih;
-
-	is_valid = TRUE;
-	hierarchy = na_object_get_hierarchy( object );
-
-	for( ih = hierarchy ; ih && is_valid ; ih = ih->next ){
-		if( NA_OBJECT_CLASS( ih->data )->is_valid ){
-			is_valid = NA_OBJECT_CLASS( ih->data )->is_valid( object );
-		}
-	}
+	static const char *thisfn = "na_object_do_dump";
 
-	na_object_free_hierarchy( hierarchy );
+	g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
 
-	return( is_valid );
+	na_iduplicable_dump( NA_IDUPLICABLE( object ));
 }
 
 static gboolean
@@ -616,142 +719,95 @@ do_is_valid( const NAObject *object )
 	return( object ? TRUE : FALSE );
 }
 
-/**
- * na_object_iduplicable_check_edition_status:
- * @object: the #NAObject object to be checked.
- *
- * Recursively checks for the edition status of @object and its childs
- * (if any).
- *
- * Internally set some properties which may be requested later. This
- * two-steps check-request let us optimize some work in the UI.
- *
- * na_object_check_edition_status( object )
- *  +- na_iduplicable_check_edition_status( object )
- *      +- get_origin( object )
- *      +- modified_status = v_are_equal( origin, object ) -> interface are_equal()
- *      +- valid_status = v_is_valid( object )             -> interface is_valid()
- */
-void
-na_object_iduplicable_check_edition_status( const NAObject *object )
+static void
+dump_hierarchy( const NAObject *object )
 {
-	/*GList *childs, *ic;*/
+	GList *hierarchy, *ih;
 
-	g_return_if_fail( NA_IS_OBJECT( object ));
-	g_return_if_fail( !object->private->dispose_has_run );
+	hierarchy = na_object_get_hierarchy( object );
 
-	na_iduplicable_check_edition_status( NA_IDUPLICABLE( object ));
+	for( ih = hierarchy ; ih ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->dump ){
+			NA_OBJECT_CLASS( ih->data )->dump( object );
+		}
+	}
 
-	/*childs = v_get_childs( object );
-	for( ic = childs ; ic ; ic = ic->next ){
-		na_iduplicable_check_edition_status( NA_IDUPLICABLE( ic->data ));
-	}*/
+	na_object_free_hierarchy( hierarchy );
 }
 
-/**
- * na_object_iduplicable_is_modified:
- * @object: the #NAObject object whose status is requested.
- *
- * Returns the current modification status of @object.
- *
- * This suppose that @object has been previously duplicated in order
- * to get benefits provided by the IDuplicable interface.
- *
- * This suppose also that the edition status of @object has previously
- * been checked via na_object_check_edited_status().
- *
- * Returns: %TRUE is the provided object has been modified regarding to
- * the original one, %FALSE else.
- */
-gboolean
-na_object_iduplicable_is_modified( const NAObject *object )
+static void
+dump_tree( GList *tree, gint level )
 {
-	g_return_val_if_fail( NA_IS_OBJECT( object ), FALSE );
-	g_return_val_if_fail( !object->private->dispose_has_run, FALSE );
+	GString *prefix;
+	gint i;
+	GList *subitems, *it;
+	gchar *id;
+	gchar *label;
 
-	return( na_iduplicable_is_modified( NA_IDUPLICABLE( object )));
-}
+	prefix = g_string_new( "" );
+	for( i = 0 ; i < level ; ++i ){
+		g_string_append_printf( prefix, "  " );
+	}
 
-/**
- * na_object_iduplicable_is_valid:
- * @object: the #NAObject object whose validity is to be checked.
- *
- * Gets the validity status of @object.
- *
- * Returns: %TRUE is @object is valid, %FALSE else.
- */
-gboolean
-na_object_iduplicable_is_valid( const NAObject *object )
-{
-	g_return_val_if_fail( NA_IS_OBJECT( object ), FALSE );
-	g_return_val_if_fail( !object->private->dispose_has_run, FALSE );
+	for( it = tree ; it ; it = it->next ){
+		id = na_object_get_id( it->data );
+		label = na_object_get_label( it->data );
+		g_debug( "na_object_dump_tree: %s%p (%s) %s \"%s\"",
+				prefix->str, ( void * ) it->data, G_OBJECT_TYPE_NAME( it->data ), id, label );
+		g_free( id );
+		g_free( label );
+
+		if( NA_IS_OBJECT_ITEM( it->data )){
+			subitems = na_object_get_items( it->data );
+			dump_tree( subitems, level+1 );
+			na_object_free_items( subitems );
+		}
+	}
 
-	return( na_iduplicable_is_valid( NA_IDUPLICABLE( object )));
+	g_string_free( prefix, TRUE );
 }
 
-/**
- * na_object_iduplicable_get_origin:
- * @object: the #NAObject object whose status is requested.
- *
- * Returns the original object which was at the origin of @object.
- *
- * Returns: a #NAObject, or NULL.
- *
- * Do not use here NA_OBJECT macro as it may return a (valid) NULL value
- */
-NAObject *
-na_object_iduplicable_get_origin( const NAObject *object )
+static gboolean
+is_valid_hierarchy( const NAObject *object )
 {
-	g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
-	g_return_val_if_fail( !object->private->dispose_has_run, NULL );
+	gboolean is_valid;
+	GList *hierarchy, *ih;
 
-	/*return( NA_OBJECT( na_iduplicable_get_origin( NA_IDUPLICABLE( object ))));*/
-	return(( NAObject * ) na_iduplicable_get_origin( NA_IDUPLICABLE( object )));
-}
+	is_valid = TRUE;
+	hierarchy = na_object_get_hierarchy( object );
 
-/**
- * na_object_iduplicable_set_origin:
- * @object: the #NAObject object whose origin is to be set.
- * @origin: a #NAObject which will be set as the new origin of @object.
- *
- * Sets the new origin of @object.
- */
-void
-na_object_iduplicable_set_origin( NAObject *object, const NAObject *origin )
-{
-	g_return_if_fail( NA_IS_OBJECT( object ));
-	g_return_if_fail( !object->private->dispose_has_run );
-	g_return_if_fail( NA_IS_OBJECT( origin ) || !origin );
-	g_return_if_fail( !origin || !origin->private->dispose_has_run );
+	for( ih = hierarchy ; ih && is_valid ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->is_valid ){
+			is_valid = NA_OBJECT_CLASS( ih->data )->is_valid( object );
+		}
+	}
 
-	na_iduplicable_set_origin( NA_IDUPLICABLE( object ), NA_IDUPLICABLE( origin ));
+	na_object_free_hierarchy( hierarchy );
+
+	return( is_valid );
 }
 
-/**
- * na_object_iduplicable_set_origin_recurse:
- * @object: the #NAObject object whose origin is to be set.
- * @origin: a #NAObject which will be set as the new origin of @object.
- *
- * Sets the new origin of @object, and of all its childs if any.
- */
-void
-na_object_iduplicable_set_origin_recurse( NAObject *object, const NAObject *origin )
+static gchar *
+most_derived_clipboard_id( const NAObject *object )
 {
-	GList *childs, *ic;
-
-	na_object_iduplicable_set_origin( object, origin );
+	gchar *clipboard_id;
+	GList *hierarchy, *ih;
+	gboolean found;
 
-	childs = v_get_childs( object );
+	found = FALSE;
+	clipboard_id = NULL;
+	hierarchy = g_list_reverse( na_object_get_hierarchy( object ));
 
-	for( ic = childs ; ic ; ic = ic->next ){
-		na_object_iduplicable_set_origin_recurse( NA_OBJECT( ic->data ), origin );
+	for( ih = hierarchy ; ih && !found ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->get_clipboard_id ){
+			clipboard_id = NA_OBJECT_CLASS( ih->data )->get_clipboard_id( object );
+			found = TRUE;
+		}
 	}
-}
 
-static GList *
-v_get_childs( const NAObject *object ){
+	na_object_free_hierarchy( hierarchy );
 
-	return( most_derived_get_childs( object ));
+	return( clipboard_id );
 }
 
 static GList *
@@ -774,3 +830,42 @@ most_derived_get_childs( const NAObject *object )
 
 	return( childs );
 }
+
+static NAObject *
+most_derived_new( const NAObject *object )
+{
+	NAObject *new_object;
+	GList *hierarchy, *ih;
+	gboolean found;
+
+	found = FALSE;
+	new_object = NULL;
+	hierarchy = g_list_reverse( na_object_get_hierarchy( object ));
+
+	for( ih = hierarchy ; ih && !found ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->new ){
+			new_object = NA_OBJECT_CLASS( ih->data )->new( object );
+			found = TRUE;
+		}
+	}
+
+	na_object_free_hierarchy( hierarchy );
+
+	return( new_object );
+}
+
+static void
+ref_hierarchy( const NAObject *object )
+{
+	GList *hierarchy, *ih;
+
+	hierarchy = na_object_get_hierarchy( object );
+
+	for( ih = hierarchy ; ih ; ih = ih->next ){
+		if( NA_OBJECT_CLASS( ih->data )->ref ){
+			NA_OBJECT_CLASS( ih->data )->ref( object );
+		}
+	}
+
+	na_object_free_hierarchy( hierarchy );
+}
diff --git a/src/common/na-pivot.c b/src/common/na-pivot.c
index 8553524..88c47da 100644
--- a/src/common/na-pivot.c
+++ b/src/common/na-pivot.c
@@ -93,28 +93,30 @@ static guint         st_event_source_id = 0;
 static gint          st_timeout_msec = 100;
 static gint          st_timeout_usec = 100000;
 
-static GType    register_type( void );
-static void     class_init( NAPivotClass *klass );
-static void     iprefs_iface_init( NAIPrefsInterface *iface );
-static void     instance_init( GTypeInstance *instance, gpointer klass );
-static void     instance_dispose( GObject *object );
-static void     instance_finalize( GObject *object );
+static GType     register_type( void );
+static void      class_init( NAPivotClass *klass );
+static void      iprefs_iface_init( NAIPrefsInterface *iface );
+static void      instance_init( GTypeInstance *instance, gpointer klass );
+static void      instance_dispose( GObject *object );
+static void      instance_finalize( GObject *object );
+
+static NAObject *get_item_from_tree( const NAPivot *pivot, GList *tree, uuid_t uuid );
 
 /* NAIPivotConsumer management */
-static void     free_consumers( GSList *list );
+static void      free_consumers( GSList *list );
 
 /* NAIIOProvider management */
-static void     register_io_providers( NAPivot *pivot );
+static void      register_io_providers( NAPivot *pivot );
 
 /* NAGConf runtime preferences management */
-static void     read_runtime_preferences( NAPivot *pivot );
+static void      read_runtime_preferences( NAPivot *pivot );
 
-static void     action_changed_handler( NAPivot *pivot, gpointer user_data );
-static gboolean on_actions_changed_timeout( gpointer user_data );
-static gulong   time_val_diff( const GTimeVal *recent, const GTimeVal *old );
+static void      action_changed_handler( NAPivot *pivot, gpointer user_data );
+static gboolean  on_actions_changed_timeout( gpointer user_data );
+static gulong    time_val_diff( const GTimeVal *recent, const GTimeVal *old );
 
-static void     on_display_order_change( NAPivot *pivot, gpointer user_data );
-static void     on_display_about_change( NAPivot *pivot, gpointer user_data );
+static void      on_display_order_change( NAPivot *pivot, gpointer user_data );
+static void      on_display_about_change( NAPivot *pivot, gpointer user_data );
 
 GType
 na_pivot_get_type( void )
@@ -455,26 +457,6 @@ na_pivot_add_item( NAPivot *pivot, const NAObject *item )
 }
 
 /**
- * na_pivot_remove_item:
- * @pivot: this #NAPivot instance.
- * @item: the #NAObjectItem to be removed from the list.
- *
- * Removes a #NAObjectItem from the hierarchical tree.
- *
- * Note that #NAPivot also g_object_unref() the removed #NAObjectItem.
- */
-void
-na_pivot_remove_item( NAPivot *pivot, NAObject *item )
-{
-	g_return_if_fail( NA_IS_PIVOT( pivot ));
-	g_return_if_fail( !pivot->private->dispose_has_run );
-	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
-
-	pivot->private->tree = g_list_remove( pivot->private->tree, ( gconstpointer ) item );
-	g_object_unref( item );
-}
-
-/**
  * na_pivot_get_action:
  * @pivot: this #NAPivot instance.
  * @uuid: the required globally unique identifier (uuid).
@@ -488,8 +470,7 @@ na_pivot_remove_item( NAPivot *pivot, NAObject *item )
 NAObject *
 na_pivot_get_item( const NAPivot *pivot, const gchar *uuid )
 {
-	uuid_t uua, i_uub;
-	GList *ia;
+	uuid_t uuid_bin;
 
 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
 	g_return_val_if_fail( !pivot->private->dispose_has_run, NULL );
@@ -498,64 +479,77 @@ na_pivot_get_item( const NAPivot *pivot, const gchar *uuid )
 		return( NULL );
 	}
 
-	uuid_parse( uuid, uua );
+	uuid_parse( uuid, uuid_bin );
 
-	for( ia = pivot->private->tree ; ia ; ia = ia->next ){
+	return( get_item_from_tree( pivot, pivot->private->tree, uuid_bin ));
+}
 
-		gchar *i_uuid = na_object_get_id( NA_OBJECT( ia->data ));
-		uuid_parse( i_uuid, i_uub );
-		g_free( i_uuid );
+/**
+ * na_pivot_remove_item:
+ * @pivot: this #NAPivot instance.
+ * @item: the #NAObjectItem to be removed from the list.
+ *
+ * Removes a #NAObjectItem from the hierarchical tree.
+ *
+ * Note that #NAPivot also g_object_unref() the removed #NAObjectItem.
+ */
+void
+na_pivot_remove_item( NAPivot *pivot, NAObject *item )
+{
+	g_debug( "na_pivot_remove_item: pivot=%p, item=%p (%s)",
+			( void * ) pivot,
+			( void * ) item, G_OBJECT_TYPE_NAME( item ));
 
-		if( !uuid_compare( uua, i_uub )){
-			return( NA_OBJECT( ia->data ));
-		}
-	}
+	g_return_if_fail( NA_IS_PIVOT( pivot ));
+	g_return_if_fail( !pivot->private->dispose_has_run );
+	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 
-	return( NULL );
+	pivot->private->tree = g_list_remove( pivot->private->tree, ( gconstpointer ) item );
+	g_object_unref( item );
 }
 
 /**
- * na_pivot_write_item:
+ * na_pivot_delete_item:
  * @pivot: this #NAPivot instance.
- * @item: a #NAObjectItem to be written by the storage subsystem.
+ * @item: the #NAObjectItem to be deleted from the storage subsystem.
  * @message: the I/O provider can allocate and store here an error
  * message.
  *
- * Writes an item (an action or a menu).
+ * Deletes an action from the I/O storage subsystem.
  *
  * Returns: the #NAIIOProvider return code.
  */
 guint
-na_pivot_write_item( const NAPivot *pivot, NAObject *item, gchar **message )
+na_pivot_delete_item( const NAPivot *pivot, const NAObject *item, gchar **message )
 {
 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( !pivot->private->dispose_has_run, NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( message, NA_IIO_PROVIDER_PROGRAM_ERROR );
 
-	return( na_iio_provider_write_item( pivot, item, message ));
+	return( na_iio_provider_delete_item( pivot, item, message ));
 }
 
 /**
- * na_pivot_delete_item:
+ * na_pivot_write_item:
  * @pivot: this #NAPivot instance.
- * @item: the #NAObjectItem to be deleted from the storage subsystem.
+ * @item: a #NAObjectItem to be written by the storage subsystem.
  * @message: the I/O provider can allocate and store here an error
  * message.
  *
- * Deletes an action from the I/O storage subsystem.
+ * Writes an item (an action or a menu).
  *
  * Returns: the #NAIIOProvider return code.
  */
 guint
-na_pivot_delete_item( const NAPivot *pivot, const NAObject *item, gchar **message )
+na_pivot_write_item( const NAPivot *pivot, NAObject *item, gchar **message )
 {
 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( !pivot->private->dispose_has_run, NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NA_IIO_PROVIDER_PROGRAM_ERROR );
 	g_return_val_if_fail( message, NA_IIO_PROVIDER_PROGRAM_ERROR );
 
-	return( na_iio_provider_delete_item( pivot, item, message ));
+	return( na_iio_provider_write_item( pivot, item, message ));
 }
 
 /**
@@ -618,6 +612,33 @@ na_pivot_set_automatic_reload( NAPivot *pivot, gboolean reload )
 	pivot->private->automatic_reload = reload;
 }
 
+static NAObject *
+get_item_from_tree( const NAPivot *pivot, GList *tree, uuid_t uuid )
+{
+	uuid_t i_uuid_bin;
+	GList *subitems, *ia;
+	NAObject *found = NULL;
+
+	for( ia = tree ; ia && !found ; ia = ia->next ){
+
+		gchar *i_uuid = na_object_get_id( NA_OBJECT( ia->data ));
+		uuid_parse( i_uuid, i_uuid_bin );
+		g_free( i_uuid );
+
+		if( !uuid_compare( uuid, i_uuid_bin )){
+			found = NA_OBJECT( ia->data );
+		}
+
+		if( !found && NA_IS_OBJECT_ITEM( ia->data )){
+			subitems = na_object_get_items( ia->data );
+			found = get_item_from_tree( pivot, subitems, uuid );
+			na_object_free_items( subitems );
+		}
+	}
+
+	return( found );
+}
+
 static void
 free_consumers( GSList *consumers )
 {
diff --git a/src/common/na-pivot.h b/src/common/na-pivot.h
index 51b7e8a..fbc9ba7 100644
--- a/src/common/na-pivot.h
+++ b/src/common/na-pivot.h
@@ -114,11 +114,11 @@ GList    *na_pivot_get_items( const NAPivot *pivot );
 void      na_pivot_reload_items( NAPivot *pivot );
 
 void      na_pivot_add_item( NAPivot *pivot, const NAObject *item );
-void      na_pivot_remove_item( NAPivot *pivot, NAObject *item );
 NAObject *na_pivot_get_item( const NAPivot *pivot, const gchar *uuid );
+void      na_pivot_remove_item( NAPivot *pivot, NAObject *item );
 
-guint     na_pivot_write_item( const NAPivot *pivot, NAObject *item, gchar **message );
 guint     na_pivot_delete_item( const NAPivot *pivot, const NAObject *item, gchar **message );
+guint     na_pivot_write_item( const NAPivot *pivot, NAObject *item, gchar **message );
 
 void      na_pivot_register_consumer( NAPivot *pivot, const NAIPivotConsumer *consumer );
 
diff --git a/src/nact/nact-assistant-import.c b/src/nact/nact-assistant-import.c
index c588cd9..e120263 100644
--- a/src/nact/nact-assistant-import.c
+++ b/src/nact/nact-assistant-import.c
@@ -43,6 +43,7 @@
 
 #include "base-iprefs.h"
 #include "base-application.h"
+#include "nact-iactions-list.h"
 #include "nact-assistant-import.h"
 #include "nact-xml-reader.h"
 
@@ -247,11 +248,12 @@ assist_new( BaseApplication *application )
 }
 
 /**
- * Run the assistant.
+ * nact_assistant_import_run:
+ * @main: the #NactMainWindow parent window of this assistant.
  *
- * @main: the main window of the application.
+ * Run the assistant.
  */
-GSList *
+void
 nact_assistant_import_run( BaseWindow *main_window )
 {
 	BaseApplication *appli;
@@ -265,7 +267,7 @@ nact_assistant_import_run( BaseWindow *main_window )
 
 	base_window_run( BASE_WINDOW( assist ));
 
-	return( assist->private->actions );
+	/*g_object_unref( assist );*/
 }
 
 static gchar *
@@ -433,6 +435,8 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 	GSList *uris, *is, *msg;
 	NAObjectAction *action;
 	ImportUriStruct *str;
+	GList *items;
+	BaseWindow *mainwnd;
 
 	g_debug( "%s: window=%p, assistant=%p", thisfn, ( void * ) wnd, ( void * ) assistant );
 	g_assert( NACT_IS_ASSISTANT_IMPORT( wnd ));
@@ -441,6 +445,8 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 	chooser = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
 	uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( chooser ));
 
+	g_object_get( G_OBJECT( wnd ), BASE_WINDOW_PROP_PARENT, &mainwnd, NULL );
+
 	for( is = uris ; is ; is = is->next ){
 
 		msg = NULL;
@@ -450,10 +456,13 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 		str->uri = g_strdup(( const gchar * ) is->data );
 		str->action = action;
 		str->msg = na_utils_duplicate_string_list( msg );
+		na_utils_free_string_list( msg );
 
 		window->private->results = g_slist_prepend( window->private->results, str );
 
-		na_utils_free_string_list( msg );
+		items = g_list_prepend( NULL, action );
+		nact_iactions_list_insert_items( NACT_IACTIONS_LIST( mainwnd ), items, NULL );
+		na_object_free_items( items );
 	}
 
 	na_utils_free_string_list( uris );
diff --git a/src/nact/nact-assistant-import.h b/src/nact/nact-assistant-import.h
index 0ca3572..2e679cb 100644
--- a/src/nact/nact-assistant-import.h
+++ b/src/nact/nact-assistant-import.h
@@ -62,9 +62,9 @@ typedef struct {
 }
 	NactAssistantImportClass;
 
-GType   nact_assistant_import_get_type( void );
+GType nact_assistant_import_get_type( void );
 
-GSList *nact_assistant_import_run( BaseWindow *main );
+void  nact_assistant_import_run( BaseWindow *main );
 
 G_END_DECLS
 
diff --git a/src/nact/nact-clipboard.c b/src/nact/nact-clipboard.c
index a52b4ac..3ba9cf3 100644
--- a/src/nact/nact-clipboard.c
+++ b/src/nact/nact-clipboard.c
@@ -277,7 +277,7 @@ nact_clipboard_primary_get( void )
 
 		for( it = data->items ; it ; it = it->next ){
 			obj = na_object_duplicate( it->data );
-			na_object_set_origin_rec( obj, NULL );
+			na_object_set_origin( obj, NULL );
 			items = g_list_prepend( items, obj );
 		}
 		items = g_list_reverse( items );
diff --git a/src/nact/nact-iactions-list.c b/src/nact/nact-iactions-list.c
index f20980b..fd95d2e 100644
--- a/src/nact/nact-iactions-list.c
+++ b/src/nact/nact-iactions-list.c
@@ -95,6 +95,14 @@ typedef struct {
 }
 	ObjectToPathIter;
 
+/* when iterating while searching for an object
+ */
+typedef struct {
+	NAObject *object;
+	gchar    *uuid;
+}
+	IdToObjectIter;
+
 /* data set against GObject
  */
 #define SELECTION_CHANGED_SIGNAL_MODE	"nact-iactions-list-selection-changed-signal-mode"
@@ -117,7 +125,8 @@ static void         extend_selection_to_childs( NactIActionsList *instance, GtkT
 static gboolean     filter_selection( GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, NactIActionsList *instance );
 static void         filter_selection_iter( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, SelectionIter *str );
 static GtkTreeView *get_actions_list_treeview( NactIActionsList *instance );
-static gboolean     get_item_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, GList **items );
+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 );
@@ -481,7 +490,7 @@ nact_iactions_list_delete_selection( NactIActionsList *instance )
 	g_list_free( selected );
 
 	if( path ){
-		/*gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( model ));*/
+		gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( model ));
 		select_row_at_path( instance, treeview, model, path );
 		gtk_tree_path_free( path );
 	}
@@ -518,6 +527,41 @@ nact_iactions_list_fill( NactIActionsList *instance, GList *items )
 }
 
 /**
+ * nact_iactions_list_get_item:
+ * @window: this #NactIActionsList instance.
+ * @uuid: the id of the searched object.
+ *
+ * Returns: a pointer on the #NAObject which has this id, or NULL.
+ *
+ * The returned pointer is owned by IActionsList (actually by the
+ * underlying tree store), and should not be released by the caller.
+ */
+NAObject *
+nact_iactions_list_get_item( NactIActionsList *instance, const gchar *uuid )
+{
+	NAObject *item = NULL;
+	GtkTreeView *treeview;
+	NactTreeModel *model;
+	IdToObjectIter *ito;
+
+	g_return_val_if_fail( NACT_IS_IACTIONS_LIST( instance ), NULL );
+
+	treeview = get_actions_list_treeview( instance );
+	model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
+
+	ito = g_new0( IdToObjectIter, 1 );
+	ito->uuid = ( gchar * ) uuid;
+
+	nact_tree_model_iter( model, ( FnIterOnStore ) get_item_iter, ito );
+
+	item = ito->object;
+
+	g_free( ito );
+
+	return( item );
+}
+
+/**
  * nact_iactions_list_get_items:
  * @window: this #NactIActionsList instance.
  *
@@ -538,7 +582,7 @@ nact_iactions_list_get_items( NactIActionsList *instance )
 	treeview = get_actions_list_treeview( instance );
 	model = NACT_TREE_MODEL( gtk_tree_view_get_model( treeview ));
 
-	nact_tree_model_iter( model, ( FnIterOnStore ) get_item_iter, &items );
+	nact_tree_model_iter( model, ( FnIterOnStore ) get_items_iter, &items );
 
 	return( g_list_reverse( items ));
 }
@@ -1115,10 +1159,31 @@ get_actions_list_treeview( NactIActionsList *instance )
 }
 
 /*
+ * search for an object, given its uuid
+ */
+static gboolean
+get_item_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, IdToObjectIter *ito )
+{
+	gchar *id;
+	gboolean found = FALSE;
+
+	id = na_object_get_id( object );
+	found = ( g_ascii_strcasecmp( id, ito->uuid ) == 0 );
+	g_free( id );
+
+	if( found ){
+		ito->object = object;
+	}
+
+	/* stop iteration if found */
+	return( found );
+}
+
+/*
  * builds the tree
  */
 static gboolean
-get_item_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, GList **items )
+get_items_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, GList **items )
 {
 	if( gtk_tree_path_get_depth( path ) == 1 ){
 		*items = g_list_prepend( *items, object );
@@ -1328,7 +1393,7 @@ on_iactions_list_item_updated( NactIActionsList *instance, NAObject *object )
 static void
 on_iactions_list_item_updated_treeview( NactIActionsList *instance, NAObject *object )
 {
-	NAObjectAction *item;
+	NAObject *item;
 	GtkTreeView *treeview;
 	GtkTreeModel *model;
 
@@ -1336,12 +1401,15 @@ on_iactions_list_item_updated_treeview( NactIActionsList *instance, NAObject *ob
 		treeview = get_actions_list_treeview( instance );
 		model = gtk_tree_view_get_model( treeview );
 
-		na_object_check_edition_status( object );
-		nact_tree_model_display( NACT_TREE_MODEL( model ), object );
+		item = object;
+		if( NA_IS_OBJECT_PROFILE( object )){
+			item = NA_OBJECT( na_object_profile_get_action( NA_OBJECT_PROFILE( object )));
+		}
 
+		na_object_check_edition_status( item );
+
+		nact_tree_model_display( NACT_TREE_MODEL( model ), object );
 		if( NA_IS_OBJECT_PROFILE( object )){
-			item = na_object_profile_get_action( NA_OBJECT_PROFILE( object ));
-			na_object_check_edition_status( item );
 			nact_tree_model_display( NACT_TREE_MODEL( model ), NA_OBJECT( item ));
 		}
 	}
diff --git a/src/nact/nact-iactions-list.h b/src/nact/nact-iactions-list.h
index a05c7ec..3522f60 100644
--- a/src/nact/nact-iactions-list.h
+++ b/src/nact/nact-iactions-list.h
@@ -78,6 +78,7 @@ void      nact_iactions_list_dispose( NactIActionsList *instance );
 
 void      nact_iactions_list_delete_selection( NactIActionsList *instance );
 void      nact_iactions_list_fill( NactIActionsList *instance, GList *items );
+NAObject *nact_iactions_list_get_item( NactIActionsList *instance, const gchar *uuid );
 GList    *nact_iactions_list_get_items( NactIActionsList *instance );
 GList    *nact_iactions_list_get_selected_items( NactIActionsList *instance );
 gboolean  nact_iactions_list_has_exportable( NactIActionsList *instance );
diff --git a/src/nact/nact-main-menubar.c b/src/nact/nact-main-menubar.c
index 47e715a..3fe1747 100644
--- a/src/nact/nact-main-menubar.c
+++ b/src/nact/nact-main-menubar.c
@@ -54,13 +54,28 @@
 #define MENUBAR_PROP_UI_MANAGER			"nact-menubar-ui-manager"
 #define MENUBAR_PROP_ACTIONS_GROUP		"nact-menubar-actions-group"
 
+/* GtkActivatable
+ * gtk_action_get_tooltip are only available starting with Gtk 2.16
+ * until this is a required level, we must have some code to do the
+ * same thing
+ */
+#undef GTK_HAS_ACTIVATABLE
+#if(( GTK_MAJOR_VERSION > 2 ) || ( GTK_MINOR_VERSION >= 16 ))
+	#define GTK_HAS_ACTIVATABLE
+#endif
+
+#ifndef GTK_HAS_ACTIVATABLE
+#define MENUBAR_PROP_ITEM_ACTION		"nact-menubar-item-action"
+#endif
+
 static void     on_tab_updatable_selection_changed( NactMainWindow *window, gint count_selected );
 
 static void     on_new_menu_activated( GtkAction *action, NactMainWindow *window );
 static void     on_new_action_activated( GtkAction *action, NactMainWindow *window );
 static void     on_new_profile_activated( GtkAction *action, NactMainWindow *window );
 static void     on_save_activated( GtkAction *action, NactMainWindow *window );
-static void     save_item( NAObject *object, NactMainWindow *window );
+static void     save_items( NactMainWindow *window, NAPivot *pivot, GList *items );
+static void     save_item( NactMainWindow *window, NAPivot *pivot, NAObjectItem *item );
 static void     on_quit_activated( GtkAction *action, NactMainWindow *window );
 
 static void     on_cut_activated( GtkAction *action, NactMainWindow *window );
@@ -242,12 +257,23 @@ nact_main_menubar_runtime_init( NactMainWindow *window )
 /**
  * nact_main_menubar_refresh_actions_sensitivity:
  *
- * Sensitivity of items in the menubar is primarily refreshed when
- * "tab-updatable-selection-updated" signal is received.
- * This signal itself is sent on new selection in IActionsList.
- * E.g in "cut" action, this happens before having stored the items
- * in the clipboard.
- * We so have to refresh the menubar items on demand.
+ * 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.
+ *
  */
 void
 nact_main_menubar_refresh_actions_sensitivity( NactMainWindow *window )
@@ -333,30 +359,34 @@ 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
+ * modified (or has been successfully saved)
  */
 static void
 on_save_activated( GtkAction *gtk_action, NactMainWindow *window )
 {
 	GList *items;
+	NactApplication *application;
+	NAPivot *pivot;
 
 	g_return_if_fail( GTK_IS_ACTION( gtk_action ));
 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
 
+	/* delete the removed actions
+	 */
+	nact_main_window_remove_deleted( window );
+
 	items = nact_iactions_list_get_items( NACT_IACTIONS_LIST( window ));
 	nact_window_write_level_zero( NACT_WINDOW( window ), items );
 
 	/* recursively save the valid modified items
 	 */
-	g_list_foreach( items, ( GFunc ) save_item, window );
-
-	/* doesn't unref object owned by the tree store
-	 */
+	application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
+	pivot = nact_application_get_pivot( application );
+	save_items( window, pivot, items );
 	g_list_free( items );
 
-	/* delete the removed actions
-	 */
-	nact_main_window_remove_deleted( window );
-
 	/* required as selection has not changed
 	 */
 	nact_main_menubar_refresh_actions_sensitivity( window );
@@ -367,48 +397,61 @@ on_save_activated( GtkAction *gtk_action, NactMainWindow *window )
 }
 
 /*
+ * only recurse in menus
+ */
+static void
+save_items( NactMainWindow *window, NAPivot *pivot, GList *items )
+{
+	GList *it;
+
+	for( it = items ; it ; it = it->next ){
+		save_item( window, pivot, NA_OBJECT_ITEM( it->data ));
+	}
+}
+
+/*
  * iterates here on each and every row stored in the tree
  * - do not deal with profiles as they are directly managed by their
  *   action parent
- * - do not deal with not modified, or not valid, items
+ * - do not deal with not modified, or not valid, items, but allow
+ *   for save their subitems
  */
 static void
-save_item( NAObject *object, NactMainWindow *window )
+save_item( NactMainWindow *window, NAPivot *pivot, NAObjectItem *item )
 {
-	NactApplication *application;
-	NAPivot *pivot;
 	NAObjectItem *origin;
 	NAObjectItem *dup_pivot;
+	GList *subitems;
 
-	g_return_if_fail( NA_IS_OBJECT( object ));
 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
+	g_return_if_fail( NA_IS_PIVOT( pivot ));
+	g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
 
-	if( !NA_IS_OBJECT_PROFILE( object ) &&
-		na_object_is_modified( NA_OBJECT( object )) &&
-		na_object_is_valid( NA_OBJECT( object ))){
+	if( NA_IS_OBJECT_MENU( item )){
+		subitems = na_object_get_items( item );
+		save_items( window, pivot, subitems );
+		na_object_free_items( subitems );
+	}
 
-		g_return_if_fail( NA_IS_OBJECT_ITEM( object ));
+	if( na_object_is_modified( item ) &&
+		na_object_is_valid( item )){
 
-		if( nact_window_save_item( NACT_WINDOW( window ), NA_OBJECT_ITEM( object ))){
+		if( nact_window_save_item( NACT_WINDOW( window ), item )){
 
-			/* do not use NA_OBJECT_ITEM macro as this may return a (valid)
-			 * NULL value
+			/* do not use NA_OBJECT_ITEM macro as this may return a
+			 * (valid) NULL value
 			 */
-			origin = ( NAObjectItem * ) na_object_get_origin( object );
+			origin = ( NAObjectItem * ) na_object_get_origin( item );
 
 			if( origin ){
-				na_object_copy( origin, object );
-
-			} else {
-				application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
-				pivot = nact_application_get_pivot( application );
-				dup_pivot = NA_OBJECT_ITEM( na_object_duplicate( object ));
-				na_object_set_origin( dup_pivot, NULL );
-				na_object_set_origin( object, dup_pivot );
-				na_pivot_add_item( pivot, NA_OBJECT( dup_pivot ));
+				na_pivot_remove_item( pivot, NA_OBJECT( origin ));
 			}
 
-			na_object_check_edition_status( object );
+			dup_pivot = NA_OBJECT_ITEM( na_object_duplicate( item ));
+			na_object_rewind_origin( dup_pivot, item );
+			na_pivot_add_item( pivot, NA_OBJECT( dup_pivot ));
+
+			na_object_check_edition_status( item );
 		}
 	}
 }
@@ -432,11 +475,10 @@ on_quit_activated( GtkAction *gtk_action, NactMainWindow *window )
 /*
  * cuts the visible selection
  * - (tree) get new refs on selected items
- * - (tree) remove selected items, unreffing objects
  * - (main) add selected items to main list of deleted,
  *          moving newref from list_from_tree to main_list_of_deleted
  * - (menu) install in clipboard a copy of selected objects
- * - (tree) select next row (if any, or previous if any, or none)
+ * - (tree) remove selected items, unreffing objects
  */
 static void
 on_cut_activated( GtkAction *gtk_action, NactMainWindow *window )
@@ -485,7 +527,7 @@ on_copy_activated( GtkAction *gtk_action, NactMainWindow *window )
  *          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
- *          checking edition status of the topmost parent
+ *          recursively checking edition status of the topmost parent
  *          selecting the first item at end
  * - (menu) unreffing the copy got from clipboard
  */
@@ -523,7 +565,7 @@ on_duplicate_activated( GtkAction *gtk_action, NactMainWindow *window )
 		if( NA_IS_OBJECT_ITEM( obj )){
 			na_object_set_new_id( obj );
 		}
-		na_object_set_origin_rec( obj, NULL );
+		na_object_set_origin( obj, NULL );
 		dup = g_list_prepend( NULL, obj );
 		nact_iactions_list_insert_items( NACT_IACTIONS_LIST( window ), dup, it->data );
 		na_object_free_items( dup );
@@ -626,27 +668,49 @@ on_delete_event( GtkWidget *toplevel, GdkEvent *event, NactMainWindow *window )
 static void
 on_destroy_callback( gpointer data )
 {
-	g_debug( "nact_main_menubar_on_destroy_callback: data=%p", ( void * ) data );
+	static const gchar *thisfn = "nact_main_menubar_on_destroy_callback";
+
+	g_debug( "%s: data=%p (%s)", thisfn,
+			( void * ) data, G_OBJECT_TYPE_NAME( data ));
+
 	g_object_unref( G_OBJECT( data ));
 }
 
+/*
+ * gtk_activatable_get_related_action() and gtk_action_get_tooltip()
+ * are only available starting with Gtk 2.16
+ */
 static void
 on_menu_item_selected( GtkMenuItem *proxy, NactMainWindow *window )
 {
 	GtkAction *action;
-	const gchar *tooltip;
+	gchar *tooltip;
+
+	/*g_debug( "nact_main_menubar_on_menu_item_selected: proxy=%p (%s), window=%p (%s)",
+			( void * ) proxy, G_OBJECT_TYPE_NAME( proxy ),
+			( void * ) window, G_OBJECT_TYPE_NAME( window ));*/
+
+	tooltip = NULL;
 
+#ifdef GTK_HAS_ACTIVATABLE
 	action = gtk_activatable_get_related_action( GTK_ACTIVATABLE( proxy ));
-	if( !action ){
-		return;
+	if( action ){
+		tooltip = ( gchar * ) gtk_action_get_tooltip( action );
 	}
+#else
+	action = GTK_ACTION( g_object_get_data( G_OBJECT( proxy ), MENUBAR_PROP_ITEM_ACTION ));
+	if( action ){
+		g_object_get( G_OBJECT( action ), "tooltip", &tooltip, NULL );
+	}
+#endif
 
-	tooltip = gtk_action_get_tooltip( action );
-	if( !tooltip ){
-		return;
+	if( tooltip ){
+		nact_main_statusbar_display_status( window, MENUBAR_PROP_STATUS_CONTEXT, tooltip );
 	}
 
-	nact_main_statusbar_display_status( window, MENUBAR_PROP_STATUS_CONTEXT, tooltip );
+#ifndef GTK_HAS_ACTIVATABLE
+	g_free( tooltip );
+#endif
 }
 
 static void
@@ -658,8 +722,14 @@ on_menu_item_deselected( GtkMenuItem *proxy, NactMainWindow *window )
 static void
 on_proxy_connect( GtkActionGroup *action_group, GtkAction *action, GtkWidget *proxy, NactMainWindow *window )
 {
-	g_debug( "on_proxy_connect: action_group=%p, action=%p, proxy=%p, window=%p",
-			( void * ) action_group, ( void * ) action, ( void * ) proxy, ( void * ) window );
+	static const gchar *thisfn = "nact_main_menubar_on_proxy_connect";
+
+	g_debug( "%s: action_group=%p (%s), action=%p (%s), proxy=%p (%s), window=%p (%s)",
+			thisfn,
+			( void * ) action_group, G_OBJECT_TYPE_NAME( action_group ),
+			( void * ) action, G_OBJECT_TYPE_NAME( action ),
+			( void * ) proxy, G_OBJECT_TYPE_NAME( proxy ),
+			( void * ) window, G_OBJECT_TYPE_NAME( window ));
 
 	if( GTK_IS_MENU_ITEM( proxy )){
 
@@ -674,6 +744,10 @@ on_proxy_connect( GtkActionGroup *action_group, GtkAction *action, GtkWidget *pr
 				G_OBJECT( proxy ),
 				"deselect",
 				G_CALLBACK( on_menu_item_deselected ));
+
+#ifndef GTK_HAS_ACTIVATABLE
+		g_object_set_data( G_OBJECT( proxy ), MENUBAR_PROP_ITEM_ACTION, action );
+#endif
 	}
 }
 
diff --git a/src/nact/nact-main-window.c b/src/nact/nact-main-window.c
index 79749df..ea55e59 100644
--- a/src/nact/nact-main-window.c
+++ b/src/nact/nact-main-window.c
@@ -548,19 +548,21 @@ nact_main_window_new( BaseApplication *application )
 gboolean
 nact_main_window_action_exists( const NactMainWindow *window, const gchar *uuid )
 {
-	/*GSList *ia;*/
-
-	g_assert( FALSE );
-	/* TODO: search in current state of the tree store + in pivot */
-	/*for( ia = window->private->actions ; ia ; ia = ia->next ){
-		NAObjectAction *action = NA_ACTION( ia->data );
-		gchar *action_uuid = na_object_action_get_uuid( action );
-		gboolean ok = ( g_ascii_strcasecmp( action_uuid, uuid ) == 0 );
-		g_free( action_uuid );
-		if( ok ){
-			return( TRUE );
-		}
-	}*/
+	NactApplication *application;
+	NAPivot *pivot;
+	NAObject *action;
+
+	application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
+	pivot = nact_application_get_pivot( application );
+	action = na_pivot_get_item( pivot, uuid );
+	if( action ){
+		return( TRUE );
+	}
+
+	action = nact_iactions_list_get_item( NACT_IACTIONS_LIST( window ), uuid );
+	if( action ){
+		return( TRUE );
+	}
 
 	return( FALSE );
 }
@@ -654,16 +656,24 @@ nact_main_window_remove_deleted( NactMainWindow *window )
 /*
  * from nact_main_window_remove_deleted:
  * Removes the deleted items from the underlying I/O storage subsystem.
+ *
+ * If the deleted item is a profile, then do nothing because the parent
+ * action has been marked as modified when the profile has been deleted,
+ * and thus updated in the storage subsystem as well as in the pivot
  */
 static void
 actually_delete_item( NactMainWindow *window, NAObject *item, NAPivot *pivot )
 {
 	GList *items, *it;
+	NAObject *origin;
+
+	g_debug( "nact_main_window_actually_delete_item: item=%p (%s)",
+			( void * ) item, G_OBJECT_TYPE_NAME( item ));
 
-	g_debug( "actually_delete_item %p", ( void * ) item );
-	if( nact_window_delete_item( NACT_WINDOW( window ), NA_OBJECT_ITEM( item ))){
+	if( NA_IS_OBJECT_ITEM( item )){
+		nact_window_delete_item( NACT_WINDOW( window ), NA_OBJECT_ITEM( item ));
 
-		NAObject *origin = na_object_get_origin( item );
+		origin = na_object_get_origin( item );
 		if( origin ){
 			na_pivot_remove_item( pivot, origin );
 		}
diff --git a/src/nact/nact-tree-model.c b/src/nact/nact-tree-model.c
index de492bc..3ab8e5a 100644
--- a/src/nact/nact-tree-model.c
+++ b/src/nact/nact-tree-model.c
@@ -499,14 +499,14 @@ nact_tree_model_dispose( NactTreeModel *model )
 void
 nact_tree_model_display( NactTreeModel *model, NAObject *object )
 {
-	static const gchar *thisfn = "nact_tree_model_display";
+	/*static const gchar *thisfn = "nact_tree_model_display";*/
 	GtkTreeStore *store;
 	GtkTreeIter iter;
 	GtkTreePath *path;
 
-	g_debug( "%s: model=%p (%s), object=%p (%s)", thisfn,
+	/*g_debug( "%s: model=%p (%s), object=%p (%s)", thisfn,
 			( void * ) model, G_OBJECT_TYPE_NAME( model ),
-			( void * ) object, G_OBJECT_TYPE_NAME( object ));
+			( void * ) object, G_OBJECT_TYPE_NAME( object ));*/
 
 	store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
 
@@ -792,7 +792,7 @@ insert_get_iters_menu( GtkTreeModel *model, const NAObject *select_object, GtkTr
 }
 
 /*
- * insert an action or a menu where there is no current selection
+ * 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
  */
@@ -801,10 +801,12 @@ insert_before_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const N
 {
 	GtkTreePath *path;
 	GtkTreeIter iter;
+	NAObject *sibling_obj;
 
 	g_debug( "nact_tree_model_insert_before_get_iters" );
 
 	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;
 
 	if( gtk_tree_path_get_depth( select_path ) > 1 ){
@@ -813,10 +815,12 @@ insert_before_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const N
 		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 );
+		na_object_insert_item( *parent_object, object, sibling_obj );
 		g_object_unref( *parent_object );
 		gtk_tree_path_free( path );
 	}
+
+	g_object_unref( sibling_obj );
 }
 
 /*
@@ -827,12 +831,14 @@ insert_before_parent_get_iters( GtkTreeModel *model, GtkTreePath *select_path, c
 {
 	GtkTreePath *path;
 	GtkTreeIter iter;
+	NAObject *sibling_obj;
 
 	g_debug( "nact_tree_model_insert_before_parent_get_iters" );
 
 	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;
 
 	if( gtk_tree_path_get_depth( path ) > 1 ){
@@ -840,10 +846,11 @@ insert_before_parent_get_iters( GtkTreeModel *model, GtkTreePath *select_path, c
 		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 );
+		na_object_insert_item( *parent_object, object, sibling_obj );
 		g_object_unref( *parent_object );
 	}
 
+	g_object_unref( sibling_obj );
 	gtk_tree_path_free( path );
 }
 
@@ -861,7 +868,7 @@ insert_as_last_child_get_iters( GtkTreeModel *model, GtkTreePath *select_path, c
 
 	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_insert_item( *parent_object, object );
+	na_object_append_item( *parent_object, object );
 	g_object_unref( *parent_object );
 }
 
@@ -980,6 +987,7 @@ dump_store( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmDumpSt
 	gint depth;
 	gint i;
 	GString *prefix;
+	gchar *id, *label;
 
 	depth = gtk_tree_path_get_depth( path );
 	prefix = g_string_new( ntm->prefix );
@@ -987,7 +995,12 @@ dump_store( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmDumpSt
 		g_string_append_printf( prefix, "  " );
 	}
 
-	g_debug( "%s: %s%s at %p", ntm->fname, prefix->str, G_OBJECT_TYPE_NAME( object ), ( void * ) object );
+	id = na_object_get_id( object );
+	label = na_object_get_label( object );
+	g_debug( "%s: %s%s at %p \"[%s] %s\"",
+			ntm->fname, prefix->str, G_OBJECT_TYPE_NAME( object ), ( void * ) object, id, label );
+	g_free( label );
+	g_free( id );
 
 	g_string_free( prefix, TRUE );
 
diff --git a/src/nact/nact-window.c b/src/nact/nact-window.c
index e43b0c7..393416c 100644
--- a/src/nact/nact-window.c
+++ b/src/nact/nact-window.c
@@ -202,7 +202,8 @@ nact_window_get_pivot( NactWindow *window )
  * An action is always written at once, with all its profiles.
  *
  * Writing a menu only involves writing its NAObjectItem properties,
- * along with the list and the order of its subitems.
+ * along with the list and the order of its subitems, but not the
+ * subitems themselves (because they may be unmodified)
  */
 gboolean
 nact_window_save_item( NactWindow *window, NAObjectItem *item )
@@ -212,12 +213,14 @@ nact_window_save_item( NactWindow *window, NAObjectItem *item )
 	gchar *msg = NULL;
 	guint ret;
 
-	g_debug( "%s: window=%p, item=%p", thisfn, ( void * ) window, ( void * ) item );
+	g_debug( "%s: window=%p (%s), item=%p (%s)", thisfn,
+			( void * ) window, G_OBJECT_TYPE_NAME( window ),
+			( void * ) item, G_OBJECT_TYPE_NAME( item ));
 
 	pivot = nact_window_get_pivot( window );
 	g_assert( NA_IS_PIVOT( pivot ));
 
-	na_object_dump( item );
+	na_object_dump_norec( item );
 
 	ret = na_pivot_write_item( pivot, NA_OBJECT( item ), &msg );
 
@@ -251,7 +254,7 @@ nact_window_delete_item( NactWindow *window, NAObjectItem *item )
 	pivot = nact_window_get_pivot( window );
 	g_assert( NA_IS_PIVOT( pivot ));
 
-	na_object_dump( item );
+	na_object_dump_norec( item );
 
 	ret = na_pivot_delete_item( pivot, NA_OBJECT( item ), &msg );
 
diff --git a/src/nact/nautilus-actions-config-tool.ui b/src/nact/nautilus-actions-config-tool.ui
index bea6bb8..f065a22 100644
--- a/src/nact/nautilus-actions-config-tool.ui
+++ b/src/nact/nautilus-actions-config-tool.ui
@@ -1426,7 +1426,6 @@ Defining several profiles lets you have several commands, each applying with a d
     <child internal-child="vbox">
       <object class="GtkVBox" id="dialog-vbox1">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
           <object class="GtkFrame" id="frame1">
@@ -1441,7 +1440,6 @@ Defining several profiles lets you have several commands, each applying with a d
                   <object class="GtkVBox" id="vbox17">
                     <property name="visible">True</property>
                     <property name="border_width">10</property>
-                    <property name="orientation">vertical</property>
                     <property name="spacing">10</property>
                     <child>
                       <object class="GtkCheckButton" id="SortAlphabeticalButton">



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