From 671ad508d0e86581f5ac81f777bda27f775ae56e Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Sat, 14 Jan 2012 19:35:15 +0300 Subject: [PATCH] Thumbnail desktop icons from tumblerd and display icon tooltips Adds support for drawing thumbnails from the dbus thumbnail service. Adds a show-thumbnails xfconf property to toggle showing thumbnails. Moves the marshal.list into common to allow xfdesktop-settings to use thumbnails as well. It's possible to add code to fallback to gdk_pixbuf loading in a worker thread in the future should tumblerd not be installed but this isn't implemented or needed for this patch to operate. This patch also sets it so that xfdesktop will display preview icons on the tooltip popup. For bug 4344. --- common/Makefile.am | 37 +++- common/xfdesktop-marshal.list | 4 + common/xfdesktop-thumbnailer.c | 588 +++++++++++++++++++++++++++++++++++++ common/xfdesktop-thumbnailer.h | 101 +++++++ doc/README.xfconf | 1 + src/Makefile.am | 16 +- src/xfdesktop-file-icon-manager.c | 124 ++++++++- src/xfdesktop-file-icon-manager.h | 4 + src/xfdesktop-icon-view.c | 1 + src/xfdesktop-icon.c | 31 ++ src/xfdesktop-icon.h | 6 + src/xfdesktop-marshal.list | 2 - src/xfdesktop-regular-file-icon.c | 78 +++++- 13 files changed, 970 insertions(+), 23 deletions(-) create mode 100644 common/xfdesktop-marshal.list create mode 100644 common/xfdesktop-thumbnailer.c create mode 100644 common/xfdesktop-thumbnailer.h delete mode 100644 src/xfdesktop-marshal.list diff --git a/common/Makefile.am b/common/Makefile.am index b6145ba..1af6ac8 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -2,9 +2,44 @@ noinst_LTLIBRARIES = libxfdesktop.la libxfdesktop_la_SOURCES = \ xfdesktop-common.c \ - xfdesktop-common.h + xfdesktop-common.h \ + xfdesktop-marshal.c \ + xfdesktop-marshal.h libxfdesktop_la_CFLAGS = \ -I$(top_srcdir)/src \ $(LIBXFCE4UTIL_CFLAGS) \ $(GTK_CFLAGS) + +if ENABLE_DESKTOP_ICONS +if ENABLE_FILE_ICONS + +libxfdesktop_la_SOURCES += \ + xfdesktop-thumbnailer.c \ + xfdesktop-thumbnailer.h + +libxfdesktop_la_CFLAGS += \ + -DDBUS_API_SUBJECT_TO_CHANGE \ + $(DBUS_CFLAGS) + +endif +endif + +DISTCLEANFILES = \ + $(xfdesktop_built_sources) \ + stamp-xfdesktop-marshal.h \ + xfdesktop-marshal.c \ + xfdesktop-marshal.h + +xfdesktop-marshal.h: stamp-xfdesktop-marshal.h + @true +stamp-xfdesktop-marshal.h: xfdesktop-marshal.list Makefile + $(AM_V_GEN) glib-genmarshal --prefix=xfdesktop_marshal xfdesktop-marshal.list --header > xfdesktop-marshal.h && \ + echo timestamp > $(@F) +xfdesktop-marshal.c: xfdesktop-marshal.list Makefile + $(AM_V_GEN) echo '#include "xfdesktop-marshal.h"' > xfdesktop-marshal.c && \ + glib-genmarshal --prefix=xfdesktop_marshal xfdesktop-marshal.list --body >> xfdesktop-marshal.c && \ + glib-genmarshal --prefix=xfdesktop_marshal xfdesktop-marshal.list --header > xfdesktop-marshal.h + +EXTRA_DIST = \ + xfdesktop-marshal.list diff --git a/common/xfdesktop-marshal.list b/common/xfdesktop-marshal.list new file mode 100644 index 0000000..8fe4148 --- /dev/null +++ b/common/xfdesktop-marshal.list @@ -0,0 +1,4 @@ +BOOLEAN:VOID +BOOLEAN:ENUM,INT +VOID:UINT,BOXED +VOID:STRING,STRING diff --git a/common/xfdesktop-thumbnailer.c b/common/xfdesktop-thumbnailer.c new file mode 100644 index 0000000..20bf489 --- /dev/null +++ b/common/xfdesktop-thumbnailer.c @@ -0,0 +1,588 @@ +/* + * xfdesktop - xfce4's desktop manager + * + * Copyright(c) 2006 Brian Tarricone, + * Copyright(c) 2006 Benedikt Meurer, + * Copyright(c) 2010-2011 Jannis Pohlmann, + * + * This program 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. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * xfdesktop-thumbnailer is based on thumbnailer code from Ristretto + * Copyright (c) Stephan Arts 2009-2011 + * + * Thumbnailer Spec + * http://live.gnome.org/ThumbnailerSpec + * Thumbnail Managing Standard + * http://people.freedesktop.org/~vuntz/thumbnail-spec-cache/creation.html + */ + +#include + +#include + +#include +#include +#include + +#include + +#include "xfdesktop-thumbnailer.h" +#include "xfdesktop-marshal.h" + +static void +xfdesktop_thumbnailer_init (GObject *); +static void +xfdesktop_thumbnailer_class_init (GObjectClass *); + +static void +xfdesktop_thumbnailer_dispose (GObject *object); +static void +xfdesktop_thumbnailer_finalize (GObject *object); + +static void +xfdesktop_thumbnailer_request_finished_dbus ( + DBusGProxy *proxy, + gint handle, + gpointer data); +static void +xfdesktop_thumbnailer_thumbnail_ready_dbus ( + DBusGProxy *proxy, + gint handle, + const gchar **uri, + gpointer data); + +static gboolean +xfdesktop_thumbnailer_queue_request_timer (XfdesktopThumbnailer *thumbnailer); + +static GObjectClass *parent_class = NULL; +static XfdesktopThumbnailer *thumbnailer_object = NULL; + +typedef enum +{ + XFDESKTOP_NULL_SERVICE, + XFDESKTOP_THUMBNAILER_SERVICE, + XFDESKTOP_GTK_SERVICE, + XFDESKTOP_SERVICE_COUNT +} XFDESKTOP_ICON_SERVICE; + +enum +{ + THUMBNAIL_READY, + LAST_SIGNAL, +}; + +static guint thumbnailer_signals[LAST_SIGNAL] = { 0, }; + +GType +xfdesktop_thumbnailer_get_type (void) +{ + static GType xfdesktop_thumbnailer_type = 0; + + if (!xfdesktop_thumbnailer_type) { + static const GTypeInfo xfdesktop_thumbnailer_info = + { + sizeof (XfdesktopThumbnailerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) xfdesktop_thumbnailer_class_init, + (GClassFinalizeFunc) NULL, + NULL, + sizeof (XfdesktopThumbnailer), + 0, + (GInstanceInitFunc) xfdesktop_thumbnailer_init, + NULL + }; + + xfdesktop_thumbnailer_type = g_type_register_static ( + G_TYPE_OBJECT, + "XfdesktopThumbnailer", + &xfdesktop_thumbnailer_info, + 0); + } + return xfdesktop_thumbnailer_type; +} + +struct _XfdesktopThumbnailerPriv +{ + DBusGProxy *proxy; + + GSList *queue; + gchar **supported_mimetypes; + gint handle; + XFDESKTOP_ICON_SERVICE service; + + gint request_timer_id; +}; + +static void +xfdesktop_thumbnailer_init (GObject *object) +{ + XfdesktopThumbnailer *thumbnailer; + DBusGConnection *connection; + + thumbnailer = XFDESKTOP_THUMBNAILER (object); + + thumbnailer->priv = g_new0 (XfdesktopThumbnailerPriv, 1); + + connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + + if (connection) { + thumbnailer->priv->proxy = dbus_g_proxy_new_for_name ( + connection, + "org.freedesktop.thumbnails.Thumbnailer1", + "/org/freedesktop/thumbnails/Thumbnailer1", + "org.freedesktop.thumbnails.Thumbnailer1"); + + if(thumbnailer->priv->proxy) { + gchar **supported_uris; + + /* We have a thumbnail service */ + thumbnailer->priv->service = XFDESKTOP_THUMBNAILER_SERVICE; + + dbus_g_object_register_marshaller ( + (GClosureMarshal) xfdesktop_marshal_VOID__UINT_BOXED, + G_TYPE_NONE, G_TYPE_UINT, + G_TYPE_STRV, G_TYPE_INVALID); + + dbus_g_proxy_add_signal ( + thumbnailer->priv->proxy, + "Finished", G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_add_signal ( + thumbnailer->priv->proxy, + "Ready", G_TYPE_UINT, G_TYPE_STRV, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal ( + thumbnailer->priv->proxy, + "Finished", G_CALLBACK (xfdesktop_thumbnailer_request_finished_dbus), + thumbnailer, NULL); + dbus_g_proxy_connect_signal ( + thumbnailer->priv->proxy, + "Ready", G_CALLBACK(xfdesktop_thumbnailer_thumbnail_ready_dbus), + thumbnailer, NULL); + + dbus_g_proxy_call (thumbnailer->priv->proxy, "GetSupported", NULL, G_TYPE_INVALID, + G_TYPE_STRV, &supported_uris, + G_TYPE_STRV, &thumbnailer->priv->supported_mimetypes, + G_TYPE_INVALID); + + } else { + /* We don't have a thumbnail service, use the GTK one */ + thumbnailer->priv->service = XFDESKTOP_GTK_SERVICE; + } + + dbus_g_connection_unref (connection); + } +} + +static void +xfdesktop_thumbnailer_class_init (GObjectClass *object_class) +{ + XfdesktopThumbnailerClass *thumbnailer_class = XFDESKTOP_THUMBNAILER_CLASS ( + object_class); + + parent_class = g_type_class_peek_parent (thumbnailer_class); + + object_class->dispose = xfdesktop_thumbnailer_dispose; + object_class->finalize = xfdesktop_thumbnailer_finalize; + + thumbnailer_signals[THUMBNAIL_READY] = g_signal_new ( + "thumbnail-ready", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(XfdesktopThumbnailerClass, thumbnail_ready), + NULL, NULL, + xfdesktop_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, G_TYPE_STRING); +} + +/** + * xfdesktop_thumbnailer_dispose: + * @object: + * + */ +static void +xfdesktop_thumbnailer_dispose (GObject *object) +{ + XfdesktopThumbnailer *thumbnailer = XFDESKTOP_THUMBNAILER (object); + + if(thumbnailer->priv->proxy) + g_object_unref(thumbnailer->priv->proxy); + + if(thumbnailer->priv->supported_mimetypes) + g_free(thumbnailer->priv->supported_mimetypes); + + if (thumbnailer->priv) { + g_free (thumbnailer->priv); + thumbnailer->priv = NULL; + } + + thumbnailer_object = NULL; +} + +/** + * xfdesktop_thumbnailer_finalize: + * @object: + * + */ +static void +xfdesktop_thumbnailer_finalize (GObject *object) +{ +} + + + +/** + * xfdesktop_thumbnailer_new: + * + * + * Singleton + */ +XfdesktopThumbnailer * +xfdesktop_thumbnailer_new (void) +{ + if (thumbnailer_object == NULL) { + thumbnailer_object = g_object_new (XFDESKTOP_TYPE_THUMBNAILER, NULL); + } else { + g_object_ref (thumbnailer_object); + } + + return thumbnailer_object; +} + +static gchar * +xfdesktop_get_file_mimetype(gchar *file) +{ + GFile *temp_file; + GFileInfo *file_info; + gchar *mime_type = NULL; + + g_return_val_if_fail (file != NULL, NULL); + + temp_file = g_file_new_for_path (file); + file_info = g_file_query_info (temp_file, + "standard::content-type", + 0, + NULL, + NULL); + + mime_type = g_strdup(g_file_info_get_content_type (file_info)); + + g_object_unref (file_info); + g_object_unref (temp_file); + + return mime_type; +} + +gboolean +xfdesktop_thumbnailer_is_supported (XfdesktopThumbnailer *thumbnailer, + gchar *file) +{ + guint n; + gchar *mime_type = NULL; + + g_return_val_if_fail (XFDESKTOP_IS_THUMBNAILER (thumbnailer), FALSE); + g_return_val_if_fail (file != NULL, FALSE); + + mime_type = xfdesktop_get_file_mimetype (file); + + if (!mime_type) { + g_warning ("File has no mime type"); + return FALSE; + } + + if(thumbnailer->priv->service == XFDESKTOP_GTK_SERVICE) { + /* This is a hack because I don't want to test and hard code + * every mime type gdk supports. */ + gboolean ret = g_str_has_prefix(mime_type, "image/"); + + g_free(mime_type); + return ret; + } else if (thumbnailer->priv->supported_mimetypes != NULL) { + for (n = 0; thumbnailer->priv->supported_mimetypes[n] != NULL; ++n) { + if (g_content_type_is_a (mime_type, thumbnailer->priv->supported_mimetypes[n])) { + g_free(mime_type); + return TRUE; + } + } + } + + g_free(mime_type); + return FALSE; +} + +gboolean +xfdesktop_thumbnailer_queue_thumbnail ( + XfdesktopThumbnailer *thumbnailer, + gchar *file) +{ + g_return_val_if_fail (XFDESKTOP_IS_THUMBNAILER (thumbnailer), FALSE); + g_return_val_if_fail (file != NULL, FALSE); + + if(!xfdesktop_thumbnailer_is_supported(thumbnailer, file)) { + g_debug("file: %s not supported", file); + return FALSE; + } + if (thumbnailer->priv->request_timer_id) { + g_source_remove (thumbnailer->priv->request_timer_id); + + if (thumbnailer->priv->handle) { + if(thumbnailer->priv->service == XFDESKTOP_THUMBNAILER_SERVICE) { + if(dbus_g_proxy_call(thumbnailer->priv->proxy, + "Dequeue", + NULL, + G_TYPE_UINT, thumbnailer->priv->handle, + G_TYPE_INVALID) == FALSE) + { + g_warning("Dequeue of thumbnailer->priv->handle: %d failed", thumbnailer->priv->handle); + } + } + thumbnailer->priv->handle = 0; + } + } + + if (g_slist_find (thumbnailer->priv->queue, file) == NULL) { + thumbnailer->priv->queue = g_slist_prepend (thumbnailer->priv->queue, + file); + } + + thumbnailer->priv->request_timer_id = g_timeout_add_full ( + G_PRIORITY_LOW, + 300, + (GSourceFunc)xfdesktop_thumbnailer_queue_request_timer, + thumbnailer, + NULL); + + + return TRUE; +} + +void +xfdesktop_thumbnailer_dequeue_thumbnail ( + XfdesktopThumbnailer *thumbnailer, + gchar *file) +{ + g_return_if_fail ( XFDESKTOP_IS_THUMBNAILER (thumbnailer) ); + g_return_if_fail ( file != NULL ); + + if (thumbnailer->priv->request_timer_id) { + g_source_remove (thumbnailer->priv->request_timer_id); + + if (thumbnailer->priv->handle) { + if(thumbnailer->priv->service == XFDESKTOP_THUMBNAILER_SERVICE) { + if(dbus_g_proxy_call(thumbnailer->priv->proxy, + "Dequeue", + NULL, + G_TYPE_UINT, thumbnailer->priv->handle, + G_TYPE_INVALID) == FALSE) + { + g_warning("Dequeue of thumbnailer->priv->handle: %d failed", thumbnailer->priv->handle); + } + } + } + thumbnailer->priv->handle = 0; + } + + if (g_slist_find (thumbnailer->priv->queue, file) != NULL) { + thumbnailer->priv->queue = g_slist_remove_all ( + thumbnailer->priv->queue, + file); + } + + thumbnailer->priv->request_timer_id = g_timeout_add_full ( + G_PRIORITY_LOW, + 300, + (GSourceFunc)xfdesktop_thumbnailer_queue_request_timer, + thumbnailer, + NULL); +} + +static gboolean +xfdesktop_thumbnailer_queue_request_timer ( + XfdesktopThumbnailer *thumbnailer) +{ + gchar **uris; + gchar **mimetypes; + GSList *iter; + gint i = 0; + GFile *file; + GError *error = NULL; + + g_return_val_if_fail ( XFDESKTOP_IS_THUMBNAILER (thumbnailer), FALSE); + + uris = g_new0 (gchar *, + g_slist_length(thumbnailer->priv->queue) + 1); + mimetypes = g_new0 (gchar *, + g_slist_length (thumbnailer->priv->queue) + 1); + + iter = thumbnailer->priv->queue; + while (iter) { + if (iter->data) { + file = g_file_new_for_path(iter->data); + uris[i] = g_file_get_uri (file); + mimetypes[i] = xfdesktop_get_file_mimetype (iter->data); + g_object_unref(file); + } + iter = g_slist_next(iter); + i++; + } + + if(thumbnailer->priv->service == XFDESKTOP_THUMBNAILER_SERVICE) { + if(dbus_g_proxy_call(thumbnailer->priv->proxy, + "Queue", + &error, + G_TYPE_STRV, uris, + G_TYPE_STRV, mimetypes, + G_TYPE_STRING, "normal", + G_TYPE_STRING, "default", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &thumbnailer->priv->handle, + G_TYPE_INVALID) == FALSE) + { + if (NULL != error) { + g_warning("DBUS-call failed:%s", error->message); + if ((error->domain == DBUS_GERROR) && + (error->code == DBUS_GERROR_SERVICE_UNKNOWN)) + { + /* TODO: fall back to gtk */ + } + } + } + } + + g_free(uris); + g_free(mimetypes); + + if(error) + g_error_free(error); + + thumbnailer->priv->request_timer_id = 0; + + return FALSE; +} + +static void +xfdesktop_thumbnailer_request_finished_dbus ( + DBusGProxy *proxy, + gint handle, + gpointer data) +{ + XfdesktopThumbnailer *thumbnailer = XFDESKTOP_THUMBNAILER (data); + + g_return_if_fail ( XFDESKTOP_IS_THUMBNAILER (thumbnailer) ); + + thumbnailer->priv->handle = 0; +} + +static void +xfdesktop_thumbnailer_thumbnail_ready_dbus ( + DBusGProxy *proxy, + gint handle, + const gchar **uri, + gpointer data) +{ + XfdesktopThumbnailer *thumbnailer = XFDESKTOP_THUMBNAILER (data); + gchar *thumbnail_location; + GFile *file; + GSList *iter = thumbnailer->priv->queue; + gchar *f_uri, *f_uri_checksum, *filename; + gint x = 0; + + g_return_if_fail ( XFDESKTOP_IS_THUMBNAILER (thumbnailer) ); + + while (iter) { + if ((uri[x] == NULL) || (iter->data == NULL)) { + break; + } + + file = g_file_new_for_path(iter->data); + f_uri = g_file_get_uri (file); + + if (strcmp (uri[x], f_uri) == 0) { + /* The thumbnail is in the format/location + * /homedir/.thumbnails/normal/MD5_Hash_Of_URI.png + */ + f_uri_checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, + f_uri, strlen (f_uri)); + filename = g_strconcat (f_uri_checksum, ".png", NULL); + + thumbnail_location = g_build_path ("/", g_get_home_dir(), + ".thumbnails", "normal", + filename, NULL); + + g_debug("thumbnail-ready src: %s thumbnail: %s", + (char*)iter->data, + thumbnail_location); + g_signal_emit(G_OBJECT(thumbnailer), + thumbnailer_signals[THUMBNAIL_READY], + 0, + iter->data, + thumbnail_location); + + thumbnailer->priv->queue = g_slist_remove (thumbnailer->priv->queue, + iter->data); + + iter = thumbnailer->priv->queue; + x++; + + g_free (filename); + g_free (f_uri_checksum); + } else { + iter = g_slist_next(iter); + } + + g_object_unref (file); + g_free (f_uri); + } +} + +void +xfdesktop_thumbnailer_delete_thumbnail(XfdesktopThumbnailer *thumbnailer, gchar *src_file) +{ + DBusGConnection *connection; + gchar **uris; + GFile *file; + GError *error = NULL; + static DBusGProxy *cache = NULL; + + if(!cache) { + connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (connection != NULL) { + cache = dbus_g_proxy_new_for_name(connection, + "org.freedesktop.thumbnails.Cache1", + "/org/freedesktop/thumbnails/Cache1", + "org.freedesktop.thumbnails.Cache1"); + + dbus_g_connection_unref (connection); + } + } + + file = g_file_new_for_path(src_file); + + if(cache) { + uris = g_new0 (gchar *, 2); + uris[0] = g_file_get_uri(file); + dbus_g_proxy_call(cache, "Delete", &error, G_TYPE_STRV, uris, G_TYPE_INVALID, G_TYPE_INVALID); + if(error != NULL) { + g_warning("DBUS-call failed:%s", error->message); + } + g_free(uris); + } + + g_object_unref(file); + if(error) + g_error_free(error); +} diff --git a/common/xfdesktop-thumbnailer.h b/common/xfdesktop-thumbnailer.h new file mode 100644 index 0000000..fc16bdb --- /dev/null +++ b/common/xfdesktop-thumbnailer.h @@ -0,0 +1,101 @@ +/* + * xfdesktop - xfce4's desktop manager + * + * Copyright(c) 2006 Brian Tarricone, + * Copyright(c) 2006 Benedikt Meurer, + * Copyright(c) 2010-2011 Jannis Pohlmann, + * + * This program 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. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * xfdesktop-thumbnailer is based on thumbnailer code from Ristretto + * Copyright (c) Stephan Arts 2009-2011 + */ + +#ifndef __XFDESKTOP_THUMBNAILER_H__ +#define __XFDESKTOP_THUMBNAILER_H__ + +#include + +G_BEGIN_DECLS + + +#define XFDESKTOP_TYPE_THUMBNAILER (xfdesktop_thumbnailer_get_type()) +#define XFDESKTOP_THUMBNAILER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDESKTOP_TYPE_THUMBNAILER, XfdesktopThumbnailer)) +#define XFDESKTOP_IS_THUMBNAILER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDESKTOP_TYPE_THUMBNAILER)) +#define XFDESKTOP_THUMBNAILER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XFDESKTOP_TYPE_THUMBNAILER, XfdesktopThumbnailerClass)) +#define XFDESKTOP_IS_THUMBNAILER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFDESKTOP_TYPE_THUMBNAILER())) + +typedef struct _XfdesktopThumbnailer XfdesktopThumbnailer; +typedef struct _XfdesktopThumbnailerPriv XfdesktopThumbnailerPriv; + +struct _XfdesktopThumbnailer +{ + GObject parent; + + XfdesktopThumbnailerPriv *priv; +}; + +typedef struct _XfdesktopThumbnailerClass XfdesktopThumbnailerClass; + +struct _XfdesktopThumbnailerClass +{ + GObjectClass parent_class; + + /*< signals >*/ + void (*thumbnail_ready)(gchar *src_file, gchar *thumb_file); +}; + +XfdesktopThumbnailer * +xfdesktop_thumbnailer_new (void); + +GType +xfdesktop_thumbnailer_get_type (void); + +gboolean +xfdesktop_thumbnailer_is_supported (XfdesktopThumbnailer *thumbnailer, + gchar *file); + +/* Queues a file for thumbnail creation. + * A "thumbnail-ready" signal will be emitted when the thumbnail is ready. + * The signal will pass 2 parameters: a gchar *file which will be file + * that's passed in here and a gchar *thumbnail_file which will be the + * location of the thumbnail. + */ +gboolean +xfdesktop_thumbnailer_queue_thumbnail ( + XfdesktopThumbnailer *thumbnailer, + gchar *file); + +/* Removes a file from the list of pending thumbnail creations. + * This is not guaranteed to always remove the file, if processing + * of that thumbnail has started it won't stop. + */ +void +xfdesktop_thumbnailer_dequeue_thumbnail ( + XfdesktopThumbnailer *thumbnailer, + gchar *file); + +/* This function tells the thumbnail service the src_file will be deleted. + * This function should be called when the file is deleted or moved so + * the thumbnail file doesn't take up space on the user's drive. + */ +void +xfdesktop_thumbnailer_delete_thumbnail ( + XfdesktopThumbnailer *thumbnailer, + gchar *src_file); + +G_END_DECLS + +#endif /* __XFDESKTOP_THUMBNAILER_H__ */ diff --git a/doc/README.xfconf b/doc/README.xfconf index 2fa1c40..3832521 100644 --- a/doc/README.xfconf +++ b/doc/README.xfconf @@ -37,6 +37,7 @@ property is listd after the name. + diff --git a/src/Makefile.am b/src/Makefile.am index 5d1fd4d..3adb3d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,9 +4,7 @@ bin_PROGRAMS = xfdesktop xfdesktop_built_sources = \ xfce-desktop-enum-types.c \ - xfce-desktop-enum-types.h \ - xfdesktop-marshal.c \ - xfdesktop-marshal.h + xfce-desktop-enum-types.h if HAVE_LIBNOTIFY xfdesktop_notify_sources = \ @@ -149,17 +147,8 @@ BUILT_SOURCES = \ DISTCLEANFILES = \ $(xfdesktop_built_sources) \ - stamp-xfdesktop-marshal.h \ stamp-xfce-desktop-enum-types.h -xfdesktop-marshal.h: stamp-xfdesktop-marshal.h - @true -stamp-xfdesktop-marshal.h: xfdesktop-marshal.list Makefile - $(AM_V_GEN) glib-genmarshal --prefix=xfdesktop_marshal xfdesktop-marshal.list --header > xfdesktop-marshal.h && \ - echo timestamp > $(@F) -xfdesktop-marshal.c: xfdesktop-marshal.list Makefile - $(AM_V_GEN) echo '#include "xfdesktop-marshal.h"' > xfdesktop-marshal.c && \ - glib-genmarshal --prefix=xfdesktop_marshal xfdesktop-marshal.list --body >> xfdesktop-marshal.c enum_headers = \ xfce-backdrop.h \ @@ -227,5 +216,4 @@ endif EXTRA_DIST = \ $(desktop_menu_sources) \ $(desktop_icon_sources) \ - $(desktop_file_icon_sources) \ - xfdesktop-marshal.list + $(desktop_file_icon_sources) diff --git a/src/xfdesktop-file-icon-manager.c b/src/xfdesktop-file-icon-manager.c index d3ca2eb..dc1bc93 100644 --- a/src/xfdesktop-file-icon-manager.c +++ b/src/xfdesktop-file-icon-manager.c @@ -71,6 +71,7 @@ #include "xfdesktop-special-file-icon.h" #include "xfdesktop-trash-proxy.h" #include "xfdesktop-volume-icon.h" +#include "xfdesktop-thumbnailer.h" #include #include @@ -82,6 +83,7 @@ #define SETTING_SHOW_HOME "/desktop-icons/file-icons/show-home" #define SETTING_SHOW_TRASH "/desktop-icons/file-icons/show-trash" #define SETTING_SHOW_REMOVABLE "/desktop-icons/file-icons/show-removable" +#define SETTING_SHOW_THUMBNAILS "/desktop-icons/show-thumbnails" enum { @@ -91,6 +93,7 @@ enum PROP_SHOW_HOME, PROP_SHOW_TRASH, PROP_SHOW_REMOVABLE, + PROP_SHOW_THUMBNAILS }; struct _XfdesktopFileIconManagerPrivate @@ -117,6 +120,7 @@ struct _XfdesktopFileIconManagerPrivate gboolean show_removable_media; gboolean show_special[XFDESKTOP_SPECIAL_FILE_ICON_TRASH+1]; + gboolean show_thumbnails; guint save_icons_id; @@ -129,6 +133,8 @@ struct _XfdesktopFileIconManagerPrivate GList *thunarx_menu_providers; GList *thunarx_properties_providers; #endif + + XfdesktopThumbnailer *thumbnailer; }; static void xfdesktop_file_icon_manager_set_property(GObject *object, @@ -175,6 +181,7 @@ static void xfdesktop_file_icon_manager_remove_removable_media(XfdesktopFileIcon static void xfdesktop_file_icon_position_changed(XfdesktopFileIcon *icon, gpointer user_data); +static void xfdesktop_file_icon_manager_update_image (GtkWidget *widget, gchar *srcfile, gchar *thumbfile, XfdesktopFileIconManager *fmanager); G_DEFINE_TYPE_EXTENDED(XfdesktopFileIconManager, xfdesktop_file_icon_manager, @@ -264,6 +271,12 @@ xfdesktop_file_icon_manager_class_init(XfdesktopFileIconManagerClass *klass) "show removable", TRUE, XFDESKTOP_PARAM_FLAGS)); + g_object_class_install_property(gobject_class, PROP_SHOW_THUMBNAILS, + g_param_spec_boolean("show-thumbnails", + "show-thumbnails", + "show-thumbnails", + TRUE, + XFDESKTOP_PARAM_FLAGS)); #undef XFDESKTOP_PARAM_FLAGS xfdesktop_app_info_quark = g_quark_from_static_string("xfdesktop-app-info-quark"); @@ -282,6 +295,10 @@ xfdesktop_file_icon_manager_init(XfdesktopFileIconManager *fmanager) n_drag_targets); fmanager->priv->drop_targets = gtk_target_list_new(drop_targets, n_drop_targets); + + fmanager->priv->thumbnailer = xfdesktop_thumbnailer_new(); + + g_signal_connect(G_OBJECT(fmanager->priv->thumbnailer), "thumbnail-ready", G_CALLBACK(xfdesktop_file_icon_manager_update_image), fmanager); } static void @@ -321,6 +338,11 @@ xfdesktop_file_icon_manager_set_property(GObject *object, g_value_get_boolean(value)); break; + case PROP_SHOW_THUMBNAILS: + xfdesktop_file_icon_manager_set_show_thumbnails(fmanager, + g_value_get_boolean(value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } @@ -357,6 +379,10 @@ xfdesktop_file_icon_manager_get_property(GObject *object, case PROP_SHOW_REMOVABLE: g_value_set_boolean(value, fmanager->priv->show_removable_media); break; + + case PROP_SHOW_THUMBNAILS: + g_value_set_boolean(value, fmanager->priv->show_thumbnails); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); @@ -377,6 +403,7 @@ xfdesktop_file_icon_manager_finalize(GObject *obj) gtk_target_list_unref(fmanager->priv->drop_targets); g_object_unref(fmanager->priv->folder); + g_object_unref(fmanager->priv->thumbnailer); G_OBJECT_CLASS(xfdesktop_file_icon_manager_parent_class)->finalize(obj); } @@ -1829,6 +1856,15 @@ xfdesktop_file_icon_manager_add_icon(XfdesktopFileIconManager *fmanager, gint16 row = -1, col = -1; gboolean do_add = FALSE; const gchar *name; + GFile *file; + + file = xfdesktop_file_icon_peek_file(icon); + + if(fmanager->priv->show_thumbnails) { + xfdesktop_thumbnailer_queue_thumbnail(fmanager->priv->thumbnailer, + g_file_get_path(file)); + } + name = xfdesktop_icon_peek_label(XFDESKTOP_ICON(icon)); if(xfdesktop_file_icon_manager_get_cached_icon_position(fmanager, name, @@ -1839,7 +1875,6 @@ xfdesktop_file_icon_manager_add_icon(XfdesktopFileIconManager *fmanager, do_add = TRUE; } else { if(defer_if_missing) { - GFile *file = xfdesktop_file_icon_peek_file(icon); fmanager->priv->deferred_icons = g_list_prepend(fmanager->priv->deferred_icons, g_object_ref(file)); } else @@ -2195,6 +2230,13 @@ xfdesktop_file_icon_manager_file_changed(GFileMonitor *monitor, icon = g_hash_table_lookup(fmanager->priv->icons, file); if(icon) { + /* Always try to remove thumbnail so it doesn't take up + * space on the user's disk. + */ + xfdesktop_thumbnailer_delete_thumbnail(fmanager->priv->thumbnailer, + g_file_get_path(file)); + xfdesktop_icon_delete_thumbnail(XFDESKTOP_ICON(icon)); + xfdesktop_icon_view_remove_item(fmanager->priv->icon_view, XFDESKTOP_ICON(icon)); g_hash_table_remove(fmanager->priv->icons, file); @@ -2996,6 +3038,8 @@ xfdesktop_file_icon_manager_new(GFile *folder, G_OBJECT(fmanager), "show-trash"); xfconf_g_property_bind(channel, SETTING_SHOW_REMOVABLE, G_TYPE_BOOLEAN, G_OBJECT(fmanager), "show-removable"); + xfconf_g_property_bind(channel, SETTING_SHOW_THUMBNAILS, G_TYPE_BOOLEAN, + G_OBJECT(fmanager), "show-thumbnails"); return XFDESKTOP_ICON_VIEW_MANAGER(fmanager); } @@ -3027,6 +3071,64 @@ xfdesktop_file_icon_manager_get_show_removable_media(XfdesktopFileIconManager *m return manager->priv->show_removable_media; } +static void +xfdesktop_file_icon_manager_requeue_thumbnails(gpointer key, + gpointer value, + gpointer data) +{ + GFile *file = key; + XfdesktopFileIconManager *fmanager = XFDESKTOP_FILE_ICON_MANAGER(data); + + xfdesktop_thumbnailer_queue_thumbnail(fmanager->priv->thumbnailer, + g_file_get_path(file)); +} + +static void +xfdesktop_file_icon_manager_remove_thumbnails(gpointer key, + gpointer value, + gpointer data) +{ + XfdesktopRegularFileIcon *icon = XFDESKTOP_REGULAR_FILE_ICON(value); + + xfdesktop_icon_delete_thumbnail(XFDESKTOP_ICON(icon)); +} + +void +xfdesktop_file_icon_manager_set_show_thumbnails(XfdesktopFileIconManager *manager, + gboolean show_thumbnails) +{ + g_return_if_fail(XFDESKTOP_IS_FILE_ICON_MANAGER(manager)); + + if(show_thumbnails == manager->priv->show_thumbnails) + return; + + manager->priv->show_thumbnails = show_thumbnails; + + if(!manager->priv->inited) + return; + + if(show_thumbnails) { + /* We have to request to create the thumbnails everytime. */ + g_hash_table_foreach(manager->priv->icons, + xfdesktop_file_icon_manager_requeue_thumbnails, + manager); + } else { + /* We have to remove the thumbnails because the regular file + * icons can't check if thumbnails are allowed. + */ + g_hash_table_foreach(manager->priv->icons, + xfdesktop_file_icon_manager_remove_thumbnails, + manager); + } +} + +gboolean +xfdesktop_file_icon_manager_get_show_thumbnails(XfdesktopFileIconManager *manager) +{ + g_return_val_if_fail(XFDESKTOP_IS_FILE_ICON_MANAGER(manager), FALSE); + return manager->priv->show_thumbnails; +} + void xfdesktop_file_icon_manager_set_show_special_file(XfdesktopFileIconManager *manager, XfdesktopSpecialFileIconType type, @@ -3068,3 +3170,23 @@ xfdesktop_file_icon_manager_get_show_special_file(XfdesktopFileIconManager *mana return manager->priv->show_special[type]; } + +static void +xfdesktop_file_icon_manager_update_image (GtkWidget *widget, gchar *srcfile, gchar *thumbfile, XfdesktopFileIconManager *manager) +{ + GFile *file; + XfdesktopIcon *icon; + + g_return_if_fail(srcfile && thumbfile); + g_return_if_fail(XFDESKTOP_FILE_ICON_MANAGER(manager)); + + file = g_file_new_for_path(srcfile); + + icon = g_hash_table_lookup(manager->priv->icons, file); + if(icon) + { + g_object_unref(file); + file = g_file_new_for_path(thumbfile); + xfdesktop_icon_set_thumbnail_file(icon, file); + } +} diff --git a/src/xfdesktop-file-icon-manager.h b/src/xfdesktop-file-icon-manager.h index 703a6f4..1c45ecb 100644 --- a/src/xfdesktop-file-icon-manager.h +++ b/src/xfdesktop-file-icon-manager.h @@ -64,6 +64,10 @@ void xfdesktop_file_icon_manager_set_show_special_file(XfdesktopFileIconManager gboolean show_special_file); gboolean xfdesktop_file_icon_manager_get_show_special_file(XfdesktopFileIconManager *manager, XfdesktopSpecialFileIconType type); +void xfdesktop_file_icon_manager_set_show_thumbnails(XfdesktopFileIconManager *manager, + gboolean show_thumbnails); +gboolean xfdesktop_file_icon_manager_get_show_thumbnails(XfdesktopFileIconManager *manager); + G_END_DECLS diff --git a/src/xfdesktop-icon-view.c b/src/xfdesktop-icon-view.c index 0736006..3ea5fcb 100644 --- a/src/xfdesktop-icon-view.c +++ b/src/xfdesktop-icon-view.c @@ -927,6 +927,7 @@ xfdesktop_icon_view_show_tooltip(GtkWidget *widget, if(!tip_text) return FALSE; + gtk_tooltip_set_icon(tooltip, xfdesktop_icon_peek_pixbuf(icon_view->priv->item_under_pointer, 64)); gtk_tooltip_set_text(tooltip, tip_text); return TRUE; diff --git a/src/xfdesktop-icon.c b/src/xfdesktop-icon.c index 431ba79..31c082b 100644 --- a/src/xfdesktop-icon.c +++ b/src/xfdesktop-icon.c @@ -283,6 +283,37 @@ xfdesktop_icon_peek_tooltip(XfdesktopIcon *icon) } /*< optional >*/ +void xfdesktop_icon_delete_thumbnail(XfdesktopIcon *icon) +{ + XfdesktopIconClass *klass; + + g_return_if_fail(XFDESKTOP_IS_ICON(icon)); + + klass = XFDESKTOP_ICON_GET_CLASS(icon); + + if(!klass->delete_thumbnail_file) + return; + + klass->delete_thumbnail_file(icon); +} + +/*< optional >*/ +void +xfdesktop_icon_set_thumbnail_file(XfdesktopIcon *icon, GFile *file) +{ + XfdesktopIconClass *klass; + + g_return_if_fail(XFDESKTOP_IS_ICON(icon)); + + klass = XFDESKTOP_ICON_GET_CLASS(icon); + + if(!klass->set_thumbnail_file) + return; + + klass->set_thumbnail_file(icon, file); +} + +/*< optional >*/ gboolean xfdesktop_icon_populate_context_menu(XfdesktopIcon *icon, GtkWidget *menu) diff --git a/src/xfdesktop-icon.h b/src/xfdesktop-icon.h index b838ed5..36e4925 100644 --- a/src/xfdesktop-icon.h +++ b/src/xfdesktop-icon.h @@ -73,6 +73,9 @@ struct _XfdesktopIconClass G_CONST_RETURN gchar *(*peek_tooltip)(XfdesktopIcon *icon); + void (*set_thumbnail_file)(XfdesktopIcon *icon, GFile *file); + void (*delete_thumbnail_file)(XfdesktopIcon *icon); + gboolean (*populate_context_menu)(XfdesktopIcon *icon, GtkWidget *menu); }; @@ -105,6 +108,9 @@ gboolean xfdesktop_icon_populate_context_menu(XfdesktopIcon *icon, GtkWidget *xfdesktop_icon_peek_icon_view(XfdesktopIcon *icon); +void xfdesktop_icon_set_thumbnail_file(XfdesktopIcon *icon, GFile *file); +void xfdesktop_icon_delete_thumbnail(XfdesktopIcon *icon); + /*< signal triggers >*/ void xfdesktop_icon_pixbuf_changed(XfdesktopIcon *icon); diff --git a/src/xfdesktop-marshal.list b/src/xfdesktop-marshal.list deleted file mode 100644 index 819c32e..0000000 --- a/src/xfdesktop-marshal.list +++ /dev/null @@ -1,2 +0,0 @@ -BOOLEAN:VOID -BOOLEAN:ENUM,INT diff --git a/src/xfdesktop-regular-file-icon.c b/src/xfdesktop-regular-file-icon.c index e14256a..742a197 100644 --- a/src/xfdesktop-regular-file-icon.c +++ b/src/xfdesktop-regular-file-icon.c @@ -68,11 +68,15 @@ struct _XfdesktopRegularFileIconPrivate GFileInfo *file_info; GFileInfo *filesystem_info; GFile *file; + GFile *thumbnail_file; GdkScreen *gscreen; }; static void xfdesktop_regular_file_icon_finalize(GObject *obj); +static void xfdesktop_regular_file_icon_set_thumbnail_file(XfdesktopIcon *icon, GFile *file); +static void xfdesktop_regular_file_icon_delete_thumbnail_file(XfdesktopIcon *icon); + static GdkPixbuf *xfdesktop_regular_file_icon_peek_pixbuf(XfdesktopIcon *icon, gint size); static G_CONST_RETURN gchar *xfdesktop_regular_file_icon_peek_label(XfdesktopIcon *icon); @@ -127,6 +131,8 @@ xfdesktop_regular_file_icon_class_init(XfdesktopRegularFileIconClass *klass) icon_class->get_allowed_drag_actions = xfdesktop_regular_file_icon_get_allowed_drag_actions; icon_class->get_allowed_drop_actions = xfdesktop_regular_file_icon_get_allowed_drop_actions; icon_class->do_drop_dest = xfdesktop_regular_file_icon_do_drop_dest; + icon_class->set_thumbnail_file = xfdesktop_regular_file_icon_set_thumbnail_file; + icon_class->delete_thumbnail_file = xfdesktop_regular_file_icon_delete_thumbnail_file; file_icon_class->peek_file_info = xfdesktop_regular_file_icon_peek_file_info; file_icon_class->peek_filesystem_info = xfdesktop_regular_file_icon_peek_filesystem_info; @@ -171,6 +177,9 @@ xfdesktop_regular_file_icon_finalize(GObject *obj) if(icon->priv->tooltip) g_free(icon->priv->tooltip); + + if(icon->priv->thumbnail_file) + g_object_unref(icon->priv->thumbnail_file); G_OBJECT_CLASS(xfdesktop_regular_file_icon_parent_class)->finalize(obj); } @@ -201,6 +210,43 @@ xfdesktop_regular_file_icon_invalidate_pixbuf(XfdesktopRegularFileIcon *icon) } } +static void +xfdesktop_regular_file_icon_delete_thumbnail_file(XfdesktopIcon *icon) +{ + XfdesktopRegularFileIcon *file_icon; + + if(!XFDESKTOP_IS_REGULAR_FILE_ICON(icon)) + return; + + file_icon = XFDESKTOP_REGULAR_FILE_ICON(icon); + + if(file_icon->priv->thumbnail_file) { + g_object_unref(file_icon->priv->thumbnail_file); + file_icon->priv->thumbnail_file = NULL; + } + + xfdesktop_regular_file_icon_invalidate_pixbuf(file_icon); + xfdesktop_icon_pixbuf_changed(XFDESKTOP_ICON(icon)); +} + +static void +xfdesktop_regular_file_icon_set_thumbnail_file(XfdesktopIcon *icon, GFile *file) +{ + XfdesktopRegularFileIcon *file_icon; + + if(!XFDESKTOP_IS_REGULAR_FILE_ICON(icon)) + return; + + file_icon = XFDESKTOP_REGULAR_FILE_ICON(icon); + + if(file_icon->priv->thumbnail_file) + g_object_unref(file_icon->priv->thumbnail_file); + + file_icon->priv->thumbnail_file = file; + + xfdesktop_regular_file_icon_invalidate_pixbuf(file_icon); + xfdesktop_icon_pixbuf_changed(XFDESKTOP_ICON(icon)); +} static GdkPixbuf * xfdesktop_regular_file_icon_peek_pixbuf(XfdesktopIcon *icon, @@ -224,6 +270,10 @@ xfdesktop_regular_file_icon_peek_pixbuf(XfdesktopIcon *icon, if(g_file_has_prefix(file_icon->priv->file, thumbnail_dir)) { /* use the filename as custom icon name for thumbnails */ icon_name = g_file_get_path(file_icon->priv->file); + + /* release thumbnail path */ + g_object_unref(thumbnail_dir); + g_free(thumbnail_dir_path); } else if(xfdesktop_file_utils_is_desktop_file(file_icon->priv->file_info)) { gchar *contents; gsize length; @@ -248,12 +298,16 @@ xfdesktop_regular_file_icon_peek_pixbuf(XfdesktopIcon *icon, g_key_file_free(key_file); g_free(contents); } + } else { + /* If we have a thumbnail then they are enabled, use it. */ + if(file_icon->priv->thumbnail_file) + { + file_icon->priv->pix = gdk_pixbuf_new_from_file_at_scale(g_file_get_path(file_icon->priv->thumbnail_file), + size, size, + FALSE, NULL); + } } - /* release thumbnail path */ - g_object_unref(thumbnail_dir); - g_free(thumbnail_dir_path); - /* load the symlink emblem if necessary */ if(g_file_info_get_attribute_boolean(file_icon->priv->file_info, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK)) @@ -280,10 +334,24 @@ xfdesktop_regular_file_icon_peek_pixbuf(XfdesktopIcon *icon, if(file_icon->priv->file_info) gicon = g_file_info_get_icon(file_icon->priv->file_info); - + + if(file_icon->priv->pix) { + if(emblem_pix) { + gint emblem_pix_size = gdk_pixbuf_get_width(emblem_pix); + gint dest_size = size - emblem_pix_size; + + /* We have to add the emblem */ + gdk_pixbuf_composite(emblem_pix, file_icon->priv->pix, + dest_size, dest_size, + emblem_pix_size, emblem_pix_size, + dest_size, dest_size, + 1.0, 1.0, GDK_INTERP_BILINEAR, 255); + } + } else { file_icon->priv->pix = xfdesktop_file_utils_get_icon(icon_name, gicon, size, emblem_pix, file_icon->priv->pix_opacity); + } file_icon->priv->cur_pix_size = size; -- 1.7.5.4