3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011, 2012 Pierre Wieser and others (see AUTHORS)
9 * This Program 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 * This Program 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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
35 #include <glib/gi18n.h>
38 #include <api/na-core-utils.h>
39 #include <api/na-iimporter.h>
40 #include <api/na-object-api.h>
42 #include "na-import-mode.h"
43 #include "na-importer.h"
44 #include "na-importer-ask.h"
47 guint id
; /* the import mode used in switch statement in the code */
48 const gchar
*mode
; /* the import mode saved in user's preferences */
49 const gchar
*label
; /* short label */
50 const gchar
*description
; /* full description */
51 gchar
*image
; /* associated image */
55 static NAImportModeStr st_import_modes
[] = {
57 { IMPORTER_MODE_NO_IMPORT
,
59 N_( "Do _not import the item" ),
60 N_( "This used to be the historical behavior.\n" \
61 "The selected file will be marked as \"NOT OK\" in the Summary page.\n" \
62 "The existing item will not be modified." ),
63 "import-mode-no-import.png" },
65 { IMPORTER_MODE_RENUMBER
,
67 N_( "Import the item, _allocating it a new identifier" ),
68 N_( "The selected file will be imported with a slightly modified label " \
69 "indicating the renumbering.\n" \
70 "The existing item will not be modified." ),
71 "import-mode-renumber.png" },
73 { IMPORTER_MODE_OVERRIDE
,
75 N_( "_Override the existing item" ),
76 N_( "The item found in the selected file will silently override the " \
77 "current one which has the same identifier.\n" \
78 "Be warned: this mode may be dangerous. You will not be prompted another time." ),
79 "import-mode-override.png" },
84 static NAImportModeStr st_import_ask_mode
= {
89 N_( "You will be asked each time an imported ID already exists." ),
93 static NAImporterResult
*import_from_uri( const NAPivot
*pivot
, GList
*modules
, const gchar
*uri
);
94 static void manage_import_mode( NAImporterParms
*parms
, GList
*results
, NAImporterAskUserParms
*ask_parms
, NAImporterResult
*result
);
95 static NAObjectItem
*is_importing_already_exists( NAImporterParms
*parms
, GList
*results
, NAImporterResult
*result
);
96 static void renumber_label_item( NAObjectItem
*item
);
97 static guint
ask_user_for_mode( const NAObjectItem
*importing
, const NAObjectItem
*existing
, NAImporterAskUserParms
*parms
);
98 static guint
get_id_from_string( const gchar
*str
);
99 static NAIOption
*get_mode_from_struct( const NAImportModeStr
*str
);
101 /* i18n: '%s' stands for the file URI */
102 #define ERR_NOT_LOADABLE _( "%s is not loadable (empty or too big or not a regular file)" )
105 * na_importer_import_from_uris:
106 * @pivot: the #NAPivot pivot for this application.
107 * @parms: a #NAImporterParms structure.
109 * Imports a list of URIs.
111 * For each URI to import, we search through the available #NAIImporter
112 * providers until the first which returns with something different from
113 * "not_willing_to" code.
115 * #parms.uris contains a list of URIs to import.
117 * Each import operation will have its corresponding newly allocated
118 * #NAImporterResult structure which will contain:
120 * - the #NAIImporter provider if one has been found, or %NULL
121 * - a #NAObjectItem item if import was successful, or %NULL
122 * - a list of error messages, or %NULL.
124 * Returns: a #GList of #NAImporterResult structures
125 * (was the last import operation code up to 3.2).
130 na_importer_import_from_uris( const NAPivot
*pivot
, NAImporterParms
*parms
)
132 static const gchar
*thisfn
= "na_importer_import_from_uris";
133 GList
*results
, *ires
;
136 NAImporterResult
*import_result
;
137 NAImporterAskUserParms ask_parms
;
140 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
141 g_return_val_if_fail( parms
!= NULL
, NULL
);
145 g_debug( "%s: pivot=%p, parms=%p", thisfn
, ( void * ) pivot
, ( void * ) parms
);
147 /* first phase: just try to import the uris into memory
149 modules
= na_pivot_get_providers( pivot
, NA_TYPE_IIMPORTER
);
151 for( uri
= parms
->uris
; uri
; uri
= uri
->next
){
152 import_result
= import_from_uri( pivot
, modules
, ( const gchar
* ) uri
->data
);
153 results
= g_list_prepend( results
, import_result
);
156 na_pivot_free_providers( modules
);
158 results
= g_list_reverse( results
);
160 memset( &ask_parms
, '\0', sizeof( NAImporterAskUserParms
));
161 ask_parms
.parent
= parms
->parent_toplevel
;
163 ask_parms
.keep_choice
= FALSE
;
164 ask_parms
.pivot
= pivot
;
166 /* set the default import mode
168 if( !parms
->preferred_mode
){
169 mode_str
= na_settings_get_string( NA_IPREFS_IMPORT_PREFERRED_MODE
, NULL
, NULL
);
170 parms
->preferred_mode
= get_id_from_string( mode_str
);
174 /* second phase: check for their pre-existence
176 for( ires
= results
; ires
; ires
= ires
->next
){
177 import_result
= ( NAImporterResult
* ) ires
->data
;
179 if( import_result
->imported
){
180 g_return_val_if_fail( NA_IS_OBJECT_ITEM( import_result
->imported
), NULL
);
181 g_return_val_if_fail( NA_IS_IIMPORTER( import_result
->importer
), NULL
);
183 ask_parms
.uri
= import_result
->uri
;
184 manage_import_mode( parms
, results
, &ask_parms
, import_result
);
192 * na_importer_free_result:
193 * @result: the #NAImporterResult structure to be released.
195 * Release the structure.
198 na_importer_free_result( NAImporterResult
*result
)
200 g_free( result
->uri
);
201 na_core_utils_slist_free( result
->messages
);
207 * Each NAIImporter interface may return some messages, specially if it
208 * recognized but is not able to import the provided URI. But as long
209 * we do not have yet asked to all available interfaces, we are not sure
210 * of whether this URI is eventually importable or not.
212 * We so let each interface push its messages in the list, but be ready to
213 * only keep the messages provided by the interface which has successfully
216 static NAImporterResult
*
217 import_from_uri( const NAPivot
*pivot
, GList
*modules
, const gchar
*uri
)
219 NAImporterResult
*result
;
220 NAIImporterImportFromUriParmsv2 provider_parms
;
223 GSList
*all_messages
;
224 NAIImporter
*provider
;
229 code
= IMPORTER_CODE_NOT_WILLING_TO
;
231 memset( &provider_parms
, '\0', sizeof( NAIImporterImportFromUriParmsv2
));
232 provider_parms
.version
= 2;
233 provider_parms
.content
= 1;
234 provider_parms
.uri
= uri
;
237 im
&& ( code
== IMPORTER_CODE_NOT_WILLING_TO
|| code
== IMPORTER_CODE_NOT_LOADABLE
) ;
240 code
= na_iimporter_import_from_uri( NA_IIMPORTER( im
->data
), &provider_parms
);
242 if( code
== IMPORTER_CODE_NOT_WILLING_TO
){
243 all_messages
= g_slist_concat( all_messages
, provider_parms
.messages
);
244 provider_parms
.messages
= NULL
;
246 } else if( code
== IMPORTER_CODE_NOT_LOADABLE
){
247 na_core_utils_slist_free( all_messages
);
249 na_core_utils_slist_free( provider_parms
.messages
);
250 provider_parms
.messages
= NULL
;
251 na_core_utils_slist_add_message( &all_messages
, ERR_NOT_LOADABLE
, ( const gchar
* ) uri
);
254 na_core_utils_slist_free( all_messages
);
255 all_messages
= provider_parms
.messages
;
256 provider
= NA_IIMPORTER( im
->data
);
260 result
= g_new0( NAImporterResult
, 1 );
261 result
->uri
= g_strdup( uri
);
262 result
->imported
= provider_parms
.imported
;
263 result
->importer
= provider
;
264 result
->messages
= all_messages
;
270 * check for existence of the imported item
271 * ask for the user if needed
274 manage_import_mode( NAImporterParms
*parms
, GList
*results
, NAImporterAskUserParms
*ask_parms
, NAImporterResult
*result
)
276 static const gchar
*thisfn
= "na_importer_manage_import_mode";
277 NAObjectItem
*exists
;
282 result
->exist
= FALSE
;
283 result
->mode
= parms
->preferred_mode
;
286 /* if no check function is provided, then we systematically allocate
287 * a new identifier to the imported item
289 if( !parms
->check_fn
){
290 renumber_label_item( result
->imported
);
291 na_core_utils_slist_add_message(
294 _( "Item was renumbered because the caller did not provide any check function." ));
295 result
->mode
= IMPORTER_MODE_RENUMBER
;
298 exists
= is_importing_already_exists( parms
, results
, result
);
301 g_debug( "%s: exists=%p", thisfn
, exists
);
304 result
->exist
= TRUE
;
306 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
307 mode
= ask_user_for_mode( result
->imported
, exists
, ask_parms
);
310 mode
= parms
->preferred_mode
;
314 /* mode is only set if asked mode was "ask me" and an ask function was provided
315 * or if asked mode was not "ask me"
321 case IMPORTER_MODE_RENUMBER
:
322 renumber_label_item( result
->imported
);
323 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
324 na_core_utils_slist_add_message(
327 _( "Item was renumbered due to user request." ));
331 case IMPORTER_MODE_OVERRIDE
:
332 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
333 na_core_utils_slist_add_message(
336 _( "Existing item was overriden due to user request." ));
340 case IMPORTER_MODE_NO_IMPORT
:
342 id
= na_object_get_id( result
->imported
);
343 na_core_utils_slist_add_message(
345 _( "Item %s already exists." ),
347 if( parms
->preferred_mode
== IMPORTER_MODE_ASK
){
348 na_core_utils_slist_add_message(
351 _( "Import was canceled due to user request." ));
354 g_object_unref( result
->imported
);
355 result
->imported
= NULL
;
361 * First check here for duplicates inside of imported population,
362 * then delegates to the caller-provided check function the rest of work...
364 static NAObjectItem
*
365 is_importing_already_exists( NAImporterParms
*parms
, GList
*results
, NAImporterResult
*result
)
367 static const gchar
*thisfn
= "na_importer_is_importing_already_exists";
368 NAObjectItem
*exists
;
373 gchar
*importing_id
= na_object_get_id( result
->imported
);
374 g_debug( "%s: importing=%p, id=%s", thisfn
, ( void * ) result
->imported
, importing_id
);
376 /* is the importing item already in the current importation list ?
377 * (only tries previous items of the list)
379 for( ip
= results
; ip
&& !exists
&& ip
->data
!= result
; ip
= ip
->next
){
380 NAImporterResult
*try_result
= ( NAImporterResult
* ) ip
->data
;
382 if( try_result
->imported
){
383 g_return_val_if_fail( NA_IS_OBJECT_ITEM( try_result
->imported
), NULL
);
385 gchar
*id
= na_object_get_id( try_result
->imported
);
386 if( !strcmp( importing_id
, id
)){
387 exists
= NA_OBJECT_ITEM( try_result
->imported
);
393 g_free( importing_id
);
395 /* if not found in our current importation list,
396 * then check the existence via provided function and data
399 exists
= parms
->check_fn( result
->imported
, parms
->check_fn_data
);
406 * renumber the item, and set a new label
409 renumber_label_item( NAObjectItem
*item
)
413 na_object_set_new_id( item
, NULL
);
415 label
= na_object_get_label( item
);
417 /* i18n: the action has been renumbered during import operation */
418 tmp
= g_strdup_printf( "%s %s", label
, _( "(renumbered)" ));
420 na_object_set_label( item
, tmp
);
427 ask_user_for_mode( const NAObjectItem
*importing
, const NAObjectItem
*existing
, NAImporterAskUserParms
*parms
)
432 if( parms
->count
== 0 || !parms
->keep_choice
){
433 mode
= na_importer_ask_user( importing
, existing
, parms
);
436 mode_str
= na_settings_get_string( NA_IPREFS_IMPORT_ASK_USER_LAST_MODE
, NULL
, NULL
);
437 mode
= get_id_from_string( mode_str
);
445 get_id_from_string( const gchar
*str
)
449 /* search in standard import modes
451 for( i
= 0 ; st_import_modes
[i
].id
; ++i
){
452 if( !strcmp( st_import_modes
[i
].mode
, str
)){
453 return( st_import_modes
[i
].id
);
457 /* else, is it ask option ?
459 if( !strcmp( st_import_ask_mode
.mode
, str
)){
460 return( st_import_ask_mode
.id
);
467 * na_importer_get_modes:
469 * Returns: the list of available import modes.
470 * This list should later be released by calling na_importer_free_modes();
473 na_importer_get_modes( void )
475 static const gchar
*thisfn
= "na_importer_get_modes";
480 g_debug( "%s", thisfn
);
484 for( i
= 0 ; st_import_modes
[i
].id
; ++i
){
485 mode
= get_mode_from_struct( st_import_modes
+i
);
486 modes
= g_list_prepend( modes
, mode
);
493 get_mode_from_struct( const NAImportModeStr
*str
)
500 if( !gtk_icon_size_lookup( GTK_ICON_SIZE_DIALOG
, &width
, &height
)){
504 mode
= na_import_mode_new( str
->id
);
507 if( str
->image
&& g_utf8_strlen( str
->image
, -1 )){
508 fname
= g_strdup_printf( "%s/%s", PKGDATADIR
, str
->image
);
509 pixbuf
= gdk_pixbuf_new_from_file_at_size( fname
, width
, height
, NULL
);
512 g_object_set( G_OBJECT( mode
),
513 NA_IMPORT_PROP_MODE
, str
->mode
,
514 NA_IMPORT_PROP_LABEL
, gettext( str
->label
),
515 NA_IMPORT_PROP_DESCRIPTION
, gettext( str
->description
),
516 NA_IMPORT_PROP_IMAGE
, pixbuf
,
519 return( NA_IOPTION( mode
));
523 * na_importer_free_modes:
524 * @modes: a #GList of #NAImportMode items, as returned by na_importer_get_modes().
526 * Releases the resources allocated to the @modes list.
529 na_importer_free_modes( GList
*modes
)
531 static const gchar
*thisfn
= "na_importer_free_modes";
533 g_debug( "%s: modes=%p", thisfn
, ( void * ) modes
);
535 g_list_foreach( modes
, ( GFunc
) g_object_unref
, NULL
);
536 g_list_free( modes
);
540 * na_importer_get_ask_mode:
542 * Returns: a #NAImportMode object which describes the 'Ask me' option.
545 na_importer_get_ask_mode( void )
547 static const gchar
*thisfn
= "na_importer_get_ask_mode";
549 g_debug( "%s", thisfn
);
551 return( get_mode_from_struct( &st_import_ask_mode
));