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 <glib/gi18n.h>
35 
36 #include <api/na-object-api.h>
37 
38 #include <core/na-exporter.h>
39 #include <core/na-export-format.h>
40 #include <core/na-gtk-utils.h>
41 #include <core/na-ioptions-list.h>
42 
43 #include "nact-application.h"
44 #include "nact-export-ask.h"
45 #include "base-gtk-utils.h"
46 
47 /* private class data
48  */
49 struct _NactExportAskClassPrivate {
50 	void *empty;						/* so that gcc -pedantic is happy */
51 };
52 
53 /* private instance data
54  */
55 struct _NactExportAskPrivate {
56 	gboolean      dispose_has_run;
57 	BaseWindow   *parent;
58 	gboolean      preferences_locked;
59 	NAObjectItem *item;
60 	gchar        *format;
61 	gboolean      format_mandatory;
62 	gboolean      keep_last_choice;
63 	gboolean      keep_last_choice_mandatory;
64 };
65 
66 static const gchar     *st_xmlui_filename = PKGUIDIR "/nact-assistant-export.ui";
67 static const gchar     *st_toplevel_name  = "ExportAskDialog";
68 static const gchar     *st_wsp_name       = NA_IPREFS_EXPORT_ASK_USER_WSP;
69 
70 static BaseDialogClass *st_parent_class   = NULL;
71 
72 static GType    register_type( void );
73 static void     class_init( NactExportAskClass *klass );
74 static void     ioptions_list_iface_init( NAIOptionsListInterface *iface, void *user_data );
75 static GList   *ioptions_list_get_formats( const NAIOptionsList *instance, GtkWidget *container );
76 static void     ioptions_list_free_formats( const NAIOptionsList *instance, GtkWidget *container, GList *formats );
77 static void     instance_init( GTypeInstance *instance, gpointer klass );
78 static void     instance_constructed( GObject *dialog );
79 static void     instance_dispose( GObject *dialog );
80 static void     instance_finalize( GObject *dialog );
81 
82 static void     on_base_initialize_gtk( NactExportAsk *editor, GtkDialog *toplevel, gpointer user_data );
83 static void     on_base_initialize_window( NactExportAsk *editor, gpointer user_data );
84 static void     keep_choice_on_toggled( GtkToggleButton *button, NactExportAsk *editor );
85 static void     on_cancel_clicked( GtkButton *button, NactExportAsk *editor );
86 static void     on_ok_clicked( GtkButton *button, NactExportAsk *editor );
87 static gchar   *get_export_format( NactExportAsk *editor );
88 
89 GType
nact_export_ask_get_type(void)90 nact_export_ask_get_type( void )
91 {
92 	static GType dialog_type = 0;
93 
94 	if( !dialog_type ){
95 		dialog_type = register_type();
96 	}
97 
98 	return( dialog_type );
99 }
100 
101 static GType
register_type(void)102 register_type( void )
103 {
104 	static const gchar *thisfn = "nact_export_ask_register_type";
105 	GType type;
106 
107 	static GTypeInfo info = {
108 		sizeof( NactExportAskClass ),
109 		( GBaseInitFunc ) NULL,
110 		( GBaseFinalizeFunc ) NULL,
111 		( GClassInitFunc ) class_init,
112 		NULL,
113 		NULL,
114 		sizeof( NactExportAsk ),
115 		0,
116 		( GInstanceInitFunc ) instance_init
117 	};
118 
119 	static const GInterfaceInfo ioptions_list_iface_info = {
120 		( GInterfaceInitFunc ) ioptions_list_iface_init,
121 		NULL,
122 		NULL
123 	};
124 
125 	g_debug( "%s", thisfn );
126 
127 	type = g_type_register_static( BASE_TYPE_DIALOG, "NactExportAsk", &info, 0 );
128 
129 	g_type_add_interface_static( type, NA_TYPE_IOPTIONS_LIST, &ioptions_list_iface_info );
130 
131 	return( type );
132 }
133 
134 static void
class_init(NactExportAskClass * klass)135 class_init( NactExportAskClass *klass )
136 {
137 	static const gchar *thisfn = "nact_export_ask_class_init";
138 	GObjectClass *object_class;
139 
140 	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
141 
142 	st_parent_class = g_type_class_peek_parent( klass );
143 
144 	object_class = G_OBJECT_CLASS( klass );
145 	object_class->constructed = instance_constructed;
146 	object_class->dispose = instance_dispose;
147 	object_class->finalize = instance_finalize;
148 
149 	klass->private = g_new0( NactExportAskClassPrivate, 1 );
150 }
151 
152 static void
ioptions_list_iface_init(NAIOptionsListInterface * iface,void * user_data)153 ioptions_list_iface_init( NAIOptionsListInterface *iface, void *user_data )
154 {
155 	static const gchar *thisfn = "nact_assistant_export_ioptions_list_iface_init";
156 
157 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
158 
159 	iface->get_options = ioptions_list_get_formats;
160 	iface->free_options = ioptions_list_free_formats;
161 }
162 
163 static GList *
ioptions_list_get_formats(const NAIOptionsList * instance,GtkWidget * container)164 ioptions_list_get_formats( const NAIOptionsList *instance, GtkWidget *container )
165 {
166 	NactExportAsk *window;
167 	NactApplication *application;
168 	NAUpdater *updater;
169 	GList *formats;
170 
171 	g_return_val_if_fail( NACT_IS_EXPORT_ASK( instance ), NULL );
172 	window = NACT_EXPORT_ASK( instance );
173 
174 	application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
175 	updater = nact_application_get_updater( application );
176 	formats = na_exporter_get_formats( NA_PIVOT( updater ));
177 
178 	return( formats );
179 }
180 
181 static void
ioptions_list_free_formats(const NAIOptionsList * instance,GtkWidget * container,GList * formats)182 ioptions_list_free_formats( const NAIOptionsList *instance, GtkWidget *container, GList *formats )
183 {
184 	na_exporter_free_formats( formats );
185 }
186 
187 static void
instance_init(GTypeInstance * instance,gpointer klass)188 instance_init( GTypeInstance *instance, gpointer klass )
189 {
190 	static const gchar *thisfn = "nact_export_ask_instance_init";
191 	NactExportAsk *self;
192 
193 	g_return_if_fail( NACT_IS_EXPORT_ASK( instance ));
194 
195 	g_debug( "%s: instance=%p (%s), klass=%p",
196 			thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
197 
198 	self = NACT_EXPORT_ASK( instance );
199 
200 	self->private = g_new0( NactExportAskPrivate, 1 );
201 
202 	self->private->dispose_has_run = FALSE;
203 }
204 
205 static void
instance_constructed(GObject * dialog)206 instance_constructed( GObject *dialog )
207 {
208 	static const gchar *thisfn = "nact_export_ask_instance_constructed";
209 	NactExportAskPrivate *priv;
210 
211 	g_return_if_fail( NACT_IS_EXPORT_ASK( dialog ));
212 
213 	priv = NACT_EXPORT_ASK( dialog )->private;
214 
215 	if( !priv->dispose_has_run ){
216 
217 		/* chain up to the parent class */
218 		if( G_OBJECT_CLASS( st_parent_class )->constructed ){
219 			G_OBJECT_CLASS( st_parent_class )->constructed( dialog );
220 		}
221 
222 		g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
223 
224 		base_window_signal_connect(
225 				BASE_WINDOW( dialog ),
226 				G_OBJECT( dialog ),
227 				BASE_SIGNAL_INITIALIZE_GTK,
228 				G_CALLBACK( on_base_initialize_gtk ));
229 
230 		base_window_signal_connect(
231 				BASE_WINDOW( dialog ),
232 				G_OBJECT( dialog ),
233 				BASE_SIGNAL_INITIALIZE_WINDOW,
234 				G_CALLBACK( on_base_initialize_window ));
235 	}
236 }
237 
238 static void
instance_dispose(GObject * dialog)239 instance_dispose( GObject *dialog )
240 {
241 	static const gchar *thisfn = "nact_export_ask_instance_dispose";
242 	NactExportAsk *self;
243 
244 	g_return_if_fail( NACT_IS_EXPORT_ASK( dialog ));
245 
246 	self = NACT_EXPORT_ASK( dialog );
247 
248 	if( !self->private->dispose_has_run ){
249 		g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
250 
251 		self->private->dispose_has_run = TRUE;
252 
253 		/* chain up to the parent class */
254 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
255 			G_OBJECT_CLASS( st_parent_class )->dispose( dialog );
256 		}
257 	}
258 }
259 
260 static void
instance_finalize(GObject * dialog)261 instance_finalize( GObject *dialog )
262 {
263 	static const gchar *thisfn = "nact_export_ask_instance_finalize";
264 	NactExportAsk *self;
265 
266 	g_return_if_fail( NACT_IS_EXPORT_ASK( dialog ));
267 
268 	g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
269 
270 	self = NACT_EXPORT_ASK( dialog );
271 
272 	g_free( self->private->format );
273 
274 	g_free( self->private );
275 
276 	/* chain call to parent class */
277 	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
278 		G_OBJECT_CLASS( st_parent_class )->finalize( dialog );
279 	}
280 }
281 
282 /**
283  * nact_export_ask_user:
284  * @parent: the NactAssistant parent of this dialog.
285  * @item: the NAObjectItem to be exported.
286  * @first: whether this is the first call of a serie.
287  *  On a first call, the user is really asked for his choice.
288  *  The next times, the 'keep-last-choice' flag will be considered.
289  *
290  * Initializes and runs the dialog.
291  *
292  * This is a small dialog which is to be ran during export operations,
293  * when the set export format is 'Ask me'. Each exported file runs this
294  * dialog, unless the user selects the 'keep same choice' box.
295  *
296  * Returns: the export format chosen by the user as a newly allocated
297  * string which should be g_free() by the caller.
298  * The function defaults to returning NA_IPREFS_DEFAULT_EXPORT_FORMAT.
299  *
300  * When the user selects 'Keep same choice without asking me', this choice
301  * becomes his new preferred export format.
302  */
303 gchar *
nact_export_ask_user(BaseWindow * parent,NAObjectItem * item,gboolean first)304 nact_export_ask_user( BaseWindow *parent, NAObjectItem *item, gboolean first )
305 {
306 	static const gchar *thisfn = "nact_export_ask_user";
307 	NactExportAsk *editor;
308 	gboolean are_locked, mandatory;
309 	gboolean keep, keep_mandatory;
310 	int code;
311 	gchar *format;
312 
313 	g_return_val_if_fail( BASE_IS_WINDOW( parent ), NULL );
314 
315 	g_debug( "%s: parent=%p, item=%p (%s), first=%s",
316 			thisfn,
317 			( void * ) parent,
318 			( void * ) item, G_OBJECT_TYPE_NAME( item ),
319 			first ? "True":"False" );
320 
321 	format = na_settings_get_string( NA_IPREFS_EXPORT_ASK_USER_LAST_FORMAT, NULL, &mandatory );
322 	keep = na_settings_get_boolean( NA_IPREFS_EXPORT_ASK_USER_KEEP_LAST_CHOICE, NULL, &keep_mandatory );
323 
324 	if( first || !keep ){
325 		editor = g_object_new( NACT_TYPE_EXPORT_ASK,
326 				BASE_PROP_PARENT,         parent,
327 				BASE_PROP_XMLUI_FILENAME, st_xmlui_filename,
328 				BASE_PROP_TOPLEVEL_NAME,  st_toplevel_name,
329 				BASE_PROP_WSP_NAME,       st_wsp_name,
330 				NULL );
331 
332 		editor->private->format = g_strdup( format );
333 		editor->private->format_mandatory = mandatory;
334 		editor->private->keep_last_choice = keep;
335 		editor->private->keep_last_choice_mandatory = keep_mandatory;
336 		editor->private->parent = parent;
337 		editor->private->item = item;
338 
339 		are_locked = na_settings_get_boolean( NA_IPREFS_ADMIN_PREFERENCES_LOCKED, NULL, &mandatory );
340 		editor->private->preferences_locked = are_locked && mandatory;
341 		code = base_window_run( BASE_WINDOW( editor ));
342 
343 		switch( code ){
344 			case GTK_RESPONSE_OK:
345 				g_free( format );
346 				format = get_export_format( editor );
347 				break;
348 
349 			case GTK_RESPONSE_CANCEL:
350 			/* base_dialog::do_run only returns OK or CANCEL */
351 			default:
352 				g_free( format );
353 				format = g_strdup( EXPORTER_FORMAT_NOEXPORT );
354 				break;
355 		}
356 
357 		g_object_unref( editor );
358 	}
359 
360 	return( format );
361 }
362 
363 static void
on_base_initialize_gtk(NactExportAsk * editor,GtkDialog * toplevel,gpointer user_data)364 on_base_initialize_gtk( NactExportAsk *editor, GtkDialog *toplevel, gpointer user_data )
365 {
366 	static const gchar *thisfn = "nact_export_ask_on_base_initialize_gtk";
367 	GtkWidget *container;
368 
369 	g_return_if_fail( NACT_IS_EXPORT_ASK( editor ));
370 
371 	if( !editor->private->dispose_has_run ){
372 
373 		g_debug( "%s: dialog=%p, toplevel=%p, user_data=%p",
374 				thisfn, ( void * ) editor, ( void * ) toplevel, ( void * ) user_data );
375 
376 		container = base_window_get_widget( BASE_WINDOW( editor ), "ExportFormatAskVBox" );
377 		na_ioptions_list_gtk_init( NA_IOPTIONS_LIST( editor ), container, FALSE );
378 
379 #if !GTK_CHECK_VERSION( 2,22,0 )
380 		gtk_dialog_set_has_separator( toplevel, FALSE );
381 #endif
382 	}
383 }
384 
385 static void
on_base_initialize_window(NactExportAsk * editor,gpointer user_data)386 on_base_initialize_window( NactExportAsk *editor, gpointer user_data )
387 {
388 	static const gchar *thisfn = "nact_export_ask_on_base_initialize_window";
389 	gchar *item_label, *label;
390 	GtkWidget *widget;
391 	NactExportAskPrivate *priv;
392 
393 	g_return_if_fail( NACT_IS_EXPORT_ASK( editor ));
394 
395 	priv = editor->private;
396 
397 	if( !priv->dispose_has_run ){
398 
399 		g_debug( "%s: dialog=%p, user_data=%p", thisfn, ( void * ) editor, ( void * ) user_data );
400 
401 		item_label = na_object_get_label( priv->item );
402 
403 		if( NA_IS_OBJECT_ACTION( priv->item )){
404 			/* i18n: The action <label> is about to be exported */
405 			label = g_strdup_printf( _( "The action \"%s\" is about to be exported." ), item_label );
406 		} else {
407 			/* i18n: The menu <label> is about to be exported */
408 			label = g_strdup_printf( _( "The menu \"%s\" is about to be exported." ), item_label );
409 		}
410 
411 		widget = base_window_get_widget( BASE_WINDOW( editor ), "ExportAskLabel" );
412 		gtk_label_set_text( GTK_LABEL( widget ), label );
413 		g_free( label );
414 		g_free( item_label );
415 
416 		widget = base_window_get_widget( BASE_WINDOW( editor ), "ExportFormatAskVBox" );
417 		na_ioptions_list_set_editable(
418 				NA_IOPTIONS_LIST( editor ), widget,
419 				!priv->format_mandatory && !priv->preferences_locked );
420 		na_ioptions_list_set_default(
421 				NA_IOPTIONS_LIST( editor ), widget,
422 				priv->format );
423 
424 		base_gtk_utils_toggle_set_initial_state( BASE_WINDOW( editor ),
425 				"AskKeepChoiceButton", G_CALLBACK( keep_choice_on_toggled ),
426 				priv->keep_last_choice,
427 				!priv->keep_last_choice_mandatory, !priv->preferences_locked );
428 
429 		base_window_signal_connect_by_name(
430 				BASE_WINDOW( editor ),
431 				"CancelButton",
432 				"clicked",
433 				G_CALLBACK( on_cancel_clicked ));
434 
435 		base_window_signal_connect_by_name(
436 				BASE_WINDOW( editor ),
437 				"OKButton",
438 				"clicked",
439 				G_CALLBACK( on_ok_clicked ));
440 	}
441 }
442 
443 static void
keep_choice_on_toggled(GtkToggleButton * button,NactExportAsk * editor)444 keep_choice_on_toggled( GtkToggleButton *button, NactExportAsk *editor )
445 {
446 	gboolean editable;
447 
448 	editable = ( gboolean ) GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( button ), NA_TOGGLE_DATA_EDITABLE ));
449 
450 	if( editable ){
451 		editor->private->keep_last_choice = gtk_toggle_button_get_active( button );
452 
453 	} else {
454 		base_gtk_utils_toggle_reset_initial_state( button );
455 	}
456 }
457 
458 static void
on_cancel_clicked(GtkButton * button,NactExportAsk * editor)459 on_cancel_clicked( GtkButton *button, NactExportAsk *editor )
460 {
461 	GtkWindow *toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( editor ));
462 	gtk_dialog_response( GTK_DIALOG( toplevel ), GTK_RESPONSE_CLOSE );
463 }
464 
465 static void
on_ok_clicked(GtkButton * button,NactExportAsk * editor)466 on_ok_clicked( GtkButton *button, NactExportAsk *editor )
467 {
468 	GtkWindow *toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( editor ));
469 	gtk_dialog_response( GTK_DIALOG( toplevel ), GTK_RESPONSE_OK );
470 }
471 
472 /*
473  * we have come here because preferred_export_format was 'Ask'
474  * in all cases, the chosen format is kept in 'ask_user_last_chosen_format'
475  * and so this will be the default format which will be proposed the next
476  * time we come here
477  * if the 'remember_my_choice' is cheecked, then we do not even ask the user
478  * but returns as soon as we enter
479  *
480  * not overrinding the preferred export format (i.e. letting it to 'Ask')
481  * let the user go back in ask dialog box the next time he will have some
482  * files to export
483  */
484 static gchar *
get_export_format(NactExportAsk * editor)485 get_export_format( NactExportAsk *editor )
486 {
487 	GtkWidget *widget;
488 	NAIOption *format;
489 	gchar *format_id;
490 
491 	widget = base_window_get_widget( BASE_WINDOW( editor ), "ExportFormatAskVBox" );
492 	format = na_ioptions_list_get_selected( NA_IOPTIONS_LIST( editor ), widget );
493 	g_return_val_if_fail( NA_IS_EXPORT_FORMAT( format ), 0 );
494 
495 	if( !editor->private->keep_last_choice_mandatory ){
496 		na_settings_set_boolean( NA_IPREFS_EXPORT_ASK_USER_KEEP_LAST_CHOICE, editor->private->keep_last_choice );
497 	}
498 
499 	format_id = na_ioption_get_id( format );
500 	na_settings_set_string( NA_IPREFS_EXPORT_ASK_USER_LAST_FORMAT, format_id );
501 
502 	return( format_id );
503 }
504