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