1 /* nautilus-batch-rename-dialog.c
2 *
3 * Copyright (C) 2016 Alexandru Pandelea <alexandru.pandelea@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "nautilus-batch-rename-dialog.h"
22 #include "nautilus-file.h"
23 #include "nautilus-error-reporting.h"
24 #include "nautilus-batch-rename-utilities.h"
25
26 #include <glib/gprintf.h>
27 #include <glib.h>
28 #include <string.h>
29 #include <glib/gi18n.h>
30
31 #define ROW_MARGIN_START 6
32 #define ROW_MARGIN_TOP_BOTTOM 4
33
34 struct _NautilusBatchRenameDialog
35 {
36 GtkDialog parent;
37
38 GtkWidget *grid;
39 NautilusWindow *window;
40
41 GtkWidget *cancel_button;
42 GtkWidget *original_name_listbox;
43 GtkWidget *arrow_listbox;
44 GtkWidget *result_listbox;
45 GtkWidget *name_entry;
46 GtkWidget *rename_button;
47 GtkWidget *find_entry;
48 GtkWidget *mode_stack;
49 GtkWidget *replace_entry;
50 GtkWidget *format_mode_button;
51 GtkWidget *replace_mode_button;
52 GtkWidget *add_button;
53 GtkWidget *add_popover;
54 GtkWidget *numbering_order_label;
55 GtkWidget *numbering_label;
56 GtkWidget *scrolled_window;
57 GtkWidget *numbering_order_popover;
58 GtkWidget *numbering_order_button;
59 GtkWidget *numbering_revealer;
60 GtkWidget *conflict_box;
61 GtkWidget *conflict_label;
62 GtkWidget *conflict_down;
63 GtkWidget *conflict_up;
64
65 GList *original_name_listbox_rows;
66 GList *arrow_listbox_rows;
67 GList *result_listbox_rows;
68 GList *listbox_labels_new;
69 GList *listbox_labels_old;
70 GList *listbox_icons;
71 GtkSizeGroup *size_group;
72
73 GList *selection;
74 GList *new_names;
75 NautilusBatchRenameDialogMode mode;
76 NautilusDirectory *directory;
77
78 GActionGroup *action_group;
79
80 GMenu *numbering_order_menu;
81 GMenu *add_tag_menu;
82
83 GHashTable *create_date;
84 GList *selection_metadata;
85
86 /* the index of the currently selected conflict */
87 gint selected_conflict;
88 /* total conflicts number */
89 gint conflicts_number;
90
91 GList *duplicates;
92 GList *distinct_parent_directories;
93 GList *directories_pending_conflict_check;
94
95 /* this hash table has information about the status
96 * of all tags: availability, if it's currently used
97 * and position */
98 GHashTable *tag_info_table;
99
100 GtkWidget *preselected_row1;
101 GtkWidget *preselected_row2;
102
103 gint row_height;
104 gboolean rename_clicked;
105
106 GCancellable *metadata_cancellable;
107 };
108
109 typedef struct
110 {
111 gboolean available;
112 gboolean set;
113 gint position;
114 /* if the tag was just added, then we shouldn't update it's position */
115 gboolean just_added;
116 TagConstants tag_constants;
117 } TagData;
118
119
120 static void update_display_text (NautilusBatchRenameDialog *dialog);
121 static void cancel_conflict_check (NautilusBatchRenameDialog *self);
122
123 G_DEFINE_TYPE (NautilusBatchRenameDialog, nautilus_batch_rename_dialog, GTK_TYPE_DIALOG);
124
125 static void
change_numbering_order(GSimpleAction * action,GVariant * value,gpointer user_data)126 change_numbering_order (GSimpleAction *action,
127 GVariant *value,
128 gpointer user_data)
129 {
130 NautilusBatchRenameDialog *dialog;
131 const gchar *target_name;
132 guint i;
133
134 dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
135
136 target_name = g_variant_get_string (value, NULL);
137
138 for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
139 {
140 if (g_strcmp0 (sorts_constants[i].action_target_name, target_name) == 0)
141 {
142 gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
143 gettext (sorts_constants[i].label));
144 dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
145 sorts_constants[i].sort_mode,
146 dialog->create_date);
147 break;
148 }
149 }
150
151 g_simple_action_set_state (action, value);
152
153 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE);
154
155 update_display_text (dialog);
156 }
157
158 static void
enable_action(NautilusBatchRenameDialog * self,const gchar * action_name)159 enable_action (NautilusBatchRenameDialog *self,
160 const gchar *action_name)
161 {
162 GAction *action;
163
164 action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group),
165 action_name);
166 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
167 }
168
169 static void
disable_action(NautilusBatchRenameDialog * self,const gchar * action_name)170 disable_action (NautilusBatchRenameDialog *self,
171 const gchar *action_name)
172 {
173 GAction *action;
174
175 action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group),
176 action_name);
177 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
178 }
179
180 static void
add_tag(NautilusBatchRenameDialog * self,TagConstants tag_constants)181 add_tag (NautilusBatchRenameDialog *self,
182 TagConstants tag_constants)
183 {
184 g_autofree gchar *tag_text_representation = NULL;
185 gint cursor_position;
186 TagData *tag_data;
187
188 g_object_get (self->name_entry, "cursor-position", &cursor_position, NULL);
189
190 tag_text_representation = batch_rename_get_tag_text_representation (tag_constants);
191 tag_data = g_hash_table_lookup (self->tag_info_table, tag_text_representation);
192 tag_data->available = TRUE;
193 tag_data->set = TRUE;
194 tag_data->just_added = TRUE;
195 tag_data->position = cursor_position;
196
197 /* FIXME: We can add a tag when the cursor is inside a tag, which breaks this.
198 * We need to check the cursor movement and update the actions acordingly or
199 * even better add the tag at the end of the previous tag if this happens.
200 */
201 gtk_editable_insert_text (GTK_EDITABLE (self->name_entry),
202 tag_text_representation,
203 strlen (tag_text_representation),
204 &cursor_position);
205 tag_data->just_added = FALSE;
206 gtk_editable_set_position (GTK_EDITABLE (self->name_entry), cursor_position);
207
208 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (self->name_entry));
209 }
210
211 static void
add_metadata_tag(GSimpleAction * action,GVariant * value,gpointer user_data)212 add_metadata_tag (GSimpleAction *action,
213 GVariant *value,
214 gpointer user_data)
215 {
216 NautilusBatchRenameDialog *self;
217 const gchar *action_name;
218 guint i;
219
220 self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
221 action_name = g_action_get_name (G_ACTION (action));
222
223 for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
224 {
225 if (g_strcmp0 (metadata_tags_constants[i].action_name, action_name) == 0)
226 {
227 add_tag (self, metadata_tags_constants[i]);
228 disable_action (self, metadata_tags_constants[i].action_name);
229
230 break;
231 }
232 }
233 }
234
235 static void
add_numbering_tag(GSimpleAction * action,GVariant * value,gpointer user_data)236 add_numbering_tag (GSimpleAction *action,
237 GVariant *value,
238 gpointer user_data)
239 {
240 NautilusBatchRenameDialog *self;
241 const gchar *action_name;
242 guint i;
243
244 self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
245 action_name = g_action_get_name (G_ACTION (action));
246
247 for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
248 {
249 if (g_strcmp0 (numbering_tags_constants[i].action_name, action_name) == 0)
250 {
251 add_tag (self, numbering_tags_constants[i]);
252 }
253 /* We want to allow only one tag of numbering type, so we disable all
254 * of them */
255 disable_action (self, numbering_tags_constants[i].action_name);
256 }
257 }
258
259 const GActionEntry dialog_entries[] =
260 {
261 { "numbering-order-changed", NULL, "s", "'name-ascending'", change_numbering_order },
262 { "add-numbering-no-zero-pad-tag", add_numbering_tag },
263 { "add-numbering-one-zero-pad-tag", add_numbering_tag },
264 { "add-numbering-two-zero-pad-tag", add_numbering_tag },
265 { "add-original-file-name-tag", add_metadata_tag },
266 { "add-creation-date-tag", add_metadata_tag },
267 { "add-equipment-tag", add_metadata_tag },
268 { "add-season-number-tag", add_metadata_tag },
269 { "add-episode-number-tag", add_metadata_tag },
270 { "add-video-album-tag", add_metadata_tag },
271 { "add-track-number-tag", add_metadata_tag },
272 { "add-artist-name-tag", add_metadata_tag },
273 { "add-title-tag", add_metadata_tag },
274 { "add-album-name-tag", add_metadata_tag },
275 };
276
277 static void
row_selected(GtkListBox * box,GtkListBoxRow * listbox_row,gpointer user_data)278 row_selected (GtkListBox *box,
279 GtkListBoxRow *listbox_row,
280 gpointer user_data)
281 {
282 NautilusBatchRenameDialog *dialog;
283 GtkListBoxRow *row;
284 gint index;
285
286 if (!GTK_IS_LIST_BOX_ROW (listbox_row))
287 {
288 return;
289 }
290
291 dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
292 index = gtk_list_box_row_get_index (listbox_row);
293
294 if (GTK_WIDGET (box) == dialog->original_name_listbox)
295 {
296 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index);
297 gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
298 row);
299 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index);
300 gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
301 row);
302 }
303
304 if (GTK_WIDGET (box) == dialog->arrow_listbox)
305 {
306 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index);
307 gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
308 row);
309 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index);
310 gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
311 row);
312 }
313
314 if (GTK_WIDGET (box) == dialog->result_listbox)
315 {
316 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index);
317 gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
318 row);
319 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index);
320 gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
321 row);
322 }
323 }
324
325 static gint
compare_int(gconstpointer a,gconstpointer b)326 compare_int (gconstpointer a,
327 gconstpointer b)
328 {
329 int *number1 = (int *) a;
330 int *number2 = (int *) b;
331
332 return *number1 - *number2;
333 }
334
335 /* This function splits the entry text into a list of regular text and tags.
336 * For instance, "[1, 2, 3]Paris[Creation date]" would result in:
337 * "[1, 2, 3]", "Paris", "[Creation date]" */
338 static GList *
split_entry_text(NautilusBatchRenameDialog * self,gchar * entry_text)339 split_entry_text (NautilusBatchRenameDialog *self,
340 gchar *entry_text)
341 {
342 GString *normal_text;
343 GString *tag;
344 GArray *tag_positions;
345 g_autoptr (GList) tag_info_keys = NULL;
346 GList *l;
347 gint tags;
348 gint i;
349 gchar *substring;
350 gint tag_end_position;
351 GList *result = NULL;
352 TagData *tag_data;
353
354 tags = 0;
355 tag_end_position = 0;
356 tag_positions = g_array_new (FALSE, FALSE, sizeof (gint));
357
358 tag_info_keys = g_hash_table_get_keys (self->tag_info_table);
359
360 for (l = tag_info_keys; l != NULL; l = l->next)
361 {
362 tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
363 if (tag_data->set)
364 {
365 g_array_append_val (tag_positions, tag_data->position);
366 tags++;
367 }
368 }
369
370 g_array_sort (tag_positions, compare_int);
371
372 for (i = 0; i < tags; i++)
373 {
374 tag = g_string_new ("");
375
376 substring = g_utf8_substring (entry_text, tag_end_position,
377 g_array_index (tag_positions, gint, i));
378 normal_text = g_string_new (substring);
379 g_free (substring);
380
381 if (g_strcmp0 (normal_text->str, ""))
382 {
383 result = g_list_prepend (result, normal_text);
384 }
385 else
386 {
387 g_string_free (normal_text, TRUE);
388 }
389
390 for (l = tag_info_keys; l != NULL; l = l->next)
391 {
392 g_autofree gchar *tag_text_representation = NULL;
393
394 tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
395 if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position)
396 {
397 tag_text_representation = batch_rename_get_tag_text_representation (tag_data->tag_constants);
398 tag_end_position = g_array_index (tag_positions, gint, i) +
399 g_utf8_strlen (tag_text_representation, -1);
400 tag = g_string_append (tag, tag_text_representation);
401
402 break;
403 }
404 }
405
406 result = g_list_prepend (result, tag);
407 }
408
409 normal_text = g_string_new (g_utf8_offset_to_pointer (entry_text, tag_end_position));
410
411 if (g_strcmp0 (normal_text->str, "") != 0)
412 {
413 result = g_list_prepend (result, normal_text);
414 }
415 else
416 {
417 g_string_free (normal_text, TRUE);
418 }
419
420 result = g_list_reverse (result);
421
422 g_array_free (tag_positions, TRUE);
423 return result;
424 }
425
426 static GList *
batch_rename_dialog_get_new_names(NautilusBatchRenameDialog * dialog)427 batch_rename_dialog_get_new_names (NautilusBatchRenameDialog *dialog)
428 {
429 GList *result = NULL;
430 GList *selection;
431 GList *text_chunks;
432 g_autofree gchar *entry_text = NULL;
433 g_autofree gchar *replace_text = NULL;
434
435 selection = dialog->selection;
436 text_chunks = NULL;
437
438 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
439 {
440 entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->find_entry)));
441 }
442 else
443 {
444 entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
445 }
446
447 replace_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry)));
448
449 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
450 {
451 result = batch_rename_dialog_get_new_names_list (dialog->mode,
452 selection,
453 NULL,
454 NULL,
455 entry_text,
456 replace_text);
457 }
458 else
459 {
460 text_chunks = split_entry_text (dialog, entry_text);
461
462 result = batch_rename_dialog_get_new_names_list (dialog->mode,
463 selection,
464 text_chunks,
465 dialog->selection_metadata,
466 entry_text,
467 replace_text);
468 g_list_free_full (text_chunks, string_free);
469 }
470
471 result = g_list_reverse (result);
472
473 return result;
474 }
475
476 static void
begin_batch_rename(NautilusBatchRenameDialog * dialog,GList * new_names)477 begin_batch_rename (NautilusBatchRenameDialog *dialog,
478 GList *new_names)
479 {
480 batch_rename_sort_lists_for_rename (&dialog->selection, &new_names, NULL, NULL, NULL, FALSE);
481
482 /* do the actual rename here */
483 nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL);
484
485 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)), NULL);
486 }
487
488 static void
listbox_header_func(GtkListBoxRow * row,GtkListBoxRow * before,NautilusBatchRenameDialog * dialog)489 listbox_header_func (GtkListBoxRow *row,
490 GtkListBoxRow *before,
491 NautilusBatchRenameDialog *dialog)
492 {
493 gboolean show_separator;
494
495 show_separator = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row),
496 "show-separator"));
497
498 if (show_separator)
499 {
500 GtkWidget *separator;
501
502 separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
503 gtk_widget_show (separator);
504
505 gtk_list_box_row_set_header (row, separator);
506 }
507 }
508
509 /* This is manually done instead of using GtkSizeGroup because of the computational
510 * complexity of the later.*/
511 static void
update_rows_height(NautilusBatchRenameDialog * dialog)512 update_rows_height (NautilusBatchRenameDialog *dialog)
513 {
514 GList *l;
515 gint current_row_natural_height;
516 gint maximum_height;
517
518 maximum_height = -1;
519
520 /* check if maximum height has changed */
521 for (l = dialog->listbox_labels_new; l != NULL; l = l->next)
522 {
523 gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
524 NULL,
525 ¤t_row_natural_height);
526
527 if (current_row_natural_height > maximum_height)
528 {
529 maximum_height = current_row_natural_height;
530 }
531 }
532
533 for (l = dialog->listbox_labels_old; l != NULL; l = l->next)
534 {
535 gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
536 NULL,
537 ¤t_row_natural_height);
538
539 if (current_row_natural_height > maximum_height)
540 {
541 maximum_height = current_row_natural_height;
542 }
543 }
544
545 for (l = dialog->listbox_icons; l != NULL; l = l->next)
546 {
547 gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
548 NULL,
549 ¤t_row_natural_height);
550
551 if (current_row_natural_height > maximum_height)
552 {
553 maximum_height = current_row_natural_height;
554 }
555 }
556
557 if (maximum_height != dialog->row_height)
558 {
559 dialog->row_height = maximum_height + ROW_MARGIN_TOP_BOTTOM * 2;
560
561 for (l = dialog->listbox_icons; l != NULL; l = l->next)
562 {
563 g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
564 }
565
566 for (l = dialog->listbox_labels_new; l != NULL; l = l->next)
567 {
568 g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
569 }
570
571 for (l = dialog->listbox_labels_old; l != NULL; l = l->next)
572 {
573 g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
574 }
575 }
576 }
577
578 static GtkWidget *
create_original_name_row_for_label(NautilusBatchRenameDialog * dialog,const gchar * old_text,gboolean show_separator)579 create_original_name_row_for_label (NautilusBatchRenameDialog *dialog,
580 const gchar *old_text,
581 gboolean show_separator)
582 {
583 GtkWidget *row;
584 GtkWidget *label_old;
585
586 row = gtk_list_box_row_new ();
587
588 g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
589
590 label_old = gtk_label_new (old_text);
591 gtk_label_set_xalign (GTK_LABEL (label_old), 0.0);
592 gtk_widget_set_hexpand (label_old, TRUE);
593 gtk_widget_set_margin_start (label_old, ROW_MARGIN_START);
594 gtk_label_set_ellipsize (GTK_LABEL (label_old), PANGO_ELLIPSIZE_END);
595
596 dialog->listbox_labels_old = g_list_prepend (dialog->listbox_labels_old, label_old);
597
598 gtk_container_add (GTK_CONTAINER (row), label_old);
599 gtk_widget_show_all (row);
600
601 return row;
602 }
603
604 static GtkWidget *
create_result_row_for_label(NautilusBatchRenameDialog * dialog,const gchar * new_text,gboolean show_separator)605 create_result_row_for_label (NautilusBatchRenameDialog *dialog,
606 const gchar *new_text,
607 gboolean show_separator)
608 {
609 GtkWidget *row;
610 GtkWidget *label_new;
611
612 row = gtk_list_box_row_new ();
613
614 g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
615
616 label_new = gtk_label_new (new_text);
617 gtk_label_set_xalign (GTK_LABEL (label_new), 0.0);
618 gtk_widget_set_hexpand (label_new, TRUE);
619 gtk_widget_set_margin_start (label_new, ROW_MARGIN_START);
620 gtk_label_set_ellipsize (GTK_LABEL (label_new), PANGO_ELLIPSIZE_END);
621
622 dialog->listbox_labels_new = g_list_prepend (dialog->listbox_labels_new, label_new);
623
624 gtk_container_add (GTK_CONTAINER (row), label_new);
625 gtk_widget_show_all (row);
626
627 return row;
628 }
629
630 static GtkWidget *
create_arrow_row_for_label(NautilusBatchRenameDialog * dialog,gboolean show_separator)631 create_arrow_row_for_label (NautilusBatchRenameDialog *dialog,
632 gboolean show_separator)
633 {
634 GtkWidget *row;
635 GtkWidget *icon;
636
637 row = gtk_list_box_row_new ();
638
639 g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
640
641 if (gtk_widget_get_direction (row) == GTK_TEXT_DIR_RTL)
642 {
643 icon = gtk_label_new ("←");
644 }
645 else
646 {
647 icon = gtk_label_new ("→");
648 }
649
650 gtk_label_set_xalign (GTK_LABEL (icon), 1.0);
651 gtk_widget_set_hexpand (icon, FALSE);
652 gtk_widget_set_margin_start (icon, ROW_MARGIN_START);
653
654 dialog->listbox_icons = g_list_prepend (dialog->listbox_icons, icon);
655
656 gtk_container_add (GTK_CONTAINER (row), icon);
657 gtk_widget_show_all (row);
658
659 return row;
660 }
661
662 static void
prepare_batch_rename(NautilusBatchRenameDialog * dialog)663 prepare_batch_rename (NautilusBatchRenameDialog *dialog)
664 {
665 GdkCursor *cursor;
666 GdkDisplay *display;
667
668 /* wait for checking conflicts to finish, to be sure that
669 * the rename can actually take place */
670 if (dialog->directories_pending_conflict_check != NULL)
671 {
672 dialog->rename_clicked = TRUE;
673 return;
674 }
675
676 if (!gtk_widget_is_sensitive (dialog->rename_button))
677 {
678 return;
679 }
680
681 display = gtk_widget_get_display (GTK_WIDGET (dialog->window));
682 cursor = gdk_cursor_new_from_name (display, "progress");
683 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)),
684 cursor);
685 g_object_unref (cursor);
686
687 display = gtk_widget_get_display (GTK_WIDGET (dialog));
688 cursor = gdk_cursor_new_from_name (display, "progress");
689 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog)),
690 cursor);
691 g_object_unref (cursor);
692
693 gtk_widget_hide (GTK_WIDGET (dialog));
694 begin_batch_rename (dialog, dialog->new_names);
695
696 gtk_widget_destroy (GTK_WIDGET (dialog));
697 }
698
699 static void
batch_rename_dialog_on_response(NautilusBatchRenameDialog * dialog,gint response_id,gpointer user_data)700 batch_rename_dialog_on_response (NautilusBatchRenameDialog *dialog,
701 gint response_id,
702 gpointer user_data)
703 {
704 if (response_id == GTK_RESPONSE_OK)
705 {
706 prepare_batch_rename (dialog);
707 }
708 else
709 {
710 if (dialog->directories_pending_conflict_check != NULL)
711 {
712 cancel_conflict_check (dialog);
713 }
714
715 gtk_widget_destroy (GTK_WIDGET (dialog));
716 }
717 }
718
719 static void
fill_display_listbox(NautilusBatchRenameDialog * dialog)720 fill_display_listbox (NautilusBatchRenameDialog *dialog)
721 {
722 GtkWidget *row;
723 GList *l1;
724 GList *l2;
725 NautilusFile *file;
726 GString *new_name;
727 gchar *name;
728
729 dialog->original_name_listbox_rows = NULL;
730 dialog->arrow_listbox_rows = NULL;
731 dialog->result_listbox_rows = NULL;
732
733 gtk_size_group_add_widget (dialog->size_group, dialog->result_listbox);
734 gtk_size_group_add_widget (dialog->size_group, dialog->original_name_listbox);
735
736 for (l1 = dialog->new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
737 {
738 file = NAUTILUS_FILE (l2->data);
739 new_name = l1->data;
740
741 name = nautilus_file_get_name (file);
742 row = create_original_name_row_for_label (dialog, name, TRUE);
743 gtk_container_add (GTK_CONTAINER (dialog->original_name_listbox), row);
744 dialog->original_name_listbox_rows = g_list_prepend (dialog->original_name_listbox_rows,
745 row);
746
747 row = create_arrow_row_for_label (dialog, TRUE);
748 gtk_container_add (GTK_CONTAINER (dialog->arrow_listbox), row);
749 dialog->arrow_listbox_rows = g_list_prepend (dialog->arrow_listbox_rows,
750 row);
751
752 row = create_result_row_for_label (dialog, new_name->str, TRUE);
753 gtk_container_add (GTK_CONTAINER (dialog->result_listbox), row);
754 dialog->result_listbox_rows = g_list_prepend (dialog->result_listbox_rows,
755 row);
756
757 g_free (name);
758 }
759
760 dialog->original_name_listbox_rows = g_list_reverse (dialog->original_name_listbox_rows);
761 dialog->arrow_listbox_rows = g_list_reverse (dialog->arrow_listbox_rows);
762 dialog->result_listbox_rows = g_list_reverse (dialog->result_listbox_rows);
763 dialog->listbox_labels_old = g_list_reverse (dialog->listbox_labels_old);
764 dialog->listbox_labels_new = g_list_reverse (dialog->listbox_labels_new);
765 dialog->listbox_icons = g_list_reverse (dialog->listbox_icons);
766 }
767
768 static void
select_nth_conflict(NautilusBatchRenameDialog * dialog)769 select_nth_conflict (NautilusBatchRenameDialog *dialog)
770 {
771 GList *l;
772 GString *conflict_file_name;
773 GString *display_text;
774 GString *new_name;
775 gint nth_conflict_index;
776 gint nth_conflict;
777 gint name_occurences;
778 GtkAdjustment *adjustment;
779 GtkAllocation allocation;
780 ConflictData *conflict_data;
781
782 nth_conflict = dialog->selected_conflict;
783 l = g_list_nth (dialog->duplicates, nth_conflict);
784 conflict_data = l->data;
785
786 /* the conflict that has to be selected */
787 conflict_file_name = g_string_new (conflict_data->name);
788 display_text = g_string_new ("");
789
790 nth_conflict_index = conflict_data->index;
791
792 l = g_list_nth (dialog->original_name_listbox_rows, nth_conflict_index);
793 gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
794 l->data);
795
796 l = g_list_nth (dialog->arrow_listbox_rows, nth_conflict_index);
797 gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
798 l->data);
799
800 l = g_list_nth (dialog->result_listbox_rows, nth_conflict_index);
801 gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
802 l->data);
803
804 /* scroll to the selected row */
805 adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (dialog->scrolled_window));
806 gtk_widget_get_allocation (GTK_WIDGET (l->data), &allocation);
807 gtk_adjustment_set_value (adjustment, (allocation.height + 1) * nth_conflict_index);
808
809 name_occurences = 0;
810 for (l = dialog->new_names; l != NULL; l = l->next)
811 {
812 new_name = l->data;
813 if (g_string_equal (new_name, conflict_file_name))
814 {
815 name_occurences++;
816 }
817 }
818 if (name_occurences > 1)
819 {
820 g_string_append_printf (display_text,
821 _("“%s” would not be a unique new name."),
822 conflict_file_name->str);
823 }
824 else
825 {
826 g_string_append_printf (display_text,
827 _("“%s” would conflict with an existing file."),
828 conflict_file_name->str);
829 }
830
831 gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
832 display_text->str);
833
834 g_string_free (conflict_file_name, TRUE);
835 g_string_free (display_text, TRUE);
836 }
837
838 static void
select_next_conflict_down(NautilusBatchRenameDialog * dialog)839 select_next_conflict_down (NautilusBatchRenameDialog *dialog)
840 {
841 dialog->selected_conflict++;
842
843 if (dialog->selected_conflict == 1)
844 {
845 gtk_widget_set_sensitive (dialog->conflict_up, TRUE);
846 }
847
848 if (dialog->selected_conflict == dialog->conflicts_number - 1)
849 {
850 gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
851 }
852
853 select_nth_conflict (dialog);
854 }
855
856 static void
select_next_conflict_up(NautilusBatchRenameDialog * dialog)857 select_next_conflict_up (NautilusBatchRenameDialog *dialog)
858 {
859 dialog->selected_conflict--;
860
861 if (dialog->selected_conflict == 0)
862 {
863 gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
864 }
865
866 if (dialog->selected_conflict == dialog->conflicts_number - 2)
867 {
868 gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
869 }
870
871 select_nth_conflict (dialog);
872 }
873
874 static void
update_conflict_row_background(NautilusBatchRenameDialog * dialog)875 update_conflict_row_background (NautilusBatchRenameDialog *dialog)
876 {
877 GList *l1;
878 GList *l2;
879 GList *l3;
880 GList *duplicates;
881 gint index;
882 GtkStyleContext *context;
883 ConflictData *conflict_data;
884
885 index = 0;
886
887 duplicates = dialog->duplicates;
888
889 for (l1 = dialog->original_name_listbox_rows,
890 l2 = dialog->arrow_listbox_rows,
891 l3 = dialog->result_listbox_rows;
892 l1 != NULL && l2 != NULL && l3 != NULL;
893 l1 = l1->next, l2 = l2->next, l3 = l3->next)
894 {
895 context = gtk_widget_get_style_context (GTK_WIDGET (l1->data));
896
897 if (gtk_style_context_has_class (context, "conflict-row"))
898 {
899 gtk_style_context_remove_class (context, "conflict-row");
900
901 context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
902 gtk_style_context_remove_class (context, "conflict-row");
903
904 context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
905 gtk_style_context_remove_class (context, "conflict-row");
906 }
907
908 if (duplicates != NULL)
909 {
910 conflict_data = duplicates->data;
911 if (conflict_data->index == index)
912 {
913 context = gtk_widget_get_style_context (GTK_WIDGET (l1->data));
914 gtk_style_context_add_class (context, "conflict-row");
915
916 context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
917 gtk_style_context_add_class (context, "conflict-row");
918
919 context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
920 gtk_style_context_add_class (context, "conflict-row");
921
922 duplicates = duplicates->next;
923 }
924 }
925 index++;
926 }
927 }
928
929 static void
update_listbox(NautilusBatchRenameDialog * dialog)930 update_listbox (NautilusBatchRenameDialog *dialog)
931 {
932 GList *l1;
933 GList *l2;
934 NautilusFile *file;
935 gchar *old_name;
936 GtkLabel *label;
937 GString *new_name;
938 gboolean empty_name = FALSE;
939
940 for (l1 = dialog->new_names, l2 = dialog->listbox_labels_new; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
941 {
942 label = GTK_LABEL (l2->data);
943 new_name = l1->data;
944
945 gtk_label_set_label (label, new_name->str);
946 gtk_widget_set_tooltip_text (GTK_WIDGET (label), new_name->str);
947
948 if (g_strcmp0 (new_name->str, "") == 0)
949 {
950 empty_name = TRUE;
951 }
952 }
953
954 for (l1 = dialog->selection, l2 = dialog->listbox_labels_old; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
955 {
956 label = GTK_LABEL (l2->data);
957 file = NAUTILUS_FILE (l1->data);
958
959 old_name = nautilus_file_get_name (file);
960 gtk_widget_set_tooltip_text (GTK_WIDGET (label), old_name);
961
962 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT)
963 {
964 gtk_label_set_label (label, old_name);
965 }
966 else
967 {
968 new_name = batch_rename_replace_label_text (old_name,
969 gtk_entry_get_text (GTK_ENTRY (dialog->find_entry)));
970 gtk_label_set_markup (GTK_LABEL (label), new_name->str);
971
972 g_string_free (new_name, TRUE);
973 }
974
975 g_free (old_name);
976 }
977
978 update_rows_height (dialog);
979
980 if (empty_name)
981 {
982 gtk_widget_set_sensitive (dialog->rename_button, FALSE);
983
984 return;
985 }
986
987 /* check if there are name conflicts and display them if they exist */
988 if (dialog->duplicates != NULL)
989 {
990 update_conflict_row_background (dialog);
991
992 gtk_widget_set_sensitive (dialog->rename_button, FALSE);
993
994 gtk_widget_show (dialog->conflict_box);
995
996 dialog->selected_conflict = 0;
997 dialog->conflicts_number = g_list_length (dialog->duplicates);
998
999 select_nth_conflict (dialog);
1000
1001 gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
1002
1003 if (g_list_length (dialog->duplicates) == 1)
1004 {
1005 gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
1006 }
1007 else
1008 {
1009 gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
1010 }
1011 }
1012 else
1013 {
1014 gtk_widget_hide (dialog->conflict_box);
1015
1016 /* re-enable the rename button if there are no more name conflicts */
1017 if (dialog->duplicates == NULL && !gtk_widget_is_sensitive (dialog->rename_button))
1018 {
1019 update_conflict_row_background (dialog);
1020 gtk_widget_set_sensitive (dialog->rename_button, TRUE);
1021 }
1022 }
1023
1024 /* if the rename button was clicked and there's no conflict, then start renaming */
1025 if (dialog->rename_clicked && dialog->duplicates == NULL)
1026 {
1027 prepare_batch_rename (dialog);
1028 }
1029
1030 if (dialog->rename_clicked && dialog->duplicates != NULL)
1031 {
1032 dialog->rename_clicked = FALSE;
1033 }
1034 }
1035
1036 static void
check_conflict_for_files(NautilusBatchRenameDialog * dialog,NautilusDirectory * directory,GList * files)1037 check_conflict_for_files (NautilusBatchRenameDialog *dialog,
1038 NautilusDirectory *directory,
1039 GList *files)
1040 {
1041 gchar *current_directory;
1042 gchar *parent_uri;
1043 gchar *name;
1044 NautilusFile *file;
1045 GString *new_name;
1046 GString *file_name;
1047 GList *l1, *l2;
1048 GHashTable *directory_files_table;
1049 GHashTable *new_names_table;
1050 GHashTable *names_conflicts_table;
1051 gboolean exists;
1052 gboolean have_conflict;
1053 gboolean tag_present;
1054 gboolean same_parent_directory;
1055 ConflictData *conflict_data;
1056
1057 current_directory = nautilus_directory_get_uri (directory);
1058
1059 directory_files_table = g_hash_table_new_full (g_str_hash,
1060 g_str_equal,
1061 (GDestroyNotify) g_free,
1062 NULL);
1063 new_names_table = g_hash_table_new_full (g_str_hash,
1064 g_str_equal,
1065 (GDestroyNotify) g_free,
1066 (GDestroyNotify) g_free);
1067 names_conflicts_table = g_hash_table_new_full (g_str_hash,
1068 g_str_equal,
1069 (GDestroyNotify) g_free,
1070 (GDestroyNotify) g_free);
1071
1072 /* names_conflicts_table is used for knowing which names from the list are not unique,
1073 * so that they can easily be reached when needed */
1074 for (l1 = dialog->new_names, l2 = dialog->selection;
1075 l1 != NULL && l2 != NULL;
1076 l1 = l1->next, l2 = l2->next)
1077 {
1078 new_name = l1->data;
1079 file = NAUTILUS_FILE (l2->data);
1080 parent_uri = nautilus_file_get_parent_uri (file);
1081
1082 tag_present = g_hash_table_lookup (new_names_table, new_name->str) != NULL;
1083 same_parent_directory = g_strcmp0 (parent_uri, current_directory) == 0;
1084
1085 if (same_parent_directory)
1086 {
1087 if (!tag_present)
1088 {
1089 g_hash_table_insert (new_names_table,
1090 g_strdup (new_name->str),
1091 nautilus_file_get_parent_uri (file));
1092 }
1093 else
1094 {
1095 g_hash_table_insert (names_conflicts_table,
1096 g_strdup (new_name->str),
1097 nautilus_file_get_parent_uri (file));
1098 }
1099 }
1100
1101 g_free (parent_uri);
1102 }
1103
1104 for (l1 = files; l1 != NULL; l1 = l1->next)
1105 {
1106 file = NAUTILUS_FILE (l1->data);
1107 g_hash_table_insert (directory_files_table,
1108 nautilus_file_get_name (file),
1109 GINT_TO_POINTER (TRUE));
1110 }
1111
1112 for (l1 = dialog->selection, l2 = dialog->new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
1113 {
1114 file = NAUTILUS_FILE (l1->data);
1115
1116 name = nautilus_file_get_name (file);
1117 file_name = g_string_new (name);
1118 g_free (name);
1119
1120 parent_uri = nautilus_file_get_parent_uri (file);
1121
1122 new_name = l2->data;
1123
1124 have_conflict = FALSE;
1125
1126 /* check for duplicate only if the parent of the current file is
1127 * the current directory and the name of the file has changed */
1128 if (g_strcmp0 (parent_uri, current_directory) == 0 &&
1129 !g_string_equal (new_name, file_name))
1130 {
1131 exists = GPOINTER_TO_INT (g_hash_table_lookup (directory_files_table, new_name->str));
1132
1133 if (exists == TRUE &&
1134 !file_name_conflicts_with_results (dialog->selection, dialog->new_names, new_name, parent_uri))
1135 {
1136 conflict_data = g_new (ConflictData, 1);
1137 conflict_data->name = g_strdup (new_name->str);
1138 conflict_data->index = g_list_index (dialog->selection, l1->data);
1139 dialog->duplicates = g_list_prepend (dialog->duplicates,
1140 conflict_data);
1141
1142 have_conflict = TRUE;
1143 }
1144 }
1145
1146 if (!have_conflict)
1147 {
1148 tag_present = g_hash_table_lookup (names_conflicts_table, new_name->str) != NULL;
1149 same_parent_directory = g_strcmp0 (parent_uri, current_directory) == 0;
1150
1151 if (tag_present && same_parent_directory)
1152 {
1153 conflict_data = g_new (ConflictData, 1);
1154 conflict_data->name = g_strdup (new_name->str);
1155 conflict_data->index = g_list_index (dialog->selection, l1->data);
1156 dialog->duplicates = g_list_prepend (dialog->duplicates,
1157 conflict_data);
1158
1159 have_conflict = TRUE;
1160 }
1161 }
1162
1163 g_string_free (file_name, TRUE);
1164 g_free (parent_uri);
1165 }
1166
1167 g_free (current_directory);
1168 g_hash_table_destroy (directory_files_table);
1169 g_hash_table_destroy (new_names_table);
1170 g_hash_table_destroy (names_conflicts_table);
1171 }
1172
1173 static void
on_directory_attributes_ready_for_conflicts_check(NautilusDirectory * conflict_directory,GList * files,gpointer callback_data)1174 on_directory_attributes_ready_for_conflicts_check (NautilusDirectory *conflict_directory,
1175 GList *files,
1176 gpointer callback_data)
1177 {
1178 NautilusBatchRenameDialog *self;
1179
1180 self = NAUTILUS_BATCH_RENAME_DIALOG (callback_data);
1181
1182 check_conflict_for_files (self, conflict_directory, files);
1183
1184 g_assert (g_list_find (self->directories_pending_conflict_check, conflict_directory) != NULL);
1185
1186 self->directories_pending_conflict_check = g_list_remove (self->directories_pending_conflict_check, conflict_directory);
1187
1188 nautilus_directory_unref (conflict_directory);
1189
1190 if (self->directories_pending_conflict_check == NULL)
1191 {
1192 self->duplicates = g_list_reverse (self->duplicates);
1193
1194 update_listbox (self);
1195 }
1196 }
1197
1198 static void
cancel_conflict_check(NautilusBatchRenameDialog * self)1199 cancel_conflict_check (NautilusBatchRenameDialog *self)
1200 {
1201 GList *l;
1202 NautilusDirectory *directory;
1203
1204 for (l = self->directories_pending_conflict_check; l != NULL; l = l->next)
1205 {
1206 directory = l->data;
1207
1208 nautilus_directory_cancel_callback (directory,
1209 on_directory_attributes_ready_for_conflicts_check,
1210 self);
1211 }
1212
1213 g_clear_list (&self->directories_pending_conflict_check, g_object_unref);
1214 }
1215
1216 static void
file_names_list_has_duplicates_async(NautilusBatchRenameDialog * self)1217 file_names_list_has_duplicates_async (NautilusBatchRenameDialog *self)
1218 {
1219 GList *l;
1220
1221 if (self->directories_pending_conflict_check != NULL)
1222 {
1223 cancel_conflict_check (self);
1224 }
1225
1226 self->directories_pending_conflict_check = nautilus_directory_list_copy (self->distinct_parent_directories);
1227 self->duplicates = NULL;
1228
1229 for (l = self->distinct_parent_directories; l != NULL; l = l->next)
1230 {
1231 nautilus_directory_call_when_ready (l->data,
1232 NAUTILUS_FILE_ATTRIBUTE_INFO,
1233 TRUE,
1234 on_directory_attributes_ready_for_conflicts_check,
1235 self);
1236 }
1237 }
1238
1239 static gboolean
have_unallowed_character(NautilusBatchRenameDialog * dialog)1240 have_unallowed_character (NautilusBatchRenameDialog *dialog)
1241 {
1242 GList *names;
1243 GString *new_name;
1244 const gchar *entry_text;
1245 gboolean have_empty_name;
1246 gboolean have_unallowed_character_slash;
1247 gboolean have_unallowed_character_dot;
1248 gboolean have_unallowed_character_dotdot;
1249
1250 have_empty_name = FALSE;
1251 have_unallowed_character_slash = FALSE;
1252 have_unallowed_character_dot = FALSE;
1253 have_unallowed_character_dotdot = FALSE;
1254
1255 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT)
1256 {
1257 entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
1258 }
1259 else
1260 {
1261 entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
1262 }
1263
1264 if (strstr (entry_text, "/") != NULL)
1265 {
1266 have_unallowed_character_slash = TRUE;
1267 }
1268
1269 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT && g_strcmp0 (entry_text, ".") == 0)
1270 {
1271 have_unallowed_character_dot = TRUE;
1272 }
1273 else if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
1274 {
1275 for (names = dialog->new_names; names != NULL; names = names->next)
1276 {
1277 new_name = names->data;
1278
1279 if (g_strcmp0 (new_name->str, ".") == 0)
1280 {
1281 have_unallowed_character_dot = TRUE;
1282 break;
1283 }
1284 }
1285 }
1286
1287 if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT && g_strcmp0 (entry_text, "..") == 0)
1288 {
1289 have_unallowed_character_dotdot = TRUE;
1290 }
1291 else if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
1292 {
1293 for (names = dialog->new_names; names != NULL; names = names->next)
1294 {
1295 new_name = names->data;
1296
1297 if (g_strcmp0 (new_name->str, "") == 0)
1298 {
1299 have_empty_name = TRUE;
1300 break;
1301 }
1302
1303 if (g_strcmp0 (new_name->str, "..") == 0)
1304 {
1305 have_unallowed_character_dotdot = TRUE;
1306 break;
1307 }
1308 }
1309 }
1310
1311 if (have_empty_name)
1312 {
1313 gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1314 _("Name cannot be empty."));
1315 }
1316
1317 if (have_unallowed_character_slash)
1318 {
1319 gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1320 _("Name cannot contain “/”."));
1321 }
1322
1323 if (have_unallowed_character_dot)
1324 {
1325 gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1326 _("“.” is not a valid name."));
1327 }
1328
1329 if (have_unallowed_character_dotdot)
1330 {
1331 gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1332 _("“..” is not a valid name."));
1333 }
1334
1335 if (have_unallowed_character_slash || have_unallowed_character_dot || have_unallowed_character_dotdot
1336 || have_empty_name)
1337 {
1338 gtk_widget_set_sensitive (dialog->rename_button, FALSE);
1339 gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
1340 gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
1341
1342 gtk_widget_show (dialog->conflict_box);
1343
1344 return TRUE;
1345 }
1346 else
1347 {
1348 gtk_widget_hide (dialog->conflict_box);
1349
1350 return FALSE;
1351 }
1352 }
1353
1354 static gboolean
numbering_tag_is_some_added(NautilusBatchRenameDialog * self)1355 numbering_tag_is_some_added (NautilusBatchRenameDialog *self)
1356 {
1357 guint i;
1358 TagData *tag_data;
1359
1360 for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
1361 {
1362 g_autofree gchar *tag_text_representation = NULL;
1363
1364 tag_text_representation = batch_rename_get_tag_text_representation (numbering_tags_constants[i]);
1365 tag_data = g_hash_table_lookup (self->tag_info_table, tag_text_representation);
1366 if (tag_data->set)
1367 {
1368 return TRUE;
1369 }
1370 }
1371
1372 return FALSE;
1373 }
1374
1375 static void
update_display_text(NautilusBatchRenameDialog * dialog)1376 update_display_text (NautilusBatchRenameDialog *dialog)
1377 {
1378 if (dialog->selection == NULL)
1379 {
1380 return;
1381 }
1382
1383 if (dialog->duplicates != NULL)
1384 {
1385 g_list_free_full (dialog->duplicates, conflict_data_free);
1386 dialog->duplicates = NULL;
1387 }
1388
1389 if (dialog->new_names != NULL)
1390 {
1391 g_list_free_full (dialog->new_names, string_free);
1392 }
1393
1394 if (!numbering_tag_is_some_added (dialog))
1395 {
1396 gtk_revealer_set_reveal_child (GTK_REVEALER (dialog->numbering_revealer), FALSE);
1397 }
1398 else
1399 {
1400 gtk_revealer_set_reveal_child (GTK_REVEALER (dialog->numbering_revealer), TRUE);
1401 }
1402
1403 dialog->new_names = batch_rename_dialog_get_new_names (dialog);
1404
1405 if (have_unallowed_character (dialog))
1406 {
1407 return;
1408 }
1409
1410 file_names_list_has_duplicates_async (dialog);
1411 }
1412
1413 static void
batch_rename_dialog_mode_changed(NautilusBatchRenameDialog * dialog)1414 batch_rename_dialog_mode_changed (NautilusBatchRenameDialog *dialog)
1415 {
1416 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->format_mode_button)))
1417 {
1418 gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "format");
1419
1420 dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT;
1421
1422 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry));
1423 }
1424 else
1425 {
1426 gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "replace");
1427
1428 dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_REPLACE;
1429
1430 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->find_entry));
1431 }
1432
1433 update_display_text (dialog);
1434 }
1435
1436 static void
add_button_clicked(NautilusBatchRenameDialog * dialog)1437 add_button_clicked (NautilusBatchRenameDialog *dialog)
1438 {
1439 if (gtk_widget_is_visible (dialog->add_popover))
1440 {
1441 gtk_widget_set_visible (dialog->add_popover, FALSE);
1442 }
1443 else
1444 {
1445 gtk_widget_set_visible (dialog->add_popover, TRUE);
1446 }
1447 }
1448
1449 static void
add_popover_closed(NautilusBatchRenameDialog * dialog)1450 add_popover_closed (NautilusBatchRenameDialog *dialog)
1451 {
1452 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->add_button), FALSE);
1453 }
1454
1455 static void
numbering_order_button_clicked(NautilusBatchRenameDialog * dialog)1456 numbering_order_button_clicked (NautilusBatchRenameDialog *dialog)
1457 {
1458 if (gtk_widget_is_visible (dialog->numbering_order_popover))
1459 {
1460 gtk_widget_set_visible (dialog->numbering_order_popover, FALSE);
1461 }
1462 else
1463 {
1464 gtk_widget_set_visible (dialog->numbering_order_popover, TRUE);
1465 }
1466 }
1467
1468 static void
numbering_order_popover_closed(NautilusBatchRenameDialog * dialog)1469 numbering_order_popover_closed (NautilusBatchRenameDialog *dialog)
1470 {
1471 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE);
1472 }
1473
1474 void
nautilus_batch_rename_dialog_query_finished(NautilusBatchRenameDialog * dialog,GHashTable * hash_table,GList * selection_metadata)1475 nautilus_batch_rename_dialog_query_finished (NautilusBatchRenameDialog *dialog,
1476 GHashTable *hash_table,
1477 GList *selection_metadata)
1478 {
1479 GMenuItem *first_created;
1480 GMenuItem *last_created;
1481 FileMetadata *file_metadata;
1482 MetadataType metadata_type;
1483 gboolean is_metadata;
1484 TagData *tag_data;
1485 g_autoptr (GList) tag_info_keys = NULL;
1486 GList *l;
1487
1488 /* for files with no metadata */
1489 if (hash_table != NULL && g_hash_table_size (hash_table) == 0)
1490 {
1491 g_hash_table_destroy (hash_table);
1492
1493 hash_table = NULL;
1494 }
1495
1496 if (hash_table == NULL)
1497 {
1498 dialog->create_date = NULL;
1499 }
1500 else
1501 {
1502 dialog->create_date = hash_table;
1503 }
1504
1505 if (dialog->create_date != NULL)
1506 {
1507 first_created = g_menu_item_new ("First Created",
1508 "dialog.numbering-order-changed('first-created')");
1509
1510 g_menu_append_item (dialog->numbering_order_menu, first_created);
1511
1512 last_created = g_menu_item_new ("Last Created",
1513 "dialog.numbering-order-changed('last-created')");
1514
1515 g_menu_append_item (dialog->numbering_order_menu, last_created);
1516 }
1517
1518 dialog->selection_metadata = selection_metadata;
1519 file_metadata = selection_metadata->data;
1520 tag_info_keys = g_hash_table_get_keys (dialog->tag_info_table);
1521 for (l = tag_info_keys; l != NULL; l = l->next)
1522 {
1523 /* Only metadata has to be handled here. */
1524 tag_data = g_hash_table_lookup (dialog->tag_info_table, l->data);
1525 is_metadata = tag_data->tag_constants.is_metadata;
1526 if (!is_metadata)
1527 {
1528 continue;
1529 }
1530
1531 metadata_type = tag_data->tag_constants.metadata_type;
1532 if (file_metadata->metadata[metadata_type] == NULL ||
1533 file_metadata->metadata[metadata_type]->len <= 0)
1534 {
1535 disable_action (dialog, tag_data->tag_constants.action_name);
1536 tag_data->available = FALSE;
1537 }
1538 }
1539 }
1540
1541 static void
update_row_shadowing(GtkWidget * row,gboolean shown)1542 update_row_shadowing (GtkWidget *row,
1543 gboolean shown)
1544 {
1545 GtkStyleContext *context;
1546 GtkStateFlags flags;
1547
1548 if (!GTK_IS_LIST_BOX_ROW (row))
1549 {
1550 return;
1551 }
1552
1553 context = gtk_widget_get_style_context (row);
1554 flags = gtk_style_context_get_state (context);
1555
1556 if (shown)
1557 {
1558 flags |= GTK_STATE_PRELIGHT;
1559 }
1560 else
1561 {
1562 flags &= ~GTK_STATE_PRELIGHT;
1563 }
1564
1565 gtk_style_context_set_state (context, flags);
1566 }
1567
1568 static gboolean
on_motion_notify(GtkWidget * widget,GdkEvent * event,gpointer user_data)1569 on_motion_notify (GtkWidget *widget,
1570 GdkEvent *event,
1571 gpointer user_data)
1572 {
1573 NautilusBatchRenameDialog *dialog;
1574 gdouble y;
1575 GtkListBoxRow *row;
1576
1577 dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
1578
1579 if (dialog->preselected_row1 && dialog->preselected_row2)
1580 {
1581 update_row_shadowing (dialog->preselected_row1, FALSE);
1582 update_row_shadowing (dialog->preselected_row2, FALSE);
1583 }
1584
1585 if (G_UNLIKELY (!gdk_event_get_coords (event, NULL, &y)))
1586 {
1587 g_return_val_if_reached (GDK_EVENT_PROPAGATE);
1588 }
1589
1590 if (widget == dialog->result_listbox)
1591 {
1592 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), y);
1593 update_row_shadowing (GTK_WIDGET (row), TRUE);
1594 dialog->preselected_row1 = GTK_WIDGET (row);
1595
1596 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), y);
1597 update_row_shadowing (GTK_WIDGET (row), TRUE);
1598 dialog->preselected_row2 = GTK_WIDGET (row);
1599 }
1600
1601 if (widget == dialog->arrow_listbox)
1602 {
1603 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), y);
1604 update_row_shadowing (GTK_WIDGET (row), TRUE);
1605 dialog->preselected_row1 = GTK_WIDGET (row);
1606
1607 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), y);
1608 update_row_shadowing (GTK_WIDGET (row), TRUE);
1609 dialog->preselected_row2 = GTK_WIDGET (row);
1610 }
1611
1612 if (widget == dialog->original_name_listbox)
1613 {
1614 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), y);
1615 update_row_shadowing (GTK_WIDGET (row), TRUE);
1616 dialog->preselected_row1 = GTK_WIDGET (row);
1617
1618 row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), y);
1619 update_row_shadowing (GTK_WIDGET (row), TRUE);
1620 dialog->preselected_row2 = GTK_WIDGET (row);
1621 }
1622
1623 return GDK_EVENT_PROPAGATE;
1624 }
1625
1626 static gboolean
on_leave_notify(GtkWidget * widget,GdkEvent * event,gpointer user_data)1627 on_leave_notify (GtkWidget *widget,
1628 GdkEvent *event,
1629 gpointer user_data)
1630 {
1631 NautilusBatchRenameDialog *dialog;
1632
1633 dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
1634
1635 update_row_shadowing (dialog->preselected_row1, FALSE);
1636 update_row_shadowing (dialog->preselected_row2, FALSE);
1637
1638 dialog->preselected_row1 = NULL;
1639 dialog->preselected_row2 = NULL;
1640
1641 return GDK_EVENT_PROPAGATE;
1642 }
1643
1644 static gboolean
on_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)1645 on_event (GtkWidget *widget,
1646 GdkEvent *event,
1647 gpointer user_data)
1648 {
1649 GdkEventType event_type;
1650
1651 event_type = gdk_event_get_event_type (event);
1652
1653 if (event_type == GDK_MOTION_NOTIFY)
1654 {
1655 return on_motion_notify (widget, event, user_data);
1656 }
1657
1658 if (event_type == GDK_LEAVE_NOTIFY)
1659 {
1660 return on_leave_notify (widget, event, user_data);
1661 }
1662
1663 return GDK_EVENT_PROPAGATE;
1664 }
1665
1666 static void
nautilus_batch_rename_dialog_initialize_actions(NautilusBatchRenameDialog * dialog)1667 nautilus_batch_rename_dialog_initialize_actions (NautilusBatchRenameDialog *dialog)
1668 {
1669 GAction *action;
1670
1671 dialog->action_group = G_ACTION_GROUP (g_simple_action_group_new ());
1672
1673 g_action_map_add_action_entries (G_ACTION_MAP (dialog->action_group),
1674 dialog_entries,
1675 G_N_ELEMENTS (dialog_entries),
1676 dialog);
1677 gtk_widget_insert_action_group (GTK_WIDGET (dialog),
1678 "dialog",
1679 G_ACTION_GROUP (dialog->action_group));
1680
1681 action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
1682 metadata_tags_constants[ORIGINAL_FILE_NAME].action_name);
1683 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
1684
1685 check_metadata_for_selection (dialog, dialog->selection,
1686 dialog->metadata_cancellable);
1687 }
1688
1689 static void
file_names_widget_on_activate(NautilusBatchRenameDialog * dialog)1690 file_names_widget_on_activate (NautilusBatchRenameDialog *dialog)
1691 {
1692 prepare_batch_rename (dialog);
1693 }
1694
1695 static void
remove_tag(NautilusBatchRenameDialog * dialog,TagData * tag_data)1696 remove_tag (NautilusBatchRenameDialog *dialog,
1697 TagData *tag_data)
1698 {
1699 GAction *action;
1700
1701 if (!tag_data->set)
1702 {
1703 g_warning ("Trying to remove an already removed tag");
1704
1705 return;
1706 }
1707
1708 tag_data->set = FALSE;
1709 tag_data->position = -1;
1710 action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
1711 tag_data->tag_constants.action_name);
1712 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
1713 }
1714
1715 static gint
compare_tag_position(gconstpointer a,gconstpointer b)1716 compare_tag_position (gconstpointer a,
1717 gconstpointer b)
1718 {
1719 const TagData *tag_data1 = a;
1720 const TagData *tag_data2 = b;
1721
1722 return tag_data1->position - tag_data2->position;
1723 }
1724
1725 typedef enum
1726 {
1727 TEXT_WAS_DELETED,
1728 TEXT_WAS_INSERTED
1729 } TextChangedMode;
1730
1731 static GList *
get_tags_intersecting_sorted(NautilusBatchRenameDialog * self,gint start_position,gint end_position,TextChangedMode text_changed_mode)1732 get_tags_intersecting_sorted (NautilusBatchRenameDialog *self,
1733 gint start_position,
1734 gint end_position,
1735 TextChangedMode text_changed_mode)
1736 {
1737 g_autoptr (GList) tag_info_keys = NULL;
1738 TagData *tag_data;
1739 GList *l;
1740 GList *intersecting_tags = NULL;
1741 gint tag_end_position;
1742
1743 tag_info_keys = g_hash_table_get_keys (self->tag_info_table);
1744 for (l = tag_info_keys; l != NULL; l = l->next)
1745 {
1746 g_autofree gchar *tag_text_representation = NULL;
1747
1748 tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
1749 tag_text_representation = batch_rename_get_tag_text_representation (tag_data->tag_constants);
1750 tag_end_position = tag_data->position + g_utf8_strlen (tag_text_representation, -1);
1751 if (tag_data->set && !tag_data->just_added)
1752 {
1753 gboolean selection_intersects_tag_start;
1754 gboolean selection_intersects_tag_end;
1755 gboolean tag_is_contained_in_selection;
1756
1757 if (text_changed_mode == TEXT_WAS_DELETED)
1758 {
1759 selection_intersects_tag_start = end_position > tag_data->position &&
1760 end_position <= tag_end_position;
1761 selection_intersects_tag_end = start_position >= tag_data->position &&
1762 start_position < tag_end_position;
1763 tag_is_contained_in_selection = start_position <= tag_data->position &&
1764 end_position >= tag_end_position;
1765 }
1766 else
1767 {
1768 selection_intersects_tag_start = start_position > tag_data->position &&
1769 start_position < tag_end_position;
1770 selection_intersects_tag_end = FALSE;
1771 tag_is_contained_in_selection = FALSE;
1772 }
1773 if (selection_intersects_tag_end || selection_intersects_tag_start || tag_is_contained_in_selection)
1774 {
1775 intersecting_tags = g_list_prepend (intersecting_tags, tag_data);
1776 }
1777 }
1778 }
1779
1780 return g_list_sort (intersecting_tags, compare_tag_position);
1781 }
1782
1783 static void
update_tags_positions(NautilusBatchRenameDialog * self,gint start_position,gint end_position,TextChangedMode text_changed_mode)1784 update_tags_positions (NautilusBatchRenameDialog *self,
1785 gint start_position,
1786 gint end_position,
1787 TextChangedMode text_changed_mode)
1788 {
1789 g_autoptr (GList) tag_info_keys = NULL;
1790 TagData *tag_data;
1791 GList *l;
1792
1793 tag_info_keys = g_hash_table_get_keys (self->tag_info_table);
1794 for (l = tag_info_keys; l != NULL; l = l->next)
1795 {
1796 tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
1797 if (tag_data->set && !tag_data->just_added && tag_data->position >= start_position)
1798 {
1799 if (text_changed_mode == TEXT_WAS_DELETED)
1800 {
1801 tag_data->position -= end_position - start_position;
1802 }
1803 else
1804 {
1805 tag_data->position += end_position - start_position;
1806 }
1807 }
1808 }
1809 }
1810
1811 static void
on_delete_text(GtkEditable * editable,gint start_position,gint end_position,gpointer user_data)1812 on_delete_text (GtkEditable *editable,
1813 gint start_position,
1814 gint end_position,
1815 gpointer user_data)
1816 {
1817 NautilusBatchRenameDialog *self;
1818 g_autoptr (GList) intersecting_tags = NULL;
1819 gint final_start_position;
1820 gint final_end_position;
1821 GList *l;
1822
1823 self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
1824 intersecting_tags = get_tags_intersecting_sorted (self, start_position,
1825 end_position, TEXT_WAS_DELETED);
1826 if (intersecting_tags)
1827 {
1828 gint last_tag_end_position;
1829 g_autofree gchar *tag_text_representation = NULL;
1830 TagData *first_tag = g_list_first (intersecting_tags)->data;
1831 TagData *last_tag = g_list_last (intersecting_tags)->data;
1832
1833 tag_text_representation = batch_rename_get_tag_text_representation (last_tag->tag_constants);
1834 last_tag_end_position = last_tag->position +
1835 g_utf8_strlen (tag_text_representation, -1);
1836 final_start_position = MIN (start_position, first_tag->position);
1837 final_end_position = MAX (end_position, last_tag_end_position);
1838 }
1839 else
1840 {
1841 final_start_position = start_position;
1842 final_end_position = end_position;
1843 }
1844
1845 g_signal_handlers_block_by_func (editable, (gpointer) on_delete_text, user_data);
1846 gtk_editable_delete_text (editable, final_start_position, final_end_position);
1847 g_signal_handlers_unblock_by_func (editable, (gpointer) on_delete_text, user_data);
1848
1849 /* Mark the tags as removed */
1850 for (l = intersecting_tags; l != NULL; l = l->next)
1851 {
1852 remove_tag (self, l->data);
1853 }
1854
1855 /* If we removed the numbering tag, we want to enable all numbering actions */
1856 if (!numbering_tag_is_some_added (self))
1857 {
1858 guint i;
1859
1860 for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
1861 {
1862 enable_action (self, numbering_tags_constants[i].action_name);
1863 }
1864 }
1865
1866 update_tags_positions (self, final_start_position,
1867 final_end_position, TEXT_WAS_DELETED);
1868 update_display_text (self);
1869
1870 g_signal_stop_emission_by_name (editable, "delete-text");
1871 }
1872
1873 static void
on_insert_text(GtkEditable * editable,const gchar * new_text,gint new_text_length,gpointer position,gpointer user_data)1874 on_insert_text (GtkEditable *editable,
1875 const gchar *new_text,
1876 gint new_text_length,
1877 gpointer position,
1878 gpointer user_data)
1879 {
1880 NautilusBatchRenameDialog *self;
1881 gint start_position;
1882 gint end_position;
1883 g_autoptr (GList) intersecting_tags = NULL;
1884
1885 self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
1886 start_position = *(int *) position;
1887 end_position = start_position + g_utf8_strlen (new_text, -1);
1888 intersecting_tags = get_tags_intersecting_sorted (self, start_position,
1889 end_position, TEXT_WAS_INSERTED);
1890 if (!intersecting_tags)
1891 {
1892 g_signal_handlers_block_by_func (editable, (gpointer) on_insert_text, user_data);
1893 gtk_editable_insert_text (editable, new_text, new_text_length, position);
1894 g_signal_handlers_unblock_by_func (editable, (gpointer) on_insert_text, user_data);
1895
1896 update_tags_positions (self, start_position, end_position, TEXT_WAS_INSERTED);
1897 update_display_text (self);
1898 }
1899
1900 g_signal_stop_emission_by_name (editable, "insert-text");
1901 }
1902
1903 static void
file_names_widget_entry_on_changed(NautilusBatchRenameDialog * self)1904 file_names_widget_entry_on_changed (NautilusBatchRenameDialog *self)
1905 {
1906 update_display_text (self);
1907 }
1908
1909 static void
nautilus_batch_rename_dialog_finalize(GObject * object)1910 nautilus_batch_rename_dialog_finalize (GObject *object)
1911 {
1912 NautilusBatchRenameDialog *dialog;
1913 GList *l;
1914 guint i;
1915
1916 dialog = NAUTILUS_BATCH_RENAME_DIALOG (object);
1917
1918 if (dialog->directories_pending_conflict_check != NULL)
1919 {
1920 cancel_conflict_check (dialog);
1921 }
1922
1923 g_clear_object (&dialog->numbering_order_menu);
1924 g_clear_object (&dialog->add_tag_menu);
1925 g_list_free (dialog->original_name_listbox_rows);
1926 g_list_free (dialog->arrow_listbox_rows);
1927 g_list_free (dialog->result_listbox_rows);
1928 g_list_free (dialog->listbox_labels_new);
1929 g_list_free (dialog->listbox_labels_old);
1930 g_list_free (dialog->listbox_icons);
1931
1932 for (l = dialog->selection_metadata; l != NULL; l = l->next)
1933 {
1934 FileMetadata *file_metadata;
1935
1936 file_metadata = l->data;
1937 for (i = 0; i < G_N_ELEMENTS (file_metadata->metadata); i++)
1938 {
1939 if (file_metadata->metadata[i])
1940 {
1941 g_string_free (file_metadata->metadata[i], TRUE);
1942 }
1943 }
1944
1945 g_string_free (file_metadata->file_name, TRUE);
1946 g_free (file_metadata);
1947 }
1948
1949 if (dialog->create_date != NULL)
1950 {
1951 g_hash_table_destroy (dialog->create_date);
1952 }
1953
1954 g_list_free_full (dialog->new_names, string_free);
1955 g_list_free_full (dialog->duplicates, conflict_data_free);
1956
1957 nautilus_file_list_free (dialog->selection);
1958 nautilus_directory_unref (dialog->directory);
1959 nautilus_directory_list_free (dialog->distinct_parent_directories);
1960
1961 g_object_unref (dialog->size_group);
1962
1963 g_hash_table_destroy (dialog->tag_info_table);
1964
1965 g_cancellable_cancel (dialog->metadata_cancellable);
1966 g_clear_object (&dialog->metadata_cancellable);
1967
1968 G_OBJECT_CLASS (nautilus_batch_rename_dialog_parent_class)->finalize (object);
1969 }
1970
1971 static void
nautilus_batch_rename_dialog_class_init(NautilusBatchRenameDialogClass * klass)1972 nautilus_batch_rename_dialog_class_init (NautilusBatchRenameDialogClass *klass)
1973 {
1974 GtkWidgetClass *widget_class;
1975 GObjectClass *oclass;
1976
1977 widget_class = GTK_WIDGET_CLASS (klass);
1978 oclass = G_OBJECT_CLASS (klass);
1979
1980 oclass->finalize = nautilus_batch_rename_dialog_finalize;
1981
1982 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-batch-rename-dialog.ui");
1983
1984 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, grid);
1985 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, cancel_button);
1986 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, original_name_listbox);
1987 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, arrow_listbox);
1988 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, result_listbox);
1989 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, name_entry);
1990 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, rename_button);
1991 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, find_entry);
1992 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_entry);
1993 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, mode_stack);
1994 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_mode_button);
1995 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, format_mode_button);
1996 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_button);
1997 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_popover);
1998 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_label);
1999 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, scrolled_window);
2000 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_popover);
2001 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_button);
2002 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_revealer);
2003 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_box);
2004 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_label);
2005 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_up);
2006 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_down);
2007 gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_label);
2008
2009 gtk_widget_class_bind_template_callback (widget_class, file_names_widget_on_activate);
2010 gtk_widget_class_bind_template_callback (widget_class, file_names_widget_entry_on_changed);
2011 gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_mode_changed);
2012 gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
2013 gtk_widget_class_bind_template_callback (widget_class, add_popover_closed);
2014 gtk_widget_class_bind_template_callback (widget_class, numbering_order_button_clicked);
2015 gtk_widget_class_bind_template_callback (widget_class, numbering_order_popover_closed);
2016 gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_up);
2017 gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_down);
2018 gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_on_response);
2019 gtk_widget_class_bind_template_callback (widget_class, on_insert_text);
2020 gtk_widget_class_bind_template_callback (widget_class, on_delete_text);
2021 }
2022
2023 GtkWidget *
nautilus_batch_rename_dialog_new(GList * selection,NautilusDirectory * directory,NautilusWindow * window)2024 nautilus_batch_rename_dialog_new (GList *selection,
2025 NautilusDirectory *directory,
2026 NautilusWindow *window)
2027 {
2028 NautilusBatchRenameDialog *dialog;
2029 GString *dialog_title;
2030 GList *l;
2031 gboolean all_targets_are_folders;
2032 gboolean all_targets_are_regular_files;
2033
2034 dialog = g_object_new (NAUTILUS_TYPE_BATCH_RENAME_DIALOG, "use-header-bar", TRUE, NULL);
2035
2036 dialog->selection = nautilus_file_list_copy (selection);
2037 dialog->directory = nautilus_directory_ref (directory);
2038 dialog->window = window;
2039
2040 gtk_window_set_transient_for (GTK_WINDOW (dialog),
2041 GTK_WINDOW (window));
2042
2043 all_targets_are_folders = TRUE;
2044 for (l = selection; l != NULL; l = l->next)
2045 {
2046 if (!nautilus_file_is_directory (NAUTILUS_FILE (l->data)))
2047 {
2048 all_targets_are_folders = FALSE;
2049 break;
2050 }
2051 }
2052
2053 all_targets_are_regular_files = TRUE;
2054 for (l = selection; l != NULL; l = l->next)
2055 {
2056 if (!nautilus_file_is_regular_file (NAUTILUS_FILE (l->data)))
2057 {
2058 all_targets_are_regular_files = FALSE;
2059 break;
2060 }
2061 }
2062
2063 dialog_title = g_string_new ("");
2064 if (all_targets_are_folders)
2065 {
2066 g_string_append_printf (dialog_title,
2067 ngettext ("Rename %d Folder",
2068 "Rename %d Folders",
2069 g_list_length (selection)),
2070 g_list_length (selection));
2071 }
2072 else if (all_targets_are_regular_files)
2073 {
2074 g_string_append_printf (dialog_title,
2075 ngettext ("Rename %d File",
2076 "Rename %d Files",
2077 g_list_length (selection)),
2078 g_list_length (selection));
2079 }
2080 else
2081 {
2082 g_string_append_printf (dialog_title,
2083 /* To translators: %d is the total number of files and folders.
2084 * Singular case of the string is never used */
2085 ngettext ("Rename %d File and Folder",
2086 "Rename %d Files and Folders",
2087 g_list_length (selection)),
2088 g_list_length (selection));
2089 }
2090
2091 gtk_window_set_title (GTK_WINDOW (dialog), dialog_title->str);
2092
2093 dialog->distinct_parent_directories = batch_rename_files_get_distinct_parents (selection);
2094
2095 add_tag (dialog, metadata_tags_constants[ORIGINAL_FILE_NAME]);
2096
2097 nautilus_batch_rename_dialog_initialize_actions (dialog);
2098
2099 update_display_text (dialog);
2100
2101 fill_display_listbox (dialog);
2102
2103 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL);
2104
2105 g_string_free (dialog_title, TRUE);
2106
2107 return GTK_WIDGET (dialog);
2108 }
2109
2110 static void
nautilus_batch_rename_dialog_init(NautilusBatchRenameDialog * self)2111 nautilus_batch_rename_dialog_init (NautilusBatchRenameDialog *self)
2112 {
2113 TagData *tag_data;
2114 guint i;
2115 g_autoptr (GtkBuilder) builder = NULL;
2116
2117 gtk_widget_init_template (GTK_WIDGET (self));
2118
2119 gtk_list_box_set_header_func (GTK_LIST_BOX (self->original_name_listbox),
2120 (GtkListBoxUpdateHeaderFunc) listbox_header_func,
2121 self,
2122 NULL);
2123 gtk_list_box_set_header_func (GTK_LIST_BOX (self->arrow_listbox),
2124 (GtkListBoxUpdateHeaderFunc) listbox_header_func,
2125 self,
2126 NULL);
2127 gtk_list_box_set_header_func (GTK_LIST_BOX (self->result_listbox),
2128 (GtkListBoxUpdateHeaderFunc) listbox_header_func,
2129 self,
2130 NULL);
2131
2132
2133 self->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT;
2134
2135 builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-batch-rename-dialog-menu.ui");
2136 self->numbering_order_menu = g_object_ref_sink (G_MENU (gtk_builder_get_object (builder, "numbering_order_menu")));
2137 self->add_tag_menu = g_object_ref_sink (G_MENU (gtk_builder_get_object (builder, "add_tag_menu")));
2138
2139 gtk_popover_bind_model (GTK_POPOVER (self->numbering_order_popover),
2140 G_MENU_MODEL (self->numbering_order_menu),
2141 NULL);
2142 gtk_popover_bind_model (GTK_POPOVER (self->add_popover),
2143 G_MENU_MODEL (self->add_tag_menu),
2144 NULL);
2145
2146 gtk_label_set_ellipsize (GTK_LABEL (self->conflict_label), PANGO_ELLIPSIZE_END);
2147 gtk_label_set_max_width_chars (GTK_LABEL (self->conflict_label), 1);
2148
2149 self->duplicates = NULL;
2150 self->distinct_parent_directories = NULL;
2151 self->directories_pending_conflict_check = NULL;
2152 self->new_names = NULL;
2153 self->rename_clicked = FALSE;
2154
2155 self->tag_info_table = g_hash_table_new_full (g_str_hash,
2156 g_str_equal,
2157 (GDestroyNotify) g_free,
2158 (GDestroyNotify) g_free);
2159
2160 for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
2161 {
2162 g_autofree gchar *tag_text_representation = NULL;
2163
2164 tag_text_representation = batch_rename_get_tag_text_representation (numbering_tags_constants[i]);
2165 tag_data = g_new (TagData, 1);
2166 tag_data->available = TRUE;
2167 tag_data->set = FALSE;
2168 tag_data->position = -1;
2169 tag_data->tag_constants = numbering_tags_constants[i];
2170 g_hash_table_insert (self->tag_info_table, g_strdup (tag_text_representation), tag_data);
2171 }
2172
2173 for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
2174 {
2175 g_autofree gchar *tag_text_representation = NULL;
2176
2177 /* Only the original name is available and set at the start */
2178 tag_text_representation = batch_rename_get_tag_text_representation (metadata_tags_constants[i]);
2179
2180 tag_data = g_new (TagData, 1);
2181 tag_data->available = FALSE;
2182 tag_data->set = FALSE;
2183 tag_data->position = -1;
2184 tag_data->tag_constants = metadata_tags_constants[i];
2185 g_hash_table_insert (self->tag_info_table, g_strdup (tag_text_representation), tag_data);
2186 }
2187
2188 self->row_height = -1;
2189
2190 g_signal_connect (self->original_name_listbox, "row-selected", G_CALLBACK (row_selected), self);
2191 g_signal_connect (self->arrow_listbox, "row-selected", G_CALLBACK (row_selected), self);
2192 g_signal_connect (self->result_listbox, "row-selected", G_CALLBACK (row_selected), self);
2193
2194 self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
2195
2196 g_signal_connect (self->original_name_listbox,
2197 "event",
2198 G_CALLBACK (on_event),
2199 self);
2200 g_signal_connect (self->result_listbox,
2201 "event",
2202 G_CALLBACK (on_event),
2203 self);
2204 g_signal_connect (self->arrow_listbox,
2205 "event",
2206 G_CALLBACK (on_event),
2207 self);
2208
2209 self->metadata_cancellable = g_cancellable_new ();
2210 }
2211