From a2e88f2f2241f4de4b63fe0ebbd75f8796a164e7 Mon Sep 17 00:00:00 2001 From: Ernestas Kulik Date: Mon, 7 Aug 2017 14:49:01 +0300 Subject: 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(-) create mode 100644 src-ng/res/org.gnome.Nautilus.gresource.xml create mode 100644 src-ng/res/text-x-preview.png create mode 100644 src-ng/tasks/nautilus-thumbnail-task.c create mode 100644 src-ng/tasks/nautilus-thumbnail-task.h diff --git a/src-ng/main.c b/src-ng/main.c index df39bd467..19c3162c3 100644 --- a/src-ng/main.c +++ b/src-ng/main.c @@ -2,11 +2,13 @@ #include #include +#include #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 27399e6f3..493af051f 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 8cac9b0ae..9701be12b 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 179914300..8d26aea1c 100644 --- a/src-ng/nautilus-file.h +++ b/src-ng/nautilus-file.h @@ -21,6 +21,7 @@ #include +#include #include #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 c53470021..c540ed283 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 000000000..ba6aee81f --- /dev/null +++ b/src-ng/res/org.gnome.Nautilus.gresource.xml @@ -0,0 +1,6 @@ + + + + text-x-preview.png + + diff --git a/src-ng/res/text-x-preview.png b/src-ng/res/text-x-preview.png new file mode 100644 index 000000000..0d45ff934 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 000000000..a5aaa613b --- /dev/null +++ b/src-ng/tasks/nautilus-thumbnail-task.c @@ -0,0 +1,287 @@ +/* Copyright (C) 2017 Ernestas Kulik + * + * 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 . + */ + +#include "nautilus-thumbnail-task.h" + +#include "nautilus-marshallers.h" +#include "nautilus-task-private.h" + +#include + +#ifndef GNOME_DESKTOP_USE_UNSTABLE_API +#define GNOME_DESKTOP_USE_UNSTABLE_API +#endif + +#include + +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 000000000..1c95bd09e --- /dev/null +++ b/src-ng/tasks/nautilus-thumbnail-task.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2017 Ernestas Kulik + * + * 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 . + */ + +#ifndef NAUTILUS_THUMBNAIL_TASK_H_INCLUDED +#define NAUTILUS_THUMBNAIL_TASK_H_INCLUDED + +#include "nautilus-task.h" + +#include + +#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 -- cgit v1.2.1