[nautilus/wip/ernestask/tasks] Implement renaming with change notifications



commit 5a77ac6d13575558f42dcb844935594b5ee6d61b
Author: Ernestas Kulik <ernestask gnome org>
Date:   Tue Jul 18 12:47:24 2017 +0300

    Implement renaming with change notifications
    
    This is fairly bare-bones in that there is still quite a couple of
    problems to solve before this is actually useful.
    
    1. Rename task: error signaling? Possible solutions are:
        1. Block thread on each error, fire a signal and wait for return
        value.
        2. Collect all errors, fire a signal, wait for return value.
        3. Pass errors to the “finished” signal.
    2. Although unlikely, but, in theory, the changes queue could never get
       flushed, as the timer is reset with each push (a hardcoded limit can
       be used, which would force a flush - sound familiar? :p).
    3. Rename task (again): the “finished” signal will usually arrive before
       changed files and their parents are notified, which presents a
       glorious race to work around in the test executable (quitting the
       main loop, namely).
    
    There is probably something else that I forgot to mention.

 src-ng/main.c                                   |  131 ++++++++++++++----
 src-ng/meson.build                              |    7 +
 src-ng/nautilus-cache.c                         |    2 +-
 src-ng/nautilus-cache.h                         |    2 +-
 src-ng/nautilus-directory.c                     |   27 ++++-
 src-ng/nautilus-directory.h                     |    4 +-
 src-ng/nautilus-file-changes.c                  |  168 +++++++++++++++++++++++
 src-ng/nautilus-file-changes.h                  |   27 ++++
 src-ng/nautilus-file.c                          |  101 +++++++++++++-
 src-ng/nautilus-file.h                          |   15 ++-
 src-ng/nautilus-signal-utilities.c              |  153 +++++++++++++++++++++
 src-ng/nautilus-signal-utilities.h              |   39 ++++++
 src-ng/nautilus-task-manager.c                  |    2 +-
 src-ng/nautilus-task-manager.h                  |    2 +-
 src-ng/nautilus-task-private.h                  |   29 ++++
 src-ng/nautilus-task.c                          |  124 ++++-------------
 src-ng/nautilus-task.h                          |   11 +-
 src-ng/tasks/nautilus-attribute-task.c          |   11 +-
 src-ng/tasks/nautilus-attribute-task.h          |    2 +-
 src-ng/tasks/nautilus-enumerate-children-task.c |   19 ++--
 src-ng/tasks/nautilus-enumerate-children-task.h |    2 +-
 src-ng/tasks/nautilus-rename-task.c             |  142 +++++++++++++++++++
 src-ng/tasks/nautilus-rename-task.h             |   35 +++++
 23 files changed, 896 insertions(+), 159 deletions(-)
---
diff --git a/src-ng/main.c b/src-ng/main.c
index 49a3281..086a250 100644
--- a/src-ng/main.c
+++ b/src-ng/main.c
@@ -1,3 +1,4 @@
+#include <locale.h>
 #include <stdlib.h>
 
 #include <glib.h>
@@ -5,6 +6,7 @@
 #include "nautilus-directory.h"
 #include "nautilus-file.h"
 #include "nautilus-task-manager.h"
+#include "tasks/nautilus-rename-task.h"
 
 static void
 got_info (NautilusFile *file,
@@ -12,12 +14,12 @@ got_info (NautilusFile *file,
           GError       *error,
           gpointer      user_data)
 {
-    g_message ("Got info for %p",
-               (gpointer) file);
-    g_message ("\tDisplay name: %s",
-               g_file_info_get_display_name (info));
-    g_message ("\tFile is directory: %s\n",
-               NAUTILUS_IS_DIRECTORY (file)? "yes" : "no");
+    g_print ("Got info for %p\n"
+             "\tDisplay name: %s\n"
+             "\tFile is directory: %s\n\n",
+             (gpointer) file,
+             g_file_info_get_display_name (info),
+             NAUTILUS_IS_DIRECTORY (file)? "yes" : "no");
 
     g_object_unref (info);
 
@@ -33,7 +35,7 @@ got_children (NautilusDirectory *directory,
               GError            *error,
               gpointer           user_data)
 {
-    g_message ("Got children for %p", (gpointer) directory);
+    g_print ("Got children for %p\n", (gpointer) directory);
 
     if (children == NULL)
     {
@@ -61,34 +63,25 @@ got_children (NautilusDirectory *directory,
     g_list_free (children);
 }
 
-int
-main (int    argc,
-      char **argv)
+static void
+perform_self_test_checks (const gchar *path)
 {
-    g_autoptr (NautilusTaskManager) manager = NULL;
     g_autoptr (GFile) location = NULL;
     g_autoptr (NautilusFile) file = NULL;
     g_autoptr (NautilusFile) duplicate_file = NULL;
     GMainLoop *loop;
 
-    if (!(argc > 1))
-    {
-        g_message ("No file provided, exiting");
-        return EXIT_SUCCESS;
-    }
-
-    manager = nautilus_task_manager_dup_singleton ();
-    location = g_file_new_for_commandline_arg (argv[1]);
+    location = g_file_new_for_path (path);
 
-    g_message ("Creating NautilusFile");
+    g_print ("Creating NautilusFile\n");
     file = nautilus_file_new (location);
-    g_message ("\tGot %p\n", (gpointer) file);
+    g_print ("\tGot %p\n\n", (gpointer) file);
 
-    g_message ("Creating another NautilusFile for the same location");
+    g_print ("Creating another NautilusFile for the same location\n");
     duplicate_file = nautilus_file_new (location);
-    g_message ("\tGot %p, which is %s\n",
-               (gpointer) duplicate_file,
-               file == duplicate_file? "the same" : "not the same");
+    g_print ("\tGot %p, which is %s\n\n",
+             (gpointer) duplicate_file,
+             file == duplicate_file? "the same" : "not the same");
 
     loop = g_main_loop_new (NULL, TRUE);
 
@@ -110,6 +103,94 @@ main (int    argc,
     }
 
     g_main_loop_run (loop);
+}
+
+static void
+rename (const gchar *target,
+        const gchar *name)
+{
+    g_autoptr (GFile) location = NULL;
+    g_autoptr (NautilusFile) file = NULL;
+    g_autoptr (NautilusTaskManager) manager = NULL;
+    g_autoptr (NautilusTask) task = NULL;
+    GMainLoop *loop;
+
+    location = g_file_new_for_path (target);
+    g_message ("Constructed GFile %p for path %s",
+               (gpointer) location, target);
+    file = nautilus_file_new (location);
+    g_message ("Constructed NautilusFile %p for location %p",
+               (gpointer) file, (gpointer) location);
+    manager = nautilus_task_manager_dup_singleton ();
+    task = nautilus_rename_task_new ();
+    loop = g_main_loop_new (NULL, TRUE);
+
+    nautilus_rename_task_add_target (NAUTILUS_RENAME_TASK (task),
+                                     location, name);
+
+    nautilus_task_manager_queue_task (manager, task);
+
+    g_main_loop_run (loop);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+    g_autoptr (GOptionContext) option_context = NULL;
+    gchar **files = NULL;
+    gboolean check = FALSE;
+    gchar *new_name = NULL;
+    const GOptionEntry option_entries[] =
+    {
+        {
+            G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE,
+            G_OPTION_ARG_FILENAME_ARRAY, &files, NULL, NULL
+        },
+        {
+            "check", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &check,
+            "Perform self-test checks with FILE as input", NULL
+        },
+        {
+            "rename", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &new_name,
+            "Rename FILE to NAME", "NAME"
+        },
+        { NULL }
+    };
+    GError *error = NULL;
+    g_autoptr (NautilusTaskManager) manager = NULL;
+
+    setlocale (LC_ALL, "");
+
+    option_context = g_option_context_new ("[FILE]");
+
+    g_option_context_add_main_entries (option_context, option_entries, NULL);
+
+    if (!g_option_context_parse (option_context, &argc, &argv, &error))
+    {
+        g_print ("%s\n", error->message);
+
+        return EXIT_FAILURE;
+    }
+
+    if (files == NULL)
+    {
+        g_print ("No input file specified\n");
+
+        return EXIT_FAILURE;
+    }
+
+    manager = nautilus_task_manager_dup_singleton ();
+
+    if (check)
+    {
+        perform_self_test_checks (files[0]);
+    }
+
+    if (new_name != NULL && new_name[0] != '\0')
+    {
+        rename (files[0], new_name);
+    }
 
     return EXIT_SUCCESS;
 }
diff --git a/src-ng/meson.build b/src-ng/meson.build
index aa62884..81b7d02 100644
--- a/src-ng/meson.build
+++ b/src-ng/meson.build
@@ -1,5 +1,6 @@
 nautilus_ng_sources = ['nautilus-task.c',
                        'nautilus-task.h',
+                       'nautilus-task-private.h',
                        'nautilus-task-manager.c',
                        'nautilus-task-manager.h',
                        'nautilus-file.c',
@@ -12,6 +13,12 @@ nautilus_ng_sources = ['nautilus-task.c',
                        'tasks/nautilus-enumerate-children-task.h',
                        'nautilus-cache.c',
                        'nautilus-cache.h',
+                       'tasks/nautilus-rename-task.c',
+                       'tasks/nautilus-rename-task.h',
+                       'nautilus-file-changes.c',
+                       'nautilus-file-changes.h',
+                       'nautilus-signal-utilities.c',
+                       'nautilus-signal-utilities.h',
                        'main.c']
 
 nautilus_ng_dependencies = [gio, glib]
diff --git a/src-ng/nautilus-cache.c b/src-ng/nautilus-cache.c
index f27a6d9..124a3aa 100644
--- a/src-ng/nautilus-cache.c
+++ b/src-ng/nautilus-cache.c
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-cache.h"
diff --git a/src-ng/nautilus-cache.h b/src-ng/nautilus-cache.h
index eade4ee..b153761 100644
--- a/src-ng/nautilus-cache.h
+++ b/src-ng/nautilus-cache.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <glib.h>
diff --git a/src-ng/nautilus-directory.c b/src-ng/nautilus-directory.c
index 9ded884..beab748 100644
--- a/src-ng/nautilus-directory.c
+++ b/src-ng/nautilus-directory.c
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-directory.h"
@@ -37,9 +37,34 @@ typedef struct
 G_DEFINE_TYPE_WITH_PRIVATE (NautilusDirectory, nautilus_directory,
                             NAUTILUS_TYPE_FILE)
 
+enum
+{
+    CHILDREN_CHANGED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+children_changed (NautilusDirectory *directory)
+{
+    g_message ("Children changed in NautilusDirectory %p",
+               (gpointer) directory);
+}
+
 static void
 nautilus_directory_class_init (NautilusDirectoryClass *klass)
 {
+    klass->children_changed = children_changed;
+
+    signals[CHILDREN_CHANGED] = g_signal_new ("children-changed",
+                                              G_TYPE_FROM_CLASS (klass),
+                                              G_SIGNAL_RUN_LAST,
+                                              G_STRUCT_OFFSET (NautilusDirectoryClass, children_changed),
+                                              NULL, NULL,
+                                              g_cclosure_marshal_VOID__VOID,
+                                              G_TYPE_NONE,
+                                              0);
 }
 
 static void
diff --git a/src-ng/nautilus-directory.h b/src-ng/nautilus-directory.h
index 9743467..60e3803 100644
--- a/src-ng/nautilus-directory.h
+++ b/src-ng/nautilus-directory.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-file.h"
@@ -32,6 +32,8 @@ typedef void (*NautilusEnumerateChildrenCallback) (NautilusDirectory *directory,
 struct _NautilusDirectoryClass
 {
     NautilusFileClass parent_class;
+
+    void (*children_changed) (NautilusDirectory *directory);
 };
 
 void nautilus_directory_enumerate_children (NautilusDirectory                 *directory,
diff --git a/src-ng/nautilus-file-changes.c b/src-ng/nautilus-file-changes.c
new file mode 100644
index 0000000..0f9752f
--- /dev/null
+++ b/src-ng/nautilus-file-changes.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-file-changes.h"
+
+#include "nautilus-file.h"
+#include "nautilus-signal-utilities.h"
+
+typedef struct
+{
+    NautilusFileChange type;
+    GFile *location;
+} Change;
+
+typedef struct
+{
+    NautilusFileChange type;
+    GFile *location_from;
+    GFile *location_to;
+} MoveChange;
+
+static void move_change_free (MoveChange *change);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MoveChange, move_change_free)
+
+static guint source = 0;
+static GMutex source_mutex;
+
+static void
+move_change_free (MoveChange *change)
+{
+    g_clear_object (&change->location_to);
+    g_clear_object (&change->location_from);
+    g_free (change);
+}
+
+static gpointer
+init_default_queue (gpointer data)
+{
+    return g_async_queue_new ();
+}
+
+static GAsyncQueue *
+get_default_queue (void)
+{
+    static GOnce once = G_ONCE_INIT;
+
+    g_once (&once, init_default_queue, NULL);
+
+    return once.retval;
+}
+
+static gboolean
+emit_signals (gpointer user_data)
+{
+    GAsyncQueue *queue;
+    Change *change;
+
+    queue = user_data;
+
+    g_async_queue_lock (queue);
+
+    while ((change = g_async_queue_try_pop_unlocked (queue)) != NULL)
+    {
+        g_autoptr (NautilusFile) file = NULL;
+        g_autoptr (NautilusFile) parent = NULL;
+
+        file = nautilus_file_new (change->location);
+        if (file == NULL)
+        {
+            continue;
+        }
+        parent = nautilus_file_get_parent (file);
+
+        switch (change->type)
+        {
+            case NAUTILUS_FILE_CHANGE_RENAMED:
+            {
+                g_autoptr (MoveChange) move_change = NULL;
+
+                move_change = (MoveChange *) change;
+
+                nautilus_emit_signal_in_main_context_by_name (file,
+                                                              NULL,
+                                                              "renamed",
+                                                              move_change->location_to);
+
+                if (parent == NULL)
+                {
+                    break;
+                }
+
+                nautilus_emit_signal_in_main_context_by_name (parent,
+                                                              NULL,
+                                                              "children-changed");
+            }
+            break;
+        }
+    }
+
+    g_async_queue_unlock (queue);
+
+    g_mutex_lock (&source_mutex);
+    source = 0;
+    g_mutex_unlock (&source_mutex);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+schedule_signal_emission (void)
+{
+    g_mutex_lock (&source_mutex);
+
+    if (source == 0)
+    {
+        source = g_timeout_add (100, emit_signals, get_default_queue ());
+    }
+    else
+    {
+        g_source_remove (source);
+    }
+
+    g_mutex_unlock (&source_mutex);
+}
+
+static void
+notify_file_moved_or_renamed (GFile    *from,
+                              GFile    *to,
+                              gboolean  move_is_rename)
+{
+    MoveChange *change;
+    GAsyncQueue *queue;
+
+    change = g_new0 (MoveChange, 1);
+    queue = get_default_queue ();
+
+    change->type = move_is_rename? NAUTILUS_FILE_CHANGE_RENAMED
+                                 : NAUTILUS_FILE_CHANGE_MOVED;
+    change->location_from = g_object_ref (from);
+    change->location_to = g_object_ref (to);
+
+    g_async_queue_push (queue, change);
+
+    schedule_signal_emission ();
+}
+
+void
+nautilus_notify_file_renamed (GFile *location,
+                              GFile *new_location)
+{
+    notify_file_moved_or_renamed (location, new_location, TRUE);
+}
diff --git a/src-ng/nautilus-file-changes.h b/src-ng/nautilus-file-changes.h
new file mode 100644
index 0000000..dd1cc8b
--- /dev/null
+++ b/src-ng/nautilus-file-changes.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_FILE_CHANGES_H_INCLUDED
+#define NAUTILUS_FILE_CHANGES_H_INCLUDED
+
+#include <gio/gio.h>
+
+void nautilus_notify_file_renamed (GFile *location,
+                                   GFile *new_location);
+
+#endif
diff --git a/src-ng/nautilus-file.c b/src-ng/nautilus-file.c
index b19a1e2..763a2cf 100644
--- a/src-ng/nautilus-file.c
+++ b/src-ng/nautilus-file.c
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-file.h"
@@ -45,9 +45,16 @@ enum
     N_PROPERTIES
 };
 
+enum
+{
+    RENAMED,
+    LAST_SIGNAL
+};
+
 static GParamSpec *properties[N_PROPERTIES] = { NULL };
-static GHashTable *files = NULL;
-static GMutex files_mutex;
+static guint       signals[LAST_SIGNAL]     = { 0 };
+static GHashTable *files                    = NULL;
+static GMutex      files_mutex;
 
 static GObject *
 constructor (GType                  type,
@@ -137,6 +144,32 @@ finalize (GObject *object)
 }
 
 static void
+renamed (NautilusFile *file,
+         GFile        *new_location)
+{
+    NautilusFilePrivate *priv;
+
+    priv = nautilus_file_get_instance_private (file);
+
+    g_message ("NautilusFile %p renamed; changing location: %p -> %p",
+               (gpointer) file, (gpointer) priv->location,
+               (gpointer) new_location);
+
+    g_mutex_lock (&files_mutex);
+
+    g_hash_table_remove (files, priv->location);
+
+    priv->location = g_object_ref (new_location);
+
+    g_assert (g_hash_table_insert (files, new_location, file));
+
+    g_mutex_unlock (&files_mutex);
+
+    nautilus_cache_item_invalidate (priv->cache, priv->cache_items[INFO],
+                                    FALSE);
+}
+
+static void
 nautilus_file_class_init (NautilusFileClass *klass)
 {
     GObjectClass *object_class;
@@ -147,12 +180,24 @@ nautilus_file_class_init (NautilusFileClass *klass)
     object_class->set_property = set_property;
     object_class->finalize = finalize;
 
+    klass->renamed = renamed;
+
     properties[PROP_LOCATION] =
         g_param_spec_object ("location", "Location", "Location",
                              G_TYPE_FILE,
                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME);
 
     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+    signals[RENAMED] = g_signal_new ("renamed",
+                                     G_TYPE_FROM_CLASS (klass),
+                                     G_SIGNAL_RUN_LAST,
+                                     G_STRUCT_OFFSET (NautilusFileClass, renamed),
+                                     NULL, NULL,
+                                     g_cclosure_marshal_VOID__OBJECT,
+                                     G_TYPE_NONE,
+                                     1,
+                                     G_TYPE_OBJECT);
 }
 
 static void
@@ -278,6 +323,29 @@ nautilus_file_query_info (NautilusFile             *file,
     nautilus_task_manager_queue_task (manager, task);
 }
 
+NautilusFile *
+nautilus_file_get_existing (GFile *location)
+{
+    NautilusFile *file = NULL;
+
+    g_return_val_if_fail (G_IS_FILE (location), NULL);
+
+    g_mutex_lock (&files_mutex);
+
+    if (files != NULL)
+    {
+        file = g_hash_table_lookup (files, location);
+        if (file != NULL)
+        {
+            file = g_object_ref (file);
+        }
+    }
+
+    g_mutex_unlock (&files_mutex);
+
+    return file;
+}
+
 GFile *
 nautilus_file_get_location (NautilusFile *file)
 {
@@ -291,6 +359,26 @@ nautilus_file_get_location (NautilusFile *file)
 }
 
 NautilusFile *
+nautilus_file_get_parent (NautilusFile *file)
+{
+    NautilusFilePrivate *priv;
+    g_autoptr (GFile) parent_location = NULL;
+    NautilusFile *parent = NULL;
+
+    g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL);
+
+    priv = nautilus_file_get_instance_private (file);
+    parent_location = g_file_get_parent (priv->location);
+
+    if (parent_location != NULL)
+    {
+        parent = nautilus_file_new (parent_location);
+    }
+
+    return parent;
+}
+
+NautilusFile *
 nautilus_file_new_with_info (GFile     *location,
                              GFileInfo *info)
 {
@@ -314,10 +402,17 @@ nautilus_file_new_with_info (GFile     *location,
 NautilusFile *
 nautilus_file_new (GFile *location)
 {
+    NautilusFile *file;
     GFileType file_type;
 
     g_return_val_if_fail (G_IS_FILE (location), NULL);
 
+    file = nautilus_file_get_existing (location);
+    if (file != NULL)
+    {
+        return g_object_ref (file);
+    }
+
     /* TODO: extension points? */
     file_type = g_file_query_file_type (location, G_FILE_QUERY_INFO_NONE,
                                         NULL);
diff --git a/src-ng/nautilus-file.h b/src-ng/nautilus-file.h
index b0caaaf..1799143 100644
--- a/src-ng/nautilus-file.h
+++ b/src-ng/nautilus-file.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifndef NAUTILUS_FILE_H_INCLUDED
@@ -32,9 +32,18 @@ typedef void (*NautilusFileInfoCallback) (NautilusFile *file,
                                           GError       *error,
                                           gpointer      user_data);
 
+typedef enum
+{
+    NAUTILUS_FILE_CHANGE_MOVED,
+    NAUTILUS_FILE_CHANGE_RENAMED
+} NautilusFileChange;
+
 struct _NautilusFileClass
 {
     GObjectClass parent_class;
+
+    void (*renamed) (NautilusFile *file,
+                     GFile        *new_location);
 };
 
 void nautilus_file_query_info (NautilusFile             *file,
@@ -42,7 +51,9 @@ void nautilus_file_query_info (NautilusFile             *file,
                                NautilusFileInfoCallback  callback,
                                gpointer                  user_data);
 
-GFile *nautilus_file_get_location (NautilusFile *file);
+NautilusFile *nautilus_file_get_existing (GFile        *location);
+GFile        *nautilus_file_get_location (NautilusFile *file);
+NautilusFile *nautilus_file_get_parent   (NautilusFile *file);
 
 /* Overwrites the info if the file exists in cache.
  * Used by NautilusDirectory when enumerating children.
diff --git a/src-ng/nautilus-signal-utilities.c b/src-ng/nautilus-signal-utilities.c
new file mode 100644
index 0000000..7435a17
--- /dev/null
+++ b/src-ng/nautilus-signal-utilities.c
@@ -0,0 +1,153 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-signal-utilities.h"
+
+#include <gobject/gvaluecollector.h>
+
+typedef struct
+{
+    GValue *instance_and_params;
+    guint signal_id;
+    GQuark detail;
+    gint n_values;
+} EmissionData;
+
+static void
+emission_data_free (EmissionData *data)
+{
+    for (int i = 0; i < data->n_values; i++)
+    {
+        g_value_unset (&data->instance_and_params[i]);
+    }
+
+    g_free (data->instance_and_params);
+    g_free (data);
+}
+
+static gboolean
+emit_signal (gpointer data)
+{
+    EmissionData *emission_data;
+
+    emission_data = data;
+
+    g_signal_emitv (emission_data->instance_and_params,
+                    emission_data->signal_id,
+                    emission_data->detail,
+                    NULL);
+
+    emission_data_free (emission_data);
+
+    return FALSE;
+}
+
+void nautilus_emit_signal_in_main_context_va_list (gpointer      instance,
+                                                   GMainContext *main_context,
+                                                   guint         signal_id,
+                                                   GQuark        detail,
+                                                   va_list       ap)
+{
+    GSignalQuery query;
+    EmissionData *emission_data;
+    g_autofree gchar *error = NULL;
+
+    g_signal_query (signal_id, &query);
+
+    if (query.signal_id == 0)
+    {
+        return;
+    }
+
+    emission_data = g_new0 (EmissionData, 1);
+
+    emission_data->instance_and_params = g_new0 (GValue, query.n_params + 1);
+    emission_data->signal_id = signal_id;
+    emission_data->detail = detail;
+
+    g_value_init (&emission_data->instance_and_params[0],
+                  G_TYPE_FROM_INSTANCE (instance));
+    g_value_set_instance (&emission_data->instance_and_params[0], instance);
+
+    for (int i = 0; i < query.n_params; i++)
+    {
+        G_VALUE_COLLECT_INIT (&emission_data->instance_and_params[i + 1],
+                              query.param_types[i],
+                              ap, 0, &error);
+
+        if (error != NULL)
+        {
+            emission_data_free (emission_data);
+
+            return;
+        }
+
+        emission_data->n_values++;
+    }
+
+    g_main_context_invoke (main_context, emit_signal, emission_data);
+}
+
+void
+nautilus_emit_signal_in_main_context_by_name (gpointer      instance,
+                                              GMainContext *main_context,
+                                              const gchar  *detailed_signal,
+                                              ...)
+{
+    guint signal_id;
+    GQuark detail;
+    gboolean signal_is_valid;
+    va_list ap;
+
+    g_return_if_fail (G_IS_OBJECT (instance));
+
+    signal_is_valid = g_signal_parse_name (detailed_signal,
+                                           G_TYPE_FROM_INSTANCE (instance),
+                                           &signal_id,
+                                           &detail,
+                                           TRUE);
+
+    g_return_if_fail (signal_is_valid);
+
+    va_start (ap, detailed_signal);
+
+    nautilus_emit_signal_in_main_context_va_list (instance, main_context,
+                                                  signal_id, detail, ap);
+
+    va_end (ap);
+}
+
+void
+nautilus_emit_signal_in_main_context (gpointer      instance,
+                                      GMainContext *main_context,
+                                      guint         signal_id,
+                                      GQuark        detail,
+                                      ...)
+{
+    va_list ap;
+
+    va_start (ap, detail);
+
+    nautilus_emit_signal_in_main_context_va_list (instance,
+                                                  main_context,
+                                                  signal_id,
+                                                  detail,
+                                                  ap);
+
+    va_end (ap);
+}
diff --git a/src-ng/nautilus-signal-utilities.h b/src-ng/nautilus-signal-utilities.h
new file mode 100644
index 0000000..4693e27
--- /dev/null
+++ b/src-ng/nautilus-signal-utilities.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_SIGNAL_UTILITIES_H_INCLUDED
+#define NAUTILUS_SIGNAL_UTILITIES_H_INCLUDED
+
+#include <glib.h>
+
+void nautilus_emit_signal_in_main_context_va_list (gpointer      instance,
+                                                   GMainContext *main_context,
+                                                   guint         signal_id,
+                                                   GQuark        detail,
+                                                   va_list       ap);
+void nautilus_emit_signal_in_main_context_by_name (gpointer      instance,
+                                                   GMainContext *main_context,
+                                                   const gchar  *detailed_signal,
+                                                   ...);
+void nautilus_emit_signal_in_main_context         (gpointer      instance,
+                                                   GMainContext *main_context,
+                                                   guint         signal_id,
+                                                   GQuark        detail,
+                                                   ...);
+
+#endif
diff --git a/src-ng/nautilus-task-manager.c b/src-ng/nautilus-task-manager.c
index 17db97a..7c20a97 100644
--- a/src-ng/nautilus-task-manager.c
+++ b/src-ng/nautilus-task-manager.c
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-task-manager.h"
diff --git a/src-ng/nautilus-task-manager.h b/src-ng/nautilus-task-manager.h
index 6d9440f..e895fde 100644
--- a/src-ng/nautilus-task-manager.h
+++ b/src-ng/nautilus-task-manager.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifndef NAUTILUS_TASK_MANAGER_H_INCLUDED
diff --git a/src-ng/nautilus-task-private.h b/src-ng/nautilus-task-private.h
new file mode 100644
index 0000000..fc4895b
--- /dev/null
+++ b/src-ng/nautilus-task-private.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_TASK_PRIVATE_H_INCLUDED
+#define NAUTILUS_TASK_PRIVATE_H_INCLUDED
+
+#include "nautilus-task.h"
+
+void nautilus_task_emit_signal_in_main_context (NautilusTask *task,
+                                                guint         signal_id,
+                                                GQuark        detail,
+                                                ...);
+
+#endif
diff --git a/src-ng/nautilus-task.c b/src-ng/nautilus-task.c
index c085455..de6bdea 100644
--- a/src-ng/nautilus-task.c
+++ b/src-ng/nautilus-task.c
@@ -13,12 +13,12 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-task.h"
 
-#include <gobject/gvaluecollector.h>
+#include "nautilus-signal-utilities.h"
 
 typedef struct
 {
@@ -29,13 +29,6 @@ typedef struct
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusTask, nautilus_task,
                                      G_TYPE_OBJECT)
 
-typedef struct
-{
-    GValue instance_and_params[4];
-    guint signal_id;
-    int n_values;
-} EmissionData;
-
 enum
 {
     PROP_CANCELLABLE = 1,
@@ -92,94 +85,6 @@ finalize (GObject *object)
 }
 
 static void
-emission_data_free (EmissionData *data)
-{
-    for (int i = 0; i < data->n_values; i++)
-    {
-        g_value_unset (&data->instance_and_params[i]);
-    }
-
-    g_free (data);
-}
-
-static gboolean
-emit_signal (gpointer data)
-{
-    EmissionData *emission_data;
-
-    emission_data = data;
-
-    g_signal_emitv (emission_data->instance_and_params,
-                    emission_data->signal_id,
-                    0, NULL);
-
-    g_clear_pointer (&emission_data, emission_data_free);
-
-    return FALSE;
-}
-
-static void
-emit_signal_in_main_context (NautilusTask *instance,
-                             guint         signal_id,
-                             ...)
-{
-    va_list ap;
-    EmissionData *emission_data;
-    GSignalQuery query;
-    g_autofree gchar *error = NULL;
-    NautilusTaskPrivate *priv;
-
-    emission_data = g_new0 (EmissionData, 1);
-    priv = nautilus_task_get_instance_private (instance);
-
-    va_start (ap, signal_id);
-
-    g_value_init (&emission_data->instance_and_params[0],
-                  G_TYPE_FROM_INSTANCE (instance));
-    g_value_set_instance (&emission_data->instance_and_params[0], instance);
-
-    emission_data->signal_id = signal_id;
-
-    g_signal_query (signal_id, &query);
-
-    if (query.signal_id == 0)
-    {
-        g_clear_pointer (&emission_data, emission_data_free);
-
-        va_end (ap);
-
-        return;
-    }
-
-    for (int i = 0; i < query.n_params; i++)
-    {
-        G_VALUE_COLLECT_INIT (&emission_data->instance_and_params[i + 1],
-                              query.param_types[i],
-                              ap, 0, &error);
-
-        if (error != NULL)
-        {
-            break;
-        }
-
-        emission_data->n_values++;
-    }
-
-    if (error != NULL)
-    {
-        g_clear_pointer (&emission_data, emission_data_free);
-
-        va_end (ap);
-
-        return;
-    }
-
-    g_main_context_invoke (priv->context, emit_signal, emission_data);
-
-    va_end (ap);
-}
-
-static void
 nautilus_task_class_init (NautilusTaskClass *klass)
 {
     GObjectClass *object_class;
@@ -189,8 +94,6 @@ nautilus_task_class_init (NautilusTaskClass *klass)
     object_class->set_property = set_property;
     object_class->finalize = finalize;
 
-    klass->emit_signal_in_main_context = emit_signal_in_main_context;
-
     properties[PROP_CANCELLABLE] =
         g_param_spec_object ("cancellable", "Cancellable", "Cancellable",
                              G_TYPE_CANCELLABLE,
@@ -239,3 +142,26 @@ nautilus_task_execute (NautilusTask *task)
 
     klass->execute (task);
 }
+
+void
+nautilus_task_emit_signal_in_main_context (NautilusTask *task,
+                                           guint         signal_id,
+                                           GQuark        detail,
+                                           ...)
+{
+    NautilusTaskPrivate *priv;
+    va_list ap;
+
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+
+    priv = nautilus_task_get_instance_private (task);
+    va_start (ap, detail);
+
+    nautilus_emit_signal_in_main_context_va_list (task,
+                                                  priv->context,
+                                                  signal_id,
+                                                  detail,
+                                                  ap);
+
+    va_end (ap);
+}
diff --git a/src-ng/nautilus-task.h b/src-ng/nautilus-task.h
index 8c6eea0..86440b3 100644
--- a/src-ng/nautilus-task.h
+++ b/src-ng/nautilus-task.h
@@ -13,11 +13,11 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#ifndef NAUTILUS_TASK_H
-#define NAUTILUS_TASK_H
+#ifndef NAUTILUS_TASK_H_INCLUDED
+#define NAUTILUS_TASK_H_INCLUDED
 
 #include <gio/gio.h>
 #include <glib-object.h>
@@ -35,10 +35,7 @@ struct _NautilusTaskClass
 {
     GObjectClass parent_class;
 
-    void (*execute)                     (NautilusTask *task);
-    void (*emit_signal_in_main_context) (NautilusTask  *instance,
-                                         guint          signal_id,
-                                         ...);
+    void (*execute) (NautilusTask *task);
 };
 
 GCancellable *nautilus_task_get_cancellable (NautilusTask *task);
diff --git a/src-ng/tasks/nautilus-attribute-task.c b/src-ng/tasks/nautilus-attribute-task.c
index 11af327..9d21517 100644
--- a/src-ng/tasks/nautilus-attribute-task.c
+++ b/src-ng/tasks/nautilus-attribute-task.c
@@ -13,12 +13,13 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-attribute-task.h"
 
 #include "nautilus-marshallers.h"
+#include "nautilus-task-private.h"
 
 struct _NautilusAttributeTask
 {
@@ -60,19 +61,17 @@ execute (NautilusTask *task)
     g_autoptr (GCancellable) cancellable = NULL;
     GError *error = NULL;
     GFileInfo *info;
-    NautilusTaskClass *klass;
 
     self = NAUTILUS_ATTRIBUTE_TASK (task);
-    cancellable = nautilus_task_get_cancellable (NAUTILUS_TASK (self));
+    cancellable = nautilus_task_get_cancellable (task);
     info = g_file_query_info (self->file,
                               self->attributes,
                               self->flags,
                               cancellable,
                               &error);
-    klass = NAUTILUS_TASK_CLASS (G_OBJECT_GET_CLASS (self));
 
-    klass->emit_signal_in_main_context (task, signals[FINISHED],
-                                        self->file, info, error);
+    nautilus_task_emit_signal_in_main_context (task, signals[FINISHED], 0,
+                                               self->file, info, error);
 }
 
 static void
diff --git a/src-ng/tasks/nautilus-attribute-task.h b/src-ng/tasks/nautilus-attribute-task.h
index fba86b2..c2d5848 100644
--- a/src-ng/tasks/nautilus-attribute-task.h
+++ b/src-ng/tasks/nautilus-attribute-task.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifndef NAUTILUS_ATTRIBUTE_TASK_H_INCLUDED
diff --git a/src-ng/tasks/nautilus-enumerate-children-task.c b/src-ng/tasks/nautilus-enumerate-children-task.c
index ddb0c42..77d9a28 100644
--- a/src-ng/tasks/nautilus-enumerate-children-task.c
+++ b/src-ng/tasks/nautilus-enumerate-children-task.c
@@ -13,12 +13,13 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "nautilus-enumerate-children-task.h"
 
 #include "nautilus-marshallers.h"
+#include "nautilus-task-private.h"
 
 struct _NautilusEnumerateChildrenTask
 {
@@ -45,7 +46,6 @@ execute (NautilusTask *task)
 {
     NautilusEnumerateChildrenTask *self;
     g_autoptr (GCancellable) cancellable = NULL;
-    NautilusTaskClass *klass;
     GError *error = NULL;
     g_autoptr (GFileEnumerator) enumerator = NULL;
     GHashTable *hash_table;
@@ -53,14 +53,14 @@ execute (NautilusTask *task)
 
     self = NAUTILUS_ENUMERATE_CHILDREN_TASK (task);
     cancellable = nautilus_task_get_cancellable (NAUTILUS_TASK (self));
-    klass = NAUTILUS_TASK_CLASS (G_OBJECT_GET_CLASS (self));
     enumerator = g_file_enumerate_children (self->file, self->attributes,
                                             self->flags, cancellable, &error);
 
     if (error != NULL)
     {
-        klass->emit_signal_in_main_context (task, signals[FINISHED],
-                                            self->file, NULL, error);
+        nautilus_task_emit_signal_in_main_context (task,
+                                                   signals[FINISHED], 0,
+                                                   self->file, NULL, error);
     }
 
     hash_table = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
@@ -76,8 +76,9 @@ execute (NautilusTask *task)
         {
             g_hash_table_destroy (hash_table);
 
-            klass->emit_signal_in_main_context (task, signals[FINISHED],
-                                                self->file, NULL, error);
+            nautilus_task_emit_signal_in_main_context (task,
+                                                       signals[FINISHED], 0,
+                                                       self->file, NULL, error);
 
             return;
         }
@@ -90,8 +91,8 @@ execute (NautilusTask *task)
         }
     } while (info != NULL);
 
-    klass->emit_signal_in_main_context (task, signals[FINISHED],
-                                        self->file, hash_table, error);
+    nautilus_task_emit_signal_in_main_context (task, signals[FINISHED], 0,
+                                               self->file, hash_table, error);
 }
 
 static void
diff --git a/src-ng/tasks/nautilus-enumerate-children-task.h b/src-ng/tasks/nautilus-enumerate-children-task.h
index 2aa76ee..f43887c 100644
--- a/src-ng/tasks/nautilus-enumerate-children-task.h
+++ b/src-ng/tasks/nautilus-enumerate-children-task.h
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Nautilus.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifndef NAUTILUS_ENUMERATE_CHILDREN_TASK_H_INCLUDED
diff --git a/src-ng/tasks/nautilus-rename-task.c b/src-ng/tasks/nautilus-rename-task.c
new file mode 100644
index 0000000..b210640
--- /dev/null
+++ b/src-ng/tasks/nautilus-rename-task.c
@@ -0,0 +1,142 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-rename-task.h"
+
+#include "nautilus-file-changes.h"
+#include "nautilus-task-private.h"
+
+#include <glib.h>
+
+struct _NautilusRenameTask
+{
+    NautilusTask parent_instance;
+
+    GHashTable *targets;
+};
+
+G_DEFINE_TYPE (NautilusRenameTask, nautilus_rename_task, NAUTILUS_TYPE_TASK)
+
+enum
+{
+    FINISHED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+finalize (GObject *object)
+{
+    NautilusRenameTask *self;
+
+    self = NAUTILUS_RENAME_TASK (object);
+
+    g_hash_table_destroy (self->targets);
+
+    G_OBJECT_CLASS (nautilus_rename_task_parent_class)->finalize (object);
+}
+
+static void
+execute (NautilusTask *task)
+{
+    NautilusRenameTask *self;
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+    g_autoptr (GCancellable) cancellable = NULL;
+    GError *error = NULL;
+
+    self = NAUTILUS_RENAME_TASK (task);
+    cancellable = nautilus_task_get_cancellable (task);
+
+    g_hash_table_iter_init (&iter, self->targets);
+
+    while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+        g_autoptr (GFile) location_from = NULL;
+        g_autoptr (GFile) location_to = NULL;
+
+        location_from = G_FILE (key);
+        location_to = g_file_set_display_name (location_from,
+                                               (const gchar *) value,
+                                               cancellable, &error);
+
+        if (location_to != NULL)
+        {
+            g_message ("GFile %p renamed to %p",
+                       (gpointer) location_from,
+                       (gpointer) location_to);
+            nautilus_notify_file_renamed (location_from, location_to);
+        }
+    }
+
+    /* This will typically be handled before the file and its parent
+     * are notified of the changes.
+     */
+    nautilus_task_emit_signal_in_main_context (task, signals[FINISHED], 0);
+}
+
+static void
+nautilus_rename_task_class_init (NautilusRenameTaskClass *klass)
+{
+    GObjectClass *object_class;
+    NautilusTaskClass *task_class;
+
+    object_class = G_OBJECT_CLASS (klass);
+    task_class = NAUTILUS_TASK_CLASS (klass);
+
+    object_class->finalize = finalize;
+
+    task_class->execute = execute;
+
+    signals[FINISHED] = g_signal_new ("finished",
+                                      G_TYPE_FROM_CLASS (klass),
+                                      G_SIGNAL_RUN_LAST,
+                                      0, NULL, NULL,
+                                      g_cclosure_marshal_VOID__VOID,
+                                      G_TYPE_NONE,
+                                      0);
+}
+
+static void
+nautilus_rename_task_init (NautilusRenameTask *self)
+{
+    self->targets = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
+                                           g_object_unref, g_free);
+}
+
+void
+nautilus_rename_task_add_target (NautilusRenameTask *task,
+                                 GFile              *file,
+                                 const gchar        *name)
+{
+    g_return_if_fail (NAUTILUS_IS_RENAME_TASK (task));
+    g_return_if_fail (!g_hash_table_contains (task->targets, file));
+    g_return_if_fail (G_IS_FILE (file));
+    g_return_if_fail (name != NULL);
+
+    (void) g_hash_table_insert (task->targets,
+                                g_object_ref (file), g_strdup (name));
+}
+
+NautilusTask *
+nautilus_rename_task_new (void)
+{
+    return g_object_new (NAUTILUS_TYPE_RENAME_TASK, NULL);
+}
diff --git a/src-ng/tasks/nautilus-rename-task.h b/src-ng/tasks/nautilus-rename-task.h
new file mode 100644
index 0000000..9ffcaf0
--- /dev/null
+++ b/src-ng/tasks/nautilus-rename-task.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2017 Ernestas Kulik <ernestask gnome org>
+ *
+ * This file is part of Nautilus.
+ *
+ * Nautilus 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.
+ *
+ * Nautilus 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 Nautilus.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_RENAME_TASK_H_INCLUDED
+#define NAUTILUS_RENAME_TASK_H_INCLUDED
+
+#include "nautilus-task.h"
+
+#define NAUTILUS_TYPE_RENAME_TASK (nautilus_rename_task_get_type ())
+
+G_DECLARE_FINAL_TYPE (NautilusRenameTask, nautilus_rename_task,
+                      NAUTILUS, RENAME_TASK, NautilusTask)
+
+void nautilus_rename_task_add_target (NautilusRenameTask *task,
+                                      GFile              *file,
+                                      const gchar        *name);
+
+NautilusTask *nautilus_rename_task_new (void);
+
+#endif


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