1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * Engrampa
5 *
6 * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include <config.h>
24 #include <string.h>
25 #include <math.h>
26 #include <unistd.h>
27 #include <gio/gio.h>
28 #include <gtk/gtk.h>
29 #include "dlg-batch-add.h"
30 #include "file-utils.h"
31 #include "fr-init.h"
32 #include "fr-window.h"
33 #include "gtk-utils.h"
34 #include "glib-utils.h"
35 #include "preferences.h"
36 #include "typedefs.h"
37
38
39 #define BAD_CHARS "/\\*"
40 #define GET_ENTRY(x) (GTK_ENTRY (gtk_builder_get_object (data->builder, (x))))
41 #define GET_FILE_CHOOSER(x) (GTK_FILE_CHOOSER (gtk_builder_get_object (data->builder, (x))))
42 #define GET_TOGGLE_BUTTON(x) (GTK_TOGGLE_BUTTON (gtk_builder_get_object (data->builder, (x))))
43 #define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (data->builder, (x))))
44 #define GET_WINDOW(x) (GTK_WINDOW (gtk_builder_get_object (data->builder, (x))))
45
46 static gboolean has_password = FALSE;
47 static gboolean can_encrypt_header = FALSE;
48
49
50 typedef struct {
51 FrWindow *window;
52 GSettings *settings;
53 GSettings *settings_general;
54 GtkBuilder *builder;
55 int *supported_types;
56 GtkWidget *archive_type_combo_box;
57 GList *file_list;
58 gboolean add_clicked;
59 const char *last_mime_type;
60 gboolean single_file;
61 } DialogData;
62
63
64 static const char *
get_ext(DialogData * data)65 get_ext (DialogData *data)
66 {
67 int idx;
68
69 idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->archive_type_combo_box));
70
71 return mime_type_desc[data->supported_types[idx]].default_ext;
72 }
73
74
75 /* called when the main dialog is closed. */
76 static void
destroy_cb(GtkWidget * widget,DialogData * data)77 destroy_cb (GtkWidget *widget,
78 DialogData *data)
79 {
80 g_settings_set_string (data->settings, PREF_BATCH_ADD_DEFAULT_EXTENSION, get_ext (data));
81 /*g_settings_set_boolean (data->settings, PREF_BATCH_ADD_OTHER_OPTIONS, data->add_clicked ? FALSE : gtk_expander_get_expanded (GTK_EXPANDER (GET_WIDGET ("a_other_options_expander"))));*/
82 g_settings_set_boolean (data->settings_general, PREF_GENERAL_ENCRYPT_HEADER, gtk_toggle_button_get_active (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton")));
83
84
85 if (! data->add_clicked) {
86 fr_window_pop_message (data->window);
87 fr_window_stop_batch (data->window);
88 }
89
90 g_object_unref (data->builder);
91 g_object_unref (data->settings_general);
92 g_object_unref (data->settings);
93 g_free (data);
94 }
95
96
97 static void
set_archive_options(DialogData * data)98 set_archive_options (DialogData *data)
99 {
100 int idx;
101
102 idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->archive_type_combo_box));
103 if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT) {
104 const char *pwd;
105
106 pwd = gtk_entry_get_text (GET_ENTRY ("a_password_entry"));
107 if (pwd != NULL) {
108 if (strcmp (pwd, "") != 0) {
109 fr_window_set_password (data->window, pwd);
110 if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER)
111 fr_window_set_encrypt_header (data->window, gtk_toggle_button_get_active (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton")));
112 }
113 }
114 }
115
116 if ((mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES)
117 && gtk_toggle_button_get_active (GET_TOGGLE_BUTTON ("a_volume_checkbutton")))
118 {
119 double value;
120 int size;
121
122 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (GET_WIDGET ("a_volume_spinbutton")));
123 size = floor (value * MEGABYTE);
124 g_settings_set_int (data->settings, PREF_BATCH_ADD_VOLUME_SIZE, size);
125 fr_window_set_volume_size (data->window, (guint) size);
126 }
127 }
128
129
130 static void
help_clicked_cb(GtkWidget * widget,DialogData * data)131 help_clicked_cb (GtkWidget *widget,
132 DialogData *data)
133 {
134 show_help_dialog (GET_WINDOW ("dialog"), "engrampa-fmgr-add");
135 }
136
137
138 static void
add_clicked_cb(GtkWidget * widget,DialogData * data)139 add_clicked_cb (GtkWidget *widget,
140 DialogData *data)
141 {
142 FrWindow *window = data->window;
143 char *archive_name;
144 char *archive_dir;
145 char *archive_file;
146 char *tmp;
147 const char *archive_ext;
148 gboolean do_not_add = FALSE;
149 GError *error = NULL;
150
151 data->add_clicked = TRUE;
152
153 /* Collect data */
154
155 archive_name = g_uri_escape_string (gtk_entry_get_text (GET_ENTRY ("a_add_to_entry")), NULL, FALSE);
156
157 /* Check whether the user entered a valid archive name. */
158
159 if ((archive_name == NULL) || (*archive_name == '\0')) {
160 GtkWidget *d;
161
162 d = _gtk_error_dialog_new (GTK_WINDOW (window),
163 GTK_DIALOG_DESTROY_WITH_PARENT,
164 NULL,
165 _("Could not create the archive"),
166 "%s",
167 _("You have to specify an archive name."));
168 gtk_dialog_run (GTK_DIALOG (d));
169 gtk_widget_destroy (GTK_WIDGET (d));
170 g_free (archive_name);
171
172 return;
173 }
174 else if (strchrs (archive_name, BAD_CHARS)) {
175 GtkWidget *d;
176 char *utf8_name = g_filename_display_name (archive_name);
177
178 d = _gtk_error_dialog_new (GTK_WINDOW (window),
179 GTK_DIALOG_DESTROY_WITH_PARENT,
180 NULL,
181 _("Could not create the archive"),
182 _("The name \"%s\" is not valid because it cannot contain the characters: %s\n\n%s"),
183 utf8_name,
184 BAD_CHARS,
185 _("Please use a different name."));
186 gtk_dialog_run (GTK_DIALOG (d));
187 gtk_widget_destroy (GTK_WIDGET (d));
188
189 g_free (utf8_name);
190 g_free (archive_name);
191
192 return;
193 }
194
195 /* Check directory existence. */
196
197 archive_dir = gtk_file_chooser_get_uri (GET_FILE_CHOOSER ("a_location_filechooserbutton"));
198 if (archive_dir == NULL) {
199 g_free (archive_dir);
200 g_free (archive_name);
201 return;
202 }
203
204 if (! check_permissions (archive_dir, R_OK|W_OK|X_OK)) {
205 GtkWidget *d;
206
207 d = _gtk_error_dialog_new (GTK_WINDOW (window),
208 GTK_DIALOG_DESTROY_WITH_PARENT,
209 NULL,
210 _("Could not create the archive"),
211 "%s",
212 _("You don't have the right permissions to create an archive in the destination folder."));
213 gtk_dialog_run (GTK_DIALOG (d));
214 gtk_widget_destroy (GTK_WIDGET (d));
215
216 g_free (archive_dir);
217 g_free (archive_name);
218 return;
219 }
220
221 if (! uri_is_dir (archive_dir)) {
222 GtkWidget *d;
223 int r;
224 char *folder_name;
225 char *msg;
226
227 folder_name = g_filename_display_name (archive_dir);
228 msg = g_strdup_printf (_("Destination folder \"%s\" does not exist.\n\nDo you want to create it?"), folder_name);
229 g_free (folder_name);
230
231 d = _gtk_message_dialog_new (GET_WINDOW ("dialog"),
232 GTK_DIALOG_MODAL,
233 "dialog-question",
234 msg,
235 NULL,
236 "gtk-cancel", GTK_RESPONSE_CANCEL,
237 _("Create _Folder"), GTK_RESPONSE_YES,
238 NULL);
239
240 gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
241 r = gtk_dialog_run (GTK_DIALOG (d));
242 gtk_widget_destroy (GTK_WIDGET (d));
243
244 g_free (msg);
245
246 do_not_add = (r != GTK_RESPONSE_YES);
247 }
248
249 if (! do_not_add && ! ensure_dir_exists (archive_dir, 0755, &error)) {
250 GtkWidget *d;
251
252 d = _gtk_error_dialog_new (GTK_WINDOW (window),
253 GTK_DIALOG_DESTROY_WITH_PARENT,
254 NULL,
255 _("Could not create the archive"),
256 _("Could not create the destination folder: %s."),
257 error->message);
258 gtk_dialog_run (GTK_DIALOG (d));
259 gtk_widget_destroy (GTK_WIDGET (d));
260
261 g_error_free (error);
262 g_free (archive_dir);
263 g_free (archive_name);
264 return;
265 }
266
267 if (do_not_add) {
268 GtkWidget *d;
269
270 d = _gtk_message_dialog_new (GTK_WINDOW (window),
271 GTK_DIALOG_DESTROY_WITH_PARENT,
272 "dialog-warning",
273 _("Archive not created"),
274 NULL,
275 "gtk-ok", GTK_RESPONSE_OK,
276 NULL);
277 gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK);
278 gtk_dialog_run (GTK_DIALOG (d));
279 gtk_widget_destroy (GTK_WIDGET (d));
280
281 g_free (archive_dir);
282 g_free (archive_name);
283
284 return;
285 }
286
287 /**/
288
289 archive_ext = get_ext (data);
290 tmp = archive_name;
291 archive_name = g_strconcat (tmp, archive_ext, NULL);
292 g_free (tmp);
293 archive_file = g_strconcat (archive_dir, "/", archive_name, NULL);
294
295 if (uri_is_dir (archive_file)) {
296 GtkWidget *d;
297
298 d = _gtk_error_dialog_new (GTK_WINDOW (window),
299 GTK_DIALOG_DESTROY_WITH_PARENT,
300 NULL,
301 _("Could not create the archive"),
302 "%s",
303 _("You have to specify an archive name."));
304 gtk_dialog_run (GTK_DIALOG (d));
305 gtk_widget_destroy (GTK_WIDGET (d));
306
307 g_free (archive_name);
308 g_free (archive_dir);
309 g_free (archive_file);
310
311 return;
312 }
313
314 if (uri_exists (archive_file)) {
315 GtkWidget *d;
316 int r;
317
318 d = _gtk_message_dialog_new (GET_WINDOW ("dialog"),
319 GTK_DIALOG_MODAL,
320 "dialog-question",
321 _("The archive is already present. Do you want to overwrite it?"),
322 NULL,
323 "gtk-no", GTK_RESPONSE_NO,
324 _("_Overwrite"), GTK_RESPONSE_YES,
325 NULL);
326
327 gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
328 r = gtk_dialog_run (GTK_DIALOG (d));
329 gtk_widget_destroy (GTK_WIDGET (d));
330
331 if (r == GTK_RESPONSE_YES) {
332 GFile *file;
333 GError *err = NULL;
334
335 file = g_file_new_for_uri (archive_file);
336 g_file_delete (file, NULL, &err);
337 if (err != NULL) {
338 g_warning ("Failed to delete file %s: %s",
339 archive_file,
340 err->message);
341 g_clear_error (&err);
342 }
343 g_object_unref (file);
344 }
345 else {
346 g_free (archive_name);
347 g_free (archive_dir);
348 g_free (archive_file);
349 return;
350 }
351 }
352 set_archive_options (data);
353 gtk_widget_destroy (GET_WIDGET ("dialog"));
354
355 fr_window_archive_new (window, archive_file);
356
357 g_free (archive_name);
358 g_free (archive_dir);
359 g_free (archive_file);
360 }
361
362
363 static void
update_sensitivity_for_mime_type(DialogData * data,const char * mime_type)364 update_sensitivity_for_mime_type (DialogData *data,
365 const char *mime_type)
366 {
367 int i;
368
369 if (mime_type == NULL) {
370 gtk_widget_set_sensitive (GET_WIDGET ("a_password_entry"), FALSE);
371 gtk_widget_set_sensitive (GET_WIDGET ("a_password_label"), FALSE);
372 gtk_widget_set_sensitive (GET_WIDGET ("a_encrypt_header_checkbutton"), FALSE);
373 gtk_toggle_button_set_inconsistent (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton"), TRUE);
374 gtk_widget_set_sensitive (GET_WIDGET ("a_volume_box"), FALSE);
375 return;
376 }
377
378 for (i = 0; mime_type_desc[i].mime_type != NULL; i++) {
379 if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) {
380 gboolean sensitive;
381
382 sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT;
383 gtk_widget_set_sensitive (GET_WIDGET ("a_password_entry"), sensitive);
384 gtk_widget_set_sensitive (GET_WIDGET ("a_password_label"), sensitive);
385
386 sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER;
387 can_encrypt_header = sensitive;
388 gtk_widget_set_sensitive (GET_WIDGET ("a_encrypt_header_checkbutton"), sensitive ? has_password : FALSE);
389 gtk_toggle_button_set_inconsistent (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton"), sensitive ? (!has_password) : TRUE);
390
391 sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES;
392 gtk_widget_set_sensitive (GET_WIDGET ("a_volume_box"), sensitive);
393
394 break;
395 }
396 }
397 }
398
399
400 static void
archive_type_combo_box_changed_cb(GtkComboBox * combo_box,DialogData * data)401 archive_type_combo_box_changed_cb (GtkComboBox *combo_box,
402 DialogData *data)
403 {
404 const char *mime_type;
405 int idx = gtk_combo_box_get_active (combo_box);
406 char *icon_name;
407
408 mime_type = mime_type_desc[data->supported_types[idx]].mime_type;
409 if ((icon_name = g_content_type_get_generic_icon_name (mime_type)) != NULL) {
410 gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("archive_icon_image")),
411 icon_name, GTK_ICON_SIZE_DIALOG);
412 g_free (icon_name);
413 }
414 update_sensitivity_for_mime_type (data, mime_type);
415 }
416
417
418 static void
update_archive_type_combo_box_from_ext(DialogData * data,const char * ext)419 update_archive_type_combo_box_from_ext (DialogData *data,
420 const char *ext)
421 {
422 int idx = 0;
423 int i;
424
425 if (ext == NULL) {
426 gtk_combo_box_set_active (GTK_COMBO_BOX (data->archive_type_combo_box), 0);
427 return;
428 }
429
430 for (i = 0; data->supported_types[i] != -1; i++)
431 if (strcmp (ext, mime_type_desc[data->supported_types[i]].default_ext) == 0) {
432 idx = i;
433 break;
434 }
435
436 gtk_combo_box_set_active (GTK_COMBO_BOX (data->archive_type_combo_box), idx);
437 }
438
439
440 static void
update_sensitivity(DialogData * data)441 update_sensitivity (DialogData *data)
442 {
443 const char *password;
444
445 gtk_widget_set_sensitive (GET_WIDGET ("a_volume_spinbutton"),
446 gtk_toggle_button_get_active (GET_TOGGLE_BUTTON ("a_volume_checkbutton")));
447
448 password = gtk_entry_get_text (GET_ENTRY ("a_password_entry"));
449 has_password = (password != NULL) && (*password != '\0');
450 gtk_toggle_button_set_inconsistent (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton"), can_encrypt_header ? (!has_password) : TRUE);
451 gtk_widget_set_sensitive (GET_WIDGET ("a_encrypt_header_checkbutton"), can_encrypt_header ? has_password : FALSE);
452 }
453
454
455 static void
password_entry_notify_text_cb(GObject * object,GParamSpec * spec,gpointer user_data)456 password_entry_notify_text_cb (GObject *object,
457 GParamSpec *spec,
458 gpointer user_data)
459 {
460 update_sensitivity ((DialogData *) user_data);
461 }
462
463
464 static void
volume_toggled_cb(GtkToggleButton * toggle_button,gpointer user_data)465 volume_toggled_cb (GtkToggleButton *toggle_button,
466 gpointer user_data)
467 {
468 update_sensitivity ((DialogData *) user_data);
469 }
470
471
472 void
dlg_batch_add_files(FrWindow * window,GList * file_list)473 dlg_batch_add_files (FrWindow *window,
474 GList *file_list)
475 {
476 DialogData *data;
477 char *automatic_name = NULL;
478 char *default_ext;
479 const char *first_filename;
480 char *parent;
481 int i;
482
483 if (file_list == NULL)
484 return;
485
486 data = g_new0 (DialogData, 1);
487 data->settings = g_settings_new (ENGRAMPA_SCHEMA_BATCH_ADD);
488 data->settings_general = g_settings_new (ENGRAMPA_SCHEMA_GENERAL);
489 data->builder = gtk_builder_new_from_resource (ENGRAMPA_RESOURCE_UI_PATH G_DIR_SEPARATOR_S "batch-add-files.ui");
490 data->window = window;
491 data->file_list = file_list;
492 data->single_file = ((file_list->next == NULL) && uri_is_file ((char*) file_list->data));
493 data->add_clicked = FALSE;
494
495 /* Set widgets data. */
496
497 gtk_expander_set_expanded (GTK_EXPANDER (GET_WIDGET ("a_other_options_expander")), FALSE /*g_settings_get_boolean (data->settings, PREF_BATCH_ADD_OTHER_OPTIONS)*/);
498 gtk_toggle_button_set_active (GET_TOGGLE_BUTTON ("a_encrypt_header_checkbutton"), g_settings_get_boolean (data->settings_general, PREF_GENERAL_ENCRYPT_HEADER));
499 gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("a_volume_spinbutton")), g_settings_get_int (data->settings, PREF_BATCH_ADD_VOLUME_SIZE) / MEGABYTE);
500
501
502 first_filename = (char*) file_list->data;
503 parent = remove_level_from_path (first_filename);
504
505 if (file_list->next == NULL)
506 automatic_name = g_uri_unescape_string (file_name_from_path ((char*) file_list->data), NULL);
507 else {
508 automatic_name = g_uri_unescape_string (file_name_from_path (parent), NULL);
509 if ((automatic_name == NULL) || (automatic_name[0] == '\0')) {
510 g_free (automatic_name);
511 automatic_name = g_uri_unescape_string (file_name_from_path (first_filename), NULL);
512 }
513 }
514
515 _gtk_entry_set_filename_text (GET_ENTRY ("a_add_to_entry"), automatic_name);
516 g_free (automatic_name);
517
518 if (check_permissions (parent, R_OK | W_OK))
519 gtk_file_chooser_set_current_folder_uri (GET_FILE_CHOOSER ("a_location_filechooserbutton"), parent);
520 else
521 gtk_file_chooser_set_current_folder_uri (GET_FILE_CHOOSER ("a_location_filechooserbutton"), get_home_uri ());
522 g_free (parent);
523
524 /* archive type combobox */
525
526 data->archive_type_combo_box = gtk_combo_box_text_new ();
527 if (data->single_file)
528 data->supported_types = single_file_save_type;
529 else
530 data->supported_types = save_type;
531 sort_mime_types_by_extension (data->supported_types);
532
533 for (i = 0; data->supported_types[i] != -1; i++)
534 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (data->archive_type_combo_box),
535 mime_type_desc[data->supported_types[i]].default_ext);
536
537 gtk_box_pack_start (GTK_BOX (GET_WIDGET ("a_archive_type_box")), data->archive_type_combo_box, TRUE, TRUE, 0);
538 gtk_widget_show_all (GET_WIDGET ("a_archive_type_box"));
539
540 /* Set the signals handlers. */
541
542 gtk_builder_add_callback_symbols (data->builder,
543 "on_dialog_destroy", G_CALLBACK (destroy_cb),
544 "on_a_add_button_clicked", G_CALLBACK (add_clicked_cb),
545 "on_a_help_button_clicked", G_CALLBACK (help_clicked_cb),
546 "on_a_volume_checkbutton_toggled", G_CALLBACK (volume_toggled_cb),
547 NULL);
548
549 gtk_builder_connect_signals (data->builder, data);
550
551 g_signal_connect_swapped (gtk_builder_get_object (data->builder, "a_cancel_button"),
552 "clicked",
553 G_CALLBACK (gtk_widget_destroy),
554 gtk_builder_get_object (data->builder, "dialog"));
555 g_signal_connect (G_OBJECT (data->archive_type_combo_box),
556 "changed",
557 G_CALLBACK (archive_type_combo_box_changed_cb),
558 data);
559 g_signal_connect (gtk_builder_get_object (data->builder, "a_password_entry"),
560 "notify::text",
561 G_CALLBACK (password_entry_notify_text_cb),
562 data);
563
564 /* Run dialog. */
565
566 default_ext = g_settings_get_string (data->settings, PREF_BATCH_ADD_DEFAULT_EXTENSION);
567 update_archive_type_combo_box_from_ext (data, default_ext);
568 g_free (default_ext);
569
570 gtk_widget_grab_focus (GET_WIDGET ("a_add_to_entry"));
571 gtk_editable_select_region (GTK_EDITABLE (GET_WIDGET ("a_add_to_entry")),
572 0, -1);
573
574 update_sensitivity (data);
575
576 gtk_window_set_modal (GET_WINDOW ("dialog"), FALSE);
577 gtk_window_present (GET_WINDOW ("dialog"));
578 }
579