[nautilus/wip/ernestask/tasks-irisified: 28/28] Iris-ify tasks



commit da669e43a15442a6ee641aa5b8197837856a168b
Author: Ernestas Kulik <ernestask gnome org>
Date:   Tue Aug 15 21:38:44 2017 +0300

    Iris-ify tasks
    
    For ∞x more flexibility.

 meson.build                                        |    2 +-
 src-ng/meson.build                                 |   54 ++--
 src-ng/nautilus-context-scheduler.c                |  166 ++++++++++
 ...umbnail-task.h => nautilus-context-scheduler.h} |   23 +-
 src-ng/nautilus-directory.c                        |   49 +---
 src-ng/nautilus-file.c                             |   58 +---
 src-ng/nautilus-file.h                             |    1 +
 src-ng/{main.c => nautilus-main.c}                 |   29 +-
 src-ng/nautilus-scheduler-private.h                |   49 +++
 src-ng/nautilus-scheduler.c                        |  158 ++++++++++
 src-ng/nautilus-scheduler.h                        |   62 ++++
 src-ng/nautilus-task-manager.c                     |  117 -------
 src-ng/nautilus-task-manager.h                     |   37 ---
 src-ng/nautilus-task-private.h                     |   29 --
 src-ng/nautilus-task.c                             |  327 +++++++++++++++-----
 src-ng/nautilus-task.h                             |  131 +++++++-
 src-ng/nautilus-tasks.c                            |  235 ++++++++++++++
 .../nautilus-attribute-task.h => nautilus-tasks.h} |   26 +-
 src-ng/tasks/nautilus-attribute-task.c             |  126 --------
 src-ng/tasks/nautilus-enumerate-children-task.c    |  142 ---------
 src-ng/tasks/nautilus-enumerate-children-task.h    |   38 ---
 src-ng/tasks/nautilus-rename-task.c                |  150 ---------
 src-ng/tasks/nautilus-rename-task.h                |   35 --
 src-ng/tasks/nautilus-thumbnail-task.c             |  287 -----------------
 24 files changed, 1126 insertions(+), 1205 deletions(-)
---
diff --git a/meson.build b/meson.build
index 92e4b49..5530f94 100644
--- a/meson.build
+++ b/meson.build
@@ -20,7 +20,7 @@ searchproviderdir = join_paths (datadir, 'gnome-shell', 'search-providers')
 servicedir = join_paths (datadir, 'dbus-1', 'services')
 sysconfdir = get_option ('sysconfdir')
 
-glib_ver = '>=2.51.2'
+glib_ver = '>=2.53.5'
 gnome_desktop_ver = '>=3.0.0'
 pango_ver = '1.28.3'
 gtk_ver = '>=3.22.6'
diff --git a/src-ng/meson.build b/src-ng/meson.build
index 493af05..bfc1cf3 100644
--- a/src-ng/meson.build
+++ b/src-ng/meson.build
@@ -2,33 +2,33 @@ resources = gnome.compile_resources ('nautilus-ng-resources',
                                      join_paths ('res', 'org.gnome.Nautilus.gresource.xml'),
                                      source_dir: 'res')
 
-nautilus_ng_sources = [resources,
-                       'nautilus-task.c',
-                       'nautilus-task.h',
-                       'nautilus-task-private.h',
-                       'nautilus-task-manager.c',
-                       'nautilus-task-manager.h',
-                       'nautilus-file.c',
-                       'nautilus-file.h',
-                       'tasks/nautilus-attribute-task.c',
-                       'tasks/nautilus-attribute-task.h',
-                       'nautilus-directory.c',
-                       'nautilus-directory.h',
-                       'tasks/nautilus-enumerate-children-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',
-                       'nautilus-file-table.c',
-                       'nautilus-file-table.h',
-                       'tasks/nautilus-thumbnail-task.c',
-                       'tasks/nautilus-thumbnail-task.h',
-                       'main.c']
+nautilus_ng_sources = [
+  resources,
+  'nautilus-task.c',
+  'nautilus-task.h',
+  'nautilus-file.c',
+  'nautilus-file.h',
+  'nautilus-directory.c',
+  'nautilus-directory.h',
+  'nautilus-cache.c',
+  'nautilus-cache.h',
+  'nautilus-file-changes.c',
+  'nautilus-file-changes.h',
+  'nautilus-signal-utilities.c',
+  'nautilus-signal-utilities.h',
+  'nautilus-file-table.c',
+  'nautilus-file-table.h',
+  'nautilus-attribute.c',
+  'nautilus-attribute.h',
+  'nautilus-tasks.c',
+  'nautilus-tasks.h',
+  'nautilus-scheduler.c',
+  'nautilus-scheduler.h',
+  'nautilus-context-scheduler.c',
+  'nautilus-context-scheduler.h',
+  'nautilus-scheduler-private.h',
+  'nautilus-main.c'
+]
 
 nautilus_ng_dependencies = [gio, glib, gnome_desktop, gtk]
 
diff --git a/src-ng/nautilus-context-scheduler.c b/src-ng/nautilus-context-scheduler.c
new file mode 100644
index 0000000..5f7723a
--- /dev/null
+++ b/src-ng/nautilus-context-scheduler.c
@@ -0,0 +1,166 @@
+/* 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-context-scheduler.h"
+
+#include "nautilus-scheduler-private.h"
+
+struct _NautilusContextScheduler
+{
+    NautilusScheduler parent_instance;
+
+    GMainContext *context;
+    guint source_id;
+
+    GQueue *queue;
+};
+
+G_DEFINE_TYPE (NautilusContextScheduler, nautilus_context_scheduler,
+               NAUTILUS_TYPE_SCHEDULER)
+
+typedef struct
+{
+    GSource parent_instance;
+
+    NautilusContextScheduler *scheduler;
+} NautilusSource;
+
+G_LOCK_DEFINE_STATIC (table_mutex);
+static GHashTable *scheduler_table = NULL;
+
+static void
+queue (NautilusScheduler *scheduler,
+       NautilusCallback   func,
+       gpointer           func_data)
+{
+    NautilusContextScheduler *context_scheduler;
+    NautilusThreadWork *work;
+
+    context_scheduler = NAUTILUS_CONTEXT_SCHEDULER (scheduler);
+    work = nautilus_thread_work_new (func, func_data);
+
+    g_queue_push_tail (context_scheduler->queue, work);
+}
+
+static void
+nautilus_context_scheduler_class_init (NautilusContextSchedulerClass *klass)
+{
+    NautilusSchedulerClass *scheduler_class;
+
+    scheduler_class = NAUTILUS_SCHEDULER_CLASS (klass);
+
+    scheduler_class->queue = queue;
+}
+
+static gboolean
+source_prepare (GSource *source,
+                gint    *timeout)
+{
+    NautilusSource *nautilus_source;
+
+    nautilus_source = (NautilusSource *) source;
+
+    *timeout = -1;
+
+    return !g_queue_is_empty (nautilus_source->scheduler->queue);
+}
+
+static gboolean
+source_check (GSource *source)
+{
+    NautilusSource *nautilus_source;
+
+    nautilus_source = (NautilusSource *) source;
+
+    return !g_queue_is_empty (nautilus_source->scheduler->queue);
+}
+
+static gboolean
+source_dispatch (GSource     *source,
+                 GSourceFunc  callback,
+                 gpointer     user_data)
+{
+    return callback (user_data);
+}
+
+static GSourceFuncs source_funcs =
+{
+    source_prepare,
+    source_check,
+    source_dispatch,
+    NULL
+};
+
+static gboolean
+source_callback (gpointer data)
+{
+    NautilusContextScheduler *scheduler;
+    NautilusThreadWork *work;
+
+    scheduler = data;
+
+    while ((work = g_queue_pop_head (scheduler->queue)) != NULL)
+    {
+        nautilus_thread_work_run (work);
+    }
+
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+nautilus_context_scheduler_init (NautilusContextScheduler *self)
+{
+    g_autoptr (GSource) source = NULL;
+
+    source = g_source_new (&source_funcs, sizeof (NautilusSource));
+
+    g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE);
+    g_source_set_callback (source, source_callback, self, NULL);
+
+    self->source_id = g_source_attach (source, self->context);
+}
+
+NautilusScheduler *
+nautilus_context_scheduler_get_for_context (GMainContext *context)
+{
+    NautilusContextScheduler *scheduler;
+
+    g_return_val_if_fail (context != NULL, NULL);
+
+    G_LOCK (table_mutex);
+
+    if (scheduler_table == NULL)
+    {
+        scheduler_table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                                 NULL, g_object_unref);
+    }
+
+    scheduler = g_hash_table_lookup (scheduler_table, context);
+    if (scheduler == NULL)
+    {
+        scheduler = g_object_new (NAUTILUS_TYPE_CONTEXT_SCHEDULER, NULL);
+
+        scheduler->context = context;
+
+        g_hash_table_insert (scheduler_table, context, scheduler);
+    }
+
+    G_UNLOCK (table_mutex);
+
+    return NAUTILUS_SCHEDULER (scheduler);
+}
diff --git a/src-ng/tasks/nautilus-thumbnail-task.h b/src-ng/nautilus-context-scheduler.h
similarity index 56%
rename from src-ng/tasks/nautilus-thumbnail-task.h
rename to src-ng/nautilus-context-scheduler.h
index 1c95bd0..18a753f 100644
--- a/src-ng/tasks/nautilus-thumbnail-task.h
+++ b/src-ng/nautilus-context-scheduler.h
@@ -16,19 +16,22 @@
  * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#ifndef NAUTILUS_THUMBNAIL_TASK_H_INCLUDED
-#define NAUTILUS_THUMBNAIL_TASK_H_INCLUDED
+#ifndef NAUTILUS_CONTEXT_SCHEDULER_H_INCLUDED
+#define NAUTILUS_CONTEXT_SCHEDULER_H_INCLUDED
 
-#include "nautilus-task.h"
+#include "nautilus-scheduler.h"
 
-#include <gio/gio.h>
+#define NAUTILUS_TYPE_CONTEXT_SCHEDULER (nautilus_context_scheduler_get_type ())
 
-#define NAUTILUS_TYPE_THUMBNAIL_TASK (nautilus_thumbnail_task_get_type ())
+G_DECLARE_FINAL_TYPE (NautilusContextScheduler, nautilus_context_scheduler,
+                      NAUTILUS, CONTEXT_SCHEDULER, NautilusScheduler)
 
-G_DECLARE_FINAL_TYPE (NautilusThumbnailTask, nautilus_thumbnail_task,
-                      NAUTILUS, THUMBNAIL_TASK, NautilusTask)
-
-NautilusTask *nautilus_thumbnail_task_new (GFile    *location,
-                                           gboolean  use_external_thumbnailer);
+/**
+ * nautilus_context_scheduler_get_for_context:
+ * @context: a valid main context
+ *
+ * Returns: (transfer none): the scheduler for @context
+ */
+NautilusScheduler *nautilus_context_scheduler_get_for_context (GMainContext *context);
 
 #endif
diff --git a/src-ng/nautilus-directory.c b/src-ng/nautilus-directory.c
index c125a39..a4c8c13 100644
--- a/src-ng/nautilus-directory.c
+++ b/src-ng/nautilus-directory.c
@@ -19,8 +19,8 @@
 #include "nautilus-directory.h"
 
 #include "nautilus-cache.h"
-#include "nautilus-task-manager.h"
-#include "tasks/nautilus-enumerate-children-task.h"
+#include "nautilus-task.h"
+#include "nautilus-tasks.h"
 
 enum
 {
@@ -101,7 +101,7 @@ typedef struct
     gpointer callback_data;
 } EnumerateChildrenDetails;
 
-static void
+/*static void
 create_file_list (gpointer key,
                   gpointer value,
                   gpointer user_data)
@@ -113,9 +113,9 @@ create_file_list (gpointer key,
     *list = g_list_prepend (*list,
                             nautilus_file_new_with_info (G_FILE (key),
                                                          G_FILE_INFO (value)));
-}
+}*/
 
-static void
+/*static void
 on_enumerate_children_finished (NautilusEnumerateChildrenTask *task,
                                 GFile      *file,
                                 GHashTable *files,
@@ -133,9 +133,9 @@ on_enumerate_children_finished (NautilusEnumerateChildrenTask *task,
                                                  priv->cache_items[CHILDREN]);
 
     if (cache_state == NAUTILUS_CACHE_INVALID)
-    {
+    {*/
         /* TODO: restart */
-        return;
+        /*return;
     }
 
     g_hash_table_foreach (files, create_file_list, &children);
@@ -147,7 +147,7 @@ on_enumerate_children_finished (NautilusEnumerateChildrenTask *task,
                        details->callback_data);
 
     g_free (details);
-}
+}*/
 
 void
 nautilus_directory_enumerate_children (NautilusDirectory                 *directory,
@@ -159,8 +159,6 @@ nautilus_directory_enumerate_children (NautilusDirectory                 *direct
     NautilusCacheState cache_state;
     g_autoptr (GFile) location = NULL;
     g_autoptr (NautilusTask) task = NULL;
-    EnumerateChildrenDetails *details;
-    g_autoptr (NautilusTaskManager) task_manager = NULL;
 
     g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
 
@@ -184,33 +182,10 @@ nautilus_directory_enumerate_children (NautilusDirectory                 *direct
                                      priv->cache_items[CHILDREN]);
 
     location = nautilus_file_get_location (NAUTILUS_FILE (directory));
-    task = nautilus_enumerate_children_task_new (location,
-                                                 "standard::*,"
-                                                 "access::*,"
-                                                 "mountable::*,"
-                                                 "time::*,"
-                                                 "unix::*,"
-                                                 "owner::*,"
-                                                 "selinux::*,"
-                                                 "thumbnail::*,"
-                                                 "id::filesystem,"
-                                                 "trash::orig-path,"
-                                                 "trash::deletion-date,"
-                                                 "metadata::*,"
-                                                 "recent::*",
-                                                 G_FILE_QUERY_INFO_NONE,
-                                                 cancellable);
-    details = g_new0 (EnumerateChildrenDetails, 1);
-    task_manager = nautilus_task_manager_dup_singleton ();
-
-    details->directory = directory;
-    details->callback = callback;
-    details->callback_data = user_data;
-
-    g_signal_connect (task, "finished",
-                      G_CALLBACK (on_enumerate_children_finished), details);
-
-    nautilus_task_manager_queue_task (task_manager, task);
+    task = nautilus_task_new_with_func (nautilus_enumerate_children_task_func, location,
+                                        cancellable);
+
+    nautilus_task_run (task);
 }
 
 NautilusFile *
diff --git a/src-ng/nautilus-file.c b/src-ng/nautilus-file.c
index 9701be1..b543880 100644
--- a/src-ng/nautilus-file.c
+++ b/src-ng/nautilus-file.c
@@ -21,9 +21,8 @@
 #include "nautilus-cache.h"
 #include "nautilus-directory.h"
 #include "nautilus-file-table.h"
-#include "nautilus-task-manager.h"
-#include "tasks/nautilus-attribute-task.h"
-#include "tasks/nautilus-thumbnail-task.h"
+#include "nautilus-task.h"
+#include "nautilus-tasks.h"
 
 enum
 {
@@ -203,7 +202,7 @@ typedef struct
     gpointer callback_data;
 } QueryInfoDetails;
 
-static void
+/*static void
 on_query_info_finished (NautilusAttributeTask *task,
                         GFile                 *file,
                         GFileInfo             *info,
@@ -220,9 +219,9 @@ on_query_info_finished (NautilusAttributeTask *task,
                                                  priv->cache_items[INFO]);
 
     if (cache_state == NAUTILUS_CACHE_INVALID)
-    {
+    {*/
         /* TODO: restart */
-        return;
+        /*return;
     }
 
     nautilus_cache_item_set_value (priv->cache, priv->cache_items[INFO],
@@ -232,7 +231,7 @@ on_query_info_finished (NautilusAttributeTask *task,
                        details->callback_data);
 
     g_free (details);
-}
+}*/
 
 void
 nautilus_file_query_info (NautilusFile             *file,
@@ -242,9 +241,9 @@ nautilus_file_query_info (NautilusFile             *file,
 {
     NautilusFilePrivate *priv;
     NautilusCacheState cache_state;
+    GFile *location;
     g_autoptr (NautilusTask) task = NULL;
     QueryInfoDetails *details;
-    g_autoptr (NautilusTaskManager) manager = NULL;
 
     g_return_if_fail (NAUTILUS_IS_FILE (file));
 
@@ -277,61 +276,34 @@ nautilus_file_query_info (NautilusFile             *file,
 
     nautilus_cache_item_set_pending (priv->cache, priv->cache_items[INFO]);
 
-    task = nautilus_attribute_task_new (priv->location,
-                                        "standard::*,"
-                                        "access::*,"
-                                        "mountable::*,"
-                                        "time::*,"
-                                        "unix::*,"
-                                        "owner::*,"
-                                        "selinux::*,"
-                                        "thumbnail::*,"
-                                        "id::filesystem,"
-                                        "trash::orig-path,"
-                                        "trash::deletion-date,"
-                                        "metadata::*,"
-                                        "recent::*",
-                                        G_FILE_QUERY_INFO_NONE,
-                                        cancellable);
+    location = nautilus_file_get_location (file);
+    task = nautilus_task_new_with_func (nautilus_query_info_func, location, cancellable);
+
     details = g_new0 (QueryInfoDetails, 1);
-    manager = nautilus_task_manager_dup_singleton ();
 
     details->file = file;
     details->callback = callback;
     details->callback_data = user_data;
 
-    g_signal_connect (task, "finished",
-                      G_CALLBACK (on_query_info_finished), details);
-
-    nautilus_task_manager_queue_task (manager, task);
+    nautilus_task_run (task);
 }
 
-typedef struct
-{
-    NautilusFile *file;
-
-    NautilusFileInfoCallback callback;
-    gpointer callback_data;
-} GetThumbnailDetails;
-
 void
 nautilus_file_get_thumbnail (NautilusFile              *file,
+                             GCancellable              *cancellable,
                              NautilusThumbnailCallback  callback,
                              gpointer                   user_data)
 {
     g_autoptr (GFile) location = NULL;
     g_autoptr (NautilusTask) task = NULL;
-    GetThumbnailDetails *details;
-    g_autoptr (NautilusTaskManager) manager = NULL;
 
     g_return_if_fail (NAUTILUS_IS_FILE (file));
 
     location = nautilus_file_get_location (file);
-    task = nautilus_thumbnail_task_new (location, TRUE);
-    details = g_new0 (GetThumbnailDetails, 1);
-    manager = nautilus_task_manager_dup_singleton ();
+    task = nautilus_task_new_with_func (nautilus_thumbnail_task_func,
+                                        g_object_ref (location), cancellable);
 
-    nautilus_task_manager_queue_task (manager, task);
+    nautilus_task_run (task);
 }
 
 NautilusFile *
diff --git a/src-ng/nautilus-file.h b/src-ng/nautilus-file.h
index 8d26aea..8d9407b 100644
--- a/src-ng/nautilus-file.h
+++ b/src-ng/nautilus-file.h
@@ -55,6 +55,7 @@ void nautilus_file_query_info    (NautilusFile              *file,
                                   NautilusFileInfoCallback   callback,
                                   gpointer                   user_data);
 void nautilus_file_get_thumbnail (NautilusFile              *file,
+                                  GCancellable              *cancellable,
                                   NautilusThumbnailCallback  callback,
                                   gpointer                   user_data);
 
diff --git a/src-ng/main.c b/src-ng/nautilus-main.c
similarity index 91%
rename from src-ng/main.c
rename to src-ng/nautilus-main.c
index fa36b59..57f5e80 100644
--- a/src-ng/main.c
+++ b/src-ng/nautilus-main.c
@@ -6,9 +6,8 @@
 
 #include "nautilus-directory.h"
 #include "nautilus-file.h"
-#include "nautilus-task-manager.h"
-#include "tasks/nautilus-rename-task.h"
-#include "tasks/nautilus-thumbnail-task.h"
+#include "nautilus-task.h"
+#include "nautilus-tasks.h"
 
 static void
 got_info (NautilusFile *file,
@@ -121,7 +120,7 @@ _rename (const gchar *target,
     g_autoptr (GFile) location = NULL;
     g_autoptr (NautilusFile) file = NULL;
     g_autoptr (NautilusFile) parent = NULL;
-    g_autoptr (NautilusTaskManager) manager = NULL;
+    g_autoptr (GHashTable) targets = NULL;
     g_autoptr (NautilusTask) task = NULL;
     GMainLoop *loop;
 
@@ -132,22 +131,21 @@ _rename (const gchar *target,
     parent = nautilus_file_get_parent (file);
     g_message ("Constructed NautilusFile %p for location %p",
                (gpointer) file, (gpointer) location);
-    manager = nautilus_task_manager_dup_singleton ();
-    task = nautilus_rename_task_new ();
+    targets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+    task = nautilus_task_new_with_func (nautilus_rename_task_func, targets, NULL);
     loop = g_main_loop_new (NULL, TRUE);
 
+    (void) g_hash_table_insert (targets, location, name);
+
     g_signal_connect (parent, "children-changed",
                       G_CALLBACK (on_children_changed), loop);
 
-    nautilus_rename_task_add_target (NAUTILUS_RENAME_TASK (task),
-                                     location, name);
-
-    nautilus_task_manager_queue_task (manager, task);
+    nautilus_task_run (task);
 
     g_main_loop_run (loop);
 }
 
-static void
+/*static void
 on_thumbnail_finished (NautilusThumbnailTask *task,
                        GFile                 *location,
                        GdkPixbuf             *thumbnail,
@@ -164,7 +162,7 @@ on_thumbnail_finished (NautilusThumbnailTask *task,
     gtk_container_add (GTK_CONTAINER (user_data), image);
 
     g_object_unref (thumbnail);
-}
+}*/
 
 static gboolean
 on_window_deleted (GtkWidget *widget,
@@ -179,7 +177,7 @@ on_window_deleted (GtkWidget *widget,
 static void
 display_thumbnail (const gchar *path)
 {
-    GtkWidget *window;
+    /*GtkWidget *window;
     g_autoptr (GFile) location = NULL;
     g_autoptr (NautilusTask) task = NULL;
     g_autoptr (NautilusTaskManager) task_manager = NULL;
@@ -198,7 +196,7 @@ display_thumbnail (const gchar *path)
 
     nautilus_task_manager_queue_task (task_manager, task);
 
-    gtk_main ();
+    gtk_main ();*/
 }
 
 int
@@ -231,7 +229,6 @@ main (int    argc,
         { NULL }
     };
     GError *error = NULL;
-    g_autoptr (NautilusTaskManager) manager = NULL;
 
     setlocale (LC_ALL, "");
 
@@ -253,8 +250,6 @@ main (int    argc,
         return EXIT_FAILURE;
     }
 
-    manager = nautilus_task_manager_dup_singleton ();
-
     if (check)
     {
         perform_self_test_checks (files[0]);
diff --git a/src-ng/nautilus-scheduler-private.h b/src-ng/nautilus-scheduler-private.h
new file mode 100644
index 0000000..737c2a5
--- /dev/null
+++ b/src-ng/nautilus-scheduler-private.h
@@ -0,0 +1,49 @@
+/* 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_SCHEDULER_PRIVATE_H_INCLUDED
+#define NAUTILUS_SCHEDULER_PRIVATE_H_INCLUDED
+
+#include "nautilus-scheduler.h"
+
+typedef struct NautilusThreadWork NautilusThreadWork;
+
+/**
+ * nautilus_thread_work_run:
+ * @work: an initialized #NautilusThreadWork
+ */
+void nautilus_thread_work_run (NautilusThreadWork *work);
+
+/**
+ * nautilus_thread_work_free:
+ * @work: an initialized #NautilusThreadWork
+ */
+void                nautilus_thread_work_free (NautilusThreadWork *work);
+/**
+ * nautilus_thread_work_new:
+ * @callback: the function to call
+ * @data: data to pass to @callback
+ *
+ * Returns: (transfer full): a new #NautilusThreadWork instance
+ */
+NautilusThreadWork *nautilus_thread_work_new  (NautilusCallback    callback,
+                                               gpointer            data);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (NautilusThreadWork, nautilus_thread_work_free)
+
+#endif
diff --git a/src-ng/nautilus-scheduler.c b/src-ng/nautilus-scheduler.c
new file mode 100644
index 0000000..6695efc
--- /dev/null
+++ b/src-ng/nautilus-scheduler.c
@@ -0,0 +1,158 @@
+/* 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-scheduler-private.h"
+
+typedef struct
+{
+    GThreadPool *thread_pool;
+} NautilusSchedulerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (NautilusScheduler, nautilus_scheduler, G_TYPE_OBJECT)
+
+struct NautilusThreadWork
+{
+    NautilusCallback func;
+    gpointer func_data;
+};
+
+static void
+finalize (GObject *object)
+{
+    NautilusScheduler *scheduler;
+    NautilusSchedulerPrivate *priv;
+
+    scheduler = NAUTILUS_SCHEDULER (object);
+    priv = nautilus_scheduler_get_instance_private (scheduler);
+
+    g_thread_pool_free (priv->thread_pool, TRUE, TRUE);
+
+    G_OBJECT_CLASS (nautilus_scheduler_parent_class)->finalize (object);
+}
+
+static void
+queue (NautilusScheduler *scheduler,
+       NautilusCallback   func,
+       gpointer           func_data)
+{
+    NautilusSchedulerPrivate *priv;
+    NautilusThreadWork *work;
+
+    priv = nautilus_scheduler_get_instance_private (scheduler);
+    work = nautilus_thread_work_new (func, func_data);
+
+    (void) g_thread_pool_push (priv->thread_pool, work, NULL);
+}
+
+static void
+nautilus_scheduler_class_init (NautilusSchedulerClass *klass)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = finalize;
+
+    klass->queue = queue;
+}
+
+static void
+nautilus_scheduler_init (NautilusScheduler *self)
+{
+}
+
+void
+nautilus_scheduler_queue (NautilusScheduler *scheduler,
+                          NautilusCallback   func,
+                          gpointer           func_data)
+{
+    g_return_if_fail (NAUTILUS_IS_SCHEDULER (scheduler));
+    g_return_if_fail (func != NULL);
+
+    NAUTILUS_SCHEDULER_GET_CLASS (scheduler)->queue (scheduler, func, func_data);
+}
+
+static void
+thread_pool_func (gpointer data,
+                  gpointer user_data)
+{
+    g_autoptr (NautilusThreadWork) work = NULL;
+
+    work = data;
+
+    nautilus_thread_work_run (work);
+}
+
+static NautilusScheduler *
+nautilus_scheduler_new (gint max_threads)
+{
+    NautilusScheduler *scheduler;
+    NautilusSchedulerPrivate *priv;
+
+    scheduler = g_object_new (NAUTILUS_TYPE_SCHEDULER, NULL);
+    priv = nautilus_scheduler_get_instance_private (scheduler);
+
+    priv->thread_pool = g_thread_pool_new (thread_pool_func, NULL, max_threads, FALSE, NULL);
+
+    return scheduler;
+}
+
+static gpointer
+create_default_instance (gpointer data)
+{
+    (void) data;
+
+    return nautilus_scheduler_new (16);
+}
+
+NautilusScheduler *
+nautilus_scheduler_get_default (void)
+{
+    static GOnce once = G_ONCE_INIT;
+
+    g_once (&once, create_default_instance, NULL);
+
+    return g_object_ref (once.retval);
+}
+
+
+void
+nautilus_thread_work_run (NautilusThreadWork *work)
+{
+    work->func (work->func_data);
+}
+
+void
+nautilus_thread_work_free (NautilusThreadWork *work)
+{
+    g_free (work);
+}
+
+NautilusThreadWork *
+nautilus_thread_work_new (NautilusCallback callback,
+                          gpointer         data)
+{
+    NautilusThreadWork *work;
+
+    work = g_new0 (NautilusThreadWork, 1);
+
+    work->func = callback;
+    work->func_data = data;
+
+    return work;
+}
diff --git a/src-ng/nautilus-scheduler.h b/src-ng/nautilus-scheduler.h
new file mode 100644
index 0000000..e589e7a
--- /dev/null
+++ b/src-ng/nautilus-scheduler.h
@@ -0,0 +1,62 @@
+/* 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_SCHEDULER_H_INCLUDED
+#define NAUTILUS_SCHEDULER_H_INCLUDED
+
+#include <glib-object.h>
+
+#define NAUTILUS_CALLBACK(x)    ((NautilusCallback) x)
+#define NAUTILUS_TYPE_SCHEDULER (nautilus_scheduler_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusScheduler, nautilus_scheduler,
+                          NAUTILUS, SCHEDULER, GObject)
+
+/**
+ * NautilusCallback:
+ * @data: (nullable): user data passed to #nautilus_scheduler_queue
+ */
+typedef void (*NautilusCallback) (gpointer data);
+
+struct _NautilusSchedulerClass
+{
+    GObjectClass parent_class;
+
+    void (*queue) (NautilusScheduler *scheduler,
+                   NautilusCallback   func,
+                   gpointer           func_data);
+};
+
+/**
+ * nautilus_scheduler_queue:
+ * @scheduler: an initialized #NautilusScheduler
+ * @func: the #NautilusCallback to call
+ * @func_data: (nullable): additional data to pass to @func
+ */
+void nautilus_scheduler_queue (NautilusScheduler *scheduler,
+                               NautilusCallback   func,
+                               gpointer           func_data);
+
+/**
+ * nautilus_scheduler_get_default:
+ *
+ * Returns: (transfer full): the default #NautilusScheduler instance
+ */
+NautilusScheduler *nautilus_scheduler_get_default (void);
+
+#endif
diff --git a/src-ng/nautilus-task.c b/src-ng/nautilus-task.c
index de6bdea..8506648 100644
--- a/src-ng/nautilus-task.c
+++ b/src-ng/nautilus-task.c
@@ -18,150 +18,309 @@
 
 #include "nautilus-task.h"
 
-#include "nautilus-signal-utilities.h"
+#include "nautilus-context-scheduler.h"
 
-typedef struct
+struct _NautilusTask
 {
+    GObject parent_instance;
+
+    NautilusScheduler *scheduler;
+
+    GClosure *closure;
     GCancellable *cancellable;
-    GMainContext *context;
-} NautilusTaskPrivate;
+    GError *error;
 
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusTask, nautilus_task,
-                                     G_TYPE_OBJECT)
+    GValue result;
 
-enum
-{
-    PROP_CANCELLABLE = 1,
-    N_PROPERTIES
+    GMainContext *context;
+    GQueue *callbacks;
 };
 
-static GParamSpec *properties[N_PROPERTIES] = { NULL };
+G_DEFINE_TYPE (NautilusTask, nautilus_task, G_TYPE_OBJECT)
 
 static void
-set_property (GObject      *object,
-              guint         property_id,
-              const GValue *value,
-              GParamSpec   *pspec)
+finalize (GObject *object)
 {
-    switch (property_id)
+    NautilusTask *self;
+
+    self = NAUTILUS_TASK (object);
+
+    if (self->closure != NULL)
     {
-        case PROP_CANCELLABLE:
-        {
-            NautilusTask *self;
-            NautilusTaskPrivate *priv;
+        g_clear_pointer (&self->closure, g_closure_unref);
+    }
+    if (self->cancellable != NULL)
+    {
+        g_clear_object (&self->cancellable);
+    }
+    if (self->error != NULL)
+    {
+        g_clear_pointer (&self->error, g_error_free);
+    }
 
-            self = NAUTILUS_TASK (object);
-            priv = nautilus_task_get_instance_private (self);
+    g_queue_free (self->callbacks);
 
-            if (G_UNLIKELY (priv->cancellable) != NULL)
-            {
-                g_clear_object (&priv->cancellable);
-            }
+    G_OBJECT_CLASS (nautilus_task_parent_class)->finalize (object);
+}
 
-            priv->cancellable = g_value_dup_object (value);
-        }
-        break;
+static void
+nautilus_task_class_init (NautilusTaskClass *klass)
+{
+    GObjectClass *object_class;
 
-        default:
-        {
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-        }
-    }
+    object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = finalize;
 }
 
 static void
-finalize (GObject *object)
+nautilus_task_init (NautilusTask *self)
 {
-    NautilusTask *self;
-    NautilusTaskPrivate *priv;
+    self->scheduler = nautilus_scheduler_get_default ();
+    self->cancellable = NULL;
+    self->error = NULL;
+    self->context = NULL;
+    self->callbacks = g_queue_new ();
+}
 
-    self = NAUTILUS_TASK (object);
-    priv = nautilus_task_get_instance_private (self);
+GMainContext *
+nautilus_task_get_main_context (NautilusTask *task)
+{
+    g_return_val_if_fail (NAUTILUS_IS_TASK (task), NULL);
 
-    g_clear_object (&priv->cancellable);
-    g_clear_pointer (&priv->context, g_main_context_unref);
+    if (task->context == NULL)
+    {
+        return NULL;
+    }
 
-    G_OBJECT_CLASS (nautilus_task_parent_class)->finalize (object);
+    return g_main_context_ref (task->context);
+}
+
+void
+nautilus_task_set_main_context (NautilusTask *task,
+                                GMainContext *context)
+{
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+    g_return_if_fail (context != NULL);
+
+    if (task->context != NULL)
+    {
+        g_main_context_unref (task->context);
+    }
+
+    task->context = g_main_context_ref (context);
 }
 
 static void
-nautilus_task_class_init (NautilusTaskClass *klass)
+nautilus_task_add_callback_closure (NautilusTask *task,
+                                    GClosure     *closure)
 {
-    GObjectClass *object_class;
+    g_queue_push_tail (task->callbacks, g_closure_ref (closure));
+}
 
-    object_class = G_OBJECT_CLASS (klass);
+void
+nautilus_task_add_callback (NautilusTask     *task,
+                            NautilusTaskFunc  callback,
+                            gpointer          user_data)
+{
+    g_autoptr (GClosure) closure = NULL;
 
-    object_class->set_property = set_property;
-    object_class->finalize = finalize;
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+    g_return_if_fail (callback != NULL);
+
+    closure = g_cclosure_new (G_CALLBACK (callback), user_data, NULL);
 
-    properties[PROP_CANCELLABLE] =
-        g_param_spec_object ("cancellable", "Cancellable", "Cancellable",
-                             G_TYPE_CANCELLABLE,
-                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME);
+    g_closure_set_marshal (closure, g_cclosure_marshal_VOID__VOID);
 
-    g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+    nautilus_task_add_callback_closure (task, closure);
 }
 
 static void
-nautilus_task_init (NautilusTask *self)
+invoke_callbacks_iteration (gpointer data)
+{
+    NautilusTask *task;
+    g_autoptr (GClosure) closure = NULL;
+    GValue params = { 0 };
+
+    task = data;
+    closure = g_queue_pop_head (task->callbacks);
+
+    g_value_init (&params, G_TYPE_OBJECT);
+    g_value_set_object (&params, task);
+    g_closure_invoke (closure, NULL, 1, &params, NULL);
+    g_closure_invalidate (closure);
+    g_value_unset (&params);
+}
+
+void
+nautilus_task_complete (NautilusTask *task)
 {
-    NautilusTaskPrivate *priv;
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+
+    while (g_queue_peek_head (task->callbacks) != NULL)
+    {
+        if (task->context != NULL)
+        {
+            NautilusScheduler *context_scheduler;
 
-    priv = nautilus_task_get_instance_private (self);
+            context_scheduler = nautilus_context_scheduler_get_for_context (task->context);
 
-    priv->context = g_main_context_ref_thread_default ();
+            nautilus_scheduler_queue (context_scheduler, invoke_callbacks_iteration, task);
+        }
+        else
+        {
+            invoke_callbacks_iteration (task);
+        }
+    }
 }
 
-GCancellable *
-nautilus_task_get_cancellable (NautilusTask *task)
+GValue *
+nautilus_task_get_result (NautilusTask *task)
 {
-    NautilusTaskPrivate *priv;
+    g_return_val_if_fail (NAUTILUS_IS_TASK (task), NULL);
 
-    g_return_val_if_fail (NAUTILUS_TASK (task), NULL);
+    return &task->result;
+}
 
-    priv = nautilus_task_get_instance_private (task);
+void
+nautilus_task_set_result (NautilusTask *task,
+                          GType         type,
+                          gpointer      result)
+{
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+    g_return_if_fail (type != G_TYPE_INVALID);
+    g_return_if_fail (result != NULL);
 
-    if (priv->cancellable == NULL)
+    if (G_VALUE_TYPE (&task->result) != G_TYPE_INVALID)
     {
-        return NULL;
+        g_value_unset (&task->result);
     }
 
-    return g_object_ref (priv->cancellable);
+    g_value_init (&task->result, type);
+    g_value_set_instance (&task->result, result);
+}
+
+GError *
+nautilus_task_get_error (NautilusTask *task)
+{
+    GError *error;
+
+    g_return_val_if_fail (NAUTILUS_IS_TASK (task), NULL);
+
+    error = task->error;
+    task->error = NULL;
+
+    return error;
 }
 
 void
-nautilus_task_execute (NautilusTask *task)
+nautilus_task_set_error (NautilusTask *task,
+                         GError       *error)
 {
-    NautilusTaskClass *klass;
+    g_return_if_fail (NAUTILUS_IS_TASK (task));
+    g_return_if_fail (error != NULL);
+
+    task->error = error;
+}
 
+void
+nautilus_task_set_scheduler (NautilusTask          *task,
+                             NautilusScheduler *scheduler)
+{
     g_return_if_fail (NAUTILUS_IS_TASK (task));
+    g_return_if_fail (NAUTILUS_IS_SCHEDULER (scheduler));
 
-    klass = NAUTILUS_TASK_GET_CLASS (task);
+    g_object_unref (task->scheduler);
+
+    task->scheduler = g_object_ref (scheduler);
+}
+
+static void
+nautilus_task_execute (gpointer user_data)
+{
+    NautilusTask *task;
+    GValue params = { 0 };
 
-    g_return_if_fail (klass->execute != NULL);
+    task = user_data;
 
-    klass->execute (task);
+    g_value_init (&params, G_TYPE_OBJECT);
+    g_value_set_object (&params, task);
+    g_closure_invoke (task->closure, NULL, 1, &params, NULL);
+    g_closure_invalidate (task->closure);
+    g_clear_pointer (&task->closure, g_closure_unref);
+    g_value_unset (&params);
 }
 
 void
-nautilus_task_emit_signal_in_main_context (NautilusTask *task,
-                                           guint         signal_id,
-                                           GQuark        detail,
-                                           ...)
+nautilus_task_run (NautilusTask *task)
 {
-    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_scheduler_queue (task->scheduler,
+                              NAUTILUS_CALLBACK (nautilus_task_execute),
+                              g_object_ref (task));
+}
+
+GCancellable *
+nautilus_task_get_cancellable (NautilusTask *task)
+{
+    g_return_val_if_fail (NAUTILUS_IS_TASK (task), NULL);
+
+    return g_object_ref (task->cancellable);
+}
+
+NautilusTask *
+nautilus_task_new_with_closure (GClosure     *closure,
+                                GCancellable *cancellable)
+{
+    NautilusTask *instance;
+
+    g_return_val_if_fail (closure != NULL, NULL);
+    if (cancellable != NULL)
+    {
+        g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), NULL);
+    }
+
+    instance = g_object_new (NAUTILUS_TYPE_TASK, NULL);
 
-    nautilus_emit_signal_in_main_context_va_list (task,
-                                                  priv->context,
-                                                  signal_id,
-                                                  detail,
-                                                  ap);
+    instance->closure = g_closure_ref (closure);
+    if (cancellable != NULL)
+    {
+        instance->cancellable = g_object_ref (cancellable);
+    }
 
-    va_end (ap);
+    return instance;
+}
+
+/* Would be nice to accept a #GDestroyNotify for @user_data,
+ * but it’s not a #GClosureNotify, ergo UB.
+ */
+NautilusTask *
+nautilus_task_new_with_func (NautilusTaskFunc  func,
+                             gpointer          func_data,
+                             GCancellable     *cancellable)
+{
+    g_autoptr (GClosure) closure = NULL;
+
+    g_return_val_if_fail (func != NULL, NULL);
+
+    closure = g_cclosure_new (G_CALLBACK (func), func_data, NULL);
+
+    g_closure_set_marshal (closure, g_cclosure_marshal_VOID__VOID);
+
+    return nautilus_task_new_with_closure (closure, cancellable);
+}
+
+static void
+dummy_task_func (NautilusTask *task,
+                 gpointer      user_data)
+{
+    (void) task;
+    (void) user_data;
+}
+
+NautilusTask *
+nautilus_task_new (GCancellable *cancellable)
+{
+    return nautilus_task_new_with_func (dummy_task_func, NULL, cancellable);
 }
diff --git a/src-ng/nautilus-task.h b/src-ng/nautilus-task.h
index 86440b3..8e3281b 100644
--- a/src-ng/nautilus-task.h
+++ b/src-ng/nautilus-task.h
@@ -19,27 +19,136 @@
 #ifndef NAUTILUS_TASK_H_INCLUDED
 #define NAUTILUS_TASK_H_INCLUDED
 
+#include "nautilus-scheduler.h"
+
 #include <gio/gio.h>
 #include <glib-object.h>
 
 #define NAUTILUS_TYPE_TASK (nautilus_task_get_type ())
 
-G_DECLARE_DERIVABLE_TYPE (NautilusTask, nautilus_task,
-                          NAUTILUS, TASK,
-                          GObject)
+G_DECLARE_FINAL_TYPE (NautilusTask, nautilus_task, NAUTILUS, TASK, GObject)
+
+/**
+ * NautilusTaskFunc:
+ * @task: a #NautilusTask instance
+ * @task_data: (nullable): task data
+ */
+typedef void (*NautilusTaskFunc) (NautilusTask *task,
+                                  gpointer      task_data);
 
-typedef void (*NautilusTaskCallback) (NautilusTask *task,
-                                      gpointer      user_data);
+/**
+ * nautilus_task_get_main_context:
+ * @task: an initialized #NautilusTask
+ *
+ * Returns: (nullable) (transfer full): the main context set or %NULL
+ */
+GMainContext *nautilus_task_get_main_context (NautilusTask *task);
+/**
+ * nautilus_task_set_main_context:
+ * @task: an initialized #NautilusTask
+ * @context: (transfer full): the main context
+ */
+void          nautilus_task_set_main_context (NautilusTask *task,
+                                              GMainContext *context);
 
-struct _NautilusTaskClass
-{
-    GObjectClass parent_class;
+/**
+ * nautilus_task_add_callback:
+ * @task: an initialized #NautilusTask
+ * @callback: the function to call when @task completes
+ * @user_data: (nullable): additional data to pass to @callback
+ */
+void nautilus_task_add_callback (NautilusTask     *task,
+                                 NautilusTaskFunc  callback,
+                                 gpointer          user_data);
 
-    void (*execute) (NautilusTask *task);
-};
+/**
+ * nautilus_task_complete:
+ * @task: an initialized #NautilusTask
+ */
+void nautilus_task_complete (NautilusTask *task);
 
+/**
+ * nautilus_task_get_result:
+ * @task: an initialized #NautilusTask
+ *
+ * Returns: (nullable) (transfer full): the set result or %NULL
+ */
+GValue *nautilus_task_get_result (NautilusTask *task);
+/**
+ * nautilus_task_set_result:
+ * @task: an initialized #NautilusTask
+ * @type: the #GType of @result
+ * @result: the result
+ */
+void    nautilus_task_set_result (NautilusTask *task,
+                                  GType         type,
+                                  gpointer      result);
+
+/**
+ * nautilus_task_get_error:
+ * @task: an initialized #NautilusTask
+ *
+ * Returns: (nullable) (transfer full): the set #GError or %NULL
+ */
+GError *nautilus_task_get_error (NautilusTask *task);
+/**
+ * nautilus_task_set_error:
+ * @task: an initialized #NautilusTask
+ * @error: (transfer full): a #GError
+ */
+void    nautilus_task_set_error (NautilusTask *task,
+                                 GError       *error);
+
+/**
+ * nautilus_task_set_scheduler:
+ * @task: an initialized #NautilusTask
+ * @scheduler: (transfer full): the scheduler to use
+ */
+void nautilus_task_set_scheduler (NautilusTask      *task,
+                                  NautilusScheduler *scheduler);
+
+/**
+ * nautilus_task_run:
+ * @task: an initialized #NautilusTask
+ *
+ * Schedules the task to be run asynchronously.
+ */
+void nautilus_task_run (NautilusTask *task);
+
+/**
+ * nautilus_task_get_cancellable:
+ * @task: an initialized #NautilusTask
+ *
+ * Returns: (nullable) (transfer full): a #GCancellable for @task
+ */
 GCancellable *nautilus_task_get_cancellable (NautilusTask *task);
 
-void nautilus_task_execute (NautilusTask *task);
+/**
+ * nautilus_task_new_with_closure:
+ * @closure: (transfer full): the closure to invoke when executing
+ * @cancellable: (nullable) (transfer full): an initialized #GCancellable or %NULL
+ *
+ * Returns: a new #NautilusTask instance
+ */
+NautilusTask *nautilus_task_new_with_closure (GClosure         *closure,
+                                              GCancellable     *cancellable);
+/**
+ * nautilus_task_new_with_func:
+ * @func: the function to call when executing
+ * @func_data: (nullable): data to pass to @func
+ * @cancellable: (nullable) (transfer full): an initialized #GCancellable or %NULL
+ *
+ * Returns: a new #NautilusTask instance
+ */
+NautilusTask *nautilus_task_new_with_func    (NautilusTaskFunc  func,
+                                              gpointer          func_data,
+                                              GCancellable     *cancellable);
+/**
+ * nautilus_task_new:
+ * @cancellable: (nullable) (transfer full): an initialized #GCancellable or %NULL
+ *
+ * Returns: a new #NautilusTask instance
+ */
+NautilusTask *nautilus_task_new              (GCancellable     *cancellable);
 
 #endif
diff --git a/src-ng/nautilus-tasks.c b/src-ng/nautilus-tasks.c
new file mode 100644
index 0000000..518a64b
--- /dev/null
+++ b/src-ng/nautilus-tasks.c
@@ -0,0 +1,235 @@
+/* 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-tasks.h"
+
+#include "nautilus-file-changes.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#endif
+
+#include <libgnome-desktop/gnome-desktop-thumbnail.h>
+
+const char *const DEFAULT_ATTRIBUTES = "standard::*,"
+                                       "access::*,"
+                                       "mountable::*,"
+                                       "time::*,"
+                                       "unix::*,"
+                                       "owner::*,"
+                                       "selinux::*,"
+                                       "thumbnail::*,"
+                                       "id::filesystem,"
+                                       "trash::orig-path,"
+                                       "trash::deletion-date,"
+                                       "metadata::*,"
+                                       "recent::*";
+
+void
+nautilus_enumerate_children_task_func (NautilusTask *task,
+                                       gpointer      data)
+{
+    GFile *location;
+    g_autoptr (GCancellable) cancellable = NULL;
+    GError *error = NULL;
+    g_autoptr (GFileEnumerator) enumerator = NULL;
+    GHashTable *hash_table;
+    GFileInfo *info;
+
+    location = data;
+    cancellable = nautilus_task_get_cancellable (task);
+    enumerator = g_file_enumerate_children (location, DEFAULT_ATTRIBUTES,
+                                            G_FILE_QUERY_INFO_NONE, cancellable, &error);
+
+    if (error != NULL)
+    {
+        nautilus_task_set_error (task, error);
+        nautilus_task_complete (task);
+
+        return;
+    }
+
+    hash_table = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
+                                        g_object_unref, g_object_unref);
+
+    do
+    {
+        GFile *child;
+
+        info = g_file_enumerator_next_file (enumerator, cancellable, &error);
+
+        if (error != NULL)
+        {
+            g_hash_table_destroy (hash_table);
+
+            nautilus_task_set_error (task, error);
+            nautilus_task_complete (task);
+
+            return;
+        }
+
+        if (info != NULL)
+        {
+            child = g_file_enumerator_get_child (enumerator, info);
+
+            g_assert (g_hash_table_insert (hash_table, child, info));
+        }
+    } while (info != NULL);
+
+    nautilus_task_set_result (task, G_TYPE_HASH_TABLE, hash_table);
+    nautilus_task_complete (task);
+}
+
+void
+nautilus_load_pixbuf_func (NautilusTask *task,
+                           gpointer      task_data)
+{
+    GError *error = NULL;
+    GdkPixbuf *pixbuf;
+
+    pixbuf = gdk_pixbuf_new_from_file (task_data, &error);
+
+    nautilus_task_set_error (task, error);
+    nautilus_task_set_result (task, GDK_TYPE_PIXBUF, pixbuf);
+    nautilus_task_complete (task);
+}
+
+void
+nautilus_rename_task_func (NautilusTask *task,
+                           gpointer      task_data)
+{
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+    g_autoptr (GCancellable) cancellable = NULL;
+    GError *error = NULL;
+
+    g_hash_table_iter_init (&iter, (GHashTable *) task_data);
+
+    while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+        GFile *location_from;
+        GFile *location_to;
+
+        location_from = G_FILE (key);
+        location_to = g_file_set_display_name (location_from,
+                                               (const gchar *) value,
+                                               cancellable, &error);
+
+        if (location_to != NULL)
+        {
+            nautilus_notify_file_renamed (location_from, location_to);
+        }
+        else
+        {
+        }
+    }
+
+    nautilus_task_complete (task);
+}
+
+static gpointer
+create_thumbnail_factory (gpointer data)
+{
+    (void) data;
+
+    return gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
+}
+
+static GnomeDesktopThumbnailFactory *
+get_thumbnail_factory (void)
+{
+    static GOnce once = G_ONCE_INIT;
+
+    g_once (&once, create_thumbnail_factory, NULL);
+
+    return once.retval;
+}
+
+void
+nautilus_thumbnail_task_func (NautilusTask *task,
+                              gpointer      task_data)
+{
+    GFile *location;
+    GnomeDesktopThumbnailFactory *thumbnail_factory;
+    g_autofree gchar *uri = NULL;
+    g_autoptr (GFileInfo) file_info = NULL;
+    const gchar *content_type;
+    guint64 mtime;
+    GdkPixbuf *pixbuf;
+
+    location = task_data;
+    thumbnail_factory = get_thumbnail_factory ();
+    uri = g_file_get_uri (location);
+    file_info = g_file_query_info (location,
+                                   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+                                   G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                   G_FILE_QUERY_INFO_NONE,
+                                   NULL, NULL);
+    content_type = g_file_info_get_content_type (file_info);
+    mtime = g_file_info_get_attribute_uint64 (file_info,
+                                              G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+    if (!gnome_desktop_thumbnail_factory_can_thumbnail (thumbnail_factory,
+                                                        uri, content_type,
+                                                        mtime))
+    {
+        nautilus_task_complete (task);
+        return;
+    }
+
+    pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (thumbnail_factory,
+                                                                 uri,
+                                                                 content_type);
+
+    if (pixbuf != NULL)
+    {
+        gnome_desktop_thumbnail_factory_save_thumbnail (thumbnail_factory,
+                                                        pixbuf, uri, mtime);
+    }
+    else
+    {
+        gnome_desktop_thumbnail_factory_create_failed_thumbnail (thumbnail_factory,
+                                                                 uri, mtime);
+    }
+
+    nautilus_task_set_result (task, GDK_TYPE_PIXBUF, g_object_ref (pixbuf));
+    nautilus_task_complete (task);
+}
+
+void
+nautilus_query_info_func (NautilusTask *task,
+                          gpointer      task_data)
+{
+    g_autoptr (GCancellable) cancellable = NULL;
+    GError *error = NULL;
+    GFileInfo *info;
+
+    cancellable = nautilus_task_get_cancellable (task);
+    info = g_file_query_info (task_data,
+                              DEFAULT_ATTRIBUTES,
+                              G_FILE_QUERY_INFO_NONE,
+                              cancellable,
+                              &error);
+
+    nautilus_task_set_error (task, error);
+    nautilus_task_set_result (task, G_TYPE_FILE_INFO, info);
+    nautilus_task_complete (task);
+}
diff --git a/src-ng/tasks/nautilus-attribute-task.h b/src-ng/nautilus-tasks.h
similarity index 51%
rename from src-ng/tasks/nautilus-attribute-task.h
rename to src-ng/nautilus-tasks.h
index c2d5848..3ead615 100644
--- a/src-ng/tasks/nautilus-attribute-task.h
+++ b/src-ng/nautilus-tasks.h
@@ -16,22 +16,20 @@
  * along with Nautilus.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#ifndef NAUTILUS_ATTRIBUTE_TASK_H_INCLUDED
-#define NAUTILUS_ATTRIBUTE_TASK_H_INCLUDED
+#ifndef NAUTILUS_TASKS_H_INCLUDED
+#define NAUTILUS_TASKS_H_INCLUDED
 
 #include "nautilus-task.h"
 
-#include <gio/gio.h>
-
-#define NAUTILUS_TYPE_ATTRIBUTE_TASK (nautilus_attribute_task_get_type ())
-
-G_DECLARE_FINAL_TYPE (NautilusAttributeTask, nautilus_attribute_task,
-                      NAUTILUS, ATTRIBUTE_TASK,
-                      NautilusTask)
-
-NautilusTask *nautilus_attribute_task_new (GFile               *file,
-                                           const char          *attributes,
-                                           GFileQueryInfoFlags  flags,
-                                           GCancellable        *cancellable);
+void nautilus_enumerate_children_task_func (NautilusTask *task,
+                                            gpointer      task_data);
+void nautilus_load_pixbuf_func             (NautilusTask *task,
+                                            gpointer      task_data);
+void nautilus_rename_task_func             (NautilusTask *task,
+                                            gpointer      task_data);
+void nautilus_thumbnail_task_func          (NautilusTask *task,
+                                            gpointer      task_data);
+void nautilus_query_info_func              (NautilusTask *task,
+                                            gpointer      task_data);
 
 #endif


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