diff options
author | Alexandru Pandelea <alexandru.pandelea@gmail.com> | 2016-08-27 19:34:08 +0300 |
---|---|---|
committer | Alexandru Pandelea <alexandru.pandelea@gmail.com> | 2016-08-29 13:02:50 +0300 |
commit | be12a7510090b2ec38229b6e86bc601800d2056b (patch) | |
tree | cac557d7d2ca16476d8d803eb80e5c1beba1f54b /src/nautilus-batch-rename-dialog.c | |
parent | d2961830106abe6c6576207f90e2d968b554a115 (diff) | |
download | nautilus-be12a7510090b2ec38229b6e86bc601800d2056b.tar.gz |
Implement batch renaming
Renaming multiple files at once has been a missing feature in Nautilus
for a long time. This patch implements that feature in the following way:
This operation is launched in the same way as the rename one, when the
selection has more than one file.
When the batch renaming is launched, a dialog is shown, offering two
modes.
In the first mode, the user can use metadata (if available), numbering and
original file name tags to create the new names. Between the tags, there
also can be written normal text, which will be added in the new names. If
numbering is used, the order of the files can be modified by using several
criteria.
In the second mode, the user can replace an existing part of the name.
https://bugzilla.gnome.org/show_bug.cgi?id=768311
Diffstat (limited to 'src/nautilus-batch-rename-dialog.c')
-rw-r--r-- | src/nautilus-batch-rename-dialog.c | 3027 |
1 files changed, 3027 insertions, 0 deletions
diff --git a/src/nautilus-batch-rename-dialog.c b/src/nautilus-batch-rename-dialog.c new file mode 100644 index 000000000..eb76a7387 --- /dev/null +++ b/src/nautilus-batch-rename-dialog.c @@ -0,0 +1,3027 @@ +/* nautilus-batch-rename-dialog.c + * + * Copyright (C) 2016 Alexandru Pandelea <alexandru.pandelea@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "nautilus-batch-rename-dialog.h" +#include "nautilus-file.h" +#include "nautilus-error-reporting.h" +#include "nautilus-batch-rename-utilities.h" + +#include <glib/gprintf.h> +#include <glib.h> +#include <string.h> +#include <glib/gi18n.h> + +struct _NautilusBatchRenameDialog +{ + GtkDialog parent; + + GtkWidget *grid; + NautilusWindow *window; + + GtkWidget *cancel_button; + GtkWidget *original_name_listbox; + GtkWidget *arrow_listbox; + GtkWidget *result_listbox; + GtkWidget *name_entry; + GtkWidget *rename_button; + GtkWidget *find_entry; + GtkWidget *mode_stack; + GtkWidget *replace_entry; + GtkWidget *format_mode_button; + GtkWidget *replace_mode_button; + GtkWidget *add_button; + GtkWidget *add_popover; + GtkWidget *numbering_order_label; + GtkWidget *numbering_label; + GtkWidget *scrolled_window; + GtkWidget *numbering_order_popover; + GtkWidget *numbering_order_button; + GtkWidget *conflict_box; + GtkWidget *conflict_label; + GtkWidget *conflict_down; + GtkWidget *conflict_up; + + GList *original_name_listbox_rows; + GList *arrow_listbox_rows; + GList *result_listbox_rows; + GList *listbox_labels_new; + GList *listbox_labels_old; + GList *listbox_icons; + GtkSizeGroup *size_group; + + GList *selection; + GList *new_names; + NautilusBatchRenameDialogMode mode; + NautilusDirectory *directory; + + GActionGroup *action_group; + + GMenu *numbering_order_menu; + GMenu *add_tag_menu; + + GHashTable *create_date; + GList *selection_metadata; + + /* check if all files in selection have the same parent */ + gboolean same_parent; + /* the index of the currently selected conflict */ + gint selected_conflict; + /* total conflicts number */ + gint conflicts_number; + + gint checked_parents; + GList *duplicates; + GList *distinct_parents; + GTask *conflicts_task; + GCancellable *conflict_cancellable; + gboolean checking_conflicts; + + /* this hash table has information about the status + * of all tags: availability, if it's currently used + * and position */ + GHashTable *tag_info_table; + + GtkWidget *preselected_row1; + GtkWidget *preselected_row2; + + gint row_height; + gboolean rename_clicked; + + /* the numbers of characters from the name entry */ + gint name_entry_characters; + gboolean tags_deleted; + gint cursor_position; + gboolean use_manual_cursor_position; +}; + +typedef struct +{ + gboolean available; + gboolean set; + gint position; + gint original_position; + gint new_position; + /* if the tag was just added, then we shouldn't update it's position */ + gboolean just_added; +} TagData; + +static void update_display_text (NautilusBatchRenameDialog *dialog); + +G_DEFINE_TYPE (NautilusBatchRenameDialog, nautilus_batch_rename_dialog, GTK_TYPE_DIALOG); + +static void +add_numbering_order (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + const gchar *target_name; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + target_name = g_variant_get_string (value, NULL); + + if (g_strcmp0 (target_name, "name-ascending") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("Original name (Ascending)")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + ORIGINAL_ASCENDING, + NULL); + } + + if (g_strcmp0 (target_name, "name-descending") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("Original name (Descending)")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + ORIGINAL_DESCENDING, + NULL); + } + + if (g_strcmp0 (target_name, "first-modified") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("First Modified")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + FIRST_MODIFIED, + NULL); + } + + if (g_strcmp0 (target_name, "last-modified") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("Last Modified")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + LAST_MODIFIED, + NULL); + } + + if (g_strcmp0 (target_name, "first-created") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("First Created")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + FIRST_CREATED, + dialog->create_date); + } + + if (g_strcmp0 (target_name, "last-created") == 0) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label), + _("Last Created")); + dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection, + LAST_CREATED, + dialog->create_date); + } + + g_simple_action_set_state (action, value); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE); + + update_display_text (dialog); +} + +static void +add_original_file_name_tag (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + gint cursor_position; + TagData *tag_data; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL); + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME); + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + ORIGINAL_FILE_NAME, + g_utf8_strlen (ORIGINAL_FILE_NAME, -1), + &cursor_position); + + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry)); + + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); +} + +static void +disable_action (NautilusBatchRenameDialog *dialog, + gchar *action_name) +{ + GAction *action; + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + action_name); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); +} + +static void +add_metadata_tag (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + const gchar *action_name; + gint cursor_position; + TagData *tag_data; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + action_name = g_action_get_name (G_ACTION (action)); + g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL); + + if (g_strrstr (action_name, "creation-date")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + CREATION_DATE, + g_utf8_strlen (CREATION_DATE, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-creation-date-tag"); + } + + if (g_strrstr (action_name, "equipment")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + CAMERA_MODEL, + g_utf8_strlen (CAMERA_MODEL, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-equipment-tag"); + } + + if (g_strrstr (action_name, "season")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + SEASON_NUMBER, + g_utf8_strlen (SEASON_NUMBER, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-season-tag"); + } + + if (g_strrstr (action_name, "episode")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + EPISODE_NUMBER, + g_utf8_strlen (EPISODE_NUMBER, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-episode-tag"); + } + + if (g_strrstr (action_name, "track")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + TRACK_NUMBER, + g_utf8_strlen (TRACK_NUMBER, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-track-number-tag"); + } + + if (g_strrstr (action_name, "artist")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + ARTIST_NAME, + g_utf8_strlen (ARTIST_NAME, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-artist-name-tag"); + } + + if (g_strrstr (action_name, "title")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + TITLE, + g_utf8_strlen (TITLE, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-title-tag"); + } + + if (g_strrstr (action_name, "album")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + ALBUM_NAME, + g_utf8_strlen (ALBUM_NAME, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + disable_action (dialog, "add-album-name-tag"); + } + + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry)); +} + +static void +add_numbering_tag (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + const gchar *action_name; + gint cursor_position; + GAction *add_numbering_action; + TagData *tag_data; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + action_name = g_action_get_name (G_ACTION (action)); + g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL); + + if (g_strrstr (action_name, "zero")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + NUMBERING, + g_utf8_strlen (NUMBERING, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + } + + if (g_strrstr (action_name, "one")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + NUMBERING0, + g_utf8_strlen (NUMBERING0, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + } + + if (g_strrstr (action_name, "two")) { + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + tag_data->set = TRUE; + tag_data->just_added = TRUE; + tag_data->position = cursor_position; + + gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry), + NUMBERING00, + g_utf8_strlen (NUMBERING00, -1), + &cursor_position); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position); + } + + add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-zero"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE); + add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-one"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE); + + add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-two"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE); + + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry)); +} + +const GActionEntry dialog_entries[] = { + { "numbering-order-changed", NULL, "s", "'name-ascending'", add_numbering_order }, + { "add-original-file-name-tag", add_original_file_name_tag }, + { "add-numbering-tag-zero", add_numbering_tag }, + { "add-numbering-tag-one", add_numbering_tag }, + { "add-numbering-tag-two", add_numbering_tag }, + { "add-creation-date-tag", add_metadata_tag }, + { "add-equipment-tag", add_metadata_tag }, + { "add-season-tag", add_metadata_tag }, + { "add-episode-tag", add_metadata_tag }, + { "add-video-album-tag", add_metadata_tag }, + { "add-track-number-tag", add_metadata_tag }, + { "add-artist-name-tag", add_metadata_tag }, + { "add-title-tag", add_metadata_tag }, + { "add-album-name-tag", add_metadata_tag }, + +}; + +static void +row_selected (GtkListBox *box, + GtkListBoxRow *listbox_row, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + GtkListBoxRow *row; + gint index; + + if (!GTK_IS_LIST_BOX_ROW (listbox_row)) + return; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + index = gtk_list_box_row_get_index (listbox_row); + + if (GTK_WIDGET (box) == dialog->original_name_listbox) { + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox), + row); + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox), + row); + } + + if (GTK_WIDGET (box) == dialog->arrow_listbox) { + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox), + row); + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox), + row); + } + + if (GTK_WIDGET (box) == dialog->result_listbox) { + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox), + row); + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox), + row); + } +} + +static gint +compare_tag_position (gconstpointer a, + gconstpointer b) +{ + int *number1 = (int*) a; + int *number2 = (int*) b; + + return *number1 - *number2; +} + +/* This function splits the entry text into a list of regular text and tags. + * For instance, "[1, 2, 3]Paris[Creation date]" would result in: + * "[1, 2, 3]", "Paris", "[Creation date]" */ +static GList* +split_entry_text (NautilusBatchRenameDialog *dialog, + gchar *entry_text) +{ + GString *normal_text; + GString *tag; + GArray *tag_positions; + gint tags; + gint i; + gchar *substring; + gint tag_end_position; + GList *result = NULL; + TagData *tag_data; + + tags = 0; + tag_end_position = 0; + tag_positions = g_array_new (FALSE, FALSE, sizeof (gint)); + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + if (tag_data->set) { + g_array_append_val (tag_positions, tag_data->position); + tags++; + } + + g_array_sort (tag_positions, compare_tag_position); + + for (i = 0; i < tags; i++) { + tag = g_string_new (""); + + substring = g_utf8_substring (entry_text, tag_end_position, + g_array_index (tag_positions, gint, i)); + normal_text = g_string_new (substring); + g_free (substring); + + if (g_strcmp0 (normal_text->str, "")) + result = g_list_prepend (result, normal_text); + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (ORIGINAL_FILE_NAME, -1); + tag = g_string_append (tag, ORIGINAL_FILE_NAME); + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (NUMBERING, -1); + tag = g_string_append (tag, NUMBERING); + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (NUMBERING0, -1); + tag = g_string_append (tag, NUMBERING0); + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (NUMBERING00, -1); + tag = g_string_append (tag, NUMBERING00); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (CREATION_DATE, -1); + tag = g_string_append (tag, CREATION_DATE); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (CAMERA_MODEL, -1); + tag = g_string_append (tag, CAMERA_MODEL); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (SEASON_NUMBER, -1); + tag = g_string_append (tag, SEASON_NUMBER); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (EPISODE_NUMBER, -1); + tag = g_string_append (tag, EPISODE_NUMBER); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (TRACK_NUMBER, -1); + tag = g_string_append (tag, TRACK_NUMBER); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (ARTIST_NAME, -1); + tag = g_string_append (tag, ARTIST_NAME); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (TITLE, -1); + tag = g_string_append (tag, TITLE); + } + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) { + tag_end_position = g_array_index (tag_positions, gint, i) + + g_utf8_strlen (ALBUM_NAME, -1); + tag = g_string_append (tag, ALBUM_NAME); + } + + result = g_list_prepend (result, tag); + } + normal_text = g_string_new (g_utf8_offset_to_pointer (entry_text, tag_end_position)); + + if (g_strcmp0 (normal_text->str, "") != 0) + result = g_list_prepend (result, normal_text); + + result = g_list_reverse (result); + + g_array_free (tag_positions, TRUE); + return result; +} + +static GList* +batch_rename_dialog_get_new_names (NautilusBatchRenameDialog *dialog) +{ + GList *result = NULL; + GList *selection; + GList *text_chunks; + g_autofree gchar *entry_text; + g_autofree gchar *replace_text; + + selection = dialog->selection; + text_chunks = NULL; + + if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE) + entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->find_entry))); + else + entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry))); + + replace_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry))); + + if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE) { + result = batch_rename_dialog_get_new_names_list (dialog->mode, + selection, + NULL, + NULL, + entry_text, + replace_text); + } else { + text_chunks = split_entry_text (dialog, entry_text); + + result = batch_rename_dialog_get_new_names_list (dialog->mode, + selection, + text_chunks, + dialog->selection_metadata, + entry_text, + replace_text); + g_list_free_full (text_chunks, string_free); + } + + result = g_list_reverse (result); + + return result; +} + +static void +begin_batch_rename (NautilusBatchRenameDialog *dialog, + GList *new_names) +{ + GList *new_names_list; + GList *files; + GList *files2; + GList *new_names_list2; + gchar *file_name; + gchar *old_file_name; + GString *new_file_name; + GString *new_name; + NautilusFile *file; + + /* in the following case: + * file1 -> file2 + * file2 -> file3 + * file2 must be renamed first, so because of that, the list has to be reordered */ + for (new_names_list = new_names, files = dialog->selection; + new_names_list != NULL && files != NULL; + new_names_list = new_names_list->next, files = files->next) { + old_file_name = nautilus_file_get_name (NAUTILUS_FILE (files->data)); + new_file_name = new_names_list->data; + + for (files2 = dialog->selection, new_names_list2 = new_names; + files2 != NULL && new_names_list2 != NULL; + files2 = files2->next, new_names_list2 = new_names_list2->next) { + file_name = nautilus_file_get_name (NAUTILUS_FILE (files2->data)); + if (files2 != files && g_strcmp0 (file_name, new_file_name->str) == 0) { + file = NAUTILUS_FILE (files2->data); + new_name = new_names_list2->data; + + dialog->selection = g_list_remove_link (dialog->selection, files2); + new_names = g_list_remove_link (new_names, new_names_list2); + + dialog->selection = g_list_prepend (dialog->selection, file); + new_names = g_list_prepend (new_names, new_name); + + g_free (file_name); + + break; + } + + g_free (file_name); + } + + g_free (old_file_name); + } + + /* do the actual rename here */ + nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL); + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)), NULL); +} + +static void +listbox_header_func (GtkListBoxRow *row, + GtkListBoxRow *before, + NautilusBatchRenameDialog *dialog) +{ + gboolean show_separator; + + show_separator = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), + "show-separator")); + + if (show_separator) + { + GtkWidget *separator; + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_widget_show (separator); + + gtk_list_box_row_set_header (row, separator); + } +} + +/* This is manually done instead of using GtkSizeGroup because of the computational + * complexity of the later.*/ +static void +update_rows_height (NautilusBatchRenameDialog *dialog) +{ + GList *l; + gint current_row_natural_height; + gint maximum_height; + + maximum_height = -1; + + /* check if maximum height has changed */ + for (l = dialog->listbox_labels_new; l != NULL; l = l->next) { + gtk_widget_get_preferred_height (GTK_WIDGET (l->data), + NULL, + ¤t_row_natural_height); + + if (current_row_natural_height > maximum_height) { + maximum_height = current_row_natural_height; + } + } + + for (l = dialog->listbox_labels_old; l != NULL; l = l->next) { + gtk_widget_get_preferred_height (GTK_WIDGET (l->data), + NULL, + ¤t_row_natural_height); + + if (current_row_natural_height > maximum_height) { + maximum_height = current_row_natural_height; + } + } + + for (l = dialog->listbox_icons; l != NULL; l = l->next) { + gtk_widget_get_preferred_height (GTK_WIDGET (l->data), + NULL, + ¤t_row_natural_height); + + if (current_row_natural_height > maximum_height) { + maximum_height = current_row_natural_height; + } + } + + if (maximum_height != dialog->row_height) { + dialog->row_height = maximum_height; + + for (l = dialog->listbox_icons; l != NULL; l = l->next) { + g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL); + } + + for (l = dialog->listbox_labels_new; l != NULL; l = l->next) { + g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL); + } + + for (l = dialog->listbox_labels_old; l != NULL; l = l->next) { + g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL); + } + } +} + +static GtkWidget* +create_original_name_row_for_label (NautilusBatchRenameDialog *dialog, + const gchar *old_text, + gboolean show_separator) +{ + GtkWidget *row; + GtkWidget *label_old; + + row = gtk_list_box_row_new (); + + g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator)); + + label_old = gtk_label_new (old_text); + gtk_label_set_xalign (GTK_LABEL (label_old), 0.0); + gtk_widget_set_hexpand (label_old, TRUE); + gtk_widget_set_margin_start (label_old, 6); + + gtk_label_set_ellipsize (GTK_LABEL (label_old), PANGO_ELLIPSIZE_END); + + dialog->listbox_labels_old = g_list_prepend (dialog->listbox_labels_old, label_old); + + gtk_container_add (GTK_CONTAINER (row), label_old); + gtk_widget_show_all (row); + + return row; +} + +static GtkWidget* +create_result_row_for_label (NautilusBatchRenameDialog *dialog, + const gchar *new_text, + gboolean show_separator) +{ + GtkWidget *row; + GtkWidget *label_new; + + row = gtk_list_box_row_new (); + + g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator)); + + label_new = gtk_label_new (new_text); + gtk_label_set_xalign (GTK_LABEL (label_new), 0.0); + gtk_widget_set_hexpand (label_new, TRUE); + gtk_widget_set_margin_start (label_new, 6); + + gtk_label_set_ellipsize (GTK_LABEL (label_new), PANGO_ELLIPSIZE_END); + + dialog->listbox_labels_new = g_list_prepend (dialog->listbox_labels_new, label_new); + + gtk_container_add (GTK_CONTAINER (row), label_new); + gtk_widget_show_all (row); + + return row; +} + +static GtkWidget* +create_arrow_row_for_label (NautilusBatchRenameDialog *dialog, + gboolean show_separator) +{ + GtkWidget *row; + GtkWidget *icon; + + row = gtk_list_box_row_new (); + + g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator)); + + icon = gtk_label_new ("→"); + gtk_label_set_xalign (GTK_LABEL (icon), 1.0); + gtk_widget_set_hexpand (icon, FALSE); + gtk_widget_set_margin_start (icon, 6); + + dialog->listbox_icons = g_list_prepend (dialog->listbox_icons, icon); + + gtk_container_add (GTK_CONTAINER (row), icon); + gtk_widget_show_all (row); + + return row; +} + +static void +prepare_batch_rename (NautilusBatchRenameDialog *dialog) +{ + GdkCursor *cursor; + GdkDisplay *display; + + /* wait for checking conflicts to finish, to be sure that + * the rename can actually take place */ + if (dialog->checking_conflicts) { + dialog->rename_clicked = TRUE; + return; + } + + if (!gtk_widget_is_sensitive (dialog->rename_button)) + return; + + display = gtk_widget_get_display (GTK_WIDGET (dialog->window)); + cursor = gdk_cursor_new_from_name (display, "progress"); + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)), + cursor); + g_object_unref (cursor); + + display = gtk_widget_get_display (GTK_WIDGET (dialog)); + cursor = gdk_cursor_new_from_name (display, "progress"); + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog)), + cursor); + g_object_unref (cursor); + + gtk_widget_hide (GTK_WIDGET (dialog)); + begin_batch_rename (dialog, dialog->new_names); + + if (dialog->conflict_cancellable) + g_cancellable_cancel (dialog->conflict_cancellable); + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +batch_rename_dialog_on_response (NautilusBatchRenameDialog *dialog, + gint response_id, + gpointer user_data) +{ + if (response_id == GTK_RESPONSE_OK) { + prepare_batch_rename (dialog); + } else { + if (dialog->conflict_cancellable) + g_cancellable_cancel (dialog->conflict_cancellable); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + } +} + +static void +fill_display_listbox (NautilusBatchRenameDialog *dialog) +{ + GtkWidget *row; + GList *l1; + GList *l2; + NautilusFile *file; + GString *new_name; + gchar *name; + + dialog->original_name_listbox_rows = NULL; + dialog->arrow_listbox_rows = NULL; + dialog->result_listbox_rows = NULL; + + gtk_size_group_add_widget (dialog->size_group, dialog->result_listbox); + gtk_size_group_add_widget (dialog->size_group, dialog->original_name_listbox); + + for (l1 = dialog->new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) { + file = NAUTILUS_FILE (l2->data); + new_name = l1->data; + + name = nautilus_file_get_name (file); + row = create_original_name_row_for_label (dialog, name, TRUE); + gtk_container_add (GTK_CONTAINER (dialog->original_name_listbox), row); + dialog->original_name_listbox_rows = g_list_prepend (dialog->original_name_listbox_rows, + row); + + row = create_arrow_row_for_label (dialog, TRUE); + gtk_container_add (GTK_CONTAINER (dialog->arrow_listbox), row); + dialog->arrow_listbox_rows = g_list_prepend (dialog->arrow_listbox_rows, + row); + + row = create_result_row_for_label (dialog, new_name->str, TRUE); + gtk_container_add (GTK_CONTAINER (dialog->result_listbox), row); + dialog->result_listbox_rows = g_list_prepend (dialog->result_listbox_rows, + row); + + g_free (name); + } + + dialog->original_name_listbox_rows = g_list_reverse (dialog->original_name_listbox_rows); + dialog->arrow_listbox_rows = g_list_reverse (dialog->arrow_listbox_rows); + dialog->result_listbox_rows = g_list_reverse (dialog->result_listbox_rows); + dialog->listbox_labels_old = g_list_reverse (dialog->listbox_labels_old); + dialog->listbox_labels_new = g_list_reverse (dialog->listbox_labels_new); + dialog->listbox_icons = g_list_reverse (dialog->listbox_icons); +} + +static void +select_nth_conflict (NautilusBatchRenameDialog *dialog) +{ + GList *l; + GString *conflict_file_name; + GString *display_text; + GString *new_name; + gint nth_conflict_index; + gint nth_conflict; + gint name_occurences; + GtkAdjustment *adjustment; + GtkAllocation allocation; + ConflictData *conflict_data; + + nth_conflict = dialog->selected_conflict; + l = g_list_nth (dialog->duplicates, nth_conflict); + conflict_data = l->data; + + /* the conflict that has to be selected */ + conflict_file_name = g_string_new (conflict_data->name); + display_text = g_string_new (""); + + nth_conflict_index = conflict_data->index; + + l = g_list_nth (dialog->original_name_listbox_rows, nth_conflict_index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox), + l->data); + + l = g_list_nth (dialog->arrow_listbox_rows, nth_conflict_index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox), + l->data); + + l = g_list_nth (dialog->result_listbox_rows, nth_conflict_index); + gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox), + l->data); + + /* scroll to the selected row */ + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (dialog->scrolled_window)); + gtk_widget_get_allocation (GTK_WIDGET (l->data), &allocation); + gtk_adjustment_set_value (adjustment, (allocation.height + 1) * nth_conflict_index); + + name_occurences = 0; + for (l = dialog->new_names; l != NULL; l = l->next) { + new_name = l->data; + if (g_string_equal (new_name, conflict_file_name)) + name_occurences++; + } + if (name_occurences > 1) + g_string_append_printf (display_text, + _("\"%s\" would not be a unique new name"), + conflict_file_name->str); + else + g_string_append_printf (display_text, + _("\"%s\" would conflict with an existing file."), + conflict_file_name->str); + + gtk_label_set_label (GTK_LABEL (dialog->conflict_label), + display_text->str); + + g_string_free (conflict_file_name, TRUE); +} + +static void +select_next_conflict_down (NautilusBatchRenameDialog *dialog) +{ + dialog->selected_conflict++; + + if (dialog->selected_conflict == 1) + gtk_widget_set_sensitive (dialog->conflict_up, TRUE); + + if (dialog->selected_conflict == dialog->conflicts_number - 1) + gtk_widget_set_sensitive (dialog->conflict_down, FALSE); + + select_nth_conflict (dialog); +} + +static void +select_next_conflict_up (NautilusBatchRenameDialog *dialog) +{ + dialog->selected_conflict--; + + if (dialog->selected_conflict == 0) + gtk_widget_set_sensitive (dialog->conflict_up, FALSE); + + if (dialog->selected_conflict == dialog->conflicts_number - 2) + gtk_widget_set_sensitive (dialog->conflict_down, TRUE); + + select_nth_conflict (dialog); +} + +static void +update_conflict_row_background (NautilusBatchRenameDialog *dialog) +{ + GList *l1; + GList *l2; + GList *l3; + GList *duplicates; + gint index; + GtkStyleContext *context; + ConflictData *conflict_data; + + index = 0; + + duplicates = dialog->duplicates; + + for (l1 = dialog->original_name_listbox_rows, + l2 = dialog->arrow_listbox_rows, + l3 = dialog->result_listbox_rows; + l1 != NULL && l2 != NULL && l3 != NULL; + l1 = l1->next, l2 = l2->next, l3 = l3->next) { + context = gtk_widget_get_style_context (GTK_WIDGET (l1->data)); + + if (gtk_style_context_has_class (context, "conflict-row")) { + gtk_style_context_remove_class (context, "conflict-row"); + + context = gtk_widget_get_style_context (GTK_WIDGET (l2->data)); + gtk_style_context_remove_class (context, "conflict-row"); + + context = gtk_widget_get_style_context (GTK_WIDGET (l3->data)); + gtk_style_context_remove_class (context, "conflict-row"); + + } + + if (duplicates != NULL) { + conflict_data = duplicates->data; + if (conflict_data->index == index) { + context = gtk_widget_get_style_context (GTK_WIDGET (l1->data)); + gtk_style_context_add_class (context, "conflict-row"); + + context = gtk_widget_get_style_context (GTK_WIDGET (l2->data)); + gtk_style_context_add_class (context, "conflict-row"); + + context = gtk_widget_get_style_context (GTK_WIDGET (l3->data)); + gtk_style_context_add_class (context, "conflict-row"); + + duplicates = duplicates->next; + } + } + index++; + } +} + +static void +update_listbox (NautilusBatchRenameDialog *dialog) +{ + GList *l1; + GList *l2; + NautilusFile *file; + gchar *old_name; + GtkLabel *label; + GString *new_name; + + for (l1 = dialog->new_names, l2 = dialog->listbox_labels_new; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) { + label = GTK_LABEL (l2->data); + new_name = l1->data; + + gtk_label_set_label (label, new_name->str); + } + + for (l1 = dialog->selection, l2 = dialog->listbox_labels_old; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) { + label = GTK_LABEL (l2->data); + file = NAUTILUS_FILE (l1->data); + + old_name = nautilus_file_get_name (file); + + if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) { + gtk_label_set_label (label, old_name); + } else { + new_name = batch_rename_replace_label_text (old_name, + gtk_entry_get_text (GTK_ENTRY (dialog->find_entry))); + gtk_label_set_markup (GTK_LABEL (label), new_name->str); + + g_string_free (new_name, TRUE); + } + + g_free (old_name); + } + + update_rows_height (dialog); + + /* check if there are name conflicts and display them if they exist */ + if (dialog->duplicates != NULL) { + update_conflict_row_background (dialog); + + gtk_widget_set_sensitive (dialog->rename_button, FALSE); + + gtk_widget_show (dialog->conflict_box); + + dialog->selected_conflict = 0; + dialog->conflicts_number = g_list_length (dialog->duplicates); + + select_nth_conflict (dialog); + + gtk_widget_set_sensitive (dialog->conflict_up, FALSE); + + if (g_list_length (dialog->duplicates) == 1) + gtk_widget_set_sensitive (dialog->conflict_down, FALSE); + else + gtk_widget_set_sensitive (dialog->conflict_down, TRUE); + } else { + gtk_widget_hide (dialog->conflict_box); + + /* re-enable the rename button if there are no more name conflicts */ + if (dialog->duplicates == NULL && !gtk_widget_is_sensitive (dialog->rename_button)) { + update_conflict_row_background (dialog); + gtk_widget_set_sensitive (dialog->rename_button, TRUE); + } + } + + /* if the rename button was clicked and there's no conflict, then start renaming */ + if (dialog->rename_clicked && dialog->duplicates == NULL) { + prepare_batch_rename (dialog); + } + + if (dialog->rename_clicked && dialog->duplicates != NULL) { + dialog->rename_clicked = FALSE; + } +} + + +void +check_conflict_for_files (NautilusBatchRenameDialog *dialog, + NautilusDirectory *directory, + GList *files) +{ + gchar *current_directory; + gchar *parent_uri; + gchar *name; + NautilusFile *file; + GString *new_name; + GString *file_name; + GList *l1, *l2; + GHashTable *directory_files_table; + GHashTable *new_names_table; + GHashTable *names_conflicts_table; + gboolean exists; + gboolean have_conflict; + gboolean tag_present; + gboolean same_parent_directory; + ConflictData *conflict_data; + + current_directory = nautilus_directory_get_uri (directory); + + directory_files_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + new_names_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + names_conflicts_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + + /* names_conflicts_table is used for knowing which names from the list are not unique, + * so that they can easily be reached when needed */ + for (l1 = dialog->new_names, l2 = dialog->selection; + l1 != NULL && l2 != NULL; + l1 = l1->next, l2 = l2->next) { + new_name = l1->data; + file = NAUTILUS_FILE (l2->data); + parent_uri = nautilus_file_get_parent_uri (file); + + tag_present = g_hash_table_lookup (new_names_table, new_name->str) != NULL; + same_parent_directory = g_strcmp0 (parent_uri, current_directory) == 0; + + if (same_parent_directory) { + if (!tag_present) { + g_hash_table_insert (new_names_table, + g_strdup (new_name->str), + nautilus_file_get_parent_uri (file)); + } else { + g_hash_table_insert (names_conflicts_table, + g_strdup (new_name->str), + nautilus_file_get_parent_uri (file)); + } + } + + g_free (parent_uri); + } + + for (l1 = files; l1 != NULL; l1 = l1->next) { + file = NAUTILUS_FILE (l1->data); + g_hash_table_insert (directory_files_table, + nautilus_file_get_name (file), + GINT_TO_POINTER (TRUE)); + } + + for (l1 = dialog->selection, l2 = dialog->new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) { + file = NAUTILUS_FILE (l1->data); + + name = nautilus_file_get_name (file); + file_name = g_string_new (name); + g_free (name); + + parent_uri = nautilus_file_get_parent_uri (file); + + new_name = l2->data; + + have_conflict = FALSE; + + /* check for duplicate only if the parent of the current file is + * the current directory and the name of the file has changed */ + if (g_strcmp0 (parent_uri, current_directory) == 0 && + !g_string_equal (new_name, file_name)) { + exists = GPOINTER_TO_INT (g_hash_table_lookup (directory_files_table, new_name->str)); + + if (exists == TRUE && + !file_name_conflicts_with_results (dialog->selection, dialog->new_names, new_name, parent_uri)) { + conflict_data = g_new (ConflictData, 1); + conflict_data->name = g_strdup (new_name->str); + conflict_data->index = g_list_index (dialog->selection, l1->data); + dialog->duplicates = g_list_prepend (dialog->duplicates, + conflict_data); + + have_conflict = TRUE; + } + } + if (!have_conflict) { + tag_present = g_hash_table_lookup (names_conflicts_table, new_name->str) != NULL; + same_parent_directory = g_strcmp0 (nautilus_file_get_parent_uri (file), current_directory) == 0; + + if (tag_present && same_parent_directory) { + conflict_data = g_new (ConflictData, 1); + conflict_data->name = g_strdup (new_name->str); + conflict_data->index = g_list_index (dialog->selection, l1->data); + dialog->duplicates = g_list_prepend (dialog->duplicates, + conflict_data); + + have_conflict = TRUE; + } + } + + g_string_free (file_name, TRUE); + g_free (parent_uri); + } + + /* check if this is the last call of the callback. Update + * the listbox with the conflicts if it is. */ + if (dialog->checked_parents == g_list_length (dialog->distinct_parents) - 1) { + dialog->duplicates = g_list_reverse (dialog->duplicates); + + dialog->checking_conflicts = FALSE; + + update_listbox (dialog); + } + + dialog->checked_parents++; + + g_free (current_directory); + g_hash_table_destroy (directory_files_table); + g_hash_table_destroy (new_names_table); + g_hash_table_destroy (names_conflicts_table); +} + +static void +file_names_list_has_duplicates_callback (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (object); + + if (g_cancellable_is_cancelled (dialog->conflict_cancellable)) + return; + + if (dialog->same_parent) + update_listbox (dialog); +} + +static void +on_call_when_ready (NautilusDirectory *directory, + GList *files, + gpointer callback_data) +{ + check_conflict_for_files (NAUTILUS_BATCH_RENAME_DIALOG (callback_data), + directory, + files); +} + +static void +file_names_list_has_duplicates_async_thread (GTask *task, + gpointer object, + gpointer task_data, + GCancellable *cancellable) +{ + NautilusBatchRenameDialog *dialog; + GList *new_names; + GList *directory_files; + GList *l1; + GList *l2; + NautilusFile *file; + GString *file_name; + GString *new_name; + NautilusDirectory *parent; + gboolean have_conflict; + gboolean hash_table_insertion; + gchar *name; + GHashTable *directory_names_table; + GHashTable *new_names_table; + GHashTable *names_conflicts_table; + gint exists; + ConflictData *conflict_data; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (object); + + dialog->duplicates = NULL; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + g_return_if_fail (g_list_length (dialog->new_names) == g_list_length (dialog->selection)); + + /* If the batch rename is launched in a search, then for each file we have to check for + * conflicts with each file in the file's parent directory */ + if (dialog->distinct_parents != NULL) { + for (l1 = dialog->distinct_parents; l1 != NULL; l1 = l1->next) { + if (g_cancellable_is_cancelled (cancellable)) + return; + + parent = nautilus_directory_get_by_uri (l1->data); + + nautilus_directory_call_when_ready (parent, + NAUTILUS_FILE_ATTRIBUTE_INFO, + TRUE, + on_call_when_ready, + dialog); + } + + g_task_return_pointer (task, object, NULL); + return; + } + + new_names = batch_rename_dialog_get_new_names (dialog); + + directory_names_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + new_names_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + names_conflicts_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + + directory_files = nautilus_directory_get_file_list (dialog->directory); + + for (l1 = new_names; l1 != NULL; l1 = l1->next) { + new_name = l1->data; + hash_table_insertion = g_hash_table_insert (new_names_table, + g_strdup (new_name->str), + GINT_TO_POINTER (TRUE)); + + if (!hash_table_insertion) { + g_hash_table_insert (names_conflicts_table, + g_strdup (new_name->str), + GINT_TO_POINTER (TRUE)); + } + } + + for (l1 = directory_files; l1 != NULL; l1 = l1->next) { + file = NAUTILUS_FILE (l1->data); + g_hash_table_insert (directory_names_table, + nautilus_file_get_name (file), + GINT_TO_POINTER (TRUE)); + } + + for (l1 = new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) { + if (g_cancellable_is_cancelled (cancellable)) { + g_list_free_full (dialog->duplicates, conflict_data_free); + break; + } + + file = NAUTILUS_FILE (l2->data); + new_name = l1->data; + + have_conflict = FALSE; + + name = nautilus_file_get_name (file); + file_name = g_string_new (name); + + g_free (name); + + /* check for duplicate only if the name has changed */ + if (!g_string_equal (new_name, file_name)) { + /* check with already existing files */ + exists = GPOINTER_TO_INT (g_hash_table_lookup (directory_names_table, new_name->str)); + + if (exists == TRUE && + !file_name_conflicts_with_results (dialog->selection, new_names, new_name, NULL)) { + conflict_data = g_new (ConflictData, 1); + conflict_data->name = g_strdup (new_name->str); + conflict_data->index = g_list_index (dialog->selection, l2->data); + dialog->duplicates = g_list_prepend (dialog->duplicates, + conflict_data); + have_conflict = TRUE; + } + + /* check with files that will result from the batch rename, unless + * this file already has a conflict */ + if (!have_conflict) { + exists = GPOINTER_TO_INT (g_hash_table_lookup (names_conflicts_table, new_name->str)); + + if (exists == TRUE) { + conflict_data = g_new (ConflictData, 1); + conflict_data->name = g_strdup (new_name->str); + conflict_data->index = g_list_index (dialog->selection, l2->data); + dialog->duplicates = g_list_prepend (dialog->duplicates, + conflict_data); + } + } + } + + g_string_free (file_name, TRUE); + } + + g_hash_table_destroy (directory_names_table); + g_hash_table_destroy (new_names_table); + g_hash_table_destroy (names_conflicts_table); + nautilus_file_list_free (directory_files); + g_list_free_full (new_names, string_free); + + dialog->duplicates = g_list_reverse (dialog->duplicates); + + dialog->checking_conflicts = FALSE; + + g_task_return_pointer (task, object, NULL); + +} + +static void +file_names_list_has_duplicates_async (NautilusBatchRenameDialog *dialog, + GAsyncReadyCallback callback, + gpointer user_data) +{ + if (dialog->checking_conflicts == TRUE) + g_cancellable_cancel (dialog->conflict_cancellable); + + dialog->conflict_cancellable = g_cancellable_new (); + + dialog->checking_conflicts = TRUE; + dialog->conflicts_task = g_task_new (dialog, dialog->conflict_cancellable, callback, user_data); + + g_task_run_in_thread (dialog->conflicts_task, file_names_list_has_duplicates_async_thread); + + g_object_unref (dialog->conflicts_task); +} + +static void +update_tags (NautilusBatchRenameDialog *dialog) +{ + TagData *tag_data; + const gchar *entry_text; + gint character_difference; + gint cursor_position; + + entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)); + + if (dialog->use_manual_cursor_position) { + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), + dialog->cursor_position); + } + + if (dialog->tags_deleted) { + dialog->tags_deleted = FALSE; + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), + g_utf8_strlen (entry_text, -1)); + } + + g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL); + + character_difference = g_utf8_strlen (entry_text, -1) - dialog->name_entry_characters; + dialog->name_entry_characters = g_utf8_strlen (entry_text, -1); + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + if (tag_data->just_added) { + tag_data->just_added = FALSE; + } else { + if (tag_data->set && cursor_position <= tag_data->position) { + tag_data->position += character_difference; + } + } +} + +static gboolean +have_unallowed_character (NautilusBatchRenameDialog *dialog) +{ + const gchar *entry_text; + gboolean have_unallowed_character_slash; + gboolean have_unallowed_character_dot; + gboolean have_unallowed_character_dotdot; + + have_unallowed_character_slash = FALSE; + have_unallowed_character_dot = FALSE; + have_unallowed_character_dotdot = FALSE; + + + + if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) { + entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)); + + } else { + entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry)); + } + + if (strstr (entry_text, "/") != NULL) { + have_unallowed_character_slash = TRUE; + } + + if (g_strcmp0 (entry_text, ".") == 0) { + have_unallowed_character_dot = TRUE; + } + + if (g_strcmp0 (entry_text, "..") == 0) { + have_unallowed_character_dotdot = TRUE; + } + + if (have_unallowed_character_slash) { + gtk_label_set_label (GTK_LABEL (dialog->conflict_label), + "\"/\" is an unallowed character"); + } + + if (have_unallowed_character_dot) { + gtk_label_set_label (GTK_LABEL (dialog->conflict_label), + "\".\" is an unallowed file name"); + } + + if (have_unallowed_character_dotdot) { + gtk_label_set_label (GTK_LABEL (dialog->conflict_label), + "\"..\" is an unallowed file name"); + } + + if (have_unallowed_character_slash || have_unallowed_character_dot || have_unallowed_character_dotdot) { + gtk_widget_set_sensitive (dialog->rename_button, FALSE); + gtk_widget_set_sensitive (dialog->conflict_down, FALSE); + gtk_widget_set_sensitive (dialog->conflict_up, FALSE); + + gtk_widget_show (dialog->conflict_box); + + return TRUE; + } else { + gtk_widget_hide (dialog->conflict_box); + + return FALSE; + } +} + +static void +update_display_text (NautilusBatchRenameDialog *dialog) +{ + TagData *tag_data; + TagData *tag_data0; + TagData *tag_data00; + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + tag_data0 = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + tag_data00 = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + + if (dialog->conflict_cancellable != NULL) + g_cancellable_cancel (dialog->conflict_cancellable); + + if(dialog->selection == NULL) + return; + + if (dialog->duplicates != NULL) { + g_list_free_full (dialog->duplicates, conflict_data_free); + dialog->duplicates = NULL; + } + + if (have_unallowed_character (dialog)) + return; + + update_tags (dialog); + + if (dialog->new_names != NULL) + g_list_free_full (dialog->new_names, string_free); + + if (!tag_data->set && !tag_data0->set && !tag_data00->set) { + gtk_label_set_label (GTK_LABEL (dialog->numbering_label), ""); + gtk_widget_hide (dialog->numbering_order_button); + } else { + gtk_label_set_label (GTK_LABEL (dialog->numbering_label), _("Automatic Numbering Order")); + gtk_widget_show (dialog->numbering_order_button); + } + + dialog->new_names = batch_rename_dialog_get_new_names (dialog); + dialog->checked_parents = 0; + + file_names_list_has_duplicates_async (dialog, + file_names_list_has_duplicates_callback, + NULL); +} + +static void +file_names_widget_entry_on_changed (NautilusBatchRenameDialog *dialog) +{ + update_display_text (dialog); +} + +static void +batch_rename_dialog_mode_changed (NautilusBatchRenameDialog *dialog) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->format_mode_button))) { + gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "format"); + + dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT; + + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry)); + + } else { + gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "replace"); + + dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_REPLACE; + + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->find_entry)); + } + + update_display_text (dialog); +} + +static void +add_button_clicked (NautilusBatchRenameDialog *dialog) +{ + if (gtk_widget_is_visible (dialog->add_popover)) + gtk_widget_set_visible (dialog->add_popover, FALSE); + else + gtk_widget_set_visible (dialog->add_popover, TRUE); +} + +static void +add_popover_closed (NautilusBatchRenameDialog *dialog) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->add_button), FALSE); +} + +static void +numbering_order_button_clicked (NautilusBatchRenameDialog *dialog) +{ + if (gtk_widget_is_visible (dialog->numbering_order_popover)) + gtk_widget_set_visible (dialog->numbering_order_popover, FALSE); + else + gtk_widget_set_visible (dialog->numbering_order_popover, TRUE); +} + +static void +numbering_order_popover_closed (NautilusBatchRenameDialog *dialog) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE); +} + +void +nautilus_batch_rename_dialog_query_finished (NautilusBatchRenameDialog *dialog, + GHashTable *hash_table, + GList *selection_metadata) +{ + GMenuItem *first_created; + GMenuItem *last_created; + FileMetadata *metadata; + TagData *tag_data; + + /* for files with no metadata */ + if (hash_table != NULL && g_hash_table_size (hash_table) == 0) { + g_hash_table_destroy (hash_table); + + hash_table = NULL; + } + + if (hash_table == NULL) + dialog->create_date = NULL; + else + dialog->create_date = hash_table; + + if (dialog->create_date != NULL) { + first_created = g_menu_item_new ("First Created", + "dialog.numbering-order-changed('first-created')"); + + g_menu_append_item (dialog->numbering_order_menu, first_created); + + last_created = g_menu_item_new ("Last Created", + "dialog.numbering-order-changed('last-created')"); + + g_menu_append_item (dialog->numbering_order_menu, last_created); + + } + + dialog->selection_metadata = selection_metadata; + metadata = selection_metadata->data; + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + if (metadata->creation_date == NULL || g_strcmp0 (metadata->creation_date->str, "") == 0) { + disable_action (dialog, "add-creation-date-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + if (metadata->equipment == NULL || g_strcmp0 (metadata->equipment->str, "") == 0) { + disable_action (dialog, "add-equipment-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + if (metadata->season == NULL || g_strcmp0 (metadata->season->str, "") == 0) { + disable_action (dialog, "add-season-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + if (metadata->episode_number == NULL || g_strcmp0 (metadata->episode_number->str, "") == 0) { + disable_action (dialog, "add-episode-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + if (metadata->track_number == NULL || g_strcmp0 (metadata->track_number->str, "") == 0) { + disable_action (dialog, "add-track-number-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + if (metadata->artist_name == NULL || g_strcmp0 (metadata->artist_name->str, "") == 0) { + disable_action (dialog, "add-artist-name-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + if (metadata->title == NULL || g_strcmp0 (metadata->title->str, "") == 0) { + disable_action (dialog, "add-title-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + if (metadata->album_name == NULL || g_strcmp0 (metadata->album_name->str, "") == 0) { + disable_action (dialog, "add-album-name-tag"); + tag_data->available = FALSE; + } else { + tag_data->set = FALSE; + } +} + +static void +update_row_shadowing (GtkWidget *row, + gboolean shown) +{ + GtkStyleContext *context; + GtkStateFlags flags; + + if (!GTK_IS_LIST_BOX_ROW (row)) + return; + + context = gtk_widget_get_style_context (row); + flags = gtk_style_context_get_state (context); + + if (shown) + flags |= GTK_STATE_PRELIGHT; + else + flags &= ~GTK_STATE_PRELIGHT; + + gtk_style_context_set_state (context, flags); + +} + +static gboolean +on_leave_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + update_row_shadowing (dialog->preselected_row1, FALSE); + update_row_shadowing (dialog->preselected_row2, FALSE); + + dialog->preselected_row1 = NULL; + dialog->preselected_row2 = NULL; + + return FALSE; +} + +static gboolean +on_motion (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + GtkListBoxRow *row; + NautilusBatchRenameDialog *dialog; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + if (dialog->preselected_row1 && dialog->preselected_row2) { + update_row_shadowing (dialog->preselected_row1, FALSE); + update_row_shadowing (dialog->preselected_row2, FALSE); + } + + if (widget == dialog->result_listbox) { + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row1 = GTK_WIDGET (row); + + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row2 = GTK_WIDGET (row); + + } + + if (widget == dialog->arrow_listbox) { + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row1 = GTK_WIDGET (row); + + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row2 = GTK_WIDGET (row); + } + + if (widget == dialog->original_name_listbox) { + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row1 = GTK_WIDGET (row); + + row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), event->y); + update_row_shadowing (GTK_WIDGET (row), TRUE); + dialog->preselected_row2 = GTK_WIDGET (row); + } + + return FALSE; +} + +static void +nautilus_batch_rename_dialog_initialize_actions (NautilusBatchRenameDialog *dialog) +{ + GAction *action; + + dialog->action_group = G_ACTION_GROUP (g_simple_action_group_new ()); + + g_action_map_add_action_entries (G_ACTION_MAP (dialog->action_group), + dialog_entries, + G_N_ELEMENTS (dialog_entries), + dialog); + gtk_widget_insert_action_group (GTK_WIDGET (dialog), + "dialog", + G_ACTION_GROUP (dialog->action_group)); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-original-file-name-tag"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + check_metadata_for_selection (dialog, dialog->selection); +} + +static void +file_names_widget_on_activate (NautilusBatchRenameDialog *dialog) +{ + prepare_batch_rename (dialog); +} + +static gboolean +remove_tag (NautilusBatchRenameDialog *dialog, + gchar *tag_name, + gchar *action_name, + gint keyval, + gboolean is_modifier) +{ + TagData *tag_data; + gint cursor_position; + GString *new_entry_text; + GString *entry_text; + gboolean delete_tag; + GAction *action; + + delete_tag = FALSE; + + g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL); + + tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name); + + entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry))); + + if (!tag_data->set) + return FALSE; + + if (keyval == GDK_KEY_BackSpace) { + if (cursor_position > tag_data->position && + cursor_position <= tag_data->position + g_utf8_strlen (tag_name, -1)) { + delete_tag = TRUE; + } + } + + if (keyval == GDK_KEY_Delete) { + if (cursor_position >= tag_data->position && + cursor_position < tag_data->position + g_utf8_strlen (tag_name, -1)) { + delete_tag = TRUE; + } + } + + if (!is_modifier && + keyval != GDK_KEY_Left && + keyval != GDK_KEY_Right && + keyval != GDK_KEY_Delete && + keyval != GDK_KEY_Return && + keyval != GDK_KEY_Escape && + keyval != GDK_KEY_Tab && + keyval != GDK_KEY_End && + keyval != GDK_KEY_Home) { + if (cursor_position > tag_data->position && + cursor_position < tag_data->position + g_utf8_strlen (tag_name, -1)) { + delete_tag = TRUE; + } + } + + if (delete_tag) { + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + action_name); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + new_entry_text = g_string_new (""); + new_entry_text = g_string_append_len (new_entry_text, + entry_text->str, + tag_data->position); + new_entry_text = g_string_append (new_entry_text, + g_utf8_offset_to_pointer (entry_text->str, + tag_data->position + g_utf8_strlen (tag_name, -1))); + + tag_data->set = FALSE; + dialog->cursor_position = tag_data->position; + dialog->use_manual_cursor_position = TRUE; + gtk_entry_set_text (GTK_ENTRY (dialog->name_entry), new_entry_text->str); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), tag_data->position); + dialog->use_manual_cursor_position = FALSE; + + g_string_free (new_entry_text, TRUE); + g_string_free (entry_text, TRUE); + + return TRUE; + } + + return FALSE; +} + +static GString* +remove_tag_selection (NautilusBatchRenameDialog *dialog, + GString *old_entry_text, + gchar *action_name, + gchar *tag_name, + gint start, + gint end) +{ + TagData *tag_data; + GAction *action; + GString *new_entry_text; + + new_entry_text = NULL; + + tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name); + + if (tag_data->set && tag_data->original_position < end && + tag_data->original_position + g_utf8_strlen (tag_name, -1) > start) { + new_entry_text = g_string_new (""); + new_entry_text = g_string_append_len (new_entry_text, + old_entry_text->str, + tag_data->new_position); + new_entry_text = g_string_append (new_entry_text, + g_utf8_offset_to_pointer (old_entry_text->str, + tag_data->new_position + g_utf8_strlen (tag_name, -1))); + + tag_data->set = FALSE; + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + action_name); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + + if (new_entry_text == NULL) + return g_string_new (old_entry_text->str); + return new_entry_text; +} + +static void +update_tag_position (NautilusBatchRenameDialog *dialog, + gchar *tag_name, + GString *new_entry_text) +{ + TagData *tag_data; + + tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name); + + if (tag_data->set) { + tag_data->original_position = tag_data->position; + tag_data->new_position = g_utf8_pointer_to_offset(new_entry_text->str, + g_strrstr (new_entry_text->str, tag_name)); + } +} + +static gboolean +on_key_press_event (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + NautilusBatchRenameDialog *dialog; + gint keyval; + GdkEvent *gdk_event; + GString *old_entry_text; + GString *new_entry_text; + gboolean entry_has_selection; + gint start; + gint end; + gboolean tag_removed; + TagData *tag_data; + gint minimum_tag_position; + GAction *action; + + gdk_event = (GdkEvent *) event; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data); + + keyval = event->keyval; + + entry_has_selection = (gtk_editable_get_selection_bounds (GTK_EDITABLE (dialog->name_entry), + &start, + &end)); + + if (event->state & GDK_CONTROL_MASK) + return GDK_EVENT_PROPAGATE; + + if (entry_has_selection && + ((keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) || + (!gdk_event->key.is_modifier && + keyval != GDK_KEY_Left && + keyval != GDK_KEY_Right && + keyval != GDK_KEY_Return && + keyval != GDK_KEY_Escape && + keyval != GDK_KEY_Tab && + keyval != GDK_KEY_End && + keyval != GDK_KEY_Home))) { + old_entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry))); + + minimum_tag_position = G_MAXINT; + tag_removed = FALSE; + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME); + if (tag_data->set) { + update_tag_position (dialog, ORIGINAL_FILE_NAME, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-original-file-name-tag", + ORIGINAL_FILE_NAME, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE); + if (tag_data->set) { + update_tag_position (dialog, CREATION_DATE, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-creation-date-tag", + CREATION_DATE, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING); + if (tag_data->set) { + update_tag_position (dialog, NUMBERING, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-numbering-tag-zero", + NUMBERING, + start, + end); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-one"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-two"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0); + if (tag_data->set) { + update_tag_position (dialog, NUMBERING0, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-numbering-tag-one", + NUMBERING0, + start, + end); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-zero"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-two"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00); + if (tag_data->set) { + update_tag_position (dialog, NUMBERING00, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-numbering-tag-two", + NUMBERING0, + start, + end); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-one"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-zero"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL); + if (tag_data->set) { + update_tag_position (dialog, CAMERA_MODEL, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-equipment-tag", + CAMERA_MODEL, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER); + if (tag_data->set) { + update_tag_position (dialog, TRACK_NUMBER, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-track-number-tag", + TRACK_NUMBER, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER); + if (tag_data->set) { + update_tag_position (dialog, SEASON_NUMBER, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-season-tag", + SEASON_NUMBER, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER); + if (tag_data->set) { + update_tag_position (dialog, EPISODE_NUMBER, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-episode-tag", + EPISODE_NUMBER, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME); + if (tag_data->set) { + update_tag_position (dialog, ARTIST_NAME, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-artist-name-tag", + ARTIST_NAME, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE); + if (tag_data->set) { + update_tag_position (dialog, TITLE, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-title-tag", + TITLE, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME); + if (tag_data->set) { + update_tag_position (dialog, ALBUM_NAME, old_entry_text); + new_entry_text = remove_tag_selection (dialog, + old_entry_text, + "add-album-name-tag", + ALBUM_NAME, + start, + end); + + if (!g_string_equal (new_entry_text, old_entry_text)) { + if (tag_data->position < minimum_tag_position) + minimum_tag_position = tag_data->position; + + tag_removed = TRUE; + } + g_string_free (old_entry_text, TRUE); + old_entry_text = new_entry_text; + } + + if (minimum_tag_position != G_MAXINT) { + dialog->use_manual_cursor_position = TRUE; + dialog->cursor_position = minimum_tag_position; + + gtk_entry_set_text (GTK_ENTRY (dialog->name_entry), new_entry_text->str); + gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), minimum_tag_position); + + dialog->use_manual_cursor_position = FALSE; + + g_string_free (new_entry_text, TRUE); + } + + if ((keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) && + tag_removed) + return TRUE; + + return GDK_EVENT_PROPAGATE; + } + + tag_removed = FALSE; + + if (remove_tag (dialog, + ORIGINAL_FILE_NAME, + "add-original-file-name-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + CREATION_DATE, + "add-creation-date-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + NUMBERING, + "add-numbering-tag-zero", + keyval, + gdk_event->key.is_modifier)) { + tag_removed = TRUE; + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-one"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-two"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + + if (!tag_removed && remove_tag (dialog, + NUMBERING0, + "add-numbering-tag-one", + keyval, + gdk_event->key.is_modifier)) { + tag_removed = TRUE; + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-zero"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-two"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + + if (!tag_removed && remove_tag (dialog, + NUMBERING00, + "add-numbering-tag-two", + keyval, + gdk_event->key.is_modifier)) { + tag_removed = TRUE; + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-one"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group), + "add-numbering-tag-zero"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + + if (!tag_removed && remove_tag (dialog, + CAMERA_MODEL, + "add-equipment-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + SEASON_NUMBER, + "add-season-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + EPISODE_NUMBER, + "add-episode-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + TRACK_NUMBER, + "add-track-number-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + ARTIST_NAME, + "add-artist-name-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + TITLE, + "add-title-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (!tag_removed && remove_tag (dialog, + ALBUM_NAME, + "add-album-name-tag", + keyval, + gdk_event->key.is_modifier)) + tag_removed = TRUE; + + if (tag_removed) { + if (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) + return TRUE; + + return GDK_EVENT_PROPAGATE; + } + + return GDK_EVENT_PROPAGATE; +} + +static void +nautilus_batch_rename_dialog_finalize (GObject *object) +{ + NautilusBatchRenameDialog *dialog; + GList *l; + + dialog = NAUTILUS_BATCH_RENAME_DIALOG (object); + + if (dialog->checking_conflicts) { + g_cancellable_cancel (dialog->conflict_cancellable); + g_object_unref (dialog->conflict_cancellable); + } + + g_list_free (dialog->original_name_listbox_rows); + g_list_free (dialog->arrow_listbox_rows); + g_list_free (dialog->result_listbox_rows); + g_list_free (dialog->listbox_labels_new); + g_list_free (dialog->listbox_labels_old); + g_list_free (dialog->listbox_icons); + + for (l = dialog->selection_metadata; l != NULL; l = l->next) { + FileMetadata *metadata; + + metadata = l->data; + + if (metadata->file_name != NULL) + g_string_free (metadata->file_name, TRUE); + if (metadata->creation_date != NULL) + g_string_free (metadata->creation_date, TRUE); + if (metadata->equipment != NULL) + g_string_free (metadata->equipment, TRUE); + if (metadata->season != NULL) + g_string_free (metadata->season, TRUE); + if (metadata->episode_number != NULL) + g_string_free (metadata->episode_number, TRUE); + if (metadata->track_number != NULL) + g_string_free (metadata->track_number, TRUE); + if (metadata->artist_name != NULL) + g_string_free (metadata->artist_name, TRUE); + if (metadata->album_name != NULL) + g_string_free (metadata->album_name, TRUE); + } + + if (dialog->create_date != NULL) + g_hash_table_destroy (dialog->create_date); + + g_list_free_full (dialog->distinct_parents, g_free); + g_list_free_full (dialog->new_names, string_free); + g_list_free_full (dialog->duplicates, conflict_data_free); + + G_OBJECT_CLASS (nautilus_batch_rename_dialog_parent_class)->finalize (object); +} + +static void +nautilus_batch_rename_dialog_class_init (NautilusBatchRenameDialogClass *klass) +{ + GtkWidgetClass *widget_class; + GObjectClass *oclass; + + widget_class = GTK_WIDGET_CLASS (klass); + oclass = G_OBJECT_CLASS (klass); + + oclass->finalize = nautilus_batch_rename_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-batch-rename-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, grid); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, cancel_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, original_name_listbox); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, arrow_listbox); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, result_listbox); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, name_entry); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, rename_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, find_entry); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_entry); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, mode_stack); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_mode_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, format_mode_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_popover); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_label); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, scrolled_window); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_popover); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_button); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_menu); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_box); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_label); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_up); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_down); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_tag_menu); + gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_label); + + gtk_widget_class_bind_template_callback (widget_class, file_names_widget_entry_on_changed); + gtk_widget_class_bind_template_callback (widget_class, file_names_widget_on_activate); + gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_mode_changed); + gtk_widget_class_bind_template_callback (widget_class, add_button_clicked); + gtk_widget_class_bind_template_callback (widget_class, add_popover_closed); + gtk_widget_class_bind_template_callback (widget_class, numbering_order_button_clicked); + gtk_widget_class_bind_template_callback (widget_class, numbering_order_popover_closed); + gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_up); + gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_down); + gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_on_response); + gtk_widget_class_bind_template_callback (widget_class, on_key_press_event); +} + +GtkWidget* +nautilus_batch_rename_dialog_new (GList *selection, + NautilusDirectory *directory, + NautilusWindow *window) +{ + NautilusBatchRenameDialog *dialog; + GString *dialog_title; + + dialog = g_object_new (NAUTILUS_TYPE_BATCH_RENAME_DIALOG, "use-header-bar", TRUE, NULL); + + dialog->selection = selection; + dialog->directory = directory; + dialog->window = window; + + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (window)); + + dialog_title = g_string_new (""); + g_string_append_printf (dialog_title, "Rename %d Files", g_list_length (selection)); + gtk_window_set_title (GTK_WINDOW (dialog), dialog_title->str); + + nautilus_batch_rename_dialog_initialize_actions (dialog); + + dialog->same_parent = !NAUTILUS_IS_SEARCH_DIRECTORY (directory); + + if (!dialog->same_parent) + dialog->distinct_parents = batch_rename_files_get_distinct_parents (dialog->selection); + else + dialog->distinct_parents = NULL; + + update_display_text (dialog); + + fill_display_listbox (dialog); + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL); + + g_string_free (dialog_title, TRUE); + + return GTK_WIDGET (dialog); +} + +static void +nautilus_batch_rename_dialog_init (NautilusBatchRenameDialog *self) +{ + TagData *tag_data; + + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_header_func (GTK_LIST_BOX (self->original_name_listbox), + (GtkListBoxUpdateHeaderFunc) listbox_header_func, + self, + NULL); + gtk_list_box_set_header_func (GTK_LIST_BOX (self->arrow_listbox), + (GtkListBoxUpdateHeaderFunc) listbox_header_func, + self, + NULL); + gtk_list_box_set_header_func (GTK_LIST_BOX (self->result_listbox), + (GtkListBoxUpdateHeaderFunc) listbox_header_func, + self, + NULL); + + + self->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT; + + gtk_popover_bind_model (GTK_POPOVER (self->numbering_order_popover), + G_MENU_MODEL (self->numbering_order_menu), + NULL); + gtk_popover_bind_model (GTK_POPOVER (self->add_popover), + G_MENU_MODEL (self->add_tag_menu), + NULL); + + gtk_label_set_ellipsize (GTK_LABEL (self->conflict_label), PANGO_ELLIPSIZE_END); + gtk_label_set_max_width_chars (GTK_LABEL (self->conflict_label), 1); + + self->duplicates = NULL; + self->new_names = NULL; + + self->checking_conflicts = FALSE; + + self->rename_clicked = FALSE; + + + self->tag_info_table = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + tag_data = g_new (TagData, 1); + tag_data->available = TRUE; + tag_data->set = TRUE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (ORIGINAL_FILE_NAME), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = TRUE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = TRUE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING0), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = TRUE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING00), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (CREATION_DATE), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (CAMERA_MODEL), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (SEASON_NUMBER), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (EPISODE_NUMBER), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (TRACK_NUMBER), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (ARTIST_NAME), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (TITLE), tag_data); + + tag_data = g_new (TagData, 1); + tag_data->available = FALSE; + tag_data->set = FALSE; + tag_data->position = 0; + g_hash_table_insert (self->tag_info_table, g_strdup (ALBUM_NAME), tag_data); + + gtk_entry_set_text (GTK_ENTRY (self->name_entry),ORIGINAL_FILE_NAME); + self->name_entry_characters = g_utf8_strlen (ORIGINAL_FILE_NAME, -1); + + self->row_height = -1; + + g_signal_connect (self->original_name_listbox, "row-selected", G_CALLBACK (row_selected), self); + g_signal_connect (self->arrow_listbox, "row-selected", G_CALLBACK (row_selected), self); + g_signal_connect (self->result_listbox, "row-selected", G_CALLBACK (row_selected), self); + + self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + g_signal_connect (self->original_name_listbox, + "motion-notify-event", + G_CALLBACK (on_motion), + self); + g_signal_connect (self->result_listbox, + "motion-notify-event", + G_CALLBACK (on_motion), + self); + g_signal_connect (self->arrow_listbox, + "motion-notify-event", + G_CALLBACK (on_motion), + self); + + g_signal_connect (self->original_name_listbox, + "leave-notify-event", + G_CALLBACK (on_leave_event), + self); + g_signal_connect (self->result_listbox, + "leave-notify-event", + G_CALLBACK (on_leave_event), + self); + g_signal_connect (self->arrow_listbox, + "leave-notify-event", + G_CALLBACK (on_leave_event), + self); +}
\ No newline at end of file |