[Xfce4-commits] <xfdesktop:eric/bugzilla-patches> Thumbnail desktop icon previews using tumblerd

Eric Koegel noreply at xfce.org
Sun Feb 12 14:32:04 CET 2012


Updating branch refs/heads/eric/bugzilla-patches
         to d5e4dbea733153b74caba0e7a9c2bccc2ea5bf9c (commit)
       from 803ea66061417317df2b2968a81a4cfdfd052e8d (commit)

commit d5e4dbea733153b74caba0e7a9c2bccc2ea5bf9c
Author: Eric Koegel <eric.koegel at gmail.com>
Date:   Fri Feb 10 19:56:59 2012 +0300

    Thumbnail desktop icon previews using tumblerd
    
    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 the common folder. Creates
    a checkbox option on the xfdesktop-settings app to toggle
    thumbnails. Adds a tooltip-size gtk style property to change the
    tooltip image size, ranges from 0 (not shown) to 512, with 128
    as the default. Added a timer when the icon size spinner changed
    value in the settings app to prevent the icons redrawing on every
    value change. Removed xfdesktop_icon_mark_extents_dirty so that
    size changes to the pixbuf are properly rendered. Bug 4344.

 README                               |    8 +-
 common/Makefile.am                   |   37 ++-
 common/xfdesktop-marshal.list        |    4 +
 common/xfdesktop-thumbnailer.c       |  606 ++++++++++++++++++++++++++++++++++
 common/xfdesktop-thumbnailer.h       |   76 +++++
 doc/README.xfconf                    |    1 +
 settings/main.c                      |   60 +++-
 settings/xfdesktop-settings-ui.glade |   11 +
 src/Makefile.am                      |   16 +-
 src/xfdesktop-file-icon-manager.c    |  130 +++++++-
 src/xfdesktop-file-icon-manager.h    |    4 +
 src/xfdesktop-icon-view.c            |   54 ++--
 src/xfdesktop-icon.c                 |   51 ++--
 src/xfdesktop-icon.h                 |    7 +-
 src/xfdesktop-marshal.list           |    2 -
 src/xfdesktop-regular-file-icon.c    |   84 +++++-
 16 files changed, 1070 insertions(+), 81 deletions(-)

diff --git a/README b/README
index e8aad4b..ad68c54 100644
--- a/README
+++ b/README
@@ -41,6 +41,7 @@ style "xfdesktop-icon-view" {
     XfdesktopIconView::label-alpha = 75
     XfdesktopIconView::selected-label-alpha = 100
     XfdesktopIconVIew::ellipsize-icon-labels = 1
+    XfdesktopIconView::tooltip-size = 128
 
     XfdesktopIconView::shadow-x-offset = 1
     XfdesktopIconView::shadow-y-offset = 1
@@ -63,11 +64,14 @@ style "xfdesktop-icon-view" {
 }
 widget_class "*XfdesktopIconView*" style "xfdesktop-icon-view"
 
-The first three entries set the opacity of the rounded text background
+The first four entries set the opacity of the rounded text background
 (allowed values are from 0 (totally transparent) to 255 (totally opaque),
 and whether or not unselected icons get their labels ellipsized
 (truncated) to fit on one line.  (The 'selected-' version controls the
-opacity of icons that have been selected with the mouse.)
+opacity of icons that have been selected with the mouse.) The tooltip-size
+determines how large the image preview will be when the mouse is hovered
+over an icon (allowed values are from 0 (not shown) to 512, values larger
+than 256 may cause poor image quality however.)
 
 The second six entries can be used to enable a text shadow to be painted
 with the icon labels.  The offsets are in pixels.  Setting them to 0 (the
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..3cc157b
--- /dev/null
+++ b/common/xfdesktop-thumbnailer.c
@@ -0,0 +1,606 @@
+/*
+ *  xfdesktop - xfce4's desktop manager
+ *
+ *  Copyright(c) 2006 Brian Tarricone, <bjt23 at cornell.edu>
+ *  Copyright(c) 2006 Benedikt Meurer, <benny at xfce.org>
+ *  Copyright(c) 2010-2011 Jannis Pohlmann, <jannis at xfce.org>
+ *
+ *  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 <stephan at xfce.org>
+ *
+ *  Thumbnailer Spec
+ *  http://live.gnome.org/ThumbnailerSpec
+ *  Thumbnail Managing Standard
+ *  http://people.freedesktop.org/~vuntz/thumbnail-spec-cache/creation.html
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include <dbus/dbus-glib.h>
+
+#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;
+
+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;
+    gboolean                  big_thumbnails;
+    gint                      handle;
+
+    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;
+            gchar **supported_flavors;
+
+            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);
+
+            dbus_g_proxy_call(thumbnailer->priv->proxy, "GetFlavors", NULL, G_TYPE_INVALID,
+                              G_TYPE_STRV, &supported_flavors,
+                              G_TYPE_INVALID);
+
+            if(supported_flavors != NULL) {
+                gint n;
+                for(n = 0; supported_flavors[n] != NULL; ++n) {
+                    g_debug("flavor: %s", supported_flavors[n]);
+                    if(g_strcmp0(supported_flavors[n], "large")) {
+                        thumbnailer->priv->big_thumbnails = TRUE;
+                    }
+                }
+            } else {
+                thumbnailer->priv->big_thumbnails = FALSE;
+                g_debug("Thumbnailer failed to Get Flavors");
+            }
+
+            g_strfreev(supported_flavors);
+            g_strfreev(supported_uris);
+        }
+
+        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);
+
+    g_return_val_if_fail(temp_file != NULL, NULL);
+
+    file_info = g_file_query_info(temp_file,
+                                  "standard::content-type",
+                                  0,
+                                  NULL,
+                                  NULL);
+
+    if(file_info != 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 == NULL) {
+        g_warning("File has no mime type");
+        return FALSE;
+    }
+
+    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;
+}
+
+/**
+ * xfdesktop_thumbnailer_queue_thumbnail:
+ *
+ * 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)
+{
+    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 && thumbnailer->priv->proxy != NULL) {
+            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;
+}
+
+/**
+ * xfdesktop_thumbnailer_dequeue_thumbnail:
+ * 
+ * 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)
+{
+    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 && thumbnailer->priv->proxy) {
+            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;
+    gchar *thumbnail_flavor;
+
+    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->big_thumbnails == TRUE)
+        thumbnail_flavor = "large";
+    else
+        thumbnail_flavor = "normal";
+
+    if(thumbnailer->priv->proxy != NULL) {
+        if(dbus_g_proxy_call(thumbnailer->priv->proxy,
+                             "Queue",
+                             &error,
+                             G_TYPE_STRV, uris,
+                             G_TYPE_STRV, mimetypes,
+                             G_TYPE_STRING, thumbnail_flavor,
+                             G_TYPE_STRING, "default",
+                             G_TYPE_UINT, 0,
+                             G_TYPE_INVALID,
+                             G_TYPE_UINT, &thumbnailer->priv->handle,
+                             G_TYPE_INVALID) == FALSE)
+        {
+            if(error != NULL)
+                g_warning("DBUS-call failed: %s", error->message);
+        }
+    }
+
+    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;
+    gchar *thumbnail_flavor;
+    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|large)/MD5_Hash_Of_URI.png
+             */
+            f_uri_checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5,
+                                                           f_uri, strlen (f_uri));
+
+            if(thumbnailer->priv->big_thumbnails == TRUE)
+                thumbnail_flavor = "large";
+            else
+                thumbnail_flavor = "normal";
+
+            filename = g_strconcat(f_uri_checksum, ".png", NULL);
+
+            thumbnail_location = g_build_path("/", g_get_home_dir(),
+                                              ".thumbnails", thumbnail_flavor,
+                                              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);
+    }
+}
+
+/**
+ * xfdesktop_thumbnailer_delete_thumbnail:
+ * 
+ * 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)
+{
+    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..842f155
--- /dev/null
+++ b/common/xfdesktop-thumbnailer.h
@@ -0,0 +1,76 @@
+/*
+ *  xfdesktop - xfce4's desktop manager
+ *
+ *  Copyright(c) 2006 Brian Tarricone, <bjt23 at cornell.edu>
+ *  Copyright(c) 2006 Benedikt Meurer, <benny at xfce.org>
+ *  Copyright(c) 2010-2011 Jannis Pohlmann, <jannis at xfce.org>
+ *
+ *  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 <stephan at xfce.org>
+ */
+
+#ifndef __XFDESKTOP_THUMBNAILER_H__
+#define __XFDESKTOP_THUMBNAILER_H__
+
+#include <glib-object.h>
+
+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);
+
+gboolean xfdesktop_thumbnailer_queue_thumbnail(XfdesktopThumbnailer *thumbnailer,
+                                               gchar *file);
+void xfdesktop_thumbnailer_dequeue_thumbnail(XfdesktopThumbnailer *thumbnailer,
+                                             gchar *file);
+
+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.
     <use-custom-font-size bool>
     <font-size uint>
     <icon-size uint>
+    <show-thumbnails bool>
     <file-icons>
         <show-filesystem bool>
         <show-home bool>
diff --git a/settings/main.c b/settings/main.c
index 1f76be9..a6be3b8 100644
--- a/settings/main.c
+++ b/settings/main.c
@@ -70,6 +70,7 @@
 #define DESKTOP_ICONS_ICON_SIZE_PROP         "/desktop-icons/icon-size"
 #define DESKTOP_ICONS_FONT_SIZE_PROP         "/desktop-icons/font-size"
 #define DESKTOP_ICONS_CUSTOM_FONT_SIZE_PROP  "/desktop-icons/use-custom-font-size"
+#define DESKTOP_ICONS_SHOW_THUMBNAILS_PROP   "/desktop-icons/show-thumbnails"
 #define DESKTOP_ICONS_SHOW_HOME              "/desktop-icons/file-icons/show-home"
 #define DESKTOP_ICONS_SHOW_TRASH             "/desktop-icons/file-icons/show-trash"
 #define DESKTOP_ICONS_SHOW_FILESYSTEM        "/desktop-icons/file-icons/show-filesystem"
@@ -758,6 +759,41 @@ cb_xfdesktop_chk_custom_font_size_toggled(GtkCheckButton *button,
 }
 
 static gboolean
+xfdesktop_spin_icon_size_timer(GtkSpinButton *button)
+{
+    XfconfChannel *channel = g_object_get_data(G_OBJECT(button), "xfconf-chanel");
+
+    g_return_val_if_fail(XFCONF_IS_CHANNEL(channel), FALSE);
+
+    xfconf_channel_set_uint(channel,
+                            DESKTOP_ICONS_ICON_SIZE_PROP,
+                            gtk_spin_button_get_value(button));
+
+    return FALSE;
+}
+
+static void
+cb_xfdesktop_spin_icon_size_changed(GtkSpinButton *button,
+                                    gpointer user_data)
+{
+    guint timer_id = 0;
+
+    g_object_set_data(G_OBJECT(button), "xfconf-chanel", user_data);
+
+    timer_id = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(button), "timer-id"));
+    if(timer_id != 0) {
+        g_source_remove(timer_id);
+        timer_id = 0;
+    }
+
+    timer_id = g_timeout_add(2000,
+                             (GSourceFunc)xfdesktop_spin_icon_size_timer,
+                             button);
+
+    g_object_set_data(G_OBJECT(button), "timer-id", GUINT_TO_POINTER(timer_id));
+}
+
+static gboolean
 xfdesktop_settings_save_backdrop_list(AppearancePanel *panel,
                                       GtkTreeModel *model)
 {
@@ -1225,11 +1261,23 @@ xfdesktop_settings_dialog_add_screens(GtkBuilder *main_gxml,
 {
     gint i, j, nmonitors, nscreens;
     GtkWidget *appearance_container, *chk_custom_font_size,
-              *spin_font_size, *color_style_widget, *w, *box;
+              *spin_font_size, *color_style_widget, *w, *box,
+              *spin_icon_size, *chk_show_thumbnails;
 
     appearance_container = GTK_WIDGET(gtk_builder_get_object(main_gxml,
                                                              "notebook_screens"));
 
+    spin_icon_size = GTK_WIDGET(gtk_builder_get_object(main_gxml, "spin_icon_size"));
+
+    g_signal_connect(G_OBJECT(spin_icon_size), "value-changed",
+                     G_CALLBACK(cb_xfdesktop_spin_icon_size_changed),
+                     channel);
+
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_icon_size),
+                              xfconf_channel_get_uint(channel,
+                                                      DESKTOP_ICONS_ICON_SIZE_PROP,
+                                                      DEFAULT_ICON_SIZE));
+
     chk_custom_font_size = GTK_WIDGET(gtk_builder_get_object(main_gxml,
                                                              "chk_custom_font_size"));
     spin_font_size = GTK_WIDGET(gtk_builder_get_object(main_gxml, "spin_font_size"));
@@ -1238,6 +1286,9 @@ xfdesktop_settings_dialog_add_screens(GtkBuilder *main_gxml,
                      G_CALLBACK(cb_xfdesktop_chk_custom_font_size_toggled),
                      spin_font_size);
 
+    chk_show_thumbnails = GTK_WIDGET(gtk_builder_get_object(main_gxml,
+                                                            "chk_show_thumbnails"));
+
     nscreens = gdk_display_get_n_screens(gdk_display_get_default());
 
     for(i = 0; i < nscreens; ++i) {
@@ -1477,16 +1528,15 @@ xfdesktop_settings_dialog_add_screens(GtkBuilder *main_gxml,
 #endif
     xfconf_g_property_bind(channel, DESKTOP_ICONS_STYLE_PROP, G_TYPE_INT,
                            G_OBJECT(w), "active");
-    xfconf_g_property_bind(channel, DESKTOP_ICONS_ICON_SIZE_PROP, G_TYPE_UINT,
-                           G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(gtk_builder_get_object(main_gxml,
-                                                                                                          "spin_icon_size")))),
-                           "value");
     xfconf_g_property_bind(channel, DESKTOP_ICONS_FONT_SIZE_PROP, G_TYPE_DOUBLE,
                            G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin_font_size))),
                            "value");
     xfconf_g_property_bind(channel, DESKTOP_ICONS_CUSTOM_FONT_SIZE_PROP,
                            G_TYPE_BOOLEAN, G_OBJECT(chk_custom_font_size),
                            "active");
+    xfconf_g_property_bind(channel, DESKTOP_ICONS_SHOW_THUMBNAILS_PROP,
+                           G_TYPE_BOOLEAN, G_OBJECT(chk_show_thumbnails),
+                           "active");
 
     setup_special_icon_list(main_gxml, channel);
 }
diff --git a/settings/xfdesktop-settings-ui.glade b/settings/xfdesktop-settings-ui.glade
index 0ae289c..5fcc836 100644
--- a/settings/xfdesktop-settings-ui.glade
+++ b/settings/xfdesktop-settings-ui.glade
@@ -685,6 +685,17 @@
                                   </packing>
                                 </child>
                                 <child>
+                                      <object class="GtkCheckButton" id="chk_show_thumbnails">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="label" translatable="yes">Show t_humbnails</property>
+                                        <property name="tooltip-text" translatable="yes">Select this option to display preview-able files on the desktop as automatically generated thumbnail icons.</property>
+                                        <property name="use_underline">True</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                </child>
+                                <child>
                                   <object class="GtkHBox" id="hbox9">
                                     <property name="visible">True</property>
                                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
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 8b82fdc..5a897a1 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 <libxfce4util/libxfce4util.h>
 #include <libxfce4ui/libxfce4ui.h>
@@ -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,10 @@ 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 +274,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 +298,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 +341,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 +382,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 +406,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 +1859,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 && g_file_get_path(file) != NULL) {
+        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 +1878,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 +2233,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);
@@ -3136,6 +3181,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);
 }
@@ -3167,6 +3214,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 easily 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,
@@ -3208,3 +3313,26 @@ 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 e1c918a..fb18856 100644
--- a/src/xfdesktop-icon-view.c
+++ b/src/xfdesktop-icon-view.c
@@ -169,6 +169,7 @@ struct _XfdesktopIconViewPrivate
     gdouble cell_text_width_proportion;
 
     gboolean ellipsize_icon_labels;
+    guint    tooltip_size;
 };
 
 static gboolean xfdesktop_icon_view_button_press(GtkWidget *widget,
@@ -546,6 +547,13 @@ xfdesktop_icon_view_class_init(XfdesktopIconViewClass *klass)
                                                                 0.0, 50.0, 4.0,
                                                                 G_PARAM_READABLE));
 
+    gtk_widget_class_install_style_property(widget_class,
+                                            g_param_spec_uint("tooltip-size",
+                                                              "Tooltip Image Size",
+                                                              "The size of the tooltip image preview",
+                                                              0, 512, 128,
+                                                              G_PARAM_READABLE));
+
     /* same binding entries as GtkIconView */
     gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
                                  "select-all", 0);
@@ -932,7 +940,13 @@ xfdesktop_icon_view_show_tooltip(GtkWidget *widget,
     tip_text = xfdesktop_icon_peek_tooltip(icon_view->priv->item_under_pointer);
     if(!tip_text)
         return FALSE;
-    
+
+    if(icon_view->priv->tooltip_size > 0) {
+        gtk_tooltip_set_icon(tooltip,
+                xfdesktop_icon_peek_pixbuf(icon_view->priv->item_under_pointer,
+                                           icon_view->priv->tooltip_size));
+    }
+
     gtk_tooltip_set_text(tooltip, tip_text);
 
     return TRUE;
@@ -1382,7 +1396,6 @@ xfdesktop_icon_view_drag_drop(GtkWidget *widget,
         
         /* clear out old extents, if any */
         /* FIXME: is this right? */
-        //xfdesktop_icon_mark_extents_dirty(icon);
         xfdesktop_icon_view_invalidate_icon(icon_view, icon, TRUE);
         
         DBG("drag succeeded");
@@ -1520,12 +1533,14 @@ xfdesktop_icon_view_style_set(GtkWidget *widget,
                          "cell-padding", &icon_view->priv->cell_padding,
                          "cell-text-width-proportion", &icon_view->priv->cell_text_width_proportion,
                          "ellipsize-icon-labels", &icon_view->priv->ellipsize_icon_labels,
+                         "tooltip-size", &icon_view->priv->tooltip_size,
                          NULL);
 
     DBG("cell spacing is %d", icon_view->priv->cell_spacing);
     DBG("cell padding is %d", icon_view->priv->cell_padding);
     DBG("cell text width proportion is %f", icon_view->priv->cell_text_width_proportion);
     DBG("ellipsize icon label is %s", icon_view->priv->ellipsize_icon_labels?"true":"false");
+    DBG("tooltip size is %d", icon_view->priv->tooltip_size);
 
     if(icon_view->priv->selection_box_color) {
         gdk_color_free(icon_view->priv->selection_box_color);
@@ -2628,29 +2643,17 @@ xfdesktop_icon_view_paint_icon(XfdesktopIconView *icon_view,
 
     playout = icon_view->priv->playout;
     
-    if(xfdesktop_icon_get_extents(icon, &pixbuf_extents,
-                                   &text_extents, &total_extents))
+    xfdesktop_icon_get_extents(icon, &pixbuf_extents,
+                               &text_extents, &total_extents);
+    xfdesktop_icon_view_setup_pango_layout(icon_view, icon, playout);
+
+    if(!xfdesktop_icon_view_update_icon_extents(icon_view, icon,
+                                                &pixbuf_extents,
+                                                &text_extents,
+                                                &total_extents))
     {
-        xfdesktop_icon_view_setup_pango_layout(icon_view, icon, playout);
-    } else {
-        /* if we get here, it's likely that the expose area doesn't
-         * include everything we *actually* need to repaint.  the
-         * extents should be recalculated before invalidating rects
-         * in the first place.  for now just fix it up and re-expose
-         * the correct area. */
-        if(!xfdesktop_icon_view_update_icon_extents(icon_view, icon,
-                                                    &pixbuf_extents,
-                                                    &text_extents,
-                                                    &total_extents))
-        {
-            g_warning("Can't update extents for icon '%s'",
-                      xfdesktop_icon_peek_label(icon));
-        } else {
-            gtk_widget_queue_draw_area(GTK_WIDGET(icon_view),
-                                       total_extents.x, total_extents.y,
-                                       total_extents.width, total_extents.height);
-        }
-        return;
+        g_warning("Can't update extents for icon '%s'",
+                  xfdesktop_icon_peek_label(icon));
     }
 
     if(g_list_find(icon_view->priv->selected_icons, icon)) {
@@ -2790,7 +2793,6 @@ xfdesktop_grid_do_resize(XfdesktopIconView *icon_view)
     
     /* move all icons into the pending_icons list */
     for(l = icon_view->priv->icons; l; l = l->next) {
-        xfdesktop_icon_mark_extents_dirty(XFDESKTOP_ICON(l->data));
         g_signal_handlers_disconnect_by_func(G_OBJECT(l->data),
                                              G_CALLBACK(xfdesktop_icon_view_icon_changed),
                                              icon_view);
@@ -3210,7 +3212,6 @@ xfdesktop_icon_view_remove_item(XfdesktopIconView *icon_view,
         
         if(xfdesktop_icon_get_position(icon, &row, &col)) {
             xfdesktop_icon_view_invalidate_icon(icon_view, icon, FALSE);
-            xfdesktop_icon_mark_extents_dirty(icon);
             xfdesktop_grid_set_position_free(icon_view, row, col);
         }
         icon_view->priv->icons = g_list_delete_link(icon_view->priv->icons, l);
@@ -3258,7 +3259,6 @@ xfdesktop_icon_view_remove_all(XfdesktopIconView *icon_view)
         if(xfdesktop_icon_get_position(icon, &row, &col)) {
             xfdesktop_icon_view_invalidate_icon(icon_view, icon, FALSE);
             xfdesktop_grid_set_position_free(icon_view, row, col);
-            xfdesktop_icon_mark_extents_dirty(icon);
         }
         
         g_signal_handlers_disconnect_by_func(G_OBJECT(l->data),
diff --git a/src/xfdesktop-icon.c b/src/xfdesktop-icon.c
index 431ba79..38c1538 100644
--- a/src/xfdesktop-icon.c
+++ b/src/xfdesktop-icon.c
@@ -37,7 +37,6 @@ struct _XfdesktopIconPrivate
     gint16 row;
     gint16 col;
 
-    gboolean extents_dirty;
     GdkRectangle pixbuf_extents;
     GdkRectangle text_extents;
     GdkRectangle total_extents;
@@ -116,7 +115,6 @@ xfdesktop_icon_init(XfdesktopIcon *icon)
 {
     icon->priv = G_TYPE_INSTANCE_GET_PRIVATE(icon, XFDESKTOP_TYPE_ICON,
                                              XfdesktopIconPrivate);
-    icon->priv->extents_dirty = TRUE;
 }
 
 
@@ -158,7 +156,6 @@ xfdesktop_icon_set_extents(XfdesktopIcon *icon,
     icon->priv->pixbuf_extents = *pixbuf_extents;
     icon->priv->text_extents = *text_extents;
     icon->priv->total_extents = *total_extents;
-    icon->priv->extents_dirty = FALSE;
 }
 
 gboolean
@@ -169,9 +166,6 @@ xfdesktop_icon_get_extents(XfdesktopIcon *icon,
 {
     g_return_val_if_fail(XFDESKTOP_IS_ICON(icon), FALSE);
 
-    if(icon->priv->extents_dirty)
-        return FALSE;
-
     if(pixbuf_extents)
         *pixbuf_extents = icon->priv->pixbuf_extents;
     if(text_extents)
@@ -182,16 +176,6 @@ xfdesktop_icon_get_extents(XfdesktopIcon *icon,
     return TRUE;
 }
 
-void
-xfdesktop_icon_mark_extents_dirty(XfdesktopIcon *icon)
-{
-    g_return_if_fail(XFDESKTOP_IS_ICON(icon));
-
-    icon->priv->extents_dirty = TRUE;
-}
-
-
-
 /*< required >*/
 GdkPixbuf *
 xfdesktop_icon_peek_pixbuf(XfdesktopIcon *icon,
@@ -283,6 +267,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)
@@ -312,7 +327,6 @@ void
 xfdesktop_icon_pixbuf_changed(XfdesktopIcon *icon)
 {
     g_return_if_fail(XFDESKTOP_IS_ICON(icon));
-    xfdesktop_icon_mark_extents_dirty(icon);
     g_signal_emit(icon, __signals[SIG_PIXBUF_CHANGED], 0);
 }
 
@@ -320,7 +334,6 @@ void
 xfdesktop_icon_label_changed(XfdesktopIcon *icon)
 {
     g_return_if_fail(XFDESKTOP_IS_ICON(icon));
-    xfdesktop_icon_mark_extents_dirty(icon);
     g_signal_emit(icon, __signals[SIG_LABEL_CHANGED], 0);
 }
 
@@ -328,7 +341,6 @@ void
 xfdesktop_icon_position_changed(XfdesktopIcon *icon)
 {
     g_return_if_fail(XFDESKTOP_IS_ICON(icon));
-    xfdesktop_icon_mark_extents_dirty(icon);
     g_signal_emit(icon, __signals[SIG_POS_CHANGED], 0);
 }
 
@@ -337,7 +349,6 @@ void
 xfdesktop_icon_selected(XfdesktopIcon *icon)
 {
     g_return_if_fail(XFDESKTOP_IS_ICON(icon));
-    xfdesktop_icon_mark_extents_dirty(icon);
     g_signal_emit(G_OBJECT(icon), __signals[SIG_SELECTED], 0, NULL);
 }
 
diff --git a/src/xfdesktop-icon.h b/src/xfdesktop-icon.h
index b838ed5..e42261e 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);
@@ -123,7 +129,6 @@ gboolean xfdesktop_icon_get_extents(XfdesktopIcon *icon,
                                     GdkRectangle *pixbuf_extents,
                                     GdkRectangle *text_extents,
                                     GdkRectangle *total_extents);
-void xfdesktop_icon_mark_extents_dirty(XfdesktopIcon *icon);
 
 G_END_DECLS
 
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..4a2d6c6 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_size(g_file_get_path(file_icon->priv->thumbnail_file),
+                                                                        size, size,
+                                                                        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);
-        
-        file_icon->priv->pix = xfdesktop_file_utils_get_icon(icon_name, gicon, 
-                                                             size, emblem_pix,
-                                                             file_icon->priv->pix_opacity);
+
+        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;
 


More information about the Xfce4-commits mailing list