[nautilus/wip/ernestask/tasks] Add thumbnail task



commit a2e88f2f2241f4de4b63fe0ebbd75f8796a164e7
Author: Ernestas Kulik <ernestask gnome org>
Date:   Mon Aug 7 14:49:01 2017 +0300

    Add thumbnail task
    
    Things to consider:
        scaling/sizing,
        NautilusIconInfo?

 src-ng/main.c                               |   66 ++++++
 src-ng/meson.build                          |   11 +-
 src-ng/nautilus-file.c                      |   29 +++
 src-ng/nautilus-file.h                      |   15 +-
 src-ng/nautilus-marshallers.list            |    1 +
 src-ng/res/org.gnome.Nautilus.gresource.xml |    6 +
 src-ng/res/text-x-preview.png               |  Bin 0 -> 923 bytes
 src-ng/tasks/nautilus-thumbnail-task.c      |  287 +++++++++++++++++++++++++++
 src-ng/tasks/nautilus-thumbnail-task.h      |   34 ++++
 9 files changed, 443 insertions(+), 6 deletions(-)
---
diff --git a/src-ng/main.c b/src-ng/main.c
index df39bd4..19c3162 100644
--- a/src-ng/main.c
+++ b/src-ng/main.c
@@ -2,11 +2,13 @@
 #include <stdlib.h>
 
 #include <glib.h>
+#include <gtk/gtk.h>
 
 #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"
 
 static void
 got_info (NautilusFile *file,
@@ -145,6 +147,60 @@ _rename (const gchar *target,
     g_main_loop_run (loop);
 }
 
+static void
+on_thumbnail_finished (NautilusThumbnailTask *task,
+                       GFile                 *location,
+                       GdkPixbuf             *thumbnail,
+                       gpointer               user_data)
+{
+    GtkWidget *image;
+
+    image = gtk_image_new_from_pixbuf (thumbnail);
+
+    gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
+    gtk_widget_set_valign (image, GTK_ALIGN_CENTER);
+    gtk_widget_set_visible (image, TRUE);
+
+    gtk_container_add (GTK_CONTAINER (user_data), image);
+
+    g_object_unref (thumbnail);
+}
+
+static gboolean
+on_window_deleted (GtkWidget *widget,
+                   GdkEvent  *event,
+                   gpointer   user_data)
+{
+    gtk_main_quit ();
+
+    return GDK_EVENT_PROPAGATE;
+}
+
+static void
+display_thumbnail (const gchar *path)
+{
+    GtkWidget *window;
+    g_autoptr (GFile) location = NULL;
+    g_autoptr (NautilusTask) task = NULL;
+    g_autoptr (NautilusTaskManager) task_manager = NULL;
+
+    gtk_init (NULL, NULL);
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    location = g_file_new_for_path (path);
+    task = nautilus_thumbnail_task_new (location, TRUE);
+    task_manager = nautilus_task_manager_dup_singleton ();
+
+    gtk_widget_show_all (window);
+
+    g_signal_connect_after (window, "delete-event", on_window_deleted, NULL);
+    g_signal_connect (task, "finished", on_thumbnail_finished, window);
+
+    nautilus_task_manager_queue_task (task_manager, task);
+
+    gtk_main ();
+}
+
 int
 main (int    argc,
       char **argv)
@@ -153,6 +209,7 @@ main (int    argc,
     gchar **files = NULL;
     gboolean check = FALSE;
     gchar *new_name = NULL;
+    gboolean thumbnail = FALSE;
     const GOptionEntry option_entries[] =
     {
         {
@@ -167,6 +224,10 @@ main (int    argc,
             "rename", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &new_name,
             "Rename FILE to NAME", "NAME"
         },
+        {
+            "thumbnail", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &thumbnail,
+            "Display thumbnail for FILE", NULL
+        },
         { NULL }
     };
     GError *error = NULL;
@@ -204,5 +265,10 @@ main (int    argc,
         _rename (files[0], new_name);
     }
 
+    if (thumbnail)
+    {
+        display_thumbnail (files[0]);
+    }
+
     return EXIT_SUCCESS;
 }
diff --git a/src-ng/meson.build b/src-ng/meson.build
index 27399e6..493af05 100644
--- a/src-ng/meson.build
+++ b/src-ng/meson.build
@@ -1,4 +1,9 @@
-nautilus_ng_sources = ['nautilus-task.c',
+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',
@@ -21,9 +26,11 @@ nautilus_ng_sources = ['nautilus-task.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_dependencies = [gio, glib]
+nautilus_ng_dependencies = [gio, glib, gnome_desktop, gtk]
 
 nautilus_marshallers = gnome.genmarshal ('nautilus-marshallers',
                                          sources: 'nautilus-marshallers.list',
diff --git a/src-ng/nautilus-file.c b/src-ng/nautilus-file.c
index 8cac9b0..9701be1 100644
--- a/src-ng/nautilus-file.c
+++ b/src-ng/nautilus-file.c
@@ -23,6 +23,7 @@
 #include "nautilus-file-table.h"
 #include "nautilus-task-manager.h"
 #include "tasks/nautilus-attribute-task.h"
+#include "tasks/nautilus-thumbnail-task.h"
 
 enum
 {
@@ -305,6 +306,34 @@ nautilus_file_query_info (NautilusFile             *file,
     nautilus_task_manager_queue_task (manager, task);
 }
 
+typedef struct
+{
+    NautilusFile *file;
+
+    NautilusFileInfoCallback callback;
+    gpointer callback_data;
+} GetThumbnailDetails;
+
+void
+nautilus_file_get_thumbnail (NautilusFile              *file,
+                             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 ();
+
+    nautilus_task_manager_queue_task (manager, task);
+}
+
 NautilusFile *
 nautilus_file_get_existing (GFile *location)
 {
diff --git a/src-ng/nautilus-file.h b/src-ng/nautilus-file.h
index 1799143..8d26aea 100644
--- a/src-ng/nautilus-file.h
+++ b/src-ng/nautilus-file.h
@@ -21,6 +21,7 @@
 
 #include <glib-object.h>
 
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gio/gio.h>
 
 #define NAUTILUS_TYPE_FILE (nautilus_file_get_type ())
@@ -31,6 +32,9 @@ typedef void (*NautilusFileInfoCallback) (NautilusFile *file,
                                           GFileInfo    *info,
                                           GError       *error,
                                           gpointer      user_data);
+typedef void (*NautilusThumbnailCallback) (NautilusFile *file,
+                                           GdkPixbuf    *pixbuf,
+                                           gpointer      user_data);
 
 typedef enum
 {
@@ -46,10 +50,13 @@ struct _NautilusFileClass
                      GFile        *new_location);
 };
 
-void nautilus_file_query_info (NautilusFile             *file,
-                               GCancellable             *cancellable,
-                               NautilusFileInfoCallback  callback,
-                               gpointer                  user_data);
+void nautilus_file_query_info    (NautilusFile              *file,
+                                  GCancellable              *cancellable,
+                                  NautilusFileInfoCallback   callback,
+                                  gpointer                   user_data);
+void nautilus_file_get_thumbnail (NautilusFile              *file,
+                                  NautilusThumbnailCallback  callback,
+                                  gpointer                   user_data);
 
 NautilusFile *nautilus_file_get_existing (GFile        *location);
 GFile        *nautilus_file_get_location (NautilusFile *file);
diff --git a/src-ng/nautilus-marshallers.list b/src-ng/nautilus-marshallers.list
index c534700..c540ed2 100644
--- a/src-ng/nautilus-marshallers.list
+++ b/src-ng/nautilus-marshallers.list
@@ -1,2 +1,3 @@
 VOID:OBJECT,OBJECT,BOXED
 VOID:OBJECT,BOXED,BOXED
+VOID:OBJECT,OBJECT
diff --git a/src-ng/res/org.gnome.Nautilus.gresource.xml b/src-ng/res/org.gnome.Nautilus.gresource.xml
new file mode 100644
index 0000000..ba6aee8
--- /dev/null
+++ b/src-ng/res/org.gnome.Nautilus.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gresources>
+  <gresource prefix='/org/gnome/Nautilus'>
+    <file>text-x-preview.png</file>
+  </gresource>
+</gresources>
diff --git a/src-ng/res/text-x-preview.png b/src-ng/res/text-x-preview.png
new file mode 100644
index 0000000..0d45ff9
Binary files /dev/null and b/src-ng/res/text-x-preview.png differ
diff --git a/src-ng/tasks/nautilus-thumbnail-task.c b/src-ng/tasks/nautilus-thumbnail-task.c
new file mode 100644
index 0000000..a5aaa61
--- /dev/null
+++ b/src-ng/tasks/nautilus-thumbnail-task.c
@@ -0,0 +1,287 @@
+/* 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-thumbnail-task.h"
+
+#include "nautilus-marshallers.h"
+#include "nautilus-task-private.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>
+
+struct _NautilusThumbnailTask
+{
+    NautilusTask parent_instance;
+
+    GFile *location;
+    gboolean use_external_thumbnailer;
+};
+
+G_DEFINE_TYPE (NautilusThumbnailTask, nautilus_thumbnail_task, NAUTILUS_TYPE_TASK)
+
+enum
+{
+    FINISHED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+finalize (GObject *object)
+{
+    NautilusThumbnailTask *self;
+
+    self = NAUTILUS_THUMBNAIL_TASK (object);
+
+    g_object_unref (self->location);
+
+    G_OBJECT_CLASS (nautilus_thumbnail_task_parent_class)->finalize (object);
+}
+
+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;
+}
+
+static GdkPixbuf *
+thumbnail_gnome_desktop (GFile *location)
+{
+    GnomeDesktopThumbnailFactory *thumbnail_factory;
+    g_autofree gchar *uri = NULL;
+    g_autoptr (GFileInfo) file_info = NULL;
+    const gchar *content_type;
+    guint64 mtime;
+    GdkPixbuf *pixbuf;
+
+    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))
+    {
+        return NULL;
+    }
+
+    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);
+    }
+
+    return pixbuf;
+}
+
+static GdkPixbuf *
+thumbnail_from_cache (GFile *location)
+{
+    g_autoptr (GFileInfo) file_info = NULL;
+    gboolean thumbnail_is_valid;
+    const char *thumbnail_path;
+
+    file_info = g_file_query_info (location,
+                                   G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
+                                   G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID,
+                                   G_FILE_QUERY_INFO_NONE,
+                                   NULL, NULL);
+
+    thumbnail_is_valid = g_file_info_get_attribute_boolean (file_info,
+                                                            G_FILE_ATTRIBUTE_THUMBNAIL_IS_VALID);
+
+    if (!thumbnail_is_valid)
+    {
+        return NULL;
+    }
+
+    thumbnail_path = g_file_info_get_attribute_byte_string (file_info,
+                                                            G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
+
+    return gdk_pixbuf_new_from_file (thumbnail_path, NULL);
+}
+
+static gpointer
+create_gdk_pixbuf_mime_types_table (gpointer data)
+{
+    GHashTable *hash_table;
+    g_autoptr (GSList) gdk_pixbuf_formats = NULL;
+
+    (void) data;
+
+    hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+    gdk_pixbuf_formats = gdk_pixbuf_get_formats ();
+
+    for (GSList *i = gdk_pixbuf_formats; i != NULL; i = i->next)
+    {
+        g_autofree GStrv mime_types = NULL;
+
+        mime_types = gdk_pixbuf_format_get_mime_types (i->data);
+
+        for (gsize j = 0; mime_types[j] != NULL; j++)
+        {
+            g_hash_table_insert (hash_table,
+                                 mime_types[j], GUINT_TO_POINTER (1));
+        }
+    }
+
+    return hash_table;
+}
+
+static GHashTable *
+get_gdk_pixbuf_mime_types (void)
+{
+    static GOnce once = G_ONCE_INIT;
+
+    g_once (&once, create_gdk_pixbuf_mime_types_table, NULL);
+
+    return once.retval;
+}
+
+static GdkPixbuf *
+thumbnail_gdk_pixbuf (GFile *location)
+{
+    GHashTable *gdk_pixbuf_mime_types;
+    g_autoptr (GFileInfo) file_info = NULL;
+    const gchar *content_type;
+
+    gdk_pixbuf_mime_types = get_gdk_pixbuf_mime_types ();
+    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);
+
+    if (g_hash_table_lookup (gdk_pixbuf_mime_types, content_type) == NULL)
+    {
+        return NULL;
+    }
+
+    return NULL;
+}
+
+static void
+execute (NautilusTask *task)
+{
+    NautilusThumbnailTask *self;
+    g_autoptr (GdkPixbuf) pixbuf = NULL;
+
+    self = NAUTILUS_THUMBNAIL_TASK (task);
+
+    if (self->use_external_thumbnailer)
+    {
+        pixbuf = thumbnail_from_cache (self->location);
+
+        if (pixbuf == NULL)
+        {
+            pixbuf = thumbnail_gnome_desktop (self->location);
+        }
+    }
+    else
+    {
+        pixbuf = thumbnail_gdk_pixbuf (self->location);
+    }
+
+    if (pixbuf == NULL)
+    {
+        pixbuf = gdk_pixbuf_new_from_resource ("/org/gnome/Nautilus/text-x-preview.png",
+                                               NULL);
+    }
+
+    nautilus_task_emit_signal_in_main_context (task, signals[FINISHED], 0,
+                                               self->location, pixbuf);
+}
+
+static void
+nautilus_thumbnail_task_class_init (NautilusThumbnailTaskClass *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,
+                                      nautilus_cclosure_marshal_VOID__OBJECT_OBJECT,
+                                      G_TYPE_NONE,
+                                      2,
+                                      G_TYPE_FILE, GDK_TYPE_PIXBUF);
+}
+
+static void
+nautilus_thumbnail_task_init (NautilusThumbnailTask *self)
+{
+}
+
+NautilusTask *
+nautilus_thumbnail_task_new (GFile    *location,
+                             gboolean  use_external_thumbnailer)
+{
+    NautilusThumbnailTask *instance;
+
+    g_return_val_if_fail (G_IS_FILE (location), NULL);
+
+    instance = g_object_new (NAUTILUS_TYPE_THUMBNAIL_TASK, NULL);
+
+    instance->location = g_object_ref (location);
+    instance->use_external_thumbnailer = use_external_thumbnailer;
+
+    return NAUTILUS_TASK (instance);
+}
diff --git a/src-ng/tasks/nautilus-thumbnail-task.h b/src-ng/tasks/nautilus-thumbnail-task.h
new file mode 100644
index 0000000..1c95bd0
--- /dev/null
+++ b/src-ng/tasks/nautilus-thumbnail-task.h
@@ -0,0 +1,34 @@
+/* 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_THUMBNAIL_TASK_H_INCLUDED
+#define NAUTILUS_THUMBNAIL_TASK_H_INCLUDED
+
+#include "nautilus-task.h"
+
+#include <gio/gio.h>
+
+#define NAUTILUS_TYPE_THUMBNAIL_TASK (nautilus_thumbnail_task_get_type ())
+
+G_DECLARE_FINAL_TYPE (NautilusThumbnailTask, nautilus_thumbnail_task,
+                      NAUTILUS, THUMBNAIL_TASK, NautilusTask)
+
+NautilusTask *nautilus_thumbnail_task_new (GFile    *location,
+                                           gboolean  use_external_thumbnailer);
+
+#endif


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