1
0
mirror of https://gitlab.gnome.org/GNOME/nautilus synced 2024-06-30 23:46:35 +00:00

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
This commit is contained in:
Alexandru Pandelea 2016-08-27 19:34:08 +03:00
parent d296183010
commit be12a75100
17 changed files with 5465 additions and 101 deletions

View File

@ -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 \

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;

View File

@ -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)

View File

@ -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))

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);
new_file_name = nautilus_file_can_rename_file (file,
new_name,
callback,
callback_data);
if (new_file_name == NULL)
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;
}
/* 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);
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);

View File

@ -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);

View File

@ -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 &&

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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>