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