[nautilus] Implement batch renaming



commit be12a7510090b2ec38229b6e86bc601800d2056b
Author: Alexandru Pandelea <alexandru pandelea gmail com>
Date:   Sat Aug 27 19:34:08 2016 +0300

    Implement batch renaming
    
    Renaming multiple files at once has been a missing feature in Nautilus
    for a long time. This patch implements that feature in the following way:
    
    This operation is launched in the same way as the rename one, when the
    selection has more than one file.
    
    When the batch renaming is launched, a dialog is shown, offering two
    modes.
    
    In the first mode, the user can use metadata (if available), numbering and
    original file name tags to create the new names. Between the tags, there
    also can be written normal text, which will be added in the new names. If
    numbering is used, the order of the files can be modified by using several
    criteria.
    
    In the second mode, the user can replace an existing part of the name.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768311

 src/Makefile.am                                    |    4 +
 src/nautilus-batch-rename-dialog.c                 | 3027 ++++++++++++++++++++
 src/nautilus-batch-rename-dialog.h                 |  100 +
 src/nautilus-batch-rename-utilities.c              | 1050 +++++++
 src/nautilus-batch-rename-utilities.h              |   63 +
 src/nautilus-file-private.h                        |    3 +
 src/nautilus-file-undo-operations.c                |  286 ++
 src/nautilus-file-undo-operations.h                |   29 +
 src/nautilus-file-utilities.c                      |   16 +
 src/nautilus-file-utilities.h                      |    2 +
 src/nautilus-file.c                                |  433 +++-
 src/nautilus-file.h                                |    4 +
 src/nautilus-files-view.c                          |   26 +-
 src/resources/css/Adwaita.css                      |   16 +
 src/resources/nautilus.gresource.xml               |    1 +
 src/resources/ui/nautilus-batch-rename-dialog.ui   |  502 ++++
 .../ui/nautilus-files-view-context-menus.ui        |    2 +-
 17 files changed, 5464 insertions(+), 100 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..eb76a73
--- /dev/null
+++ b/src/nautilus-batch-rename-dialog.c
@@ -0,0 +1,3027 @@
+/* nautilus-batch-rename-dialog.c
+ *
+ * Copyright (C) 2016 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "nautilus-batch-rename-dialog.h"
+#include "nautilus-file.h"
+#include "nautilus-error-reporting.h"
+#include "nautilus-batch-rename-utilities.h"
+
+#include <glib/gprintf.h>
+#include <glib.h>
+#include <string.h>
+#include <glib/gi18n.h>
+
+struct _NautilusBatchRenameDialog
+{
+        GtkDialog                        parent;
+
+        GtkWidget                       *grid;
+        NautilusWindow                  *window;
+
+        GtkWidget                       *cancel_button;
+        GtkWidget                       *original_name_listbox;
+        GtkWidget                       *arrow_listbox;
+        GtkWidget                       *result_listbox;
+        GtkWidget                       *name_entry;
+        GtkWidget                       *rename_button;
+        GtkWidget                       *find_entry;
+        GtkWidget                       *mode_stack;
+        GtkWidget                       *replace_entry;
+        GtkWidget                       *format_mode_button;
+        GtkWidget                       *replace_mode_button;
+        GtkWidget                       *add_button;
+        GtkWidget                       *add_popover;
+        GtkWidget                       *numbering_order_label;
+        GtkWidget                       *numbering_label;
+        GtkWidget                       *scrolled_window;
+        GtkWidget                       *numbering_order_popover;
+        GtkWidget                       *numbering_order_button;
+        GtkWidget                       *conflict_box;
+        GtkWidget                       *conflict_label;
+        GtkWidget                       *conflict_down;
+        GtkWidget                       *conflict_up;
+
+        GList                           *original_name_listbox_rows;
+        GList                           *arrow_listbox_rows;
+        GList                           *result_listbox_rows;
+        GList                           *listbox_labels_new;
+        GList                           *listbox_labels_old;
+        GList                           *listbox_icons;
+        GtkSizeGroup                    *size_group;
+
+        GList                           *selection;
+        GList                           *new_names;
+        NautilusBatchRenameDialogMode    mode;
+        NautilusDirectory               *directory;
+
+        GActionGroup                    *action_group;
+
+        GMenu                           *numbering_order_menu;
+        GMenu                           *add_tag_menu;
+
+        GHashTable                      *create_date;
+        GList                           *selection_metadata;
+
+        /* check if all files in selection have the same parent */
+        gboolean                         same_parent;
+        /* the index of the currently selected conflict */
+        gint                             selected_conflict;
+        /* total conflicts number */
+        gint                             conflicts_number;
+
+        gint                             checked_parents;
+        GList                           *duplicates;
+        GList                           *distinct_parents;
+        GTask                           *conflicts_task;
+        GCancellable                    *conflict_cancellable;
+        gboolean                         checking_conflicts;
+
+        /* this hash table has information about the status
+         * of all tags: availability, if it's currently used
+         * and position */
+        GHashTable                      *tag_info_table;
+
+        GtkWidget                       *preselected_row1;
+        GtkWidget                       *preselected_row2;
+
+        gint                             row_height;
+        gboolean                         rename_clicked;
+
+        /* the numbers of characters from the name entry */
+        gint                             name_entry_characters;
+        gboolean                         tags_deleted;
+        gint                             cursor_position;
+        gboolean                         use_manual_cursor_position;
+};
+
+typedef struct
+{
+        gboolean available;
+        gboolean set;
+        gint position;
+        gint original_position;
+        gint new_position;
+        /* if the tag was just added, then we shouldn't update it's position */
+        gboolean just_added;
+} TagData;
+
+static void     update_display_text     (NautilusBatchRenameDialog *dialog);
+
+G_DEFINE_TYPE (NautilusBatchRenameDialog, nautilus_batch_rename_dialog, GTK_TYPE_DIALOG);
+
+static void
+add_numbering_order (GSimpleAction       *action,
+                     GVariant            *value,
+                     gpointer             user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        const gchar *target_name;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        target_name = g_variant_get_string (value, NULL);
+
+        if (g_strcmp0 (target_name, "name-ascending") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("Original name (Ascending)"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       ORIGINAL_ASCENDING,
+                                                                       NULL);
+        }
+
+        if (g_strcmp0 (target_name, "name-descending") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("Original name (Descending)"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       ORIGINAL_DESCENDING,
+                                                                       NULL);
+        }
+
+        if (g_strcmp0 (target_name, "first-modified") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("First Modified"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       FIRST_MODIFIED,
+                                                                       NULL);
+        }
+
+        if (g_strcmp0 (target_name, "last-modified") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("Last Modified"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       LAST_MODIFIED,
+                                                                       NULL);
+        }
+
+        if (g_strcmp0 (target_name, "first-created") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("First Created"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       FIRST_CREATED,
+                                                                       dialog->create_date);
+        }
+
+        if (g_strcmp0 (target_name, "last-created") == 0) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
+                                     _("Last Created"));
+                dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
+                                                                       LAST_CREATED,
+                                                                       dialog->create_date);
+        }
+
+        g_simple_action_set_state (action, value);
+
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE);
+
+        update_display_text (dialog);
+}
+
+static void
+add_original_file_name_tag (GSimpleAction       *action,
+                            GVariant            *value,
+                            gpointer             user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        gint cursor_position;
+        TagData *tag_data;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+        tag_data->set = TRUE;
+        tag_data->just_added = TRUE;
+        tag_data->position = cursor_position;
+
+        gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                  ORIGINAL_FILE_NAME,
+                                  g_utf8_strlen (ORIGINAL_FILE_NAME, -1),
+                                  &cursor_position);
+
+        gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+
+        gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry));
+
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+}
+
+static void
+disable_action (NautilusBatchRenameDialog *dialog,
+                gchar                     *action_name)
+{
+        GAction *action;
+
+        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                             action_name);
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+}
+
+static void
+add_metadata_tag (GSimpleAction       *action,
+                  GVariant            *value,
+                  gpointer             user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        const gchar *action_name;
+        gint cursor_position;
+        TagData *tag_data;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        action_name = g_action_get_name (G_ACTION (action));
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        if (g_strrstr (action_name, "creation-date")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          CREATION_DATE,
+                                          g_utf8_strlen (CREATION_DATE, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-creation-date-tag");
+        }
+
+        if (g_strrstr (action_name, "equipment")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          CAMERA_MODEL,
+                                          g_utf8_strlen (CAMERA_MODEL, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-equipment-tag");
+        }
+
+        if (g_strrstr (action_name, "season")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          SEASON_NUMBER,
+                                          g_utf8_strlen (SEASON_NUMBER, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-season-tag");
+        }
+
+        if (g_strrstr (action_name, "episode")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          EPISODE_NUMBER,
+                                          g_utf8_strlen (EPISODE_NUMBER, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-episode-tag");
+        }
+
+        if (g_strrstr (action_name, "track")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          TRACK_NUMBER,
+                                          g_utf8_strlen (TRACK_NUMBER, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-track-number-tag");
+        }
+
+        if (g_strrstr (action_name, "artist")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          ARTIST_NAME,
+                                          g_utf8_strlen (ARTIST_NAME, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-artist-name-tag");
+        }
+
+        if (g_strrstr (action_name, "title")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          TITLE,
+                                          g_utf8_strlen (TITLE, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-title-tag");
+        }
+
+        if (g_strrstr (action_name, "album")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+                tag_data->available = TRUE;
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          ALBUM_NAME,
+                                          g_utf8_strlen (ALBUM_NAME, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+                disable_action (dialog, "add-album-name-tag");
+        }
+
+        gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry));
+}
+
+static void
+add_numbering_tag (GSimpleAction       *action,
+                   GVariant            *value,
+                   gpointer             user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        const gchar *action_name;
+        gint cursor_position;
+        GAction *add_numbering_action;
+        TagData *tag_data;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        action_name = g_action_get_name (G_ACTION (action));
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        if (g_strrstr (action_name, "zero")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING,
+                                          g_utf8_strlen (NUMBERING, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+        }
+
+        if (g_strrstr (action_name, "one")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING0,
+                                          g_utf8_strlen (NUMBERING0, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+        }
+
+        if (g_strrstr (action_name, "two")) {
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+                tag_data->set = TRUE;
+                tag_data->just_added = TRUE;
+                tag_data->position = cursor_position;
+
+                gtk_editable_insert_text (GTK_EDITABLE (dialog->name_entry),
+                                          NUMBERING00,
+                                          g_utf8_strlen (NUMBERING00, -1),
+                                          &cursor_position);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), cursor_position);
+        }
+
+        add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                           "add-numbering-tag-zero");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE);
+        add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                           "add-numbering-tag-one");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE);
+
+        add_numbering_action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                           "add-numbering-tag-two");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (add_numbering_action), FALSE);
+
+        gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry));
+}
+
+const GActionEntry dialog_entries[] = {
+        { "numbering-order-changed", NULL, "s", "'name-ascending'",  add_numbering_order },
+        { "add-original-file-name-tag", add_original_file_name_tag },
+        { "add-numbering-tag-zero", add_numbering_tag },
+        { "add-numbering-tag-one", add_numbering_tag },
+        { "add-numbering-tag-two", add_numbering_tag },
+        { "add-creation-date-tag", add_metadata_tag },
+        { "add-equipment-tag", add_metadata_tag },
+        { "add-season-tag", add_metadata_tag },
+        { "add-episode-tag", add_metadata_tag },
+        { "add-video-album-tag", add_metadata_tag },
+        { "add-track-number-tag", add_metadata_tag },
+        { "add-artist-name-tag", add_metadata_tag },
+        { "add-title-tag", add_metadata_tag },
+        { "add-album-name-tag", add_metadata_tag },
+
+};
+
+static void
+row_selected (GtkListBox    *box,
+              GtkListBoxRow *listbox_row,
+              gpointer       user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        GtkListBoxRow *row;
+        gint index;
+
+        if (!GTK_IS_LIST_BOX_ROW (listbox_row))
+                return;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+        index = gtk_list_box_row_get_index (listbox_row);
+
+        if (GTK_WIDGET (box) == dialog->original_name_listbox) {
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
+                                         row);
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
+                                         row);
+        }
+
+        if (GTK_WIDGET (box) == dialog->arrow_listbox) {
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
+                                         row);
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->result_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
+                                         row);
+        }
+
+        if (GTK_WIDGET (box) == dialog->result_listbox) {
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->arrow_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
+                                         row);
+                row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->original_name_listbox), index);
+                gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
+                                         row);
+        }
+}
+
+static gint
+compare_tag_position (gconstpointer a,
+                      gconstpointer b)
+{
+        int *number1 = (int*) a;
+        int *number2 = (int*) b;
+
+        return *number1 - *number2;
+}
+
+/* This function splits the entry text into a list of regular text and tags.
+ * For instance, "[1, 2, 3]Paris[Creation date]" would result in:
+ * "[1, 2, 3]", "Paris", "[Creation date]" */
+static GList*
+split_entry_text (NautilusBatchRenameDialog *dialog,
+                  gchar                     *entry_text)
+{
+        GString *normal_text;
+        GString *tag;
+        GArray *tag_positions;
+        gint tags;
+        gint i;
+        gchar *substring;
+        gint tag_end_position;
+        GList *result = NULL;
+        TagData *tag_data;
+
+        tags = 0;
+        tag_end_position = 0;
+        tag_positions = g_array_new (FALSE, FALSE, sizeof (gint));
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+        if (tag_data->set) {
+                g_array_append_val (tag_positions, tag_data->position);
+                tags++;
+        }
+
+        g_array_sort (tag_positions, compare_tag_position);
+
+        for (i = 0; i < tags; i++) {
+                tag = g_string_new ("");
+
+                substring = g_utf8_substring (entry_text, tag_end_position,
+                                              g_array_index (tag_positions, gint, i));
+                normal_text = g_string_new (substring);
+                g_free (substring);
+
+                if (g_strcmp0 (normal_text->str, ""))
+                        result = g_list_prepend (result, normal_text);
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (ORIGINAL_FILE_NAME, -1);
+                        tag = g_string_append (tag, ORIGINAL_FILE_NAME);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (NUMBERING, -1);
+                        tag = g_string_append (tag, NUMBERING);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (NUMBERING0, -1);
+                        tag = g_string_append (tag, NUMBERING0);
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (NUMBERING00, -1);
+                        tag = g_string_append (tag, NUMBERING00);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (CREATION_DATE, -1);
+                        tag = g_string_append (tag, CREATION_DATE);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (CAMERA_MODEL, -1);
+                        tag = g_string_append (tag, CAMERA_MODEL);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (SEASON_NUMBER, -1);
+                        tag = g_string_append (tag, SEASON_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (EPISODE_NUMBER, -1);
+                        tag = g_string_append (tag, EPISODE_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (TRACK_NUMBER, -1);
+                        tag = g_string_append (tag, TRACK_NUMBER);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (ARTIST_NAME, -1);
+                        tag = g_string_append (tag, ARTIST_NAME);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (TITLE, -1);
+                        tag = g_string_append (tag, TITLE);
+                }
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+                if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position) {
+                        tag_end_position = g_array_index (tag_positions, gint, i) +
+                                           g_utf8_strlen (ALBUM_NAME, -1);
+                        tag = g_string_append (tag, ALBUM_NAME);
+                }
+
+                result = g_list_prepend (result, tag);
+        }
+        normal_text = g_string_new (g_utf8_offset_to_pointer (entry_text, tag_end_position));
+
+        if (g_strcmp0 (normal_text->str, "") != 0)
+                result = g_list_prepend (result, normal_text);
+
+        result = g_list_reverse (result);
+
+        g_array_free (tag_positions, TRUE);
+        return result;
+}
+
+static GList*
+batch_rename_dialog_get_new_names (NautilusBatchRenameDialog *dialog)
+{
+        GList *result = NULL;
+        GList *selection;
+        GList *text_chunks;
+        g_autofree gchar *entry_text;
+        g_autofree gchar *replace_text;
+
+        selection = dialog->selection;
+        text_chunks = NULL;
+
+        if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
+                entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->find_entry)));
+        else
+                entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
+
+        replace_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry)));
+
+        if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE) {
+                result = batch_rename_dialog_get_new_names_list (dialog->mode,
+                                                                 selection,
+                                                                 NULL,
+                                                                 NULL,
+                                                                 entry_text,
+                                                                 replace_text);
+        } else {
+                text_chunks = split_entry_text (dialog, entry_text);
+
+                result = batch_rename_dialog_get_new_names_list (dialog->mode,
+                                                                 selection,
+                                                                 text_chunks,
+                                                                 dialog->selection_metadata,
+                                                                 entry_text,
+                                                                 replace_text);
+                g_list_free_full (text_chunks, string_free);
+        }
+
+        result = g_list_reverse (result);
+
+        return result;
+}
+
+static void
+begin_batch_rename (NautilusBatchRenameDialog *dialog,
+                    GList                     *new_names)
+{
+        GList *new_names_list;
+        GList *files;
+        GList *files2;
+        GList *new_names_list2;
+        gchar *file_name;
+        gchar *old_file_name;
+        GString *new_file_name;
+        GString *new_name;
+        NautilusFile *file;
+
+        /* in the following case:
+         * file1 -> file2
+         * file2 -> file3
+         * file2 must be renamed first, so because of that, the list has to be reordered */
+        for (new_names_list = new_names, files = dialog->selection;
+             new_names_list != NULL && files != NULL;
+             new_names_list = new_names_list->next, files = files->next) {
+                old_file_name = nautilus_file_get_name (NAUTILUS_FILE (files->data));
+                new_file_name = new_names_list->data;
+
+                for (files2 = dialog->selection, new_names_list2 = new_names;
+                     files2 != NULL && new_names_list2 != NULL;
+                     files2 = files2->next, new_names_list2 = new_names_list2->next) {
+                        file_name = nautilus_file_get_name (NAUTILUS_FILE (files2->data));
+                        if (files2 != files && g_strcmp0 (file_name, new_file_name->str) == 0) {
+                                file = NAUTILUS_FILE (files2->data);
+                                new_name = new_names_list2->data;
+
+                                dialog->selection = g_list_remove_link (dialog->selection, files2);
+                                new_names = g_list_remove_link (new_names, new_names_list2);
+
+                                dialog->selection = g_list_prepend (dialog->selection, file);
+                                new_names = g_list_prepend (new_names, new_name);
+
+                                g_free (file_name);
+
+                                break;
+                        }
+
+                        g_free (file_name);
+                }
+
+                g_free (old_file_name);
+        }
+
+        /* do the actual rename here */
+        nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL);
+
+        gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)), NULL);
+}
+
+static void
+listbox_header_func (GtkListBoxRow               *row,
+                     GtkListBoxRow               *before,
+                     NautilusBatchRenameDialog   *dialog)
+{
+        gboolean show_separator;
+
+        show_separator = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row),
+                                          "show-separator"));
+
+        if (show_separator)
+        {
+                GtkWidget *separator;
+
+                separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+                gtk_widget_show (separator);
+
+                gtk_list_box_row_set_header (row, separator);
+        }
+}
+
+/* This is manually done instead of using GtkSizeGroup because of the computational
+ * complexity of the later.*/
+static void
+update_rows_height (NautilusBatchRenameDialog *dialog)
+{
+        GList *l;
+        gint current_row_natural_height;
+        gint maximum_height;
+
+        maximum_height = -1;
+
+        /* check if maximum height has changed */
+        for (l = dialog->listbox_labels_new; l != NULL; l = l->next) {
+                gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
+                                                 NULL,
+                                                 &current_row_natural_height);
+
+                if (current_row_natural_height > maximum_height) {
+                        maximum_height = current_row_natural_height;
+                }
+        }
+
+        for (l = dialog->listbox_labels_old; l != NULL; l = l->next) {
+                gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
+                                                 NULL,
+                                                 &current_row_natural_height);
+
+                if (current_row_natural_height > maximum_height) {
+                        maximum_height = current_row_natural_height;
+                }
+        }
+
+        for (l = dialog->listbox_icons; l != NULL; l = l->next) {
+                gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
+                                                 NULL,
+                                                 &current_row_natural_height);
+
+                if (current_row_natural_height > maximum_height) {
+                        maximum_height = current_row_natural_height;
+                }
+        }
+
+        if (maximum_height != dialog->row_height) {
+                dialog->row_height = maximum_height;
+
+                for (l = dialog->listbox_icons; l != NULL; l = l->next) {
+                        g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
+                }
+
+               for (l = dialog->listbox_labels_new; l != NULL; l = l->next) {
+                        g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
+                }
+
+               for (l = dialog->listbox_labels_old; l != NULL; l = l->next) {
+                        g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
+                }
+        }
+}
+
+static GtkWidget*
+create_original_name_row_for_label (NautilusBatchRenameDialog *dialog,
+                                    const gchar               *old_text,
+                                    gboolean                   show_separator)
+{
+        GtkWidget *row;
+        GtkWidget *label_old;
+
+        row = gtk_list_box_row_new ();
+
+        g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
+
+        label_old = gtk_label_new (old_text);
+        gtk_label_set_xalign (GTK_LABEL (label_old), 0.0);
+        gtk_widget_set_hexpand (label_old, TRUE);
+        gtk_widget_set_margin_start (label_old, 6);
+
+        gtk_label_set_ellipsize (GTK_LABEL (label_old), PANGO_ELLIPSIZE_END);
+
+        dialog->listbox_labels_old = g_list_prepend (dialog->listbox_labels_old, label_old);
+
+        gtk_container_add (GTK_CONTAINER (row), label_old);
+        gtk_widget_show_all (row);
+
+        return row;
+}
+
+static GtkWidget*
+create_result_row_for_label (NautilusBatchRenameDialog *dialog,
+                             const gchar               *new_text,
+                             gboolean                   show_separator)
+{
+        GtkWidget *row;
+        GtkWidget *label_new;
+
+        row = gtk_list_box_row_new ();
+
+        g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
+
+        label_new = gtk_label_new (new_text);
+        gtk_label_set_xalign (GTK_LABEL (label_new), 0.0);
+        gtk_widget_set_hexpand (label_new, TRUE);
+        gtk_widget_set_margin_start (label_new, 6);
+
+        gtk_label_set_ellipsize (GTK_LABEL (label_new), PANGO_ELLIPSIZE_END);
+
+        dialog->listbox_labels_new = g_list_prepend (dialog->listbox_labels_new, label_new);
+
+        gtk_container_add (GTK_CONTAINER (row), label_new);
+        gtk_widget_show_all (row);
+
+        return row;
+}
+
+static GtkWidget*
+create_arrow_row_for_label (NautilusBatchRenameDialog *dialog,
+                            gboolean                   show_separator)
+{
+        GtkWidget *row;
+        GtkWidget *icon;
+
+        row = gtk_list_box_row_new ();
+
+        g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
+
+        icon = gtk_label_new ("→");
+        gtk_label_set_xalign (GTK_LABEL (icon), 1.0);
+        gtk_widget_set_hexpand (icon, FALSE);
+        gtk_widget_set_margin_start (icon, 6);
+
+        dialog->listbox_icons = g_list_prepend (dialog->listbox_icons, icon);
+
+        gtk_container_add (GTK_CONTAINER (row), icon);
+        gtk_widget_show_all (row);
+
+        return row;
+}
+
+static void
+prepare_batch_rename (NautilusBatchRenameDialog *dialog)
+{
+        GdkCursor *cursor;
+        GdkDisplay *display;
+
+        /* wait for checking conflicts to finish, to be sure that
+         * the rename can actually take place */
+        if (dialog->checking_conflicts) {
+                dialog->rename_clicked = TRUE;
+                return;
+        }
+
+        if (!gtk_widget_is_sensitive (dialog->rename_button))
+                return;
+
+        display = gtk_widget_get_display (GTK_WIDGET (dialog->window));
+        cursor = gdk_cursor_new_from_name (display, "progress");
+        gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)),
+                               cursor);
+        g_object_unref (cursor);
+
+        display = gtk_widget_get_display (GTK_WIDGET (dialog));
+        cursor = gdk_cursor_new_from_name (display, "progress");
+        gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog)),
+                               cursor);
+        g_object_unref (cursor);
+
+        gtk_widget_hide (GTK_WIDGET (dialog));
+        begin_batch_rename (dialog, dialog->new_names);
+
+        if (dialog->conflict_cancellable)
+                g_cancellable_cancel (dialog->conflict_cancellable);
+
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+batch_rename_dialog_on_response (NautilusBatchRenameDialog *dialog,
+                                 gint                       response_id,
+                                 gpointer                   user_data)
+{
+        if (response_id == GTK_RESPONSE_OK) {
+                prepare_batch_rename (dialog);
+        } else {
+                if (dialog->conflict_cancellable)
+                        g_cancellable_cancel (dialog->conflict_cancellable);
+
+                gtk_widget_destroy (GTK_WIDGET (dialog));
+        }
+}
+
+static void
+fill_display_listbox (NautilusBatchRenameDialog *dialog)
+{
+        GtkWidget *row;
+        GList *l1;
+        GList *l2;
+        NautilusFile *file;
+        GString *new_name;
+        gchar *name;
+
+        dialog->original_name_listbox_rows = NULL;
+        dialog->arrow_listbox_rows = NULL;
+        dialog->result_listbox_rows = NULL;
+
+        gtk_size_group_add_widget (dialog->size_group, dialog->result_listbox);
+        gtk_size_group_add_widget (dialog->size_group, dialog->original_name_listbox);
+
+        for (l1 = dialog->new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = 
l2->next) {
+                file = NAUTILUS_FILE (l2->data);
+                new_name = l1->data;
+
+                name = nautilus_file_get_name (file);
+                row = create_original_name_row_for_label (dialog, name, TRUE);
+                gtk_container_add (GTK_CONTAINER (dialog->original_name_listbox), row);
+                dialog->original_name_listbox_rows = g_list_prepend (dialog->original_name_listbox_rows,
+                                                                     row);
+
+                row = create_arrow_row_for_label (dialog, TRUE);
+                gtk_container_add (GTK_CONTAINER (dialog->arrow_listbox), row);
+                dialog->arrow_listbox_rows = g_list_prepend (dialog->arrow_listbox_rows,
+                                                             row);
+
+                row = create_result_row_for_label (dialog, new_name->str, TRUE);
+                gtk_container_add (GTK_CONTAINER (dialog->result_listbox), row);
+                dialog->result_listbox_rows = g_list_prepend (dialog->result_listbox_rows,
+                                                              row);
+
+                g_free (name);
+        }
+
+        dialog->original_name_listbox_rows = g_list_reverse (dialog->original_name_listbox_rows);
+        dialog->arrow_listbox_rows = g_list_reverse (dialog->arrow_listbox_rows);
+        dialog->result_listbox_rows = g_list_reverse (dialog->result_listbox_rows);
+        dialog->listbox_labels_old = g_list_reverse (dialog->listbox_labels_old);
+        dialog->listbox_labels_new = g_list_reverse (dialog->listbox_labels_new);
+        dialog->listbox_icons = g_list_reverse (dialog->listbox_icons);
+}
+
+static void
+select_nth_conflict (NautilusBatchRenameDialog *dialog)
+{
+        GList *l;
+        GString *conflict_file_name;
+        GString *display_text;
+        GString *new_name;
+        gint nth_conflict_index;
+        gint nth_conflict;
+        gint name_occurences;
+        GtkAdjustment *adjustment;
+        GtkAllocation allocation;
+        ConflictData *conflict_data;
+
+        nth_conflict = dialog->selected_conflict;
+        l = g_list_nth (dialog->duplicates, nth_conflict);
+        conflict_data = l->data;
+
+        /* the conflict that has to be selected */
+        conflict_file_name = g_string_new (conflict_data->name);
+        display_text = g_string_new ("");
+
+        nth_conflict_index = conflict_data->index;
+
+        l = g_list_nth (dialog->original_name_listbox_rows, nth_conflict_index);
+        gtk_list_box_select_row (GTK_LIST_BOX (dialog->original_name_listbox),
+                                 l->data);
+
+        l = g_list_nth (dialog->arrow_listbox_rows, nth_conflict_index);
+        gtk_list_box_select_row (GTK_LIST_BOX (dialog->arrow_listbox),
+                                 l->data);
+
+        l = g_list_nth (dialog->result_listbox_rows, nth_conflict_index);
+        gtk_list_box_select_row (GTK_LIST_BOX (dialog->result_listbox),
+                                 l->data);
+
+        /* scroll to the selected row */
+        adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (dialog->scrolled_window));
+        gtk_widget_get_allocation (GTK_WIDGET (l->data), &allocation);
+        gtk_adjustment_set_value (adjustment, (allocation.height + 1) * nth_conflict_index);
+
+        name_occurences = 0;
+        for (l = dialog->new_names; l != NULL; l = l->next) {
+                new_name = l->data;
+                if (g_string_equal (new_name, conflict_file_name))
+                        name_occurences++;
+        }
+        if (name_occurences > 1)
+                g_string_append_printf (display_text,
+                                        _("\"%s\" would not be a unique new name"),
+                                        conflict_file_name->str);
+        else
+                g_string_append_printf (display_text,
+                                        _("\"%s\" would conflict with an existing file."),
+                                        conflict_file_name->str);
+
+        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                             display_text->str);
+
+        g_string_free (conflict_file_name, TRUE);
+}
+
+static void
+select_next_conflict_down (NautilusBatchRenameDialog *dialog)
+{
+        dialog->selected_conflict++;
+
+        if (dialog->selected_conflict == 1)
+                gtk_widget_set_sensitive (dialog->conflict_up, TRUE);
+
+        if (dialog->selected_conflict == dialog->conflicts_number - 1)
+                gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
+
+        select_nth_conflict (dialog);
+}
+
+static void
+select_next_conflict_up (NautilusBatchRenameDialog *dialog)
+{
+        dialog->selected_conflict--;
+
+        if (dialog->selected_conflict == 0)
+                gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
+
+        if (dialog->selected_conflict == dialog->conflicts_number - 2)
+                gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
+
+        select_nth_conflict (dialog);
+}
+
+static void
+update_conflict_row_background (NautilusBatchRenameDialog *dialog)
+{
+        GList *l1;
+        GList *l2;
+        GList *l3;
+        GList *duplicates;
+        gint index;
+        GtkStyleContext *context;
+        ConflictData *conflict_data;
+
+        index = 0;
+
+        duplicates = dialog->duplicates;
+
+        for (l1 = dialog->original_name_listbox_rows,
+             l2 = dialog->arrow_listbox_rows,
+             l3 = dialog->result_listbox_rows;
+             l1 != NULL && l2 != NULL && l3 != NULL;
+             l1 = l1->next, l2 = l2->next, l3 = l3->next) {
+                context = gtk_widget_get_style_context (GTK_WIDGET (l1->data));
+
+                if (gtk_style_context_has_class (context, "conflict-row")) {
+                        gtk_style_context_remove_class (context, "conflict-row");
+
+                        context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
+                        gtk_style_context_remove_class (context, "conflict-row");
+
+                        context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
+                        gtk_style_context_remove_class (context, "conflict-row");
+
+                }
+
+                if (duplicates != NULL) {
+                        conflict_data = duplicates->data;
+                        if (conflict_data->index == index) {
+                                        context = gtk_widget_get_style_context (GTK_WIDGET (l1->data));
+                                        gtk_style_context_add_class (context, "conflict-row");
+
+                                        context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
+                                        gtk_style_context_add_class (context, "conflict-row");
+
+                                        context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
+                                        gtk_style_context_add_class (context, "conflict-row");
+
+                                        duplicates = duplicates->next;
+                        }
+                }
+                index++;
+        }
+}
+
+static void
+update_listbox (NautilusBatchRenameDialog *dialog)
+{
+        GList *l1;
+        GList *l2;
+        NautilusFile *file;
+        gchar *old_name;
+        GtkLabel *label;
+        GString *new_name;
+
+        for (l1 = dialog->new_names, l2 = dialog->listbox_labels_new; l1 != NULL && l2 != NULL; l1 = 
l1->next, l2 = l2->next) {
+                label = GTK_LABEL (l2->data);
+                new_name = l1->data;
+
+                gtk_label_set_label (label, new_name->str);
+        }
+
+        for (l1 = dialog->selection, l2 = dialog->listbox_labels_old; l1 != NULL && l2 != NULL; l1 = 
l1->next, l2 = l2->next) {
+                label = GTK_LABEL (l2->data);
+                file = NAUTILUS_FILE (l1->data);
+
+                old_name = nautilus_file_get_name (file);
+
+                if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) {
+                        gtk_label_set_label (label, old_name);
+                } else {
+                        new_name = batch_rename_replace_label_text (old_name,
+                                                                    gtk_entry_get_text (GTK_ENTRY 
(dialog->find_entry)));
+                        gtk_label_set_markup (GTK_LABEL (label), new_name->str);
+
+                        g_string_free (new_name, TRUE);
+                }
+
+                g_free (old_name);
+        }
+
+        update_rows_height (dialog);
+
+        /* check if there are name conflicts and display them if they exist */
+        if (dialog->duplicates != NULL) {
+                update_conflict_row_background (dialog);
+
+                gtk_widget_set_sensitive (dialog->rename_button, FALSE);
+
+                gtk_widget_show (dialog->conflict_box);
+
+                dialog->selected_conflict = 0;
+                dialog->conflicts_number = g_list_length (dialog->duplicates);
+
+                select_nth_conflict (dialog);
+
+                gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
+
+                if (g_list_length (dialog->duplicates) == 1)
+                    gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
+                else
+                    gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
+        } else {
+                gtk_widget_hide (dialog->conflict_box);
+
+                /* re-enable the rename button if there are no more name conflicts */
+                if (dialog->duplicates == NULL && !gtk_widget_is_sensitive (dialog->rename_button)) {
+                        update_conflict_row_background (dialog);
+                        gtk_widget_set_sensitive (dialog->rename_button, TRUE);
+                }
+        }
+
+        /* if the rename button was clicked and there's no conflict, then start renaming */
+        if (dialog->rename_clicked && dialog->duplicates == NULL) {
+                prepare_batch_rename (dialog);
+        }
+
+        if (dialog->rename_clicked && dialog->duplicates != NULL) {
+                dialog->rename_clicked = FALSE;
+        }
+}
+
+
+void
+check_conflict_for_files (NautilusBatchRenameDialog *dialog,
+                          NautilusDirectory         *directory,
+                          GList                     *files)
+{
+        gchar *current_directory;
+        gchar *parent_uri;
+        gchar *name;
+        NautilusFile *file;
+        GString *new_name;
+        GString *file_name;
+        GList *l1, *l2;
+        GHashTable *directory_files_table;
+        GHashTable *new_names_table;
+        GHashTable *names_conflicts_table;
+        gboolean exists;
+        gboolean have_conflict;
+        gboolean tag_present;
+        gboolean same_parent_directory;
+        ConflictData *conflict_data;
+
+        current_directory = nautilus_directory_get_uri (directory);
+
+        directory_files_table = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       (GDestroyNotify) g_free,
+                                                       NULL);
+        new_names_table = g_hash_table_new_full (g_str_hash,
+                                                 g_str_equal,
+                                                 (GDestroyNotify) g_free,
+                                                 (GDestroyNotify) g_free);
+        names_conflicts_table = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       (GDestroyNotify) g_free,
+                                                       (GDestroyNotify) g_free);
+
+        /* names_conflicts_table is used for knowing which names from the list are not unique,
+         * so that they can easily be reached when needed */
+        for (l1 = dialog->new_names, l2 = dialog->selection;
+             l1 != NULL && l2 != NULL;
+             l1 = l1->next, l2 = l2->next) {
+                new_name = l1->data;
+                file = NAUTILUS_FILE (l2->data);
+                parent_uri = nautilus_file_get_parent_uri (file);
+
+                tag_present = g_hash_table_lookup (new_names_table, new_name->str) != NULL;
+                same_parent_directory = g_strcmp0 (parent_uri, current_directory) == 0;
+
+                if (same_parent_directory) {
+                        if (!tag_present) {
+                                g_hash_table_insert (new_names_table,
+                                                     g_strdup (new_name->str),
+                                                     nautilus_file_get_parent_uri (file));
+                        } else {
+                                g_hash_table_insert (names_conflicts_table,
+                                                     g_strdup (new_name->str),
+                                                     nautilus_file_get_parent_uri (file));
+                        }
+                }
+
+                g_free (parent_uri);
+        }
+
+        for (l1 = files; l1 != NULL; l1 = l1->next) {
+                file = NAUTILUS_FILE (l1->data);
+                g_hash_table_insert (directory_files_table,
+                                     nautilus_file_get_name (file),
+                                     GINT_TO_POINTER (TRUE));
+        }
+
+        for (l1 = dialog->selection, l2 = dialog->new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = 
l2->next) {
+                file = NAUTILUS_FILE (l1->data);
+
+                name = nautilus_file_get_name (file);
+                file_name = g_string_new (name);
+                g_free (name);
+
+                parent_uri = nautilus_file_get_parent_uri (file);
+
+                new_name = l2->data;
+
+                have_conflict = FALSE;
+
+                /* check for duplicate only if the parent of the current file is
+                 * the current directory and the name of the file has changed */
+                if (g_strcmp0 (parent_uri, current_directory) == 0 &&
+                    !g_string_equal (new_name, file_name)) {
+                        exists = GPOINTER_TO_INT (g_hash_table_lookup (directory_files_table, 
new_name->str));
+
+                        if (exists == TRUE &&
+                            !file_name_conflicts_with_results (dialog->selection, dialog->new_names, 
new_name, parent_uri)) {
+                                conflict_data = g_new (ConflictData, 1);
+                                conflict_data->name = g_strdup (new_name->str);
+                                conflict_data->index = g_list_index (dialog->selection, l1->data);
+                                dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                     conflict_data);
+
+                                have_conflict = TRUE;
+                        }
+                }
+                if (!have_conflict) {
+                        tag_present = g_hash_table_lookup (names_conflicts_table, new_name->str) != NULL;
+                        same_parent_directory = g_strcmp0 (nautilus_file_get_parent_uri (file), 
current_directory) == 0;
+
+                        if (tag_present && same_parent_directory) {
+                                conflict_data = g_new (ConflictData, 1);
+                                conflict_data->name = g_strdup (new_name->str);
+                                conflict_data->index = g_list_index (dialog->selection, l1->data);
+                                dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                     conflict_data);
+
+                                have_conflict = TRUE;
+                        }
+                }
+
+                g_string_free (file_name, TRUE);
+                g_free (parent_uri);
+        }
+
+        /* check if this is the last call of the callback. Update
+         * the listbox with the conflicts if it is. */
+        if (dialog->checked_parents == g_list_length (dialog->distinct_parents) - 1) {
+                dialog->duplicates = g_list_reverse (dialog->duplicates);
+
+                dialog->checking_conflicts = FALSE;
+
+                update_listbox (dialog);
+        }
+
+        dialog->checked_parents++;
+
+        g_free (current_directory);
+        g_hash_table_destroy (directory_files_table);
+        g_hash_table_destroy (new_names_table);
+        g_hash_table_destroy  (names_conflicts_table);
+}
+
+static void
+file_names_list_has_duplicates_callback (GObject     *object,
+                                        GAsyncResult *res,
+                                        gpointer      user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (object);
+
+        if (g_cancellable_is_cancelled (dialog->conflict_cancellable))
+                return;
+
+        if (dialog->same_parent)
+                update_listbox (dialog);
+}
+
+static void
+on_call_when_ready (NautilusDirectory *directory,
+                    GList             *files,
+                    gpointer           callback_data)
+{
+        check_conflict_for_files (NAUTILUS_BATCH_RENAME_DIALOG (callback_data),
+                                  directory,
+                                  files);
+}
+
+static void
+file_names_list_has_duplicates_async_thread (GTask        *task,
+                                             gpointer      object,
+                                             gpointer      task_data,
+                                             GCancellable *cancellable)
+{
+        NautilusBatchRenameDialog *dialog;
+        GList *new_names;
+        GList *directory_files;
+        GList *l1;
+        GList *l2;
+        NautilusFile *file;
+        GString *file_name;
+        GString *new_name;
+        NautilusDirectory *parent;
+        gboolean have_conflict;
+        gboolean hash_table_insertion;
+        gchar *name;
+        GHashTable *directory_names_table;
+        GHashTable *new_names_table;
+        GHashTable *names_conflicts_table;
+        gint exists;
+        ConflictData *conflict_data;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (object);
+
+        dialog->duplicates = NULL;
+
+       if (g_cancellable_is_cancelled (cancellable))
+                return;
+
+        g_return_if_fail (g_list_length (dialog->new_names) == g_list_length (dialog->selection));
+
+        /* If the batch rename is launched in a search, then for each file we have to check for
+         * conflicts with each file in the file's parent directory */
+        if (dialog->distinct_parents != NULL) {
+                for (l1 = dialog->distinct_parents; l1 != NULL; l1 = l1->next) {
+                        if (g_cancellable_is_cancelled (cancellable))
+                                return;
+
+                        parent = nautilus_directory_get_by_uri (l1->data);
+
+                        nautilus_directory_call_when_ready (parent,
+                                                            NAUTILUS_FILE_ATTRIBUTE_INFO,
+                                                            TRUE,
+                                                            on_call_when_ready,
+                                                            dialog);
+                }
+
+                g_task_return_pointer (task, object, NULL);
+                return;
+        }
+
+        new_names = batch_rename_dialog_get_new_names (dialog);
+
+        directory_names_table = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       (GDestroyNotify) g_free,
+                                                       NULL);
+        new_names_table = g_hash_table_new_full (g_str_hash,
+                                                 g_str_equal,
+                                                 (GDestroyNotify) g_free,
+                                                 NULL);
+        names_conflicts_table = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       (GDestroyNotify) g_free,
+                                                       NULL);
+
+        directory_files = nautilus_directory_get_file_list (dialog->directory);
+
+        for (l1 = new_names; l1 != NULL; l1 = l1->next) {
+                new_name = l1->data;
+                hash_table_insertion = g_hash_table_insert (new_names_table,
+                                                            g_strdup (new_name->str),
+                                                            GINT_TO_POINTER (TRUE));
+
+                if (!hash_table_insertion) {
+                        g_hash_table_insert (names_conflicts_table,
+                                             g_strdup (new_name->str),
+                                             GINT_TO_POINTER (TRUE));
+                }
+        }
+
+        for (l1 = directory_files; l1 != NULL; l1 = l1->next) {
+                file = NAUTILUS_FILE (l1->data);
+                g_hash_table_insert (directory_names_table,
+                                     nautilus_file_get_name (file),
+                                     GINT_TO_POINTER (TRUE));
+        }
+
+        for (l1 = new_names, l2 = dialog->selection; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) 
{
+                if (g_cancellable_is_cancelled (cancellable)) {
+                        g_list_free_full (dialog->duplicates, conflict_data_free);
+                        break;
+                }
+
+                file = NAUTILUS_FILE (l2->data);
+                new_name = l1->data;
+
+                have_conflict = FALSE;
+
+                name = nautilus_file_get_name (file);
+                file_name = g_string_new (name);
+
+                g_free (name);
+
+                /* check for duplicate only if the name has changed */
+                if (!g_string_equal (new_name, file_name)) {
+                        /* check with already existing files */
+                        exists = GPOINTER_TO_INT (g_hash_table_lookup (directory_names_table, 
new_name->str));
+
+                        if (exists == TRUE &&
+                            !file_name_conflicts_with_results (dialog->selection, new_names, new_name, 
NULL)) {
+                                        conflict_data = g_new (ConflictData, 1);
+                                        conflict_data->name = g_strdup (new_name->str);
+                                        conflict_data->index = g_list_index (dialog->selection, l2->data);
+                                        dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                             conflict_data);
+                                        have_conflict = TRUE;
+                        }
+
+                        /* check with files that will result from the batch rename, unless
+                         * this file already has a conflict */
+                        if (!have_conflict) {
+                                exists = GPOINTER_TO_INT (g_hash_table_lookup (names_conflicts_table, 
new_name->str));
+
+                                if (exists == TRUE) {
+                                        conflict_data = g_new (ConflictData, 1);
+                                        conflict_data->name = g_strdup (new_name->str);
+                                        conflict_data->index = g_list_index (dialog->selection, l2->data);
+                                        dialog->duplicates = g_list_prepend (dialog->duplicates,
+                                                                             conflict_data);
+                                }
+                        }
+                }
+
+                g_string_free (file_name, TRUE);
+        }
+
+        g_hash_table_destroy (directory_names_table);
+        g_hash_table_destroy (new_names_table);
+        g_hash_table_destroy (names_conflicts_table);
+        nautilus_file_list_free (directory_files);
+        g_list_free_full (new_names, string_free);
+
+        dialog->duplicates = g_list_reverse (dialog->duplicates);
+
+        dialog->checking_conflicts = FALSE;
+
+        g_task_return_pointer (task, object, NULL);
+
+}
+
+static void
+file_names_list_has_duplicates_async (NautilusBatchRenameDialog *dialog,
+                                      GAsyncReadyCallback        callback,
+                                      gpointer                   user_data)
+{
+        if (dialog->checking_conflicts == TRUE)
+                g_cancellable_cancel (dialog->conflict_cancellable);
+
+        dialog->conflict_cancellable = g_cancellable_new ();
+
+        dialog->checking_conflicts = TRUE;
+        dialog->conflicts_task = g_task_new (dialog, dialog->conflict_cancellable, callback, user_data);
+
+        g_task_run_in_thread (dialog->conflicts_task, file_names_list_has_duplicates_async_thread);
+
+        g_object_unref (dialog->conflicts_task);
+}
+
+static void
+update_tags (NautilusBatchRenameDialog *dialog)
+{
+        TagData *tag_data;
+        const gchar *entry_text;
+        gint character_difference;
+        gint cursor_position;
+
+        entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
+
+        if (dialog->use_manual_cursor_position) {
+                        gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry),
+                                                   dialog->cursor_position);
+        }
+
+        if (dialog->tags_deleted) {
+                dialog->tags_deleted = FALSE;
+                        gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry),
+                                                   g_utf8_strlen (entry_text, -1));
+        }
+
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        character_difference = g_utf8_strlen (entry_text, -1) - dialog->name_entry_characters;
+        dialog->name_entry_characters = g_utf8_strlen (entry_text, -1);
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+        if (tag_data->just_added) {
+                tag_data->just_added = FALSE;
+        } else {
+                if (tag_data->set && cursor_position <= tag_data->position) {
+                     tag_data->position += character_difference;
+                }
+        }
+}
+
+static gboolean
+have_unallowed_character (NautilusBatchRenameDialog *dialog)
+{
+        const gchar *entry_text;
+        gboolean have_unallowed_character_slash;
+        gboolean have_unallowed_character_dot;
+        gboolean have_unallowed_character_dotdot;
+
+        have_unallowed_character_slash = FALSE;
+        have_unallowed_character_dot = FALSE;
+        have_unallowed_character_dotdot = FALSE;
+
+
+
+        if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT) {
+                entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
+
+        } else {
+                entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+        }
+
+        if (strstr (entry_text, "/") != NULL) {
+                have_unallowed_character_slash = TRUE;
+        }
+
+        if (g_strcmp0 (entry_text, ".") == 0) {
+                have_unallowed_character_dot = TRUE;
+        }
+
+        if (g_strcmp0 (entry_text, "..") == 0) {
+                have_unallowed_character_dotdot = TRUE;
+        }
+
+        if (have_unallowed_character_slash) {
+                gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                                    "\"/\" is an unallowed character");
+        }
+
+        if (have_unallowed_character_dot) {
+                gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                                    "\".\" is an unallowed file name");
+        }
+
+        if (have_unallowed_character_dotdot) {
+                gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
+                                    "\"..\" is an unallowed file name");
+        }
+
+        if (have_unallowed_character_slash || have_unallowed_character_dot || 
have_unallowed_character_dotdot) {
+                gtk_widget_set_sensitive (dialog->rename_button, FALSE);
+                gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
+                gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
+
+                gtk_widget_show (dialog->conflict_box);
+
+                return TRUE;
+        } else {
+                gtk_widget_hide (dialog->conflict_box);
+
+                return FALSE;
+        }
+}
+
+static void
+update_display_text (NautilusBatchRenameDialog *dialog)
+{
+        TagData *tag_data;
+        TagData *tag_data0;
+        TagData *tag_data00;
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+        tag_data0 = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+        tag_data00 = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+
+        if (dialog->conflict_cancellable != NULL)
+                g_cancellable_cancel (dialog->conflict_cancellable);
+
+        if(dialog->selection == NULL)
+                return;
+
+        if (dialog->duplicates != NULL) {
+                g_list_free_full (dialog->duplicates, conflict_data_free);
+                dialog->duplicates = NULL;
+        }
+
+        if (have_unallowed_character (dialog))
+                return;
+
+        update_tags (dialog);
+
+        if (dialog->new_names != NULL)
+                g_list_free_full (dialog->new_names, string_free);
+
+        if (!tag_data->set && !tag_data0->set && !tag_data00->set) {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_label), "");
+                gtk_widget_hide (dialog->numbering_order_button);
+        } else {
+                gtk_label_set_label (GTK_LABEL (dialog->numbering_label), _("Automatic Numbering Order"));
+                gtk_widget_show (dialog->numbering_order_button);
+        }
+
+        dialog->new_names = batch_rename_dialog_get_new_names (dialog);
+        dialog->checked_parents = 0;
+
+        file_names_list_has_duplicates_async (dialog,
+                                              file_names_list_has_duplicates_callback,
+                                              NULL);
+}
+
+static void
+file_names_widget_entry_on_changed (NautilusBatchRenameDialog *dialog)
+{
+        update_display_text (dialog);
+}
+
+static void
+batch_rename_dialog_mode_changed (NautilusBatchRenameDialog *dialog)
+{
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->format_mode_button))) {
+                gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "format");
+
+                dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT;
+
+                gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->name_entry));
+
+        } else {
+                gtk_stack_set_visible_child_name (GTK_STACK (dialog->mode_stack), "replace");
+
+                dialog->mode = NAUTILUS_BATCH_RENAME_DIALOG_REPLACE;
+
+                gtk_entry_grab_focus_without_selecting (GTK_ENTRY (dialog->find_entry));
+        }
+
+        update_display_text (dialog);
+}
+
+static void
+add_button_clicked (NautilusBatchRenameDialog *dialog)
+{
+        if (gtk_widget_is_visible (dialog->add_popover))
+                gtk_widget_set_visible (dialog->add_popover, FALSE);
+        else
+                gtk_widget_set_visible (dialog->add_popover, TRUE);
+}
+
+static void
+add_popover_closed (NautilusBatchRenameDialog *dialog)
+{
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->add_button), FALSE);
+}
+
+static void
+numbering_order_button_clicked (NautilusBatchRenameDialog *dialog)
+{
+        if (gtk_widget_is_visible (dialog->numbering_order_popover))
+                gtk_widget_set_visible (dialog->numbering_order_popover, FALSE);
+        else
+                gtk_widget_set_visible (dialog->numbering_order_popover, TRUE);
+}
+
+static void
+numbering_order_popover_closed (NautilusBatchRenameDialog *dialog)
+{
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE);
+}
+
+void
+nautilus_batch_rename_dialog_query_finished (NautilusBatchRenameDialog *dialog,
+                                             GHashTable                *hash_table,
+                                             GList                     *selection_metadata)
+{
+        GMenuItem *first_created;
+        GMenuItem *last_created;
+        FileMetadata *metadata;
+        TagData *tag_data;
+
+        /* for files with no metadata */
+        if (hash_table != NULL && g_hash_table_size (hash_table) == 0) {
+                g_hash_table_destroy (hash_table);
+
+                hash_table = NULL;
+        }
+
+        if (hash_table == NULL)
+                dialog->create_date = NULL;
+        else
+                dialog->create_date = hash_table;
+
+        if (dialog->create_date != NULL) {
+                first_created = g_menu_item_new ("First Created",
+                                                 "dialog.numbering-order-changed('first-created')");
+
+                g_menu_append_item (dialog->numbering_order_menu, first_created);
+
+                last_created = g_menu_item_new ("Last Created",
+                                                 "dialog.numbering-order-changed('last-created')");
+
+                g_menu_append_item (dialog->numbering_order_menu, last_created);
+
+        }
+
+        dialog->selection_metadata = selection_metadata;
+        metadata = selection_metadata->data;
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+        if (metadata->creation_date == NULL || g_strcmp0 (metadata->creation_date->str, "") == 0) {
+               disable_action (dialog, "add-creation-date-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+        if (metadata->equipment == NULL || g_strcmp0 (metadata->equipment->str, "") == 0) {
+               disable_action (dialog, "add-equipment-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+        if (metadata->season == NULL || g_strcmp0 (metadata->season->str, "") == 0) {
+               disable_action (dialog, "add-season-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+        if (metadata->episode_number == NULL || g_strcmp0 (metadata->episode_number->str, "") == 0) {
+               disable_action (dialog, "add-episode-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+        if (metadata->track_number == NULL || g_strcmp0 (metadata->track_number->str, "") == 0) {
+               disable_action (dialog, "add-track-number-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+        if (metadata->artist_name == NULL || g_strcmp0 (metadata->artist_name->str, "") == 0) {
+               disable_action (dialog, "add-artist-name-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+        if (metadata->title == NULL || g_strcmp0 (metadata->title->str, "") == 0) {
+               disable_action (dialog, "add-title-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+        if (metadata->album_name == NULL || g_strcmp0 (metadata->album_name->str, "") == 0) {
+               disable_action (dialog, "add-album-name-tag");
+               tag_data->available = FALSE;
+        } else {
+                tag_data->set = FALSE;
+        }
+}
+
+static void
+update_row_shadowing (GtkWidget *row,
+                      gboolean   shown)
+{
+        GtkStyleContext *context;
+        GtkStateFlags flags;
+
+        if (!GTK_IS_LIST_BOX_ROW (row))
+                return;
+
+        context = gtk_widget_get_style_context (row);
+        flags = gtk_style_context_get_state (context);
+
+        if (shown)
+                flags |= GTK_STATE_PRELIGHT;
+        else
+                flags &= ~GTK_STATE_PRELIGHT;
+
+        gtk_style_context_set_state (context, flags);
+
+}
+
+static gboolean
+on_leave_event (GtkWidget      *widget,
+                      GdkEventMotion *event,
+                      gpointer        user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        update_row_shadowing (dialog->preselected_row1, FALSE);
+        update_row_shadowing (dialog->preselected_row2, FALSE);
+
+        dialog->preselected_row1 = NULL;
+        dialog->preselected_row2 = NULL;
+
+        return FALSE;
+}
+
+static gboolean
+on_motion (GtkWidget      *widget,
+           GdkEventMotion *event,
+           gpointer        user_data)
+{
+        GtkListBoxRow *row;
+        NautilusBatchRenameDialog *dialog;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        if (dialog->preselected_row1 && dialog->preselected_row2) {
+                update_row_shadowing (dialog->preselected_row1, FALSE);
+                update_row_shadowing (dialog->preselected_row2, FALSE);
+        }
+
+        if (widget == dialog->result_listbox) {
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row1 = GTK_WIDGET (row);
+
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row2 = GTK_WIDGET (row);
+
+        }
+
+        if (widget == dialog->arrow_listbox) {
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->original_name_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row1 = GTK_WIDGET (row);
+
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row2 = GTK_WIDGET (row);
+        }
+
+        if (widget == dialog->original_name_listbox) {
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->result_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row1 = GTK_WIDGET (row);
+
+                row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (dialog->arrow_listbox), event->y);
+                update_row_shadowing (GTK_WIDGET (row), TRUE);
+                dialog->preselected_row2 = GTK_WIDGET (row);
+        }
+
+        return FALSE;
+}
+
+static void
+nautilus_batch_rename_dialog_initialize_actions (NautilusBatchRenameDialog *dialog)
+{
+        GAction *action;
+
+        dialog->action_group = G_ACTION_GROUP (g_simple_action_group_new ());
+
+        g_action_map_add_action_entries (G_ACTION_MAP (dialog->action_group),
+                                        dialog_entries,
+                                        G_N_ELEMENTS (dialog_entries),
+                                        dialog);
+        gtk_widget_insert_action_group (GTK_WIDGET (dialog),
+                                        "dialog",
+                                        G_ACTION_GROUP (dialog->action_group));
+
+        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                             "add-original-file-name-tag");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+
+        check_metadata_for_selection (dialog, dialog->selection);
+}
+
+static void
+file_names_widget_on_activate (NautilusBatchRenameDialog *dialog)
+{
+        prepare_batch_rename (dialog);
+}
+
+static gboolean
+remove_tag (NautilusBatchRenameDialog *dialog,
+            gchar                     *tag_name,
+            gchar                     *action_name,
+            gint                       keyval,
+            gboolean                   is_modifier)
+{
+        TagData *tag_data;
+        gint cursor_position;
+        GString *new_entry_text;
+        GString *entry_text;
+        gboolean delete_tag;
+        GAction *action;
+
+        delete_tag = FALSE;
+
+        g_object_get (dialog->name_entry, "cursor-position", &cursor_position, NULL);
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name);
+
+        entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
+
+        if (!tag_data->set)
+                return FALSE;
+
+        if (keyval == GDK_KEY_BackSpace) {
+                if (cursor_position > tag_data->position &&
+                    cursor_position <= tag_data->position + g_utf8_strlen (tag_name, -1)) {
+                        delete_tag = TRUE;
+                }
+        }
+
+        if (keyval == GDK_KEY_Delete) {
+                if (cursor_position >= tag_data->position &&
+                    cursor_position < tag_data->position + g_utf8_strlen (tag_name, -1)) {
+                        delete_tag = TRUE;
+                }
+        }
+
+        if (!is_modifier &&
+            keyval != GDK_KEY_Left &&
+            keyval != GDK_KEY_Right &&
+            keyval != GDK_KEY_Delete &&
+            keyval != GDK_KEY_Return &&
+            keyval != GDK_KEY_Escape &&
+            keyval != GDK_KEY_Tab &&
+            keyval != GDK_KEY_End &&
+            keyval != GDK_KEY_Home) {
+                if (cursor_position > tag_data->position &&
+                    cursor_position < tag_data->position + g_utf8_strlen (tag_name, -1)) {
+                        delete_tag = TRUE;
+                }
+        }
+
+        if (delete_tag) {
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                     action_name);
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                new_entry_text = g_string_new ("");
+                new_entry_text = g_string_append_len (new_entry_text,
+                                                      entry_text->str,
+                                                      tag_data->position);
+                new_entry_text = g_string_append (new_entry_text,
+                                                  g_utf8_offset_to_pointer (entry_text->str,
+                                                                            tag_data->position + 
g_utf8_strlen (tag_name, -1)));
+
+                tag_data->set = FALSE;
+                dialog->cursor_position = tag_data->position;
+                dialog->use_manual_cursor_position = TRUE;
+                gtk_entry_set_text (GTK_ENTRY (dialog->name_entry), new_entry_text->str);
+                gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), tag_data->position);
+                dialog->use_manual_cursor_position = FALSE;
+
+                g_string_free (new_entry_text, TRUE);
+                g_string_free (entry_text, TRUE);
+
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static GString*
+remove_tag_selection (NautilusBatchRenameDialog *dialog,
+                      GString                   *old_entry_text,
+                      gchar                     *action_name,
+                      gchar                     *tag_name,
+                      gint                       start,
+                      gint                       end)
+{
+        TagData *tag_data;
+        GAction *action;
+        GString *new_entry_text;
+
+        new_entry_text = NULL;
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name);
+
+        if (tag_data->set && tag_data->original_position < end &&
+            tag_data->original_position + g_utf8_strlen (tag_name, -1) > start) {
+                new_entry_text = g_string_new ("");
+                new_entry_text = g_string_append_len (new_entry_text,
+                                                      old_entry_text->str,
+                                                      tag_data->new_position);
+                new_entry_text = g_string_append (new_entry_text,
+                                                  g_utf8_offset_to_pointer (old_entry_text->str,
+                                                                            tag_data->new_position + 
g_utf8_strlen (tag_name, -1)));
+
+                tag_data->set = FALSE;
+
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                     action_name);
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+        }
+
+        if (new_entry_text == NULL)
+                return g_string_new (old_entry_text->str);
+        return new_entry_text;
+}
+
+static void
+update_tag_position (NautilusBatchRenameDialog *dialog,
+                     gchar                     *tag_name,
+                     GString                   *new_entry_text)
+{
+        TagData *tag_data;
+
+        tag_data = g_hash_table_lookup (dialog->tag_info_table, tag_name);
+
+        if (tag_data->set) {
+                tag_data->original_position = tag_data->position;
+                tag_data->new_position = g_utf8_pointer_to_offset(new_entry_text->str,
+                                                                  g_strrstr (new_entry_text->str, tag_name));
+        }
+}
+
+static gboolean
+on_key_press_event (GtkWidget    *widget,
+                    GdkEventKey  *event,
+                    gpointer      user_data)
+{
+        NautilusBatchRenameDialog *dialog;
+        gint keyval;
+        GdkEvent *gdk_event;
+        GString *old_entry_text;
+        GString *new_entry_text;
+        gboolean entry_has_selection;
+        gint start;
+        gint end;
+        gboolean tag_removed;
+        TagData *tag_data;
+        gint minimum_tag_position;
+        GAction *action;
+
+        gdk_event = (GdkEvent *) event;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
+
+        keyval = event->keyval;
+
+        entry_has_selection =  (gtk_editable_get_selection_bounds (GTK_EDITABLE (dialog->name_entry),
+                                                                   &start,
+                                                                   &end));
+
+        if (event->state & GDK_CONTROL_MASK)
+                return GDK_EVENT_PROPAGATE;
+
+        if (entry_has_selection &&
+            ((keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) ||
+            (!gdk_event->key.is_modifier &&
+            keyval != GDK_KEY_Left &&
+            keyval != GDK_KEY_Right &&
+            keyval != GDK_KEY_Return &&
+            keyval != GDK_KEY_Escape &&
+            keyval != GDK_KEY_Tab &&
+            keyval != GDK_KEY_End &&
+            keyval != GDK_KEY_Home))) {
+                old_entry_text = g_string_new (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
+
+                minimum_tag_position = G_MAXINT;
+                tag_removed = FALSE;
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ORIGINAL_FILE_NAME);
+                if (tag_data->set) {
+                        update_tag_position (dialog, ORIGINAL_FILE_NAME, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-original-file-name-tag",
+                                                               ORIGINAL_FILE_NAME,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CREATION_DATE);
+                if (tag_data->set) {
+                        update_tag_position (dialog, CREATION_DATE, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-creation-date-tag",
+                                                               CREATION_DATE,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING);
+                if (tag_data->set) {
+                        update_tag_position (dialog, NUMBERING, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-numbering-tag-zero",
+                                                               NUMBERING,
+                                                               start,
+                                                               end);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-one");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-two");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING0);
+                if (tag_data->set) {
+                        update_tag_position (dialog, NUMBERING0, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-numbering-tag-one",
+                                                               NUMBERING0,
+                                                               start,
+                                                               end);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-zero");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-two");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, NUMBERING00);
+                if (tag_data->set) {
+                        update_tag_position (dialog, NUMBERING00, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-numbering-tag-two",
+                                                               NUMBERING0,
+                                                               start,
+                                                               end);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-one");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                           "add-numbering-tag-zero");
+                        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, CAMERA_MODEL);
+                if (tag_data->set) {
+                        update_tag_position (dialog, CAMERA_MODEL, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-equipment-tag",
+                                                               CAMERA_MODEL,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TRACK_NUMBER);
+                if (tag_data->set) {
+                        update_tag_position (dialog, TRACK_NUMBER, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-track-number-tag",
+                                                               TRACK_NUMBER,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, SEASON_NUMBER);
+                if (tag_data->set) {
+                        update_tag_position (dialog, SEASON_NUMBER, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-season-tag",
+                                                               SEASON_NUMBER,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, EPISODE_NUMBER);
+                if (tag_data->set) {
+                        update_tag_position (dialog, EPISODE_NUMBER, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-episode-tag",
+                                                               EPISODE_NUMBER,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ARTIST_NAME);
+                if (tag_data->set) {
+                        update_tag_position (dialog, ARTIST_NAME, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-artist-name-tag",
+                                                               ARTIST_NAME,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, TITLE);
+                if (tag_data->set) {
+                        update_tag_position (dialog, TITLE, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-title-tag",
+                                                               TITLE,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                tag_data = g_hash_table_lookup (dialog->tag_info_table, ALBUM_NAME);
+                if (tag_data->set) {
+                        update_tag_position (dialog, ALBUM_NAME, old_entry_text);
+                        new_entry_text = remove_tag_selection (dialog,
+                                                               old_entry_text,
+                                                               "add-album-name-tag",
+                                                               ALBUM_NAME,
+                                                               start,
+                                                               end);
+
+                        if (!g_string_equal (new_entry_text, old_entry_text)) {
+                                if (tag_data->position < minimum_tag_position)
+                                        minimum_tag_position = tag_data->position;
+
+                                tag_removed = TRUE;
+                        }
+                        g_string_free (old_entry_text, TRUE);
+                        old_entry_text = new_entry_text;
+                }
+
+                if (minimum_tag_position != G_MAXINT) {
+                        dialog->use_manual_cursor_position = TRUE;
+                        dialog->cursor_position = minimum_tag_position;
+
+                        gtk_entry_set_text (GTK_ENTRY (dialog->name_entry), new_entry_text->str);
+                        gtk_editable_set_position (GTK_EDITABLE (dialog->name_entry), minimum_tag_position);
+
+                        dialog->use_manual_cursor_position = FALSE;
+
+                        g_string_free (new_entry_text, TRUE);
+                }
+
+                if ((keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) &&
+                    tag_removed)
+                        return TRUE;
+
+                return GDK_EVENT_PROPAGATE;
+        }
+
+        tag_removed = FALSE;
+
+        if (remove_tag (dialog,
+                        ORIGINAL_FILE_NAME,
+                        "add-original-file-name-tag",
+                        keyval,
+                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        CREATION_DATE,
+                                        "add-creation-date-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        NUMBERING,
+                                        "add-numbering-tag-zero",
+                                        keyval,
+                                        gdk_event->key.is_modifier)) {
+                tag_removed = TRUE;
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-one");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-two");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+        }
+
+        if (!tag_removed && remove_tag (dialog,
+                                        NUMBERING0,
+                                        "add-numbering-tag-one",
+                                        keyval,
+                                        gdk_event->key.is_modifier)) {
+                tag_removed = TRUE;
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-zero");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-two");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+        }
+
+        if (!tag_removed && remove_tag (dialog,
+                                        NUMBERING00,
+                                        "add-numbering-tag-two",
+                                        keyval,
+                                        gdk_event->key.is_modifier)) {
+                tag_removed = TRUE;
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-one");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+
+                action = g_action_map_lookup_action (G_ACTION_MAP (dialog->action_group),
+                                                                   "add-numbering-tag-zero");
+                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+        }
+
+        if (!tag_removed && remove_tag (dialog,
+                                        CAMERA_MODEL,
+                                        "add-equipment-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        SEASON_NUMBER,
+                                        "add-season-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        EPISODE_NUMBER,
+                                        "add-episode-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        TRACK_NUMBER,
+                                        "add-track-number-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        ARTIST_NAME,
+                                        "add-artist-name-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        TITLE,
+                                        "add-title-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (!tag_removed && remove_tag (dialog,
+                                        ALBUM_NAME,
+                                        "add-album-name-tag",
+                                        keyval,
+                                        gdk_event->key.is_modifier))
+                tag_removed = TRUE;
+
+        if (tag_removed) {
+                if (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace)
+                        return TRUE;
+
+                return GDK_EVENT_PROPAGATE;
+        }
+
+        return GDK_EVENT_PROPAGATE;
+}
+
+static void
+nautilus_batch_rename_dialog_finalize (GObject *object)
+{
+        NautilusBatchRenameDialog *dialog;
+        GList *l;
+
+        dialog = NAUTILUS_BATCH_RENAME_DIALOG (object);
+
+        if (dialog->checking_conflicts) {
+                g_cancellable_cancel (dialog->conflict_cancellable);
+                g_object_unref (dialog->conflict_cancellable);
+        }
+
+        g_list_free (dialog->original_name_listbox_rows);
+        g_list_free (dialog->arrow_listbox_rows);
+        g_list_free (dialog->result_listbox_rows);
+        g_list_free (dialog->listbox_labels_new);
+        g_list_free (dialog->listbox_labels_old);
+        g_list_free (dialog->listbox_icons);
+
+        for (l = dialog->selection_metadata; l != NULL; l = l->next) {
+                FileMetadata *metadata;
+
+                metadata = l->data;
+
+                if (metadata->file_name != NULL)
+                        g_string_free (metadata->file_name, TRUE);
+                if (metadata->creation_date != NULL)
+                        g_string_free (metadata->creation_date, TRUE);
+                if (metadata->equipment != NULL)
+                        g_string_free (metadata->equipment, TRUE);
+                if (metadata->season != NULL)
+                        g_string_free (metadata->season, TRUE);
+                if (metadata->episode_number != NULL)
+                        g_string_free (metadata->episode_number, TRUE);
+                if (metadata->track_number != NULL)
+                        g_string_free (metadata->track_number, TRUE);
+                if (metadata->artist_name != NULL)
+                        g_string_free (metadata->artist_name, TRUE);
+                if (metadata->album_name != NULL)
+                        g_string_free (metadata->album_name, TRUE);
+        }
+
+        if (dialog->create_date != NULL)
+                g_hash_table_destroy (dialog->create_date);
+
+        g_list_free_full (dialog->distinct_parents, g_free);
+        g_list_free_full (dialog->new_names, string_free);
+        g_list_free_full (dialog->duplicates, conflict_data_free);
+
+        G_OBJECT_CLASS (nautilus_batch_rename_dialog_parent_class)->finalize (object);
+}
+
+static void
+nautilus_batch_rename_dialog_class_init (NautilusBatchRenameDialogClass *klass)
+{
+        GtkWidgetClass *widget_class;
+        GObjectClass *oclass;
+
+        widget_class = GTK_WIDGET_CLASS (klass);
+        oclass = G_OBJECT_CLASS (klass);
+
+        oclass->finalize = nautilus_batch_rename_dialog_finalize;
+
+        gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-batch-rename-dialog.ui");
+
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, grid);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, cancel_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, 
original_name_listbox);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, arrow_listbox);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, result_listbox);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, name_entry);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, rename_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, find_entry);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_entry);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, mode_stack);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, replace_mode_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, format_mode_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_popover);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, 
numbering_order_label);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, scrolled_window);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, 
numbering_order_popover);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, 
numbering_order_button);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_order_menu);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_box);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_label);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_up);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, conflict_down);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, add_tag_menu);
+        gtk_widget_class_bind_template_child (widget_class, NautilusBatchRenameDialog, numbering_label);
+
+        gtk_widget_class_bind_template_callback (widget_class, file_names_widget_entry_on_changed);
+        gtk_widget_class_bind_template_callback (widget_class, file_names_widget_on_activate);
+        gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_mode_changed);
+        gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
+        gtk_widget_class_bind_template_callback (widget_class, add_popover_closed);
+        gtk_widget_class_bind_template_callback (widget_class, numbering_order_button_clicked);
+        gtk_widget_class_bind_template_callback (widget_class, numbering_order_popover_closed);
+        gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_up);
+        gtk_widget_class_bind_template_callback (widget_class, select_next_conflict_down);
+        gtk_widget_class_bind_template_callback (widget_class, batch_rename_dialog_on_response);
+        gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
+}
+
+GtkWidget*
+nautilus_batch_rename_dialog_new (GList             *selection,
+                                  NautilusDirectory *directory,
+                                  NautilusWindow    *window)
+{
+        NautilusBatchRenameDialog *dialog;
+        GString *dialog_title;
+
+        dialog = g_object_new (NAUTILUS_TYPE_BATCH_RENAME_DIALOG, "use-header-bar", TRUE, NULL);
+
+        dialog->selection = selection;
+        dialog->directory = directory;
+        dialog->window = window;
+
+        gtk_window_set_transient_for (GTK_WINDOW (dialog),
+                                      GTK_WINDOW (window));
+
+        dialog_title = g_string_new ("");
+        g_string_append_printf (dialog_title, "Rename %d Files", g_list_length (selection));
+        gtk_window_set_title (GTK_WINDOW (dialog), dialog_title->str);
+
+        nautilus_batch_rename_dialog_initialize_actions (dialog);
+
+        dialog->same_parent = !NAUTILUS_IS_SEARCH_DIRECTORY (directory);
+
+        if (!dialog->same_parent)
+                dialog->distinct_parents = batch_rename_files_get_distinct_parents (dialog->selection);
+        else
+                dialog->distinct_parents = NULL;
+
+        update_display_text (dialog);
+
+        fill_display_listbox (dialog);
+
+        gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL);
+
+        g_string_free (dialog_title, TRUE);
+
+        return GTK_WIDGET (dialog);
+}
+
+static void
+nautilus_batch_rename_dialog_init (NautilusBatchRenameDialog *self)
+{
+        TagData *tag_data;
+
+        gtk_widget_init_template (GTK_WIDGET (self));
+
+        gtk_list_box_set_header_func (GTK_LIST_BOX (self->original_name_listbox),
+                                                    (GtkListBoxUpdateHeaderFunc) listbox_header_func,
+                                                    self,
+                                                    NULL);
+        gtk_list_box_set_header_func (GTK_LIST_BOX (self->arrow_listbox),
+                                                    (GtkListBoxUpdateHeaderFunc) listbox_header_func,
+                                                    self,
+                                                    NULL);
+        gtk_list_box_set_header_func (GTK_LIST_BOX (self->result_listbox),
+                                                    (GtkListBoxUpdateHeaderFunc) listbox_header_func,
+                                                    self,
+                                                    NULL);
+
+
+        self->mode = NAUTILUS_BATCH_RENAME_DIALOG_FORMAT;
+
+        gtk_popover_bind_model (GTK_POPOVER (self->numbering_order_popover),
+                                G_MENU_MODEL (self->numbering_order_menu),
+                                NULL);
+        gtk_popover_bind_model (GTK_POPOVER (self->add_popover),
+                                G_MENU_MODEL (self->add_tag_menu),
+                                NULL);
+
+        gtk_label_set_ellipsize (GTK_LABEL (self->conflict_label), PANGO_ELLIPSIZE_END);
+        gtk_label_set_max_width_chars (GTK_LABEL (self->conflict_label), 1);
+
+        self->duplicates = NULL;
+        self->new_names = NULL;
+
+        self->checking_conflicts = FALSE;
+
+        self->rename_clicked = FALSE;
+
+
+        self->tag_info_table = g_hash_table_new_full (g_str_hash,
+                                                      g_str_equal,
+                                                      (GDestroyNotify) g_free,
+                                                      (GDestroyNotify) g_free);
+        tag_data = g_new (TagData, 1);
+        tag_data->available = TRUE;
+        tag_data->set = TRUE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (ORIGINAL_FILE_NAME), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = TRUE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = TRUE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING0), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = TRUE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (NUMBERING00), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (CREATION_DATE), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (CAMERA_MODEL), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (SEASON_NUMBER), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (EPISODE_NUMBER), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (TRACK_NUMBER), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (ARTIST_NAME), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (TITLE), tag_data);
+
+        tag_data = g_new (TagData, 1);
+        tag_data->available = FALSE;
+        tag_data->set = FALSE;
+        tag_data->position = 0;
+        g_hash_table_insert (self->tag_info_table, g_strdup (ALBUM_NAME), tag_data);
+
+        gtk_entry_set_text (GTK_ENTRY (self->name_entry),ORIGINAL_FILE_NAME);
+        self->name_entry_characters = g_utf8_strlen (ORIGINAL_FILE_NAME, -1);
+
+        self->row_height = -1;
+
+        g_signal_connect (self->original_name_listbox, "row-selected", G_CALLBACK (row_selected), self);
+        g_signal_connect (self->arrow_listbox, "row-selected", G_CALLBACK (row_selected), self);
+        g_signal_connect (self->result_listbox, "row-selected", G_CALLBACK (row_selected), self);
+
+        self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+        g_signal_connect (self->original_name_listbox,
+                          "motion-notify-event",
+                          G_CALLBACK (on_motion),
+                          self);
+        g_signal_connect (self->result_listbox,
+                          "motion-notify-event",
+                          G_CALLBACK (on_motion),
+                          self);
+        g_signal_connect (self->arrow_listbox,
+                          "motion-notify-event",
+                          G_CALLBACK (on_motion),
+                          self);
+
+        g_signal_connect (self->original_name_listbox,
+                          "leave-notify-event",
+                          G_CALLBACK (on_leave_event),
+                          self);
+        g_signal_connect (self->result_listbox,
+                          "leave-notify-event",
+                          G_CALLBACK (on_leave_event),
+                          self);
+        g_signal_connect (self->arrow_listbox,
+                          "leave-notify-event",
+                          G_CALLBACK (on_leave_event),
+                          self);
+}
\ No newline at end of file
diff --git a/src/nautilus-batch-rename-dialog.h b/src/nautilus-batch-rename-dialog.h
new file mode 100644
index 0000000..3f11f4e
--- /dev/null
+++ b/src/nautilus-batch-rename-dialog.h
@@ -0,0 +1,100 @@
+/* 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/>.
+ */
+
+#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]"
+#define ALBUM_NAME "[Album name]"
+
+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;
+        GString *album_name;
+} 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_files                               (NautilusBatchRenameDialog *dialog,
+                                                                        NautilusDirectory         *directory,
+                                                                        GList                     *files);
+
+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..7fb9383
--- /dev/null
+++ b/src/nautilus-batch-rename-utilities.c
@@ -0,0 +1,1050 @@
+/* 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>
+
+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;
+        gboolean have_album_name;
+} QueryData;
+
+enum {
+       FILE_NAME_INDEX,
+       CREATION_DATE_INDEX,
+       YEAR_INDEX,
+       MONTH_INDEX,
+       DAY_INDEX,
+       HOURS_INDEX,
+       MINUTES_INDEX,
+       SECONDS_INDEX,
+       CAMERA_MODEL_INDEX,
+       SEASON_INDEX,
+       EPISODE_NUMBER_INDEX,
+       TRACK_NUMBER_INDEX,
+       ARTIST_NAME_INDEX,
+       TITLE_INDEX,
+       ALBUM_NAME_INDEX,
+
+} QueryMetadata;
+
+static void on_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 *conflict_data = mem;
+
+        g_free (conflict_data->name);
+        g_free (conflict_data);
+}
+
+static GString*
+batch_rename_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;
+}
+
+/* This function changes the background color of the replaced part of the name */
+GString*
+batch_rename_replace_label_text (gchar       *label,
+                                 const gchar *substring)
+{
+        GString *new_label;
+        gchar **splitted_string;
+        gchar *token;
+        gint i, n_splits;
+
+        new_label = g_string_new ("");
+
+        if (substring == NULL || g_strcmp0 (substring, "") == 0) {
+                token = g_markup_escape_text (label, g_utf8_strlen (label, -1));
+                new_label = g_string_append (new_label, token);
+                g_free (token);
+
+                return new_label;
+        }
+
+        splitted_string = g_strsplit (label, substring, -1);
+        if (splitted_string == NULL) {
+                token = g_markup_escape_text (label, g_utf8_strlen (label, -1));
+                new_label = g_string_append (new_label, token);
+                g_free (token);
+
+                return new_label;
+        }
+
+        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_label = g_string_append (new_label, token);
+
+                g_free (token);
+
+                if (i != n_splits - 1) {
+                        token = g_markup_escape_text (substring, g_utf8_strlen (substring, -1));
+                        g_string_append_printf (new_label,
+                                                "<span background=\'#f57900\' color='white'>%s</span>",
+                                                token);
+
+                        g_free (token);
+                }
+        }
+
+        g_strfreev (splitted_string);
+
+        return new_label;
+}
+
+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;
+
+                        if (g_strcmp0 (metadata, "album_name") == 0 &&
+                            file_metadata->album_name != NULL &&
+                            file_metadata->album_name->len != 0)
+                                return file_metadata->album_name->str;
+                }
+        }
+
+        return NULL;
+}
+
+static GString*
+batch_rename_format (NautilusFile *file,
+                     GList        *text_chunks,
+                     GList        *selection_metadata,
+                     gint          count)
+{
+        GList *l;
+        GString *tag;
+        GString *new_name;
+        gboolean added_tag;
+        g_autofree gchar *file_name;
+        g_autofree gchar *extension;
+        gchar *metadata;
+        gchar *base_name;
+
+        file_name = nautilus_file_get_display_name (file);
+        extension = nautilus_file_get_extension (file);
+
+        new_name = g_string_new ("");
+
+        for (l = text_chunks; 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) {
+                        g_string_append_printf (new_name, "%02d", count);
+
+                        added_tag = TRUE;
+                }
+
+                if (!added_tag && g_strcmp0 (tag->str, NUMBERING00) == 0) {
+                        g_string_append_printf (new_name, "%03d", 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) {
+                                new_name = g_string_append (new_name, metadata);
+                                added_tag = TRUE;
+                        }
+                }
+
+                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 && g_strcmp0 (tag->str, ALBUM_NAME) == 0) {
+                        metadata = get_metadata (selection_metadata, file_name, "album_name");
+
+                        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                        *text_chunks,
+                                        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_format (file,
+                                                        text_chunks,
+                                                        selection_metadata,
+                                                        count++);
+                        result = g_list_prepend (result, new_name);
+                }
+
+                if (mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE) {
+                        new_name = batch_rename_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);
+        }
+
+        /* the case this function searched for doesn't exist, so the file
+         * has a conlfict */
+        return FALSE;
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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;
+}
+
+static 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           *query_data,
+             TrackerSparqlCursor *cursor)
+{
+        tracker_sparql_cursor_next_async (cursor,
+                                          NULL,
+                                          on_cursor_callback,
+                                          query_data);
+}
+
+static void
+on_cursor_callback (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+        GHashTable *hash_table;
+        TrackerSparqlCursor *cursor;
+        gboolean success;
+        QueryData *query_data;
+        GError *error;
+        GList *l;
+        FileMetadata *metadata;
+        FileMetadata *metadata_clear;
+        GDateTime *datetime;
+        gchar *date;
+        const gchar *file_name;
+        const gchar *creation_date;
+        const gchar *year;
+        const gchar *month;
+        const gchar *day;
+        const gchar *hours;
+        const gchar *minutes;
+        const gchar *seconds;
+        const gchar *equipment;
+        const gchar *season_number;
+        const gchar *episode_number;
+        const gchar *track_number;
+        const gchar *artist_name;
+        const gchar *title;
+        const gchar *album_name;
+
+        error = NULL;
+        metadata = NULL;
+
+        cursor = TRACKER_SPARQL_CURSOR (object);
+        query_data = user_data;
+        hash_table = query_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 (query_data->dialog,
+                                                             query_data->hash_table,
+                                                             query_data->selection_metadata);
+
+                return;
+        }
+
+        creation_date = tracker_sparql_cursor_get_string (cursor, CREATION_DATE_INDEX, NULL);
+
+        year = tracker_sparql_cursor_get_string (cursor, YEAR_INDEX, NULL);
+        month = tracker_sparql_cursor_get_string (cursor, MONTH_INDEX, NULL);
+        day = tracker_sparql_cursor_get_string (cursor, DAY_INDEX, NULL);
+        hours = tracker_sparql_cursor_get_string (cursor, HOURS_INDEX, NULL);
+        minutes = tracker_sparql_cursor_get_string (cursor, MINUTES_INDEX, NULL);
+        seconds = tracker_sparql_cursor_get_string (cursor, SECONDS_INDEX, NULL);
+        equipment = tracker_sparql_cursor_get_string (cursor, CAMERA_MODEL_INDEX, NULL);
+        season_number = tracker_sparql_cursor_get_string (cursor, SEASON_INDEX, NULL);
+        episode_number = tracker_sparql_cursor_get_string (cursor, EPISODE_NUMBER_INDEX, NULL);
+        track_number = tracker_sparql_cursor_get_string (cursor, TRACK_NUMBER_INDEX, NULL);
+        artist_name = tracker_sparql_cursor_get_string (cursor, ARTIST_NAME_INDEX, NULL);
+        title = tracker_sparql_cursor_get_string (cursor, TITLE_INDEX, NULL);
+        album_name = tracker_sparql_cursor_get_string (cursor, ALBUM_NAME_INDEX, NULL);
+
+        /* creation date used for sorting criteria */
+        if (creation_date == NULL) {
+                if (hash_table != NULL)
+                        g_hash_table_destroy (hash_table);
+
+                query_data->hash_table = NULL;
+                query_data->have_creation_date = FALSE;
+        } else {
+                if (query_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, FILE_NAME_INDEX, NULL);
+        for (l = query_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 (query_data->have_creation_date) {
+                if (!creation_date) {
+                        query_data->have_creation_date = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->creation_date != NULL) {
+                                        g_string_free (metadata_clear->creation_date, TRUE);
+                                        metadata_clear->creation_date = NULL;
+                                }
+                        }
+                } else {
+                        datetime = g_date_time_new_local (atoi (year),
+                                                          atoi (month),
+                                                          atoi (day),
+                                                          atoi (hours),
+                                                          atoi (minutes),
+                                                          atoi (seconds));
+
+                        date = g_date_time_format (datetime, "%x");
+
+                        if (strstr (date, "/") != NULL) {
+                                metadata->creation_date = batch_rename_replace (date, "/", "-");
+                        } else {
+                                metadata->creation_date = g_string_new (date);
+                        }
+
+                        g_free (date);
+                }
+        }
+
+        /* equipment */
+        if (query_data->have_equipment) {
+                if (equipment == NULL) {
+                        query_data->have_equipment = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->equipment != NULL) {
+                                        g_string_free (metadata_clear->equipment, TRUE);
+                                        metadata_clear->equipment = NULL;
+                                }
+                        }
+                } else {
+                        metadata->equipment = g_string_new (equipment);
+                }
+        }
+
+        /* season number */
+        if (query_data->have_season) {
+                if (season_number == NULL) {
+                        query_data->have_season = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->season != NULL) {
+                                        g_string_free (metadata_clear->season, TRUE);
+                                        metadata_clear->season = NULL;
+                                }
+                        }
+                } else {
+                        metadata->season = g_string_new (season_number);
+                }
+        }
+
+        /* episode number */
+        if (query_data->have_episode_number) {
+                if (episode_number == NULL) {
+                        query_data->have_episode_number = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->episode_number != NULL) {
+                                        g_string_free (metadata_clear->episode_number, TRUE);
+                                        metadata_clear->episode_number = NULL;
+                                }
+                        }
+                } else {
+                        metadata->episode_number = g_string_new (episode_number);
+                }
+        }
+
+        /* track number */
+        if (query_data->have_track_number) {
+                if (track_number == NULL) {
+                        query_data->have_track_number = FALSE;
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->track_number != NULL) {
+                                        g_string_free (metadata_clear->track_number, TRUE);
+                                        metadata_clear->track_number = NULL;
+                                }
+                        }
+                } else {
+                        metadata->track_number = g_string_new (track_number);
+                }
+        }
+
+        /* artist name */
+        if (query_data->have_artist_name) {
+                if (artist_name == NULL) {
+                        query_data->have_artist_name = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->artist_name != NULL) {
+                                        g_string_free (metadata_clear->artist_name, TRUE);
+                                        metadata_clear->artist_name = NULL;
+                                }
+                        }
+                } else {
+                        metadata->artist_name = g_string_new (artist_name);
+                }
+        }
+
+        /* title */
+        if (query_data->have_title) {
+                if (title == NULL) {
+                        query_data->have_title = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->title != NULL) {
+                                        g_string_free (metadata_clear->title, TRUE);
+                                        metadata_clear->title = NULL;
+                                }
+                        }
+                } else {
+                        metadata->title = g_string_new (title);
+                }
+        }
+
+        /* album name */
+        if (query_data->have_album_name) {
+                if (album_name == NULL) {
+                        query_data->have_album_name = FALSE;
+
+                        for (l = query_data->selection_metadata; l != NULL; l = l->next) {
+                                metadata_clear = l->data;
+
+                                if (metadata_clear->album_name != NULL) {
+                                        g_string_free (metadata_clear->album_name, TRUE);
+                                        metadata_clear->album_name = NULL;
+                                }
+                        }
+                } else {
+                        metadata->album_name = g_string_new (album_name);
+                }
+        }
+
+        /* Get next */
+        cursor_next (query_data, cursor);
+}
+
+static void
+batch_rename_dialog_query_callback (GObject      *object,
+                                    GAsyncResult *result,
+                                    gpointer      user_data)
+{
+        TrackerSparqlConnection *connection;
+        TrackerSparqlCursor *cursor;
+        QueryData *query_data;
+        GError *error;
+
+        error = NULL;
+
+        connection = TRACKER_SPARQL_CONNECTION (object);
+        query_data = user_data;
+
+        cursor = tracker_sparql_connection_query_finish (connection,
+                                                         result,
+                                                         &error);
+
+        if (error != NULL) {
+                g_warning ("Error on batch rename query for metadata: %s", error->message);
+                g_error_free (error);
+
+                nautilus_batch_rename_dialog_query_finished (query_data->dialog,
+                                                             query_data->hash_table,
+                                                             query_data->selection_metadata);
+        } else {
+                cursor_next (query_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 *query_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) "
+                              "year(nie:contentCreated(?file)) "
+                              "month(nie:contentCreated(?file)) "
+                              "day(nie:contentCreated(?file)) "
+                              "hours(nie:contentCreated(?file)) "
+                              "minutes(nie:contentCreated(?file)) "
+                              "seconds(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) "
+                              "nmm:albumTitle(nmm:musicAlbum(?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 = NULL;
+                metadata->equipment = NULL;
+                metadata->season = NULL;
+                metadata->episode_number = NULL;
+                metadata->track_number = NULL;
+                metadata->artist_name = NULL;
+                metadata->title = NULL;
+                metadata->album_name = NULL;
+
+                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);
+
+        query_data = g_new (QueryData, 1);
+        query_data->hash_table = hash_table;
+        query_data->dialog = dialog;
+        query_data->selection_metadata = selection_metadata;
+
+        query_data->have_season = TRUE;
+        query_data->have_creation_date = TRUE;
+        query_data->have_artist_name = TRUE;
+        query_data->have_track_number = TRUE;
+        query_data->have_equipment = TRUE;
+        query_data->have_episode_number = TRUE;
+        query_data->have_title = TRUE;
+        query_data->have_album_name = TRUE;
+
+        /* Make an asynchronous query to the store */
+        tracker_sparql_connection_query_async (connection,
+                                               query->str,
+                                               NULL,
+                                               batch_rename_dialog_query_callback,
+                                               query_data);
+
+        g_object_unref (connection);
+        g_string_free (query, TRUE);
+}
+
+GList*
+batch_rename_files_get_distinct_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..e343b25
--- /dev/null
+++ b/src/nautilus-batch-rename-utilities.h
@@ -0,0 +1,63 @@
+/* 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/>.
+ */
+
+#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);
+
+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* batch_rename_files_get_distinct_parents  (GList *selection);
+
+gboolean file_name_conflicts_with_results       (GList        *selection,
+                                                 GList        *new_names,
+                                                 GString      *old_name,
+                                                 gchar        *parent_uri);
+
+GString* batch_rename_replace_label_text        (gchar             *label,
+                                                 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..3ec2832 100644
--- a/src/nautilus-file-undo-operations.c
+++ b/src/nautilus-file-undo-operations.c
@@ -994,6 +994,292 @@ 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;
+        GFile *new_file;
+        GList *l1;
+        GList *l2;
+        GList *l3;
+        GList *l4;
+        GList *l5;
+        GList *l6;
+        GList *l7;
+        gchar *file_name;
+        gchar *old_file_name;
+        GString *new_file_name;
+        GString *new_name;
+        GString *old_name;
+
+        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);
+        }
+
+        for (l1 = self->priv->new_display_names, l2 = files; 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 = files, l4 = self->priv->new_display_names, l5 = self->priv->old_display_names, l6 
= self->priv->old_files, l7 = self->priv->new_files;
+                     l3 != NULL && l4 != NULL && l5 != NULL && l6 != NULL && l7 != NULL;
+                     l3 = l3->next, l4 = l4->next, l5 = l5->next, l6 = l6->next, l7 = l7->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 = l4->data;
+                                old_name = l5->data;
+                                old_file = l6->data;
+                                new_file = l7->data;
+
+                                files = g_list_remove_link (files, l3);
+                                self->priv->new_display_names = g_list_remove_link 
(self->priv->new_display_names, l4);
+                                self->priv->old_display_names = g_list_remove_link 
(self->priv->old_display_names, l5);
+                                self->priv->old_files = g_list_remove_link (self->priv->old_files, l6);
+                                self->priv->new_files = g_list_remove_link (self->priv->new_files, l7);
+
+                                files = g_list_prepend (files, file);
+                                self->priv->new_display_names = g_list_prepend 
(self->priv->new_display_names, new_name);
+                                self->priv->old_display_names = g_list_prepend 
(self->priv->old_display_names, old_name);
+                                self->priv->old_files = g_list_prepend (self->priv->old_files, old_file);
+                                self->priv->new_files = g_list_prepend (self->priv->new_files, new_file);
+
+                                g_free (file_name);
+
+                                break;
+                        }
+
+                        g_free (file_name);
+                }
+
+                g_free (old_file_name);
+        }
+
+        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;
+        GFile *old_file;
+        GList *l1;
+        GList *l2;
+        GList *l3;
+        GList *l4;
+        GList *l5;
+        GList *l6;
+        GList *l7;
+        gchar *file_name;
+        gchar *old_file_name;
+        GString *new_file_name;
+        GString *new_name;
+        GString *old_name;
+
+        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);
+        }
+
+        for (l1 = self->priv->old_display_names, l2 = files; 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 = files, l4 = self->priv->old_display_names, l5 = self->priv->new_display_names, l6 
= self->priv->old_files, l7 = self->priv->new_files;
+                     l3 != NULL && l4 != NULL && l5 != NULL && l6 != NULL && l7 != NULL;
+                     l3 = l3->next, l4 = l4->next, l5 = l5->next, l6 = l6->next, l7 = l7->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 = l4->data;
+                                old_name = l5->data;
+                                old_file = l6->data;
+                                new_file = l7->data;
+
+                                files = g_list_remove_link (files, l3);
+                                self->priv->old_display_names = g_list_remove_link 
(self->priv->old_display_names, l4);
+                                self->priv->new_display_names = g_list_remove_link 
(self->priv->new_display_names, l5);
+                                self->priv->old_files = g_list_remove_link (self->priv->old_files, l6);
+                                self->priv->new_files = g_list_remove_link (self->priv->new_files, l7);
+
+                                files = g_list_prepend (files, file);
+                                self->priv->old_display_names = g_list_prepend 
(self->priv->old_display_names, new_name);
+                                self->priv->new_display_names = g_list_prepend 
(self->priv->new_display_names, old_name);
+                                self->priv->old_files = g_list_prepend (self->priv->old_files, old_file);
+                                self->priv->new_files = g_list_prepend (self->priv->new_files, new_file);
+
+                                g_free (file_name);
+
+                                break;
+                        }
+
+                        g_free (file_name);
+                }
+
+                g_free (old_file_name);
+        }
+
+        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..c68753e 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,10 +1701,12 @@ nautilus_file_operation_complete (NautilusFileOperation *op,
         * as "changing back".
         */
        nautilus_file_operation_remove (op);
-       nautilus_file_changed (op->file);
-       if (op->callback) {
+
+        if (op->files == NULL)
+                nautilus_file_changed (op->file);
+
+       if (op->callback)
                (* op->callback) (op->file, result_file, error, op->callback_data);
-       }
 
        if (error != NULL) {
                g_clear_object (&op->undo_info);
@@ -1759,6 +1782,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 +1882,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 +1914,217 @@ 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_warning ("Batch rename for file \"%s\" failed", nautilus_file_get_name (file));
+                        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..48beeee 100644
--- a/src/resources/css/Adwaita.css
+++ b/src/resources/css/Adwaita.css
@@ -169,3 +169,19 @@
  * 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;
+    color: black;
+}
+
+.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..6628abf
--- /dev/null
+++ b/src/resources/ui/nautilus-batch-rename-dialog.ui
@@ -0,0 +1,502 @@
+<?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="height-request">563</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">
+        <property name="border-width">0</property>
+        <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">Rename _using a template</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">True</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>
+      <item>
+        <attribute name="label" translatable="yes">Album Name</attribute>
+        <attribute name="action">dialog.add-album-name-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
diff --git a/src/resources/ui/nautilus-files-view-context-menus.ui 
b/src/resources/ui/nautilus-files-view-context-menus.ui
index 31f26fb..f69e673 100644
--- a/src/resources/ui/nautilus-files-view-context-menus.ui
+++ b/src/resources/ui/nautilus-files-view-context-menus.ui
@@ -215,7 +215,7 @@
     </section>
     <section>
       <item>
-        <attribute name="label" translatable="yes">Rena_me</attribute>
+        <attribute name="label" translatable="yes">Rena_me…</attribute>
         <attribute name="action">view.rename</attribute>
       </item>
     </section>



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