[nautilus/wip/csoriano/file-operations-rename: 2/2] file-operations: implement files rename



commit 75f61633c72c7190256f24a2e8e902f3c64a2e83
Author: Carlos Soriano <csoriano gnome org>
Date:   Thu Aug 25 15:49:05 2016 +0200

    file-operations: implement files rename

 src/nautilus-file-operations.c |  407 +++++++++++++++++++++++++++++++++++++---
 src/nautilus-file-operations.h |    3 +
 2 files changed, 381 insertions(+), 29 deletions(-)
---
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index 24124da..0fdcc00 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -107,6 +107,14 @@ typedef struct {
 
 typedef struct {
        CommonJob common;
+       GList *files;
+       GList *new_names;
+       NautilusRenameCallback done_callback;
+       gpointer done_callback_data;
+} RenameJob;
+
+typedef struct {
+       CommonJob common;
        GFile *dest_dir;
        char *filename;
        gboolean make_dir;
@@ -120,7 +128,6 @@ typedef struct {
        gpointer done_callback_data;
 } CreateJob;
 
-
 typedef struct {
        CommonJob common;
        GList *trash_dirs;
@@ -153,7 +160,8 @@ typedef enum {
        OP_KIND_MOVE,
        OP_KIND_DELETE,
        OP_KIND_TRASH,
-        OP_KIND_COMPRESS
+        OP_KIND_COMPRESS,
+       OP_KIND_RENAME
 } OpKind;
 
 typedef struct {
@@ -243,7 +251,8 @@ is_all_button_text (const char *button_text)
 static void scan_sources (GList *files,
                          SourceInfo *source_info,
                          CommonJob *job,
-                         OpKind kind);
+                         OpKind kind,
+                          gboolean recursive);
 
 
 static void empty_trash_thread_func (GTask *task,
@@ -1793,7 +1802,7 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
        SourceInfo source_info;
        TransferInfo transfer_info;
         DeleteData data;
-       
+
        if (job_aborted (job)) {
                return;
        }
@@ -1801,7 +1810,8 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
        scan_sources (files,
                      &source_info,
                      job,
-                     OP_KIND_DELETE);
+                     OP_KIND_DELETE,
+                      TRUE);
        if (job_aborted (job)) {
                return;
        }
@@ -2088,7 +2098,8 @@ trash_files (CommonJob *job,
        scan_sources (files,
                      &source_info,
                      job,
-                     OP_KIND_TRASH);
+                     OP_KIND_TRASH,
+                      TRUE);
        if (job_aborted (job)) {
                return;
        }
@@ -2281,6 +2292,334 @@ nautilus_file_operations_delete (GList                  *files,
                                  done_callback,  done_callback_data);
 }
 
+static gchar*
+can_rename_file (GFile      *file,
+                 const char *new_name)
+{
+        GError *error;
+        gboolean is_renameable_desktop_file;
+        gboolean success;
+        gboolean name_changed;
+        gchar *new_file_name;
+        gchar *uri;
+        gchar *old_name;
+        g_autoptr (GFileInfo) info;
+        g_autofree gchar *content_type;
+
+
+        info = g_file_query_info (file,
+                                  G_FILE_ATTRIBUTE_STANDARD_NAME","
+                                  G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                                  G_FILE_COPY_NOFOLLOW_SYMLINKS,
+                                  job->common.cancellable,
+                                  NULL);
+
+        content_type = g_file_info_get_content_type (info);
+        is_desktop_file = g_content_type_is_a (content_type, );
+        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
+rename_files (CommonJob *job,
+              GList *files,
+              GList *new_names)
+{
+
+}
+static void
+report_rename_progress (CommonJob    *job,
+                        SourceInfo   *source_info,
+                        TransferInfo *transfer_info)
+{
+       int files_left;
+       char *details;
+        char *status;
+        RenameJob *delete_job;
+        GFile *current_file;
+        gchar *new_file_name;
+
+        rename_job = (RenameJob *) job;
+       files_left = source_info->num_files - transfer_info->num_files;
+
+       /* Races and whatnot could cause this to be negative... */
+       if (files_left < 0) {
+               files_left = 0;
+       }
+
+        if (source_info->num_files == 1) {
+                if (files_left > 0) {
+                        status = _("Renaming “%B”");
+                } else {
+                        status = _("Renamed “%B”");
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       (GFile*) rename_job->files->data));
+
+        } else {
+                if (files_left > 0) {
+                        status = ngettext ("Renaming %'d file",
+                                           "Renaming %'d files",
+                                            source_info->num_files);
+                } else {
+                        status = ngettext ("Renamed %'d file",
+                                           "Renamed %'d files",
+                                           source_info->num_files);
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       source_info->num_files));
+        }
+
+        current_file = g_list_nth_data (source_info->files, transfer->num_files);
+        new_file_name = g_list_nth_data (source_info->new_names);
+        details = f (_("“%B” to “%s”"),
+                     current_file,
+                     new_file_name);
+
+       nautilus_progress_info_set_details (job->progress, details);
+
+       if (source_info->num_files != 0) {
+               nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, 
source_info->num_files);
+       }
+}
+
+
+static void
+rename_task_thread_func (GTask        *task,
+                         gpointer      source_object,
+                         gpointer      task_data,
+                         GCancellable *cancellable)
+{
+       RenameJob *job = task_data;
+        SourceInfo source_info;
+       GList *l;
+       GList *to_rename_files;
+       gchar *valid_new_name;
+       GList *valid_new_names;
+       GFile *file;
+       gboolean confirmed;
+       CommonJob *common;
+       int files_skipped;
+
+       common = (CommonJob *)job;
+
+       nautilus_progress_info_start (job->common.progress);
+
+       scan_sources (job->files,
+                     &source_info,
+                     common,
+                     OP_KIND_RENAME,
+                      FALSE);
+
+       if (job_aborted (common)) {
+               return;
+       }
+
+       files_skipped = 0;
+
+        for (l = job->source_files, i = 0;
+             l != NULL && !job_aborted ((CommonJob *)job);
+             l = l->next, i++) {
+                GFile *source_file;
+                g_autoptr (GFileInfo) info;
+
+                source_file = G_FILE (l->data);
+                info = g_file_query_info (source_file,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME","
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                                          G_FILE_COPY_NOFOLLOW_SYMLINKS,
+                                          job->common.cancellable,
+                                          NULL);
+
+                if (info) {
+                        archive_compressed_sizes[i] = g_file_info_get_size (info);
+                        extract_job->total_compressed_size += archive_compressed_sizes[i];
+                }
+        }
+
+       for (l = job->files; l != NULL; l = l->next) {
+               file = l->data;
+                valid_new_name = can_rename_file (file, new_name)
+                if (valid_new_name) {
+                        valid_new_names = g_list_prepend (valid_new_names,
+                                                          valid_new_name);
+                        to_rename_files = g_list_prepend (to_rename_files,
+                                                          file);
+                } else {
+                        files_skipped++;
+                        transfer_add_file_to_count (file, common, transfer_info);
+                       report_rename_progress (job, &source_info, &transfer_info);
+                }
+       }
+
+       if (files_skipped == g_list_length (job->files)) {
+               /* User has skipped all files, report user cancel */
+               job->user_cancel = TRUE;
+       }
+}
+
+void
+nautilus_file_operations_rename_file (GFile                  *file,
+                                      const gchar            *new_name,
+                                      GtkWindow              *parent_window,
+                                      NautilusRenameCallback  done_callback,
+                                      gpointer                done_callback_data)
+{
+       GTask *task;
+       RenameJob *job;
+
+       job = op_job_new (RenameJob, parent_window);
+       job->files = g_list_append (NULL, g_object_ref (file));
+       job->new_names = g_list_append (NULL, g_strdup (new_name));
+       job->done_callback = done_callback;
+       job->done_callback_data = done_callback_data;
+
+               inhibit_power_manager ((CommonJob *)job, _("Renaming File"));
+
+       if (!nautilus_file_undo_manager_is_operating () && try_trash) {
+    job->common.undo_info = nautilus_file_undo_info_rename_new ();
+       }
+
+       task = g_task_new (NULL, NULL, rename_task_done, job);
+       g_task_set_task_data (task, job, NULL);
+       g_task_run_in_thread (task, rename_task_thread_func);
+       g_object_unref (task);
+
+}
+
+void
+nautilus_file_operations_rename (GList                  *files,
+                                 GList                  *new_names,
+                                 GtkWindow              *parent_window,
+                                 NautilusRenameCallback  done_callback,
+                                 gpointer                done_callback_data)
+{
+       GTask *task;
+       RenameJob *job;
+
+       job = op_job_new (RenameJob, parent_window);
+       job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
+       job->new_names = g_list_copy_deep (new_names, (GCopyFunc) g_strdup, NULL);
+       job->done_callback = done_callback;
+       job->done_callback_data = done_callback_data;
+
+               inhibit_power_manager ((CommonJob *)job, _("Renaming Files"));
+
+       if (!nautilus_file_undo_manager_is_operating () && try_trash) {
+                job->common.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 
(job->common.undo_info),
+                                                                   files);
+
+                nautilus_file_undo_info_batch_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME 
(job->common.undo_info),
+                                                                    new_files);
+
+                nautilus_file_undo_manager_set_action (op->undo_info);
+       }
+
+       task = g_task_new (NULL, NULL, rename_task_done, job);
+       g_task_set_task_data (task, job, NULL);
+       g_task_run_in_thread (task, rename_task_thread_func);
+       g_object_unref (task);
+}
+
 
 
 typedef struct {
@@ -2493,7 +2832,7 @@ prompt_empty_trash (GtkWindow *parent_window)
                                                    "the trash must be emptied. "
                                                    "All trashed items on the volume "
                                                    "will be permanently lost."));
-       gtk_dialog_add_buttons (GTK_DIALOG (dialog), 
+       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                                _("Do _not Empty Trash"), GTK_RESPONSE_REJECT, 
                                CANCEL, GTK_RESPONSE_CANCEL, 
                                _("Empty _Trash"), GTK_RESPONSE_ACCEPT, NULL);
@@ -2560,7 +2899,7 @@ nautilus_file_operations_unmount_mount_full (GtkWindow                      *par
                if (response == GTK_RESPONSE_ACCEPT) {
                        GTask *task;
                        EmptyTrashJob *job;
-                       
+
                        job = op_job_new (EmptyTrashJob, parent_window);
                        job->should_confirm = FALSE;
                        job->trash_dirs = get_trash_dirs_for_mount (mount);
@@ -2581,7 +2920,7 @@ nautilus_file_operations_unmount_mount_full (GtkWindow                      *par
                        return;
                }
        }
-       
+
        do_unmount (data);
 }
 
@@ -2767,6 +3106,8 @@ get_scan_primary (OpKind kind)
                return f (_("Error while moving files to trash."));
         case OP_KIND_COMPRESS:
                 return f (_("Error while compressing files."));
+        case OP_KIND_RENAME:
+                return f (_("Error while renaming files."));
         }
 }
 
@@ -2894,9 +3235,10 @@ scan_dir (GFile *dir,
 }      
 
 static void
-scan_file (GFile *file,
-          SourceInfo *source_info,
-          CommonJob *job)
+scan_file (GFile      *file,
+           SourceInfo *source_info,
+           CommonJob  *job,
+           gboolean    recursive)
 {
        GFileInfo *info;
        GError *error;
@@ -2969,22 +3311,25 @@ scan_file (GFile *file,
                }
        }
                
-       while (!job_aborted (job) && 
-              (dir = g_queue_pop_head (dirs)) != NULL) {
-               scan_dir (dir, source_info, job, dirs);
-               g_object_unref (dir);
-       }
-
+        if (recursive)
+          {
+               while (!job_aborted (job) &&
+                      (dir = g_queue_pop_head (dirs)) != NULL) {
+                       scan_dir (dir, source_info, job, dirs);
+                       g_object_unref (dir);
+         }
+    }
        /* Free all from queue if we exited early */
        g_queue_foreach (dirs, (GFunc)g_object_unref, NULL);
        g_queue_free (dirs);
 }
 
 static void
-scan_sources (GList *files,
-             SourceInfo *source_info,
-             CommonJob *job,
-             OpKind kind)
+scan_sources (GList      *files,
+              SourceInfo *source_info,
+              CommonJob  *job,
+              OpKind      kind,
+              gboolean    recursive)
 {
        GList *l;
        GFile *file;
@@ -2999,7 +3344,8 @@ scan_sources (GList *files,
 
                scan_file (file,
                           source_info,
-                          job);
+                          job,
+                           recursive);
        }
 
        /* Make sure we report the final count */
@@ -4850,7 +5196,7 @@ copy_files (CopyMoveJob *job,
                        point = NULL;
                }
 
-               
+
                same_fs = FALSE;
                if (dest_fs_id) {
                        same_fs = has_fs_id (src, dest_fs_id);
@@ -4939,7 +5285,8 @@ copy_task_thread_func (GTask *task,
        scan_sources (job->files,
                      &source_info,
                      common,
-                     OP_KIND_COPY);
+                     OP_KIND_COPY,
+                      TRUE);
        if (job_aborted (common)) {
                goto aborted;
        }
@@ -5186,12 +5533,12 @@ move_file_prepare (CopyMoveJob *move_job,
        }
 
  retry:
-       
+
        flags = G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
        if (overwrite) {
                flags |= G_FILE_COPY_OVERWRITE;
        }
-       
+
        error = NULL;
        if (g_file_move (src, dest,
                         flags,
@@ -5525,7 +5872,8 @@ move_task_thread_func (GTask *task,
        scan_sources (fallback_files,
                      &source_info,
                      common,
-                     OP_KIND_MOVE);
+                     OP_KIND_MOVE,
+                      TRUE);
        
        g_list_free (fallback_files);
        
@@ -7643,7 +7991,8 @@ compress_task_thread_func (GTask        *task,
         scan_sources (compress_job->source_files,
                       &source_info,
                       (CommonJob *)compress_job,
-                      OP_KIND_COMPRESS);
+                      OP_KIND_COMPRESS,
+                      TRUE);
 
         compress_job->total_files = source_info.num_files;
         compress_job->total_size = source_info.num_bytes;
diff --git a/src/nautilus-file-operations.h b/src/nautilus-file-operations.h
index 31b782d..9c94eb2 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -42,6 +42,9 @@ typedef void (* NautilusOpCallback)        (gboolean    success,
 typedef void (* NautilusDeleteCallback)    (GHashTable *debuting_uris,
                                            gboolean    user_cancel,
                                            gpointer    callback_data);
+typedef void (* NautilusRenameCallback)    (GHashTable *debuting_uris,
+                                            gboolean    success,
+                                            gpointer    callback_data);
 typedef void (* NautilusMountCallback)     (GVolume    *volume,
                                            gboolean    success,
                                            GObject    *callback_data_object);


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