README: add deprecation notice
[nautilus-actions.git] / src / core / fma-importer.c
blobb9b919ad85c83e89c3edd4801523a6f61ce28487
1 /*
2 * FileManager-Actions
3 * A file-manager extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2015 Pierre Wieser and others (see AUTHORS)
9 * FileManager-Actions is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * FileManager-Actions is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with FileManager-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
23 * Authors:
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
34 #include <glib/gi18n.h>
35 #include <string.h>
37 #include <api/fma-core-utils.h>
38 #include <api/fma-iimporter.h>
39 #include <api/fma-object-api.h>
41 #include "fma-import-mode.h"
42 #include "fma-importer.h"
43 #include "fma-importer-ask.h"
45 typedef struct {
46 guint id; /* the import mode used in switch statement in the code */
47 const gchar *mode; /* the import mode saved in user's preferences */
48 const gchar *label; /* short label */
49 const gchar *description; /* full description */
50 gchar *image; /* associated image */
52 FMAImportModeStr;
54 static FMAImportModeStr st_import_modes[] = {
56 { IMPORTER_MODE_NO_IMPORT,
57 "NoImport",
58 N_( "Do _not import the item" ),
59 N_( "This used to be the historical behavior.\n" \
60 "The selected file will be marked as \"NOT OK\" in the Summary page.\n" \
61 "The existing item will not be modified." ),
62 "fma-import-mode-no-import.png" },
64 { IMPORTER_MODE_RENUMBER,
65 "Renumber",
66 N_( "Import the item, _allocating it a new identifier" ),
67 N_( "The selected file will be imported with a slightly modified label " \
68 "indicating the renumbering.\n" \
69 "The existing item will not be modified." ),
70 "fma-import-mode-renumber.png" },
72 { IMPORTER_MODE_OVERRIDE,
73 "Override",
74 N_( "_Override the existing item" ),
75 N_( "The item found in the selected file will silently override the " \
76 "current one which has the same identifier.\n" \
77 "Be warned: this mode may be dangerous. You will not be prompted another time." ),
78 "fma-import-mode-override.png" },
80 { 0 }
83 static FMAImportModeStr st_import_ask_mode = {
85 IMPORTER_MODE_ASK,
86 "Ask",
87 N_( "_Ask me" ),
88 N_( "You will be asked each time an imported ID already exists." ),
89 "fma-import-mode-ask.png"
92 static FMAImporterResult *import_from_uri( const FMAPivot *pivot, GList *modules, const gchar *uri );
93 static void manage_import_mode( FMAImporterParms *parms, GList *results, FMAImporterAskUserParms *ask_parms, FMAImporterResult *result );
94 static FMAObjectItem *is_importing_already_exists( FMAImporterParms *parms, GList *results, FMAImporterResult *result );
95 static void renumber_label_item( FMAObjectItem *item );
96 static guint ask_user_for_mode( const FMAObjectItem *importing, const FMAObjectItem *existing, FMAImporterAskUserParms *parms );
97 static guint get_id_from_string( const gchar *str );
98 static FMAIOption *get_mode_from_struct( const FMAImportModeStr *str );
100 /* i18n: '%s' stands for the file URI */
101 #define ERR_NOT_LOADABLE _( "%s is not loadable (empty or too big or not a regular file)" )
104 * fma_importer_import_from_uris:
105 * @pivot: the #FMAPivot pivot for this application.
106 * @parms: a #FMAImporterParms structure.
108 * Imports a list of URIs.
110 * For each URI to import, we search through the available #FMAIImporter
111 * providers until the first which returns with something different from
112 * "not_willing_to" code.
114 * #parms.uris contains a list of URIs to import.
116 * Each import operation will have its corresponding newly allocated
117 * #FMAImporterResult structure which will contain:
118 * - the imported URI
119 * - the #FMAIImporter provider if one has been found, or %NULL
120 * - a #FMAObjectItem item if import was successful, or %NULL
121 * - a list of error messages, or %NULL.
123 * Returns: a #GList of #FMAImporterResult structures
124 * (was the last import operation code up to 3.2).
126 * Since: 2.30
128 GList *
129 fma_importer_import_from_uris( const FMAPivot *pivot, FMAImporterParms *parms )
131 static const gchar *thisfn = "fma_importer_import_from_uris";
132 GList *results, *ires;
133 GList *modules;
134 GSList *uri;
135 FMAImporterResult *import_result;
136 FMAImporterAskUserParms ask_parms;
137 gchar *mode_str;
139 g_return_val_if_fail( FMA_IS_PIVOT( pivot ), NULL );
140 g_return_val_if_fail( parms != NULL, NULL );
142 results = NULL;
144 g_debug( "%s: pivot=%p, parms=%p", thisfn, ( void * ) pivot, ( void * ) parms );
146 /* first phase: just try to import the uris into memory
148 modules = fma_pivot_get_providers( pivot, FMA_TYPE_IIMPORTER );
150 for( uri = parms->uris ; uri ; uri = uri->next ){
151 import_result = import_from_uri( pivot, modules, ( const gchar * ) uri->data );
152 results = g_list_prepend( results, import_result );
155 fma_pivot_free_providers( modules );
157 results = g_list_reverse( results );
159 memset( &ask_parms, '\0', sizeof( FMAImporterAskUserParms ));
160 ask_parms.parent = parms->parent_toplevel;
161 ask_parms.count = 0;
162 ask_parms.keep_choice = FALSE;
163 ask_parms.pivot = pivot;
165 /* set the default import mode
167 if( !parms->preferred_mode ){
168 mode_str = fma_settings_get_string( IPREFS_IMPORT_PREFERRED_MODE, NULL, NULL );
169 parms->preferred_mode = get_id_from_string( mode_str );
170 g_free( mode_str );
173 /* second phase: check for their pre-existence
175 for( ires = results ; ires ; ires = ires->next ){
176 import_result = ( FMAImporterResult * ) ires->data;
178 if( import_result->imported ){
179 g_return_val_if_fail( FMA_IS_OBJECT_ITEM( import_result->imported ), NULL );
180 g_return_val_if_fail( FMA_IS_IIMPORTER( import_result->importer ), NULL );
182 ask_parms.uri = import_result->uri;
183 manage_import_mode( parms, results, &ask_parms, import_result );
187 return( results );
191 * fma_importer_free_result:
192 * @result: the #FMAImporterResult structure to be released.
194 * Release the structure.
196 void
197 fma_importer_free_result( FMAImporterResult *result )
199 g_free( result->uri );
200 fma_core_utils_slist_free( result->messages );
202 g_free( result );
206 * Each FMAIImporter interface may return some messages, specially if it
207 * recognized but is not able to import the provided URI. But as long
208 * we do not have yet asked to all available interfaces, we are not sure
209 * of whether this URI is eventually importable or not.
211 * We so let each interface push its messages in the list, but be ready to
212 * only keep the messages provided by the interface which has successfully
213 * imported the item.
215 static FMAImporterResult *
216 import_from_uri( const FMAPivot *pivot, GList *modules, const gchar *uri )
218 FMAImporterResult *result;
219 FMAIImporterImportFromUriParmsv2 provider_parms;
220 GList *im;
221 guint code;
222 GSList *all_messages;
223 FMAIImporter *provider;
225 result = NULL;
226 all_messages = NULL;
227 provider = NULL;
228 code = IMPORTER_CODE_NOT_WILLING_TO;
230 memset( &provider_parms, '\0', sizeof( FMAIImporterImportFromUriParmsv2 ));
231 provider_parms.version = 2;
232 provider_parms.content = 1;
233 provider_parms.uri = uri;
235 for( im = modules ;
236 im && ( code == IMPORTER_CODE_NOT_WILLING_TO || code == IMPORTER_CODE_NOT_LOADABLE ) ;
237 im = im->next ){
239 code = fma_iimporter_import_from_uri( FMA_IIMPORTER( im->data ), &provider_parms );
241 if( code == IMPORTER_CODE_NOT_WILLING_TO ){
242 all_messages = g_slist_concat( all_messages, provider_parms.messages );
243 provider_parms.messages = NULL;
245 } else if( code == IMPORTER_CODE_NOT_LOADABLE ){
246 fma_core_utils_slist_free( all_messages );
247 all_messages = NULL;
248 fma_core_utils_slist_free( provider_parms.messages );
249 provider_parms.messages = NULL;
250 fma_core_utils_slist_add_message( &all_messages, ERR_NOT_LOADABLE, ( const gchar * ) uri );
252 } else {
253 fma_core_utils_slist_free( all_messages );
254 all_messages = provider_parms.messages;
255 provider = FMA_IIMPORTER( im->data );
259 result = g_new0( FMAImporterResult, 1 );
260 result->uri = g_strdup( uri );
261 result->imported = provider_parms.imported;
262 result->importer = provider;
263 result->messages = all_messages;
265 return( result );
269 * check for existence of the imported item
270 * ask for the user if needed
272 static void
273 manage_import_mode( FMAImporterParms *parms, GList *results, FMAImporterAskUserParms *ask_parms, FMAImporterResult *result )
275 static const gchar *thisfn = "fma_importer_manage_import_mode";
276 FMAObjectItem *exists;
277 guint mode;
278 gchar *id;
280 exists = NULL;
281 result->exist = FALSE;
282 result->mode = parms->preferred_mode;
283 mode = 0;
285 /* if no check function is provided, then we systematically allocate
286 * a new identifier to the imported item
288 if( !parms->check_fn ){
289 renumber_label_item( result->imported );
290 fma_core_utils_slist_add_message(
291 &result->messages,
292 "%s",
293 _( "Item was renumbered because the caller did not provide any check function." ));
294 result->mode = IMPORTER_MODE_RENUMBER;
296 } else {
297 exists = is_importing_already_exists( parms, results, result );
300 g_debug( "%s: exists=%p", thisfn, exists );
302 if( exists ){
303 result->exist = TRUE;
305 if( parms->preferred_mode == IMPORTER_MODE_ASK ){
306 mode = ask_user_for_mode( result->imported, exists, ask_parms );
308 } else {
309 mode = parms->preferred_mode;
313 /* mode is only set if asked mode was "ask me" and an ask function was provided
314 * or if asked mode was not "ask me"
316 if( mode ){
317 result->mode = mode;
319 switch( mode ){
320 case IMPORTER_MODE_RENUMBER:
321 renumber_label_item( result->imported );
322 if( parms->preferred_mode == IMPORTER_MODE_ASK ){
323 fma_core_utils_slist_add_message(
324 &result->messages,
325 "%s",
326 _( "Item was renumbered due to user request." ));
328 break;
330 case IMPORTER_MODE_OVERRIDE:
331 if( parms->preferred_mode == IMPORTER_MODE_ASK ){
332 fma_core_utils_slist_add_message(
333 &result->messages,
334 "%s",
335 _( "Existing item was overriden due to user request." ));
337 break;
339 case IMPORTER_MODE_NO_IMPORT:
340 default:
341 id = fma_object_get_id( result->imported );
342 fma_core_utils_slist_add_message(
343 &result->messages,
344 _( "Item %s already exists." ),
345 id );
346 if( parms->preferred_mode == IMPORTER_MODE_ASK ){
347 fma_core_utils_slist_add_message(
348 &result->messages,
349 "%s",
350 _( "Import was canceled due to user request." ));
352 g_free( id );
353 g_object_unref( result->imported );
354 result->imported = NULL;
360 * First check here for duplicates inside of imported population,
361 * then delegates to the caller-provided check function the rest of work...
363 static FMAObjectItem *
364 is_importing_already_exists( FMAImporterParms *parms, GList *results, FMAImporterResult *result )
366 static const gchar *thisfn = "fma_importer_is_importing_already_exists";
367 FMAObjectItem *exists;
368 GList *ip;
370 exists = NULL;
372 gchar *importing_id = fma_object_get_id( result->imported );
373 g_debug( "%s: importing=%p, id=%s", thisfn, ( void * ) result->imported, importing_id );
375 /* is the importing item already in the current importation list ?
376 * (only tries previous items of the list)
378 for( ip = results ; ip && !exists && ip->data != result ; ip = ip->next ){
379 FMAImporterResult *try_result = ( FMAImporterResult * ) ip->data;
381 if( try_result->imported ){
382 g_return_val_if_fail( FMA_IS_OBJECT_ITEM( try_result->imported ), NULL );
384 gchar *id = fma_object_get_id( try_result->imported );
385 if( !strcmp( importing_id, id )){
386 exists = FMA_OBJECT_ITEM( try_result->imported );
388 g_free( id );
392 g_free( importing_id );
394 /* if not found in our current importation list,
395 * then check the existence via provided function and data
397 if( !exists ){
398 exists = parms->check_fn( result->imported, parms->check_fn_data );
401 return( exists );
405 * renumber the item, and set a new label
407 static void
408 renumber_label_item( FMAObjectItem *item )
410 gchar *label, *tmp;
412 fma_object_set_new_id( item, NULL );
414 label = fma_object_get_label( item );
416 /* i18n: the action has been renumbered during import operation */
417 tmp = g_strdup_printf( "%s %s", label, _( "(renumbered)" ));
419 fma_object_set_label( item, tmp );
421 g_free( tmp );
422 g_free( label );
425 static guint
426 ask_user_for_mode( const FMAObjectItem *importing, const FMAObjectItem *existing, FMAImporterAskUserParms *parms )
428 guint mode;
429 gchar *mode_str;
431 if( parms->count == 0 || !parms->keep_choice ){
432 mode = fma_importer_ask_user( importing, existing, parms );
434 } else {
435 mode_str = fma_settings_get_string( IPREFS_IMPORT_ASK_USER_LAST_MODE, NULL, NULL );
436 mode = get_id_from_string( mode_str );
437 g_free( mode_str );
440 return( mode );
443 static guint
444 get_id_from_string( const gchar *str )
446 int i;
448 /* search in standard import modes
450 for( i = 0 ; st_import_modes[i].id ; ++i ){
451 if( !strcmp( st_import_modes[i].mode, str )){
452 return( st_import_modes[i].id );
456 /* else, is it ask option ?
458 if( !strcmp( st_import_ask_mode.mode, str )){
459 return( st_import_ask_mode.id );
462 return( 0 );
466 * fma_importer_get_modes:
468 * Returns: the list of available import modes.
469 * This list should later be released by calling fma_importer_free_modes();
471 GList *
472 fma_importer_get_modes( void )
474 static const gchar *thisfn = "fma_importer_get_modes";
475 GList *modes;
476 FMAIOption *mode;
477 guint i;
479 g_debug( "%s", thisfn );
481 modes = NULL;
483 for( i = 0 ; st_import_modes[i].id ; ++i ){
484 mode = get_mode_from_struct( st_import_modes+i );
485 modes = g_list_prepend( modes, mode );
488 return( modes );
491 static FMAIOption *
492 get_mode_from_struct( const FMAImportModeStr *str )
494 FMAImportMode *mode;
495 gint width, height;
496 gchar *fname;
497 GdkPixbuf *pixbuf;
499 if( !gtk_icon_size_lookup( GTK_ICON_SIZE_DIALOG, &width, &height )){
500 width = height = 48;
503 mode = fma_import_mode_new( str->id );
504 pixbuf = NULL;
506 if( str->image && g_utf8_strlen( str->image, -1 )){
507 fname = g_strdup_printf( "%s/%s", PKGIMPORTMODEDIR, str->image );
508 pixbuf = gdk_pixbuf_new_from_file_at_size( fname, width, height, NULL );
509 g_free( fname );
511 g_object_set( G_OBJECT( mode ),
512 FMA_IMPORT_PROP_MODE, str->mode,
513 FMA_IMPORT_PROP_LABEL, gettext( str->label ),
514 FMA_IMPORT_PROP_DESCRIPTION, gettext( str->description ),
515 FMA_IMPORT_PROP_IMAGE, pixbuf,
516 NULL );
518 return( FMA_IOPTION( mode ));
522 * fma_importer_free_modes:
523 * @modes: a #GList of #FMAImportMode items, as returned by fma_importer_get_modes().
525 * Releases the resources allocated to the @modes list.
527 void
528 fma_importer_free_modes( GList *modes )
530 static const gchar *thisfn = "fma_importer_free_modes";
532 g_debug( "%s: modes=%p", thisfn, ( void * ) modes );
534 g_list_foreach( modes, ( GFunc ) g_object_unref, NULL );
535 g_list_free( modes );
539 * fma_importer_get_ask_mode:
541 * Returns: a #FMAImportMode object which describes the 'Ask me' option.
543 FMAIOption *
544 fma_importer_get_ask_mode( void )
546 static const gchar *thisfn = "fma_importer_get_ask_mode";
548 g_debug( "%s", thisfn );
550 return( get_mode_from_struct( &st_import_ask_mode ));