[nautilus-actions] Define an import mode in the Import assistant



commit aa31f46cd114ad8d09001abd95f77b80a942cdad
Author: Pierre Wieser <pwieser trychlos org>
Date:   Fri Sep 25 02:59:17 2009 +0200

    Define an import mode in the Import assistant
    
    Let the user decide what to do if UUID of imported actions already exist.

 ChangeLog                                |   21 +++
 data/nautilus-actions.schemas.in         |   14 ++
 src/nact/nact-assistant-export.ui        |    6 +-
 src/nact/nact-assistant-import.c         |  234 +++++++++++++++++++++++-------
 src/nact/nact-tree-model.c               |   78 ++++++++++-
 src/nact/nact-xml-reader.c               |  115 +++++++++++++--
 src/nact/nact-xml-reader.h               |   10 +-
 src/nact/nautilus-actions-config-tool.ui |  199 ++++++++++++++++++++++++-
 8 files changed, 595 insertions(+), 82 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index c63c0a9..b937c65 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2009-09-25 Pierre Wieser <pwieser trychlos org>
+
+	* data/nautilus-actions.schemas.in:
+	Defines a new key for the "import-mode" preference.
+
+	* src/nact/nact-assistant-export.ui:
+	All titles are verbs.
+
+	* src/nact/nact-assistant-import.c:
+	Let the user define an import mode if the UUID already exists.
+
+	* src/nact/nact-tree-model.c:
+	Override the object when inserting an already existing UUID.
+
+	* src/nact/nact-xml-reader.c:
+	* src/nact/nact-xml-reader.h:
+	Manage the import mode, renumbering the imported action if needed.
+
+	* src/nact/nautilus-actions-config-tool.ui:
+	Add a page to the Import assistant to manage the import mode.
+
 2009-09-24 Pierre Wieser <pwieser trychlos org>
 
 	* src/nact/nact-main-menubar.c:
diff --git a/data/nautilus-actions.schemas.in b/data/nautilus-actions.schemas.in
index ccc1995..c5890ea 100644
--- a/data/nautilus-actions.schemas.in
+++ b/data/nautilus-actions.schemas.in
@@ -298,6 +298,20 @@ All schemes used by Nautilus can be used here.</long>
     </schema>
 
     <schema>
+      <key>/schemas/apps/nautilus-actions/preferences/import-mode</key>
+      <owner>nautilus-actions</owner>
+      <type>int</type>
+      <locale name="C">
+        <short>Import mode</short>
+        <long>Last import mode choosen in the Import assistant. Possible values are:
+1: do not import an action whose UUID already exists
+2: allocate a new UUID if the imported UUID already exists
+3: override the existing action with the imported one.</long>
+      </locale>
+      <default>/tmp</default>
+    </schema>
+
+    <schema>
       <key>/schemas/apps/nautilus-actions/preferences/iprefs-add-about-item</key>
       <owner>nautilus-actions</owner>
       <type>bool</type>
diff --git a/src/nact/nact-assistant-export.ui b/src/nact/nact-assistant-export.ui
index 92a758f..cc47eaa 100644
--- a/src/nact/nact-assistant-export.ui
+++ b/src/nact/nact-assistant-export.ui
@@ -76,7 +76,7 @@ to extend a selection.</property>
         </child>
       </object>
       <packing>
-        <property name="title">Selection of the exported actions</property>
+        <property name="title">Selecting the exported actions</property>
       </packing>
     </child>
     <child>
@@ -96,7 +96,7 @@ to extend a selection.</property>
         </child>
       </object>
       <packing>
-        <property name="title">Selection of the target folder</property>
+        <property name="title">Selecting the target folder</property>
       </packing>
     </child>
     <child>
@@ -283,7 +283,7 @@ The exported file may later be imported via :
         </child>
       </object>
       <packing>
-        <property name="title">Select the export format</property>
+        <property name="title">Selecting the export format</property>
       </packing>
     </child>
     <child>
diff --git a/src/nact/nact-assistant-import.c b/src/nact/nact-assistant-import.c
index b2efe75..b0fa595 100644
--- a/src/nact/nact-assistant-import.c
+++ b/src/nact/nact-assistant-import.c
@@ -50,9 +50,10 @@
 /* Import Assistant
  *
  * pos.  type     title
- * ---   -------  ------------------------------------
+ * ---   -------  --------------------------------------------------
  *   0   Intro    Introduction
  *   1   Content  Selection of the files
+ *   2   Content  Duplicate management: what to do with duplicates ?
  *   2   Confirm  Display the selected files before import
  *   3   Summary  Import is done: summary of the done operations
  */
@@ -60,6 +61,7 @@
 enum {
 	ASSIST_PAGE_INTRO = 0,
 	ASSIST_PAGE_FILES_SELECTION,
+	ASSIST_PAGE_DUPLICATES,
 	ASSIST_PAGE_CONFIRM,
 	ASSIST_PAGE_DONE
 };
@@ -86,6 +88,7 @@ struct NactAssistantImportPrivate {
 };
 
 #define IPREFS_IMPORT_ACTIONS_FOLDER_URI		"import-folder-uri"
+#define IPREFS_IMPORT_ACTIONS_IMPORT_MODE		"import-mode"
 
 static BaseAssistantClass *st_parent_class = NULL;
 
@@ -105,11 +108,14 @@ static void     runtime_init_intro( NactAssistantImport *window, GtkAssistant *a
 static void     runtime_init_file_selector( NactAssistantImport *window, GtkAssistant *assistant );
 static void     on_file_selection_changed( GtkFileChooser *chooser, gpointer user_data );
 static gboolean has_readable_files( GSList *uris );
+static void     runtime_init_duplicates( NactAssistantImport *window, GtkAssistant *assistant );
+static void     set_import_mode( NactAssistantImport *window, gint mode );
 
-static void     assistant_apply( BaseAssistant *window, GtkAssistant *assistant );
 static void     assistant_prepare( BaseAssistant *window, GtkAssistant *assistant, GtkWidget *page );
-
 static void     prepare_confirm( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page );
+static gint     get_import_mode( NactAssistantImport *window );
+static gchar   *add_import_mode( NactAssistantImport *window, const gchar *text );
+static void     assistant_apply( BaseAssistant *window, GtkAssistant *assistant );
 static void     prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page );
 static void     free_results( GSList *list );
 
@@ -288,40 +294,49 @@ on_runtime_init_dialog( NactAssistantImport *dialog, gpointer user_data )
 	GtkAssistant *assistant;
 
 	g_debug( "%s: dialog=%p, user_data=%p", thisfn, ( void * ) dialog, ( void * ) user_data );
-	g_assert( NACT_IS_ASSISTANT_IMPORT( dialog ));
+	g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( dialog ));
+
+	if( !dialog->private->dispose_has_run ){
 
-	assistant = GTK_ASSISTANT( base_window_get_toplevel_window( BASE_WINDOW( dialog )));
+		base_assistant_set_cancel_on_esc( BASE_ASSISTANT( dialog ), TRUE );
+		base_assistant_set_warn_on_esc( BASE_ASSISTANT( dialog ), TRUE );
 
-	runtime_init_intro( dialog, assistant );
-	runtime_init_file_selector( dialog, assistant );
+		assistant = GTK_ASSISTANT( base_window_get_toplevel_window( BASE_WINDOW( dialog )));
+
+		runtime_init_intro( dialog, assistant );
+		runtime_init_file_selector( dialog, assistant );
+		runtime_init_duplicates( dialog, assistant );
+	}
 }
 
 static void
 runtime_init_intro( NactAssistantImport *window, GtkAssistant *assistant )
 {
 	static const gchar *thisfn = "nact_assistant_import_runtime_init_intro";
-	GtkWidget *content;
+	GtkWidget *page;
 
-	content = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_INTRO );
+	page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_INTRO );
 
-	g_debug( "%s: window=%p, assistant=%p, content=%p",
-			thisfn, ( void * ) window, ( void * ) assistant, ( void * ) content );
+	g_debug( "%s: window=%p, assistant=%p, page=%p",
+			thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
 
-	gtk_assistant_set_page_complete( assistant, content, TRUE );
+	gtk_assistant_set_page_complete( assistant, page, TRUE );
 }
 
 static void
 runtime_init_file_selector( NactAssistantImport *window, GtkAssistant *assistant )
 {
 	static const gchar *thisfn = "nact_assistant_import_runtime_init_file_selector";
+	GtkWidget *page;
 	GtkWidget *chooser;
 	gchar *uri;
 
-	chooser = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
+	page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
 
-	g_debug( "%s: window=%p, assistant=%p, chooser=%p",
-			thisfn, ( void * ) window, ( void * ) assistant, ( void * ) chooser );
+	g_debug( "%s: window=%p, assistant=%p, page=%p",
+			thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
 
+	chooser = base_window_get_widget( BASE_WINDOW( window ), "ImportFileChooser" );
 	uri = base_iprefs_get_string( BASE_WINDOW( window ), IPREFS_IMPORT_ACTIONS_FOLDER_URI );
 	if( uri && strlen( uri )){
 		gtk_file_chooser_set_current_folder_uri( GTK_FILE_CHOOSER( chooser ), uri );
@@ -334,7 +349,7 @@ runtime_init_file_selector( NactAssistantImport *window, GtkAssistant *assistant
 			"selection-changed",
 			G_CALLBACK( on_file_selection_changed ));
 
-	gtk_assistant_set_page_complete( assistant, chooser, FALSE );
+	gtk_assistant_set_page_complete( assistant, page, FALSE );
 }
 
 static void
@@ -426,45 +441,42 @@ has_readable_files( GSList *uris )
 }
 
 static void
-assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
+runtime_init_duplicates( NactAssistantImport *window, GtkAssistant *assistant )
 {
-	static const gchar *thisfn = "nact_assistant_import_assistant_apply";
-	NactAssistantImport *window;
-	GtkWidget *chooser;
-	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 ));
-	window = NACT_ASSISTANT_IMPORT( wnd );
+	static const gchar *thisfn = "nact_assistant_import_runtime_init_duplicates";
+	GtkWidget *page;
+	gint mode;
 
-	chooser = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
-	uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( chooser ));
+	g_debug( "%s: window=%p", thisfn, ( void * ) window );
 
-	g_object_get( G_OBJECT( wnd ), BASE_WINDOW_PROP_PARENT, &mainwnd, NULL );
+	mode = base_iprefs_get_int( BASE_WINDOW( window ), IPREFS_IMPORT_ACTIONS_IMPORT_MODE );
+	set_import_mode( window, mode );
 
-	for( is = uris ; is ; is = is->next ){
+	page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_DUPLICATES );
+	gtk_assistant_set_page_complete( assistant, page, TRUE );
+}
 
-		msg = NULL;
-		action = nact_xml_reader_import( BASE_WINDOW( window ), ( const gchar * ) is->data, &msg );
+static void
+set_import_mode( NactAssistantImport *window, gint mode )
+{
+	GtkToggleButton *button;
 
-		str = g_new0( ImportUriStruct, 1 );
-		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 );
+	switch( mode ){
+		case NO_IMPORT_MODE:
+			button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "NoImportButton" ));
+			gtk_toggle_button_set_active( button, TRUE );
+			break;
 
-		window->private->results = g_slist_prepend( window->private->results, str );
+		case RENUMBER_MODE:
+			button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "RenumberButton" ));
+			gtk_toggle_button_set_active( button, TRUE );
+			break;
 
-		items = g_list_prepend( NULL, action );
-		nact_iactions_list_insert_items( NACT_IACTIONS_LIST( mainwnd ), items, NULL );
-		na_object_free_items( items );
+		case OVERRIDE_MODE:
+			button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "OverrideButton" ));
+			gtk_toggle_button_set_active( button, TRUE );
+			break;
 	}
-
-	na_utils_free_string_list( uris );
 }
 
 static void
@@ -509,7 +521,7 @@ prepare_confirm( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget
 	g_free( text );
 	text = tmp;
 
-	chooser = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
+	chooser = base_window_get_widget( BASE_WINDOW( window ), "ImportFileChooser" );
 	uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( chooser ));
 
 	for( is = uris ; is ; is = is->next ){
@@ -518,12 +530,128 @@ prepare_confirm( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget
 		text = tmp;
 	}
 
+	tmp = add_import_mode( window, text );
+	g_free( text );
+	text = tmp;
+
 	gtk_label_set_markup( GTK_LABEL( page ), text );
 	g_free( text );
 
 	gtk_assistant_set_page_complete( assistant, page, TRUE );
 }
 
+static gint
+get_import_mode( NactAssistantImport *window )
+{
+	GtkToggleButton *no_import_button;
+	GtkToggleButton *renumber_button;
+	GtkToggleButton *override_button;
+	gint mode;
+
+	no_import_button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "NoImportButton" ));
+	renumber_button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "RenumberButton" ));
+	override_button = GTK_TOGGLE_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "OverrideButton" ));
+
+	if( gtk_toggle_button_get_active( no_import_button )){
+		mode = NO_IMPORT_MODE;
+	} else if( gtk_toggle_button_get_active( renumber_button )){
+		mode = RENUMBER_MODE;
+	} else {
+		g_return_val_if_fail( gtk_toggle_button_get_active( override_button ), 0 );
+		mode = OVERRIDE_MODE;
+	}
+
+	return( mode );
+}
+
+static gchar *
+add_import_mode( NactAssistantImport *window, const gchar *text )
+{
+	gint mode;
+	gchar *label1, *label2;
+	gchar *result;
+
+	mode = get_import_mode( window );
+	label1 = NULL;
+	label2 = NULL;
+	result = NULL;
+
+	switch( mode ){
+		case NO_IMPORT_MODE:
+			label1 = g_strdup( gtk_button_get_label( GTK_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "NoImportButton" ))));
+			label2 = g_strdup( gtk_label_get_text( GTK_LABEL( base_window_get_widget( BASE_WINDOW( window ), "NoImportLabel"))));
+			break;
+
+		case RENUMBER_MODE:
+			label1 = g_strdup( gtk_button_get_label( GTK_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "RenumberButton" ))));
+			label2 = g_strdup( gtk_label_get_text( GTK_LABEL( base_window_get_widget( BASE_WINDOW( window ), "RenumberLabel"))));
+			break;
+
+		case OVERRIDE_MODE:
+			label1 = g_strdup( gtk_button_get_label( GTK_BUTTON( base_window_get_widget( BASE_WINDOW( window ), "OverrideButton" ))));
+			label2 = g_strdup( gtk_label_get_text( GTK_LABEL( base_window_get_widget( BASE_WINDOW( window ), "OverrideLabel"))));
+			break;
+
+		default:
+			break;
+	}
+
+	if( label1 ){
+		result = g_strdup_printf( "%s\n\n<b>%s</b>\n\n%s", text, label1, label2 );
+		g_free( label2 );
+		g_free( label1 );
+	}
+
+	return( result );
+}
+
+/*
+ * do import here
+ */
+static void
+assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
+{
+	static const gchar *thisfn = "nact_assistant_import_assistant_apply";
+	NactAssistantImport *window;
+	GtkWidget *chooser;
+	GSList *uris, *is, *msg;
+	NAObjectAction *action;
+	ImportUriStruct *str;
+	GList *items;
+	BaseWindow *mainwnd;
+	gint mode;
+
+	g_debug( "%s: window=%p, assistant=%p", thisfn, ( void * ) wnd, ( void * ) assistant );
+	g_assert( NACT_IS_ASSISTANT_IMPORT( wnd ));
+	window = NACT_ASSISTANT_IMPORT( wnd );
+
+	chooser = base_window_get_widget( BASE_WINDOW( window ), "ImportFileChooser" );
+	uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( chooser ));
+	mode = get_import_mode( window );
+
+	g_object_get( G_OBJECT( wnd ), BASE_WINDOW_PROP_PARENT, &mainwnd, NULL );
+
+	for( is = uris ; is ; is = is->next ){
+
+		msg = NULL;
+		action = nact_xml_reader_import( BASE_WINDOW( window ), ( const gchar * ) is->data, mode, &msg );
+
+		str = g_new0( ImportUriStruct, 1 );
+		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 );
+
+		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 );
+}
+
 static void
 prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page )
 {
@@ -533,6 +661,7 @@ prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWid
 	GSList *is, *im;
 	ImportUriStruct *str;
 	GFile *file;
+	gint mode;
 
 	g_debug( "%s: window=%p, assistant=%p, page=%p",
 			thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
@@ -551,7 +680,7 @@ prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWid
 		file = g_file_new_for_uri( str->uri );
 		bname = g_file_get_basename( file );
 		g_object_unref( file );
-		tmp = g_strdup_printf( "%s\t%s\n\n", text, bname );
+		tmp = g_strdup_printf( "%s\t%s\n", text, bname );
 		g_free( text );
 		text = tmp;
 		g_free( bname );
@@ -563,26 +692,28 @@ prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWid
 			text2 = g_strdup_printf( _( "UUID: %s\t%s" ), uuid, label);
 			g_free( label );
 			g_free( uuid );
-			tmp = g_strdup_printf( "%s\t\t%s\n\n", text, text2 );
+			tmp = g_strdup_printf( "%s\t\t%s\n", text, text2 );
 
 			window->private->actions = g_slist_prepend( window->private->actions, str->action );
 
 		} else {
 			/* i18n: just indicate that the import of this file was unsuccessfull */
 			text2 = g_strdup( _( "NOT OK" ));
-			tmp = g_strdup_printf( "%s\t\t %s\n\n", text, text2 );
+			tmp = g_strdup_printf( "%s\t\t %s\n", text, text2 );
 			g_free( text2 );
 		}
 
 		g_free( text );
 		text = tmp;
 
+		/* add messages if any */
 		for( im = str->msg ; im ; im = im->next ){
 			tmp = g_strdup_printf( "%s\t\t%s\n", text, ( const char * ) im->data );
 			g_free( text );
 			text = tmp;
 		}
 
+		/* add a blank line between two actions */
 		tmp = g_strdup_printf( "%s\n", text );
 		g_free( text );
 		text = tmp;
@@ -591,6 +722,9 @@ prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWid
 	gtk_label_set_markup( GTK_LABEL( page ), text );
 	g_free( text );
 
+	mode = get_import_mode( window );
+	base_iprefs_set_int( BASE_WINDOW( window ), IPREFS_IMPORT_ACTIONS_IMPORT_MODE, mode );
+
 	gtk_assistant_set_page_complete( assistant, page, TRUE );
 	base_assistant_set_warn_on_cancel( BASE_ASSISTANT( window ), FALSE );
 }
diff --git a/src/nact/nact-tree-model.c b/src/nact/nact-tree-model.c
index 33cd5cd..5f732b4 100644
--- a/src/nact/nact-tree-model.c
+++ b/src/nact/nact-tree-model.c
@@ -133,6 +133,14 @@ typedef struct {
 }
 	ntmSearchStruct;
 
+typedef struct {
+	GtkTreeModel *store;
+	gchar        *id;
+	gboolean      found;
+	GtkTreeIter  *iter;
+}
+	ntmSearchIdStruct;
+
 static GtkTreeModelFilterClass *st_parent_class = NULL;
 
 static GType          register_type( void );
@@ -153,6 +161,7 @@ static void           insert_get_iters_menu( GtkTreeModel *model, const NAObject
 static void           insert_before_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
 static void           insert_before_parent_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
 static void           insert_as_last_child_get_iters( GtkTreeModel *model,  GtkTreePath *select_path, const NAObject *object, GtkTreeIter *parent_iter, gboolean *has_parent_iter, GtkTreeIter *sibling_iter, gboolean *has_sibling_iter, NAObject **parent_object );
+static void           remove_if_exists( NactTreeModel *model, GtkTreeModel *store, const NAObject *object );
 
 static GList         *add_parent( GList *parents, GtkTreeModel *store, GtkTreeIter *obj_iter, GtkTreePath *obj_path );
 static void           append_item( GtkTreeStore *model, GtkTreeView *treeview, GtkTreeIter *parent, GtkTreeIter *iter, const NAObject *object );
@@ -162,6 +171,8 @@ static void           iter_on_store( NactTreeModel *model, GtkTreeModel *store,
 static gboolean       iter_on_store_item( NactTreeModel *model, GtkTreeModel *store, GtkTreeIter *iter, FnIterOnStore fn, gpointer user_data );
 static gboolean       search_for_object( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *iter );
 static gboolean       search_for_objet_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchStruct *ntm );
+static gboolean       search_for_object_id( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *iter );
+static gboolean       search_for_object_id_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchIdStruct *ntm );
 
 static gboolean       imulti_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *path_list );
 static gboolean       imulti_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source, GdkDragContext *context, GtkSelectionData *selection_data, GList *path_list, guint info );
@@ -707,6 +718,8 @@ nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePat
 		has_sibling_iter = FALSE;
 		*obj_parent = NA_OBJECT( object );
 
+		remove_if_exists( model, store, object );
+
 		if( path ){
 			gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &select_iter, path );
 			gtk_tree_model_get( GTK_TREE_MODEL( model ), &select_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &select_object, -1 );
@@ -715,15 +728,15 @@ nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePat
 			g_return_val_if_fail( NA_IS_OBJECT( select_object ), NULL );
 
 			if( NA_IS_OBJECT_ACTION( object )){
-				insert_get_iters_action( GTK_TREE_MODEL( store ), select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
+				insert_get_iters_action( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
 			}
 
 			if( NA_IS_OBJECT_PROFILE( object )){
-				insert_get_iters_profile( GTK_TREE_MODEL( store ), select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
+				insert_get_iters_profile( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
 			}
 
 			if( NA_IS_OBJECT_MENU( object )){
-				insert_get_iters_menu( GTK_TREE_MODEL( store ), select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
+				insert_get_iters_menu( store, select_object, path, object, &parent_iter, &has_parent_iter, &sibling_iter, &has_sibling_iter, obj_parent );
 			}
 
 			g_object_unref( select_object );
@@ -891,6 +904,21 @@ insert_as_last_child_get_iters( GtkTreeModel *model, GtkTreePath *select_path, c
 	g_object_unref( *parent_object );
 }
 
+/*
+ * if the object, identified by its uuid, already exists, then remove it first
+ */
+static void
+remove_if_exists( NactTreeModel *model, GtkTreeModel *store, const NAObject *object )
+{
+	GtkTreeIter iter;
+
+	if( NA_IS_OBJECT_ITEM( object )){
+		if( search_for_object_id( model, store, object, &iter )){
+			gtk_tree_store_remove( GTK_TREE_STORE( store ), &iter );
+		}
+	}
+}
+
 void
 nact_tree_model_iter( NactTreeModel *model, FnIterOnStore fn, gpointer user_data )
 {
@@ -1107,6 +1135,50 @@ search_for_objet_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object
 	return( ntm->found );
 }
 
+static gboolean
+search_for_object_id( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *result_iter )
+{
+	gboolean found = FALSE;
+	ntmSearchIdStruct *ntm;
+	GtkTreeIter iter;
+
+	ntm = g_new0( ntmSearchIdStruct, 1 );
+	ntm->store = store;
+	ntm->id = na_object_get_id( object );
+	ntm->found = FALSE;
+	ntm->iter = &iter;
+
+	iter_on_store( model, store, NULL, ( FnIterOnStore ) search_for_object_id_iter, ntm );
+
+	if( ntm->found ){
+		found = TRUE;
+		memcpy( result_iter, ntm->iter, sizeof( GtkTreeIter ));
+	}
+
+	g_free( ntm->id );
+	g_free( ntm );
+	return( found );
+}
+
+static gboolean
+search_for_object_id_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchIdStruct *ntm )
+{
+	gchar *id;
+
+	id = na_object_get_id( object );
+
+	if( !g_ascii_strcasecmp( id, ntm->id )){
+		if( gtk_tree_model_get_iter( ntm->store, ntm->iter, path )){
+			ntm->found = TRUE;
+		}
+	}
+
+	g_free( id );
+
+	/* stop iteration when found */
+	return( ntm->found );
+}
+
 /*
  * all rows are draggable
  */
diff --git a/src/nact/nact-xml-reader.c b/src/nact/nact-xml-reader.c
index fcdd7cd..939d367 100644
--- a/src/nact/nact-xml-reader.c
+++ b/src/nact/nact-xml-reader.c
@@ -62,9 +62,11 @@ struct NactXMLReaderClassPrivate {
 struct NactXMLReaderPrivate {
 	gboolean         dispose_has_run;
 	BaseWindow      *window;
+	gint             import_mode;
 	NAObjectAction  *action;			/* the action that we will return, or NULL */
 	GSList          *messages;
 	gboolean         uuid_set;			/* set at first uuid, then checked against */
+	gboolean         relabel;			/* renumbered action: set a 'renumbered' label */
 
 	/* following values are reset at each schema/entry node
 	 */
@@ -156,7 +158,8 @@ static gchar   *get_uuid_from_key( NactXMLReader *reader, const gchar *key, guin
 static gboolean is_uuid_valid( const gchar *uuid );
 static gchar   *get_entry_from_key( const gchar *key );
 static void     free_reader_values( NactXMLReader *reader );
-static gboolean action_exists( NactXMLReader *reader, const gchar *uuid );
+static gboolean manage_import_mode( NactXMLReader *reader );
+static void     relabel( NactXMLReader *reader );
 
 GType
 nact_xml_reader_get_type( void )
@@ -228,6 +231,7 @@ instance_init( GTypeInstance *instance, gpointer klass )
 	self->private->action = NULL;
 	self->private->messages = NULL;
 	self->private->uuid_set = FALSE;
+	self->private->relabel = FALSE;
 	self->private->profile = NULL;
 	self->private->locale_waited = FALSE;
 	self->private->entry = NULL;
@@ -288,10 +292,18 @@ gconf_reader_new( void )
 }
 
 /**
+ * nact_xml_reader_import:
+ * window: the #NactAssistantImport instance.
+ * @uri: the uri of the file to import.
+ * import_mode: the import mode.
+ * msg: a list of error messages which may be set by this function.
+ *
  * Import the specified file as an NAAction XML description.
+ *
+ * Returns: the imported action, or NULL.
  */
 NAObjectAction *
-nact_xml_reader_import( BaseWindow *window, const gchar *uri, GSList **msg )
+nact_xml_reader_import( BaseWindow *window, const gchar *uri, gint import_mode, GSList **msg )
 {
 	static const gchar *thisfn = "nact_xml_reader_import";
 	NAObjectAction *action = NULL;
@@ -303,8 +315,9 @@ nact_xml_reader_import( BaseWindow *window, const gchar *uri, GSList **msg )
 
 	reader = gconf_reader_new();
 	reader->private->window = window;
+	reader->private->import_mode = import_mode;
 
-	g_assert( BASE_IS_ASSISTANT( window ));
+	g_return_val_if_fail( BASE_IS_ASSISTANT( window ), NULL );
 
 	doc = xmlParseFile( uri );
 
@@ -426,6 +439,10 @@ gconf_reader_parse_schemalist( NactXMLReader *reader, xmlNode *schema )
 		g_free( label );
 	}
 
+	if( ok && reader->private->relabel ){
+		relabel( reader );
+	}
+
 	if( !ok ){
 		g_object_unref( reader->private->action );
 		reader->private->action = NULL;
@@ -614,13 +631,13 @@ gconf_reader_parse_applyto( NactXMLReader *reader, xmlNode *node )
 	if( ret ){
 		if( !reader->private->uuid_set ){
 
-			if( action_exists( reader, uuid )){
-				add_message( reader, ERR_UUID_ALREADY_EXISTS, uuid );
-				ret = FALSE;
+			na_object_set_id( reader->private->action, uuid );
 
-			} else {
-				na_object_set_id( reader->private->action, uuid );
+			if( manage_import_mode( reader )){
 				reader->private->uuid_set = TRUE;
+			} else {
+				add_message( reader, ERR_UUID_ALREADY_EXISTS, uuid, node->line );
+				ret = FALSE;
 			}
 
 		} else {
@@ -836,6 +853,7 @@ gconf_reader_parse_entrylist( NactXMLReader *reader, xmlNode *entrylist )
 	static const gchar *thisfn = "gconf_reader_parse_entrylist";
 	xmlChar *path;
 	gchar *uuid, *label;
+	gboolean ok;
 
 	g_debug( "%s: reader=%p, entrylist=%p", thisfn, ( void * ) reader, ( void * ) entrylist );
 
@@ -845,19 +863,22 @@ gconf_reader_parse_entrylist( NactXMLReader *reader, xmlNode *entrylist )
 	/*g_debug( "%s: uuid=%s", thisfn, uuid );*/
 
 	if( is_uuid_valid( uuid )){
-		if( action_exists( reader, uuid )){
-			add_message( reader, ERR_UUID_ALREADY_EXISTS, uuid, entrylist->line );
 
-		} else {
-			na_object_set_id( reader->private->action, uuid );
+		na_object_set_id( reader->private->action, uuid );
+
+		if( manage_import_mode( reader )){
 			reader->private->uuid_set = TRUE;
+		} else {
+			add_message( reader, ERR_UUID_ALREADY_EXISTS, uuid, entrylist->line );
 		}
+
 	} else {
 		add_message( reader, ERR_NOT_AN_UUID, uuid, entrylist->line );
 	}
 
 	g_free( uuid );
 	xmlFree( path );
+	ok = TRUE;
 
 	if( reader->private->uuid_set ){
 		xmlNode *iter;
@@ -882,14 +903,20 @@ gconf_reader_parse_entrylist( NactXMLReader *reader, xmlNode *entrylist )
 		label = na_object_get_label( reader->private->action );
 		if( !label || !g_utf8_strlen( label, -1 )){
 			add_message( reader, ERR_ACTION_LABEL_NOT_FOUND );
+			ok = FALSE;
 		}
 		g_free( label );
 
+		if( ok && reader->private->relabel ){
+			relabel( reader );
+		}
+
 	} else {
 		g_object_unref( reader->private->action );
 		reader->private->action = NULL;
 	}
 }
+
 static gboolean
 gconf_reader_parse_entry( NactXMLReader *reader, xmlNode *entry )
 {
@@ -1245,10 +1272,66 @@ free_reader_values( NactXMLReader *reader )
 	}
 }
 
+/*
+ * returns TRUE if we can safely insert the action
+ * - the uuid doesn't already exist
+ * - the uuid already exist, but import mode is renumber
+ * - the uuid already exists, but import mode is override
+ */
 static gboolean
-action_exists( NactXMLReader *reader, const gchar *uuid )
+manage_import_mode( NactXMLReader *reader )
+{
+	gboolean exists;
+	gchar *uuid;
+	BaseApplication *appli;
+	NactMainWindow *main_window;
+	gboolean ret;
+
+	appli = base_window_get_application( BASE_WINDOW( reader->private->window ));
+	main_window = NACT_MAIN_WINDOW( base_application_get_main_window( appli ));
+
+	uuid = na_object_get_id( reader->private->action );
+	exists = nact_main_window_action_exists( main_window, uuid );
+	g_free( uuid );
+
+	if( !exists ){
+		return( TRUE );
+	}
+
+	switch( reader->private->import_mode ){
+		case RENUMBER_MODE:
+			na_object_set_new_id( reader->private->action );
+			reader->private->relabel = TRUE;
+			ret = TRUE;
+			break;
+
+		case OVERRIDE_MODE:
+			ret = TRUE;
+			break;
+
+		case NO_IMPORT_MODE:
+		default:
+			ret = FALSE;
+	}
+
+	return( ret );
+}
+
+/*
+ * set a new label because the action has been renumbered
+ */
+static void
+relabel( NactXMLReader *reader )
 {
-	BaseApplication *appli = base_window_get_application( BASE_WINDOW( reader->private->window ));
-	NactMainWindow *main_window = NACT_MAIN_WINDOW( base_application_get_main_window( appli ));
-	return( nact_main_window_action_exists( main_window, uuid ));
+	gchar *label, *tmp;
+
+	label = na_object_get_label( reader->private->action );
+
+	/* i18n: the action has been renumbered during import operation */
+	tmp = g_strdup_printf( "%s %s", label, _( "(renumbered)" ));
+
+	na_object_set_label( reader->private->action, tmp );
+
+	g_free( tmp );
+	g_free( label );
 }
diff --git a/src/nact/nact-xml-reader.h b/src/nact/nact-xml-reader.h
index a6926cc..159e3f7 100644
--- a/src/nact/nact-xml-reader.h
+++ b/src/nact/nact-xml-reader.h
@@ -67,9 +67,17 @@ typedef struct {
 }
 	NactXMLReaderClass;
 
+/* import mode of an existing uuid
+ */
+enum {
+	NO_IMPORT_MODE = 1,
+	RENUMBER_MODE,
+	OVERRIDE_MODE
+};
+
 GType           nact_xml_reader_get_type( void );
 
-NAObjectAction *nact_xml_reader_import( BaseWindow *window, const gchar *uri, GSList **msg );
+NAObjectAction *nact_xml_reader_import( BaseWindow *window, const gchar *uri, gint mode, GSList **msg );
 
 G_END_DECLS
 
diff --git a/src/nact/nautilus-actions-config-tool.ui b/src/nact/nautilus-actions-config-tool.ui
index f065a22..0918be4 100644
--- a/src/nact/nautilus-actions-config-tool.ui
+++ b/src/nact/nautilus-actions-config-tool.ui
@@ -1009,13 +1009,192 @@ Defining several profiles lets you have several commands, each applying with a d
       </packing>
     </child>
     <child>
-      <object class="GtkFileChooserWidget" id="filechooserwidget1">
+      <object class="GtkVBox" id="vbox18">
         <property name="visible">True</property>
-        <property name="use_preview_label">False</property>
-        <property name="local_only">False</property>
-        <property name="select_multiple">True</property>
-        <property name="preview_widget_active">False</property>
+        <child>
+          <object class="GtkFileChooserWidget" id="ImportFileChooser">
+            <property name="visible">True</property>
+            <property name="preview_widget_active">False</property>
+            <property name="local_only">False</property>
+            <property name="select_multiple">True</property>
+            <property name="use_preview_label">False</property>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="title">Selecting what files to import</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="vbox19">
+        <property name="visible">True</property>
+        <property name="border_width">10</property>
+        <property name="spacing">10</property>
+        <child>
+          <object class="GtkVBox" id="vbox20">
+            <property name="visible">True</property>
+            <property name="spacing">10</property>
+            <child>
+              <object class="GtkLabel" id="label38">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">What should I do when importing an action whose UUID already exists ?</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox21">
+                <property name="visible">True</property>
+                <property name="spacing">10</property>
+                <child>
+                  <object class="GtkVBox" id="vbox22">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkRadioButton" id="NoImportButton">
+                        <property name="label" translatable="yes">Do not import the action which UUID already exists</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox5">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkLabel" id="NoImportLabel">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">14</property>
+                            <property name="label" translatable="yes">This used to be the historical behavior.
+The selected file will be marked as "NOT OK" in the Summary page.
+The existing action will not be modified.</property>
+                          </object>
+                          <packing>
+                            <property name="padding">4</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox23">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkRadioButton" id="RenumberButton">
+                        <property name="label" translatable="yes">Allocate a new identifiant for the imported action</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">NoImportButton</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox6">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkLabel" id="RenumberLabel">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">14</property>
+                            <property name="label" translatable="yes">The selected file will be imported with a slightly modified label indicating the renumbering.
+The existing action will not be modified.</property>
+                            <property name="wrap">True</property>
+                          </object>
+                          <packing>
+                            <property name="padding">4</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox24">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkRadioButton" id="OverrideButton">
+                        <property name="label" translatable="yes">Override the existing action</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">NoImportButton</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox7">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkLabel" id="OverrideLabel">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">14</property>
+                            <property name="label" translatable="yes">The action found in the selected file will silently override the current action which has the same identifiant.
+Be warned: you will not be prompted another time. This mode may be dangerous.</property>
+                            <property name="wrap">True</property>
+                          </object>
+                          <packing>
+                            <property name="padding">4</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
       </object>
+      <packing>
+        <property name="title">Managing duplicates</property>
+      </packing>
     </child>
     <child>
       <object class="GtkLabel" id="label17">
@@ -1023,6 +1202,7 @@ Defining several profiles lets you have several commands, each applying with a d
       </object>
       <packing>
         <property name="page_type">confirm</property>
+        <property name="title">Summary</property>
       </packing>
     </child>
     <child>
@@ -1031,6 +1211,7 @@ Defining several profiles lets you have several commands, each applying with a d
       </object>
       <packing>
         <property name="page_type">summary</property>
+        <property name="title">Import is done</property>
       </packing>
     </child>
   </object>
@@ -1534,16 +1715,16 @@ Defining several profiles lets you have several commands, each applying with a d
   </object>
   <object class="GtkSizeGroup" id="CommandLabelSizeGroup">
     <widgets>
-      <widget name="CommandExamplePreLabel"/>
-      <widget name="CommandParametersLabel"/>
-      <widget name="CommandPathLabel"/>
       <widget name="ProfileLabelLabel"/>
+      <widget name="CommandPathLabel"/>
+      <widget name="CommandParametersLabel"/>
+      <widget name="CommandExamplePreLabel"/>
     </widgets>
   </object>
   <object class="GtkSizeGroup" id="CommandButtonSizeGroup">
     <widgets>
-      <widget name="CommandLegendButton"/>
       <widget name="CommandPathButton"/>
+      <widget name="CommandLegendButton"/>
     </widgets>
   </object>
 </interface>



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