1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtk.h>
25 #include <gthumb.h>
26 #include "dlg-rename-series.h"
27 #include "gth-template-editor-dialog.h"
28 #include "gth-template-selector.h"
29 #include "gth-rename-task.h"
30 #include "preferences.h"
31
32
33 enum {
34 SORT_DATA_COLUMN,
35 SORT_NAME_COLUMN,
36 SORT_NUM_COLUMNS
37 };
38
39 enum {
40 PREVIEW_OLD_NAME_COLUMN,
41 PREVIEW_NEW_NAME_COLUMN,
42 PREVIEW_NUM_COLUMNS
43 };
44
45
46 #define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name))
47
48 #define DEFAULT_TEMPLATE "####%E"
49 #define DEFAULT_START_AT 1
50 #define DEFAULT_SORT_BY "general::unsorted"
51 #define DEFAULT_REVERSE_ORDER FALSE
52 #define DEFAULT_CHANGE_CASE GTH_CHANGE_CASE_NONE
53 #define UPDATE_DELAY 500
54
55
56 static GthTemplateCode Rename_Special_Codes[] = {
57 { GTH_TEMPLATE_CODE_TYPE_TEXT, N_("Text"), 0 },
58 { GTH_TEMPLATE_CODE_TYPE_ENUMERATOR, N_("Enumerator"), '#' },
59 { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original filename"), 'F' },
60 { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original extension"), 'E' },
61 { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original enumerator"), 'N' },
62 { GTH_TEMPLATE_CODE_TYPE_DATE, N_("Modification date"), 'M' },
63 { GTH_TEMPLATE_CODE_TYPE_DATE, N_("Digitalization date"), 'D' },
64 { GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE, N_("File attribute"), 'A' }
65 };
66
67
68 typedef struct {
69 GthBrowser *browser;
70 GSettings *settings;
71 GList *file_list;
72 GList *file_data_list;
73 GList *new_file_list;
74 GList *new_names_list;
75 gboolean single_file;
76 gboolean first_update;
77 GtkBuilder *builder;
78 GtkWidget *dialog;
79 GtkWidget *list_view;
80 GtkWidget *sort_combobox;
81 GtkWidget *change_case_combobox;
82 GtkListStore *list_store;
83 GtkListStore *sort_model;
84 gboolean help_visible;
85 char *required_attributes;
86 guint update_id;
87 gboolean template_changed;
88 GList *tasks;
89 gboolean closing;
90 } DialogData;
91
92
93 static void
destroy_dialog(DialogData * data)94 destroy_dialog (DialogData *data)
95 {
96 if (data->dialog != NULL)
97 gtk_widget_destroy (data->dialog);
98 data->dialog = NULL;
99 gth_browser_set_dialog (data->browser, "rename_series", NULL);
100
101 if (data->update_id != 0) {
102 g_source_remove (data->update_id);
103 data->update_id = 0;
104 }
105
106 g_free (data->required_attributes);
107 g_object_unref (data->builder);
108 _g_object_list_unref (data->file_data_list);
109 _g_object_list_unref (data->file_list);
110 _g_string_list_free (data->new_names_list);
111 g_list_free (data->new_file_list);
112 g_object_unref (data->settings);
113 g_free (data);
114 }
115
116
117 typedef struct {
118 const char *template;
119 GthFileData *file_data;
120 int n;
121 GError **error;
122 } TemplateData;
123
124
125 static char *
get_original_enum(GthFileData * file_data,char * match)126 get_original_enum (GthFileData *file_data,
127 char *match)
128 {
129 char *basename;
130 GRegex *re;
131 char **a;
132 char *value = NULL;
133
134 basename = g_file_get_basename (file_data->file);
135 re = g_regex_new ("([0-9]+)", 0, 0, NULL);
136 a = g_regex_split (re, basename, 0);
137 if (g_strv_length (a) >= 2)
138 value = g_strdup (g_strstrip (a[1]));
139
140 g_strfreev (a);
141 g_regex_unref (re);
142 g_free (basename);
143
144 return value;
145 }
146
147
148 static char *
get_attribute_value(GthFileData * file_data,char * match)149 get_attribute_value (GthFileData *file_data,
150 char *match)
151 {
152 GRegex *re;
153 char **a;
154 char *attribute = NULL;
155 char *value = NULL;
156
157 re = g_regex_new ("%A\\{([^}]+)\\}", 0, 0, NULL);
158 a = g_regex_split (re, match, 0);
159 if (g_strv_length (a) >= 2)
160 attribute = g_strstrip (a[1]);
161
162 if ((attribute != NULL) && (*attribute != '\0')) {
163 value = gth_file_data_get_attribute_as_string (file_data, attribute);
164 if (value != NULL) {
165 char *tmp_value;
166
167 tmp_value = _g_utf8_replace_pattern (value, "[\r\n]", " ");
168 g_free (value);
169 value = tmp_value;
170 }
171 }
172
173 g_strfreev (a);
174 g_regex_unref (re);
175
176 return value;
177 }
178
179
180 static gboolean
template_eval_cb(const GMatchInfo * info,GString * res,gpointer data)181 template_eval_cb (const GMatchInfo *info,
182 GString *res,
183 gpointer data)
184 {
185 TemplateData *template_data = data;
186 char *r = NULL;
187 char *match;
188
189 match = g_match_info_fetch (info, 0);
190
191 if (strncmp (match, "#", 1) == 0) {
192 char *format;
193
194 format = g_strdup_printf ("%%0%" G_GSIZE_FORMAT "d", strlen (match));
195 r = g_strdup_printf (format, template_data->n);
196
197 g_free (format);
198 }
199 else if (strncmp (match, "%A", 2) == 0) {
200 r = get_attribute_value (template_data->file_data, match);
201 /*if (r == NULL)
202 *template_data->error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_FAILED, _("Malformed template"));*/
203 }
204 else if (strcmp (match, "%E") == 0) {
205 char *uri;
206
207 uri = g_file_get_uri (template_data->file_data->file);
208 r = _g_uri_get_extension (uri);
209
210 g_free (uri);
211 }
212 else if (strcmp (match, "%F") == 0) {
213 char *basename;
214
215 basename = g_file_get_basename (template_data->file_data->file);
216 r = _g_path_remove_extension (basename);
217
218 g_free (basename);
219 }
220 else if (strcmp (match, "%N") == 0) {
221 r = get_original_enum (template_data->file_data, match);
222 }
223 else if ((strncmp (match, "%D", 2) == 0) || (strncmp (match, "%M", 2) == 0)) {
224 gboolean value_available = FALSE;
225 GTimeVal timeval;
226
227 if (strncmp (match, "%D", 2) == 0) {
228 value_available = gth_file_data_get_digitalization_time (template_data->file_data, &timeval);
229 }
230 else if (strncmp (match, "%M", 2) == 0) {
231 timeval = *gth_file_data_get_modification_time (template_data->file_data);
232 value_available = TRUE;
233 }
234
235 if (value_available) {
236 GRegex *re;
237 char **a;
238 char *format = NULL;
239
240 /* Get the date format */
241
242 re = g_regex_new ("%[A-Z]\\{([^}]+)\\}", 0, 0, NULL);
243 a = g_regex_split (re, match, 0);
244 if (g_strv_length (a) >= 2)
245 format = g_strstrip (a[1]);
246 r = _g_time_val_strftime (&timeval, format);
247
248 g_strfreev (a);
249 g_regex_unref (re);
250 }
251 }
252
253 if (r != NULL)
254 g_string_append (res, r);
255
256 g_free (r);
257 g_free (match);
258
259 return FALSE;
260 }
261
262
263 static char *
get_required_attributes(DialogData * data)264 get_required_attributes (DialogData *data)
265 {
266 GtkTreeIter iter;
267 GString *required_attributes;
268 const char *template;
269
270 required_attributes = g_string_new (GFILE_STANDARD_ATTRIBUTES);
271
272 /* attributes required for sorting */
273
274 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (data->sort_combobox), &iter)) {
275 GthFileDataSort *sort_type;
276
277 gtk_tree_model_get (GTK_TREE_MODEL (data->sort_model),
278 &iter,
279 SORT_DATA_COLUMN, &sort_type,
280 -1);
281
282 if ((sort_type->required_attributes != NULL) && ! g_str_equal (sort_type->required_attributes, "")) {
283 g_string_append (required_attributes, ",");
284 g_string_append (required_attributes, sort_type->required_attributes);
285 }
286 }
287
288 /* attributes required for renaming */
289
290 template = gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry")));
291
292 if (g_strstr_len (template, -1, "%A") != NULL) {
293 GRegex *re;
294 char **a;
295 int i;
296
297 re = g_regex_new ("%A\\{([^}]+)\\}", 0, 0, NULL);
298 a = g_regex_split (re, template, 0);
299 for (i = 1; i < g_strv_length (a); i += 2) {
300 char *id;
301
302 id = g_strstrip (g_strdup (a[i]));
303 g_string_append (required_attributes, ",");
304 g_string_append (required_attributes, id);
305
306 g_free (id);
307 }
308
309 g_strfreev (a);
310 g_regex_unref (re);
311 }
312
313 if (g_strstr_len (template, -1, "%D") != NULL) {
314 int i;
315
316 for (i = 0; FileDataDigitalizationTags[i] != NULL; i++) {
317 g_string_append (required_attributes, ",");
318 g_string_append (required_attributes, FileDataDigitalizationTags[i]);
319 }
320 }
321
322 if (g_strstr_len (template, -1, "%M") != NULL) {
323 g_string_append (required_attributes, ",");
324 g_string_append (required_attributes, G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
325 }
326
327 return g_string_free (required_attributes, FALSE);
328 }
329
330
331 /* update_file_list */
332
333
334 typedef struct {
335 DialogData *data;
336 ReadyFunc ready_func;
337 GthTask *task;
338 gulong task_completed_id;
339 } UpdateData;
340
341
342 static void
update_data_free(UpdateData * update_data)343 update_data_free (UpdateData *update_data)
344 {
345 g_free(update_data);
346 }
347
348
349 static void update_preview_cb (GtkWidget *widget, DialogData *data);
350
351
352 static void
update_file_list__step2(gpointer user_data)353 update_file_list__step2 (gpointer user_data)
354 {
355 UpdateData *update_data = user_data;
356 DialogData *data = update_data->data;
357 GtkTreeIter iter;
358 int change_case;
359 TemplateData *template_data;
360 GRegex *re;
361 GList *scan;
362 GError *error = NULL;
363
364 if (data->first_update) {
365 if (data->file_data_list->next == NULL) {
366 GthFileData *file_data = data->file_data_list->data;
367 const char *edit_name;
368 const char *end_pos;
369
370 g_signal_handlers_block_by_func (GET_WIDGET ("template_entry"), update_preview_cb, data);
371 gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("template_entry")), g_file_info_get_attribute_string (file_data->info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME));
372 g_signal_handlers_unblock_by_func (GET_WIDGET ("template_entry"), update_preview_cb, data);
373
374 gtk_widget_grab_focus (GET_WIDGET ("template_entry"));
375
376 edit_name = gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry")));
377 end_pos = g_utf8_strrchr (edit_name, -1, '.');
378 if (end_pos != NULL) {
379 glong nchars;
380
381 nchars = g_utf8_strlen (edit_name, (gssize) (end_pos - edit_name));
382 gtk_editable_select_region (GTK_EDITABLE (GET_WIDGET ("template_entry")), 0, nchars);
383 }
384 }
385 else {
386 gtk_widget_grab_focus (GET_WIDGET ("template_entry"));
387 gtk_editable_select_region (GTK_EDITABLE (GET_WIDGET ("template_entry")), 0, -1);
388 }
389 }
390
391 data->first_update = FALSE;
392
393 if (data->new_names_list != NULL) {
394 _g_string_list_free (data->new_names_list);
395 data->new_names_list = NULL;
396 }
397
398 if (data->new_file_list != NULL) {
399 g_list_free (data->new_file_list);
400 data->new_file_list = NULL;
401 }
402
403 data->new_file_list = g_list_copy (data->file_data_list);
404 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (data->sort_combobox), &iter)) {
405 GthFileDataSort *sort_type;
406
407 gtk_tree_model_get (GTK_TREE_MODEL (data->sort_model),
408 &iter,
409 SORT_DATA_COLUMN, &sort_type,
410 -1);
411
412 if (sort_type->cmp_func != NULL)
413 data->new_file_list = g_list_sort (data->new_file_list, (GCompareFunc) sort_type->cmp_func);
414 }
415 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("reverse_order_checkbutton"))))
416 data->new_file_list = g_list_reverse (data->new_file_list);
417
418 change_case = gtk_combo_box_get_active (GTK_COMBO_BOX (data->change_case_combobox));
419
420 template_data = g_new0 (TemplateData, 1);
421 template_data->error = &error;
422 template_data->n = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("start_at_spinbutton")));
423 template_data->template = gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry")));
424 re = g_regex_new ("#+|%[ADEFMN](\\{[^}]+\\})?", 0, 0, NULL);
425 for (scan = data->new_file_list; scan; scan = scan->next) {
426 char *new_name;
427 char *new_name2;
428
429 template_data->file_data = scan->data;
430 new_name = g_regex_replace_eval (re, template_data->template, -1, 0, 0, template_eval_cb, template_data, &error);
431 if (error != NULL)
432 break;
433
434 switch (change_case) {
435 case GTH_CHANGE_CASE_LOWER:
436 new_name2 = g_utf8_strdown (new_name, -1);
437 break;
438 case GTH_CHANGE_CASE_UPPER:
439 new_name2 = g_utf8_strup (new_name, -1);
440 break;
441 default:
442 new_name2 = g_strdup (new_name);
443 break;
444 }
445
446 data->new_names_list = g_list_prepend (data->new_names_list, new_name2);
447 template_data->n = template_data->n + 1;
448
449 g_free (new_name);
450 }
451 g_regex_unref (re);
452 data->new_names_list = g_list_reverse (data->new_names_list);
453
454 if (update_data->ready_func)
455 update_data->ready_func (error, update_data->data);
456
457 update_data_free (update_data);
458 }
459
460
461 static void
load_file_data_task_completed_cb(GthTask * task,GError * error,gpointer user_data)462 load_file_data_task_completed_cb (GthTask *task,
463 GError *error,
464 gpointer user_data)
465 {
466 UpdateData *update_data = user_data;
467 DialogData *data = update_data->data;
468
469 gtk_widget_hide (GET_WIDGET ("task_box"));
470 gtk_widget_set_sensitive (GET_WIDGET ("options_table"), TRUE);
471 gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, TRUE);
472
473 data->tasks = g_list_remove (data->tasks, update_data->task);
474
475 g_object_unref (update_data->task);
476 update_data->task = NULL;
477 update_data->task_completed_id = 0;
478
479 if (error != NULL) {
480 if (! data->closing && update_data->ready_func)
481 update_data->ready_func (error, update_data->data);
482 update_data_free (update_data);
483
484 if (data->tasks == NULL)
485 destroy_dialog (data);
486
487 return;
488 }
489
490 _g_object_list_unref (data->file_data_list);
491 data->file_data_list = _g_object_list_ref (gth_load_file_data_task_get_result (GTH_LOAD_FILE_DATA_TASK (task)));
492 data->template_changed = FALSE;
493
494 update_file_list__step2 (update_data);
495 }
496
497
498 static void
update_file_list(DialogData * data,ReadyFunc ready_func)499 update_file_list (DialogData *data,
500 ReadyFunc ready_func)
501 {
502 UpdateData *update_data;
503
504 update_data = g_new (UpdateData, 1);
505 update_data->data = data;
506 update_data->ready_func = ready_func;
507
508 if (data->template_changed) {
509 char *required_attributes;
510 gboolean reload_required;
511
512 required_attributes = get_required_attributes (data);
513 reload_required = attribute_list_reload_required (data->required_attributes, required_attributes);
514 g_free (data->required_attributes);
515 data->required_attributes = required_attributes;
516
517 if (reload_required) {
518 GtkWidget *child;
519
520 gtk_widget_set_sensitive (GET_WIDGET ("options_table"), FALSE);
521 gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, FALSE);
522 gtk_widget_show (GET_WIDGET ("task_box"));
523
524 update_data->task = gth_load_file_data_task_new (data->file_list, data->required_attributes);
525 update_data->task_completed_id = g_signal_connect (update_data->task,
526 "completed",
527 G_CALLBACK (load_file_data_task_completed_cb),
528 update_data);
529
530 data->tasks = g_list_prepend (data->tasks, update_data->task);
531
532 child = gth_task_progress_new (update_data->task);
533 gtk_widget_show (child);
534 gtk_box_pack_start (GTK_BOX (GET_WIDGET ("task_box")), child, TRUE, TRUE, 0);
535 gth_task_exec (update_data->task, NULL);
536
537 return;
538 }
539 }
540
541 call_when_idle (update_file_list__step2, update_data);
542 }
543
544
545 static void
ok_button_clicked__step2(GError * error,gpointer user_data)546 ok_button_clicked__step2 (GError *error,
547 gpointer user_data)
548 {
549 DialogData *data = user_data;
550 GtkTreeIter iter;
551 GList *old_files;
552 GList *new_files;
553 GList *scan1;
554 GList *scan2;
555 GthTask *task;
556
557 if (error != NULL) {
558 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not rename the files"), error);
559 return;
560 }
561
562 /* -- save preferences -- */
563
564 if (data->file_list->next != NULL)
565 g_settings_set_string (data->settings, PREF_RENAME_SERIES_TEMPLATE, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry"))));
566
567 g_settings_set_int (data->settings, PREF_RENAME_SERIES_START_AT, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("start_at_spinbutton"))));
568
569 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (data->sort_combobox), &iter)) {
570 GthFileDataSort *sort_type;
571
572 gtk_tree_model_get (GTK_TREE_MODEL (data->sort_model),
573 &iter,
574 SORT_DATA_COLUMN, &sort_type,
575 -1);
576 g_settings_set_string (data->settings, PREF_RENAME_SERIES_SORT_BY, sort_type->name);
577 }
578
579 g_settings_set_boolean (data->settings, PREF_RENAME_SERIES_REVERSE_ORDER, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("reverse_order_checkbutton"))));
580 g_settings_set_enum (data->settings, PREF_RENAME_SERIES_CHANGE_CASE, gtk_combo_box_get_active (GTK_COMBO_BOX (data->change_case_combobox)));
581
582 /* -- prepare and exec rename task -- */
583
584 old_files = NULL;
585 new_files = NULL;
586
587 for (scan1 = data->new_file_list, scan2 = data->new_names_list;
588 scan1 && scan2;
589 scan1 = scan1->next, scan2 = scan2->next)
590 {
591 GthFileData *file_data = scan1->data;
592 char *new_name = scan2->data;
593 GFile *parent;
594 GFile *new_file;
595
596 parent = g_file_get_parent (file_data->file);
597 new_file = g_file_get_child (parent, new_name);
598
599 old_files = g_list_prepend (old_files, g_object_ref (file_data->file));
600 new_files = g_list_prepend (new_files, new_file);
601
602 g_object_unref (parent);
603 }
604 old_files = g_list_reverse (old_files);
605 new_files = g_list_reverse (new_files);
606
607 task = gth_rename_task_new (old_files, new_files);
608 gth_browser_exec_task (data->browser, task, GTH_TASK_FLAGS_DEFAULT);
609
610 g_object_unref (task);
611 destroy_dialog (data);
612 }
613
614
615 static void
ok_button_clicked(DialogData * data)616 ok_button_clicked (DialogData *data)
617 {
618 if (data->update_id != 0) {
619 g_source_remove (data->update_id);
620 data->update_id = 0;
621 }
622
623 update_file_list (data, ok_button_clicked__step2);
624 }
625
626
627 static void
dialog_response_cb(GtkDialog * dialog,int response_id,gpointer user_data)628 dialog_response_cb (GtkDialog *dialog,
629 int response_id,
630 gpointer user_data)
631 {
632 DialogData *data = user_data;
633
634 switch (response_id) {
635 case GTK_RESPONSE_DELETE_EVENT:
636 case GTK_RESPONSE_CANCEL:
637 if (data->tasks != NULL) {
638 GList *tasks;
639
640 data->closing = TRUE;
641
642 tasks = g_list_copy (data->tasks);
643 g_list_foreach (tasks, (GFunc) gth_task_cancel, NULL);
644 g_list_free (tasks);
645 }
646 else
647 destroy_dialog (data);
648 break;
649
650 case GTK_RESPONSE_OK:
651 ok_button_clicked (data);
652 break;
653
654 default:
655 break;
656 }
657 }
658
659
660 static void
error_dialog_response_cb(GtkDialog * dialog,int response,gpointer user_data)661 error_dialog_response_cb (GtkDialog *dialog,
662 int response,
663 gpointer user_data)
664 {
665 DialogData *data = user_data;
666
667 gtk_widget_destroy (GTK_WIDGET (dialog));
668 destroy_dialog (data);
669 }
670
671
672 static void
update_preview__step2(GError * error,gpointer user_data)673 update_preview__step2 (GError *error,
674 gpointer user_data)
675 {
676 DialogData *data = user_data;
677 GList *scan1, *scan2;
678
679 if (error != NULL) {
680 GtkWidget *d;
681
682 d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog),
683 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
684 _GTK_ICON_NAME_DIALOG_ERROR,
685 _("Could not rename the files"),
686 error->message,
687 _GTK_LABEL_OK, GTK_RESPONSE_OK,
688 NULL);
689 g_signal_connect (d, "response", G_CALLBACK (error_dialog_response_cb), data);
690 gtk_window_present (GTK_WINDOW (d));
691
692 return;
693 }
694
695 /* -- update the list view -- */
696
697 gtk_list_store_clear (data->list_store);
698 for (scan1 = data->new_file_list, scan2 = data->new_names_list;
699 scan1 && scan2;
700 scan1 = scan1->next, scan2 = scan2->next)
701 {
702 GthFileData *file_data1 = scan1->data;
703 GthFileData *new_name = scan2->data;
704 GtkTreeIter iter;
705
706 gtk_list_store_append (data->list_store, &iter);
707 gtk_list_store_set (data->list_store, &iter,
708 PREVIEW_OLD_NAME_COLUMN, g_file_info_get_display_name (file_data1->info),
709 PREVIEW_NEW_NAME_COLUMN, new_name,
710 -1);
711 }
712 }
713
714
715 static void
dlg_rename_series_update_preview(DialogData * data)716 dlg_rename_series_update_preview (DialogData *data)
717 {
718 update_file_list (data, update_preview__step2);
719 }
720
721
722 static void
template_entry_icon_release_cb(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer user_data)723 template_entry_icon_release_cb (GtkEntry *entry,
724 GtkEntryIconPosition icon_pos,
725 GdkEvent *event,
726 gpointer user_data)
727 {
728 DialogData *data = user_data;
729
730 data->help_visible = ! data->help_visible;
731
732 if (data->help_visible)
733 gtk_widget_show (GET_WIDGET("template_help_table"));
734 else
735 gtk_widget_hide (GET_WIDGET("template_help_table"));
736 }
737
738
739 static gboolean
update_preview_after_delay_cb(gpointer user_data)740 update_preview_after_delay_cb (gpointer user_data)
741 {
742 DialogData *data = user_data;
743
744 if (data->update_id != 0) {
745 g_source_remove (data->update_id);
746 data->update_id = 0;
747 }
748
749 dlg_rename_series_update_preview (data);
750
751 return FALSE;
752 }
753
754
755 static void
update_preview_cb(GtkWidget * widget,DialogData * data)756 update_preview_cb (GtkWidget *widget,
757 DialogData *data)
758 {
759 data->template_changed = TRUE;
760 if (data->update_id != 0)
761 g_source_remove (data->update_id);
762 data->update_id = g_timeout_add (UPDATE_DELAY, update_preview_after_delay_cb, data);
763 }
764
765
766 static void
template_editor_dialog_response_cb(GtkDialog * dialog,int response_id,gpointer user_data)767 template_editor_dialog_response_cb (GtkDialog *dialog,
768 int response_id,
769 gpointer user_data)
770 {
771 DialogData *data = user_data;
772 char *template;
773 GError *error = NULL;
774
775 if (response_id != GTK_RESPONSE_OK) {
776 gtk_widget_destroy (GTK_WIDGET (dialog));
777 return;
778 }
779
780 template = gth_template_editor_dialog_get_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog), &error);
781 if (error != NULL) {
782 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (dialog), _("Could not save the template"), error);
783 g_clear_error (&error);
784 return;
785 }
786
787 gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("template_entry")), template);
788 gtk_widget_destroy (GTK_WIDGET (dialog));
789
790 g_free (template);
791 }
792
793
794 static void
edit_template_button_clicked_cb(GtkWidget * widget,DialogData * data)795 edit_template_button_clicked_cb (GtkWidget *widget,
796 DialogData *data)
797 {
798 GtkWidget *dialog;
799
800 dialog = gth_template_editor_dialog_new (Rename_Special_Codes, 8, _("Edit Template"), GTK_WINDOW (data->dialog));
801 gth_template_editor_dialog_set_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog),
802 gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry"))));
803 g_signal_connect (dialog,
804 "response",
805 G_CALLBACK (template_editor_dialog_response_cb),
806 data);
807 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
808 gtk_window_present (GTK_WINDOW (dialog));
809 }
810
811 static void
return_pressed_callback(GtkDialog * dialog,gpointer user_data)812 return_pressed_callback (GtkDialog *dialog,
813 gpointer user_data)
814 {
815 ok_button_clicked (user_data);
816 }
817
818 void
dlg_rename_series(GthBrowser * browser,GList * file_list)819 dlg_rename_series (GthBrowser *browser,
820 GList *file_list)
821 {
822 DialogData *data;
823 GtkCellRenderer *renderer;
824 GtkTreeViewColumn *column;
825 int i;
826 GList *sort_types;
827 GList *scan;
828 int change_case;
829 int start_at;
830 char *sort_by;
831 gboolean found;
832
833 if (gth_browser_get_dialog (browser, "rename_series") != NULL) {
834 gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, "rename_series")));
835 return;
836 }
837
838 data = g_new0 (DialogData, 1);
839 data->browser = browser;
840 data->builder = _gtk_builder_new_from_file ("rename-series.ui", "rename_series");
841 data->settings = g_settings_new (GTHUMB_RENAME_SERIES_SCHEMA);
842 data->file_list = _g_file_list_dup (file_list);
843 data->first_update = TRUE;
844 data->template_changed = TRUE;
845 data->closing = FALSE;
846
847 /* Get the widgets. */
848
849 data->dialog = g_object_new (GTK_TYPE_DIALOG,
850 "title", _("Rename"),
851 "transient-for", GTK_WINDOW (browser),
852 "modal", FALSE,
853 "destroy-with-parent", FALSE,
854 "use-header-bar", _gtk_settings_get_dialogs_use_header (),
855 NULL);
856 gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))),
857 _gtk_builder_get_widget (data->builder, "dialog_content"));
858 gtk_dialog_add_buttons (GTK_DIALOG (data->dialog),
859 _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL,
860 _("_Rename"), GTK_RESPONSE_OK,
861 NULL);
862 _gtk_dialog_add_class_to_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
863
864 gth_browser_set_dialog (browser, "rename_series", data->dialog);
865 g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data);
866
867 /* Set widgets data. */
868
869 data->list_store = gtk_list_store_new (PREVIEW_NUM_COLUMNS,
870 G_TYPE_STRING,
871 G_TYPE_STRING);
872 data->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (data->list_store));
873 g_object_unref (data->list_store);
874
875 renderer = gtk_cell_renderer_text_new ();
876 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
877 column = gtk_tree_view_column_new_with_attributes (_("Old Name"),
878 renderer,
879 "text", PREVIEW_OLD_NAME_COLUMN,
880 NULL);
881 gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
882 gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE);
883 gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), GTK_TREE_VIEW_COLUMN_AUTOSIZE);
884 gtk_tree_view_append_column (GTK_TREE_VIEW (data->list_view), column);
885
886 renderer = gtk_cell_renderer_text_new ();
887 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
888 column = gtk_tree_view_column_new_with_attributes (_("New Name"),
889 renderer,
890 "text", PREVIEW_NEW_NAME_COLUMN,
891 NULL);
892 gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
893 gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE);
894 gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), GTK_TREE_VIEW_COLUMN_AUTOSIZE);
895 gtk_tree_view_append_column (GTK_TREE_VIEW (data->list_view), column);
896
897 gtk_widget_show (data->list_view);
898 gtk_widget_set_vexpand (data->list_view, TRUE);
899 gtk_container_add (GTK_CONTAINER (GET_WIDGET ("preview_scrolledwindow")), data->list_view);
900
901 gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("preview_label")), data->list_view);
902
903 if (data->file_list->next != NULL)
904 gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("template_entry")),
905 g_settings_get_string (data->settings, PREF_RENAME_SERIES_TEMPLATE));
906
907 start_at = g_settings_get_int (data->settings, PREF_RENAME_SERIES_START_AT);
908 if (start_at < 0)
909 start_at = DEFAULT_START_AT;
910 gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("start_at_spinbutton")), start_at * 1.0);
911
912 /* sort by */
913
914 data->sort_model = gtk_list_store_new (SORT_NUM_COLUMNS,
915 G_TYPE_POINTER,
916 G_TYPE_STRING);
917 data->sort_combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL (data->sort_model));
918 g_object_unref (data->sort_model);
919 renderer = gtk_cell_renderer_text_new ();
920 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->sort_combobox), renderer, TRUE);
921 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->sort_combobox),
922 renderer,
923 "text", SORT_NAME_COLUMN,
924 NULL);
925
926 sort_by = g_settings_get_string (data->settings, PREF_RENAME_SERIES_SORT_BY);
927 found = FALSE;
928
929 sort_types = gth_main_get_all_sort_types ();
930 for (i = 0, scan = sort_types; scan; scan = scan->next, i++) {
931 GthFileDataSort *sort_type = scan->data;
932 GtkTreeIter iter;
933
934 gtk_list_store_append (data->sort_model, &iter);
935 gtk_list_store_set (data->sort_model, &iter,
936 SORT_DATA_COLUMN, sort_type,
937 SORT_NAME_COLUMN, sort_type->display_name,
938 -1);
939
940 if (strcmp (sort_by, sort_type->name) == 0) {
941 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (data->sort_combobox), &iter);
942 found = TRUE;
943 }
944 }
945 g_list_free (sort_types);
946 g_free (sort_by);
947
948 if (!found)
949 gtk_combo_box_set_active (GTK_COMBO_BOX (data->sort_combobox), 0);
950
951 gtk_widget_show (data->sort_combobox);
952 gtk_box_pack_start (GTK_BOX (GET_WIDGET ("sort_by_box")), data->sort_combobox, FALSE, FALSE, 0);
953 gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("sort_by_label")), data->sort_combobox);
954
955 /* reverse order */
956
957 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("reverse_order_checkbutton")),
958 g_settings_get_boolean (data->settings, PREF_RENAME_SERIES_REVERSE_ORDER));
959
960 /* change case */
961
962 change_case = g_settings_get_enum (data->settings, PREF_RENAME_SERIES_CHANGE_CASE);
963 if ((change_case < GTH_CHANGE_CASE_NONE) || (change_case > GTH_CHANGE_CASE_UPPER))
964 change_case = DEFAULT_CHANGE_CASE;
965
966 data->change_case_combobox = _gtk_combo_box_new_with_texts (_("Keep original case"),
967 _("Convert to lower-case"),
968 _("Convert to upper-case"),
969 NULL);
970 gtk_combo_box_set_active (GTK_COMBO_BOX (data->change_case_combobox), change_case);
971 gtk_widget_show (data->change_case_combobox);
972 gtk_box_pack_start (GTK_BOX (GET_WIDGET ("change_case_box")), data->change_case_combobox, FALSE, FALSE, 0);
973 gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("change_case_label")), data->change_case_combobox);
974
975 /* Set the signals handlers. */
976
977 g_signal_connect (data->dialog,
978 "delete-event",
979 G_CALLBACK (gtk_true),
980 NULL);
981 g_signal_connect (data->dialog,
982 "response",
983 G_CALLBACK (dialog_response_cb),
984 data);
985 g_signal_connect (GET_WIDGET ("template_entry"),
986 "icon-release",
987 G_CALLBACK (template_entry_icon_release_cb),
988 data);
989 g_signal_connect (GET_WIDGET ("template_entry"),
990 "changed",
991 G_CALLBACK (update_preview_cb),
992 data);
993 g_signal_connect (GET_WIDGET("template_entry"),
994 "activate",
995 G_CALLBACK (return_pressed_callback),
996 data);
997 g_signal_connect (GET_WIDGET ("start_at_spinbutton"),
998 "value_changed",
999 G_CALLBACK (update_preview_cb),
1000 data);
1001 g_signal_connect (data->sort_combobox,
1002 "changed",
1003 G_CALLBACK (update_preview_cb),
1004 data);
1005 g_signal_connect (data->change_case_combobox,
1006 "changed",
1007 G_CALLBACK (update_preview_cb),
1008 data);
1009 g_signal_connect (GET_WIDGET ("reverse_order_checkbutton"),
1010 "toggled",
1011 G_CALLBACK (update_preview_cb),
1012 data);
1013 g_signal_connect (GET_WIDGET ("edit_template_button"),
1014 "clicked",
1015 G_CALLBACK (edit_template_button_clicked_cb),
1016 data);
1017
1018 /* Run dialog. */
1019
1020 gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (browser));
1021 gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
1022 gtk_widget_show (data->dialog);
1023
1024 dlg_rename_series_update_preview (data);
1025 }
1026