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/>.
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)
34 #include <glib/gi18n.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"
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 */
54 static FMAImportModeStr st_import_modes
[] = {
56 { IMPORTER_MODE_NO_IMPORT
,
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
,
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
,
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" },
83 static FMAImportModeStr st_import_ask_mode
= {
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:
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).
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
;
135 FMAImporterResult
*import_result
;
136 FMAImporterAskUserParms ask_parms
;
139 g_return_val_if_fail( FMA_IS_PIVOT( pivot
), NULL
);
140 g_return_val_if_fail( parms
!= NULL
, 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
;
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
);
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
);
191 * fma_importer_free_result:
192 * @result: the #FMAImporterResult structure to be released.
194 * Release the structure.
197 fma_importer_free_result( FMAImporterResult
*result
)
199 g_free( result
->uri
);
200 fma_core_utils_slist_free( result
->messages
);
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
215 static FMAImporterResult
*
216 import_from_uri( const FMAPivot
*pivot
, GList
*modules
, const gchar
*uri
)
218 FMAImporterResult
*result
;
219 FMAIImporterImportFromUriParmsv2 provider_parms
;
222 GSList
*all_messages
;
223 FMAIImporter
*provider
;
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
;
236 im
&& ( code
== IMPORTER_CODE_NOT_WILLING_TO
|| code
== IMPORTER_CODE_NOT_LOADABLE
) ;
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
);
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
);
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
;
269 * check for existence of the imported item
270 * ask for the user if needed
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
;
281 result
->exist
= FALSE
;
282 result
->mode
= parms
->preferred_mode
;
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(
293 _( "Item was renumbered because the caller did not provide any check function." ));
294 result
->mode
= IMPORTER_MODE_RENUMBER
;
297 exists
= is_importing_already_exists( parms
, results
, result
);
300 g_debug( "%s: exists=%p", thisfn
, exists
);
303 result
->exist
= TRUE
;
305 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
306 mode
= ask_user_for_mode( result
->imported
, exists
, ask_parms
);
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"
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(
326 _( "Item was renumbered due to user request." ));
330 case IMPORTER_MODE_OVERRIDE
:
331 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
332 fma_core_utils_slist_add_message(
335 _( "Existing item was overriden due to user request." ));
339 case IMPORTER_MODE_NO_IMPORT
:
341 id
= fma_object_get_id( result
->imported
);
342 fma_core_utils_slist_add_message(
344 _( "Item %s already exists." ),
346 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
347 fma_core_utils_slist_add_message(
350 _( "Import was canceled due to user request." ));
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
;
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
);
392 g_free( importing_id
);
394 /* if not found in our current importation list,
395 * then check the existence via provided function and data
398 exists
= parms
->check_fn( result
->imported
, parms
->check_fn_data
);
405 * renumber the item, and set a new label
408 renumber_label_item( FMAObjectItem
*item
)
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
);
426 ask_user_for_mode( const FMAObjectItem
*importing
, const FMAObjectItem
*existing
, FMAImporterAskUserParms
*parms
)
431 if( parms
->count
== 0 || !parms
->keep_choice
){
432 mode
= fma_importer_ask_user( importing
, existing
, parms
);
435 mode_str
= fma_settings_get_string( IPREFS_IMPORT_ASK_USER_LAST_MODE
, NULL
, NULL
);
436 mode
= get_id_from_string( mode_str
);
444 get_id_from_string( const gchar
*str
)
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
);
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();
472 fma_importer_get_modes( void )
474 static const gchar
*thisfn
= "fma_importer_get_modes";
479 g_debug( "%s", thisfn
);
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
);
492 get_mode_from_struct( const FMAImportModeStr
*str
)
499 if( !gtk_icon_size_lookup( GTK_ICON_SIZE_DIALOG
, &width
, &height
)){
503 mode
= fma_import_mode_new( str
->id
);
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
);
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
,
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.
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.
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
));