[nautilus-actions] Import/export .desktop files



commit 21e2b4d05d727f16ada4116ca66258a4b1f751c5
Author: Pierre Wieser <pwieser trychlos org>
Date:   Wed Jul 28 22:58:59 2010 +0200

    Import/export .desktop files
    
    This commit defines the global structure of import/export operations for .desktop files.
    Only the import operation is actually defined.

 ChangeLog                              |   42 +++++++++
 doc/export-format-id                   |    3 +-
 po/POTFILES.in                         |    1 +
 src/api/na-core-utils.h                |    6 +-
 src/api/na-iimporter.h                 |   27 +++++-
 src/core/na-core-utils.c               |   45 ++++++++++
 src/core/na-iimporter.c                |  111 ++++++++++++++++++++++++
 src/io-desktop/Makefile.am             |    2 +
 src/io-desktop/nadp-desktop-file.c     |  142 +++++++++++++++++++++++-------
 src/io-desktop/nadp-desktop-file.h     |    3 +-
 src/io-desktop/nadp-desktop-provider.c |  100 +++++++++++++++++++---
 src/io-desktop/nadp-reader.c           |   99 +++++++++++++++++++--
 src/io-desktop/nadp-reader.h           |    5 +-
 src/io-desktop/nadp-utils.c            |   43 ++++++++--
 src/io-desktop/nadp-utils.h            |    3 +-
 src/io-desktop/nadp-writer.c           |  137 ++++++++++++++++++++++-------
 src/io-desktop/nadp-writer.h           |   14 ++-
 src/io-xml/naxml-formats.c             |    3 +
 src/io-xml/naxml-reader.c              |  149 ++++++--------------------------
 19 files changed, 702 insertions(+), 233 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 29eb8f6..d9a0e15 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,47 @@
 2010-07-28 Pierre Wieser <pwieser trychlos org>
 
+	Import/export .desktop files.
+
+	* doc/export-format-id: Declare the new export format.
+
+	* po/POTFILES.in: Add a file.
+
+	* src/api/na-core-utils.h:
+	* src/core/na-core-utils.c
+	(na_core_utils_slist_add_message, na_core_utils_file_load_from_uri):
+	New functions.
+
+	* src/api/na-iimporter.h:
+	* src/core/na-iimporter.c (na_iimporter_manage_import_mode):
+	New function.
+
+	* src/io-desktop/nadp-formats.c:
+	* src/io-desktop/nadp-formats.h: New files.
+
+	* src/io-desktop/Makefile.am: Updated accordingly.
+
+	* src/io-desktop/nadp-desktop-file.c:
+	* src/io-desktop/nadp-desktop-file.h
+	(nadp_desktop_file_new_from_uri): New function.
+	(nadp_desktop_file_get_key_file_path): Renamed as nadp_desktop_file_get_key_file_uri().
+
+	* src/io-desktop/nadp-desktop-provider.c:
+	Declare NAIImporterInterface and NAIExporterInterface interfaces.
+
+	* src/io-desktop/nadp-reader.c:
+	* src/io-desktop/nadp-reader.h
+	(nadp_reader_iimporter_import_from_uri): New function.
+
+	* src/io-desktop/nadp-utils.c:
+	* src/io-desktop/nadp-utils.h
+	(nadp_utils_is_writable_file): Renamed as nadp_utils_uri_is_writable().
+	(nadp_utils_uri_delete): New function.
+
+	* src/io-desktop/nadp-writer.c:
+	* src/io-desktop/nadp-writer.h
+	(nadp_writer_iexporter_export_to_buffer, nadp_writer_iexporter_export_to_file):
+	New functions.
+
 	* src/nact/nact-add-scheme-dialog.c:
 	Prevent a default scheme to be inserted twice.
 
diff --git a/doc/export-format-id b/doc/export-format-id
index 9f8bd94..2d3ff62 100644
--- a/doc/export-format-id
+++ b/doc/export-format-id
@@ -8,8 +8,9 @@ please contact the maintainers (see nautilus-actions.doap).
 format id      module              holder            allocated
 -------------  ------------------  ----------------  ----------
 Ask            reserved id         Nautilus-Actions  2010-02-15
+Desktop1       NA Desktop module   Nautilus-Actions  2010-07-28
 GConfSchemaV1  NA XML module       Nautilus-Actions  2010-02-15
 GConfSchemaV1  NA XML module       Nautilus-Actions  2010-02-15
 GConfEntry     NA XML module       Nautilus-Actions  2010-02-15
 
-Last updated: 2010, Feb. 15th
+Last updated: 2010, July 28th.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9ce916c..aeaa66f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,6 +2,7 @@ data/nautilus-actions-prefs.schemas.in
 src/core/na-exporter.c
 src/core/na-iabout.c
 src/core/na-icontext-factory.c
+src/core/na-iimporter.c
 src/core/na-importer-ask.c
 src/core/na-importer-ask.ui
 src/core/na-io-provider.c
diff --git a/src/api/na-core-utils.h b/src/api/na-core-utils.h
index 07fba06..1fff3fa 100644
--- a/src/api/na-core-utils.h
+++ b/src/api/na-core-utils.h
@@ -55,6 +55,7 @@ gchar   *na_core_utils_str_remove_suffix( const gchar *string, const gchar *suff
 
 /* some functions to get or set GSList list of strings
  */
+void     na_core_utils_slist_add_message( GSList **list, const gchar *format, ... );
 GSList  *na_core_utils_slist_duplicate( GSList *list );
 void     na_core_utils_slist_dump( const gchar *prefix, GSList *list );
 GSList  *na_core_utils_slist_from_array( const gchar **str_array );
@@ -86,8 +87,9 @@ void     na_core_utils_dir_split_ext( const gchar *string, gchar **first, gchar
 
 /* file management
  */
-gboolean na_core_utils_file_delete( const gchar *path );
-gboolean na_core_utils_file_exists( const gchar *path );
+gboolean na_core_utils_file_delete       ( const gchar *path );
+gboolean na_core_utils_file_exists       ( const gchar *path );
+gchar   *na_core_utils_file_load_from_uri( const gchar *uri, gsize *length );
 
 /* miscellaneous
  */
diff --git a/src/api/na-iimporter.h b/src/api/na-iimporter.h
index 7a5aace..01e4705 100644
--- a/src/api/na-iimporter.h
+++ b/src/api/na-iimporter.h
@@ -50,10 +50,11 @@ G_BEGIN_DECLS
 #define NA_IS_IIMPORTER( instance )				( G_TYPE_CHECK_INSTANCE_TYPE( instance, NA_IIMPORTER_TYPE ))
 #define NA_IIMPORTER_GET_INTERFACE( instance )	( G_TYPE_INSTANCE_GET_INTERFACE(( instance ), NA_IIMPORTER_TYPE, NAIImporterInterface ))
 
-typedef struct NAIImporter                   NAIImporter;
-typedef struct NAIImporterInterfacePrivate   NAIImporterInterfacePrivate;
+typedef struct NAIImporter                      NAIImporter;
+typedef struct NAIImporterInterfacePrivate      NAIImporterInterfacePrivate;
 
-typedef struct NAIImporterImportFromUriParms NAIImporterImportFromUriParms;
+typedef struct NAIImporterImportFromUriParms    NAIImporterImportFromUriParms;
+typedef struct NAIImporterManageImportModeParms NAIImporterManageImportModeParms;
 
 typedef struct {
 	GTypeInterface               parent;
@@ -155,10 +156,30 @@ struct NAIImporterImportFromUriParms {
 										 *       but shouldn't reinitialize it. */
 };
 
+/*
+ * parameters used when managing import mode
+ */
+struct NAIImporterManageImportModeParms {
+	guint                version;		/* i 1: version of this structure */
+	NAObjectItem        *imported;		/* i 1: the imported NAObjectItem-derived object */
+	guint                asked_mode;	/* i 1: asked import mode */
+	NAIImporterCheckFn   check_fn;		/* i 1: a function to check the existence of the imported id */
+	void                *check_fn_data;	/* i 1: data function */
+	NAIImporterAskUserFn ask_fn;		/* i 1: a function to ask the user what to do in case of a duplicate id */
+	void                *ask_fn_data;	/* i 1: data function */
+	gboolean             exist;			/*  o1: whether the imported Id already existed */
+	guint                import_mode;	/*  o1: actually used import mode */
+	GSList              *messages;		/* io1: a #GSList list of localized strings;
+										 *       the provider may append messages to this list,
+										 *       but shouldn't reinitialize it. */
+};
+
 GType na_iimporter_get_type( void );
 
 guint na_iimporter_import_from_uri( const NAIImporter *importer, NAIImporterImportFromUriParms *parms );
 
+guint na_iimporter_manage_import_mode( NAIImporterManageImportModeParms *parms );
+
 G_END_DECLS
 
 #endif /* __NAUTILUS_ACTIONS_API_NA_IIMPORTER_H__ */
diff --git a/src/core/na-core-utils.c b/src/core/na-core-utils.c
index f56af6a..e276f42 100644
--- a/src/core/na-core-utils.c
+++ b/src/core/na-core-utils.c
@@ -209,6 +209,19 @@ na_core_utils_str_remove_suffix( const gchar *string, const gchar *suffix )
 	return( removed );
 }
 
+void
+na_core_utils_slist_add_message( GSList **messages, const gchar *format, ... )
+{
+	va_list va;
+	gchar *tmp;
+
+	va_start( va, format );
+	tmp = g_markup_vprintf_escaped( format, va );
+	va_end( va );
+
+	*messages = g_slist_append( *messages, tmp );
+}
+
 /**
  * na_core_utils_slist_duplicate:
  * @source_slist: the #GSList to be duplicated.
@@ -869,6 +882,38 @@ na_core_utils_file_exists( const gchar *fname )
 }
 
 /**
+ * na_core_utils_file_load_from_uri:
+ * @uri: the URI the file must be loaded from.
+ * @length: a pointer to the length of the readen content.
+ *
+ * Loads the file into a newly allocated buffer, and set up the length of the
+ * readen content if not %NULL.
+ *
+ * Returns: the newly allocated buffer which contains the file content, or %NULL.
+ * This buffer should be g_free() by the caller.
+ */
+gchar *
+na_core_utils_file_load_from_uri( const gchar *uri, gsize *length )
+{
+	gchar *data;
+	GFile *file;
+
+	file = g_file_new_for_uri( uri );
+
+	if( !g_file_load_contents( file, NULL, &data, length, NULL, NULL )){
+		g_free( data );
+		data = NULL;
+		if( length ){
+			*length = 0;
+		}
+	}
+
+	g_object_unref( file );
+
+	return( data );
+}
+
+/**
  * na_core_utils_print_version:
  *
  * Print a version message on the console
diff --git a/src/core/na-iimporter.c b/src/core/na-iimporter.c
index f57eebd..482881b 100644
--- a/src/core/na-iimporter.c
+++ b/src/core/na-iimporter.c
@@ -32,7 +32,11 @@
 #include <config.h>
 #endif
 
+#include <glib/gi18n.h>
+
+#include <api/na-core-utils.h>
 #include <api/na-iimporter.h>
+#include <api/na-object-api.h>
 
 /* private interface data
  */
@@ -49,6 +53,8 @@ static void   interface_base_finalize( NAIImporterInterface *klass );
 
 static guint  iimporter_get_version( const NAIImporter *instance );
 
+static void   renumber_label_item( NAIImporterManageImportModeParms *parms );
+
 /**
  * na_iimporter_get_type:
  *
@@ -169,3 +175,108 @@ na_iimporter_import_from_uri( const NAIImporter *importer, NAIImporterImportFrom
 
 	return( code );
 }
+
+/*
+ * Returns IMPORTER_CODE_OK if we can safely insert the action
+ * - the id doesn't already exist
+ * - the id already exist, but import mode is renumber
+ * - the id already exists, but import mode is override
+ *
+ * Returns IMPORTER_CODE_CANCELLED if user chooses to cancel the operation
+ */
+guint
+na_iimporter_manage_import_mode( NAIImporterManageImportModeParms *parms )
+{
+	guint code;
+	NAObjectItem *exists;
+	guint mode;
+	gchar *id;
+
+	g_return_val_if_fail( parms->imported != NULL, IMPORTER_CODE_CANCELLED );
+
+	code = IMPORTER_CODE_OK;
+	exists = NULL;
+	mode = 0;
+
+	if( parms->check_fn ){
+		exists = ( *parms->check_fn )( parms->imported, parms->check_fn_data );
+
+	} else {
+		renumber_label_item( parms );
+		na_core_utils_slist_add_message( &parms->messages, "%s", _( "Item was renumbered because the caller did not provide any check function." ));
+		parms->import_mode = IMPORTER_MODE_RENUMBER;
+	}
+
+	if( exists ){
+		parms->exist = TRUE;
+
+		if( parms->asked_mode == IMPORTER_MODE_ASK ){
+			if( parms->ask_fn ){
+				mode = ( *parms->ask_fn )( parms->imported, exists, parms->ask_fn_data );
+
+			} else {
+				renumber_label_item( parms );
+				na_core_utils_slist_add_message( &parms->messages, "%s", _( "Item was renumbered because the caller did not provide any ask user function." ));
+				parms->import_mode = IMPORTER_MODE_RENUMBER;
+			}
+
+		} else {
+			mode = parms->asked_mode;
+		}
+	}
+
+	/* mode is only set if asked mode is ask user and an ask function was provided
+	 * or if asked mode was not ask user
+	 */
+	if( mode ){
+		parms->import_mode = mode;
+
+		switch( mode ){
+			case IMPORTER_MODE_RENUMBER:
+				renumber_label_item( parms );
+				if( parms->asked_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message( &parms->messages, "%s", _( "Item was renumbered due to user request." ));
+				}
+				break;
+
+			case IMPORTER_MODE_OVERRIDE:
+				if( parms->asked_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message( &parms->messages, "%s", _( "Existing item was overriden due to user request." ));
+				}
+				break;
+
+			case IMPORTER_MODE_NO_IMPORT:
+			default:
+				id = na_object_get_id( parms->imported );
+				na_core_utils_slist_add_message( &parms->messages, _( "Item %s already exists." ), id );
+				if( parms->asked_mode == IMPORTER_MODE_ASK ){
+					na_core_utils_slist_add_message( &parms->messages, "%s", _( "Import was canceled due to user request." ));
+				}
+				g_free( id );
+				code = IMPORTER_CODE_CANCELLED;
+		}
+	}
+
+	return( code );
+}
+
+/*
+ * renumber the item, and set a new label
+ */
+static void
+renumber_label_item( NAIImporterManageImportModeParms *parms )
+{
+	gchar *label, *tmp;
+
+	na_object_set_new_id( parms->imported, NULL );
+
+	label = na_object_get_label( parms->imported );
+
+	/* i18n: the action has been renumbered during import operation */
+	tmp = g_strdup_printf( "%s %s", label, _( "(renumbered)" ));
+
+	na_object_set_label( parms->imported, tmp );
+
+	g_free( tmp );
+	g_free( label );
+}
diff --git a/src/io-desktop/Makefile.am b/src/io-desktop/Makefile.am
index d872003..da57973 100644
--- a/src/io-desktop/Makefile.am
+++ b/src/io-desktop/Makefile.am
@@ -40,6 +40,8 @@ libna_io_desktop_la_SOURCES = \
 	nadp-desktop-file.h									\
 	nadp-desktop-provider.c								\
 	nadp-desktop-provider.h								\
+	nadp-formats.c										\
+	nadp-formats.h										\
 	nadp-keys.c											\
 	nadp-keys.h											\
 	nadp-module.c										\
diff --git a/src/io-desktop/nadp-desktop-file.c b/src/io-desktop/nadp-desktop-file.c
index 193aee1..e44b00f 100644
--- a/src/io-desktop/nadp-desktop-file.c
+++ b/src/io-desktop/nadp-desktop-file.c
@@ -52,7 +52,7 @@ struct NadpDesktopFileClassPrivate {
 struct NadpDesktopFilePrivate {
 	gboolean   dispose_has_run;
 	gchar     *id;
-	gchar     *path;
+	gchar     *uri;
 	GKeyFile  *key_file;
 };
 
@@ -64,8 +64,9 @@ static void             instance_init( GTypeInstance *instance, gpointer klass )
 static void             instance_dispose( GObject *object );
 static void             instance_finalize( GObject *object );
 
-static NadpDesktopFile *ndf_new( const gchar *path );
+static NadpDesktopFile *ndf_new( const gchar *uri );
 static gchar           *path2id( const gchar *path );
+static gchar           *uri2id( const gchar *uri );
 static gboolean         check_key_file( NadpDesktopFile *ndf );
 
 GType
@@ -169,7 +170,7 @@ instance_finalize( GObject *object )
 	self = NADP_DESKTOP_FILE( object );
 
 	g_free( self->private->id );
-	g_free( self->private->path );
+	g_free( self->private->uri );
 
 	if( self->private->key_file ){
 		g_key_file_free( self->private->key_file );
@@ -197,14 +198,25 @@ nadp_desktop_file_new_from_path( const gchar *path )
 	static const gchar *thisfn = "nadp_desktop_file_new_from_path";
 	NadpDesktopFile *ndf;
 	GError *error;
+	gchar *uri;
 
 	ndf = NULL;
 	g_debug( "%s: path=%s", thisfn, path );
 	g_return_val_if_fail( path && g_utf8_strlen( path, -1 ) && g_path_is_absolute( path ), ndf );
 
-	ndf = ndf_new( path );
-
 	error = NULL;
+	uri = g_filename_to_uri( path, NULL, &error );
+	if( !uri || error ){
+		g_warning( "%s: %s: %s", thisfn, path, error->message );
+		g_error_free( error );
+		g_free( uri );
+		return( NULL );
+	}
+
+	ndf = ndf_new( uri );
+
+	g_free( uri );
+
 	g_key_file_load_from_file( ndf->private->key_file, path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error );
 	if( error ){
 		g_warning( "%s: %s: %s", thisfn, path, error->message );
@@ -222,6 +234,50 @@ nadp_desktop_file_new_from_path( const gchar *path )
 }
 
 /**
+ * nadp_desktop_file_new_from_uri:
+ * @uri: the URI the desktop file should be loaded from.
+ *
+ * Retuns: a newly allocated #NadpDesktopFile object, or %NULL.
+ *
+ * Key file has been loaded, and first validity checks made.
+ */
+NadpDesktopFile *
+nadp_desktop_file_new_from_uri( const gchar *uri )
+{
+	static const gchar *thisfn = "nadp_desktop_file_new_from_uri";
+	NadpDesktopFile *ndf;
+	GError *error;
+	gchar *data;
+	gsize length;
+
+	ndf = NULL;
+	g_debug( "%s: uri=%s", thisfn, uri );
+	g_return_val_if_fail( uri && g_utf8_strlen( uri, -1 ), ndf );
+
+	ndf = ndf_new( uri );
+	data = na_core_utils_file_load_from_uri( uri, &length );
+
+	error = NULL;
+	g_key_file_load_from_data( ndf->private->key_file, data, length, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error );
+	if( error ){
+		g_warning( "%s: %s", thisfn, error->message );
+		g_error_free( error );
+		g_object_unref( ndf );
+		g_free( data );
+		return( NULL );
+	}
+
+	g_free( data );
+
+	if( !check_key_file( ndf )){
+		g_object_unref( ndf );
+		return( NULL );
+	}
+
+	return( ndf );
+}
+
+/**
  * nadp_desktop_file_new_for_write:
  * @path: the full pathname of a .desktop file.
  *
@@ -232,66 +288,67 @@ nadp_desktop_file_new_for_write( const gchar *path )
 {
 	static const gchar *thisfn = "nadp_desktop_file_new_for_write";
 	NadpDesktopFile *ndf;
+	GError *error;
+	gchar *uri;
 
 	ndf = NULL;
 	g_debug( "%s: path=%s", thisfn, path );
 	g_return_val_if_fail( path && g_utf8_strlen( path, -1 ) && g_path_is_absolute( path ), ndf );
 
-	ndf = ndf_new( path );
+	error = NULL;
+	uri = g_filename_to_uri( path, NULL, &error );
+	if( !uri || error ){
+		g_warning( "%s: %s: %s", thisfn, path, error->message );
+		g_error_free( error );
+		g_free( uri );
+		return( NULL );
+	}
+
+	ndf = ndf_new( uri );
+
+	g_free( uri );
 
 	return( ndf );
 }
 
 /**
- * nadp_desktop_file_get_key_file_path:
+ * nadp_desktop_file_get_key_file_uri:
  * @ndf: the #NadpDesktopFile instance.
  *
- * Returns: the full pathname of the key file, as a newly allocated
+ * Returns: the URI of the key file, as a newly allocated
  * string which should be g_free() by the caller.
  */
 gchar *
-nadp_desktop_file_get_key_file_path( const NadpDesktopFile *ndf )
+nadp_desktop_file_get_key_file_uri( const NadpDesktopFile *ndf )
 {
-	gchar *path;
+	gchar *uri;
 
 	g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf ), NULL );
 
-	path = NULL;
+	uri = NULL;
 
 	if( !ndf->private->dispose_has_run ){
 
-		path = g_strdup( ndf->private->path );
+		uri = g_strdup( ndf->private->uri );
 	}
 
-	return( path );
+	return( uri );
 }
 
-/*
- * ndf_new:
- * @path: the full pathname of a .desktop file.
- *
- * Retuns: a newly allocated #NadpDesktopFile object.
- */
 static NadpDesktopFile *
-ndf_new( const gchar *path )
+ndf_new( const gchar *uri )
 {
 	NadpDesktopFile *ndf;
 
 	ndf = g_object_new( NADP_DESKTOP_FILE_TYPE, NULL );
 
-	ndf->private->id = path2id( path );
-	ndf->private->path = g_strdup( path );
+	ndf->private->id = uri2id( uri );
+	ndf->private->uri = g_strdup( uri );
 
 	return( ndf );
 }
 
 /*
- * path2id:
- * @path: a full pathname.
- *
- * Returns: the id of the file, as a newly allocated string which
- * should be g_free() by the caller.
- *
  * The id of the file is equal to the basename, minus the suffix.
  */
 static gchar *
@@ -307,6 +364,23 @@ path2id( const gchar *path )
 	return( id );
 }
 
+static gchar *
+uri2id( const gchar *uri )
+{
+	gchar *path;
+	gchar *id;
+
+	id = NULL;
+	path = g_filename_from_uri( uri, NULL, NULL );
+
+	if( path ){
+		id = path2id( path );
+		g_free( path );
+	}
+
+	return( id );
+}
+
 static gboolean
 check_key_file( NadpDesktopFile *ndf )
 {
@@ -320,11 +394,11 @@ check_key_file( NadpDesktopFile *ndf )
 	ret = TRUE;
 	error = NULL;
 
-	/* start group must be 'Desktop Entry' */
+	/* start group must be [Desktop Entry] */
 	start_group = g_key_file_get_start_group( ndf->private->key_file );
 	if( strcmp( start_group, NADP_GROUP_DESKTOP )){
 		g_warning( "%s: %s: invalid start group, found %s, waited for %s",
-				thisfn, ndf->private->path, start_group, NADP_GROUP_DESKTOP );
+				thisfn, ndf->private->uri, start_group, NADP_GROUP_DESKTOP );
 		ret = FALSE;
 	}
 
@@ -332,17 +406,17 @@ check_key_file( NadpDesktopFile *ndf )
 	if( ret ){
 		has_key = g_key_file_has_key( ndf->private->key_file, start_group, NADP_KEY_HIDDEN, &error );
 		if( error ){
-			g_warning( "%s: %s: %s", thisfn, ndf->private->path, error->message );
+			g_warning( "%s: %s: %s", thisfn, ndf->private->uri, error->message );
 			ret = FALSE;
 
 		} else if( has_key ){
 			hidden = g_key_file_get_boolean( ndf->private->key_file, start_group, NADP_KEY_HIDDEN, &error );
 			if( error ){
-				g_warning( "%s: %s: %s", thisfn, ndf->private->path, error->message );
+				g_warning( "%s: %s: %s", thisfn, ndf->private->uri, error->message );
 				ret = FALSE;
 
 			} else if( hidden ){
-				g_debug( "%s: %s: Hidden=true", thisfn, ndf->private->path );
+				g_debug( "%s: %s: Hidden=true", thisfn, ndf->private->uri );
 				ret = FALSE;
 			}
 		}
@@ -917,7 +991,7 @@ nadp_desktop_file_write( NadpDesktopFile *ndf )
 	if( !ndf->private->dispose_has_run ){
 
 		data = g_key_file_to_data( ndf->private->key_file, NULL, NULL );
-		file = g_file_new_for_path( ndf->private->path );
+		file = g_file_new_for_uri( ndf->private->uri );
 
 		stream = g_file_replace( file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error );
 		if( error ){
diff --git a/src/io-desktop/nadp-desktop-file.h b/src/io-desktop/nadp-desktop-file.h
index e2bcf3b..812d019 100644
--- a/src/io-desktop/nadp-desktop-file.h
+++ b/src/io-desktop/nadp-desktop-file.h
@@ -75,9 +75,10 @@ typedef struct {
 GType            nadp_desktop_file_get_type         ( void );
 
 NadpDesktopFile *nadp_desktop_file_new_from_path    ( const gchar *path );
+NadpDesktopFile *nadp_desktop_file_new_from_uri     ( const gchar *uri );
 NadpDesktopFile *nadp_desktop_file_new_for_write    ( const gchar *path );
 
-gchar           *nadp_desktop_file_get_key_file_path( const NadpDesktopFile *ndf );
+gchar           *nadp_desktop_file_get_key_file_uri ( const NadpDesktopFile *ndf );
 gboolean         nadp_desktop_file_write            ( NadpDesktopFile *ndf );
 
 gchar           *nadp_desktop_file_get_file_type    ( const NadpDesktopFile *ndf );
diff --git a/src/io-desktop/nadp-desktop-provider.c b/src/io-desktop/nadp-desktop-provider.c
index 5014995..0a198b5 100644
--- a/src/io-desktop/nadp-desktop-provider.c
+++ b/src/io-desktop/nadp-desktop-provider.c
@@ -36,7 +36,6 @@
 #include <string.h>
 
 #include <api/na-core-utils.h>
-#include <api/na-iio-provider.h>
 #include <api/na-ifactory-provider.h>
 
 #include "nadp-desktop-provider.h"
@@ -51,26 +50,36 @@ struct NadpDesktopProviderClassPrivate {
 	void *empty;						/* so that gcc -pedantic is happy */
 };
 
+extern NAIExporterFormat nadp_formats[];
+
 static GType         st_module_type = 0;
 static GObjectClass *st_parent_class = NULL;
 static guint         st_timeout_msec = 100;
 static guint         st_timeout_usec = 100000;
 
-static void     class_init( NadpDesktopProviderClass *klass );
-static void     instance_init( GTypeInstance *instance, gpointer klass );
-static void     instance_dispose( GObject *object );
-static void     instance_finalize( GObject *object );
+static void                     class_init( NadpDesktopProviderClass *klass );
+static void                     instance_init( GTypeInstance *instance, gpointer klass );
+static void                     instance_dispose( GObject *object );
+static void                     instance_finalize( GObject *object );
+
+static void                     iio_provider_iface_init( NAIIOProviderInterface *iface );
+static gchar                   *iio_provider_get_id( const NAIIOProvider *provider );
+static gchar                   *iio_provider_get_name( const NAIIOProvider *provider );
+static guint                    iio_provider_get_version( const NAIIOProvider *provider );
+
+static void                     ifactory_provider_iface_init( NAIFactoryProviderInterface *iface );
+static guint                    ifactory_provider_get_version( const NAIFactoryProvider *reader );
 
-static void     iio_provider_iface_init( NAIIOProviderInterface *iface );
-static gchar   *iio_provider_get_id( const NAIIOProvider *provider );
-static gchar   *iio_provider_get_name( const NAIIOProvider *provider );
-static guint    iio_provider_get_version( const NAIIOProvider *provider );
+static void                     iimporter_iface_init( NAIImporterInterface *iface );
+static guint                    iimporter_get_version( const NAIImporter *importer );
 
-static void     ifactory_provider_iface_init( NAIFactoryProviderInterface *iface );
-static guint    ifactory_provider_get_version( const NAIFactoryProvider *reader );
+static void                     iexporter_iface_init( NAIExporterInterface *iface );
+static guint                    iexporter_get_version( const NAIExporter *exporter );
+static gchar                   *iexporter_get_name( const NAIExporter *exporter );
+static const NAIExporterFormat *iexporter_get_formats( const NAIExporter *exporter );
 
-static gboolean on_monitor_timeout( NadpDesktopProvider *provider );
-static gulong   time_val_diff( const GTimeVal *recent, const GTimeVal *old );
+static gboolean                 on_monitor_timeout( NadpDesktopProvider *provider );
+static gulong                   time_val_diff( const GTimeVal *recent, const GTimeVal *old );
 
 GType
 nadp_desktop_provider_get_type( void )
@@ -107,6 +116,18 @@ nadp_desktop_provider_register_type( GTypeModule *module )
 		NULL
 	};
 
+	static const GInterfaceInfo iimporter_iface_info = {
+		( GInterfaceInitFunc ) iimporter_iface_init,
+		NULL,
+		NULL
+	};
+
+	static const GInterfaceInfo iexporter_iface_info = {
+		( GInterfaceInitFunc ) iexporter_iface_init,
+		NULL,
+		NULL
+	};
+
 	g_debug( "%s", thisfn );
 
 	st_module_type = g_type_module_register_type( module, G_TYPE_OBJECT, "NadpDesktopProvider", &info, 0 );
@@ -114,6 +135,10 @@ nadp_desktop_provider_register_type( GTypeModule *module )
 	g_type_module_add_interface( module, st_module_type, NA_IIO_PROVIDER_TYPE, &iio_provider_iface_info );
 
 	g_type_module_add_interface( module, st_module_type, NA_IFACTORY_PROVIDER_TYPE, &ifactory_provider_iface_info );
+
+	g_type_module_add_interface( module, st_module_type, NA_IIMPORTER_TYPE, &iimporter_iface_info );
+
+	g_type_module_add_interface( module, st_module_type, NA_IEXPORTER_TYPE, &iexporter_iface_info );
 }
 
 static void
@@ -247,6 +272,55 @@ ifactory_provider_get_version( const NAIFactoryProvider *reader )
 	return( 1 );
 }
 
+static void
+iimporter_iface_init( NAIImporterInterface *iface )
+{
+	static const gchar *thisfn = "nadp_desktop_provider_iimporter_iface_init";
+
+	g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+	iface->get_version = iimporter_get_version;
+	iface->import_from_uri = nadp_reader_iimporter_import_from_uri;
+}
+
+static guint
+iimporter_get_version( const NAIImporter *importer )
+{
+	return( 1 );
+}
+
+static void
+iexporter_iface_init( NAIExporterInterface *iface )
+{
+	static const gchar *thisfn = "nadp_desktop_iexporter_iface_init";
+
+	g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+	iface->get_version = iexporter_get_version;
+	iface->get_name = iexporter_get_name;
+	iface->get_formats = iexporter_get_formats;
+	iface->to_file = nadp_writer_iexporter_export_to_file;
+	iface->to_buffer = nadp_writer_iexporter_export_to_buffer;
+}
+
+static guint
+iexporter_get_version( const NAIExporter *exporter )
+{
+	return( 1 );
+}
+
+static gchar *
+iexporter_get_name( const NAIExporter *exporter )
+{
+	return( g_strdup( "NA Desktop Exporter" ));
+}
+
+static const NAIExporterFormat *
+iexporter_get_formats( const NAIExporter *exporter )
+{
+	return( nadp_formats );
+}
+
 /**
  * nadp_desktop_provider_add_monitor:
  * @provider: this #NadpDesktopProvider object.
diff --git a/src/io-desktop/nadp-reader.c b/src/io-desktop/nadp-reader.c
index fa79767..8847193 100644
--- a/src/io-desktop/nadp-reader.c
+++ b/src/io-desktop/nadp-reader.c
@@ -66,6 +66,7 @@ static void              get_list_of_desktop_files( const NadpDesktopProvider *p
 static gboolean          is_already_loaded( const NadpDesktopProvider *provider, GList *files, const gchar *desktop_id );
 static GList            *desktop_path_from_id( const NadpDesktopProvider *provider, GList *files, const gchar *dir, const gchar *id );
 static NAIFactoryObject *item_from_desktop_path( const NadpDesktopProvider *provider, DesktopPath *dps, GSList **messages );
+static NAIFactoryObject *item_from_desktop_file( const NadpDesktopProvider *provider, NadpDesktopFile *ndf, GSList **messages );
 static void              desktop_weak_notify( NadpDesktopFile *ndf, GObject *item );
 static void              free_desktop_paths( GList *paths );
 
@@ -251,18 +252,29 @@ desktop_path_from_id( const NadpDesktopProvider *provider, GList *files, const g
 static NAIFactoryObject *
 item_from_desktop_path( const NadpDesktopProvider *provider, DesktopPath *dps, GSList **messages )
 {
-	static const gchar *thisfn = "nadp_reader_item_from_desktop_path";
-	NAIFactoryObject *item;
 	NadpDesktopFile *ndf;
-	gchar *type;
-	NadpReaderData *reader_data;
-	gchar *id;
 
 	ndf = nadp_desktop_file_new_from_path( dps->path );
 	if( !ndf ){
 		return( NULL );
 	}
 
+	return( item_from_desktop_file( provider, ndf, messages ));
+}
+
+/*
+ * Returns a newly allocated NAIFactoryObject-derived object, initialized
+ * from the .desktop file
+ */
+static NAIFactoryObject *
+item_from_desktop_file( const NadpDesktopProvider *provider, NadpDesktopFile *ndf, GSList **messages )
+{
+	static const gchar *thisfn = "nadp_reader_item_from_desktop_file";
+	NAIFactoryObject *item;
+	gchar *type;
+	NadpReaderData *reader_data;
+	gchar *id;
+
 	item = NULL;
 	type = nadp_desktop_file_get_file_type( ndf );
 
@@ -323,6 +335,75 @@ free_desktop_paths( GList *paths )
 	g_list_free( paths );
 }
 
+/**
+ * nadp_reader_iimporter_import_from_uri:
+ * @instance: the #NAIImporter provider.
+ * @parms: a #NAIImporterUriParms structure.
+ *
+ * Imports an item.
+ *
+ * Returns: the import operation code.
+ *
+ * As soon as we have a valid .desktop file, we are most probably willing
+ * to successfully import an action or a menu of it.
+ *
+ * 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.
+ */
+guint
+nadp_reader_iimporter_import_from_uri( const NAIImporter *instance, NAIImporterImportFromUriParms *parms )
+{
+	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 );
+
+	g_return_val_if_fail( NA_IS_IIMPORTER( instance ), IMPORTER_CODE_PROGRAM_ERROR );
+	g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( instance ), IMPORTER_CODE_PROGRAM_ERROR );
+
+	code = IMPORTER_CODE_NOT_WILLING_TO;
+
+	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 );
+
+		if( parms->imported ){
+			code = IMPORTER_CODE_OK;
+			g_return_val_if_fail( NA_IS_OBJECT_ITEM( parms->imported ), IMPORTER_CODE_NOT_WILLING_TO );
+
+			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;
+		}
+
+		if( code != IMPORTER_CODE_OK ){
+			if( parms->imported ){
+				g_object_unref( parms->imported );
+				parms->imported = NULL;
+			}
+		}
+	}
+
+	return( code );
+}
+
 /*
  * at this time, the object has been allocated and its id has been set
  * read here the subitems key, which may be 'Profiles' or 'ItemsList'
@@ -533,13 +614,13 @@ static gboolean
 read_done_item_is_writable( const NAIFactoryProvider *provider, NAObjectItem *item, NadpReaderData *reader_data, GSList **messages )
 {
 	NadpDesktopFile *ndf;
-	gchar *path;
+	gchar *uri;
 	gboolean writable;
 
 	ndf = reader_data->ndf;
-	path = nadp_desktop_file_get_key_file_path( ndf );
-	writable = nadp_utils_is_writable_file( path );
-	g_free( path );
+	uri = nadp_desktop_file_get_key_file_uri( ndf );
+	writable = nadp_utils_uri_is_writable( uri );
+	g_free( uri );
 
 	return( writable );
 }
diff --git a/src/io-desktop/nadp-reader.h b/src/io-desktop/nadp-reader.h
index 4af4f92..673cd4f 100644
--- a/src/io-desktop/nadp-reader.h
+++ b/src/io-desktop/nadp-reader.h
@@ -32,10 +32,13 @@
 #define __NADP_READER_H__
 
 #include <api/na-iio-provider.h>
+#include <api/na-iimporter.h>
 
 G_BEGIN_DECLS
 
-GList       *nadp_iio_provider_read_items( const NAIIOProvider *provider, GSList **messages );
+GList       *nadp_iio_provider_read_items            ( const NAIIOProvider *provider, GSList **messages );
+
+guint        nadp_reader_iimporter_import_from_uri   ( const NAIImporter *instance, NAIImporterImportFromUriParms *parms );
 
 void         nadp_reader_ifactory_provider_read_start( const NAIFactoryProvider *reader, void *reader_data, const NAIFactoryObject *serializable, GSList **messages );
 NADataBoxed *nadp_reader_ifactory_provider_read_data ( const NAIFactoryProvider *reader, void *reader_data, const NAIFactoryObject *serializable, const NADataDef *iddef, GSList **messages );
diff --git a/src/io-desktop/nadp-utils.c b/src/io-desktop/nadp-utils.c
index 24e4ff4..455f7a4 100644
--- a/src/io-desktop/nadp-utils.c
+++ b/src/io-desktop/nadp-utils.c
@@ -35,6 +35,7 @@
 #include <errno.h>
 #include <gio/gio.h>
 #include <glib/gstdio.h>
+#include <string.h>
 #include <uuid/uuid.h>
 
 #include <api/na-core-utils.h>
@@ -67,8 +68,35 @@ nadp_utils_gslist_remove_from( GSList *list, const gchar *string )
 }
 
 /**
- * nadp_utils_is_writable_file:
- * @path: the path of the file to be tested.
+ *
+ */
+gboolean
+nadp_utils_uri_delete( const gchar *uri )
+{
+	gboolean deleted;
+	gchar *scheme;
+	gchar *path;
+
+	deleted = FALSE;
+	scheme = g_uri_parse_scheme( uri );
+
+	if( !strcmp( scheme, "file" )){
+		path = g_filename_from_uri( uri, NULL, NULL );
+
+		if( path ){
+			deleted = na_core_utils_file_delete( path );
+			g_free( path );
+		}
+	}
+
+	g_free( scheme );
+
+	return( deleted );
+}
+
+/**
+ * nadp_utils_uri_is_writable:
+ * @uri: the URI of the file to be tested.
  *
  * Returns: %TRUE if the file is writable, %FALSE else.
  *
@@ -79,19 +107,19 @@ nadp_utils_gslist_remove_from( GSList *list, const gchar *string )
  * There is no "super-test". Just try...
  */
 gboolean
-nadp_utils_is_writable_file( const gchar *path )
+nadp_utils_uri_is_writable( const gchar *uri )
 {
-	static const gchar *thisfn = "nadp_utils_is_writable_file";
+	static const gchar *thisfn = "nadp_utils_uri_is_writable";
 	GFile *file;
 	GError *error = NULL;
 	GFileInfo *info;
 	gboolean writable;
 
-	if( !path || !g_utf8_strlen( path, -1 )){
+	if( !uri || !g_utf8_strlen( uri, -1 )){
 		return( FALSE );
 	}
 
-	file = g_file_new_for_path( path );
+	file = g_file_new_for_uri( uri );
 	info = g_file_query_info( file,
 			G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
 			G_FILE_QUERY_INFO_NONE, NULL, &error );
@@ -105,8 +133,9 @@ nadp_utils_is_writable_file( const gchar *path )
 
 	writable = g_file_info_get_attribute_boolean( info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE );
 	if( !writable ){
-		g_debug( "%s: %s is not writable", thisfn, path );
+		g_debug( "%s: %s is not writable", thisfn, uri );
 	}
+
 	g_object_unref( info );
 
 	return( writable );
diff --git a/src/io-desktop/nadp-utils.h b/src/io-desktop/nadp-utils.h
index 6457146..2a06c7b 100644
--- a/src/io-desktop/nadp-utils.h
+++ b/src/io-desktop/nadp-utils.h
@@ -35,7 +35,8 @@ G_BEGIN_DECLS
 
 GSList  *nadp_utils_gslist_remove_from( GSList *list, const gchar *string );
 
-gboolean nadp_utils_is_writable_file( const gchar *path );
+gboolean nadp_utils_uri_delete     ( const gchar *uri );
+gboolean nadp_utils_uri_is_writable( const gchar *uri );
 
 G_END_DECLS
 
diff --git a/src/io-desktop/nadp-writer.c b/src/io-desktop/nadp-writer.c
index ec9266a..b8cbfa4 100644
--- a/src/io-desktop/nadp-writer.c
+++ b/src/io-desktop/nadp-writer.c
@@ -43,6 +43,7 @@
 #include "nadp-desktop-file.h"
 #include "nadp-desktop-provider.h"
 #include "nadp-keys.h"
+#include "nadp-utils.h"
 #include "nadp-writer.h"
 #include "nadp-xdg-dirs.h"
 
@@ -226,7 +227,7 @@ nadp_iio_provider_delete_item( const NAIIOProvider *provider, const NAObjectItem
 	guint ret;
 	NadpDesktopProvider *self;
 	NadpDesktopFile *ndf;
-	gchar *path;
+	gchar *uri;
 
 	g_debug( "%s: provider=%p (%s), item=%p (%s), messages=%p",
 			thisfn,
@@ -250,11 +251,11 @@ nadp_iio_provider_delete_item( const NAIIOProvider *provider, const NAObjectItem
 
 	if( ndf ){
 		g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf ), ret );
-		path = nadp_desktop_file_get_key_file_path( ndf );
-		if( na_core_utils_file_delete( path )){
+		uri = nadp_desktop_file_get_key_file_uri( ndf );
+		if( nadp_utils_uri_delete( uri )){
 			ret = NA_IIO_PROVIDER_CODE_OK;
 		}
-		g_free( path );
+		g_free( uri );
 
 	} else {
 		g_warning( "%s: NadpDesktopFile is null", thisfn );
@@ -317,47 +318,117 @@ nadp_iio_provider_duplicate_data( const NAIIOProvider *provider, NAObjectItem *d
 	return( NA_IIO_PROVIDER_CODE_OK );
 }
 
-#if 0
-/*
- * the item comes from being readen from a desktop file
- * -> see if this desktop file is writable ?
+/**
+ * nadp_writer_iexporter_export_to_buffer:
+ * @instance: this #NAIExporter instance.
+ * @parms: a #NAIExporterBufferParms structure.
  *
- * This is only used to setup the 'read-only' initial status of the
- * NAObjectItem - We don't care of all events which can suddenly make
- * this item becomes readonly (eventually we will deal for errors,
- * and reset the flag at this time)
+ * Export the specified 'item' to a newly allocated buffer.
+ */
+guint
+nadp_writer_iexporter_export_to_buffer( const NAIExporter *instance, NAIExporterBufferParms *parms )
+{
+	static const gchar *thisfn = "nadp_writer_iexporter_export_to_buffer";
+	guint code;
+
+	g_debug( "%s: instance=%p, parms=%p", thisfn, ( void * ) instance, ( void * ) parms );
+
+	code = NA_IEXPORTER_CODE_OK;
+	/*
+	NAXMLWriter *writer;
+
+	if( !parms->exported || !NA_IS_OBJECT_ITEM( parms->exported )){
+		code = NA_IEXPORTER_CODE_INVALID_ITEM;
+	}
+
+	if( code == NA_IEXPORTER_CODE_OK ){
+		writer = NAXML_WRITER( g_object_new( NAXML_WRITER_TYPE, NULL ));
+
+		writer->private->provider = ( NAIExporter * ) instance;
+		writer->private->exported = parms->exported;
+		writer->private->messages = parms->messages;
+		writer->private->fn_str = find_export_format_fn( parms->format );
+		writer->private->buffer = NULL;
+
+		if( !writer->private->fn_str ){
+			code = NA_IEXPORTER_CODE_INVALID_FORMAT;
+
+		} else {
+			code = writer_to_buffer( writer );
+			if( code == NA_IEXPORTER_CODE_OK ){
+				parms->buffer = writer->private->buffer;
+			}
+		}
+
+		g_object_unref( writer );
+	}
+	*/
+
+	g_debug( "%s: returning code=%u", thisfn, code );
+	return( code );
+}
+
+/**
+ * nadp_writer_iexporter_export_to_file:
+ * @instance: this #NAIExporter instance.
+ * @parms: a #NAIExporterFileParms structure.
  *
- * Internal function: do not call from outside the instance.
+ * Export the specified 'item' to a newly created file.
  */
-gboolean
-nadp_writer_desktop_is_writable( const NAIIOProvider *provider, const NAObjectItem *item )
+guint
+nadp_writer_iexporter_export_to_file( const NAIExporter *instance, NAIExporterFileParms *parms )
 {
-	static const gchar *thisfn = "nadp_writer_desktop_is_writable";
-	gboolean writable;
-	NadpDesktopFile *ndf;
-	gchar *path;
+	static const gchar *thisfn = "nadp_writer_iexporter_export_to_file";
+	guint code;
 
-	writable = FALSE;
-	g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider ), writable );
-	g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), writable );
+	g_debug( "%s: instance=%p, parms=%p", thisfn, ( void * ) instance, ( void * ) parms );
 
-	if( NA_IS_OBJECT_MENU( item )){
-		g_warning( "%s: menu are not yet handled by Desktop provider", thisfn );
-		return( FALSE );
+	code = NA_IEXPORTER_CODE_OK;
+
+	/*
+	NAXMLWriter *writer;
+	gchar *filename;
+
+	if( !parms->exported || !NA_IS_OBJECT_ITEM( parms->exported )){
+		code = NA_IEXPORTER_CODE_INVALID_ITEM;
 	}
 
-	ndf = ( NadpDesktopFile * ) na_object_get_provider_data( item );
+	if( code == NA_IEXPORTER_CODE_OK ){
+		writer = NAXML_WRITER( g_object_new( NAXML_WRITER_TYPE, NULL ));
 
-	if( ndf ){
-		g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf ), writable );
-		path = nadp_desktop_file_get_key_file_path( ndf );
-		writable = nadp_utils_is_writable_file( path );
-		g_free( path );
+		writer->private->provider = ( NAIExporter * ) instance;
+		writer->private->exported = parms->exported;
+		writer->private->messages = parms->messages;
+		writer->private->fn_str = find_export_format_fn( parms->format );
+		writer->private->buffer = NULL;
+
+		if( !writer->private->fn_str ){
+			code = NA_IEXPORTER_CODE_INVALID_FORMAT;
+
+		} else {
+			code = writer_to_buffer( writer );
+
+			if( code == NA_IEXPORTER_CODE_OK ){
+				filename = get_output_fname( parms->exported, parms->folder, parms->format );
+
+				if( filename ){
+					parms->basename = g_path_get_basename( filename );
+					output_xml_to_file(
+							writer->private->buffer, filename, parms->messages ? &writer->private->messages : NULL );
+					g_free( filename );
+				}
+			}
+
+			g_free( writer->private->buffer );
+		}
+
+		g_object_unref( writer );
 	}
+	*/
 
-	return( writable );
+	g_debug( "%s: returning code=%u", thisfn, code );
+	return( code );
 }
-#endif
 
 guint
 nadp_writer_ifactory_provider_write_start( const NAIFactoryProvider *provider, void *writer_data,
diff --git a/src/io-desktop/nadp-writer.h b/src/io-desktop/nadp-writer.h
index 6dfe1a9..af96b13 100644
--- a/src/io-desktop/nadp-writer.h
+++ b/src/io-desktop/nadp-writer.h
@@ -32,15 +32,19 @@
 #define __NADP_WRITER_H__
 
 #include <api/na-iio-provider.h>
+#include <api/na-iexporter.h>
 
 G_BEGIN_DECLS
 
-gboolean nadp_iio_provider_is_willing_to_write( const NAIIOProvider *provider );
-gboolean nadp_iio_provider_is_able_to_write   ( const NAIIOProvider *provider );
+gboolean nadp_iio_provider_is_willing_to_write ( const NAIIOProvider *provider );
+gboolean nadp_iio_provider_is_able_to_write    ( const NAIIOProvider *provider );
 
-guint    nadp_iio_provider_write_item    ( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages );
-guint    nadp_iio_provider_delete_item   ( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages );
-guint    nadp_iio_provider_duplicate_data( const NAIIOProvider *provider, NAObjectItem *dest, const NAObjectItem *source, GSList **messages );
+guint    nadp_iio_provider_write_item          ( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages );
+guint    nadp_iio_provider_delete_item         ( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages );
+guint    nadp_iio_provider_duplicate_data      ( const NAIIOProvider *provider, NAObjectItem *dest, const NAObjectItem *source, GSList **messages );
+
+guint    nadp_writer_iexporter_export_to_buffer( const NAIExporter *instance, NAIExporterBufferParms *parms );
+guint    nadp_writer_iexporter_export_to_file  ( const NAIExporter *instance, NAIExporterFileParms *parms );
 
 guint    nadp_writer_ifactory_provider_write_start(
 				const NAIFactoryProvider *provider, void *writer_data, const NAIFactoryObject *object,
diff --git a/src/io-xml/naxml-formats.c b/src/io-xml/naxml-formats.c
index 519fafb..f6ffd1f 100644
--- a/src/io-xml/naxml-formats.c
+++ b/src/io-xml/naxml-formats.c
@@ -49,6 +49,7 @@ NAIExporterFormat naxml_formats[] = {
 			N_( "This used to be the historical export format.\n" \
 				"The exported schema file may later be imported via :\n" \
 				"- Import assistant of the Nautilus Actions Configuration Tool,\n" \
+				"- drag-n-drop into the Nautilus Actions Configuration Tool,\n" \
 				"- or via the gconftool-2 --import-schema-file command-line tool." ) },
 
 	/* GCONF_SCHEMA_V2: the lightest schema still compatible with gconftool-2 --install-schema-file
@@ -62,6 +63,7 @@ NAIExporterFormat naxml_formats[] = {
 				"Tool versions.\n"
 				"The exported schema file may later be imported via :\n" \
 				"- Import assistant of the Nautilus Actions Configuration Tool,\n" \
+				"- drag-n-drop into the Nautilus Actions Configuration Tool,\n" \
 				"- or via the gconftool-2 --import-schema-file command-line tool." ) },
 
 	/* GCONF_ENTRY: not a schema, but a dump of the GConf entry
@@ -76,6 +78,7 @@ NAIExporterFormat naxml_formats[] = {
 				"though it may still be imported via standard GConf command-line tools.\n" \
 				"The exported dump file may later be imported via :\n" \
 				"- Import assistant of a compatible Nautilus Actions Configuration Tool,\n" \
+				"- drag-n-drop into the Nautilus Actions Configuration Tool,\n" \
 				"- or via the gconftool-2 --load command-line tool." ) },
 
 	{ NULL }
diff --git a/src/io-xml/naxml-reader.c b/src/io-xml/naxml-reader.c
index 7367144..060395d 100644
--- a/src/io-xml/naxml-reader.c
+++ b/src/io-xml/naxml-reader.c
@@ -144,7 +144,6 @@ static RootNodeStr st_root_node_str[] = {
 	{ NULL }
 };
 
-#define ERR_ITEM_ID_ALREADY_EXISTS	_( "Item ID %s already exists." )
 #define ERR_ITEM_ID_NOT_FOUND		_( "Item ID not found." )
 #define ERR_MENU_UNWAITED			_( "Unwaited key path %s while importing a menu." )
 #define ERR_NODE_ALREADY_FOUND		_( "Element %s at line %d already found, ignored." )
@@ -167,7 +166,6 @@ static guint         reader_parse_xmldoc( NAXMLReader *reader );
 static guint         iter_on_root_children( NAXMLReader *reader, xmlNode *root );
 static guint         iter_on_list_children( NAXMLReader *reader, xmlNode *first );
 
-static void          add_message( NAXMLReader *reader, const gchar *format, ... );
 static gchar        *build_key_node_list( NAXMLKeyStr *strlist );
 static gchar        *build_root_node_list( void );
 static gchar        *get_value_from_child_node( xmlNode *node, const gchar *child );
@@ -175,7 +173,6 @@ static gchar        *get_value_from_child_child_node( xmlNode *node, const gchar
 static gboolean      is_profile_path( NAXMLReader *reader, xmlChar *text );
 static guint         manage_import_mode( NAXMLReader *reader );
 static void          publish_undealt_nodes( NAXMLReader *reader );
-static void          renumber_label_item( 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 );
@@ -371,7 +368,7 @@ reader_parse_xmldoc( NAXMLReader *reader )
 
 	if( !doc ){
 		xmlErrorPtr error = xmlGetLastError();
-		add_message( reader,
+		na_core_utils_slist_add_message( &reader->private->parms->messages,
 				ERR_XMLDOC_UNABLE_TOPARSE, error->message );
 		xmlResetError( error );
 		code = IMPORTER_CODE_NOT_WILLING_TO;
@@ -393,7 +390,7 @@ reader_parse_xmldoc( NAXMLReader *reader )
 
 		if( !found ){
 			gchar *node_list = build_root_node_list();
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 						ERR_ROOT_UNKNOWN,
 						( const char * ) root_node->name, root_node->line, node_list );
 			g_free( node_list );
@@ -441,14 +438,14 @@ iter_on_root_children( NAXMLReader *reader, xmlNode *root )
 		}
 
 		if( strxcmp( iter->name, reader->private->root_node_str->list_key )){
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_UNKNOWN,
 					( const char * ) iter->name, iter->line, reader->private->root_node_str->list_key );
 			continue;
 		}
 
 		if( found ){
-			add_message( reader, ERR_NODE_ALREADY_FOUND, ( const char * ) iter->name, iter->line );
+			na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_ALREADY_FOUND, ( const char * ) iter->name, iter->line );
 			continue;
 		}
 
@@ -515,7 +512,7 @@ iter_on_list_children( NAXMLReader *reader, xmlNode *list )
 		}
 
 		if( strxcmp( iter->name, reader->private->root_node_str->element_key )){
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_UNKNOWN,
 					( const char * ) iter->name, iter->line, reader->private->root_node_str->element_key );
 			continue;
@@ -546,7 +543,7 @@ iter_on_list_children( NAXMLReader *reader, xmlNode *list )
 	 */
 	if( code == IMPORTER_CODE_OK ){
 		if( !reader->private->item_id || !strlen( reader->private->item_id )){
-			add_message( reader, ERR_ITEM_ID_NOT_FOUND );
+			na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_ITEM_ID_NOT_FOUND );
 			code = 	IMPORTER_CODE_NO_ITEM_ID;
 		}
 	}
@@ -880,7 +877,7 @@ schema_parse_schema_content( NAXMLReader *reader, xmlNode *schema )
 
 		if( !str ){
 			gchar *node_list = build_key_node_list( naxml_schema_key_schema_str );
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_UNKNOWN,
 					( const char * ) iter->name, iter->line, node_list );
 			g_free( node_list );
@@ -889,7 +886,7 @@ schema_parse_schema_content( NAXMLReader *reader, xmlNode *schema )
 		}
 
 		if( str->reader_found ){
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_ALREADY_FOUND,
 					( const char * ) iter->name, iter->line );
 			reader->private->node_ok = FALSE;
@@ -945,7 +942,7 @@ schema_check_for_id( NAXMLReader *reader, xmlNode *iter )
 
 	if( reader->private->item_id ){
 		if( strcmp( reader->private->item_id, id ) != 0 ){
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_INVALID_ID,
 					reader->private->item_id, id, iter->line );
 			reader->private->node_ok = FALSE;
@@ -978,7 +975,7 @@ schema_check_for_type( NAXMLReader *reader, xmlNode *iter )
 			reader->private->parms->imported = NA_OBJECT_ITEM( na_object_menu_new());
 
 		} else {
-			add_message( reader, ERR_NODE_UNKNOWN_TYPE, type, iter->line );
+			na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_UNKNOWN_TYPE, type, iter->line );
 			reader->private->node_ok = FALSE;
 		}
 
@@ -1050,7 +1047,7 @@ dump_parse_entry_content( NAXMLReader *reader, xmlNode *entry )
 
 		if( !str ){
 			gchar *node_list = build_key_node_list( naxml_dump_key_entry_str );
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_UNKNOWN,
 					( const char * ) iter->name, iter->line, node_list );
 			g_free( node_list );
@@ -1059,7 +1056,7 @@ dump_parse_entry_content( NAXMLReader *reader, xmlNode *entry )
 		}
 
 		if( str->reader_found ){
-			add_message( reader,
+			na_core_utils_slist_add_message( &reader->private->parms->messages,
 					ERR_NODE_ALREADY_FOUND,
 					( const char * ) iter->name, iter->line );
 			reader->private->node_ok = FALSE;
@@ -1102,7 +1099,7 @@ dump_check_for_type( NAXMLReader *reader, xmlNode *key_node )
 			reader->private->parms->imported = NA_OBJECT_ITEM( na_object_menu_new());
 
 		} else {
-			add_message( reader, ERR_NODE_UNKNOWN_TYPE, type, key_node->line );
+			na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_UNKNOWN_TYPE, type, key_node->line );
 			reader->private->node_ok = FALSE;
 		}
 
@@ -1170,19 +1167,6 @@ dump_read_value( NAXMLReader *reader, xmlNode *node, const NADataDef *def )
 	return( string );
 }
 
-static void
-add_message( NAXMLReader *reader, const gchar *format, ... )
-{
-	va_list va;
-	gchar *tmp;
-
-	va_start( va, format );
-	tmp = g_markup_vprintf_escaped( format, va );
-	va_end( va );
-
-	reader->private->parms->messages = g_slist_append( reader->private->parms->messages, tmp );
-}
-
 static gchar *
 build_key_node_list( NAXMLKeyStr *strlist )
 {
@@ -1285,85 +1269,25 @@ is_profile_path( NAXMLReader *reader, xmlChar *text )
 	return( is_profile );
 }
 
-/*
- * returns IMPORTER_CODE_OK if we can safely insert the action
- * - the id doesn't already exist
- * - the id already exist, but import mode is renumber
- * - the id already exists, but import mode is override
- *
- * returns IMPORTER_CODE_CANCELLED if user chooses to cancel the operation
- */
 static guint
 manage_import_mode( NAXMLReader *reader )
 {
+	NAIImporterManageImportModeParms parms;
 	guint code;
-	NAObjectItem *exists;
-	guint mode;
-	gchar *id;
-
-	code = IMPORTER_CODE_OK;
-	exists = NULL;
-	mode = 0;
 
-	if( reader->private->parms->check_fn ){
-		exists = ( *reader->private->parms->check_fn )
-						( reader->private->parms->imported, reader->private->parms->check_fn_data );
+	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;
 
-	} else {
-		renumber_label_item( reader );
-		add_message( reader, "%s", _( "Item was renumbered because the caller did not provide any check function." ));
-		reader->private->parms->import_mode = IMPORTER_MODE_RENUMBER;
-	}
-
-	if( exists ){
-		reader->private->parms->exist = TRUE;
+	code = na_iimporter_manage_import_mode( &parms );
 
-		if( reader->private->parms->asked_mode == IMPORTER_MODE_ASK ){
-			if( reader->private->parms->ask_fn ){
-				mode = ( *reader->private->parms->ask_fn )( reader->private->parms->imported, exists, reader->private->parms->ask_fn_data );
-
-			} else {
-				renumber_label_item( reader );
-				add_message( reader, "%s", _( "Item was renumbered because the caller did not provide any ask user function." ));
-				reader->private->parms->import_mode = IMPORTER_MODE_RENUMBER;
-			}
-
-		} else {
-			mode = reader->private->parms->asked_mode;
-		}
-	}
-
-	/* mode is only set if asked mode is ask user and an ask function was provided
-	 * or if asked mode was not ask user
-	 */
-	if( mode ){
-		reader->private->parms->import_mode = mode;
-
-		switch( mode ){
-			case IMPORTER_MODE_RENUMBER:
-				renumber_label_item( reader );
-				if( reader->private->parms->asked_mode == IMPORTER_MODE_ASK ){
-					add_message( reader, "%s", _( "Item was renumbered due to user request." ));
-				}
-				break;
-
-			case IMPORTER_MODE_OVERRIDE:
-				if( reader->private->parms->asked_mode == IMPORTER_MODE_ASK ){
-					add_message( reader, "%s", _( "Existing item was overriden due to user request." ));
-				}
-				break;
-
-			case IMPORTER_MODE_NO_IMPORT:
-			default:
-				id = na_object_get_id( reader->private->parms->imported );
-				add_message( reader, ERR_ITEM_ID_ALREADY_EXISTS, id );
-				if( reader->private->parms->asked_mode == IMPORTER_MODE_ASK ){
-					add_message( reader, "%s", _( "Import was canceled due to user request." ));
-				}
-				g_free( id );
-				code = IMPORTER_CODE_CANCELLED;
-		}
-	}
+	reader->private->parms->exist = parms.exist;
+	reader->private->parms->import_mode = parms.import_mode;
 
 	return( code );
 }
@@ -1379,33 +1303,12 @@ publish_undealt_nodes( NAXMLReader *reader )
 	for( iter = reader->private->nodes ; iter ; iter = iter->next ){
 		xmlNode *node = ( xmlNode * ) iter->data;
 		text = xmlNodeGetContent( node );
-		add_message( reader, WARN_UNDEALT_NODE, ( const gchar * ) text, node->line );
+		na_core_utils_slist_add_message( &reader->private->parms->messages, WARN_UNDEALT_NODE, ( const gchar * ) text, node->line );
 		xmlFree( text );
 	}
 }
 
 /*
- * renumber the item, and set a new label
- */
-static void
-renumber_label_item( NAXMLReader *reader )
-{
-	gchar *label, *tmp;
-
-	na_object_set_new_id( reader->private->parms->imported, NULL );
-
-	label = na_object_get_label( reader->private->parms->imported );
-
-	/* i18n: the action has been renumbered during import operation */
-	tmp = g_strdup_printf( "%s %s", label, _( "(renumbered)" ));
-
-	na_object_set_label( reader->private->parms->imported, tmp );
-
-	g_free( tmp );
-	g_free( label );
-}
-
-/*
  * data are reset before first run on nodes for an item
  */
 static void



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