[nautilus-actions] Define NAIImporter v2 new version interface



commit 08431381c4ee01f92554406b954cf7842cec7625
Author: Pierre Wieser <pwieser trychlos org>
Date:   Mon Jan 2 22:12:13 2012 +0100

    Define NAIImporter v2 new version interface

 ChangeLog                              |   17 ++
 src/api/na-iimporter.h                 |   83 ++++++++--
 src/core/na-iimporter.c                |   15 +-
 src/core/na-importer.c                 |  277 ++++++++++++++++++++++----------
 src/core/na-importer.h                 |   71 +++++++--
 src/io-desktop/nadp-desktop-provider.c |    2 +-
 src/io-desktop/nadp-reader.c           |   29 +---
 src/io-xml/naxml-provider.c            |    2 +-
 src/io-xml/naxml-reader.c              |   37 +----
 src/nact/nact-assistant-import.c       |   47 ++++--
 src/nact/nact-tree-model-dnd.c         |   35 +++--
 src/test/test-reader.c                 |   11 +-
 12 files changed, 409 insertions(+), 217 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 4117c60..da4b0bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2012-01-02 Pierre Wieser <pwieser trychlos org>
 
+	* src/api/na-iimporter.h:
+	* src/core/na-iimporter.c: Introduces version 2 of interface,
+	deprecating NAIImporterCheckFn and NAIImporterAskUserFn definitions.
+
+	* src/core/na-importer.c:
+	* src/core/na-importer.h (na_importer_import_from_uris):
+	Updated to new NAIImporter v2 interface, moving check and ask code here.
+
+	* src/io-desktop/nadp-desktop-provider.c (iimporter_get_version):
+	* src/io-desktop/nadp-reader.c (nadp_reader_iimporter_import_from_uri):
+	* src/io-xml/naxml-provider.c (iimporter_get_version):
+	* src/io-xml/naxml-reader.c (naxml_reader_import_from_uri):
+	* src/nact/nact-assistant-import.c (assistant_apply):
+	* src/nact/nact-tree-model-dnd.c (drop_uri_list):
+	* src/test/test-reader.c (main):
+	Updated to new NAIImporter v2 interface.
+
 	* src/api/na-iexporter.h: Update documentation.
 
 	* src/core/na-importer.c:
diff --git a/src/api/na-iimporter.h b/src/api/na-iimporter.h
index 625ff66..fac7f28 100644
--- a/src/api/na-iimporter.h
+++ b/src/api/na-iimporter.h
@@ -39,6 +39,16 @@
  *
  * The #NAIImporter interface imports items from the outside world.
  *
+ * &prodname; version 3.2 introduces the version 2 of this interface,
+ * which greatly simplify it. The I/O provider which implements the
+ * #NAIIMporter interface is no more required to check for existence
+ * of the imported items, but this check is pushed back to the caller
+ * responsability.
+ *
+ * Rationale is that only the caller is able to check against a valid
+ * repository in its current import context, while the #NAIImporter
+ * provider should only be responsible to import an item in memory.
+ *
  * Internal Nautilus-Actions code should never directly call a
  * #NAIImporter interface method, but rather should call the
  * corresponding na_importer_xxx() function.
@@ -51,18 +61,27 @@
  *      <colspec colname="na-version" />
  *      <colspec colname="api-version" />
  *      <colspec colname="current" />
+ *      <colspec colname="deprecated" />
  *      <thead>
  *        <row>
  *          <entry>&prodname; version</entry>
  *          <entry>#NAIImporter interface version</entry>
  *          <entry></entry>
+ *          <entry></entry>
  *        </row>
  *      </thead>
  *      <tbody>
  *        <row>
- *          <entry>since 2.30</entry>
+ *          <entry>from 2.30 up to 3.1.5</entry>
  *          <entry>1</entry>
+ *          <entry></entry>
+ *          <entry>deprecated</entry>
+ *        </row>
+ *        <row>
+ *          <entry>since 3.2</entry>
+ *          <entry>2</entry>
  *          <entry>current version</entry>
+ *          <entry></entry>
  *        </row>
  *      </tbody>
  *    </tgroup>
@@ -82,7 +101,10 @@ G_BEGIN_DECLS
 typedef struct _NAIImporter                      NAIImporter;
 typedef struct _NAIImporterInterfacePrivate      NAIImporterInterfacePrivate;
 typedef struct _NAIImporterImportFromUriParms    NAIImporterImportFromUriParms;
+
+#ifdef NA_ENABLE_DEPRECATED
 typedef struct _NAIImporterManageImportModeParms NAIImporterManageImportModeParms;
+#endif
 
 /**
  * NAIImporterInterface:
@@ -133,13 +155,10 @@ typedef struct {
 
 /**
  * NAIImporterImportMode:
- * @IMPORTER_MODE_NO_IMPORT: a "do not import anything" mode.
- * @IMPORTER_MODE_RENUMBER:  reallocate a new id when the imported one
- *                           already exists.
- * @IMPORTER_MODE_OVERRIDE:  override the existing id with the imported
- *                           one.
- * @IMPORTER_MODE_ASK:       ask the user for what to do with this particular
- *                           item.
+ * @IMPORTER_MODE_NO_IMPORT: a "do not import" mode.
+ * @IMPORTER_MODE_RENUMBER:  reallocate a new id when the imported one already exists.
+ * @IMPORTER_MODE_OVERRIDE:  override the existing id with the imported one.
+ * @IMPORTER_MODE_ASK:       ask the user for what to do with this particular item.
  *
  * Define the mode of an import operation.
  */
@@ -157,8 +176,7 @@ typedef enum {
  * @IMPORTER_CODE_PROGRAM_ERROR:     a program error has been detected.
  *                                   You should open a bug in
  *                                   <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=nautilus-actions";>Bugzilla</ulink>.
- * @IMPORTER_CODE_NOT_WILLING_TO:    the plugin is not willing to import
- *                                   anything.
+ * @IMPORTER_CODE_NOT_WILLING_TO:    the plugin is not willing to import the uri.
  * @IMPORTER_CODE_NO_ITEM_ID:        item id not found.
  * @IMPORTER_CODE_NO_ITEM_TYPE:      item type not found.
  * @IMPORTER_CODE_UNKNOWN_ITEM_TYPE: unknown item type.
@@ -182,6 +200,7 @@ typedef enum {
 }
 	NAIImporterImportStatus;
 
+#ifdef NA_ENABLE_DEPRECATED
 /**
  * NAIImporterCheckFn:
  * @imported: the currently imported #NAObjectItem -derived object.
@@ -218,6 +237,7 @@ typedef enum {
  * Returns: the already existing #NAObjectItem with same id, or %NULL.
  *
  * Since: 2.30
+ * Deprecated: 3.2
  */
 typedef NAObjectItem * ( *NAIImporterCheckFn )( const NAObjectItem *, void * );
 
@@ -239,12 +259,13 @@ typedef NAObjectItem * ( *NAIImporterCheckFn )( const NAObjectItem *, void * );
  * %IMPORTER_MODE_ASK.
  *
  * Since: 2.30
+ * Deprecated: 3.2
  */
 typedef guint ( *NAIImporterAskUserFn )( const NAObjectItem *, const NAObjectItem *, void * );
 
 /**
- * NAIImporterImportFromUriParms:
- * @version:       the version of this structure, currently equals to 1.
+ * NAIImporterImportFromUriParmsv1:
+ * @version:       the version of this structure.
  *                 input;
  *                 since version 1 of the structure.
  * @uri:           uri of the file to be imported.
@@ -283,8 +304,9 @@ typedef guint ( *NAIImporterAskUserFn )( const NAObjectItem *, const NAObjectIte
  * to be passed and received through a single structure.
  *
  * Since: 2.30
+ * Deprecated: 3.2
  */
-struct _NAIImporterImportFromUriParms {
+struct _NAIImporterImportFromUriParmsv1 {
 	guint                version;
 	gchar               *uri;
 	guint                asked_mode;
@@ -298,6 +320,37 @@ struct _NAIImporterImportFromUriParms {
 	GSList              *messages;
 };
 
+#endif /* NA_ENABLE_DEPRECATED */
+
+/**
+ * NAIImporterImportFromUriParms:
+ * @version:       the version of this structure, currently equals to 2.
+ *                 input;
+ *                 since version 1 of the structure.
+ * @uri:           uri of the file to be imported.
+ *                 input;
+ *                 since version 1 of the structure.
+ * @imported:      the imported #NAObjectItem -derived object, or %NULL.
+ *                 output;
+ *                 since version 1 of the structure.
+ * @messages:      a #GSList list of localized strings;
+ *                 the provider may append messages to this list, but shouldn't reinitialize it
+ *                 input/output;
+ *                 since version 1 of the structure.
+ *
+ * This structure allows all used parameters when importing from an URI
+ * to be passed and received through a single structure.
+ *
+ * Since: 2.30
+ */
+struct _NAIImporterImportFromUriParms {
+	guint                version;
+	const gchar         *uri;
+	NAObjectItem        *imported;
+	GSList              *messages;
+};
+
+#ifdef NA_ENABLE_DEPRECATED
 /**
  * NAIImporterManageImportModeParms:
  * @version:       the version of this structure, currently equals to 1.
@@ -332,6 +385,7 @@ struct _NAIImporterImportFromUriParms {
  * to be passed and received through a single structure.
  *
  * Since: 2.30
+ * Deprecated: 3.2
  */
 struct _NAIImporterManageImportModeParms {
 	guint                version;
@@ -345,12 +399,15 @@ struct _NAIImporterManageImportModeParms {
 	guint                import_mode;
 	GSList              *messages;
 };
+#endif /* NA_ENABLE_DEPRECATED */
 
 GType na_iimporter_get_type( void );
 
 guint na_iimporter_import_from_uri( const NAIImporter *importer, NAIImporterImportFromUriParms *parms );
 
+#ifdef NA_ENABLE_DEPRECATED
 guint na_iimporter_manage_import_mode( NAIImporterManageImportModeParms *parms );
+#endif
 
 G_END_DECLS
 
diff --git a/src/core/na-iimporter.c b/src/core/na-iimporter.c
index 598e405..d1e30e3 100644
--- a/src/core/na-iimporter.c
+++ b/src/core/na-iimporter.c
@@ -47,13 +47,14 @@ struct _NAIImporterInterfacePrivate {
 gboolean iimporter_initialized = FALSE;
 gboolean iimporter_finalized   = FALSE;
 
-static GType  register_type( void );
-static void   interface_base_init( NAIImporterInterface *klass );
-static void   interface_base_finalize( NAIImporterInterface *klass );
+static GType register_type( void );
+static void  interface_base_init( NAIImporterInterface *klass );
+static void  interface_base_finalize( NAIImporterInterface *klass );
+static guint iimporter_get_version( const NAIImporter *instance );
 
-static guint  iimporter_get_version( const NAIImporter *instance );
-
-static void   renumber_label_item( NAIImporterManageImportModeParms *parms );
+#ifdef NA_ENABLE_DEPRECATED
+static void  renumber_label_item( NAIImporterManageImportModeParms *parms );
+#endif
 
 /**
  * na_iimporter_get_type:
@@ -179,6 +180,7 @@ na_iimporter_import_from_uri( const NAIImporter *importer, NAIImporterImportFrom
 	return( code );
 }
 
+#ifdef NA_ENABLE_DEPRECATED
 /**
  * na_iimporter_manage_import_mode:
  * @parms: a NAIImporterManageImportModeParms struct.
@@ -312,3 +314,4 @@ renumber_label_item( NAIImporterManageImportModeParms *parms )
 	g_free( tmp );
 	g_free( label );
 }
+#endif /* NA_ENABLE_DEPRECATED */
diff --git a/src/core/na-importer.c b/src/core/na-importer.c
index dc4aea0..2a3ea72 100644
--- a/src/core/na-importer.c
+++ b/src/core/na-importer.c
@@ -96,20 +96,15 @@ static NAImportModeStr st_import_ask_mode = {
 			"import-mode-ask.png"
 };
 
-typedef struct {
-	GList             *just_imported;
-	NAIImporterCheckFn check_fn;
-	void              *check_fn_data;
-}
-	ImporterExistsStr;
-
 extern gboolean iimporter_initialized;		/* defined in na-iimporter.c */
 extern gboolean iimporter_finalized;		/* defined in na-iimporter.c */
 
-static guint         import_from_uri( const NAPivot *pivot, GList *modules, NAImporterParms *parms, const gchar *uri, NAImporterResult **result );
-static NAObjectItem *is_importing_already_exists( const NAObjectItem *importing, ImporterExistsStr *parms );
-static guint         ask_user_for_mode( const NAObjectItem *importing, const NAObjectItem *existing, NAImporterAskUserParms *parms );
-static NAIOption    *get_mode_from_struct( const NAImportModeStr *str );
+static NAImporterResult *import_from_uri( const NAPivot *pivot, GList *modules, const gchar *uri );
+static void              manage_import_mode( NAImporterParms *parms, GList *results, NAImporterAskUserParms *ask_parms, NAImporterResult *result );
+static NAObjectItem     *is_importing_already_exists( NAImporterParms *parms, GList *results, NAImporterResult *result );
+static void              renumber_label_item( NAObjectItem *item );
+static guint             ask_user_for_mode( const NAObjectItem *importing, const NAObjectItem *existing, NAImporterAskUserParms *parms );
+static NAIOption        *get_mode_from_struct( const NAImportModeStr *str );
 
 /* i18n: '%s' stands for the file URI */
 #define ERR_NOT_LOADABLE	_( "%s is not loadable (empty or too big or not a regular file)" )
@@ -130,55 +125,69 @@ static NAIOption    *get_mode_from_struct( const NAImportModeStr *str );
  * Each import operation will have its corresponding newly allocated
  * #NAImporterResult structure which will contain:
  * - the imported URI
+ * - the #NAIImporter provider if one has been found, or %NULL
  * - a #NAObjectItem item if import was successful, or %NULL
  * - a list of error messages, or %NULL.
  *
- * If asked mode is 'ask', then ask the user at least the first time;
- * the 'keep my choice' is active or not, depending of the last time used,
- * then the 'keep my choice' is kept for other times.
- * So preferences are:
- * - asked import mode (may be 'ask') -> import-mode
- * - keep my choice                   -> import-keep-choice
- * - last chosen import mode          -> import-ask-user-last-mode
- *
- * Returns: the count of successfully imported items
+ * Returns: a #GList of #NAImporterResult structures
  * (was the last import operation code up to 3.2).
  *
  * Since: 2.30
  */
-guint
+GList *
 na_importer_import_from_uris( const NAPivot *pivot, NAImporterParms *parms )
 {
 	static const gchar *thisfn = "na_importer_import_from_uris";
+	GList *results, *ires;
 	GList *modules;
-	GSList *iuri;
-	NAImporterResult *result;
-	guint count;
+	GSList *uri;
+	NAImporterResult *import_result;
+	NAImporterAskUserParms ask_parms;
 
-	g_return_val_if_fail( NA_IS_PIVOT( pivot ), 0 );
+	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
+	g_return_val_if_fail( parms != NULL, NULL );
 
-	count = 0;
-	parms->results = NULL;
+	results = NULL;
 
 	if( iimporter_initialized && !iimporter_finalized ){
 
 		g_debug( "%s: pivot=%p, parms=%p", thisfn, ( void * ) pivot, ( void * ) parms );
 
+		/* first phase: just try to import the uris into memory
+		 */
 		modules = na_pivot_get_providers( pivot, NA_IIMPORTER_TYPE );
 
-		for( iuri = parms->uris ; iuri ; iuri = iuri->next ){
-			import_from_uri( pivot, modules, parms, ( const gchar * ) iuri->data, &result );
-			parms->results = g_list_prepend( parms->results, result );
-			if( result->imported ){
-				count += 1;
-			}
+		for( uri = parms->uris ; uri ; uri = uri->next ){
+			import_result = import_from_uri( pivot, modules, ( const gchar * ) uri->data );
+			results = g_list_prepend( results, import_result );
 		}
 
 		na_pivot_free_providers( modules );
-		parms->results = g_list_reverse( parms->results );
+
+		results = g_list_reverse( results );
+
+		memset( &ask_parms, '\0', sizeof( NAImporterAskUserParms ));
+		ask_parms.parent = parms->parent_toplevel;
+		ask_parms.count = 0;
+		ask_parms.keep_choice = FALSE;
+		ask_parms.pivot = pivot;
+
+		/* second phase: check for their pre-existence
+		 */
+		for( ires = results ; ires ; ires = ires->next ){
+			import_result = ( NAImporterResult * ) ires->data;
+
+			if( import_result->imported ){
+				g_return_val_if_fail( NA_IS_OBJECT_ITEM( import_result->imported ), NULL );
+				g_return_val_if_fail( NA_IS_IIMPORTER( import_result->importer ), NULL );
+
+				ask_parms.uri = import_result->uri;
+				manage_import_mode( parms, results, &ask_parms, import_result );
+			}
+		}
 	}
 
-	return( count );
+	return( results );
 }
 
 /*
@@ -197,48 +206,33 @@ na_importer_free_result( NAImporterResult *result )
 }
 
 /*
- * Each NAIImporter interface may return some messages, specially if it is
- * not able to import the provided URI. But as long we do not have yet asked
- * to all available interfaces, we are not sure of whether this URI is
- * eventually importable or not.
+ * Each NAIImporter interface may return some messages, specially if it
+ * recognized but is not able to import the provided URI. But as long
+ * we do not have yet asked to all available interfaces, we are not sure
+ * of whether this URI is eventually importable or not.
+ *
  * We so let each interface push its messages in the list, but be ready to
  * only keep the messages provided by the interface which has successfully
  * imported the item.
  */
-static guint
-import_from_uri( const NAPivot *pivot, GList *modules, NAImporterParms *parms, const gchar *uri, NAImporterResult **result )
+static NAImporterResult *
+import_from_uri( const NAPivot *pivot, GList *modules, const gchar *uri )
 {
-	guint code;
-	GList *im;
+	NAImporterResult *result;
 	NAIImporterImportFromUriParms provider_parms;
-	ImporterExistsStr exists_parms;
-	NAImporterAskUserParms ask_parms;
+	GList *im;
+	guint code;
 	GSList *all_messages;
+	NAIImporter *provider;
 
+	result = NULL;
+	all_messages = NULL;
+	provider = NULL;
 	code = IMPORTER_CODE_NOT_WILLING_TO;
 
-	memset( &exists_parms, '\0', sizeof( ImporterExistsStr ));
-	exists_parms.just_imported = parms->results;
-	exists_parms.check_fn = parms->check_fn;
-	exists_parms.check_fn_data = parms->check_fn_data;
-
-	memset( &ask_parms, '\0', sizeof( NAImporterAskUserParms ));
-	ask_parms.parent = parms->parent;
-	ask_parms.uri = ( gchar * ) uri;
-	ask_parms.count = g_list_length( parms->results );
-	ask_parms.keep_choice = na_settings_get_boolean( NA_IPREFS_IMPORT_ASK_USER_KEEP_LAST_CHOICE, NULL, NULL );
-	ask_parms.pivot = pivot;
-
 	memset( &provider_parms, '\0', sizeof( NAIImporterImportFromUriParms ));
-	provider_parms.version = 1;
-	provider_parms.uri = ( gchar * ) uri;
-	provider_parms.asked_mode = parms->mode;
-	provider_parms.check_fn = ( NAIImporterCheckFn ) is_importing_already_exists;
-	provider_parms.check_fn_data = &exists_parms;
-	provider_parms.ask_fn = ( NAIImporterAskUserFn ) ask_user_for_mode;
-	provider_parms.ask_fn_data = &ask_parms;
-
-	all_messages = NULL;
+	provider_parms.version = 2;
+	provider_parms.uri = uri;
 
 	for( im = modules ;
 			im && ( code == IMPORTER_CODE_NOT_WILLING_TO || code == IMPORTER_CODE_NOT_LOADABLE ) ;
@@ -260,44 +254,136 @@ import_from_uri( const NAPivot *pivot, GList *modules, NAImporterParms *parms, c
 		} else {
 			na_core_utils_slist_free( all_messages );
 			all_messages = provider_parms.messages;
+			provider = NA_IIMPORTER( im->data );
 		}
 	}
 
-	*result = g_new0( NAImporterResult, 1 );
-	( *result )->uri = g_strdup( uri );
-	( *result )->mode = provider_parms.import_mode;
-	( *result )->exist = provider_parms.exist;
-	( *result )->imported = provider_parms.imported;
-	( *result )->messages = all_messages;
+	result = g_new0( NAImporterResult, 1 );
+	result->uri = g_strdup( uri );
+	result->imported = provider_parms.imported;
+	result->importer = provider;
+	result->messages = all_messages;
 
-	return( code );
+	return( result );
 }
 
 /*
- * to see if an imported item already exists, we have to check
- * - the current list of just imported items
- * - the main window (if any), which contains the in-memory list of items.
+ * check for existence of the imported item
+ * ask for the user if needed
+ */
+static void
+manage_import_mode( NAImporterParms *parms, GList *results, NAImporterAskUserParms *ask_parms, NAImporterResult *result )
+{
+	static const gchar *thisfn = "na_importer_manage_import_mode";
+	NAObjectItem *exists;
+	guint mode;
+	gchar *id;
+
+	exists = NULL;
+	result->exist = FALSE;
+	result->mode = parms->preferred_mode;
+	mode = 0;
+
+	/* if no check function is provided, then we systematically allocate
+	 * a new identifier to the imported item
+	 */
+	if( !parms->check_fn ){
+		renumber_label_item( result->imported );
+		na_core_utils_slist_add_message(
+				&result->messages,
+				"%s",
+				_( "Item was renumbered because the caller did not provide any check function." ));
+		result->mode = IMPORTER_MODE_RENUMBER;
+
+	} else {
+		exists = is_importing_already_exists( parms, results, result );
+	}
+
+	g_debug( "%s: exists=%p", thisfn, exists );
+
+	if( exists ){
+		result->exist = TRUE;
+
+		if( parms->preferred_mode == IMPORTER_MODE_ASK ){
+			mode = ask_user_for_mode( result->imported, exists, ask_parms );
+
+		} else {
+			mode = parms->preferred_mode;
+		}
+	}
+
+	/* mode is only set if asked mode was "ask me" and an ask function was provided
+	 * or if asked mode was not "ask me"
+	 */
+	if( mode ){
+		result->mode = mode;
+
+		switch( mode ){
+			case IMPORTER_MODE_RENUMBER:
+				renumber_label_item( result->imported );
+				if( parms->preferred_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message(
+							&result->messages,
+							"%s",
+							_( "Item was renumbered due to user request." ));
+				}
+				break;
+
+			case IMPORTER_MODE_OVERRIDE:
+				if( parms->preferred_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message(
+							&result->messages,
+							"%s",
+							_( "Existing item was overriden due to user request." ));
+				}
+				break;
+
+			case IMPORTER_MODE_NO_IMPORT:
+			default:
+				id = na_object_get_id( result->imported );
+				na_core_utils_slist_add_message(
+						&result->messages,
+						_( "Item %s already exists." ),
+						id );
+				if( parms->preferred_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message(
+							&result->messages,
+							"%s",
+							_( "Import was canceled due to user request." ));
+				}
+				g_free( id );
+		}
+	}
+}
+
+/*
+ * First check here for duplicates inside of imported population,
+ * then delegates to the caller-provided check function the rest of work...
  */
 static NAObjectItem *
-is_importing_already_exists( const NAObjectItem *importing, ImporterExistsStr *parms )
+is_importing_already_exists( NAImporterParms *parms, GList *results, NAImporterResult *result )
 {
 	static const gchar *thisfn = "na_importer_is_importing_already_exists";
 	NAObjectItem *exists;
 	GList *ip;
 
 	exists = NULL;
-	gchar *importing_id = na_object_get_id( importing );
-	g_debug( "%s: importing=%p, id=%s", thisfn, ( void * ) importing, importing_id );
+
+	gchar *importing_id = na_object_get_id( result->imported );
+	g_debug( "%s: importing=%p, id=%s", thisfn, ( void * ) result->imported, importing_id );
 
 	/* is the importing item already in the current importation list ?
+	 * (only tries previous items of the list)
 	 */
-	for( ip = parms->just_imported ; ip && !exists ; ip = ip->next ){
-		NAImporterResult *result = ( NAImporterResult * ) ip->data;
+	for( ip = results ; ip && !exists && ip->data != result ; ip = ip->next ){
+		NAImporterResult *try_result = ( NAImporterResult * ) ip->data;
+
+		if( try_result->imported ){
+			g_return_val_if_fail( NA_IS_OBJECT_ITEM( try_result->imported ), NULL );
 
-		if( result->imported ){
-			gchar *id = na_object_get_id( result->imported );
+			gchar *id = na_object_get_id( try_result->imported );
 			if( !strcmp( importing_id, id )){
-				exists = NA_OBJECT_ITEM( result->imported );
+				exists = NA_OBJECT_ITEM( try_result->imported );
 			}
 			g_free( id );
 		}
@@ -309,12 +395,33 @@ is_importing_already_exists( const NAObjectItem *importing, ImporterExistsStr *p
 	 * then check the existence via provided function and data
 	 */
 	if( !exists ){
-		exists = parms->check_fn( importing, parms->check_fn_data );
+		exists = parms->check_fn( result->imported, parms->check_fn_data );
 	}
 
 	return( exists );
 }
 
+/*
+ * renumber the item, and set a new label
+ */
+static void
+renumber_label_item( NAObjectItem *item )
+{
+	gchar *label, *tmp;
+
+	na_object_set_new_id( item, NULL );
+
+	label = na_object_get_label( item );
+
+	/* i18n: the action has been renumbered during import operation */
+	tmp = g_strdup_printf( "%s %s", label, _( "(renumbered)" ));
+
+	na_object_set_label( item, tmp );
+
+	g_free( tmp );
+	g_free( label );
+}
+
 static guint
 ask_user_for_mode( const NAObjectItem *importing, const NAObjectItem *existing, NAImporterAskUserParms *parms )
 {
diff --git a/src/core/na-importer.h b/src/core/na-importer.h
index e37b883..808abff 100644
--- a/src/core/na-importer.h
+++ b/src/core/na-importer.h
@@ -37,7 +37,23 @@
  *
  * Internal Nautilus-Actions code should never directly call a
  * #NAIImporter interface method, but rather should call the
- * corresponding na_importer_xxx() function.
+ * corresponding na_importer_xxx() functions.
+ *
+ * Importing items is a three-phase operation:
+ *
+ * - first, just try to find an i/o provider which is willing to import
+ *   the item;
+ *   at this time, only some uris have been successfully imported
+ *
+ * - check then for existence of each imported item;
+ *   depending of the preferred import mode, this may be an interactive
+ *   process;
+ *   at this time, the importation of some objects may have been cancelled
+ *   by the user
+ *
+ * - last, and depending of the exact individual import mode of each item,
+ *   insert new items or override existing ones in the referential of the
+ *   import context.
  */
 
 #include <gtk/gtk.h>
@@ -50,28 +66,53 @@
 
 G_BEGIN_DECLS
 
+/*
+ * NAImporterCheckFn:
+ * @imported: the currently imported #NAObjectItem -derived object.
+ * @fn_data: some data to be passed to the function.
+ *
+ * The library takes care of checking for duplicates inside of the imported
+ * population.
+ *
+ * The caller may provide this function in order to check for duplicates
+ * in its own import context, e.g. (and typically) by checking for a same
+ * identifier in the main window items tree view.
+ *
+ * The function should return the already existing item which has the same id
+ * than the currently being imported one, or %NULL if the imported id will be
+ * unique.
+ *
+ * If the caller does not provide its own check function, then each imported
+ * item will be systematically renumbered (allocated a new identifier).
+ *
+ * Returns: the already existing #NAObjectItem with same id, or %NULL.
+ *
+ * Since: 3.2
+ */
+typedef NAObjectItem * ( *NAImporterCheckFn )( const NAObjectItem *, void * );
+
 typedef struct {
-	GtkWindow         *parent;			/* the parent window, if any */
-	GSList            *uris;			/* the list of uris of the files to be imported */
-	guint              mode;			/* asked (preferred) import mode */
-	NAIImporterCheckFn check_fn;		/* a function to check the existence of the imported id */
-	void              *check_fn_data;	/* data function */
-	GList             *results;			/* a #GList of newly allocated NAImporterResult structures,
-										   one for each imported uri, which should be
-										   na_importer_free_result() by the caller */
+	GSList             *uris;				/* the list of uris to import */
+	NAImporterCheckFn   check_fn;			/* the check_for_duplicate function */
+	void               *check_fn_data;		/* data to be passed to the check_fn function */
+	guint               preferred_mode;		/* preferred import mode */
+	GtkWindow          *parent_toplevel;	/* parent toplevel */
 }
 	NAImporterParms;
 
 typedef struct {
-	gchar             *uri;				/* the imported uri */
-	guint              mode;			/* the actual mode in effect for this import */
-	gboolean           exist;			/* whether the imported Id already existed */
-	NAObjectItem      *imported;		/* eventually imported NAObjectItem-derived object, or %NULL */
-	GSList            *messages;		/* a #GSList list of localized strings */
+		/* phase 1: import into memory */
+	gchar             *uri;					/* the imported uri */
+	NAObjectItem      *imported;			/* the imported NAObjectItem-derived object, or %NULL */
+	NAIImporter       *importer;			/* the importer module, or %NULL */
+		/* phase 2: check for pre-existence */
+	gboolean           exist;				/* whether the imported Id already existed */
+	guint              mode;				/* the actual mode in effect for this import */
+	GSList            *messages;			/* a #GSList list of localized strings */
 }
 	NAImporterResult;
 
-guint      na_importer_import_from_uris( const NAPivot *pivot, NAImporterParms *parms );
+GList     *na_importer_import_from_uris( const NAPivot *pivot, NAImporterParms *parms );
 
 void       na_importer_free_result     ( NAImporterResult *result );
 
diff --git a/src/io-desktop/nadp-desktop-provider.c b/src/io-desktop/nadp-desktop-provider.c
index 453d604..f94e257 100644
--- a/src/io-desktop/nadp-desktop-provider.c
+++ b/src/io-desktop/nadp-desktop-provider.c
@@ -296,7 +296,7 @@ iimporter_iface_init( NAIImporterInterface *iface )
 static guint
 iimporter_get_version( const NAIImporter *importer )
 {
-	return( 1 );
+	return( 2 );
 }
 
 static void
diff --git a/src/io-desktop/nadp-reader.c b/src/io-desktop/nadp-reader.c
index 7652c62..02aae5c 100644
--- a/src/io-desktop/nadp-reader.c
+++ b/src/io-desktop/nadp-reader.c
@@ -355,6 +355,9 @@ free_desktop_paths( GList *paths )
  * GLib does not have any primitive to load a key file from an uri.
  * So we have to load the file into memory, and then try to load the key
  * file from the memory data.
+ *
+ * Starting with N-A 3.2, we only honor the version 2 of #NAIImporter interface,
+ * thus no more checking here against possible duplicate identifiers.
  */
 guint
 nadp_reader_iimporter_import_from_uri( const NAIImporter *instance, NAIImporterImportFromUriParms *parms )
@@ -362,7 +365,6 @@ nadp_reader_iimporter_import_from_uri( const NAIImporter *instance, NAIImporterI
 	static const gchar *thisfn = "nadp_reader_iimporter_import_from_uri";
 	guint code;
 	NadpDesktopFile *ndf;
-	NAIImporterManageImportModeParms manage_parms;
 
 	g_debug( "%s: instance=%p, parms=%p", thisfn, ( void * ) instance, ( void * ) parms );
 
@@ -378,8 +380,6 @@ nadp_reader_iimporter_import_from_uri( const NAIImporter *instance, NAIImporterI
 
 	ndf = nadp_desktop_file_new_from_uri( parms->uri );
 	if( ndf ){
-		parms->exist = FALSE;
-		parms->import_mode = IMPORTER_MODE_NO_IMPORT;
 		parms->imported = ( NAObjectItem * ) item_from_desktop_file(
 				( const NadpDesktopProvider * ) NADP_DESKTOP_PROVIDER( instance ),
 				ndf, &parms->messages );
@@ -394,28 +394,7 @@ nadp_reader_iimporter_import_from_uri( const NAIImporter *instance, NAIImporterI
 			g_object_weak_unref( G_OBJECT( parms->imported ), ( GWeakNotify ) desktop_weak_notify, ndf );
 			g_object_unref( ndf );
 
-			manage_parms.version = 1;
-			manage_parms.imported = parms->imported;
-			manage_parms.check_fn = parms->check_fn;
-			manage_parms.check_fn_data = parms->check_fn_data;
-			manage_parms.ask_fn = parms->ask_fn;
-			manage_parms.ask_fn_data = parms->ask_fn_data;
-			manage_parms.asked_mode = parms->asked_mode;
-			manage_parms.messages = parms->messages;
-
-			code = na_iimporter_manage_import_mode( &manage_parms );
-
-			parms->exist = manage_parms.exist;
-			parms->import_mode = manage_parms.import_mode;
-			parms->messages = manage_parms.messages;
-		}
-
-		if( code != IMPORTER_CODE_OK ){
-			if( parms->imported ){
-				g_debug( "%s: unreffing imported item %p as na_iimporter_manage_import_mode didn't return IMPORTER_CODE_OK", thisfn, parms->imported );
-				g_object_unref( parms->imported );
-				parms->imported = NULL;
-			}
+			code = IMPORTER_CODE_OK;
 		}
 	}
 
diff --git a/src/io-xml/naxml-provider.c b/src/io-xml/naxml-provider.c
index d9e7146..410801a 100644
--- a/src/io-xml/naxml-provider.c
+++ b/src/io-xml/naxml-provider.c
@@ -217,7 +217,7 @@ iimporter_iface_init( NAIImporterInterface *iface )
 static guint
 iimporter_get_version( const NAIImporter *importer )
 {
-	return( 1 );
+	return( 2 );
 }
 
 static void
diff --git a/src/io-xml/naxml-reader.c b/src/io-xml/naxml-reader.c
index 296a2ed..832a36a 100644
--- a/src/io-xml/naxml-reader.c
+++ b/src/io-xml/naxml-reader.c
@@ -173,7 +173,6 @@ static gchar        *build_root_node_list( void );
 static gchar        *get_value_from_child_node( xmlNode *node, const gchar *child );
 static gchar        *get_value_from_child_child_node( xmlNode *node, const gchar *first, const gchar *second );
 static gboolean      is_profile_path( NAXMLReader *reader, xmlChar *text );
-static guint         manage_import_mode( NAXMLReader *reader );
 static void          reset_node_data( NAXMLReader *reader );
 static xmlNode      *search_for_child_node( xmlNode *node, const gchar *key );
 static int           strxcmp( const xmlChar *a, const char *b );
@@ -314,9 +313,12 @@ reader_new( void )
  *
  * Returns: the import operation code.
  *
- * If we not found at least a well-formed XML document with a known root node,
+ * If we do not found at least a well-formed XML document with a known root node,
  *  then we do not return any error message at all, but just the 'unwilling to'
  *  code.
+ *
+ * Starting with N-A 3.2, we only honor the version 2 of #NAIImporter interface,
+ * thus no more checking here against possible duplicate identifiers.
  */
 guint
 naxml_reader_import_from_uri( const NAIImporter *instance, NAIImporterImportFromUriParms *parms )
@@ -329,8 +331,6 @@ naxml_reader_import_from_uri( const NAIImporter *instance, NAIImporterImportFrom
 
 	g_return_val_if_fail( NA_IS_IIMPORTER( instance ), IMPORTER_CODE_PROGRAM_ERROR );
 
-	parms->exist = FALSE;
-	parms->import_mode = IMPORTER_MODE_NO_IMPORT;
 	parms->imported = NULL;
 
 	if( !na_core_utils_file_is_loadable( parms->uri )){
@@ -347,11 +347,6 @@ naxml_reader_import_from_uri( const NAIImporter *instance, NAIImporterImportFrom
 		na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NOT_IOXML );
 	}
 
-	if( code == IMPORTER_CODE_OK ){
-		g_return_val_if_fail( NA_IS_OBJECT_ITEM( parms->imported ), IMPORTER_CODE_PROGRAM_ERROR );
-		code = manage_import_mode( reader );
-	}
-
 	g_object_unref( reader );
 
 	if( code == IMPORTER_CODE_OK ){
@@ -1393,30 +1388,6 @@ is_profile_path( NAXMLReader *reader, xmlChar *text )
 	return( is_profile );
 }
 
-static guint
-manage_import_mode( NAXMLReader *reader )
-{
-	NAIImporterManageImportModeParms parms;
-	guint code;
-
-	parms.version = 1;
-	parms.imported = reader->private->parms->imported;
-	parms.check_fn = reader->private->parms->check_fn;
-	parms.check_fn_data = reader->private->parms->check_fn_data;
-	parms.ask_fn = reader->private->parms->ask_fn;
-	parms.ask_fn_data = reader->private->parms->ask_fn_data;
-	parms.asked_mode = reader->private->parms->asked_mode;
-	parms.messages = reader->private->parms->messages;
-
-	code = na_iimporter_manage_import_mode( &parms );
-
-	reader->private->parms->exist = parms.exist;
-	reader->private->parms->import_mode = parms.import_mode;
-	reader->private->parms->messages = parms.messages;
-
-	return( code );
-}
-
 /*
  * data are reset before first run on nodes for an item
  */
diff --git a/src/nact/nact-assistant-import.c b/src/nact/nact-assistant-import.c
index 898dc95..102567b 100644
--- a/src/nact/nact-assistant-import.c
+++ b/src/nact/nact-assistant-import.c
@@ -653,8 +653,8 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 	NactAssistantImport *window;
 	NAImporterParms importer_parms;
 	BaseWindow *main_window;
-	GList *it;
-	GList *imported_items;
+	GList *import_results, *it;
+	GList *insertable_items, *overriden_items;
 	NAImporterResult *result;
 	NactApplication *application;
 	NAUpdater *updater;
@@ -665,29 +665,38 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 	g_debug( "%s: window=%p, assistant=%p", thisfn, ( void * ) wnd, ( void * ) assistant );
 	window = NACT_ASSISTANT_IMPORT( wnd );
 
-	imported_items = NULL;
-	memset( &importer_parms, '\0', sizeof( NAImporterParms ));
+	application = NACT_APPLICATION( base_window_get_application( main_window ));
+	updater = nact_application_get_updater( application );
 
 	g_object_get( G_OBJECT( wnd ), BASE_PROP_PARENT, &main_window, NULL );
-	importer_parms.parent = base_window_get_gtk_toplevel( BASE_WINDOW( wnd ));
+
+	memset( &importer_parms, '\0', sizeof( NAImporterParms ));
 	importer_parms.uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( window->private->file_chooser ));
-	importer_parms.mode = na_import_mode_get_id( NA_IMPORT_MODE( window->private->mode ));
-	importer_parms.check_fn = ( NAIImporterCheckFn ) check_for_existence;
+	importer_parms.check_fn = ( NAImporterCheckFn ) check_for_existence;
 	importer_parms.check_fn_data = main_window;
-	application = NACT_APPLICATION( base_window_get_application( main_window ));
-	updater = nact_application_get_updater( application );
+	importer_parms.preferred_mode = na_import_mode_get_id( NA_IMPORT_MODE( window->private->mode ));
+	importer_parms.parent_toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( wnd ));
+
+	import_results = na_importer_import_from_uris( NA_PIVOT( updater ), &importer_parms );
 
-	na_importer_import_from_uris( NA_PIVOT( updater ), &importer_parms );
+	insertable_items = NULL;
+	overriden_items = NULL;
 
-	for( it = importer_parms.results ; it ; it = it->next ){
+	for( it = import_results ; it ; it = it->next ){
 		result = ( NAImporterResult * ) it->data;
 		if( result->imported ){
-			imported_items = g_list_prepend( imported_items, result->imported );
+
+			if( !result->exist || result->mode == IMPORTER_MODE_RENUMBER ){
+				insertable_items = g_list_prepend( insertable_items, result->imported );
+
+			} else if( result->mode == IMPORTER_MODE_OVERRIDE ){
+				overriden_items = g_list_prepend( overriden_items, result->imported );
+			}
 		}
 	}
 
 	na_core_utils_slist_free( importer_parms.uris );
-	window->private->results = importer_parms.results;
+	window->private->results = import_results;
 
 	/* then insert the list
 	 * assuring that actions will be inserted in the same order as uris
@@ -696,11 +705,15 @@ assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
 	 * on the inserted objects; the pointers so remain valid even after
 	 * having released the imported_items list
 	 */
-	if( imported_items ){
-		imported_items = g_list_reverse( imported_items );
+	if( insertable_items ){
+		insertable_items = g_list_reverse( insertable_items );
 		items_view = nact_main_window_get_items_view( NACT_MAIN_WINDOW( main_window ));
-		nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view ), imported_items, NULL );
-		na_object_free_items( imported_items );
+		nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view ), insertable_items, NULL );
+		na_object_free_items( insertable_items );
+	}
+
+	if( overriden_items ){
+		na_object_free_items( overriden_items );
 	}
 }
 
diff --git a/src/nact/nact-tree-model-dnd.c b/src/nact/nact-tree-model-dnd.c
index 0e17a75..641f854 100644
--- a/src/nact/nact-tree-model-dnd.c
+++ b/src/nact/nact-tree-model-dnd.c
@@ -826,10 +826,10 @@ drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selec
 	NAUpdater *updater;
 	NactMainWindow *main_window;
 	NAImporterParms parms;
-	GList *it;
+	GList *import_results, *it;
 	guint count;
 	GSList *im;
-	GList *imported;
+	GList *imported, *overriden;
 	const gchar *selection_data_data;
 	NactTreeView *view;
 	GSList *messages;
@@ -857,32 +857,36 @@ drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selec
 	selection_data_data = ( const gchar * ) gtk_selection_data_get_data( selection_data );
 	g_debug( "%s", selection_data_data );
 
-	parms.parent = base_window_get_gtk_toplevel( BASE_WINDOW( main_window ));
+	memset( &parms, '\0', sizeof( NAImporterParms ));
 	parms.uris = g_slist_reverse( na_core_utils_slist_from_split( selection_data_data, "\r\n" ));
-
-	parms.mode = na_iprefs_get_import_mode( NA_IPREFS_IMPORT_PREFERRED_MODE, NULL );
-
-	parms.check_fn = ( NAIImporterCheckFn ) is_dropped_already_exists;
+	parms.check_fn = ( NAImporterCheckFn ) is_dropped_already_exists;
 	parms.check_fn_data = main_window;
-	parms.results = NULL;
+	parms.preferred_mode = na_iprefs_get_import_mode( NA_IPREFS_IMPORT_PREFERRED_MODE, NULL );
+	parms.parent_toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( main_window ));
 
-	na_importer_import_from_uris( NA_PIVOT( updater ), &parms );
+	import_results = na_importer_import_from_uris( NA_PIVOT( updater ), &parms );
 
 	/* analysing output results, simultaneously building a concatenation
 	 * of all lines of messages, and the list of imported items
 	 */
 	imported = NULL;
+	overriden = NULL;
 	messages = NULL;
 
-	for( it = parms.results ; it ; it = it->next ){
+	for( it = import_results ; it ; it = it->next ){
 		NAImporterResult *result = ( NAImporterResult * ) it->data;
 
 		for( im = result->messages ; im ; im = im->next ){
 			messages = g_slist_prepend( messages, im->data );
 		}
 		if( result->imported ){
-			imported = g_list_prepend( imported, result->imported );
-			na_updater_check_item_writability_status( updater, result->imported );
+			if( !result->exist || result->mode == IMPORTER_MODE_RENUMBER ){
+				imported = g_list_prepend( imported, result->imported );
+				na_updater_check_item_writability_status( updater, result->imported );
+
+			} else if( result->mode == IMPORTER_MODE_OVERRIDE ){
+				overriden = g_list_prepend( overriden, result->imported );
+			}
 		}
 	}
 
@@ -899,7 +903,7 @@ drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selec
 		dlg_message = na_core_utils_slist_join_at_end( messages, "\n" );
 		g_debug( "%s: dlg_message='%s'", thisfn, dlg_message );
 		dialog = gtk_message_dialog_new(
-				parms.parent,
+				parms.parent_toplevel,
 				GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
 				"%s", _( "Some messages have occurred during drop operation." ));
 		gtk_message_dialog_format_secondary_markup( GTK_MESSAGE_DIALOG( dialog ), "%s", dlg_message );
@@ -919,12 +923,13 @@ drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selec
 
 	drop_done = TRUE;
 	na_object_free_items( imported );
+	na_object_free_items( overriden );
 	na_core_utils_slist_free( parms.uris );
 
-	for( it = parms.results ; it ; it = it->next ){
+	for( it = import_results ; it ; it = it->next ){
 		na_importer_free_result( it->data );
 	}
-	g_list_free( parms.results );
+	g_list_free( import_results );
 
 	return( drop_done );
 }
diff --git a/src/test/test-reader.c b/src/test/test-reader.c
index 9e7421e..a44d07e 100755
--- a/src/test/test-reader.c
+++ b/src/test/test-reader.c
@@ -65,6 +65,7 @@ int
 main( int argc, char **argv )
 {
 	NAImporterParms parms;
+	GList *import_results;
 	NAImporterResult *result;
 
 	g_type_init();
@@ -76,17 +77,15 @@ main( int argc, char **argv )
 	na_pivot_set_loadable( pivot, !PIVOT_LOAD_DISABLED & !PIVOT_LOAD_INVALID );
 	na_pivot_load_items( pivot );
 
-	parms.parent = NULL;
 	parms.uris = g_slist_prepend( NULL, uri );
-	parms.mode = IMPORTER_MODE_ASK;
 	parms.check_fn = NULL;
 	parms.check_fn_data = NULL;
+	parms.preferred_mode = IMPORTER_MODE_ASK;
+	parms.parent_toplevel = NULL;
 
-	guint count = na_importer_import_from_uris( pivot, &parms );
+	import_results = na_importer_import_from_uris( pivot, &parms );
 
-	g_print( "%s: na_importer_import_from_uris() returns count=%u.\n", g_get_prgname(), count );
-
-	result = parms.results->data;
+	result = import_results->data;
 	if( result->imported ){
 		na_object_dump( result->imported );
 		g_object_unref( result->imported );



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