nautilus-batch-rename-dialog.c 75.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/* 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>

31
#define ROW_MARGIN_START 6
32
#define ROW_MARGIN_TOP_BOTTOM 4
33

34 35
struct _NautilusBatchRenameDialog
{
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    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;
59
    GtkWidget *numbering_revealer;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    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;

    /* the index of the currently selected conflict */
    gint selected_conflict;
    /* total conflicts number */
    gint conflicts_number;

    GList *duplicates;
    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;
105 106

    GCancellable *metadata_cancellable;
107 108 109 110
};

typedef struct
{
111 112 113 114 115
    gboolean available;
    gboolean set;
    gint position;
    /* if the tag was just added, then we shouldn't update it's position */
    gboolean just_added;
116
    TagConstants tag_constants;
117 118
} TagData;

119

120
static void     update_display_text (NautilusBatchRenameDialog *dialog);
121 122 123 124

G_DEFINE_TYPE (NautilusBatchRenameDialog, nautilus_batch_rename_dialog, GTK_TYPE_DIALOG);

static void
125 126 127
change_numbering_order (GSimpleAction *action,
                        GVariant      *value,
                        gpointer       user_data)
128
{
129 130
    NautilusBatchRenameDialog *dialog;
    const gchar *target_name;
131
    guint i;
132 133 134 135 136

    dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);

    target_name = g_variant_get_string (value, NULL);

137
    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
138
    {
139 140 141 142 143 144
        if (g_strcmp0 (sorts_constants[i].action_target_name, target_name) == 0)
        {
            gtk_label_set_label (GTK_LABEL (dialog->numbering_order_label),
                                 gettext (sorts_constants[i].label));
            dialog->selection = nautilus_batch_rename_dialog_sort (dialog->selection,
                                                                   sorts_constants[i].sort_mode,
145
                                                                   dialog->create_date);
146
            break;
147
        }
148 149 150 151 152 153 154 155
    }

    g_simple_action_set_state (action, value);

    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->numbering_order_button), FALSE);

    update_display_text (dialog);
}
156

157
static void
158 159
enable_action (NautilusBatchRenameDialog *self,
               const gchar               *action_name)
160
{
161
    GAction *action;
162

163 164 165
    action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group),
                                         action_name);
    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
166
}
167

168
static void
169 170
disable_action (NautilusBatchRenameDialog *self,
                const gchar               *action_name)
171 172
{
    GAction *action;
173

174
    action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group),
175 176
                                         action_name);
    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
177 178 179
}

static void
180 181
add_tag (NautilusBatchRenameDialog *self,
         TagConstants               tag_constants)
182
{
183
    g_autofree gchar *tag_text_representation = NULL;
184 185
    gint cursor_position;
    TagData *tag_data;
186

187
    g_object_get (self->name_entry, "cursor-position", &cursor_position, NULL);
188

189 190 191 192 193 194
    tag_text_representation = batch_rename_get_tag_text_representation (tag_constants);
    tag_data = g_hash_table_lookup (self->tag_info_table, tag_text_representation);
    tag_data->available = TRUE;
    tag_data->set = TRUE;
    tag_data->just_added = TRUE;
    tag_data->position = cursor_position;
195

196 197 198 199
    /* FIXME: We can add a tag when the cursor is inside a tag, which breaks this.
     * We need to check the cursor movement and update the actions acordingly or
     * even better add the tag at the end of the previous tag if this happens.
     */
200 201
    gtk_editable_insert_text (GTK_EDITABLE (self->name_entry),
                              tag_text_representation,
202
                              strlen (tag_text_representation),
203
                              &cursor_position);
204
    tag_data->just_added = FALSE;
205
    gtk_editable_set_position (GTK_EDITABLE (self->name_entry), cursor_position);
206

207 208
    gtk_entry_grab_focus_without_selecting (GTK_ENTRY (self->name_entry));
}
209

210 211 212 213 214 215 216 217
static void
add_metadata_tag (GSimpleAction *action,
                  GVariant      *value,
                  gpointer       user_data)
{
    NautilusBatchRenameDialog *self;
    const gchar *action_name;
    guint i;
218

219 220
    self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
    action_name = g_action_get_name (G_ACTION (action));
221

222
    for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
223
    {
224 225 226 227
        if (g_strcmp0 (metadata_tags_constants[i].action_name, action_name) == 0)
        {
            add_tag (self, metadata_tags_constants[i]);
            disable_action (self, metadata_tags_constants[i].action_name);
228

229 230
            break;
        }
231
    }
232 233 234
}

static void
235 236 237
add_numbering_tag (GSimpleAction *action,
                   GVariant      *value,
                   gpointer       user_data)
238
{
239
    NautilusBatchRenameDialog *self;
240
    const gchar *action_name;
241
    guint i;
242

243
    self = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
244 245
    action_name = g_action_get_name (G_ACTION (action));

246
    for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
247
    {
248 249 250 251 252 253 254
        if (g_strcmp0 (numbering_tags_constants[i].action_name, action_name) == 0)
        {
            add_tag (self, numbering_tags_constants[i]);
        }
        /* We want to allow only one tag of numbering type, so we disable all
         * of them */
        disable_action (self, numbering_tags_constants[i].action_name);
255 256
    }
}
257

258 259
const GActionEntry dialog_entries[] =
{
260 261 262 263 264
    { "numbering-order-changed", NULL, "s", "'name-ascending'", change_numbering_order },
    { "add-numbering-no-zero-pad-tag", add_numbering_tag },
    { "add-numbering-one-zero-pad-tag", add_numbering_tag },
    { "add-numbering-two-zero-pad-tag", add_numbering_tag },
    { "add-original-file-name-tag", add_metadata_tag },
265 266
    { "add-creation-date-tag", add_metadata_tag },
    { "add-equipment-tag", add_metadata_tag },
267 268
    { "add-season-number-tag", add_metadata_tag },
    { "add-episode-number-tag", add_metadata_tag },
269 270 271 272 273
    { "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 },
274 275 276 277 278 279 280
};

static void
row_selected (GtkListBox    *box,
              GtkListBoxRow *listbox_row,
              gpointer       user_data)
{
281 282 283
    NautilusBatchRenameDialog *dialog;
    GtkListBoxRow *row;
    gint index;
284

285 286 287 288
    if (!GTK_IS_LIST_BOX_ROW (listbox_row))
    {
        return;
    }
289

290 291
    dialog = NAUTILUS_BATCH_RENAME_DIALOG (user_data);
    index = gtk_list_box_row_get_index (listbox_row);
292

293 294 295 296 297 298 299 300 301
    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);
    }
302

303 304 305 306 307 308 309 310 311
    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);
    }
312

313 314 315 316 317 318 319 320 321
    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);
    }
322 323 324
}

static gint
325 326
compare_int (gconstpointer a,
             gconstpointer b)
327
{
328 329
    int *number1 = (int *) a;
    int *number2 = (int *) b;
330

331
    return *number1 - *number2;
332 333 334 335 336
}

/* 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]" */
337
static GList *
338
split_entry_text (NautilusBatchRenameDialog *self,
339 340
                  gchar                     *entry_text)
{
341 342 343
    GString *normal_text;
    GString *tag;
    GArray *tag_positions;
344 345
    g_autoptr (GList) tag_info_keys = NULL;
    GList *l;
346 347 348 349 350 351 352 353 354 355 356
    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));

357
    tag_info_keys = g_hash_table_get_keys (self->tag_info_table);
358

359
    for (l = tag_info_keys; l != NULL; l = l->next)
360
    {
361 362 363 364 365 366
        tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
        if (tag_data->set)
        {
            g_array_append_val (tag_positions, tag_data->position);
            tags++;
        }
367 368
    }

369
    g_array_sort (tag_positions, compare_int);
370 371 372 373 374 375 376 377 378 379 380 381 382 383

    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);
        }
384 385 386 387
        else
        {
            g_string_free (normal_text, TRUE);
        }
388

389
        for (l = tag_info_keys; l != NULL; l = l->next)
390
        {
391
            g_autofree gchar *tag_text_representation = NULL;
392

393 394 395 396 397 398 399
            tag_data = g_hash_table_lookup (self->tag_info_table, l->data);
            if (tag_data->set && g_array_index (tag_positions, gint, i) == tag_data->position)
            {
                tag_text_representation = batch_rename_get_tag_text_representation (tag_data->tag_constants);
                tag_end_position = g_array_index (tag_positions, gint, i) +
                                   g_utf8_strlen (tag_text_representation, -1);
                tag = g_string_append (tag, tag_text_representation);
400

401 402
                break;
            }
403 404
        }

405 406
        result = g_list_prepend (result, tag);
    }
407

408
    normal_text = g_string_new (g_utf8_offset_to_pointer (entry_text, tag_end_position));
409

410 411 412 413
    if (g_strcmp0 (normal_text->str, "") != 0)
    {
        result = g_list_prepend (result, normal_text);
    }
414 415 416 417
    else
    {
        g_string_free (normal_text, TRUE);
    }
418

419
    result = g_list_reverse (result);
420

421 422
    g_array_free (tag_positions, TRUE);
    return result;
423 424
}

425
static GList *
426 427
batch_rename_dialog_get_new_names (NautilusBatchRenameDialog *dialog)
{
428 429 430
    GList *result = NULL;
    GList *selection;
    GList *text_chunks;
431 432
    g_autofree gchar *entry_text = NULL;
    g_autofree gchar *replace_text = NULL;
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

    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;
473 474 475 476 477 478
}

static void
begin_batch_rename (NautilusBatchRenameDialog *dialog,
                    GList                     *new_names)
{
479
    batch_rename_sort_lists_for_rename (&dialog->selection, &new_names, NULL, NULL, NULL, FALSE);
480 481 482

    /* do the actual rename here */
    nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL);
483

484
    gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog->window)), NULL);
485 486 487
}

static void
488 489 490
listbox_header_func (GtkListBoxRow             *row,
                     GtkListBoxRow             *before,
                     NautilusBatchRenameDialog *dialog)
491
{
492
    gboolean show_separator;
493

494 495
    show_separator = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row),
                                                         "show-separator"));
496

497 498 499
    if (show_separator)
    {
        GtkWidget *separator;
500

501 502
        separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
        gtk_widget_show (separator);
503

504 505
        gtk_list_box_row_set_header (row, separator);
    }
506 507 508 509 510 511 512
}

/* This is manually done instead of using GtkSizeGroup because of the computational
 * complexity of the later.*/
static void
update_rows_height (NautilusBatchRenameDialog *dialog)
{
513 514 515
    GList *l;
    gint current_row_natural_height;
    gint maximum_height;
516

517
    maximum_height = -1;
518

519 520 521 522 523 524
    /* 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);
525

526 527 528
        if (current_row_natural_height > maximum_height)
        {
            maximum_height = current_row_natural_height;
529
        }
530
    }
531

532 533 534 535 536
    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);
537

538 539 540
        if (current_row_natural_height > maximum_height)
        {
            maximum_height = current_row_natural_height;
541
        }
542
    }
543

544 545 546 547 548
    for (l = dialog->listbox_icons; l != NULL; l = l->next)
    {
        gtk_widget_get_preferred_height (GTK_WIDGET (l->data),
                                         NULL,
                                         &current_row_natural_height);
549

550 551 552
        if (current_row_natural_height > maximum_height)
        {
            maximum_height = current_row_natural_height;
553
        }
554
    }
555

556 557
    if (maximum_height != dialog->row_height)
    {
558
        dialog->row_height = maximum_height + ROW_MARGIN_TOP_BOTTOM * 2;
559

560 561 562 563
        for (l = dialog->listbox_icons; l != NULL; l = l->next)
        {
            g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
        }
564

565 566 567 568
        for (l = dialog->listbox_labels_new; l != NULL; l = l->next)
        {
            g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
        }
569

570 571 572
        for (l = dialog->listbox_labels_old; l != NULL; l = l->next)
        {
            g_object_set (G_OBJECT (l->data), "height-request", dialog->row_height, NULL);
573
        }
574
    }
575 576
}

577
static GtkWidget *
578 579 580 581
create_original_name_row_for_label (NautilusBatchRenameDialog *dialog,
                                    const gchar               *old_text,
                                    gboolean                   show_separator)
{
582 583
    GtkWidget *row;
    GtkWidget *label_old;
584

585
    row = gtk_list_box_row_new ();
586

587
    g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
588

589 590 591
    label_old = gtk_label_new (old_text);
    gtk_label_set_xalign (GTK_LABEL (label_old), 0.0);
    gtk_widget_set_hexpand (label_old, TRUE);
592
    gtk_widget_set_margin_start (label_old, ROW_MARGIN_START);
593
    gtk_label_set_ellipsize (GTK_LABEL (label_old), PANGO_ELLIPSIZE_END);
594

595
    dialog->listbox_labels_old = g_list_prepend (dialog->listbox_labels_old, label_old);
596

597 598
    gtk_container_add (GTK_CONTAINER (row), label_old);
    gtk_widget_show_all (row);
599

600
    return row;
601 602
}

603
static GtkWidget *
604 605 606 607
create_result_row_for_label (NautilusBatchRenameDialog *dialog,
                             const gchar               *new_text,
                             gboolean                   show_separator)
{
608 609
    GtkWidget *row;
    GtkWidget *label_new;
610

611
    row = gtk_list_box_row_new ();
612

613
    g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
614

615 616 617
    label_new = gtk_label_new (new_text);
    gtk_label_set_xalign (GTK_LABEL (label_new), 0.0);
    gtk_widget_set_hexpand (label_new, TRUE);
618
    gtk_widget_set_margin_start (label_new, ROW_MARGIN_START);
619
    gtk_label_set_ellipsize (GTK_LABEL (label_new), PANGO_ELLIPSIZE_END);
620

621
    dialog->listbox_labels_new = g_list_prepend (dialog->listbox_labels_new, label_new);
622

623 624
    gtk_container_add (GTK_CONTAINER (row), label_new);
    gtk_widget_show_all (row);
625

626
    return row;
627 628
}

629
static GtkWidget *
630 631 632
create_arrow_row_for_label (NautilusBatchRenameDialog *dialog,
                            gboolean                   show_separator)
{
633 634
    GtkWidget *row;
    GtkWidget *icon;
635

636
    row = gtk_list_box_row_new ();
637

638
    g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
639

640
    if (gtk_widget_get_direction (row) == GTK_TEXT_DIR_RTL)
641
    {
642
        icon = gtk_label_new ("←");
643
    }
644
    else
645
    {
646
        icon = gtk_label_new ("→");
647
    }
648

649 650
    gtk_label_set_xalign (GTK_LABEL (icon), 1.0);
    gtk_widget_set_hexpand (icon, FALSE);
651
    gtk_widget_set_margin_start (icon, ROW_MARGIN_START);
652

653
    dialog->listbox_icons = g_list_prepend (dialog->listbox_icons, icon);
654

655 656
    gtk_container_add (GTK_CONTAINER (row), icon);
    gtk_widget_show_all (row);
657

658
    return row;
659 660 661 662 663
}

static void
prepare_batch_rename (NautilusBatchRenameDialog *dialog)
{
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
    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);
698
        g_clear_object (&dialog->conflict_cancellable);
699 700 701
    }

    gtk_widget_destroy (GTK_WIDGET (dialog));
702 703 704 705 706 707 708
}

static void
batch_rename_dialog_on_response (NautilusBatchRenameDialog *dialog,
                                 gint                       response_id,
                                 gpointer                   user_data)
{
709 710 711 712 713 714 715 716 717
    if (response_id == GTK_RESPONSE_OK)
    {
        prepare_batch_rename (dialog);
    }
    else
    {
        if (dialog->conflict_cancellable)
        {
            g_cancellable_cancel (dialog->conflict_cancellable);
718
            g_clear_object (&dialog->conflict_cancellable);
719
        }
720 721 722

        gtk_widget_destroy (GTK_WIDGET (dialog));
    }
723 724 725 726 727
}

static void
fill_display_listbox (NautilusBatchRenameDialog *dialog)
{
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    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,
751 752
                                                             row);

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
        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);
772 773 774 775 776
}

static void
select_nth_conflict (NautilusBatchRenameDialog *dialog)
{
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
    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,
827
                                _("“%s” would not be a unique new name."),
828 829 830 831 832
                                conflict_file_name->str);
    }
    else
    {
        g_string_append_printf (display_text,
833
                                _("“%s” would conflict with an existing file."),
834 835 836 837 838 839 840
                                conflict_file_name->str);
    }

    gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
                         display_text->str);

    g_string_free (conflict_file_name, TRUE);
841
    g_string_free (display_text, TRUE);
842 843 844 845 846
}

static void
select_next_conflict_down (NautilusBatchRenameDialog *dialog)
{
847
    dialog->selected_conflict++;
848

849 850 851 852
    if (dialog->selected_conflict == 1)
    {
        gtk_widget_set_sensitive (dialog->conflict_up, TRUE);
    }
853

854 855 856 857
    if (dialog->selected_conflict == dialog->conflicts_number - 1)
    {
        gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
    }
858

859
    select_nth_conflict (dialog);
860 861 862 863 864
}

static void
select_next_conflict_up (NautilusBatchRenameDialog *dialog)
{
865
    dialog->selected_conflict--;
866

867 868 869 870
    if (dialog->selected_conflict == 0)
    {
        gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
    }
871

872 873 874 875
    if (dialog->selected_conflict == dialog->conflicts_number - 2)
    {
        gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
    }
876

877
    select_nth_conflict (dialog);
878 879 880 881 882
}

static void
update_conflict_row_background (NautilusBatchRenameDialog *dialog)
{
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
    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");
906

907 908
            context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
            gtk_style_context_remove_class (context, "conflict-row");
909

910 911 912
            context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
            gtk_style_context_remove_class (context, "conflict-row");
        }
913

914 915 916 917 918 919 920
        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");
921

922 923
                context = gtk_widget_get_style_context (GTK_WIDGET (l2->data));
                gtk_style_context_add_class (context, "conflict-row");
924

925 926
                context = gtk_widget_get_style_context (GTK_WIDGET (l3->data));
                gtk_style_context_add_class (context, "conflict-row");
927

928 929
                duplicates = duplicates->next;
            }
930
        }
931 932
        index++;
    }
933 934 935 936 937
}

static void
update_listbox (NautilusBatchRenameDialog *dialog)
{
938 939 940 941 942 943
    GList *l1;
    GList *l2;
    NautilusFile *file;
    gchar *old_name;
    GtkLabel *label;
    GString *new_name;
944
    gboolean empty_name = FALSE;
945

946 947 948 949
    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;
950

951
        gtk_label_set_label (label, new_name->str);
952
        gtk_widget_set_tooltip_text (GTK_WIDGET (label), new_name->str);
953 954 955 956 957

        if (g_strcmp0 (new_name->str, "") == 0)
        {
            empty_name = TRUE;
        }
958
    }
959

960 961 962 963
    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);
964

965
        old_name = nautilus_file_get_name (file);
966
        gtk_widget_set_tooltip_text (GTK_WIDGET (label), old_name);
967

968 969 970 971 972 973 974 975 976
        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);
977

978
            g_string_free (new_name, TRUE);
979 980
        }

981 982
        g_free (old_name);
    }
983

984
    update_rows_height (dialog);
985

986 987 988 989 990 991 992
    if (empty_name)
    {
        gtk_widget_set_sensitive (dialog->rename_button, FALSE);

        return;
    }

993 994 995 996
    /* check if there are name conflicts and display them if they exist */
    if (dialog->duplicates != NULL)
    {
        update_conflict_row_background (dialog);
997

998
        gtk_widget_set_sensitive (dialog->rename_button, FALSE);
999

1000
        gtk_widget_show (dialog->conflict_box);
1001

1002 1003
        dialog->selected_conflict = 0;
        dialog->conflicts_number = g_list_length (dialog->duplicates);
1004

1005
        select_nth_conflict (dialog);
1006

1007
        gtk_widget_set_sensitive (dialog->conflict_up, FALSE);
1008

1009 1010 1011
        if (g_list_length (dialog->duplicates) == 1)
        {
            gtk_widget_set_sensitive (dialog->conflict_down, FALSE);
1012
        }
1013 1014 1015
        else
        {
            gtk_widget_set_sensitive (dialog->conflict_down, TRUE);
1016
        }
1017 1018 1019 1020
    }
    else
    {
        gtk_widget_hide (dialog->conflict_box);
1021

1022 1023 1024 1025 1026
        /* 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);
1027
        }
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
    }

    /* 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;
    }
1040 1041
}

1042
static void
1043 1044 1045 1046
check_conflict_for_files (NautilusBatchRenameDialog *dialog,
                          NautilusDirectory         *directory,
                          GList                     *files)
{
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
    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));
1138

1139 1140 1141 1142 1143 1144 1145 1146
            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);
1147

1148 1149
                have_conflict = TRUE;
            }
1150
        }
1151

1152 1153 1154
        if (!have_conflict)
        {
            tag_present = g_hash_table_lookup (names_conflicts_table, new_name->str) != NULL;
1155
            same_parent_directory = g_strcmp0 (parent_uri, current_directory) == 0;
1156

1157 1158 1159 1160 1161 1162 1163
            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);
1164

1165 1166
                have_conflict = TRUE;
            }
1167 1168
        }

1169 1170 1171
        g_string_free (file_name, TRUE);
        g_free (parent_uri);
    }
1172

1173 1174 1175 1176
    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);
1177 1178
}

1179 1180 1181 1182 1183
static gboolean
file_names_list_has_duplicates_finish (NautilusBatchRenameDialog  *self,
                                       GAsyncResult               *res,
                                       GError                    **error)
{
1184
    g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
1185

1186
    return g_task_propagate_boolean (G_TASK (res), error);
1187 1188
}

1189
static void
1190 1191 1192
on_file_names_list_has_duplicates (GObject      *object,
                                   GAsyncResult *res,
                                   gpointer      user_data)
1193
{
1194 1195 1196
    NautilusBatchRenameDialog *self;
    GError *error = NULL;
    gboolean success;
1197

1198 1199
    self = NAUTILUS_BATCH_RENAME_DIALOG (object);
    success = file_names_list_has_duplicates_finish (self, res, &error);
1200

1201
    if (!success)
1202
    {
1203
        g_clear_error (&error);
1204 1205
        return;
    }
1206

1207 1208 1209
    self->duplicates = g_list_reverse (self->duplicates);
    self->checking_conflicts = FALSE;
    update_listbox (self);
1210 1211
}

1212 1213 1214
typedef struct
{
    GList *directories;
1215
    NautilusDirectory *current_directory;
1216 1217 1218 1219 1220
    GMutex wait_ready_mutex;
    GCond wait_ready_condition;
    gboolean directory_conflicts_ready;
} CheckConflictsData;

1221
static void
1222 1223 1224
on_directory_conflicts_ready (NautilusDirectory *conflict_directory,
                              GList             *files,
                              gpointer           callback_data)
1225
{
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
    NautilusBatchRenameDialog *self;
    GTask *task;
    CheckConflictsData *task_data;
    GCancellable *cancellable;

    task = G_TASK (callback_data);
    task_data = g_task_get_task_data (task);
    cancellable = g_task_get_cancellable (task);
    self = NAUTILUS_BATCH_RENAME_DIALOG (g_task_get_source_object (task));
    if (!g_cancellable_is_cancelled (cancellable))
    {
        check_conflict_for_files (self, conflict_directory, files);
    }

    g_mutex_lock (&task_data->wait_ready_mutex);
    task_data->directory_conflicts_ready = TRUE;
    g_cond_signal (&task_data->wait_ready_condition);
    g_mutex_unlock (&task_data->wait_ready_mutex);
}

1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
static gboolean
check_conflicts_on_main_thread (gpointer user_data)
{
    GTask *task = (GTask *) user_data;
    CheckConflictsData *task_data = g_task_get_task_data (task);

    nautilus_directory_call_when_ready (task_data->current_directory,
                                        NAUTILUS_FILE_ATTRIBUTE_INFO,
                                        TRUE,
                                        on_directory_conflicts_ready,
                                        task);
    return FALSE;
}

1260
static void
1261 1262 1263 1264
file_names_list_has_duplicates_async_thread (GTask        *task,
                                             gpointer      object,
                                             gpointer      data,
                                             GCancellable *cancellable)
1265 1266 1267 1268 1269 1270 1271 1272
{
    NautilusBatchRenameDialog *self;
    CheckConflictsData *task_data;
    GList *directories;
    GList *l;

    self = g_task_get_source_object (task);
    task_data = g_task_get_task_data (task);
1273
    self->duplicates = NULL;
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283

    g_mutex_init (&task_data->wait_ready_mutex);
    g_cond_init (&task_data->wait_ready_condition);

    directories = batch_rename_files_get_distinct_parents (self->selection);
    /* check if this is the last call of the callback */
    for (l = directories; l != NULL; l = l->next)
    {
        if (g_task_return_error_if_cancelled (task))
        {
1284
            nautilus_directory_list_free (directories);
1285 1286 1287 1288 1289 1290 1291
            return;
        }

        g_mutex_lock (&task_data->wait_ready_mutex);
        task_data->directory_conflicts_ready = FALSE;


1292 1293 1294 1295 1296
        task_data->current_directory = l->data;
        /* NautilusDirectory and NautilusFile are not thread safe, we need to call
         * them on the main thread.
         */
        g_main_context_invoke (NULL, check_conflicts_on_main_thread, task);
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308

        /* We need to block this thread until the call_when_ready call is done,
         * if not the GTask would finalize. */
        while (!task_data->directory_conflicts_ready)
        {
            g_cond_wait (&task_data->wait_ready_condition,
                         &task_data->wait_ready_mutex);
        }

        g_mutex_unlock (&task_data->wait_ready_mutex);
    }

1309 1310
    g_task_return_boolean (task, TRUE);
    nautilus_directory_list_free (directories);
1311 1312
}

1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
static void
destroy_conflicts_task_data (gpointer data)
{
    CheckConflictsData *task_data = data;

    if (task_data->directories)
    {
        g_list_free (task_data->directories);
    }

1323 1324
    g_mutex_clear (&task_data->wait_ready_mutex);
    g_cond_clear (&task_data->wait_ready_condition);
1325 1326

    g_free (task_data);
1327 1328 1329 1330 1331 1332 1333
}

static void
file_names_list_has_duplicates_async (NautilusBatchRenameDialog *dialog,
                                      GAsyncReadyCallback        callback,
                                      gpointer                   user_data)
{
1334 1335 1336
    g_autoptr (GTask) task = NULL;
    CheckConflictsData *task_data;

1337 1338 1339
    if (dialog->checking_conflicts == TRUE)
    {
        g_cancellable_cancel (dialog->conflict_cancellable);
1340
        g_clear_object (&dialog->conflict_cancellable);
1341
    }
1342

1343
    dialog->conflict_cancellable = g_cancellable_new ();
1344

1345
    dialog->checking_conflicts = TRUE;
1346

1347
    task = g_task_new (dialog, dialog->conflict_cancellable, callback, user_data);
1348
    task_data = g_new0 (CheckConflictsData, 1);
1349 1350
    g_task_set_task_data (task, task_data, destroy_conflicts_task_data);
    g_task_run_in_thread (task, file_names_list_has_duplicates_async_thread);
1351 1352
}

1353 1354 1355 1356 1357 1358
static gboolean
have_unallowed_character (NautilusBatchRenameDialog *dialog)
{
    GList *names;
    GString *new_name;
    const gchar *entry_text;
1359
    gboolean have_empty_name;
1360 1361 1362 1363
    gboolean have_unallowed_character_slash;
    gboolean have_unallowed_character_dot;
    gboolean have_unallowed_character_dotdot;

1364
    have_empty_name = FALSE;
1365 1366 1367
    have_unallowed_character_slash = FALSE;
    have_unallowed_character_dot = FALSE;
    have_unallowed_character_dotdot = FALSE;
1368

1369
    if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT)
1370
    {
1371
        entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
1372 1373 1374
    }
    else
    {
1375
        entry_text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
1376 1377
    }

1378
    if (strstr (entry_text, "/") != NULL)
1379
    {
1380
        have_unallowed_character_slash = TRUE;
1381 1382
    }

1383
    if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT && g_strcmp0 (entry_text, ".") == 0)
1384
    {
1385
        have_unallowed_character_dot = TRUE;
1386
    }
1387
    else if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
1388
    {
1389
        for (names = dialog->new_names; names != NULL; names = names->next)
1390
        {
1391 1392 1393 1394 1395 1396 1397
            new_name = names->data;

            if (g_strcmp0 (new_name->str, ".") == 0)
            {
                have_unallowed_character_dot = TRUE;
                break;
            }
1398 1399 1400
        }
    }

1401
    if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT && g_strcmp0 (entry_text, "..") == 0)
1402
    {
1403
        have_unallowed_character_dotdot = TRUE;
1404
    }
1405
    else if (dialog->mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
1406
    {
1407
        for (names = dialog->new_names; names != NULL; names = names->next)
1408
        {
1409 1410
            new_name = names->data;

1411 1412 1413 1414 1415 1416
            if (g_strcmp0 (new_name->str, "") == 0)
            {
                have_empty_name = TRUE;
                break;
            }

1417 1418 1419 1420 1421
            if (g_strcmp0 (new_name->str, "..") == 0)
            {
                have_unallowed_character_dotdot = TRUE;
                break;
            }
1422 1423 1424
        }
    }

1425 1426 1427
    if (have_empty_name)
    {
        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1428
                             _("Name cannot be empty."));
1429 1430
    }

1431
    if (have_unallowed_character_slash)
1432
    {
1433
        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1434
                             _("Name cannot contain “/”."));
1435
    }
1436 1437

    if (have_unallowed_character_dot)
1438
    {
1439
        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1440
                             _("“.” is not a valid name."));
1441 1442
    }

1443
    if (have_unallowed_character_dotdot)
1444 1445
    {
        gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
1446
                             _("“..” is not a valid name."));
1447
    }
1448