1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
4 *
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8 *
9 * Nautilus-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.
13 *
14 * Nautilus-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.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Nautilus-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
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)
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <gdk/gdk.h>
35 #include <glib/gi18n.h>
36 #include <string.h>
37
38 #include <api/na-object-api.h>
39 #include <api/na-core-utils.h>
40
41 #include <core/na-import-mode.h>
42 #include <core/na-importer.h>
43 #include <core/na-ioptions-list.h>
44 #include <core/na-gtk-utils.h>
45 #include <core/na-settings.h>
46
47 #include "nact-application.h"
48 #include "nact-assistant-import.h"
49 #include "nact-main-window.h"
50 #include "nact-tree-ieditable.h"
51
52 /* Import Assistant
53 *
54 * pos. type title
55 * --- ------- --------------------------------------------------
56 * 0 Intro Introduction
57 * 1 Content Selection of the files
58 * 2 Content Duplicate management: what to do with duplicates ?
59 * 3 Confirm Display the selected files before import
60 * 4 Summary Import is done: summary of the done operations
61 */
62 enum {
63 ASSIST_PAGE_INTRO = 0,
64 ASSIST_PAGE_FILES_SELECTION,
65 ASSIST_PAGE_DUPLICATES,
66 ASSIST_PAGE_CONFIRM,
67 ASSIST_PAGE_DONE
68 };
69
70 /* column ordering in the duplicates treeview
71 */
72 enum {
73 IMAGE_COLUMN = 0,
74 LABEL_COLUMN,
75 TOOLTIP_COLUMN,
76 MODE_COLUMN,
77 INDEX_COLUMN,
78 N_COLUMN
79 };
80
81 /* private class data
82 */
83 struct _NactAssistantImportClassPrivate {
84 void *empty; /* so that gcc -pedantic is happy */
85 };
86
87 /* private instance data
88 */
89 struct _NactAssistantImportPrivate {
90 gboolean dispose_has_run;
91 GtkWidget *file_chooser;
92 GtkTreeView *duplicates_listview;
93 NAIOption *mode;
94 GList *results;
95 GList *overriden;
96 };
97
98 static const gchar *st_xmlui_filename = PKGUIDIR "/nact-assistant-import.ui";
99 static const gchar *st_toplevel_name = "ImportAssistant";
100 static const gchar *st_wsp_name = NA_IPREFS_IMPORT_ASSISTANT_WSP;
101
102 static BaseAssistantClass *st_parent_class = NULL;
103
104 static GType register_type( void );
105 static void class_init( NactAssistantImportClass *klass );
106 static void ioptions_list_iface_init( NAIOptionsListInterface *iface, void *user_data );
107 static GList *ioptions_list_get_modes( const NAIOptionsList *instance, GtkWidget *container );
108 static void ioptions_list_free_modes( const NAIOptionsList *instance, GtkWidget *container, GList *modes );
109 static NAIOption *ioptions_list_get_ask_option( const NAIOptionsList *instance, GtkWidget *container );
110 static void instance_init( GTypeInstance *instance, gpointer klass );
111 static void instance_dispose( GObject *application );
112 static void instance_finalize( GObject *application );
113
114 static void on_base_initialize_gtk( NactAssistantImport *dialog );
115 static void create_duplicates_treeview_model( NactAssistantImport *dialog );
116 static void on_base_initialize_base_window( NactAssistantImport *dialog );
117 static void runtime_init_intro( NactAssistantImport *window, GtkAssistant *assistant );
118 static void runtime_init_file_selector( NactAssistantImport *window, GtkAssistant *assistant );
119 static void on_file_selection_changed( GtkFileChooser *chooser, gpointer user_data );
120 static gboolean has_loadable_files( GSList *uris );
121 static void runtime_init_duplicates( NactAssistantImport *window, GtkAssistant *assistant );
122
123 static void assistant_prepare( BaseAssistant *window, GtkAssistant *assistant, GtkWidget *page );
124 static void prepare_confirm( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page );
125 static void assistant_apply( BaseAssistant *window, GtkAssistant *assistant );
126 static NAObjectItem *check_for_existence( const NAObjectItem *, NactMainWindow *window );
127 static void prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page );
128 static void free_results( GList *list );
129
130 static GtkWidget *find_widget_from_page( GtkWidget *page, const gchar *name );
131 static GtkTreeView *get_duplicates_treeview_from_assistant_import( NactAssistantImport *window );
132 static GtkTreeView *get_duplicates_treeview_from_page( GtkWidget *page );
133
134 GType
nact_assistant_import_get_type(void)135 nact_assistant_import_get_type( void )
136 {
137 static GType window_type = 0;
138
139 if( !window_type ){
140 window_type = register_type();
141 }
142
143 return( window_type );
144 }
145
146 static GType
register_type(void)147 register_type( void )
148 {
149 static const gchar *thisfn = "nact_assistant_import_register_type";
150 GType type;
151
152 static GTypeInfo info = {
153 sizeof( NactAssistantImportClass ),
154 ( GBaseInitFunc ) NULL,
155 ( GBaseFinalizeFunc ) NULL,
156 ( GClassInitFunc ) class_init,
157 NULL,
158 NULL,
159 sizeof( NactAssistantImport ),
160 0,
161 ( GInstanceInitFunc ) instance_init
162 };
163
164 static const GInterfaceInfo ioptions_list_iface_info = {
165 ( GInterfaceInitFunc ) ioptions_list_iface_init,
166 NULL,
167 NULL
168 };
169
170 g_debug( "%s", thisfn );
171
172 type = g_type_register_static( BASE_TYPE_ASSISTANT, "NactAssistantImport", &info, 0 );
173
174 g_type_add_interface_static( type, NA_TYPE_IOPTIONS_LIST, &ioptions_list_iface_info );
175
176 return( type );
177 }
178
179 static void
class_init(NactAssistantImportClass * klass)180 class_init( NactAssistantImportClass *klass )
181 {
182 static const gchar *thisfn = "nact_assistant_import_class_init";
183 GObjectClass *object_class;
184 BaseAssistantClass *assist_class;
185
186 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
187
188 st_parent_class = g_type_class_peek_parent( klass );
189
190 object_class = G_OBJECT_CLASS( klass );
191 object_class->dispose = instance_dispose;
192 object_class->finalize = instance_finalize;
193
194 klass->private = g_new0( NactAssistantImportClassPrivate, 1 );
195
196 assist_class = BASE_ASSISTANT_CLASS( klass );
197 assist_class->apply = assistant_apply;
198 assist_class->prepare = assistant_prepare;
199 }
200
201 static void
ioptions_list_iface_init(NAIOptionsListInterface * iface,void * user_data)202 ioptions_list_iface_init( NAIOptionsListInterface *iface, void *user_data )
203 {
204 static const gchar *thisfn = "nact_assistant_import_ioptions_list_iface_init";
205
206 g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
207
208 iface->get_options = ioptions_list_get_modes;
209 iface->free_options = ioptions_list_free_modes;
210 iface->get_ask_option = ioptions_list_get_ask_option;
211 }
212
213 static GList *
ioptions_list_get_modes(const NAIOptionsList * instance,GtkWidget * container)214 ioptions_list_get_modes( const NAIOptionsList *instance, GtkWidget *container )
215 {
216 GList *modes;
217
218 g_return_val_if_fail( NACT_IS_ASSISTANT_IMPORT( instance ), NULL );
219
220 modes = na_importer_get_modes();
221
222 return( modes );
223 }
224
225 static void
ioptions_list_free_modes(const NAIOptionsList * instance,GtkWidget * container,GList * modes)226 ioptions_list_free_modes( const NAIOptionsList *instance, GtkWidget *container, GList *modes )
227 {
228 na_importer_free_modes( modes );
229 }
230
231 static NAIOption *
ioptions_list_get_ask_option(const NAIOptionsList * instance,GtkWidget * container)232 ioptions_list_get_ask_option( const NAIOptionsList *instance, GtkWidget *container )
233 {
234 return( na_importer_get_ask_mode());
235 }
236
237 static void
instance_init(GTypeInstance * instance,gpointer klass)238 instance_init( GTypeInstance *instance, gpointer klass )
239 {
240 static const gchar *thisfn = "nact_assistant_import_instance_init";
241 NactAssistantImport *self;
242
243 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( instance ));
244
245 g_debug( "%s: instance=%p (%s), klass=%p",
246 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
247
248 self = NACT_ASSISTANT_IMPORT( instance );
249
250 self->private = g_new0( NactAssistantImportPrivate, 1 );
251
252 self->private->results = NULL;
253
254 base_window_signal_connect(
255 BASE_WINDOW( instance ),
256 G_OBJECT( instance ),
257 BASE_SIGNAL_INITIALIZE_GTK,
258 G_CALLBACK( on_base_initialize_gtk ));
259
260 base_window_signal_connect(
261 BASE_WINDOW( instance ),
262 G_OBJECT( instance ),
263 BASE_SIGNAL_INITIALIZE_WINDOW,
264 G_CALLBACK( on_base_initialize_base_window ));
265
266 self->private->dispose_has_run = FALSE;
267 }
268
269 static void
instance_dispose(GObject * window)270 instance_dispose( GObject *window )
271 {
272 static const gchar *thisfn = "nact_assistant_import_instance_dispose";
273 NactAssistantImport *self;
274
275 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( window ));
276
277 self = NACT_ASSISTANT_IMPORT( window );
278
279 if( !self->private->dispose_has_run ){
280 g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
281
282 self->private->dispose_has_run = TRUE;
283
284 /* chain up to the parent class */
285 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
286 G_OBJECT_CLASS( st_parent_class )->dispose( window );
287 }
288 }
289 }
290
291 static void
instance_finalize(GObject * window)292 instance_finalize( GObject *window )
293 {
294 static const gchar *thisfn = "nact_assistant_import_instance_finalize";
295 NactAssistantImport *self;
296
297 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( window ));
298
299 g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
300
301 self = NACT_ASSISTANT_IMPORT( window );
302
303 free_results( self->private->results );
304
305 g_free( self->private );
306
307 /* chain call to parent class */
308 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
309 G_OBJECT_CLASS( st_parent_class )->finalize( window );
310 }
311 }
312
313 /**
314 * nact_assistant_import_run:
315 * @main: the #NactMainWindow parent window of this assistant.
316 *
317 * Run the assistant.
318 */
319 void
nact_assistant_import_run(BaseWindow * main_window)320 nact_assistant_import_run( BaseWindow *main_window )
321 {
322 NactAssistantImport *assistant;
323 gboolean esc_quit, esc_confirm;
324
325 g_return_if_fail( NACT_IS_MAIN_WINDOW( main_window ));
326
327 esc_quit = na_settings_get_boolean( NA_IPREFS_ASSISTANT_ESC_QUIT, NULL, NULL );
328 esc_confirm = na_settings_get_boolean( NA_IPREFS_ASSISTANT_ESC_CONFIRM, NULL, NULL );
329
330 assistant = g_object_new( NACT_TYPE_ASSISTANT_IMPORT,
331 BASE_PROP_PARENT, main_window,
332 BASE_PROP_HAS_OWN_BUILDER, TRUE,
333 BASE_PROP_XMLUI_FILENAME, st_xmlui_filename,
334 BASE_PROP_TOPLEVEL_NAME, st_toplevel_name,
335 BASE_PROP_WSP_NAME, st_wsp_name,
336 BASE_PROP_QUIT_ON_ESCAPE, esc_quit,
337 BASE_PROP_WARN_ON_ESCAPE, esc_confirm,
338 NULL );
339
340 base_window_run( BASE_WINDOW( assistant ));
341 }
342
343 static void
on_base_initialize_gtk(NactAssistantImport * dialog)344 on_base_initialize_gtk( NactAssistantImport *dialog )
345 {
346 static const gchar *thisfn = "nact_assistant_import_on_base_initialize_gtk";
347
348 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( dialog ));
349
350 if( !dialog->private->dispose_has_run ){
351 g_debug( "%s: dialog=%p", thisfn, ( void * ) dialog );
352
353 #if !GTK_CHECK_VERSION( 3,0,0 )
354 guint padder = 8;
355 GtkAssistant *assistant = GTK_ASSISTANT( base_window_get_gtk_toplevel( BASE_WINDOW( dialog )));
356 /* selecting files */
357 GtkWidget *page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
358 GtkWidget *container = find_widget_from_page( page, "p1-l2-alignment1" );
359 g_object_set( G_OBJECT( container ), "top_padding", padder, NULL );
360 /* managing duplicates */
361 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_DUPLICATES );
362 container = find_widget_from_page( page, "p2-l2-alignment1" );
363 g_object_set( G_OBJECT( container ), "border_width", padder, NULL );
364 /* summary */
365 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_CONFIRM );
366 container = find_widget_from_page( page, "p3-l2-alignment1" );
367 g_object_set( G_OBJECT( container ), "border_width", padder, NULL );
368 /* import is done */
369 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_DONE );
370 container = find_widget_from_page( page, "p4-l2-alignment1" );
371 g_object_set( G_OBJECT( container ), "border_width", padder, NULL );
372 #endif
373
374 create_duplicates_treeview_model( dialog );
375 }
376 }
377
378 static void
create_duplicates_treeview_model(NactAssistantImport * dialog)379 create_duplicates_treeview_model( NactAssistantImport *dialog )
380 {
381 static const gchar *thisfn = "nact_assistant_import_create_duplicates_treeview_model";
382
383 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( dialog ));
384 g_return_if_fail( !dialog->private->dispose_has_run );
385
386 g_debug( "%s: dialog=%p", thisfn, ( void * ) dialog );
387
388 dialog->private->duplicates_listview = get_duplicates_treeview_from_assistant_import( dialog );
389 g_return_if_fail( GTK_IS_TREE_VIEW( dialog->private->duplicates_listview ));
390
391 na_ioptions_list_gtk_init( NA_IOPTIONS_LIST( dialog ), GTK_WIDGET( dialog->private->duplicates_listview ), TRUE );
392 }
393
394 static void
on_base_initialize_base_window(NactAssistantImport * dialog)395 on_base_initialize_base_window( NactAssistantImport *dialog )
396 {
397 static const gchar *thisfn = "nact_assistant_import_on_base_initialize_base_window";
398 GtkAssistant *assistant;
399
400 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( dialog ));
401
402 if( !dialog->private->dispose_has_run ){
403 g_debug( "%s: dialog=%p", thisfn, ( void * ) dialog );
404
405 assistant = GTK_ASSISTANT( base_window_get_gtk_toplevel( BASE_WINDOW( dialog )));
406
407 runtime_init_intro( dialog, assistant );
408 runtime_init_file_selector( dialog, assistant );
409 runtime_init_duplicates( dialog, assistant );
410 }
411 }
412
413 static void
runtime_init_intro(NactAssistantImport * window,GtkAssistant * assistant)414 runtime_init_intro( NactAssistantImport *window, GtkAssistant *assistant )
415 {
416 static const gchar *thisfn = "nact_assistant_import_runtime_init_intro";
417 GtkWidget *page;
418
419 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_INTRO );
420
421 g_debug( "%s: window=%p, assistant=%p, page=%p",
422 thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
423
424 gtk_assistant_set_page_complete( assistant, page, TRUE );
425 }
426
427 /*
428 * Starting with Gtk 3.2, the widgets of the page are no more attached
429 * to the GtkAssistant, but only to the page.
430 */
431 static void
runtime_init_file_selector(NactAssistantImport * window,GtkAssistant * assistant)432 runtime_init_file_selector( NactAssistantImport *window, GtkAssistant *assistant )
433 {
434 static const gchar *thisfn = "nact_assistant_import_runtime_init_file_selector";
435 GtkWidget *page;
436 GtkWidget *chooser;
437 gchar *uri;
438
439 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_FILES_SELECTION );
440 g_return_if_fail( GTK_IS_CONTAINER( page ));
441
442 chooser = na_gtk_utils_find_widget_by_name( GTK_CONTAINER( page ), "ImportFileChooser" );
443 g_return_if_fail( GTK_IS_FILE_CHOOSER( chooser ));
444
445 g_debug( "%s: window=%p, assistant=%p, page=%p, chooser=%p",
446 thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page, ( void * ) chooser );
447
448
449 uri = na_settings_get_string( NA_IPREFS_IMPORT_ASSISTANT_URI, NULL, NULL );
450 if( uri && strlen( uri )){
451 gtk_file_chooser_set_current_folder_uri( GTK_FILE_CHOOSER( chooser ), uri );
452 }
453 g_free( uri );
454
455 base_window_signal_connect(
456 BASE_WINDOW( window ),
457 G_OBJECT( chooser ),
458 "selection-changed",
459 G_CALLBACK( on_file_selection_changed ));
460
461 window->private->file_chooser = chooser;
462
463 gtk_assistant_set_page_complete( assistant, page, FALSE );
464 }
465
466 static void
on_file_selection_changed(GtkFileChooser * chooser,gpointer user_data)467 on_file_selection_changed( GtkFileChooser *chooser, gpointer user_data )
468 {
469 static const gchar *thisfn = "nact_assistant_import_on_file_selection_changed";
470 GtkAssistant *assistant;
471 gint pos;
472 GSList *uris;
473 gboolean enabled;
474 gchar *folder;
475 GtkWidget *content;
476
477 g_assert( NACT_IS_ASSISTANT_IMPORT( user_data ));
478 assistant = GTK_ASSISTANT( base_window_get_gtk_toplevel( BASE_WINDOW( user_data )));
479 pos = gtk_assistant_get_current_page( assistant );
480 if( pos == ASSIST_PAGE_FILES_SELECTION ){
481
482 uris = gtk_file_chooser_get_uris( chooser );
483 enabled = has_loadable_files( uris );
484
485 if( enabled ){
486 /*
487 * if user has selected the 'Recently used' place in the file chooser,
488 * then the current folder uri is null
489 * (Gtk+ 3.2.0, don't know before...)
490 */
491 folder = gtk_file_chooser_get_current_folder_uri( GTK_FILE_CHOOSER( chooser ));
492 g_debug( "%s: current folder uri=%s", thisfn, folder );
493 if( folder && strlen( folder )){
494 na_settings_set_string( NA_IPREFS_IMPORT_ASSISTANT_URI, folder );
495 }
496 g_free( folder );
497 }
498
499 na_core_utils_slist_free( uris );
500
501 content = gtk_assistant_get_nth_page( assistant, pos );
502 gtk_assistant_set_page_complete( assistant, content, enabled );
503 gtk_assistant_update_buttons_state( assistant );
504 }
505 }
506
507 /*
508 * enable forward button if current selection has at least one loadable file
509 */
510 static gboolean
has_loadable_files(GSList * uris)511 has_loadable_files( GSList *uris )
512 {
513 GSList *iuri;
514 gchar *uri;
515 int loadables = 0;
516
517 for( iuri = uris ; iuri ; iuri = iuri->next ){
518 uri = ( gchar * ) iuri->data;
519
520 if( !strlen( uri )){
521 continue;
522 }
523
524 if( na_core_utils_file_is_loadable( uri )){
525 loadables += 1;
526 }
527 }
528
529 return( loadables > 0 );
530 }
531
532 static void
runtime_init_duplicates(NactAssistantImport * window,GtkAssistant * assistant)533 runtime_init_duplicates( NactAssistantImport *window, GtkAssistant *assistant )
534 {
535 static const gchar *thisfn = "nact_assistant_import_runtime_init_duplicates";
536 gchar *import_mode;
537 GtkWidget *page;
538 gboolean mandatory;
539
540 g_return_if_fail( GTK_IS_TREE_VIEW( window->private->duplicates_listview ));
541
542 g_debug( "%s: window=%p, assistant=%p",
543 thisfn, ( void * ) window, ( void * ) assistant );
544
545 import_mode = na_settings_get_string( NA_IPREFS_IMPORT_PREFERRED_MODE, NULL, &mandatory );
546 na_ioptions_list_set_editable(
547 NA_IOPTIONS_LIST( window ), GTK_WIDGET( window->private->duplicates_listview ),
548 !mandatory );
549 na_ioptions_list_set_default(
550 NA_IOPTIONS_LIST( window ), GTK_WIDGET( window->private->duplicates_listview ),
551 import_mode );
552 g_free( import_mode );
553
554 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_DUPLICATES );
555 gtk_assistant_set_page_complete( assistant, page, TRUE );
556 }
557
558 static void
assistant_prepare(BaseAssistant * window,GtkAssistant * assistant,GtkWidget * page)559 assistant_prepare( BaseAssistant *window, GtkAssistant *assistant, GtkWidget *page )
560 {
561 static const gchar *thisfn = "nact_assistant_import_assistant_prepare";
562 GtkAssistantPageType type;
563
564 g_debug( "%s: window=%p, assistant=%p, page=%p",
565 thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
566
567 type = gtk_assistant_get_page_type( assistant, page );
568
569 switch( type ){
570 case GTK_ASSISTANT_PAGE_CONFIRM:
571 prepare_confirm( NACT_ASSISTANT_IMPORT( window ), assistant, page );
572 break;
573
574 case GTK_ASSISTANT_PAGE_SUMMARY:
575 prepare_importdone( NACT_ASSISTANT_IMPORT( window ), assistant, page );
576 break;
577
578 default:
579 break;
580 }
581 }
582
583 static void
prepare_confirm(NactAssistantImport * window,GtkAssistant * assistant,GtkWidget * page)584 prepare_confirm( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page )
585 {
586 static const gchar *thisfn = "nact_assistant_import_prepare_confirm";
587 gchar *text, *tmp;
588 GSList *uris, *is;
589 GtkWidget *label;
590 gchar *mode_label, *label2, *mode_description;
591
592 g_debug( "%s: window=%p, assistant=%p, page=%p",
593 thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
594
595 #if !GTK_CHECK_VERSION( 3,0,0 )
596 /* Note that, at least, in Gtk 2.20 (Ubuntu 10) and 2.22 (Fedora 14), GtkLabel
597 * queues its resize (when the text is being set), but the actual resize does
598 * not happen immediately - We have to wait until Gtk 3.0, most probably due
599 * to the new width-for-height and height-for-width features...
600 */
601 GtkWidget *vbox = find_widget_from_page( page, "p3-ConfirmVBox" );
602 gtk_container_set_resize_mode( GTK_CONTAINER( vbox ), GTK_RESIZE_IMMEDIATE );
603 #endif
604
605 /* adding list of uris to import
606 */
607 text = NULL;
608 uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( window->private->file_chooser ));
609 for( is = uris ; is ; is = is->next ){
610 g_debug( "%s: uri=%s", thisfn, ( const gchar * ) is->data );
611
612 if( text ){
613 tmp = g_strdup_printf( "%s\n%s", text, ( const gchar * ) is->data );
614 g_free( text );
615 text = tmp;
616
617 } else {
618 text = g_strdup(( const gchar * ) is->data );
619 }
620 }
621 label = find_widget_from_page( page, "p3-ConfirmFilesList" );
622 g_return_if_fail( GTK_IS_LABEL( label ));
623 gtk_label_set_text( GTK_LABEL( label ), text );
624 g_free( text );
625
626 /* adding import mode
627 */
628 label = find_widget_from_page( page, "p3-ConfirmImportMode" );
629 g_return_if_fail( GTK_IS_LABEL( label ));
630 window->private->mode = na_ioptions_list_get_selected(
631 NA_IOPTIONS_LIST( window ), GTK_WIDGET( window->private->duplicates_listview ));
632 g_return_if_fail( NA_IS_IMPORT_MODE( window->private->mode ));
633 mode_label = na_ioption_get_label( window->private->mode );
634 label2 = na_core_utils_str_remove_char( mode_label, "_" );
635 mode_description = na_ioption_get_description( window->private->mode );
636 text = g_markup_printf_escaped( "%s\n\n<span style=\"italic\">%s</span>", label2, mode_description );
637 gtk_label_set_markup( GTK_LABEL( label ), text );
638 g_free( text );
639 g_free( mode_description );
640 g_free( mode_label );
641 g_free( label2 );
642
643 gtk_assistant_set_page_complete( assistant, page, TRUE );
644 }
645
646 /*
647 * do import here
648 */
649 static void
assistant_apply(BaseAssistant * wnd,GtkAssistant * assistant)650 assistant_apply( BaseAssistant *wnd, GtkAssistant *assistant )
651 {
652 static const gchar *thisfn = "nact_assistant_import_assistant_apply";
653 NactAssistantImport *window;
654 NAImporterParms importer_parms;
655 BaseWindow *main_window;
656 GList *import_results, *it;
657 GList *insertable_items, *overriden_items;
658 NAImporterResult *result;
659 NactApplication *application;
660 NAUpdater *updater;
661 NactTreeView *items_view;
662
663 g_return_if_fail( NACT_IS_ASSISTANT_IMPORT( wnd ));
664
665 g_debug( "%s: window=%p, assistant=%p", thisfn, ( void * ) wnd, ( void * ) assistant );
666 window = NACT_ASSISTANT_IMPORT( wnd );
667 g_object_get( G_OBJECT( window ), BASE_PROP_PARENT, &main_window, NULL );
668 application = NACT_APPLICATION( base_window_get_application( main_window ));
669 updater = nact_application_get_updater( application );
670
671 memset( &importer_parms, '\0', sizeof( NAImporterParms ));
672 importer_parms.uris = gtk_file_chooser_get_uris( GTK_FILE_CHOOSER( window->private->file_chooser ));
673 importer_parms.check_fn = ( NAImporterCheckFn ) check_for_existence;
674 importer_parms.check_fn_data = main_window;
675 importer_parms.preferred_mode = na_import_mode_get_id( NA_IMPORT_MODE( window->private->mode ));
676 importer_parms.parent_toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( wnd ));
677
678 import_results = na_importer_import_from_uris( NA_PIVOT( updater ), &importer_parms );
679
680 insertable_items = NULL;
681 overriden_items = NULL;
682
683 for( it = import_results ; it ; it = it->next ){
684 result = ( NAImporterResult * ) it->data;
685 if( result->imported ){
686
687 if( !result->exist || result->mode == IMPORTER_MODE_RENUMBER ){
688 insertable_items = g_list_prepend( insertable_items, result->imported );
689
690 } else if( result->mode == IMPORTER_MODE_OVERRIDE ){
691 overriden_items = g_list_prepend( overriden_items, result->imported );
692 }
693 }
694 }
695
696 na_core_utils_slist_free( importer_parms.uris );
697 window->private->results = import_results;
698
699 /* then insert the list
700 * assuring that actions will be inserted in the same order as uris
701 *
702 * the tree view (and its underlying tree store) takes a new reference
703 * on the inserted objects; the pointers so remain valid even after
704 * having released the imported_items list
705 */
706 if( insertable_items ){
707 insertable_items = g_list_reverse( insertable_items );
708 items_view = nact_main_window_get_items_view( NACT_MAIN_WINDOW( main_window ));
709 nact_tree_ieditable_insert_items( NACT_TREE_IEDITABLE( items_view ), insertable_items, NULL );
710 na_object_free_items( insertable_items );
711 }
712
713 /* contrarily, the tree store may or not take a new reference on overriding
714 * items, so do not release it here
715 */
716 if( overriden_items ){
717 items_view = nact_main_window_get_items_view( NACT_MAIN_WINDOW( main_window ));
718 nact_tree_ieditable_set_items( NACT_TREE_IEDITABLE( items_view ), overriden_items );
719 window->private->overriden = overriden_items;
720 }
721 }
722
723 static NAObjectItem *
check_for_existence(const NAObjectItem * item,NactMainWindow * window)724 check_for_existence( const NAObjectItem *item, NactMainWindow *window )
725 {
726 static const gchar *thisfn = "nact_assistant_import_check_for_existence";
727 NactTreeView *items_view;
728 NAObjectItem *exists;
729 gchar *importing_id;
730
731 importing_id = na_object_get_id( item );
732 g_debug( "%s: item=%p (%s), importing_id=%s",
733 thisfn, ( void * ) item, G_OBJECT_TYPE_NAME( item ), importing_id );
734
735 items_view = nact_main_window_get_items_view( window );
736 exists = nact_tree_view_get_item_by_id( items_view, importing_id );
737
738 g_free( importing_id );
739
740 return( exists );
741 }
742
743 /*
744 * summary page is a vbox inside of a scrolled window
745 * each line in this vbox is a GtkLabel
746 * Starting with 3.1.6, uri is displayed in red if an error has occured, or
747 * in blue.
748 */
749 static void
prepare_importdone(NactAssistantImport * window,GtkAssistant * assistant,GtkWidget * page)750 prepare_importdone( NactAssistantImport *window, GtkAssistant *assistant, GtkWidget *page )
751 {
752 static const gchar *thisfn = "nact_assistant_import_prepare_importdone";
753 guint width;
754 GtkWidget *vbox;
755 GtkWidget *file_vbox, *file_uri, *file_report;
756 GList *is;
757 GSList *im;
758 NAImporterResult *result;
759 gchar *text, *id, *item_label, *text2, *tmp;
760 const gchar *color;
761 gchar *mode_id;
762
763 g_debug( "%s: window=%p, assistant=%p, page=%p",
764 thisfn, ( void * ) window, ( void * ) assistant, ( void * ) page );
765
766 width = 15;
767 vbox = find_widget_from_page( page, "p4-SummaryVBox" );
768 g_return_if_fail( GTK_IS_BOX( vbox ));
769
770 #if !GTK_CHECK_VERSION( 3,0,0 )
771 /* Note that, at least, in Gtk 2.20 (Ubuntu 10) and 2.22 (Fedora 14), GtkLabel
772 * queues its resize (when the text is being set), but the actual resize does
773 * not happen immediately - We have to wait until Gtk 3.0, most probably due
774 * to the new width-for-height and height-for-width features...
775 */
776 gtk_container_set_resize_mode( GTK_CONTAINER( vbox ), GTK_RESIZE_IMMEDIATE );
777 #endif
778
779 /* for each uri
780 * - display the uri
781 * - display a brief import log
782 */
783 for( is = window->private->results ; is ; is = is->next ){
784 result = ( NAImporterResult * ) is->data;
785 g_debug( "%s: uri=%s", thisfn, result->uri );
786
787 /* display the uri
788 */
789 #if GTK_CHECK_VERSION( 3,0,0 )
790 file_vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 4 );
791 #else
792 file_vbox = gtk_vbox_new( FALSE, 4 );
793 #endif
794 gtk_box_pack_start( GTK_BOX( vbox ), file_vbox, FALSE, FALSE, 0 );
795
796 color = result->imported ? "blue" : "red";
797 text = g_markup_printf_escaped( "<span foreground=\"%s\">%s</span>", color, result->uri );
798 file_uri = gtk_label_new( NULL );
799 gtk_label_set_markup( GTK_LABEL( file_uri ), text );
800 g_free( text );
801 g_object_set( G_OBJECT( file_uri ), "xalign", 0, NULL );
802 g_object_set( G_OBJECT( file_uri ), "xpad", width, NULL );
803 gtk_box_pack_start( GTK_BOX( file_vbox ), file_uri, FALSE, FALSE, 0 );
804
805 /* display the import log
806 */
807 if( result->imported ){
808 /* i18n: indicate that the file has been successfully imported */
809 text = g_strdup( _( "Import OK" ));
810 id = na_object_get_id( result->imported );
811 item_label = na_object_get_label( result->imported );
812 /* i18n: this is the globally unique identifier and the label of the newly imported action */
813 text2 = g_strdup_printf( _( "Id.: %s\t%s" ), id, item_label);
814 g_free( item_label );
815 g_free( id );
816 tmp = g_strdup_printf( "%s\n%s", text, text2 );
817 g_free( text );
818 g_free( text2 );
819 text = tmp;
820
821 } else {
822 /* i18n: indicate that the file was not imported */
823 text = g_strdup( _( "Not imported" ));
824 }
825
826 /* add messages if any
827 */
828 for( im = result->messages ; im ; im = im->next ){
829 tmp = g_strdup_printf( "%s\n%s", text, ( const char * ) im->data );
830 g_free( text );
831 text = tmp;
832 }
833
834 file_report = gtk_label_new( text );
835 gtk_label_set_line_wrap( GTK_LABEL( file_report ), TRUE );
836 gtk_label_set_line_wrap_mode( GTK_LABEL( file_report ), PANGO_WRAP_WORD );
837 g_object_set( G_OBJECT( file_report ), "xalign", 0, NULL );
838 g_object_set( G_OBJECT( file_report ), "xpad", 2*width, NULL );
839 gtk_box_pack_start( GTK_BOX( file_vbox ), file_report, FALSE, FALSE, 0 );
840 }
841
842 mode_id = na_ioption_get_id( window->private->mode );
843 na_settings_set_string( NA_IPREFS_IMPORT_PREFERRED_MODE, mode_id );
844 g_free( mode_id );
845
846 /* release here our reference on overriding items
847 */
848 if( window->private->overriden ){
849 na_object_free_items( window->private->overriden );
850 }
851
852 g_object_set( G_OBJECT( window ), BASE_PROP_WARN_ON_ESCAPE, FALSE, NULL );
853 gtk_assistant_set_page_complete( assistant, page, TRUE );
854 gtk_widget_show_all( page );
855 }
856
857 static void
free_results(GList * list)858 free_results( GList *list )
859 {
860 GList *it;
861
862 for( it = list ; it ; it = it->next ){
863 na_importer_free_result(( NAImporterResult * ) it->data );
864 }
865
866 g_list_free( list );
867 }
868
869 static GtkWidget *
find_widget_from_page(GtkWidget * page,const gchar * name)870 find_widget_from_page( GtkWidget *page, const gchar *name )
871 {
872 GtkWidget *widget;
873
874 g_return_val_if_fail( GTK_IS_CONTAINER( page ), NULL );
875
876 widget = na_gtk_utils_find_widget_by_name( GTK_CONTAINER( page ), name );
877
878 return( widget );
879 }
880
881 static GtkTreeView *
get_duplicates_treeview_from_assistant_import(NactAssistantImport * window)882 get_duplicates_treeview_from_assistant_import( NactAssistantImport *window )
883 {
884 GtkAssistant *assistant;
885 GtkWidget *page;
886
887 g_return_val_if_fail( NACT_IS_ASSISTANT_IMPORT( window ), NULL );
888
889 assistant = GTK_ASSISTANT( base_window_get_gtk_toplevel( BASE_WINDOW( window )));
890 page = gtk_assistant_get_nth_page( assistant, ASSIST_PAGE_DUPLICATES );
891
892 return( get_duplicates_treeview_from_page( page ));
893 }
894
895 static GtkTreeView *
get_duplicates_treeview_from_page(GtkWidget * page)896 get_duplicates_treeview_from_page( GtkWidget *page )
897 {
898 GtkWidget *listview;
899
900 listview = find_widget_from_page( page, "p2-AskTreeView" );
901
902 g_return_val_if_fail( GTK_IS_TREE_VIEW( listview ), NULL );
903
904 return( GTK_TREE_VIEW( listview ));
905 }
906