[nautilus/wip/csoriano/file-operations-rename: 1/2] Implement batch renaming



commit faea7235010370838a30cd5dc9946361bee717ff
Author: Alexandru Pandelea <alexandru pandelea gmail com>
Date:   Tue Aug 23 00:18:54 2016 +0300

    Implement batch renaming
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768311

 src/Makefile.am                                  |    4 +
 src/nautilus-batch-rename-dialog.c               | 2496 ++++++++++++++++++++++
 src/nautilus-batch-rename-dialog.h               |   84 +
 src/nautilus-batch-rename-utilities.c            |  985 +++++++++
 src/nautilus-batch-rename-utilities.h            |   63 +
 src/nautilus-file-private.h                      |    3 +
 src/nautilus-file-undo-operations.c              |  183 ++
 src/nautilus-file-undo-operations.h              |   29 +
 src/nautilus-file-utilities.c                    |   16 +
 src/nautilus-file-utilities.h                    |    2 +
 src/nautilus-file.c                              |  429 +++-
 src/nautilus-file.h                              |    4 +
 src/nautilus-files-view.c                        |   26 +-
 src/resources/css/Adwaita.css                    |   15 +
 src/resources/nautilus.gresource.xml             |    1 +
 src/resources/ui/nautilus-batch-rename-dialog.ui |  495 +++++
 16 files changed, 4738 insertions(+), 97 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c925f41..99edf8f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -161,6 +161,8 @@ nautilus_no_main_sources = \
        gtk/nautilusgtkplacesviewrowprivate.h           \
        nautilus-application.c                  \
        nautilus-application.h                  \
+       nautilus-batch-rename-dialog.c          \
+       nautilus-batch-rename-dialog.h          \
        nautilus-bookmark-list.c                \
        nautilus-bookmark-list.h                \
        nautilus-canvas-view.c                  \
@@ -214,6 +216,8 @@ nautilus_no_main_sources = \
        nautilus-properties-window.h            \
        nautilus-query-editor.c                 \
        nautilus-query-editor.h                 \
+       nautilus-batch-rename-utilities.c               \
+       nautilus-batch-rename-utilities.h               \
        nautilus-search-popover.c               \
        nautilus-search-popover.h               \
        nautilus-self-check-functions.c         \
diff --git a/src/nautilus-batch-rename-dialog.c b/src/nautilus-batch-rename-dialog.c
new file mode 100644
index 0000000..7faab4c
--- /dev/null
+++ b/src/nautilus-batch-rename-dialog.c
@@ -0,0 +1,2496 @@
+/* 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>
+
+#define ADD_TEXT_ENTRY_SIZE 550
+#define REPLACE_ENTRY_SIZE  275
+#define TAG_UNAVAILABLE -2
+#define HAVE_CONFLICT 1
+
+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;
+};
+
+typedef struct
+{
+        gboolean available;
+        gboolean set;
+        gint position;
+} 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;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                  ORIGINAL_FILE_NAME,
+                                  strlen (ORIGINAL_FILE_NAME),
+                                  &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          CREATION_DATE,
+                                          strlen (CREATION_DATE),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          CAMERA_MODEL,
+                                          strlen (CAMERA_MODEL),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          SEASON_NUMBER,
+                                          strlen (SEASON_NUMBER),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          EPISODE_NUMBER,
+                                          strlen (EPISODE_NUMBER),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          TRACK_NUMBER,
+                                          strlen (TRACK_NUMBER),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          ARTIST_NAME,
+                                          strlen (ARTIST_NAME),
+                                          &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 = FALSE;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          TITLE,
+                                          strlen (TITLE),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-title-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;
+
+        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")) {
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING,
+                                          strlen (NUMBERING),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+        }
+
+        if (g_strrstr (action_name, "one")) {
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING0,
+                                          strlen (NUMBERING0),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+        }
+
+        if (g_strrstr (action_name, "two")) {
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING00,
+                                          strlen (NUMBERING00),
+                                          &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 },
+
+};
+
+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);
+        }
+}
+
+gint compare_int (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 *string;
+        GString *tag;
+        GArray *tag_positions;
+        gint tags;
+        gint i;
+        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++;
+        }
+
+        g_array_sort (tag_positions, compare_int);
+
+        for (i = 0; i < tags; i++) {
+                tag = g_string_new ("");
+
+                string = g_string_new ("");
+
+                string = g_string_append_len (string,
+                                              entry_text + tag_end_position,
+                                              g_array_index (tag_positions, gint, i) - tag_end_position);
+
+                if (g_strcmp0 (string->str, ""))
+                        result = g_list_prepend (result, string);
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (ORIGINAL_FILE_NAME);
+                        tag = g_string_append (tag, ORIGINAL_FILE_NAME);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (NUMBERING);
+                        tag = g_string_append (tag, NUMBERING);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (NUMBERING0);
+                        tag = g_string_append (tag, NUMBERING0);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (NUMBERING00);
+                        tag = g_string_append (tag, NUMBERING00);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (CREATION_DATE);
+                        tag = g_string_append (tag, CREATION_DATE);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (CAMERA_MODEL);
+                        tag = g_string_append (tag, CAMERA_MODEL);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (SEASON_NUMBER);
+                        tag = g_string_append (tag, SEASON_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (EPISODE_NUMBER);
+                        tag = g_string_append (tag, EPISODE_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (TRACK_NUMBER);
+                        tag = g_string_append (tag, TRACK_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (ARTIST_NAME);
+                        tag = g_string_append (tag, ARTIST_NAME);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+                if (g_array_index (tag_positions, gint, i) == tag_data->position && tag_data->set) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           strlen (TITLE);
+                        tag = g_string_append (tag, TITLE);
+                }
+
+                result = g_list_prepend (result, tag);
+        }
+        string = g_string_new ("");
+        string = g_string_append (string, entry_text + tag_end_position);
+
+        if (g_strcmp0 (string->str, ""))
+                result = g_list_prepend (result, string);
+
+        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 *tags_list;
+        g_autofree gchar *entry_text;
+        g_autofree gchar *replace_text;
+
+        selection = dialog->selection;
+        tags_list = 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 {
+                tags_list = split_entry_text (dialog, entry_text);
+
+                result = batch_rename_dialog_get_new_names_list (dialog->mode,
+                                                                 selection,
+                                                                 tags_list,
+                                                                 dialog->selection_metadata,
+                                                                 entry_text,
+                                                                 replace_text);
+                g_list_free_full (tags_list, string_free);
+        }
+
+        result = g_list_reverse (result);
+
+        return result;
+}
+
+static void
+begin_batch_rename_dialog (NautilusBatchRenameDialog *dialog,
+                           GList                     *new_names)
+{
+        GList *l1;
+        GList *l2;
+        GList *l3;
+        gchar *file_name;
+        gchar *old_file_name;
+        GString *new_file_name;
+        GList *new_name;
+        NautilusFile *file;
+
+        for (l1 = new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) 
{
+                old_file_name = nautilus_file_get_name (NAUTILUS_FILE (l2->data));
+                new_file_name = l1->data;
+
+                for (l3 = dialog->selection; l3 != NULL; l3 = l3->next) {
+                        file_name = nautilus_file_get_name (NAUTILUS_FILE (l3->data));
+                        if (l3 != l2 && g_strcmp0 (file_name, new_file_name->str) == 0) {
+                                file = NAUTILUS_FILE (l3->data);
+                                new_name = l1->data;
+
+                                dialog->selection = g_list_remove_link (dialog->selection, l3);
+                                new_names = g_list_remove_link (new_names, l1);
+
+                                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 complexity of
+ * the later.*/
+static void
+update_rows_height (NautilusBatchRenameDialog *dialog)
+{
+        GList *l;
+        gint minimum_height;
+        gint natural_height;
+        gint new_maximum_height;
+
+        new_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),
+                                                 &minimum_height,
+                                                 &natural_height);
+
+                if (minimum_height > new_maximum_height) {
+                        new_maximum_height = minimum_height;
+                }
+        }
+
+        for (l = dialog->listbox_labels_old; l != NULL; l = l->next) {
+                gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
+                                                 &minimum_height,
+                                                 &natural_height);
+
+                if (minimum_height > new_maximum_height) {
+                        new_maximum_height = minimum_height;
+                }
+        }
+
+        for (l = dialog->listbox_icons; l != NULL; l = l->next) {
+                gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
+                                                 &minimum_height,
+                                                 &natural_height);
+
+                if (minimum_height > new_maximum_height) {
+                        new_maximum_height = minimum_height;
+                }
+        }
+
+        if (new_maximum_height != dialog->row_height) {
+                dialog->row_height = new_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 = g_object_new (GTK_TYPE_LABEL,
+                                  "label",old_text,
+                                  "hexpand", TRUE,
+                                  "xalign", 0.0,
+                                  "margin-start", 6,
+                                  NULL);
+
+        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 = g_object_new (GTK_TYPE_LABEL,
+                                  "label",new_text,
+                                  "hexpand", TRUE,
+                                  "xalign", 0.0,
+                                  "margin-start", 6,
+                                  NULL);
+
+        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 = g_object_new (GTK_TYPE_LABEL,
+                             "label","→",
+                             "hexpand", FALSE,
+                             "xalign", 1.0,
+                             "margin-start", 6,
+                             NULL);
+
+        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
+batch_rename_dialog_on_response (NautilusBatchRenameDialog *dialog,
+                                 gint                       response_id,
+                                 gpointer                   user_data)
+{
+        if (response_id == GTK_RESPONSE_OK) {
+                /* 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;
+
+                GdkCursor *cursor;
+                GdkDisplay *display;
+
+                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, dialog->new_names);
+        }
+
+        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);
+
+        /* add rows to a list so that they can be removed when the renaming
+         * result changes */
+        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 *file_name;
+        GString *display_text;
+        GString *new_name;
+        gint nth_conflict_index;
+        gint nth_conflict;
+        gint name_occurences;
+        GtkAdjustment *adjustment;
+        GtkAllocation allocation;
+        ConflictData *data;
+
+        nth_conflict = dialog->selected_conflict;
+        l = g_list_nth (dialog->duplicates, nth_conflict);
+        data = l->data;
+
+        /* the conflict that has to be selected */
+        file_name = g_string_new (data->name);
+        display_text = g_string_new ("");
+
+        nth_conflict_index = 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, file_name))
+                        name_occurences++;
+        }
+        if (name_occurences > 1)
+                g_string_append_printf (display_text,
+                                        _("\"%s\" would not be a unique new name"),
+                                        file_name->str);
+        else
+                g_string_append_printf (display_text,
+                                        _("\"%s\" would conflict with an existing file."),
+                                        file_name->str);
+
+        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                             display_text->str);
+
+        g_string_free (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 *l;
+        gint index;
+        GtkStyleContext *context;
+        ConflictData *data;
+
+        index = 0;
+
+        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");
+
+                }
+
+                for (l = dialog->duplicates; l != NULL; l = l->next) {
+                        data = l->data;
+
+                        if (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"); 
+                        }
+                }
+
+                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_dialog_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) {
+                batch_rename_dialog_on_response (dialog, GTK_RESPONSE_OK, NULL);
+        }
+
+        if (dialog->rename_clicked && dialog->duplicates != NULL) {
+                dialog->rename_clicked = FALSE;
+        }
+}
+
+
+void
+check_conflict_for_file (NautilusBatchRenameDialog *dialog,
+                         NautilusDirectory         *directory,
+                         GList                     *files)
+{
+        gchar *current_directory;
+        gchar *parent_uri;
+        gchar *table_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;
+        ConflictData *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);
+
+        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);
+
+                table_parent_uri = g_hash_table_lookup (new_names_table, new_name->str);
+
+                if (g_strcmp0 (parent_uri, current_directory) == 0)
+                        g_hash_table_insert (new_names_table,
+                                             g_strdup (new_name->str),
+                                             nautilus_file_get_parent_uri (file));
+
+                if (table_parent_uri != NULL  && g_strcmp0 (current_directory, parent_uri) == 0) {
+                        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 (HAVE_CONFLICT));
+        }
+
+        for (l1 = dialog->selection, l2 = dialog->new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = 
l2->next) {
+                file = NAUTILUS_FILE (l1->data);
+
+                file_name = g_string_new ("");
+                name = nautilus_file_get_name (file);
+                g_string_append (file_name, 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 == HAVE_CONFLICT &&
+                            !file_name_conflicts_with_results (dialog->selection, dialog->new_names, 
new_name, parent_uri)) {
+                                data = g_new (ConflictData, 1);
+                                data->name = g_strdup (new_name->str);
+                                data->index = g_list_index (dialog->selection, l1->data);
+                                dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                     data);
+
+                                have_conflict = TRUE;
+                        }
+                }
+                if (!have_conflict) {
+                        table_parent_uri = g_hash_table_lookup (names_conflicts_table, new_name->str);
+
+                        if (table_parent_uri != NULL && g_strcmp0 (nautilus_file_get_parent_uri (file), 
current_directory) == 0) {
+                                data = g_new (ConflictData, 1);
+                                data->name = g_strdup (new_name->str);
+                                data->index = g_list_index (dialog->selection, l1->data);
+                                dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                     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_file (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 *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 (HAVE_CONFLICT));
+
+                if (!hash_table_insertion) {
+                        g_hash_table_insert (names_conflicts_table,
+                                             g_strdup (new_name->str),
+                                             GINT_TO_POINTER (HAVE_CONFLICT));
+                }
+        }
+
+        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 (HAVE_CONFLICT));
+        }
+
+        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 == HAVE_CONFLICT &&
+                            !file_name_conflicts_with_results (dialog->selection, new_names, new_name, 
NULL)) {
+                                        data = g_new (ConflictData, 1);
+                                        data->name = g_strdup (new_name->str);
+                                        data->index = g_list_index (dialog->selection, l2->data);
+                                        dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                             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 == HAVE_CONFLICT) {
+                                        data = g_new (ConflictData, 1);
+                                        data->name = g_strdup (new_name->str);
+                                        data->index = g_list_index (dialog->selection, l2->data);
+                                        dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                             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_set_priority (dialog->conflicts_task, G_PRIORITY_DEFAULT);
+        g_task_run_in_thread (dialog->conflicts_task, file_names_list_has_duplicates_async_thread);
+
+        g_object_unref (dialog->conflicts_task);
+}
+
+static void
+check_if_tag_is_used (NautilusBatchRenameDialog *dialog,
+                      gchar                     *tag_name,
+                      gchar                     *action_name)
+{
+        GString *entry_text;
+        GAction *action;
+        TagData *tag_data;
+
+        entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name);
+
+        if (g_strrstr (entry_text->str, tag_name) && 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), FALSE);
+        }
+
+        if (g_strrstr (entry_text->str, tag_name) == NULL) {
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                     action_name);
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                tag_data->set = FALSE;
+        }
+
+        if (g_strrstr (entry_text->str, tag_name)) {
+                tag_data->position = g_strrstr (entry_text->str, tag_name) -
+                                     entry_text->str;
+                tag_data->set = TRUE;
+        }
+
+        g_string_free (entry_text, TRUE);
+}
+
+static void
+check_numbering_tags (NautilusBatchRenameDialog *dialog)
+{
+        GString *entry_text;
+        GAction *add_numbering_action;
+        TagData *tag_data;
+        TagData *tag_data0;
+        TagData *tag_data00;
+
+        entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
+
+        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 ((g_strrstr (entry_text->str, NUMBERING) ||
+            g_strrstr (entry_text->str, NUMBERING0) ||
+            g_strrstr (entry_text->str, NUMBERING00)) &&
+            (!tag_data->set && !tag_data0->set && !tag_data00->set)) {
+                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);
+
+                if (g_strrstr (entry_text->str, NUMBERING))
+                        tag_data->set = TRUE;
+                if (g_strrstr (entry_text->str, NUMBERING0))
+                        tag_data0->set = TRUE;
+                if (g_strrstr (entry_text->str, NUMBERING00))
+                        tag_data00->set = TRUE;
+        }
+
+        if (g_strrstr (entry_text->str, NUMBERING) == NULL &&
+            g_strrstr (entry_text->str, NUMBERING0) == NULL &&
+            g_strrstr (entry_text->str, NUMBERING00) == NULL &&
+            (tag_data->set || tag_data0->set || tag_data00->set)) {
+                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), TRUE);
+
+                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), TRUE);
+
+                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), TRUE);
+
+                tag_data->set = FALSE;
+                tag_data0->set = FALSE;
+                tag_data00->set = FALSE;
+        }
+
+        if (g_strrstr (entry_text->str, NUMBERING)) {
+                tag_data->position = g_strrstr (entry_text->str, NUMBERING) - entry_text->str;
+        }
+
+        if (g_strrstr (entry_text->str, NUMBERING0)) {
+                tag_data0->position = g_strrstr (entry_text->str, NUMBERING0) - entry_text->str;
+        }
+        if (g_strrstr (entry_text->str, NUMBERING00)) {
+                tag_data00->position = g_strrstr (entry_text->str, NUMBERING00) - entry_text->str;
+        }
+        g_string_free (entry_text, TRUE);
+}
+
+static void
+update_tags (NautilusBatchRenameDialog *dialog)
+{
+        TagData *tag_data;
+
+        check_if_tag_is_used (dialog,
+                              ORIGINAL_FILE_NAME,
+                              "add-original-file-name-tag");
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      CREATION_DATE,
+                                      "add-creation-date-tag");
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      CAMERA_MODEL,
+                                      "add-equipment-tag");
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      SEASON_NUMBER,
+                                      "add-season-tag");
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      EPISODE_NUMBER,
+                                      "add-episode-tag");
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      TRACK_NUMBER,
+                                      "add-track-number-tag");
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      ARTIST_NAME,
+                                      "add-artist-name-tag");
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+        if (tag_data->available)
+                check_if_tag_is_used (dialog,
+                                      TITLE,
+                                      "add-title-tag");
+
+        check_numbering_tags (dialog);
+}
+
+static gboolean
+have_unallowed_character (NautilusBatchRenameDialog *dialog)
+{
+        const gchar *entry_text;
+        gboolean have_unallowed_character;
+
+        have_unallowed_character = FALSE;
+
+        if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) {
+                entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
+
+                if (strstr (entry_text, "/") != NULL) {
+                        have_unallowed_character = TRUE;
+                }
+        } else {
+                entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+
+                if (strstr (entry_text, "/") != NULL) {
+                        have_unallowed_character = TRUE;
+                }
+        }
+
+        if (have_unallowed_character) {
+                gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                                    "\"/\" is an unallowed character");
+
+                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
+file_names_widget_entry_on_changed (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;
+
+        if (dialog->new_names != NULL)
+                g_list_free_full (dialog->new_names, string_free);
+
+        update_tags (dialog);
+
+        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
+update_display_text (NautilusBatchRenameDialog *dialog)
+{
+        file_names_widget_entry_on_changed (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;
+        }
+}
+
+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)
+{
+        batch_rename_dialog_on_response (dialog, GTK_RESPONSE_OK, NULL);
+}
+
+static gboolean
+remove_tag (NautilusBatchRenameDialog *dialog,
+            gchar                     *tag_name,
+            gchar                     *keyval_name,
+            gboolean                   is_modifier)
+{
+        TagData *tag_data;
+        gint cursor_position;
+        GString *new_entry_text;
+        GString *entry_text;
+
+        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 (gtk_editable_get_selection_bounds (GTK_EDITABLE (dialog->name_entry), NULL, NULL) &&
+            cursor_position == tag_data->position + strlen (tag_name) &&
+            g_strcmp0(keyval_name, "BackSpace") == 0)
+                return FALSE;
+
+        if (gtk_editable_get_selection_bounds (GTK_EDITABLE (dialog->name_entry), NULL, NULL) &&
+            cursor_position == tag_data->position &&
+            g_strcmp0(keyval_name, "Delete") == 0)
+                return FALSE;
+
+        if (g_strcmp0(keyval_name, "BackSpace") == 0 && tag_data->set) {
+                if (cursor_position > tag_data->position &&
+                    cursor_position <= tag_data->position + strlen (tag_name)) {
+                        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,
+                                                          entry_text->str + tag_data->position + strlen 
(tag_name));
+
+                        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);
+
+                        tag_data->set = FALSE;
+
+                        g_string_free (new_entry_text, TRUE);
+                        g_string_free (entry_text, TRUE);
+
+                        return TRUE;
+                }
+        }
+
+        if (g_strcmp0(keyval_name, "Delete") == 0 && tag_data->set) {
+                if (cursor_position >= tag_data->position &&
+                    cursor_position < tag_data->position + strlen (tag_name)) {
+                        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,
+                                                          entry_text->str + tag_data->position + strlen 
(tag_name));
+
+                        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);
+
+                        tag_data->set = FALSE;
+
+                        g_string_free (new_entry_text, TRUE);
+                        g_string_free (entry_text, TRUE);
+
+                        return TRUE;
+                }
+        }
+
+        if (!is_modifier && tag_data->set &&
+            g_strcmp0(keyval_name, "Left") != 0 &&
+            g_strcmp0(keyval_name, "Right") != 0 &&
+            g_strcmp0(keyval_name, "Return") != 0) {
+                if (cursor_position > tag_data->position &&
+                    cursor_position < tag_data->position + strlen (tag_name)) {
+                        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,
+                                                          entry_text->str + tag_data->position + strlen 
(tag_name));
+
+                        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);
+
+                        tag_data->set = FALSE;
+
+                        g_string_free (new_entry_text, TRUE);
+                        g_string_free (entry_text, TRUE);
+
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+static gboolean
+on_key_press_event (GtkWidget    *widget,
+                    GdkEventKey  *event,
+                    gpointer      user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        gchar* keyval_name;
+        GdkEvent *gdk_event;
+
+        gdk_event = (GdkEvent *) event;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        keyval_name = gdk_keyval_name (event->keyval);
+
+        if (remove_tag (dialog, ORIGINAL_FILE_NAME, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, NUMBERING, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, NUMBERING0, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, NUMBERING00, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, CAMERA_MODEL, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, CREATION_DATE, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, SEASON_NUMBER, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, EPISODE_NUMBER, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, TRACK_NUMBER, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, ARTIST_NAME, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        if (remove_tag (dialog, TITLE, keyval_name, gdk_event->key.is_modifier))
+                return TRUE;
+
+        return FALSE;
+}
+
+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 (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;
+        gint files_number;
+        GList *l;
+        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));
+
+        files_number = 0;
+
+        for (l = dialog->selection; l != NULL; l = l->next)
+                files_number++;
+
+        dialog_title = g_string_new ("");
+        g_string_append_printf (dialog_title, "Renaming %d files", files_number);
+        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 = distinct_file_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);
+
+        gtk_entry_set_text (GTK_ENTRY (self->name_entry),ORIGINAL_FILE_NAME);
+
+        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
diff --git a/src/nautilus-batch-rename-dialog.h b/src/nautilus-batch-rename-dialog.h
new file mode 100644
index 0000000..c9dd45a
--- /dev/null
+++ b/src/nautilus-batch-rename-dialog.h
@@ -0,0 +1,84 @@
+
+#ifndef NAUTILUS_BATCH_RENAME_DIALOG_H
+#define NAUTILUS_BATCH_RENAME_DIALOG_H
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+#include "nautilus-files-view.h"
+
+G_BEGIN_DECLS
+
+#define ORIGINAL_FILE_NAME "[Original file name]"
+#define NUMBERING "[1, 2, 3]"
+#define NUMBERING0 "[01, 02, 03]"
+#define NUMBERING00 "[001, 002, 003]"
+#define CAMERA_MODEL "[Camera model]"
+#define CREATION_DATE "[Creation date]"
+#define SEASON_NUMBER "[Season number]"
+#define EPISODE_NUMBER "[Episode number]"
+#define TRACK_NUMBER "[Track number]"
+#define ARTIST_NAME "[Artist name]"
+#define TITLE "[Title]"
+
+typedef enum {
+        NAUTILUS_BATCH_RENAME_DIALOG_APPEND = 0,
+        NAUTILUS_BATCH_RENAME_DIALOG_PREPEND = 1,
+        NAUTILUS_BATCH_RENAME_DIALOG_REPLACE = 2,
+        NAUTILUS_BATCH_RENAME_DIALOG_FORMAT = 3,
+} NautilusBatchRenameDialogMode;
+
+typedef enum {
+        ORIGINAL_ASCENDING = 0,
+        ORIGINAL_DESCENDING = 1,
+        FIRST_MODIFIED = 2,
+        LAST_MODIFIED = 3,
+        FIRST_CREATED = 4,
+        LAST_CREATED = 5,
+} SortingMode;
+
+typedef struct 
+{
+        gchar *name;
+        gint index;
+} ConflictData;
+
+typedef struct {
+        GString *file_name;
+
+        /* Photo */
+        GString *creation_date;
+        GString *equipment;
+
+        /* Video */
+        GString *season;
+        GString *episode_number;
+
+        /* Music */
+        GString *track_number;
+        GString *artist_name;
+        GString *title;
+} FileMetadata;
+
+#define NAUTILUS_TYPE_BATCH_RENAME_DIALOG (nautilus_batch_rename_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusBatchRenameDialog, nautilus_batch_rename_dialog, NAUTILUS, 
BATCH_RENAME_DIALOG, GtkDialog);
+
+GtkWidget*      nautilus_batch_rename_dialog_new                      (GList                     *selection,
+                                                                       NautilusDirectory         *directory,
+                                                                       NautilusWindow            *window);
+
+void            nautilus_batch_rename_dialog_query_finished           (NautilusBatchRenameDialog *dialog,
+                                                                       GHashTable                *hash_table,
+                                                                       GList                     
*selection_metadata);
+
+void            check_conflict_for_file                               (NautilusBatchRenameDialog *dialog,
+                                                                       NautilusDirectory         *directory,
+                                                                       GList                     *files);
+
+gint            compare_int                                           (gconstpointer              a,
+                                                                       gconstpointer              b);
+
+G_END_DECLS
+
+#endif
\ No newline at end of file
diff --git a/src/nautilus-batch-rename-utilities.c b/src/nautilus-batch-rename-utilities.c
new file mode 100644
index 0000000..a1f59bf
--- /dev/null
+++ b/src/nautilus-batch-rename-utilities.c
@@ -0,0 +1,985 @@
+/* nautilus-batch-rename-utilities.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 "nautilus-batch-rename-dialog.h"
+#include "nautilus-batch-rename-utilities.h"
+#include "nautilus-file.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <stdarg.h>
+#include <eel/eel-vfs-extensions.h>
+
+#define HAVE_CONFLICT 1
+
+typedef struct {
+        NautilusFile *file;
+        gint          position;
+} CreateDateElem;
+
+typedef struct {
+        NautilusBatchRenameDialog *dialog;
+        GHashTable *hash_table;
+
+        GList *selection_metadata;
+
+        gboolean have_creation_date;
+        gboolean have_equipment;
+        gboolean have_season;
+        gboolean have_episode_number;
+        gboolean have_track_number;
+        gboolean have_artist_name;
+        gboolean have_title;
+} QueryData;
+
+static void cursor_callback (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data);
+
+void
+string_free (gpointer mem)
+{
+        if (mem != NULL)
+                g_string_free (mem, TRUE);
+}
+
+void
+conflict_data_free (gpointer mem)
+{
+        ConflictData *data = mem;
+
+        g_free (data->name);
+        g_free (data);
+}
+
+static GString*
+batch_rename_dialog_replace (gchar *string,
+                             gchar *substring,
+                             gchar *replacement)
+{
+        GString *new_string;
+        gchar **splitted_string;
+        gint i, n_splits;
+
+        new_string = g_string_new ("");
+
+        if (substring == NULL || replacement == NULL) {
+                g_string_append (new_string, string);
+
+                return new_string;
+        }
+
+        if (g_utf8_strlen (substring, -1) == 0) {
+                g_string_append (new_string, string);
+
+                return new_string;
+        }
+
+        splitted_string = g_strsplit (string, substring, -1);
+        if (splitted_string == NULL) {
+                g_string_append (new_string, string);
+
+                return new_string;
+        }
+
+        n_splits = g_strv_length (splitted_string);
+
+        for (i = 0; i < n_splits; i++) {
+                g_string_append (new_string, splitted_string[i]);
+
+                if (i != n_splits - 1)
+                        g_string_append (new_string, replacement);
+        }
+
+        g_strfreev (splitted_string);
+
+        return new_string;
+}
+
+GString*
+batch_rename_dialog_replace_label_text (gchar       *string,
+                                        const gchar *substring)
+{
+        GString *new_string;
+        gchar **splitted_string;
+        gchar *token;
+        gint i, n_splits;
+
+        new_string = g_string_new ("");
+
+        if (substring == NULL || g_strcmp0 (substring, "") == 0) {
+                token = g_markup_escape_text (string, strlen (string));
+                new_string = g_string_append (new_string, token);
+                g_free (token);
+
+                return new_string;
+        }
+
+        splitted_string = g_strsplit (string, substring, -1);
+        if (splitted_string == NULL) {
+                token = g_markup_escape_text (string, strlen (string));
+                new_string = g_string_append (new_string, token);
+                g_free (token);
+
+                return new_string;
+        }
+
+        n_splits = g_strv_length (splitted_string);
+
+        for (i = 0; i < n_splits; i++) {
+                token = g_markup_escape_text (splitted_string[i], strlen (splitted_string[i]));
+                new_string = g_string_append (new_string, token);
+
+                g_free (token);
+
+                if (i != n_splits - 1) {
+                        token = g_markup_escape_text (substring, strlen (substring));
+                        g_string_append_printf (new_string,
+                                                "<span background=\'#f57900\' color='white'>%s</span>",
+                                                token);
+
+                        g_free (token);
+                }
+        }
+
+        g_strfreev (splitted_string);
+
+        return new_string;
+}
+
+static gchar*
+get_metadata (GList *selection_metadata,
+              gchar *file_name,
+              gchar *metadata)
+{
+        GList *l;
+        FileMetadata *file_metadata;
+
+        for (l = selection_metadata; l != NULL; l = l->next) {
+                file_metadata = l->data;
+                if (g_strcmp0 (file_name, file_metadata->file_name->str) == 0) {
+                        if (g_strcmp0 (metadata, "creation_date") == 0 &&
+                            file_metadata->creation_date != NULL &&
+                            file_metadata->creation_date->len != 0)
+                                return file_metadata->creation_date->str;
+
+                        if (g_strcmp0 (metadata, "equipment") == 0 &&
+                            file_metadata->equipment != NULL &&
+                            file_metadata->equipment->len != 0)
+                                return file_metadata->equipment->str;
+
+                        if (g_strcmp0 (metadata, "season") == 0 &&
+                            file_metadata->season != NULL &&
+                            file_metadata->season->len != 0)
+                                return file_metadata->season->str;
+
+                        if (g_strcmp0 (metadata, "episode_number") == 0 &&
+                            file_metadata->episode_number != NULL &&
+                            file_metadata->episode_number->len != 0)
+                                return file_metadata->episode_number->str;
+
+                        if (g_strcmp0 (metadata, "track_number") == 0 &&
+                            file_metadata->track_number != NULL &&
+                            file_metadata->track_number->len != 0)
+                                return file_metadata->track_number->str;
+
+                        if (g_strcmp0 (metadata, "artist_name") == 0 &&
+                            file_metadata->artist_name != NULL &&
+                            file_metadata->artist_name->len != 0)
+                                return file_metadata->artist_name->str;
+
+                        if (g_strcmp0 (metadata, "title") == 0 &&
+                            file_metadata->title != NULL &&
+                            file_metadata->title->len != 0)
+                                return file_metadata->title->str;
+                }
+        }
+
+        return NULL;
+}
+
+static GString*
+batch_rename_dialog_format (NautilusFile *file,
+                            GList        *tags_list,
+                            GList        *selection_metadata,
+                            gint          count)
+{
+        GDateTime *datetime;
+        GList *l;
+        GString *tag;
+        GString *new_name;
+        GString *create_date;
+        gboolean added_tag;
+        g_autofree gchar *file_name;
+        g_autofree gchar *extension;
+        gchar *metadata;
+        gchar **splitted_date;
+        gchar *base_name;
+        gchar *date;
+
+        file_name = nautilus_file_get_display_name (file);
+        extension = nautilus_file_get_extension (file);
+
+        new_name = g_string_new ("");
+
+        for (l = tags_list; l != NULL; l = l->next) {
+                tag = l->data;
+                added_tag = FALSE;
+
+                if (!added_tag && g_strcmp0 (tag->str, ORIGINAL_FILE_NAME) == 0) {
+                        base_name = eel_filename_strip_extension (file_name);
+
+                        new_name = g_string_append (new_name, base_name);
+
+                        added_tag = TRUE;
+                        g_free (base_name);
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, NUMBERING) == 0) {
+                        g_string_append_printf (new_name, "%d", count);
+                        added_tag = TRUE;
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, NUMBERING0) == 0) {
+                        if (count < 10) {
+                                g_string_append_printf (new_name, "0%d", count);
+                        } else {
+                                g_string_append_printf (new_name, "%d", count);
+                        }
+
+                        added_tag = TRUE;
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, NUMBERING00) == 0) {
+                        if (count < 10) {
+                                g_string_append_printf (new_name, "00%d", count);
+                        } else {
+                                if (count < 100) {
+                                        g_string_append_printf (new_name, "0%d", count);
+                                } else {
+                                        g_string_append_printf (new_name, "%d", count);
+                                }
+                        }
+
+                        added_tag = TRUE;
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, CAMERA_MODEL) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "equipment");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, CREATION_DATE) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "creation_date");
+
+                        if (metadata != NULL) {
+                                splitted_date = g_strsplit_set (metadata, "T:-Z", -1);
+
+                                datetime = g_date_time_new_local (atoi (splitted_date[0]),
+                                                                  atoi (splitted_date[1]),
+                                                                  atoi (splitted_date[2]),
+                                                                  atoi (splitted_date[3]),
+                                                                  atoi (splitted_date[4]),
+                                                                  atoi (splitted_date[5]));
+
+                                date = g_date_time_format (datetime, "%x");
+
+                                if (strstr (date, "/") != NULL) {
+                                        create_date = batch_rename_dialog_replace (date, "/", "-");
+                                        new_name = g_string_append (new_name, create_date->str);
+
+                                        g_string_free (create_date, TRUE);
+                                } else {
+                                        new_name = g_string_append (new_name, date);
+                                }
+
+                                added_tag = TRUE;
+
+                                g_free (date);
+                                g_strfreev (splitted_date);
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, SEASON_NUMBER) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "season");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, EPISODE_NUMBER) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "episode_number");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, TRACK_NUMBER) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "track_number");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, ARTIST_NAME) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "artist_name");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, TITLE) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "title");
+
+                        if (metadata != NULL) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                if (!added_tag)
+                        new_name = g_string_append (new_name, tag->str);
+        }
+
+        if (g_strcmp0 (new_name->str, "") == 0) {
+                new_name = g_string_append (new_name, file_name);
+        } else {
+                if (extension != NULL)
+                        new_name = g_string_append (new_name, extension);
+        }
+
+        return new_name;
+}
+
+GList*
+batch_rename_dialog_get_new_names_list (NautilusBatchRenameDialogMode mode,
+                                        GList                        *selection,
+                                        GList                        *tags_list,
+                                        GList                        *selection_metadata,
+                                        gchar                        *entry_text,
+                                        gchar                        *replace_text)
+{
+        GList *l;
+        GList *result;
+        GString *file_name;
+        GString *new_name;
+        NautilusFile *file;
+        gchar *name;
+        gint count;
+
+        result = NULL;
+        count = 1;
+        file_name = g_string_new ("");
+
+        for (l = selection; l != NULL; l = l->next) {
+                file = NAUTILUS_FILE (l->data);
+
+                file_name = g_string_new ("");
+                name = nautilus_file_get_name (file);
+                g_string_append (file_name, name);
+
+                /* get the new name here and add it to the list*/
+                if (mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) {
+                        new_name = batch_rename_dialog_format (file,
+                                                        tags_list,
+                                                        selection_metadata,
+                                                        count++);
+                        result = g_list_prepend (result, new_name);
+                }
+
+                if (mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE) {
+                        new_name = batch_rename_dialog_replace (file_name->str,
+                                                         entry_text,
+                                                         replace_text);
+                        result = g_list_prepend (result, new_name);
+                }
+                
+                g_string_free (file_name, TRUE);
+                g_free (name);
+        }
+
+        return result;
+}
+
+/* There is a case that a new name for a file conflicts with an existing file name
+ * in the directory but it's not a problem because the file in the directory that
+ * conflicts is part of the batch renaming selection and it's going to change the name anyway. */
+gboolean
+file_name_conflicts_with_results (GList        *selection,
+                                  GList        *new_names,
+                                  GString      *old_name,
+                                  gchar        *parent_uri)
+{
+        GList *l1;
+        GList *l2;
+        NautilusFile *selection_file;
+        gchar *name1;
+        GString *new_name;
+        gchar *selection_parent_uri;
+
+        for (l1 = selection, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+                selection_file = NAUTILUS_FILE (l1->data);
+                name1 = nautilus_file_get_name (selection_file);
+
+                selection_parent_uri = nautilus_file_get_parent_uri (selection_file);
+
+                if (g_strcmp0 (name1, old_name->str) == 0) {
+                        new_name = l2->data;
+
+                        /* if the name didn't change, then there's a conflict */
+                        if (g_string_equal (old_name, new_name) &&
+                            (parent_uri == NULL || g_strcmp0 (parent_uri, selection_parent_uri) == 0))
+                                return FALSE;
+
+
+                        /* if this file exists and it changed it's name, then there's no
+                         * conflict */
+                        return TRUE;
+                }
+
+                g_free (selection_parent_uri);
+        }
+
+        /* such a file doesn't exist so there actually is a conflict */
+        return FALSE;
+}
+
+gint
+compare_files_by_name_ascending (gconstpointer a,
+                                 gconstpointer b)
+{
+        NautilusFile *file1;
+        NautilusFile *file2;
+
+        file1 = NAUTILUS_FILE (a);
+        file2 = NAUTILUS_FILE (b);
+
+        return nautilus_file_compare_for_sort (file1,file2,
+                                               NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+                                               FALSE, FALSE);
+}
+
+gint
+compare_files_by_name_descending (gconstpointer a,
+                                  gconstpointer b)
+{
+        NautilusFile *file1;
+        NautilusFile *file2;
+
+        file1 = NAUTILUS_FILE (a);
+        file2 = NAUTILUS_FILE (b);
+
+        return nautilus_file_compare_for_sort (file1,file2,
+                                               NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+                                               FALSE, TRUE);
+}
+
+gint
+compare_files_by_first_modified (gconstpointer a,
+                                 gconstpointer b)
+{
+        NautilusFile *file1;
+        NautilusFile *file2;
+
+        file1 = NAUTILUS_FILE (a);
+        file2 = NAUTILUS_FILE (b);
+
+        return nautilus_file_compare_for_sort (file1,file2,
+                                               NAUTILUS_FILE_SORT_BY_MTIME,
+                                               FALSE, FALSE);
+}
+
+gint
+compare_files_by_last_modified (gconstpointer a,
+                                gconstpointer b)
+{
+        NautilusFile *file1;
+        NautilusFile *file2;
+
+        file1 = NAUTILUS_FILE (a);
+        file2 = NAUTILUS_FILE (b);
+
+        return nautilus_file_compare_for_sort (file1,file2,
+                                               NAUTILUS_FILE_SORT_BY_MTIME,
+                                               FALSE, TRUE);
+}
+
+gint
+compare_files_by_first_created (gconstpointer a,
+                                gconstpointer b)
+{
+        CreateDateElem *elem1;
+        CreateDateElem *elem2;
+
+        elem1 = (CreateDateElem*) a;
+        elem2 = (CreateDateElem*) b;
+
+        return elem1->position - elem2->position;
+}
+
+gint
+compare_files_by_last_created (gconstpointer a,
+                               gconstpointer b)
+{
+        CreateDateElem *elem1;
+        CreateDateElem *elem2;
+
+        elem1 = (CreateDateElem*) a;
+        elem2 = (CreateDateElem*) b;
+
+        return elem2->position - elem1->position;
+}
+
+GList*
+nautilus_batch_rename_dialog_sort (GList       *selection,
+                                   SortingMode  mode,
+                                   GHashTable  *creation_date_table)
+{
+        GList *l,*l2;
+        NautilusFile *file;
+        GList *create_date_list;
+        GList *create_date_list_sorted;
+        gchar *name;
+
+        if (mode == ORIGINAL_ASCENDING)
+                return g_list_sort (selection, compare_files_by_name_ascending);
+
+        if (mode == ORIGINAL_DESCENDING) {
+                return g_list_sort (selection, compare_files_by_name_descending);
+        }
+
+        if (mode == FIRST_MODIFIED) {
+            return g_list_sort (selection, compare_files_by_first_modified);
+        }
+
+        if (mode == LAST_MODIFIED) {
+            return g_list_sort (selection, compare_files_by_last_modified);
+        }
+
+        if (mode == FIRST_CREATED || mode == LAST_CREATED) {
+                create_date_list = NULL;
+
+                for (l = selection; l != NULL; l = l->next) {
+                        CreateDateElem *elem;
+                        elem = g_new (CreateDateElem, 1);
+
+                        file = NAUTILUS_FILE (l->data);
+
+                        name = nautilus_file_get_name (file);
+                        elem->file = file;
+                        elem->position = GPOINTER_TO_INT (g_hash_table_lookup (creation_date_table, name));
+                        g_free (name);
+
+                        create_date_list = g_list_prepend (create_date_list, elem);
+                }
+
+                if (mode == FIRST_CREATED)
+                        create_date_list_sorted = g_list_sort (create_date_list,
+                                                              compare_files_by_first_created);
+                else
+                        create_date_list_sorted = g_list_sort (create_date_list,
+                                                              compare_files_by_last_created);
+
+                for (l = selection, l2 = create_date_list_sorted; l2 != NULL; l = l->next, l2 = l2->next) {
+                        CreateDateElem *elem = l2->data;
+                        l->data = elem->file;
+                }
+
+                g_list_free_full (create_date_list, g_free);
+        }
+
+        return selection;
+}
+
+static void
+cursor_next (QueryData           *data,
+             TrackerSparqlCursor *cursor)
+{
+        tracker_sparql_cursor_next_async (cursor,
+                                          NULL,
+                                          cursor_callback,
+                                          data);
+}
+
+static void
+cursor_callback (GObject      *object,
+                 GAsyncResult *result,
+                 gpointer      user_data)
+{
+        GHashTable *hash_table;
+        TrackerSparqlCursor *cursor;
+        gboolean success;
+        QueryData *data;
+        GError *error;
+        GList *l;
+        FileMetadata *metadata;
+        FileMetadata *metadata_clear;
+        const gchar *file_name;
+        const gchar *creation_date;
+        const gchar *equipment;
+        const gchar *season_number;
+        const gchar *episode_number;
+        const gchar *track_number;
+        const gchar *artist_name;
+        const gchar *title;
+
+        error = NULL;
+        metadata = NULL;
+
+        cursor = TRACKER_SPARQL_CURSOR (object);
+        data = user_data;
+        hash_table = data->hash_table;
+
+        success = tracker_sparql_cursor_next_finish (cursor, result, &error);
+        if (!success) {
+                g_clear_error (&error);
+                g_clear_object (&cursor);
+
+                nautilus_batch_rename_dialog_query_finished (data->dialog, data->hash_table, 
data->selection_metadata);
+
+                return;
+        }
+
+        creation_date = tracker_sparql_cursor_get_string (cursor, 1, NULL);
+        equipment = tracker_sparql_cursor_get_string (cursor, 2, NULL);
+        season_number = tracker_sparql_cursor_get_string (cursor, 3, NULL);
+        episode_number = tracker_sparql_cursor_get_string (cursor, 4, NULL);
+        track_number = tracker_sparql_cursor_get_string (cursor, 5, NULL);
+        artist_name = tracker_sparql_cursor_get_string (cursor, 6, NULL);
+        title = tracker_sparql_cursor_get_string (cursor, 7, NULL);
+
+        /* creation date used for sorting criteria */
+        if (creation_date == NULL) {
+                if (hash_table != NULL)
+                        g_hash_table_destroy (hash_table);
+
+                data->hash_table = NULL;
+                data->have_creation_date = FALSE;
+        } else {
+                if (data->have_creation_date){
+                        g_hash_table_insert (hash_table,
+                        g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)),
+                        GINT_TO_POINTER (g_hash_table_size (hash_table)));
+                }
+        }
+        file_name = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                metadata = l->data;
+
+                if (g_strcmp0 (file_name, metadata->file_name->str) == 0)
+                        break;
+        }
+
+        /* Metadata to be used in file name
+         * creation date */
+        if (data->have_creation_date) {
+                if (creation_date == NULL) {
+                        data->have_creation_date = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->creation_date, TRUE);
+                                metadata_clear->creation_date = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->creation_date,
+                                         creation_date);
+                }
+        }
+
+        /* equipment */
+        if (data->have_equipment) {
+                if (equipment == NULL) {
+                        data->have_equipment = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->equipment, TRUE);
+                                metadata_clear->equipment = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->equipment,
+                                         equipment);
+                }
+        }
+
+        /* season number */
+        if (data->have_season) {
+                if (season_number == NULL) {
+                        data->have_season = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->season, TRUE);
+                                metadata_clear->season = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->season,
+                                         season_number);
+                }
+        }
+
+        /* episode number */
+        if (data->have_episode_number) {
+                if (episode_number == NULL) {
+                        data->have_episode_number = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->episode_number, TRUE);
+                                metadata_clear->episode_number = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->episode_number,
+                                         episode_number);
+                }
+        }
+
+        /* track number */
+        if (data->have_track_number) {
+                if (track_number == NULL) {
+                        data->have_track_number = FALSE;
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->track_number, TRUE);
+                                metadata_clear->track_number = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->track_number,
+                                         track_number);
+                }
+        }
+
+        /* artist name */
+        if (data->have_artist_name) {
+                if (artist_name == NULL) {
+                        data->have_artist_name = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->artist_name, TRUE);
+                                metadata_clear->artist_name = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->artist_name,
+                                         artist_name);
+                }
+        }
+
+        /* title */
+        if (data->have_title) {
+                if (title == NULL) {
+                        data->have_title = FALSE;
+
+                        for (l = data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                g_string_free (metadata_clear->title, TRUE);
+                                metadata_clear->title = NULL;
+                        }
+                } else {
+                        g_string_append (metadata->title,
+                                         title);
+                }
+        }
+
+        /* Get next */
+        cursor_next (data, cursor);
+}
+
+static void
+batch_rename_dialog_query_callback (GObject      *object,
+                                    GAsyncResult *result,
+                                    gpointer      user_data)
+{
+        TrackerSparqlConnection *connection;
+        TrackerSparqlCursor *cursor;
+        QueryData *data;
+        GError *error;
+
+        error = NULL;
+
+        connection = TRACKER_SPARQL_CONNECTION (object);
+        data = user_data;
+
+        cursor = tracker_sparql_connection_query_finish (connection,
+                                                         result,
+                                                         &error);
+
+        if (error != NULL) {
+                g_error_free (error);
+
+                nautilus_batch_rename_dialog_query_finished (data->dialog,
+                                                             data->hash_table,
+                                                             data->selection_metadata);
+        } else {
+                cursor_next (data, cursor);
+        }
+}
+
+void
+check_metadata_for_selection (NautilusBatchRenameDialog *dialog,
+                              GList                     *selection)
+{
+        TrackerSparqlConnection *connection;
+        GString *query;
+        GHashTable *hash_table;
+        GList *l;
+        NautilusFile *file;
+        GError *error;
+        QueryData *data;
+        gchar *file_name;
+        FileMetadata *metadata;
+        GList *selection_metadata;
+
+        error = NULL;
+        selection_metadata = NULL;
+
+        query = g_string_new ("SELECT "
+                              "nfo:fileName(?file) "
+                              "nie:contentCreated(?file) "
+                              "nfo:model(nfo:equipment(?file)) "
+                              "nmm:season(?file) "
+                              "nmm:episodeNumber(?file) "
+                              "nmm:trackNumber(?file) "
+                              "nmm:artistName(nmm:performer(?file)) "
+                              "nie:title(?file) "
+                              "WHERE { ?file a nfo:FileDataObject. ");
+
+        g_string_append_printf (query,
+                                "FILTER(tracker:uri-is-parent('%s', nie:url(?file))) ",
+                                nautilus_file_get_parent_uri (NAUTILUS_FILE (selection->data)));
+
+        for (l = selection; l != NULL; l = l->next) {
+                file = NAUTILUS_FILE (l->data);
+                file_name = nautilus_file_get_name (file);
+
+                if (l == selection)
+                        g_string_append_printf (query,
+                                                "FILTER (nfo:fileName(?file) = '%s' ",
+                                                file_name);
+                else
+                        g_string_append_printf (query,
+                                                "|| nfo:fileName(?file) = '%s' ",
+                                                file_name);
+
+                metadata = g_new (FileMetadata, 1);
+                metadata->file_name = g_string_new (file_name);
+                metadata->creation_date = g_string_new ("");
+                metadata->equipment = g_string_new ("");
+                metadata->season = g_string_new ("");
+                metadata->episode_number = g_string_new ("");
+                metadata->track_number = g_string_new ("");
+                metadata->artist_name = g_string_new ("");
+                metadata->title = g_string_new ("");
+
+                selection_metadata = g_list_append (selection_metadata, metadata);
+
+                g_free (file_name);
+        }
+
+        g_string_append (query, ")} ORDER BY ASC(nie:contentCreated(?file))");
+
+        connection = tracker_sparql_connection_get (NULL, &error);
+        if (!connection) {
+                g_error_free (error);
+
+                return;
+        }
+
+        hash_table = g_hash_table_new_full (g_str_hash,
+                                            g_str_equal,
+                                            (GDestroyNotify) g_free,
+                                            NULL);
+
+        data = g_new (QueryData, 1);
+        data->hash_table = hash_table;
+        data->dialog = dialog;
+        data->selection_metadata = selection_metadata;
+
+        data->have_season = TRUE;
+        data->have_creation_date = TRUE;
+        data->have_artist_name = TRUE;
+        data->have_track_number = TRUE;
+        data->have_equipment = TRUE;
+        data->have_episode_number = TRUE;
+        data->have_title = TRUE;
+
+        /* Make an asynchronous query to the store */
+        tracker_sparql_connection_query_async (connection,
+                                               query->str,
+                                               NULL,
+                                               batch_rename_dialog_query_callback,
+                                               data);
+
+        g_object_unref (connection);
+        g_string_free (query, TRUE);
+}
+
+GList*
+distinct_file_parents (GList *selection)
+{
+        GList *result;
+        GList *l1;
+        GList *l2;
+        NautilusFile *file;
+        gboolean exists;
+        gchar *parent_uri;
+
+        result = NULL;
+
+        for (l1 = selection; l1 != NULL; l1 = l1->next) {
+                exists = FALSE;
+
+                file = NAUTILUS_FILE (l1->data);
+                parent_uri = nautilus_file_get_parent_uri (file);
+
+                for (l2 = result; l2 != NULL; l2 = l2->next)
+                        if (g_strcmp0 (parent_uri, l2->data) == 0) {
+                                exists = TRUE;
+                                break;
+                        }
+
+                if (!exists) {
+                        result = g_list_prepend (result, parent_uri);
+                } else {
+                        g_free (parent_uri);
+                }
+        }
+
+        return result;
+}
\ No newline at end of file
diff --git a/src/nautilus-batch-rename-utilities.h b/src/nautilus-batch-rename-utilities.h
new file mode 100644
index 0000000..5520890
--- /dev/null
+++ b/src/nautilus-batch-rename-utilities.h
@@ -0,0 +1,63 @@
+#ifndef NAUTILUS_BATCH_RENAME_UTILITIES_H
+#define NAUTILUS_BATCH_RENAME_UTILITIES_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <tracker-sparql.h>
+
+GList* batch_rename_dialog_get_new_names_list          (NautilusBatchRenameDialogMode  mode,
+                                                        GList                         *selection,
+                                                        GList                         *tags_list,
+                                                        GList                         *selection_metadata,
+                                                        gchar                         *entry_text,
+                                                        gchar                         *replace_text);
+
+GList* file_names_list_has_duplicates                      (NautilusBatchRenameDialog   *dialog,
+                                                            NautilusDirectory           *model,
+                                                            GList                       *names,
+                                                            GList                       *selection,
+                                                            GList                       *parents_list,
+                                                            GCancellable                *cancellable);
+
+GList* nautilus_batch_rename_dialog_sort        (GList                       *selection,
+                                                 SortingMode                  mode,
+                                                 GHashTable                  *creation_date_table);
+
+gint compare_files_by_last_modified             (gconstpointer a,
+                                                 gconstpointer b);
+
+gint compare_files_by_first_modified            (gconstpointer a,
+                                                 gconstpointer b);
+
+gint compare_files_by_name_descending           (gconstpointer a,
+                                                 gconstpointer b);
+
+gint compare_files_by_name_ascending            (gconstpointer a,
+                                                 gconstpointer b);
+
+gint compare_files_by_first_created             (gconstpointer a,
+                                                 gconstpointer b);
+
+gint compare_files_by_last_created              (gconstpointer a,
+                                                 gconstpointer b);
+
+void check_metadata_for_selection               (NautilusBatchRenameDialog *dialog,
+                                                 GList                     *selection);
+
+gboolean selection_has_single_parent            (GList *selection);
+
+void string_free                                (gpointer mem);
+
+void conflict_data_free                         (gpointer mem);
+
+GList* distinct_file_parents                    (GList *selection);
+
+gboolean file_name_conflicts_with_results       (GList        *selection,
+                                                 GList        *new_names,
+                                                 GString      *old_name,
+                                                 gchar        *parent_uri);
+
+GString* batch_rename_dialog_replace_label_text        (gchar             *string,
+                                                        const gchar       *substr);
+
+#endif /* NAUTILUS_BATCH_RENAME_UTILITIES_H */
\ No newline at end of file
diff --git a/src/nautilus-file-private.h b/src/nautilus-file-private.h
index c498651..f0ecf72 100644
--- a/src/nautilus-file-private.h
+++ b/src/nautilus-file-private.h
@@ -214,6 +214,9 @@ struct NautilusFileDetails
 
 typedef struct {
        NautilusFile *file;
+       GList *files;
+       gint renamed_files;
+       gint skipped_files;
        GCancellable *cancellable;
        NautilusFileOperationCallback callback;
        gpointer callback_data;
diff --git a/src/nautilus-file-undo-operations.c b/src/nautilus-file-undo-operations.c
index e880547..7edefcb 100644
--- a/src/nautilus-file-undo-operations.c
+++ b/src/nautilus-file-undo-operations.c
@@ -994,6 +994,189 @@ nautilus_file_undo_info_rename_set_data_post (NautilusFileUndoInfoRename *self,
        self->priv->new_file = g_object_ref (new_file);
 }
 
+/* batch rename */
+G_DEFINE_TYPE (NautilusFileUndoInfoBatchRename, nautilus_file_undo_info_batch_rename, 
NAUTILUS_TYPE_FILE_UNDO_INFO);
+
+struct _NautilusFileUndoInfoBatchRenameDetails {
+        GList *old_files;
+        GList *new_files;
+        GList *old_display_names;
+        GList *new_display_names;
+};
+
+static void
+batch_rename_strings_func (NautilusFileUndoInfo *info,
+                           gchar               **undo_label,
+                           gchar               **undo_description,
+                           gchar               **redo_label,
+                           gchar               **redo_description)
+{
+        NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+        *undo_description = g_strdup_printf (_("Batch rename '%d' files"),
+                                             g_list_length (self->priv->new_files));
+        *redo_description = g_strdup_printf (_("Batch rename '%d' files"),
+                                             g_list_length (self->priv->new_files));
+
+        *undo_label = g_strdup (_("_Undo Batch rename"));
+        *redo_label = g_strdup (_("_Redo Batch rename"));
+}
+
+static void
+batch_rename_redo_func (NautilusFileUndoInfo *info,
+                        GtkWindow *parent_window)
+{
+        NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+        GList *l, *files;
+        NautilusFile *file;
+        GFile *old_file;
+
+        files = NULL;
+
+        for (l = self->priv->old_files; l != NULL; l = l->next) {
+                old_file = l->data;
+
+                file = nautilus_file_get (old_file);
+                files = g_list_append (files, file);
+        }
+
+        nautilus_file_batch_rename (files, self->priv->new_display_names, file_undo_info_operation_callback, 
self);
+}
+
+static void
+batch_rename_undo_func (NautilusFileUndoInfo *info,
+                        GtkWindow *parent_window)
+{
+        NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+        GList *l, *files;
+        NautilusFile *file;
+        GFile *new_file;
+
+        files = NULL;
+
+        for (l = self->priv->new_files; l != NULL; l = l->next) {
+                new_file = l->data;
+
+                file = nautilus_file_get (new_file);
+                files = g_list_append (files, file);
+        }
+
+        nautilus_file_batch_rename (files, self->priv->old_display_names, file_undo_info_operation_callback, 
self);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_init (NautilusFileUndoInfoBatchRename *self)
+{
+        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_file_undo_info_batch_rename_get_type (),
+                                                  NautilusFileUndoInfoBatchRenameDetails);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_finalize (GObject *obj)
+{
+        GList *l;
+        GFile *file;
+        GString *string;
+        NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (obj);
+
+        for (l = self->priv->new_files; l != NULL; l = l->next){
+                file = l->data;
+
+                g_clear_object (&file);
+        }
+
+        for (l = self->priv->old_files; l != NULL; l = l->next){
+                file = l->data;
+
+                g_clear_object (&file);
+        }
+
+        for (l = self->priv->new_display_names; l != NULL; l = l->next) {
+                string = l->data;
+
+                g_string_free (string, TRUE);
+        }
+
+        for (l = self->priv->old_display_names; l != NULL; l = l->next) {
+                string = l->data;
+
+                g_string_free (string, TRUE);
+        }
+
+        g_list_free (self->priv->new_files);
+        g_list_free (self->priv->old_files);
+        g_list_free (self->priv->new_display_names);
+        g_list_free (self->priv->old_display_names);
+
+        G_OBJECT_CLASS (nautilus_file_undo_info_batch_rename_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_class_init (NautilusFileUndoInfoBatchRenameClass *klass)
+{
+        GObjectClass *oclass = G_OBJECT_CLASS (klass);
+        NautilusFileUndoInfoClass *iclass = NAUTILUS_FILE_UNDO_INFO_CLASS (klass);
+
+        oclass->finalize = nautilus_file_undo_info_batch_rename_finalize;
+
+        iclass->undo_func = batch_rename_undo_func;
+        iclass->redo_func = batch_rename_redo_func;
+        iclass->strings_func = batch_rename_strings_func;
+
+        g_type_class_add_private (klass, sizeof (NautilusFileUndoInfoBatchRenameDetails));
+}
+
+NautilusFileUndoInfo *
+nautilus_file_undo_info_batch_rename_new (gint item_count)
+{
+        return g_object_new (NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME,
+                             "op-type", NAUTILUS_FILE_UNDO_OP_BATCH_RENAME,
+                             "item-count", item_count,
+                             NULL);
+}
+
+void
+nautilus_file_undo_info_batch_rename_set_data_pre (NautilusFileUndoInfoBatchRename *self,
+                                                   GList                           *old_files)
+{
+        GList *l;
+        GString *old_name;
+        GFile *file;
+
+        self->priv->old_files = old_files;
+        self->priv->old_display_names = NULL;
+
+        for (l = old_files; l != NULL; l = l->next) {
+                file = l->data;
+
+                old_name = g_string_new (g_file_get_basename (file));
+
+                self->priv->old_display_names = g_list_append (self->priv->old_display_names, old_name);
+        }
+}
+
+void
+nautilus_file_undo_info_batch_rename_set_data_post (NautilusFileUndoInfoBatchRename *self,
+                                                    GList                           *new_files)
+{
+        GList *l;
+        GString *new_name;
+        GFile *file;
+
+        self->priv->new_files = new_files;
+        self->priv->new_display_names = NULL;
+
+        for (l = new_files; l != NULL; l = l->next) {
+                file = l->data;
+
+                new_name = g_string_new (g_file_get_basename (file));
+
+                self->priv->new_display_names = g_list_append (self->priv->new_display_names, new_name);
+        }
+}
+
 /* trash */
 G_DEFINE_TYPE (NautilusFileUndoInfoTrash, nautilus_file_undo_info_trash, NAUTILUS_TYPE_FILE_UNDO_INFO)
 
diff --git a/src/nautilus-file-undo-operations.h b/src/nautilus-file-undo-operations.h
index 9402469..630443f 100644
--- a/src/nautilus-file-undo-operations.h
+++ b/src/nautilus-file-undo-operations.h
@@ -34,6 +34,7 @@ typedef enum {
        NAUTILUS_FILE_UNDO_OP_DUPLICATE,
        NAUTILUS_FILE_UNDO_OP_MOVE,
        NAUTILUS_FILE_UNDO_OP_RENAME,
+       NAUTILUS_FILE_UNDO_OP_BATCH_RENAME,
        NAUTILUS_FILE_UNDO_OP_CREATE_EMPTY_FILE,
        NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE,
        NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER,
@@ -188,6 +189,34 @@ void nautilus_file_undo_info_rename_set_data_pre (NautilusFileUndoInfoRename *se
 void nautilus_file_undo_info_rename_set_data_post (NautilusFileUndoInfoRename *self,
                                                   GFile                      *new_file);
 
+/* batch rename */
+#define NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME         (nautilus_file_undo_info_batch_rename_get_type ())
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRename))
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRenameClass))
+#define NAUTILUS_IS_FILE_UNDO_INFO_BATCH_RENAME(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME))
+#define NAUTILUS_IS_FILE_UNDO_INFO_BATCH_RENAME_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME))
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRenameClass))
+
+typedef struct _NautilusFileUndoInfoBatchRename      NautilusFileUndoInfoBatchRename;
+typedef struct _NautilusFileUndoInfoBatchRenameClass NautilusFileUndoInfoBatchRenameClass;
+typedef struct _NautilusFileUndoInfoBatchRenameDetails NautilusFileUndoInfoBatchRenameDetails;
+
+struct _NautilusFileUndoInfoBatchRename {
+       NautilusFileUndoInfo parent;
+       NautilusFileUndoInfoBatchRenameDetails *priv;
+};
+
+struct _NautilusFileUndoInfoBatchRenameClass {
+       NautilusFileUndoInfoClass parent_class;
+};
+
+GType nautilus_file_undo_info_batch_rename_get_type (void) G_GNUC_CONST;
+NautilusFileUndoInfo *nautilus_file_undo_info_batch_rename_new (gint item_count);
+void nautilus_file_undo_info_batch_rename_set_data_pre (NautilusFileUndoInfoBatchRename *self,
+                                                       GList                           *old_files);
+void nautilus_file_undo_info_batch_rename_set_data_post (NautilusFileUndoInfoBatchRename *self,
+                                                        GList                           *new_files);
+
 /* trash */
 #define NAUTILUS_TYPE_FILE_UNDO_INFO_TRASH         (nautilus_file_undo_info_trash_get_type ())
 #define NAUTILUS_FILE_UNDO_INFO_TRASH(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_TRASH, NautilusFileUndoInfoTrash))
diff --git a/src/nautilus-file-utilities.c b/src/nautilus-file-utilities.c
index 262f485..6ed4127 100644
--- a/src/nautilus-file-utilities.c
+++ b/src/nautilus-file-utilities.c
@@ -1221,3 +1221,19 @@ nautilus_ensure_extension_points (void)
 }
 
 #endif /* !NAUTILUS_OMIT_SELF_CHECK */
+
+gboolean
+nautilus_file_can_rename_files (GList *files)
+{
+    GList *l;
+    NautilusFile *file;
+
+    for (l = files; l != NULL; l = l->next) {
+        file = NAUTILUS_FILE (l->data);
+
+        if (!nautilus_file_can_rename (file))
+            return FALSE;
+    }
+
+    return TRUE;
+}
diff --git a/src/nautilus-file-utilities.h b/src/nautilus-file-utilities.h
index 4b26a8c..c4d863f 100644
--- a/src/nautilus-file-utilities.h
+++ b/src/nautilus-file-utilities.h
@@ -119,4 +119,6 @@ char * nautilus_get_common_filename_prefix_from_filenames (GList *filename_list,
 void nautilus_ensure_extension_points (void);
 void nautilus_ensure_extension_builtins (void);
 
+gboolean nautilus_file_can_rename_files (GList *files);
+
 #endif /* NAUTILUS_FILE_UTILITIES_H */
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index 60e2f9b..d01a0e5 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -1648,15 +1648,36 @@ nautilus_file_operation_new (NautilusFile *file,
 static void
 nautilus_file_operation_remove (NautilusFileOperation *op)
 {
+        GList *l;
+        NautilusFile *file;
+
        op->file->details->operations_in_progress = g_list_remove
                (op->file->details->operations_in_progress, op);
+
+
+        for (l = op->files; l != NULL; l = l->next) {
+                file = NAUTILUS_FILE (l->data);
+                file->details->operations_in_progress = g_list_remove
+                        (file->details->operations_in_progress, op);
+        }
 }
 
 void
 nautilus_file_operation_free (NautilusFileOperation *op)
 {
+        NautilusFile *file;
+        GList *l;
+
        nautilus_file_operation_remove (op);
-       nautilus_file_unref (op->file);
+
+        if (op->files == NULL)
+                nautilus_file_unref (op->file);
+        else
+                for (l = op->files; l != NULL; l = l->next) {
+                        file = NAUTILUS_FILE (l->data);
+                        nautilus_file_unref (file);
+                }
+
        g_object_unref (op->cancellable);
        if (op->free_data) {
                op->free_data (op->data);
@@ -1680,7 +1701,10 @@ nautilus_file_operation_complete (NautilusFileOperation *op,
         * as "changing back".
         */
        nautilus_file_operation_remove (op);
-       nautilus_file_changed (op->file);
+
+        if (op->files == NULL)
+                nautilus_file_changed (op->file);
+
        if (op->callback) {
                (* op->callback) (op->file, result_file, error, op->callback_data);
        }
@@ -1759,6 +1783,86 @@ rename_get_info_callback (GObject *source_object,
        }
 }
 
+typedef struct {
+        NautilusFileOperation *op;
+        NautilusFile *file;
+} BatchRenameData;
+
+static void
+batch_rename_get_info_callback (GObject      *source_object,
+                                GAsyncResult *res,
+                                gpointer      callback_data)
+{
+        NautilusFileOperation *op;
+        NautilusDirectory *directory;
+        NautilusFile *existing_file;
+        char *old_uri;
+        char *new_uri;
+        const char *new_name;
+        GFileInfo *new_info;
+        GError *error;
+        BatchRenameData *data;
+
+        data = callback_data;
+
+        op = data->op;
+        op->file = data->file;
+
+        error = NULL;
+        new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
+        if (new_info != NULL) {
+                old_uri = nautilus_file_get_uri (op->file);
+
+                new_name = g_file_info_get_name (new_info);
+
+                directory = op->file->details->directory;
+
+                /* If there was another file by the same name in this
+                 * directory and it is not the same file that we are
+                 * renaming, mark it gone.
+                 */
+                existing_file = nautilus_directory_find_file_by_name (directory, new_name);
+                if (existing_file != NULL && existing_file != op->file) {
+                        nautilus_file_mark_gone (existing_file);
+                        nautilus_file_changed (existing_file);
+                }
+
+                update_info_and_name (op->file, new_info);
+
+                new_uri = nautilus_file_get_uri (op->file);
+                nautilus_directory_moved (old_uri, new_uri);
+                g_free (new_uri);
+                g_free (old_uri);
+
+                /* the rename could have affected the display name if e.g.
+                 * we're in a vfolder where the name comes from a desktop file
+                 * and a rename affects the contents of the desktop file.
+                 */
+                if (op->file->details->got_custom_display_name) {
+                        nautilus_file_invalidate_attributes (op->file,
+                                                             NAUTILUS_FILE_ATTRIBUTE_INFO |
+                                                             NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
+                }
+
+                g_object_unref (new_info);
+        }
+
+        op->renamed_files++;
+
+        if (op->renamed_files + op->skipped_files == g_list_length (op->files)) {
+                nautilus_file_operation_complete (op, NULL, error);
+        }
+
+        if (op->files == NULL)
+                nautilus_file_operation_complete (op, NULL, error);
+
+        g_free (data);
+
+        if (error) {
+                g_error_free (error);
+        }
+}
+
 static void
 rename_callback (GObject *source_object,
                 GAsyncResult *res,
@@ -1779,7 +1883,6 @@ rename_callback (GObject *source_object,
                        nautilus_file_undo_info_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_RENAME 
(op->undo_info),
                                                                      new_file);
                }
-
                g_file_query_info_async (new_file,
                                         NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
                                         0,
@@ -1812,6 +1915,216 @@ nautilus_file_rename (NautilusFile                  *file,
                                                                  callback_data);
 }
 
+static gchar*
+nautilus_file_can_rename_file (NautilusFile                  *file,
+                               const char                    *new_name,
+                               NautilusFileOperationCallback  callback,
+                               gpointer                       callback_data)
+{
+        GError *error;
+        gboolean is_renameable_desktop_file;
+        gboolean success;
+        gboolean name_changed;
+        gchar *new_file_name;
+        gchar *uri;
+        gchar *old_name;
+
+        is_renameable_desktop_file =
+                is_desktop_file (file) && can_rename_desktop_file (file);
+
+        /* Return an error for incoming names containing path separators.
+         * But not for .desktop files as '/' are allowed for them */
+        if (strstr (new_name, "/") != NULL && !is_renameable_desktop_file) {
+                error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                     _("Slashes are not allowed in filenames"));
+                if (callback != NULL)
+                        (* callback) (file, NULL, error, callback_data);
+                g_error_free (error);
+                return NULL;
+        }
+
+        /* Can't rename a file that's already gone.
+         * We need to check this here because there may be a new
+         * file with the same name.
+         */
+        if (nautilus_file_rename_handle_file_gone (file, callback, callback_data)) {
+                return NULL;
+        }
+
+        /* Test the name-hasn't-changed case explicitly, for two reasons.
+         * (1) rename returns an error if new & old are same.
+         * (2) We don't want to send file-changed signal if nothing changed.
+         */
+        if (!is_renameable_desktop_file &&
+            name_is (file, new_name)) {
+                if (callback != NULL)
+                        (* callback) (file, NULL, NULL, callback_data);
+                return NULL;
+        }
+
+        /* Self-owned files can't be renamed. Test the name-not-actually-changing
+         * case before this case.
+         */
+        if (nautilus_file_is_self_owned (file)) {
+                /* Claim that something changed even if the rename
+                 * failed. This makes it easier for some clients who
+                 * see the "reverting" to the old name as "changing
+                 * back".
+                 */
+                nautilus_file_changed (file);
+                error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                     _("Toplevel files cannot be renamed"));
+
+                if (callback != NULL)
+                        (* callback) (file, NULL, error, callback_data);
+                g_error_free (error);
+
+                return NULL;
+        }
+
+        if (is_renameable_desktop_file) {
+                /* Don't actually change the name if the new name is the same.
+                 * This helps for the vfolder method where this can happen and
+                 * we want to minimize actual changes
+                 */
+                uri = nautilus_file_get_uri (file);
+                old_name = nautilus_link_local_get_text (uri);
+                if (old_name != NULL && strcmp (new_name, old_name) == 0) {
+                        success = TRUE;
+                        name_changed = FALSE;
+                } else {
+                        success = nautilus_link_local_set_text (uri, new_name);
+                        name_changed = TRUE;
+                }
+                g_free (old_name);
+                g_free (uri);
+
+                if (!success) {
+                        error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+                                             _("Probably the content of the file is an invalid desktop file 
format"));
+                        if (callback != NULL)
+                                (* callback) (file, NULL, error, callback_data);
+                        g_error_free (error);
+                        return NULL;
+                }
+                new_file_name = g_strdup_printf ("%s.desktop", new_name);
+                new_file_name = g_strdelimit (new_file_name, "/", '-');
+
+                if (name_is (file, new_file_name)) {
+                        if (name_changed) {
+                                nautilus_file_invalidate_attributes (file,
+                                                                     NAUTILUS_FILE_ATTRIBUTE_INFO |
+                                                                     NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
+                        }
+
+                        if (callback != NULL)
+                                (* callback) (file, NULL, NULL, callback_data);
+                        g_free (new_file_name);
+                        return NULL;
+                }
+        } else {
+                new_file_name = g_strdup (new_name);
+        }
+
+        return new_file_name;
+}
+
+static void
+real_batch_rename (GList                         *files,
+                   GList                         *new_names,
+                   NautilusFileOperationCallback  callback,
+                   gpointer                       callback_data)
+{
+        GList *l1, *l2, *old_files, *new_files;
+        NautilusFileOperation *op;
+        GFile *location;
+        gchar *new_file_name;
+        GString *new_name;
+        NautilusFile *file;
+        GError *error;
+        GFile *new_file;
+        BatchRenameData *data;
+
+        error = NULL;
+        old_files = NULL;
+        new_files = NULL;
+
+        /* Set up a batch renaming operation. */
+        op = nautilus_file_operation_new (files->data, callback, callback_data);
+        op->files = files;
+        op->renamed_files = 0;
+        op->skipped_files = 0;
+
+        for (l1 = files->next; l1 != NULL; l1 = l1->next) {
+                file = NAUTILUS_FILE (l1->data);
+
+                file->details->operations_in_progress = g_list_prepend 
(file->details->operations_in_progress,
+                                                                        op);
+        }
+
+        for (l1 = files, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+                file = NAUTILUS_FILE (l1->data);
+                new_name = l2->data;
+
+                location = nautilus_file_get_location (file);
+                old_files = g_list_append (old_files, location);
+
+                new_file_name = nautilus_file_can_rename_file (file,
+                                                               new_name->str,
+                                                               callback,
+                                                               callback_data);
+
+                if (new_file_name == NULL) {
+                     op->skipped_files++;
+
+                     new_file = nautilus_file_get_location (file);
+                     new_files = g_list_append (new_files, new_file);
+
+                     continue;
+                }
+
+                g_assert (G_IS_FILE (location));
+
+                /* Do the renaming. */
+                new_file = g_file_set_display_name (location,
+                                                    new_file_name,
+                                                    op->cancellable,
+                                                    &error);
+
+                data = g_new0 (BatchRenameData, 1);
+                data->op = op;
+                data->file = file;
+
+                new_files = g_list_append (new_files, new_file);
+
+                g_file_query_info_async (new_file,
+                                         NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
+                                         0,
+                                         G_PRIORITY_DEFAULT,
+                                         op->cancellable,
+                                         batch_rename_get_info_callback,
+                                         data);
+
+                if (error != NULL) {
+                        g_error_free (error);
+                        error = NULL;
+                }
+        }
+
+        /* Tell the undo manager a batch rename is taking place */
+        if (!nautilus_file_undo_manager_is_operating ()) {
+                op->undo_info = nautilus_file_undo_info_batch_rename_new (g_list_length (new_files));
+
+                nautilus_file_undo_info_batch_rename_set_data_pre (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME 
(op->undo_info),
+                                                                   old_files);
+
+                nautilus_file_undo_info_batch_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME 
(op->undo_info),
+                                                                    new_files);
+
+                nautilus_file_undo_manager_set_action (op->undo_info);
+        }
+}
+
 gboolean
 nautilus_file_rename_handle_file_gone (NautilusFile                  *file,
                                        NautilusFileOperationCallback  callback,
@@ -1820,7 +2133,7 @@ nautilus_file_rename_handle_file_gone (NautilusFile                  *file,
        GError *error;
 
        if (nautilus_file_is_gone (file)) {
-               /* Claim that something changed even if the rename
+               /* Claim that something changed even if the rename
                 * failed. This makes it easier for some clients who
                 * see the "reverting" to the old name as "changing
                 * back".
@@ -1836,6 +2149,18 @@ nautilus_file_rename_handle_file_gone (NautilusFile                  *file,
   return FALSE;
 }
 
+void
+nautilus_file_batch_rename (GList                         *files,
+                            GList                         *new_names,
+                            NautilusFileOperationCallback  callback,
+                            gpointer                       callback_data)
+{
+        real_batch_rename (files,
+                           new_names,
+                           callback,
+                           callback_data);
+}
+
 static void
 real_rename (NautilusFile                  *file,
              const char                    *new_name,
@@ -1843,107 +2168,21 @@ real_rename (NautilusFile                  *file,
              gpointer                       callback_data)
 {
        NautilusFileOperation *op;
-       char *uri;
        char *old_name;
        char *new_file_name;
-       gboolean success, name_changed;
-       gboolean is_renameable_desktop_file;
        GFile *location;
-       GError *error;
 
        g_return_if_fail (NAUTILUS_IS_FILE (file));
        g_return_if_fail (new_name != NULL);
        g_return_if_fail (callback != NULL);
 
-       is_renameable_desktop_file =
-               is_desktop_file (file) && can_rename_desktop_file (file);
-       
-       /* Return an error for incoming names containing path separators.
-        * But not for .desktop files as '/' are allowed for them */
-       if (strstr (new_name, "/") != NULL && !is_renameable_desktop_file) {
-               error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
-                                    _("Slashes are not allowed in filenames"));
-               (* callback) (file, NULL, error, callback_data);
-               g_error_free (error);
-               return;
-       }
-       
-       /* Can't rename a file that's already gone.
-        * We need to check this here because there may be a new
-        * file with the same name.
-        */
-        if (nautilus_file_rename_handle_file_gone (file, callback, callback_data)) {
-                return;
-        }
-       /* Test the name-hasn't-changed case explicitly, for two reasons.
-        * (1) rename returns an error if new & old are same.
-        * (2) We don't want to send file-changed signal if nothing changed.
-        */
-       if (!is_renameable_desktop_file &&
-           name_is (file, new_name)) {
-               (* callback) (file, NULL, NULL, callback_data);
-               return;
-       }
+       new_file_name = nautilus_file_can_rename_file (file,
+                                                      new_name,
+                                                      callback,
+                                                      callback_data);
 
-       /* Self-owned files can't be renamed. Test the name-not-actually-changing
-        * case before this case.
-        */
-       if (nautilus_file_is_self_owned (file)) {
-               /* Claim that something changed even if the rename
-                * failed. This makes it easier for some clients who
-                * see the "reverting" to the old name as "changing
-                * back".
-                */
-               nautilus_file_changed (file);
-               error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                                    _("Toplevel files cannot be renamed"));
-               
-               (* callback) (file, NULL, error, callback_data);
-               g_error_free (error);
+       if (new_file_name == NULL)
                return;
-       }
-
-       if (is_renameable_desktop_file) {
-               /* Don't actually change the name if the new name is the same.
-                * This helps for the vfolder method where this can happen and
-                * we want to minimize actual changes
-                */
-               uri = nautilus_file_get_uri (file);
-               old_name = nautilus_link_local_get_text (uri);
-               if (old_name != NULL && strcmp (new_name, old_name) == 0) {
-                       success = TRUE;
-                       name_changed = FALSE;
-               } else {
-                       success = nautilus_link_local_set_text (uri, new_name);
-                       name_changed = TRUE;
-               }
-               g_free (old_name);
-               g_free (uri);
-
-               if (!success) {
-                       error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
-                                            _("Probably the content of the file is an invalid desktop file 
format"));
-                       (* callback) (file, NULL, error, callback_data);
-                       g_error_free (error);
-                       return;
-               }
-               new_file_name = g_strdup_printf ("%s.desktop", new_name);
-               new_file_name = g_strdelimit (new_file_name, "/", '-');
-
-               if (name_is (file, new_file_name)) {
-                       if (name_changed) {
-                               nautilus_file_invalidate_attributes (file,
-                                                                    NAUTILUS_FILE_ATTRIBUTE_INFO |
-                                                                    NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
-                       }
-
-                       (* callback) (file, NULL, NULL, callback_data);
-                       g_free (new_file_name);
-                       return;
-               }
-       } else {
-               new_file_name = g_strdup (new_name);
-       }
 
        /* Set up a renaming operation. */
        op = nautilus_file_operation_new (file, callback, callback_data);
diff --git a/src/nautilus-file.h b/src/nautilus-file.h
index fe226c0..7552dff 100644
--- a/src/nautilus-file.h
+++ b/src/nautilus-file.h
@@ -324,6 +324,10 @@ void                    nautilus_file_rename                            (Nautilu
                                                                         const char                     
*new_name,
                                                                         NautilusFileOperationCallback   
callback,
                                                                         gpointer                        
callback_data);
+void                    nautilus_file_batch_rename                      (GList                          
*files,
+                                                                         GList                          
*new_names,
+                                                                         NautilusFileOperationCallback   
callback,
+                                                                         gpointer                        
callback_data);
 void                    nautilus_file_cancel                            (NautilusFile                   
*file,
                                                                         NautilusFileOperationCallback   
callback,
                                                                         gpointer                        
callback_data);
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index cd7aa27..b1acfe8 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -28,6 +28,8 @@
 #include "nautilus-files-view.h"
 
 #include "nautilus-application.h"
+#include "nautilus-batch-rename-dialog.h"
+#include "nautilus-batch-rename-utilities.h"
 #include "nautilus-error-reporting.h"
 #include "nautilus-file-undo-manager.h"
 #include "nautilus-floating-bar.h"
@@ -5566,6 +5568,7 @@ real_action_rename (NautilusFilesView *view)
 {
         NautilusFile *file;
         GList *selection;
+        GtkWidget *dialog;
 
         g_assert (NAUTILUS_IS_FILES_VIEW (view));
 
@@ -5576,6 +5579,21 @@ real_action_rename (NautilusFilesView *view)
                 if (selection->next != NULL) {
                         if (have_bulk_rename_tool ()) {
                                 invoke_external_bulk_rename_utility (view, selection);
+                        } else {
+                                GdkCursor *cursor;
+                                GdkDisplay *display;
+
+                                display = gtk_widget_get_display (GTK_WIDGET (nautilus_files_view_get_window 
(view)));
+                                cursor = gdk_cursor_new_from_name (display, "progress");
+                                gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET 
(nautilus_files_view_get_window (view))),
+                                                       cursor);
+                                g_object_unref (cursor);
+
+                                dialog = nautilus_batch_rename_dialog_new (nautilus_files_view_get_selection 
(NAUTILUS_VIEW (view)),
+                                                                           nautilus_files_view_get_model 
(view),
+                                                                           nautilus_files_view_get_window 
(view));
+
+                                gtk_widget_show (GTK_WIDGET (dialog));
                         }
                 } else {
                         file = NAUTILUS_FILE (selection->data);
@@ -6625,8 +6643,12 @@ real_update_actions_state (NautilusFilesView *view)
         action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
                                              "rename");
         if (selection_count > 1) {
-                g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
-                                             have_bulk_rename_tool ());
+                if (have_bulk_rename_tool())
+                    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                                 have_bulk_rename_tool ());
+                else
+                    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                                 nautilus_file_can_rename_files (selection));
         } else {
                 g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
                                              selection_count == 1 &&
diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css
index bc8e596..3dbe855 100644
--- a/src/resources/css/Adwaita.css
+++ b/src/resources/css/Adwaita.css
@@ -169,3 +169,18 @@
  * always allocates at least 1 pixel */
 searchbar { border-top: 1px solid @borders; }
 .searchbar-container { margin-top: -1px; }
+
+@define-color conflict_bg #fef6b6;
+
+.conflict-row {
+    background: @conflict_bg;
+}
+
+.conflict-row:hover {
+    background-color: shade(@conflict_bg, 0.9);
+}
+
+.conflict-row:selected {
+  background: @theme_selected_bg_color;
+  color: @theme_selected_fg_color;
+}
\ No newline at end of file
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index 05b9a4d..53682b6 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -18,6 +18,7 @@
     <file>ui/nautilus-no-search-results.ui</file>
     <file>ui/nautilus-folder-is-empty.ui</file>
     <file>gtk/help-overlay.ui</file>
+    <file>ui/nautilus-batch-rename-dialog.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesview.ui">../gtk/nautilusgtkplacesview.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesviewrow.ui">../gtk/nautilusgtkplacesviewrow.ui</file>
     <file alias="icons/thumbnail_frame.png">../../icons/thumbnail_frame.png</file>
diff --git a/src/resources/ui/nautilus-batch-rename-dialog.ui 
b/src/resources/ui/nautilus-batch-rename-dialog.ui
new file mode 100644
index 0000000..bacce60
--- /dev/null
+++ b/src/resources/ui/nautilus-batch-rename-dialog.ui
@@ -0,0 +1,495 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="NautilusBatchRenameDialog" parent="GtkDialog">
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="destroy_with_parent">True</property>
+    <signal name="response" handler="batch_rename_dialog_on_response"/>
+    <child type="action">
+      <object class="GtkButton" id="cancel_button">
+        <property name="label" translatable="yes">_Cancel</property>
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="rename_button">
+        <property name="label" translatable="yes">_Rename</property>
+        <property name="visible">True</property>
+        <property name="use_underline">True</property>
+        <property name="can_default">True</property>
+        <style>
+          <class name="suggested-action"/>
+        </style>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="ok" default="true">rename_button</action-widget>
+      <action-widget response="cancel">cancel_button</action-widget>
+    </action-widgets>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="margin">0</property>
+            <property name="row-spacing">6</property>
+            <property name="column-spacing">6</property>
+            <property name="hexpand">True</property>
+            <property name="row-homogeneous">False</property>
+            <property name="column-homogeneous">False</property>
+            <child>
+              <object class="GtkBox">
+                <property name="orientation">horizontal</property>
+                <property name="spacing">15</property>
+                <property name="visible">True</property>
+                <property name="halign">center</property>
+                <property name="margin">20</property>
+                <child>
+                  <object class="GtkRadioButton" id="format_mode_button">
+                    <property name="label" translatable="yes">_Format mode</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="active">True</property>
+                    <signal name="toggled" handler="batch_rename_dialog_mode_changed" swapped="yes" />
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="replace_mode_button">
+                    <property name="label" translatable="yes">Find and replace _text</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="active">True</property>
+                    <property name="group">format_mode_button</property>
+                    <signal name="toggled" handler="batch_rename_dialog_mode_changed" swapped="yes" />
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">3</property>
+                <property name="top-attach">0</property>
+                <property name="width">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkStack" id="mode_stack">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="vhomogeneous">False</property>
+                <property name="hhomogeneous">True</property>
+                <property name="transition_type">crossfade</property>
+                <property name="transition_duration">100</property>
+                <child>
+                  <object class="GtkGrid" id="format_stack_child">
+                    <property name="visible">True</property>
+                    <property name="margin-left">40</property>
+                    <property name="margin-right">40</property>
+                    <property name="margin-top">0</property>
+                    <property name="margin-bottom">10</property>
+                    <property name="row-spacing">15</property>
+                    <property name="column-spacing">6</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="orientation">horizontal</property>
+                        <property name="spacing">0</property>
+                        <property name="visible">True</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkEntry" id="name_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="has_focus">True</property>
+                            <property name="width_request">400</property>
+                            <property name="hexpand">False</property>
+                            <property name="activates-default">True</property>
+                            <signal name="changed" handler="file_names_widget_entry_on_changed" 
swapped="yes" />
+                            <signal name="activate" handler="file_names_widget_on_activate" swapped="yes" />
+                            <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkToggleButton" id="add_button">
+                            <property name="visible">True</property>
+                            <signal name="toggled" handler="add_button_clicked" swapped="yes" />
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="orientation">horizontal</property>
+                                <property name="spacing">0</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="icon-name">list-add-symbolic</property>
+                                    <property name="icon-size">1</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Add</property>
+                                    <property name="can_focus">False</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                            <style>
+                              <class name="linked"/>
+                            </style>
+                      </object>
+                      <packing>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">0</property>
+                        <property name="width">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="numbering_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Automatic Numbering Order</property>
+                        <property name="can_focus">False</property>
+                        <property name="height-request">35</property>
+                        <property name="sensitive">False</property>
+                      </object>
+                      <packing>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">1</property>
+                        <property name="width">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToggleButton" id="numbering_order_button">
+                        <property name="visible">True</property>
+                        <signal name="toggled" handler="numbering_order_button_clicked" swapped="yes" />
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">15</property>
+                            <child>
+                              <object class="GtkLabel" id="numbering_order_label">
+                                <property name="visible">True</property>
+                                <property name="width-request">180</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Original name 
(Ascending)</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkImage" id="action_icon">
+                                <property name="visible">True</property>
+                                <property name="icon-name">pan-down-symbolic</property>
+                                <property name="icon-size">1</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left-attach">4</property>
+                        <property name="top-attach">1</property>
+                        <property name="width">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="name">format</property>
+                    <property name="title" translatable="yes">Format</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid" id="replace_stack_child">
+                    <property name="visible">True</property>
+                    <property name="margin-left">40</property>
+                    <property name="margin-right">40</property>
+                    <property name="margin-top">0</property>
+                    <property name="margin-bottom">10</property>
+                    <property name="row-spacing">16</property>
+                    <property name="column-spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="existing_text_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Existing Text</property>
+                        <property name="can_focus">False</property>
+                        <property name="sensitive">False</property>
+                      </object>
+                        <packing>
+                          <property name="left-attach">0</property>
+                          <property name="top-attach">0</property>
+                          <property name="width">1</property>
+                        </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="find_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="width_request">375</property>
+                        <property name="hexpand">False</property>
+                        <property name="activates-default">True</property>
+                        <signal name="changed" handler="file_names_widget_entry_on_changed" swapped="yes" />
+                        <signal name="activate" handler="file_names_widget_on_activate" swapped="yes" />
+                      </object>
+                        <packing>
+                          <property name="left-attach">1</property>
+                          <property name="top-attach">0</property>
+                          <property name="width">3</property>
+                        </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="replace_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Replace With</property>
+                        <property name="can_focus">False</property>
+                        <property name="sensitive">False</property>
+                      </object>
+                        <packing>
+                          <property name="left-attach">0</property>
+                          <property name="top-attach">1</property>
+                          <property name="width">1</property>
+                        </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="replace_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="width_request">375</property>
+                        <signal name="changed" handler="file_names_widget_entry_on_changed" swapped="yes" />
+                        <signal name="activate" handler="file_names_widget_on_activate" swapped="yes" />
+                      </object>
+                        <packing>
+                          <property name="left-attach">1</property>
+                          <property name="top-attach">1</property>
+                          <property name="width">3</property>
+                        </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="name">replace</property>
+                    <property name="title" translatable="yes">Replace</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+                <property name="width">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolled_window">
+                <property name="height_request">250</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">False</property>
+                <property name="vexpand">False</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkBox" id="a_box">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkListBox" id="original_name_listbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="arrow_listbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="result_listbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">2</property>
+                <property name="width">8</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="conflict_box">
+                <property name="orientation">horizontal</property>
+                <property name="spacing">6</property>
+                <property name="visible">False</property>
+                <property name="margin-left">6</property>
+                <child>
+                  <object class="GtkLabel" id="conflict_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="pack-type">start</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkButton" id="conflict_down">
+                        <property name="visible">True</property>
+                        <property name="relief">none</property>
+                         <signal name="clicked" handler="select_next_conflict_down" swapped="yes" />
+                        <child>
+                          <object class="GtkImage">
+                            <property name="visible">True</property>
+                            <property name="icon-name">go-down-symbolic</property>
+                            <property name="icon-size">1</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="conflict_up">
+                        <property name="visible">True</property>
+                        <property name="relief">GTK_RELIEF_NONE</property>
+                        <signal name="clicked" handler="select_next_conflict_up" swapped="yes" />
+                        <child>
+                          <object class="GtkImage">
+                            <property name="visible">True</property>
+                            <property name="icon-name">go-up-symbolic</property>
+                            <property name="icon-size">1</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">3</property>
+                <property name="width">8</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <menu id="add_tag_menu">
+    <section>
+      <attribute name="label" translatable="yes">Automatic Numbers</attribute>
+      <item>
+        <attribute name="label" translatable="yes">1, 2, 3, 4</attribute>
+        <attribute name="action">dialog.add-numbering-tag-zero</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">01, 02, 03, 04</attribute>
+        <attribute name="action">dialog.add-numbering-tag-one</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">001, 002, 003, 004</attribute>
+        <attribute name="action">dialog.add-numbering-tag-two</attribute>
+      </item>
+    </section>
+    <section>
+        <attribute name="label" translatable="yes">Metadata</attribute>
+      <item>
+        <attribute name="label" translatable="yes">Creation Date</attribute>
+        <attribute name="action">dialog.add-creation-date-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Camera Model</attribute>
+        <attribute name="action">dialog.add-equipment-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Season Number</attribute>
+        <attribute name="action">dialog.add-season-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Episode Number</attribute>
+        <attribute name="action">dialog.add-episode-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Track Number</attribute>
+        <attribute name="action">dialog.add-track-number-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Artist Name</attribute>
+        <attribute name="action">dialog.add-artist-name-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Title</attribute>
+        <attribute name="action">dialog.add-title-tag</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Original File Name</attribute>
+        <attribute name="action">dialog.add-original-file-name-tag</attribute>
+      </item>
+    </section>
+  </menu>
+  <object class="GtkPopover" id="add_popover">
+    <property name="position">bottom</property>
+    <property name="relative-to">add_button</property>
+    <signal name="closed" handler="add_popover_closed" swapped="yes" />
+  </object>
+  <object class="GtkImage" id="done_image">
+    <property name="visible">True</property>
+    <property name="icon_name">object-select-symbolic</property>
+  </object>
+  <menu id="numbering_order_menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Original name (Ascending) </attribute>
+        <attribute name="action">dialog.numbering-order-changed</attribute>
+        <attribute name="target">name-ascending</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Original name (Descending)</attribute>
+        <attribute name="action">dialog.numbering-order-changed</attribute>
+        <attribute name="target">name-descending</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">First Modified</attribute>
+        <attribute name="action">dialog.numbering-order-changed</attribute>
+        <attribute name="target">first-modified</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Last Modified</attribute>
+        <attribute name="action">dialog.numbering-order-changed</attribute>
+        <attribute name="target">last-modified</attribute>
+      </item>
+    </section>
+  </menu>
+  <object class="GtkPopover" id="numbering_order_popover">
+    <property name="position">bottom</property>
+    <property name="relative-to">numbering_order_button</property>
+    <signal name="closed" handler="numbering_order_popover_closed" swapped="yes" />
+  </object>
+</interface>
\ No newline at end of file


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]