[Xfce4-commits] <thunar:nick/1.8> Make background basics work.

Nick Schermer noreply at xfce.org
Sun Aug 4 21:52:16 CEST 2013


Updating branch refs/heads/nick/1.8
         to 3e9d617d3f5b5182c2aab4c0a40d273da5838541 (commit)
       from d1bf6c50d33b00991f55e9b490655346167a0c5a (commit)

commit 3e9d617d3f5b5182c2aab4c0a40d273da5838541
Author: Nick Schermer <nick at xfce.org>
Date:   Wed Jan 16 23:07:55 2013 +0100

    Make background basics work.

 configure.ac.in                              |    2 +-
 thunar/Makefile.am                           |   11 +-
 thunar/main.c                                |   12 +
 thunar/thunar-abstract-icon-view.c           |    2 +-
 thunar/thunar-desktop-background-icon-view.c |  170 ++++
 thunar/thunar-desktop-background-icon-view.h |   40 +
 thunar/thunar-desktop-background.c           | 1163 ++++++++++++++++++++++++++
 thunar/thunar-desktop-background.h           |   61 ++
 thunar/thunar-desktop-preferences-dialog.c   |  742 ++++++++++++++++
 thunar/thunar-desktop-preferences-dialog.h   |   42 +
 thunar/thunar-desktop-window.c               |  260 ++++++
 thunar/thunar-desktop-window.h               |   44 +
 thunar/thunar-enum-types.c                   |  121 ++-
 thunar/thunar-enum-types.h                   |   39 +
 thunar/thunar-gtk-extensions.c               |    5 +-
 thunar/thunar-list-model.c                   |   29 +-
 thunar/thunar-list-model.h                   |    8 +
 thunar/thunar-standard-view.c                |   19 +-
 18 files changed, 2725 insertions(+), 45 deletions(-)

diff --git a/configure.ac.in b/configure.ac.in
index 482982d..820fdba 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -116,7 +116,7 @@ dnl **********************************
 AC_CHECK_HEADERS([ctype.h errno.h fcntl.h grp.h limits.h locale.h memory.h \
                   paths.h pwd.h sched.h signal.h stdarg.h stdlib.h string.h \
                   sys/mman.h sys/param.h sys/stat.h sys/time.h sys/types.h \
-                  sys/uio.h sys/wait.h time.h])
+                  sys/uio.h sys/wait.h time.h math.h])
 
 dnl ************************************
 dnl *** Check for standard functions ***
diff --git a/thunar/Makefile.am b/thunar/Makefile.am
index ace59c9..f9e77e5 100644
--- a/thunar/Makefile.am
+++ b/thunar/Makefile.am
@@ -59,6 +59,14 @@ thunar_SOURCES =							\
 	thunar-create-dialog.h						\
 	thunar-deep-count-job.h						\
 	thunar-deep-count-job.c						\
+	thunar-desktop-background.c					\
+	thunar-desktop-background.h					\
+	thunar-desktop-background-icon-view.c				\
+	thunar-desktop-background-icon-view.h				\
+	thunar-desktop-preferences-dialog.c				\
+	thunar-desktop-preferences-dialog.h				\
+	thunar-desktop-window.c						\
+	thunar-desktop-window.h						\
 	thunar-details-view-ui.h					\
 	thunar-details-view.c						\
 	thunar-details-view.h						\
@@ -243,7 +251,8 @@ thunar_LDADD =								\
 	$(LIBSM_LIBS)							\
 	$(LIBSTARTUP_NOTIFICATION_LIBS)					\
 	$(LIBXFCE4UI_LIBS)						\
-	$(XFCONF_LIBS)
+	$(XFCONF_LIBS)							\
+	-lm
 
 thunar_DEPENDENCIES =							\
 	$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
diff --git a/thunar/main.c b/thunar/main.c
index cbb9a2f..c9a5c94 100644
--- a/thunar/main.c
+++ b/thunar/main.c
@@ -44,11 +44,13 @@
 #include <thunar/thunar-session-client.h>
 #include <thunar/thunar-stock.h>
 #include <thunar/thunar-preferences.h>
+#include <thunar/thunar-desktop-window.h>
 
 
 
 /* --- globals --- */
 static gboolean opt_bulk_rename = FALSE;
+static gboolean opt_desktop = FALSE;
 static gboolean opt_daemon = FALSE;
 static gchar   *opt_sm_client_id = NULL;
 static gboolean opt_quit = FALSE;
@@ -60,6 +62,7 @@ static gboolean opt_version = FALSE;
 static GOptionEntry option_entries[] =
 {
   { "bulk-rename", 'B', 0, G_OPTION_ARG_NONE, &opt_bulk_rename, N_ ("Open the bulk rename dialog"), NULL, },
+  { "desktop", 0, 0, G_OPTION_ARG_NONE, &opt_desktop, N_ ("Let Thunar manage the desktop"), NULL, },
 #ifdef HAVE_DBUS
   { "daemon", 0, 0, G_OPTION_ARG_NONE, &opt_daemon, N_ ("Run in daemon mode"), NULL, },
 #else
@@ -120,6 +123,7 @@ main (int argc, char **argv)
   gchar               *working_directory;
   gchar              **filenames = NULL;
   const gchar         *startup_id;
+  GtkWidget           *window;
 
   /* setup translation domain */
   xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
@@ -202,6 +206,14 @@ main (int argc, char **argv)
     }
 #endif
 
+  if (G_UNLIKELY (opt_desktop))
+    {
+      window = thunar_desktop_window_new ();
+      gtk_widget_show (window);
+      gtk_main ();
+      return 0;
+    }
+
   /* determine the current working directory */
   working_directory = g_get_current_dir ();
 
diff --git a/thunar/thunar-abstract-icon-view.c b/thunar/thunar-abstract-icon-view.c
index c79b747..c9a4d7d 100644
--- a/thunar/thunar-abstract-icon-view.c
+++ b/thunar/thunar-abstract-icon-view.c
@@ -98,7 +98,7 @@ static void         thunar_abstract_icon_view_zoom_level_changed    (ThunarAbstr
 struct _ThunarAbstractIconViewPrivate
 {
   /* the UI manager merge id for the abstract icon view */
-  gint ui_merge_id;
+  guint  ui_merge_id;
 
   /* mouse gesture support */
   gint   gesture_start_x;
diff --git a/thunar/thunar-desktop-background-icon-view.c b/thunar/thunar-desktop-background-icon-view.c
new file mode 100644
index 0000000..32ad82b
--- /dev/null
+++ b/thunar/thunar-desktop-background-icon-view.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2013 Nick Schermer <nick 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 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <thunar/thunar-desktop-background-icon-view.h>
+#include <thunar/thunar-private.h>
+
+
+
+static void         thunar_desktop_background_icon_view_finalize               (GObject             *object);
+static AtkObject   *thunar_desktop_background_icon_view_get_accessible         (GtkWidget           *widget);
+static gboolean     thunar_desktop_background_icon_view_visible_func           (ThunarFile          *file,
+                                                                                gpointer             data);
+
+
+
+struct _ThunarDesktopBackgroundIconViewClass
+{
+  ThunarAbstractIconViewClass __parent__;
+};
+
+struct _ThunarDesktopBackgroundIconView
+{
+  ThunarAbstractIconView __parent__;
+
+  GHashTable *mime_types;
+};
+
+
+
+G_DEFINE_TYPE (ThunarDesktopBackgroundIconView, thunar_desktop_background_icon_view, THUNAR_TYPE_ABSTRACT_ICON_VIEW)
+
+
+
+static void
+thunar_desktop_background_icon_view_class_init (ThunarDesktopBackgroundIconViewClass *klass)
+{
+  ThunarStandardViewClass *thunarstandard_view_class;
+  GtkWidgetClass          *gtkwidget_class;
+  GObjectClass            *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = thunar_desktop_background_icon_view_finalize;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->get_accessible = thunar_desktop_background_icon_view_get_accessible;
+
+  thunarstandard_view_class = THUNAR_STANDARD_VIEW_CLASS (klass);
+  thunarstandard_view_class->zoom_level_property_name = NULL;
+}
+
+
+
+static void
+thunar_desktop_background_icon_view_init (ThunarDesktopBackgroundIconView *icon_view)
+{
+  ThunarListModel  *model;
+  GSList           *formats;
+  GSList           *lp;
+  gchar           **mime_types;
+  guint             n;
+  GtkWidget        *exo_view;
+
+  /* only a single selection is possible */
+  exo_view = gtk_bin_get_child (GTK_BIN (icon_view));
+  exo_icon_view_set_selection_mode (EXO_ICON_VIEW (exo_view), GTK_SELECTION_BROWSE);
+  exo_icon_view_set_enable_search (EXO_ICON_VIEW (exo_view), TRUE);
+
+  /* setup the icon renderer */
+  g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (icon_view)->icon_renderer),
+                "size", THUNAR_ICON_SIZE_LARGEST,
+                NULL);
+
+  /* setup the name renderer */
+  g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (icon_view)->name_renderer),
+                "visible", FALSE, NULL);
+
+  /* collect gdk supported types */
+  icon_view->mime_types = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  /* get a list of all formats supported by GdkPixbuf */
+  formats = gdk_pixbuf_get_formats ();
+  for (lp = formats; lp != NULL; lp = lp->next)
+    {
+      /* ignore the disabled ones */
+      if (!gdk_pixbuf_format_is_disabled (lp->data))
+        {
+          /* get a list of MIME types supported by this format */
+          mime_types = gdk_pixbuf_format_get_mime_types (lp->data);
+          if (G_UNLIKELY (mime_types == NULL))
+            continue;
+
+          /* put them all in the unqiue MIME type hash table */
+          for (n = 0; mime_types[n] != NULL; ++n)
+            g_hash_table_replace (icon_view->mime_types, g_strdup (mime_types[n]), NULL);
+
+          /* free the string array */
+          g_strfreev (mime_types);
+        }
+    }
+  g_slist_free (formats);
+
+  /* only show gdkpixbuf supported files */
+  model = THUNAR_STANDARD_VIEW (icon_view)->model;
+  thunar_list_model_set_visible_func (model, thunar_desktop_background_icon_view_visible_func, icon_view);
+}
+
+
+
+static void
+thunar_desktop_background_icon_view_finalize (GObject *object)
+{
+  ThunarDesktopBackgroundIconView *icon_view = THUNAR_DESKTOP_BACKGROUND_ICON_VIEW (object);
+
+  g_hash_table_destroy (icon_view->mime_types);
+
+  (*G_OBJECT_CLASS (thunar_desktop_background_icon_view_parent_class)->finalize) (object);
+}
+
+
+
+static AtkObject*
+thunar_desktop_background_icon_view_get_accessible (GtkWidget *widget)
+{
+  AtkObject *object;
+
+  /* query the atk object for the icon view class */
+  object = (*GTK_WIDGET_CLASS (thunar_desktop_background_icon_view_parent_class)->get_accessible) (widget);
+
+  /* set custom Atk properties for the icon view */
+  if (G_LIKELY (object != NULL))
+    {
+      atk_object_set_description (object, _("Icon based directory listing"));
+      atk_object_set_name (object, _("Icon view"));
+      atk_object_set_role (object, ATK_ROLE_DIRECTORY_PANE);
+    }
+
+  return object;
+}
+
+
+
+static gboolean
+thunar_desktop_background_icon_view_visible_func (ThunarFile *file,
+                                                  gpointer    data)
+{
+  ThunarDesktopBackgroundIconView *icon_view = THUNAR_DESKTOP_BACKGROUND_ICON_VIEW (data);
+
+  return g_hash_table_lookup_extended (icon_view->mime_types,
+                                       thunar_file_get_content_type (file),
+                                       NULL, NULL);
+}
diff --git a/thunar/thunar-desktop-background-icon-view.h b/thunar/thunar-desktop-background-icon-view.h
new file mode 100644
index 0000000..9015827
--- /dev/null
+++ b/thunar/thunar-desktop-background-icon-view.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2013 Nick Schermer <nick 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 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
+ */
+
+#ifndef __THUNAR_DESKTOP_BACKGROUND_ICON_VIEW_H__
+#define __THUNAR_DESKTOP_BACKGROUND_ICON_VIEW_H__
+
+#include <thunar/thunar-abstract-icon-view.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ThunarDesktopBackgroundIconViewClass ThunarDesktopBackgroundIconViewClass;
+typedef struct _ThunarDesktopBackgroundIconView      ThunarDesktopBackgroundIconView;
+
+#define THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW             (thunar_desktop_background_icon_view_get_type ())
+#define THUNAR_DESKTOP_BACKGROUND_ICON_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW, ThunarDesktopBackgroundIconView))
+#define THUNAR_DESKTOP_BACKGROUND_ICON_VIEW_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW, ThunarDesktopBackgroundIconViewClass))
+#define THUNAR_IS_DESKTOP_BACKGROUND_ICON_VIEW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW))
+#define THUNAR_IS_DESKTOP_BACKGROUND_ICON_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW))
+#define THUNAR_DESKTOP_BACKGROUND_ICON_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW, ThunarDesktopBackgroundIconViewClass))
+
+GType thunar_desktop_background_icon_view_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* !__THUNAR_DESKTOP_BACKGROUND_ICON_VIEW_H__ */
diff --git a/thunar/thunar-desktop-background.c b/thunar/thunar-desktop-background.c
new file mode 100644
index 0000000..a99661a
--- /dev/null
+++ b/thunar/thunar-desktop-background.c
@@ -0,0 +1,1163 @@
+/*-
+ * Copyright (c) 2012 Nick Schermer <nick 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 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+#include <exo/exo.h>
+#include <xfconf/xfconf.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#endif
+
+#include <thunar/thunar-private.h>
+#include <thunar/thunar-desktop-background.h>
+#include <thunar/thunar-enum-types.h>
+#include <thunar/thunar-gdk-extensions.h>
+
+#define FADE_ANIMATION_USEC ((gdouble) G_USEC_PER_SEC / 2.0) /* 0.5 second */
+#define FADE_ANIMATION_FPS  (1000 / 60)
+
+
+
+enum
+{
+  PROP_0,
+  PROP_SOURCE_WINDOW,
+};
+
+
+
+static void     thunar_desktop_background_constructed           (GObject                  *object);
+static void     thunar_desktop_background_finalize              (GObject                  *object);
+static void     thunar_desktop_background_get_property          (GObject                  *object,
+                                                                 guint                     prop_id,
+                                                                 GValue                   *value,
+                                                                 GParamSpec               *pspec);
+static void     thunar_desktop_background_set_property          (GObject                  *object,
+                                                                 guint                     prop_id,
+                                                                 const GValue             *value,
+                                                                 GParamSpec               *pspec);
+static void     thunar_desktop_background_expose                (ThunarDesktopBackground  *background,
+                                                                 gboolean                  pseudo_transparency);
+static void     thunar_desktop_background_paint                 (ThunarDesktopBackground  *background,
+                                                                 gboolean                  fade_animation);
+static void     thunar_desktop_background_settings_changed      (XfconfChannel            *channel,
+                                                                 const gchar              *property,
+                                                                 const GValue             *value,
+                                                                 ThunarDesktopBackground  *background);
+static void     thunar_desktop_background_cycle                 (ThunarDesktopBackground  *background,
+                                                                 gboolean                  startup);
+
+
+
+struct _ThunarDesktopBackgroundClass
+{
+  GObjectClass __parent__;
+};
+
+struct _ThunarDesktopBackground
+{
+  GObject __parent__;
+
+  XfconfChannel *settings;
+
+  GdkWindow     *source_window;
+  GdkPixmap     *pixmap;
+
+  /* fade timeout for paint() */
+  guint          fade_timeout_id;
+
+  /* for grouping xfconf changes */
+  guint          paint_idle_id;
+
+  /* background cycle timer */
+  guint          cycle_timeout_id;
+};
+
+typedef struct
+{
+  ThunarDesktopBackground *background;
+  cairo_surface_t         *start_surface;
+  cairo_surface_t         *end_surface;
+  gint64                   start_time;
+}
+BackgroundFade;
+
+
+
+G_DEFINE_TYPE (ThunarDesktopBackground, thunar_desktop_background, G_TYPE_OBJECT)
+
+
+
+static void
+thunar_desktop_background_class_init (ThunarDesktopBackgroundClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->constructed = thunar_desktop_background_constructed;
+  gobject_class->finalize = thunar_desktop_background_finalize;
+  gobject_class->get_property = thunar_desktop_background_get_property;
+  gobject_class->set_property = thunar_desktop_background_set_property;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_SOURCE_WINDOW,
+                                   g_param_spec_object ("source-window", NULL, NULL,
+                                                        GDK_TYPE_WINDOW,
+                                                        EXO_PARAM_READWRITE
+                                                        | G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+
+static void
+thunar_desktop_background_init (ThunarDesktopBackground *background)
+{
+  background->settings = xfconf_channel_get ("thunar-desktop");
+}
+
+
+
+static void
+thunar_desktop_background_constructed (GObject *object)
+{
+  ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (object);
+  GdkScreen               *screen;
+
+  _thunar_return_if_fail (GDK_IS_DRAWABLE (background->source_window));
+
+  /* create pixmap we can draw on */
+  screen = gdk_window_get_screen (background->source_window);
+  background->pixmap = gdk_pixmap_new (GDK_DRAWABLE (background->source_window),
+                                       gdk_screen_get_width (screen),
+                                       gdk_screen_get_height (screen),
+                                       -1);
+
+  /* fire cycle timer */
+  thunar_desktop_background_cycle (background, TRUE);
+
+  /* render images and colors */
+  thunar_desktop_background_paint (background, FALSE);
+
+  /* set our background */
+  gdk_window_set_back_pixmap (background->source_window,
+                              background->pixmap, FALSE);
+
+  /* expose views */
+  thunar_desktop_background_expose (background, TRUE);
+
+  /* start watching changes */
+  g_signal_connect (G_OBJECT (background->settings), "property-changed",
+                    G_CALLBACK (thunar_desktop_background_settings_changed), background);
+}
+
+
+
+static void
+thunar_desktop_background_finalize (GObject *object)
+{
+  ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (object);
+  GdkWindow               *root_window;
+
+  /* unset the backgrounds */
+  gdk_window_set_back_pixmap (background->source_window, NULL, FALSE);
+  root_window = gdk_screen_get_root_window (gdk_window_get_screen (background->source_window));
+  gdk_window_set_back_pixmap (root_window, NULL, FALSE);
+
+#ifdef GDK_WINDOWING_X11
+  /* unset speudo-transparency */
+  gdk_error_trap_push ();
+  gdk_property_delete (root_window, gdk_atom_intern_static_string ("_XROOTPMAP_ID"));
+  gdk_property_delete (root_window, gdk_atom_intern_static_string ("ESETROOT_PMAP_ID"));
+  gdk_error_trap_pop ();
+#endif
+
+  if (background->cycle_timeout_id != 0)
+    g_source_remove (background->cycle_timeout_id);
+
+  if (background->fade_timeout_id != 0)
+    g_source_remove (background->fade_timeout_id);
+
+  if (background->paint_idle_id != 0)
+    g_source_remove (background->paint_idle_id);
+
+  if (background->source_window != NULL)
+    g_object_unref (G_OBJECT (background->source_window));
+  if (background->pixmap != NULL)
+    g_object_unref (G_OBJECT (background->pixmap));
+
+  g_signal_handlers_disconnect_by_func (G_OBJECT (background->settings),
+      G_CALLBACK (thunar_desktop_background_settings_changed), background);
+
+  (*G_OBJECT_CLASS (thunar_desktop_background_parent_class)->finalize) (object);
+}
+
+
+
+static void
+thunar_desktop_background_get_property (GObject    *object,
+                                        guint       prop_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  //ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_desktop_background_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (object);
+
+  switch (prop_id)
+    {
+    case PROP_SOURCE_WINDOW:
+      _thunar_return_if_fail (background->source_window == NULL);
+      background->source_window = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static gchar *
+thunar_desktop_background_get_monitor_name (GdkScreen *screen,
+                                            gint       n)
+{
+  gchar *name;
+
+  name = gdk_screen_get_monitor_plug_name (screen, n);
+  if (G_UNLIKELY (name == NULL))
+    name = g_strdup_printf ("monitor-%d", n);
+
+  return name;
+}
+
+
+
+static void
+thunar_desktop_background_paint_image (cairo_t               *cr,
+                                       const GdkRectangle    *area,
+                                       GdkInterpType          interp,
+                                       ThunarBackgroundStyle  style,
+                                       const GdkPixbuf       *src_pixbuf)
+{
+  gint             src_w, src_h;
+  GdkPixbuf       *dst_pixbuf = NULL;
+  gint             dx, dy;
+  gint             dst_w, dst_h;
+  gdouble          wratio, hratio;
+  gboolean         scale_dst = FALSE;
+  cairo_pattern_t *pattern;
+
+  _thunar_return_if_fail (GDK_IS_PIXBUF (src_pixbuf));
+
+  src_w = gdk_pixbuf_get_width (src_pixbuf);
+  src_h = gdk_pixbuf_get_height (src_pixbuf);
+
+  dx = area->x;
+  dy = area->y;
+
+  dst_w = area->width;
+  dst_h = area->height;
+
+  switch (style)
+    {
+    case THUNAR_BACKGROUND_STYLE_NONE:
+      return;
+
+    case THUNAR_BACKGROUND_STYLE_TILED:
+      break;
+
+    case THUNAR_BACKGROUND_STYLE_CENTERED:
+      dx += (area->width - src_w) / 2;
+      dy += (area->height - src_h) / 2;
+      break;
+
+    case THUNAR_BACKGROUND_STYLE_STRETCHED:
+      if (src_w != dst_w || src_h != dst_h)
+        {
+          /* scale to fill screen */
+          scale_dst = TRUE;
+        }
+      break;
+
+    case THUNAR_BACKGROUND_STYLE_SCALED:
+      if (src_w != dst_w || src_h != dst_h)
+        {
+          /* calculate the new dimensions */
+          wratio = (gdouble) src_w / (gdouble) dst_w;
+          hratio = (gdouble) src_h / (gdouble) dst_h;
+
+          if (hratio > wratio)
+            dst_w = rint (src_w / hratio);
+          else
+            dst_h = rint (src_h / wratio);
+
+          /* scale to monitor, no corp */
+          scale_dst = TRUE;
+        }
+      break;
+
+    case THUNAR_BACKGROUND_STYLE_SPANNED:
+    case THUNAR_BACKGROUND_STYLE_ZOOMED:
+      if (src_w != dst_w || src_h != dst_h)
+        {
+          /* calculate the new dimensions */
+          wratio = (gdouble) src_w / (gdouble) dst_w;
+          hratio = (gdouble) src_h / (gdouble) dst_h;
+
+          if (hratio < wratio)
+            dst_w = rint (src_w / hratio);
+          else
+            dst_h = rint (src_h / wratio);
+
+          /* scale to monitor, no corp */
+          scale_dst = TRUE;
+        }
+      break;
+    }
+
+  if (scale_dst)
+    {
+      /* scale source */
+      dst_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, MAX (1, dst_w), MAX (1, dst_h), interp);
+
+      /* center on monitor */
+      dx += (area->width - dst_w) / 2;
+      dy += (area->height - dst_h) / 2;
+    }
+  else
+    {
+      /* no scaling was required, ref source */
+      dst_pixbuf = g_object_ref (G_OBJECT (src_pixbuf));
+    }
+
+  /* clip area */
+  cairo_save (cr);
+  gdk_cairo_rectangle (cr, area);
+  cairo_clip (cr);
+
+  /* paint the image */
+  thunar_gdk_cairo_set_source_pixbuf (cr, dst_pixbuf, dx, dy);
+
+  if (style == THUNAR_BACKGROUND_STYLE_TILED)
+    {
+      pattern = cairo_get_source (cr);
+      cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    }
+
+  cairo_paint (cr);
+  cairo_restore (cr);
+
+  g_object_unref (G_OBJECT (dst_pixbuf));
+}
+
+
+
+static void
+thunar_desktop_background_paint_color_fill (cairo_t                    *cr,
+                                            const GdkRectangle         *area,
+                                            ThunarBackgroundColorStyle  style,
+                                            GdkColor                   *color_start,
+                                            GdkColor                   *color_end)
+{
+  cairo_pattern_t *pattern;
+  gint             x1, y1;
+  gint             radius;
+
+  switch (style)
+    {
+    case THUNAR_BACKGROUND_COLOR_STYLE_SOLID:
+      gdk_cairo_set_source_color (cr, color_start);
+      cairo_rectangle (cr, area->x, area->y, area->width, area->height);
+      cairo_fill (cr);
+      break;
+
+    case THUNAR_BACKGROUND_COLOR_STYLE_HORIZONTAL:
+      x1 = area->x + area->width;
+      y1 = area->y;
+      goto paint_linear;
+
+    case THUNAR_BACKGROUND_COLOR_STYLE_VERTICAL:
+      x1 = area->x;
+      y1 = area->y + area->height;
+
+      paint_linear:
+
+      pattern = cairo_pattern_create_linear (area->x, area->y, x1, y1);
+
+      paint_gradient:
+
+      if (cairo_pattern_status (pattern) == CAIRO_STATUS_SUCCESS)
+        {
+          /* set start and stop color */
+          cairo_pattern_add_color_stop_rgb (pattern, 0,
+                                            color_start->red / 65535.0,
+                                            color_start->green / 65535.0,
+                                            color_start->blue / 65535.0);
+          cairo_pattern_add_color_stop_rgb (pattern, 1,
+                                            color_end->red / 65535.0,
+                                            color_end->green / 65535.0,
+                                            color_end->blue / 65535.0);
+
+          /* draw rectangle with gradient fill */
+          cairo_save (cr);
+          cairo_rectangle (cr, area->x, area->y, area->width, area->height);
+          cairo_set_source (cr, pattern);
+          cairo_fill (cr);
+          cairo_restore (cr);
+        }
+      cairo_pattern_destroy (pattern);
+      break;
+
+    case THUNAR_BACKGROUND_COLOR_STYLE_RADIAL:
+      x1 = area->x + area->width / 2;
+      y1 = area->y + area->height / 2;
+      radius = MAX (area->width, area->height) / 2;
+
+      pattern = cairo_pattern_create_radial (x1, y1, radius, x1, y1, 0);
+      goto paint_gradient;
+      break;
+    }
+}
+
+
+
+static cairo_surface_t *
+thunar_desktop_background_get_surface (ThunarDesktopBackground *background,
+                                       cairo_t                 *cr_source)
+{
+  cairo_surface_t *copy;
+  cairo_surface_t *target;
+  gint             w, h;
+  cairo_t         *cr;
+
+  target = cairo_get_target (cr_source);
+  gdk_drawable_get_size (GDK_DRAWABLE (background->pixmap), &w, &h);
+  copy = cairo_surface_create_similar (target, cairo_surface_get_content (target), w, h);
+
+  cr = cairo_create (copy);
+  cairo_set_source_surface (cr, target, 0.0, 0.0);
+  cairo_paint (cr);
+
+  if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+    {
+      cairo_surface_destroy (copy);
+      copy = NULL;
+    }
+
+  cairo_destroy (cr);
+
+  return copy;
+}
+
+
+
+static void
+thunar_desktop_background_expose (ThunarDesktopBackground *background,
+                                  gboolean                 pseudo_transparency)
+{
+  GdkWindow *root_window;
+  GdkScreen *screen;
+#ifdef GDK_WINDOWING_X11
+  Window     pixmap_xid;
+  GdkAtom    atom_pixmap;
+#endif
+
+  /* invalidate area of our window */
+  gdk_window_clear (background->source_window);
+
+  /* leave if we're not updating the fake transparency */
+  if (!pseudo_transparency)
+    return;
+
+  gdk_error_trap_push ();
+
+  /* root window background */
+  screen = gdk_window_get_screen (background->source_window);
+  root_window = gdk_screen_get_root_window (screen);
+  gdk_window_set_back_pixmap (root_window, background->pixmap, FALSE);
+  gdk_window_clear (root_window);
+
+#ifdef GDK_WINDOWING_X11
+  /* pseudo atoms for aterm / xterm etc */
+  atom_pixmap = gdk_atom_intern_static_string ("PIXMAP");
+  pixmap_xid = gdk_x11_drawable_get_xid (GDK_DRAWABLE (background->pixmap));
+
+  gdk_property_change (root_window,
+                       gdk_atom_intern_static_string ("ESETROOT_PMAP_ID"),
+                       atom_pixmap, 32,
+                       GDK_PROP_MODE_REPLACE, (guchar *) &pixmap_xid, 1);
+
+  gdk_property_change (root_window,
+                       gdk_atom_intern_static_string ("_XROOTPMAP_ID"),
+                       atom_pixmap, 32,
+                       GDK_PROP_MODE_REPLACE, (guchar *) &pixmap_xid, 1);
+#endif
+
+  gdk_error_trap_pop ();
+}
+
+
+
+static gboolean
+thunar_desktop_background_fade_running (gpointer data)
+{
+  BackgroundFade *fade = data;
+  gdouble         opacity;
+  cairo_t        *cr;
+
+  _thunar_return_val_if_fail (THUNAR_IS_DESKTOP_BACKGROUND (fade->background), FALSE);
+  _thunar_return_val_if_fail (GDK_IS_DRAWABLE (fade->background->pixmap), FALSE);
+  _thunar_return_val_if_fail (GDK_IS_WINDOW (fade->background->source_window), FALSE);
+
+  /* get fade opacity based on system time */
+  if (fade->start_time > 0)
+    opacity = (g_get_real_time () - fade->start_time) / FADE_ANIMATION_USEC;
+  else
+    opacity = 0;
+
+  /* prepare cairo context */
+  cr = gdk_cairo_create (GDK_DRAWABLE (fade->background->pixmap));
+  g_assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+
+  cairo_set_source_surface (cr, fade->start_surface, 0.0, 0.0);
+  cairo_paint (cr);
+
+  if (G_LIKELY (opacity > 0))
+    {
+      cairo_set_source_surface (cr, fade->end_surface, 0.0, 0.0);
+      cairo_paint_with_alpha (cr, CLAMP (opacity, 0.0, 1.0));
+    }
+
+  cairo_destroy (cr);
+
+  /* expose */
+  thunar_desktop_background_expose (fade->background, opacity >= 1.0);
+
+  return opacity < 1.0;
+}
+
+
+
+static void
+thunar_desktop_background_fade_completed (gpointer data)
+{
+  BackgroundFade *fade = data;
+
+  _thunar_return_if_fail (THUNAR_IS_DESKTOP_BACKGROUND (fade->background));
+  _thunar_return_if_fail (GDK_IS_DRAWABLE (fade->background->pixmap));
+  _thunar_return_if_fail (GDK_IS_WINDOW (fade->background->source_window));
+
+  fade->background->fade_timeout_id = 0;
+
+  /* cleanup */
+  cairo_surface_destroy (fade->start_surface);
+  cairo_surface_destroy (fade->end_surface);
+  g_slice_free (BackgroundFade, fade);
+}
+
+
+
+static void
+thunar_desktop_background_paint (ThunarDesktopBackground *background,
+                                 gboolean                 fade_animation)
+{
+  cairo_t                    *cr;
+  GdkScreen                  *screen;
+  gint                        n, n_monitors;
+  GdkRectangle                area;
+  GdkInterpType               interp;
+  GdkPixbuf                  *pixbuf;
+  cairo_surface_t            *start_surface;
+  cairo_surface_t            *end_surface;
+  BackgroundFade             *fade;
+  ThunarBackgroundStyle       bg_style;
+  ThunarBackgroundColorStyle  color_style;
+  GError                     *error = NULL;
+  gchar                      *monitor_name;
+  gint                        screen_num;
+  gchar                       prop[128];
+  GdkColor                    color_start;
+  GdkColor                    color_end;
+  gchar                      *filename;
+  gchar                      *uri;
+  gchar                      *base_prop;
+
+  _thunar_return_if_fail (THUNAR_IS_DESKTOP_BACKGROUND (background));
+  _thunar_return_if_fail (GDK_IS_DRAWABLE (background->pixmap));
+
+  /* stop pending animation */
+  if (background->fade_timeout_id != 0)
+    g_source_remove (background->fade_timeout_id);
+
+  /* prepare cairo context */
+  cr = gdk_cairo_create (GDK_DRAWABLE (background->pixmap));
+  g_assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+
+  /* cache the old surface for the fade animation */
+  if (fade_animation
+      && xfconf_channel_get_bool (background->settings, "/background/fade-animation", TRUE))
+    start_surface = thunar_desktop_background_get_surface (background, cr);
+  else
+    start_surface = NULL;
+
+  /* screen info */
+  screen = gdk_window_get_screen (background->source_window);
+  screen_num = gdk_screen_get_number (screen);
+  n_monitors = gdk_screen_get_n_monitors (screen);
+
+  /* if the screen has a bit depth of less than 24bpp, using bilinear
+   * filtering looks crappy (mainly with gradients). */
+  if (gdk_drawable_get_depth (GDK_DRAWABLE (background->source_window)) < 24)
+    interp = GDK_INTERP_HYPER;
+  else
+    interp = GDK_INTERP_BILINEAR;
+
+  /* draw each monitor */
+  for (n = 0; n < n_monitors; n++)
+    {
+      /* get the monitor name */
+      monitor_name = thunar_desktop_background_get_monitor_name (screen, n);
+
+      base_prop = g_strdup_printf ("/background/screen-%d/%s", screen_num, monitor_name);
+
+      /* get background style */
+      g_snprintf (prop, sizeof (prop), "%s/style", base_prop);
+      bg_style = thunar_desktop_background_settings_enum (background->settings,
+                                                          THUNAR_TYPE_BACKGROUND_STYLE,
+                                                          prop, DEFAULT_BACKGROUND_STYLE);
+
+      /* spanning works only on settings of the 1st monitor */
+      if (bg_style == THUNAR_BACKGROUND_STYLE_SPANNED)
+        {
+          area.x = area.y = 0;
+          area.width = gdk_screen_get_width (screen);
+          area.height = gdk_screen_get_height (screen);
+        }
+      else
+        {
+          /* get area of the monitor */
+          gdk_screen_get_monitor_geometry (screen, n, &area);
+        }
+
+      pixbuf = NULL;
+
+      if (bg_style != THUNAR_BACKGROUND_STYLE_NONE)
+        {
+          /* get file name */
+          g_snprintf (prop, sizeof (prop), "%s/uri", base_prop);
+          uri = xfconf_channel_get_string (background->settings, prop, DEFAULT_BACKGROUND_URI);
+
+          /* only support local files */
+          if (G_LIKELY (uri != NULL
+              && g_str_has_prefix (uri, "file:///")))
+            {
+              filename = g_filename_from_uri (uri, NULL, NULL);
+              if (G_LIKELY (filename != NULL))
+                {
+                  /* load the image into the memory (exo uses mmap) */
+                  pixbuf = exo_gdk_pixbuf_new_from_file_at_max_size (filename, G_MAXINT, G_MAXINT, TRUE, &error);
+                  if (G_UNLIKELY (pixbuf == NULL))
+                    {
+                      g_warning ("Unable to load image \"%s\": %s",
+                                 filename, error != NULL ? error->message : "No error");
+                      g_error_free (error);
+                    }
+                  g_free (filename);
+                }
+            }
+          g_free (uri);
+        }
+
+      /* check if we should draw a background color (if we are not sure the background
+       * image is not going to overlap the color anyway) */
+      if (pixbuf == NULL
+          || gdk_pixbuf_get_has_alpha (pixbuf)
+          || bg_style == THUNAR_BACKGROUND_STYLE_NONE
+          || bg_style == THUNAR_BACKGROUND_STYLE_TILED
+          || bg_style == THUNAR_BACKGROUND_STYLE_CENTERED
+          || bg_style == THUNAR_BACKGROUND_STYLE_SCALED)
+        {
+          /* get background style */
+          g_snprintf (prop, sizeof (prop), "%s/color-style", base_prop);
+          color_style = thunar_desktop_background_settings_enum (background->settings,
+                                                                 THUNAR_TYPE_BACKGROUND_COLOR_STYLE,
+                                                                 prop, DEFAULT_BACKGROUND_COLOR_STYLE);
+
+          /* load the colors */
+          g_snprintf (prop, sizeof (prop), "%s/color-start", base_prop);
+          thunar_desktop_background_settings_color (background->settings, prop,
+                                                    &color_start,
+                                                    DEFAULT_BACKGROUND_COLOR_START);
+          g_snprintf (prop, sizeof (prop), "%s/color-end", base_prop);
+          thunar_desktop_background_settings_color (background->settings, prop,
+                                                    &color_end,
+                                                    DEFAULT_BACKGROUND_COLOR_END);
+
+          /* paint */
+          thunar_desktop_background_paint_color_fill (cr, &area, color_style, &color_start, &color_end);
+        }
+
+      if (G_LIKELY (pixbuf != NULL))
+        {
+          /* paint the image */
+          thunar_desktop_background_paint_image (cr, &area, interp, bg_style, pixbuf);
+          g_object_unref (G_OBJECT (pixbuf));
+        }
+
+      g_free (monitor_name);
+      g_free (base_prop);
+
+      /* only need to draw once for spanning */
+      if (bg_style == THUNAR_BACKGROUND_STYLE_SPANNED)
+        break;
+    }
+
+  /* if a fade animation was requested, start the timeout */
+  if (start_surface != NULL)
+    {
+      end_surface = thunar_desktop_background_get_surface (background, cr);
+      if (G_LIKELY (end_surface != NULL))
+        {
+          fade = g_slice_new0 (BackgroundFade);
+          fade->background = background;
+          fade->start_surface = start_surface;
+          fade->end_surface = end_surface;
+
+          /* "restore" the old background (start_time == 0) */
+          thunar_desktop_background_fade_running (fade);
+
+          /* start animation */
+          fade->start_time = g_get_real_time ();
+          background->fade_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, FADE_ANIMATION_FPS,
+                                                            thunar_desktop_background_fade_running, fade,
+                                                            thunar_desktop_background_fade_completed);
+        }
+      else
+        {
+          /* cairo error, clear the start surface */
+          cairo_surface_destroy (start_surface);
+          thunar_desktop_background_expose (background, TRUE);
+        }
+    }
+  else if (fade_animation)
+    {
+      /* animations are disabled, clear the window now */
+      thunar_desktop_background_expose (background, TRUE);
+    }
+
+  cairo_destroy (cr);
+}
+
+
+
+static gint
+thunar_desktop_background_info_sort (gconstpointer a,
+                                     gconstpointer b)
+{
+  return g_strcmp0 (g_file_info_get_attribute_byte_string (G_FILE_INFO (a), "sortkey"),
+                    g_file_info_get_attribute_byte_string (G_FILE_INFO (b), "sortkey"));
+}
+
+
+
+static gchar *
+thunar_desktop_background_next_image (const gchar  *last_uri,
+                                      gboolean      choose_random)
+{
+  GFile           *current_file;
+  GFile           *parent_dir;
+  GFileEnumerator *enumerator;
+  GError          *error = NULL;
+  GFileInfo       *info;
+  GFileInfo       *current_info = NULL;
+  GSList          *image_files = NULL;
+  const gchar     *content_type;
+  gchar           *key;
+  guint            n, n_image_files = 0;
+  GSList          *li = NULL;
+  const gchar     *filename;
+  GFile           *file;
+  gchar           *path;
+  gint             offset;
+  gchar           *uri = NULL;
+
+  _thunar_return_val_if_fail (last_uri != NULL, NULL);
+  _thunar_return_val_if_fail (g_str_has_prefix (last_uri, "file:///"), NULL);
+
+  /* get the parent file */
+  current_file = g_file_new_for_uri (last_uri);
+  parent_dir = g_file_get_parent (current_file);
+
+  /* read the directory contents */
+  enumerator = g_file_enumerate_children (parent_dir,
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                          G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
+                                          G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+                                          G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
+                                          G_FILE_QUERY_INFO_NONE,
+                                          NULL, &error);
+
+  if (enumerator != NULL)
+    {
+      for (;;)
+        {
+          /* query info of the child */
+          info = g_file_enumerator_next_file (enumerator, NULL, NULL);
+          if (G_UNLIKELY (info == NULL))
+            break;
+
+          /* skip hidden files and non-images */
+          content_type = g_file_info_get_content_type (info);
+          if (g_file_info_get_is_hidden (info)
+              || !g_content_type_is_a (content_type, "image/*"))
+            {
+              g_object_unref (G_OBJECT (info));
+              continue;
+            }
+
+          filename = g_file_info_get_name (info);
+
+          /* add a collate key for sorting */
+          key = g_utf8_collate_key_for_filename (filename, -1);
+          g_file_info_set_attribute_byte_string (info, "sortkey", key);
+          g_free (key);
+
+          /* find the current file */
+          file = g_file_get_child (parent_dir, filename);
+          if (g_file_equal (file, current_file))
+            current_info = g_object_ref (G_OBJECT (info));
+          g_object_unref (G_OBJECT (file));
+
+          /* keep the items in a list */
+          image_files = g_slist_insert_sorted (image_files, info, thunar_desktop_background_info_sort);
+          n_image_files++;
+        }
+
+      g_object_unref (G_OBJECT (enumerator));
+    }
+  else
+    {
+      g_critical ("Unable to find next background: %s", error->message);
+      g_error_free (error);
+    }
+
+  if (image_files != NULL)
+    {
+      /* pick a place in the list */
+      if (choose_random)
+        {
+          /* choose a random item in the list */
+          offset = g_random_int_range (0, n_image_files);
+          li = g_slist_nth (image_files, offset);
+        }
+      else if (current_info != NULL)
+        {
+          /* find the next item in the list */
+          li = g_slist_find (image_files, current_info);
+        }
+      else
+        {
+          /* use head */
+          li = image_files;
+        }
+
+      /* find next suitable image */
+      for (n = 0; uri == NULL && n < n_image_files; n++)
+        {
+          /* check if we've hit the current item (once) */
+          if (current_info != NULL
+              && li->data == current_info)
+            {
+              li = g_slist_next (li);
+
+              g_object_unref (G_OBJECT (current_info));
+              current_info = NULL;
+            }
+
+          /* resume at the start of the list if we reached the tail */
+          if (li == NULL)
+            li = image_files;
+
+          info = G_FILE_INFO (li->data);
+
+          /* create gfile for reliable uri */
+          file = g_file_get_child (parent_dir, g_file_info_get_name (info));
+
+          /* load the image into the memory (exo uses mmap) */
+          path = g_file_get_path (file);
+          if (gdk_pixbuf_get_file_info (path, NULL, NULL) != NULL)
+            uri = g_file_get_uri (file);
+          g_free (path);
+
+          g_object_unref (file);
+        }
+    }
+
+  /* cleanup */
+  g_slist_free_full (image_files, g_object_unref);
+  g_object_unref (G_OBJECT (current_file));
+  g_object_unref (G_OBJECT (parent_dir));
+  if (current_info != NULL)
+    g_object_unref (G_OBJECT (current_info));
+
+  return uri;
+}
+
+
+
+static gboolean
+thunar_desktop_background_cycle_timeout (gpointer data)
+{
+  ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (data);
+  GdkScreen               *screen;
+  gint                     screen_num;
+  gint                     n, n_monitors;
+  gchar                   *monitor_name;
+  gchar                   *uri, *new_uri;
+  gchar                    prop[128];
+  ThunarBackgroundStyle    bg_style = THUNAR_BACKGROUND_STYLE_NONE;
+  gboolean                 random_image;
+
+  /* screen info */
+  screen = gdk_window_get_screen (background->source_window);
+  screen_num = gdk_screen_get_number (screen);
+  n_monitors = gdk_screen_get_n_monitors (screen);
+
+  /* whether random is enabled */
+  random_image = xfconf_channel_get_bool (background->settings, "/background/cycle/random", FALSE);
+
+  for (n = 0; bg_style != THUNAR_BACKGROUND_STYLE_SPANNED && n < n_monitors; n++)
+    {
+      /* get the monitor name */
+      monitor_name = thunar_desktop_background_get_monitor_name (screen, n);
+
+      g_snprintf (prop, sizeof (prop), "/background/screen-%d/%s/style", screen_num, monitor_name);
+      bg_style = thunar_desktop_background_settings_enum (background->settings,
+                                                          THUNAR_TYPE_BACKGROUND_STYLE,
+                                                          prop, DEFAULT_BACKGROUND_STYLE);
+
+      if (bg_style != THUNAR_BACKGROUND_STYLE_NONE)
+        {
+          /* get file name */
+          g_snprintf (prop, sizeof (prop), "/background/screen-%d/%s/uri", screen_num, monitor_name);
+          uri = xfconf_channel_get_string (background->settings, prop, DEFAULT_BACKGROUND_URI);
+          if (uri != NULL
+              && g_str_has_prefix (uri, "file:///"))
+            {
+              new_uri = thunar_desktop_background_next_image (uri, random_image);
+              if (new_uri != NULL)
+                xfconf_channel_set_string (background->settings, prop, new_uri);
+              g_free (new_uri);
+            }
+
+          g_free (uri);
+        }
+
+      g_free (monitor_name);
+    }
+
+  return TRUE;
+}
+
+
+
+static void
+thunar_desktop_background_cycle_timeout_destroyed (gpointer data)
+{
+  THUNAR_DESKTOP_BACKGROUND (data)->cycle_timeout_id = 0;
+}
+
+
+
+static void
+thunar_desktop_background_cycle (ThunarDesktopBackground *background,
+                                 gboolean                 startup)
+{
+  guint timeout;
+
+  /* stop pending timer */
+  if (background->cycle_timeout_id != 0)
+    g_source_remove (background->cycle_timeout_id);
+
+  if (xfconf_channel_get_bool (background->settings, "/background/cycle/enabled", FALSE))
+    {
+      timeout = xfconf_channel_get_uint (background->settings, "/background/cycle/time",
+                                         DEFAULT_BACKGROUND_CYCLE_TIME);
+
+      if (timeout == 0 && startup)
+        {
+          /* Cycle during login, so only fire once */
+          thunar_desktop_background_cycle_timeout (background);
+        }
+      else if (timeout >= 10)
+        {
+          /* start the cycle timer */
+          background->cycle_timeout_id =
+              g_timeout_add_seconds_full (G_PRIORITY_LOW, timeout,
+                  thunar_desktop_background_cycle_timeout, background,
+                  thunar_desktop_background_cycle_timeout_destroyed);
+
+          /* show we're doing something */
+          if (!startup)
+            thunar_desktop_background_cycle_timeout (background);
+        }
+    }
+}
+
+
+
+static gboolean
+thunar_desktop_background_settings_changed_idle (gpointer data)
+{
+  ThunarDesktopBackground *background = THUNAR_DESKTOP_BACKGROUND (data);
+
+  background->paint_idle_id = 0;
+
+  thunar_desktop_background_paint (background, TRUE);
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_desktop_background_settings_changed (XfconfChannel           *channel,
+                                            const gchar             *property,
+                                            const GValue            *value,
+                                            ThunarDesktopBackground *background)
+{
+  _thunar_return_if_fail (THUNAR_IS_DESKTOP_BACKGROUND (background));
+  _thunar_return_if_fail (XFCONF_IS_CHANNEL (channel));
+  _thunar_return_if_fail (background->source_window == NULL
+                          || GDK_IS_DRAWABLE (background->source_window));
+
+  /* only respond to background changes */
+  if (g_str_has_prefix (property, "/background/screen-")
+      && background->paint_idle_id == 0)
+    {
+      /* idle to group multiple changes a bit */
+      background->paint_idle_id =
+          g_idle_add (thunar_desktop_background_settings_changed_idle, background);
+    }
+  else if (g_str_has_prefix (property, "/background/cycle/"))
+    {
+      /* restart cycle timer */
+      thunar_desktop_background_cycle (background, FALSE);
+    }
+}
+
+
+
+ThunarDesktopBackground *
+thunar_desktop_background_new (GdkWindow *source_window)
+{
+  _thunar_return_val_if_fail (GDK_IS_WINDOW (source_window), NULL);
+  return g_object_new (THUNAR_TYPE_DESKTOP_BACKGROUND,
+                       "source-window", source_window, NULL);
+}
+
+
+
+guint
+thunar_desktop_background_settings_enum (XfconfChannel *channel,
+                                         GType          enum_type,
+                                         const gchar   *prop,
+                                         guint          default_value)
+{
+  gchar      *str;
+  GEnumClass *klass;
+  guint       value = default_value;
+  guint       n;
+
+  _thunar_return_val_if_fail (XFCONF_IS_CHANNEL (channel), 0);
+
+  /* get string name from settings */
+  str = xfconf_channel_get_string (channel, prop, NULL);
+  if (str == NULL)
+    return value;
+
+  /* determine the enum value matching the src... */
+  klass = g_type_class_ref (enum_type);
+  _thunar_assert (G_IS_ENUM_CLASS (klass));
+  for (n = 0; n < klass->n_values; ++n)
+    {
+      if (exo_str_is_equal (klass->values[n].value_name, str))
+        {
+          value = klass->values[n].value;
+          break;
+        }
+    }
+  g_type_class_unref (klass);
+  g_free (str);
+
+  return value;
+}
+
+
+
+void
+thunar_desktop_background_settings_color (XfconfChannel *channel,
+                                          const gchar   *prop,
+                                          GdkColor      *color_return,
+                                          const gchar   *default_color)
+{
+  gchar *str;
+
+  _thunar_return_if_fail (XFCONF_IS_CHANNEL (channel));
+
+  /* get string name from settings */
+  str = xfconf_channel_get_string (channel, prop, default_color);
+  if (!gdk_color_parse (str, color_return))
+    {
+      g_warning ("Failed to parse color %s from %s", str, prop);
+
+      /* return black */
+      color_return->red = 0;
+      color_return->green = 0;
+      color_return->blue = 0;
+    }
+  g_free (str);
+}
diff --git a/thunar/thunar-desktop-background.h b/thunar/thunar-desktop-background.h
new file mode 100644
index 0000000..983a919
--- /dev/null
+++ b/thunar/thunar-desktop-background.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2012 Nick Schermer <nick 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 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
+ */
+
+#ifndef __THUNAR_DESKTOP_BACKGROUND_H__
+#define __THUNAR_DESKTOP_BACKGROUND_H__
+
+#include <gtk/gtk.h>
+#include <xfconf/xfconf.h>
+
+/* defaults for the background */
+#define DEFAULT_BACKGROUND_URI         "file://" DATADIR "/backgrounds/xfce/xfce-blue.jpg"
+#define DEFAULT_BACKGROUND_STYLE       THUNAR_BACKGROUND_STYLE_ZOOMED
+#define DEFAULT_BACKGROUND_COLOR_STYLE THUNAR_BACKGROUND_COLOR_STYLE_SOLID
+#define DEFAULT_BACKGROUND_COLOR_START "#152233"
+#define DEFAULT_BACKGROUND_COLOR_END   "#152233"
+#define DEFAULT_BACKGROUND_CYCLE_TIME  900 /* 15 minutes */
+
+G_BEGIN_DECLS
+
+typedef struct _ThunarDesktopBackgroundClass ThunarDesktopBackgroundClass;
+typedef struct _ThunarDesktopBackground      ThunarDesktopBackground;
+
+#define THUNAR_TYPE_DESKTOP_BACKGROUND            (thunar_desktop_background_get_type ())
+#define THUNAR_DESKTOP_BACKGROUND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND, ThunarDesktopBackground))
+#define THUNAR_DESKTOP_BACKGROUND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DESKTOP_BACKGROUND, ThunarDesktopBackgroundClass))
+#define THUNAR_IS_DESKTOP_BACKGROUND(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND))
+#define THUNAR_IS_DESKTOP_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_DESKTOP_BACKGROUND))
+#define THUNAR_DESKTOP_BACKGROUND_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DESKTOP_BACKGROUND, ThunarDesktopBackgroundClass))
+
+GType                    thunar_desktop_background_get_type         (void) G_GNUC_CONST;
+
+ThunarDesktopBackground *thunar_desktop_background_new              (GdkWindow     *source_window);
+
+guint                    thunar_desktop_background_settings_enum    (XfconfChannel *channel,
+                                                                     GType          enum_type,
+                                                                     const gchar   *prop,
+                                                                     guint          default_value);
+
+void                     thunar_desktop_background_settings_color   (XfconfChannel *channel,
+                                                                     const gchar   *prop,
+                                                                     GdkColor      *color_return,
+                                                                     const gchar   *default_color);
+
+G_END_DECLS
+
+#endif /* !__THUNAR_DESKTOP_BACKGROUND_H__ */
diff --git a/thunar/thunar-desktop-preferences-dialog.c b/thunar/thunar-desktop-preferences-dialog.c
new file mode 100644
index 0000000..a63feaa
--- /dev/null
+++ b/thunar/thunar-desktop-preferences-dialog.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2013 Nick Schermer <nick 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 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libxfce4ui/libxfce4ui.h>
+#include <xfconf/xfconf.h>
+
+#include <thunar/thunar-desktop-preferences-dialog.h>
+#include <thunar/thunar-enum-types.h>
+#include <thunar/thunar-desktop-background-icon-view.h>
+#include <thunar/thunar-desktop-background.h>
+#include <thunar/thunar-private.h>
+
+
+
+enum
+{
+  PROP_0,
+  PROP_CYCLE_TIME,
+};
+
+
+
+static void     thunar_desktop_preferences_dialog_get_property              (GObject                        *object,
+                                                                             guint                           prop_id,
+                                                                             GValue                         *value,
+                                                                             GParamSpec                     *pspec);
+static void     thunar_desktop_preferences_dialog_set_property              (GObject                        *object,
+                                                                             guint                           prop_id,
+                                                                             const GValue                   *value,
+                                                                             GParamSpec                     *pspec);
+static void     thunar_desktop_preferences_dialog_finalize                  (GObject                        *object);
+static void     thunar_desktop_preferences_dialog_realize                   (GtkWidget                      *widget);
+static void     thunar_desktop_preferences_dialog_screen_changed            (GtkWidget                      *widget,
+                                                                             GdkScreen                      *old_screen);
+static gboolean thunar_desktop_preferences_dialog_configure_event           (GtkWidget                      *widget,
+                                                                             GdkEventConfigure              *event);
+static void     thunar_desktop_preferences_dialog_response                  (GtkDialog                      *dialog,
+                                                                             gint                            response);
+static void     thunar_desktop_preferences_dialog_background_changed        (ThunarDesktopPreferencesDialog *dialog);
+static void     thunar_desktop_preferences_dialog_background_prop           (ThunarDesktopPreferencesDialog *dialog);
+static gboolean thunar_desktop_preferences_dialog_update                    (gpointer                        data);
+static void     thunar_desktop_preferences_dialog_folder_changed            (ThunarDesktopPreferencesDialog *dialog);
+static void     thunar_desktop_preferences_dialog_style_changed             (ThunarDesktopPreferencesDialog *dialog);
+static void     thunar_desktop_preferences_dialog_color_style_changed       (ThunarDesktopPreferencesDialog *dialog);
+static void     thunar_desktop_preferences_dialog_color_changed             (GtkWidget                      *color_button,
+                                                                             ThunarDesktopPreferencesDialog *dialog);
+static void     thunar_desktop_preferences_dialog_cycle_time_changed        (ThunarDesktopPreferencesDialog *dialog);
+
+
+
+struct _ThunarDesktopPreferencesDialogClass
+{
+  XfceTitledDialogClass __parent__;
+};
+
+struct _ThunarDesktopPreferencesDialog
+{
+  XfceTitledDialog __parent__;
+
+  XfconfChannel *settings;
+
+  gchar         *background_prop;
+
+  guint          dialog_update_timeout;
+
+  GtkWidget     *view;
+  GtkWidget     *folder_chooser;
+  GtkWidget     *image_style;
+
+  GtkWidget     *color_style;
+  GtkWidget     *color_start;
+  GtkWidget     *color_end;
+
+  GtkWidget     *cycle_time;
+  guint          cycle_time_sec;
+};
+
+static struct
+{
+  guint        seconds;
+  const gchar *name;
+}
+default_cycle_times[] =
+{
+  { 0,      N_("During login")      },
+  { 10,     N_("Every 10 seconds")  },
+  { 60,     N_("Every minute")      },
+  { 300,    N_("Every 5 minutes")   },
+  { 900,    N_("Every 15 minutes")  },
+  { 1800,   N_("Every 30 minutes")  },
+  { 3600,   N_("Every hour")        },
+  { 86400,  N_("Every day")         },
+};
+
+
+
+G_DEFINE_TYPE (ThunarDesktopPreferencesDialog, thunar_desktop_preferences_dialog, XFCE_TYPE_TITLED_DIALOG)
+
+
+
+static void
+thunar_desktop_preferences_dialog_class_init (ThunarDesktopPreferencesDialogClass *klass)
+{
+  GtkDialogClass *gtkdialog_class;
+  GObjectClass   *gobject_class;
+  GtkWidgetClass *gtkwidget_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = thunar_desktop_preferences_dialog_get_property;
+  gobject_class->set_property = thunar_desktop_preferences_dialog_set_property;
+  gobject_class->finalize = thunar_desktop_preferences_dialog_finalize;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->realize = thunar_desktop_preferences_dialog_realize;
+  gtkwidget_class->screen_changed = thunar_desktop_preferences_dialog_screen_changed;
+  gtkwidget_class->configure_event = thunar_desktop_preferences_dialog_configure_event;
+
+  gtkdialog_class = GTK_DIALOG_CLASS (klass);
+  gtkdialog_class->response = thunar_desktop_preferences_dialog_response;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_CYCLE_TIME,
+                                   g_param_spec_uint ("cycle-time",
+                                                      NULL, NULL,
+                                                      0, 86400,
+                                                      DEFAULT_BACKGROUND_CYCLE_TIME,
+                                                      EXO_PARAM_READWRITE));
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_init (ThunarDesktopPreferencesDialog *dialog)
+{
+  GtkWidget    *notebook;
+  GtkWidget    *label;
+  GtkWidget    *vbox;
+  GtkWidget    *hbox;
+  GtkWidget    *combo;
+  GtkWidget    *button;
+  GtkWidget    *button2;
+  GEnumClass   *klass;
+  guint         n;
+  GtkSizeGroup *size_group;
+  GtkWidget    *seperator;
+
+  dialog->settings = xfconf_channel_get ("thunar-desktop");
+
+  /* configure the dialog properties */
+  gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-wallpaper");
+  gtk_window_set_default_size (GTK_WINDOW (dialog), 900, 700);
+
+  /* add "Help" and "Close" buttons */
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                          GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+                          NULL);
+
+  notebook = gtk_notebook_new ();
+  gtk_container_set_border_width (GTK_CONTAINER (notebook), 6);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), notebook, TRUE, TRUE, 0);
+  gtk_widget_show (notebook);
+
+  /*
+     Display
+   */
+  label = gtk_label_new (_("Background"));
+  vbox = g_object_new (GTK_TYPE_VBOX, "border-width", 12, "spacing", 6, NULL);
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+  gtk_widget_show (label);
+  gtk_widget_show (vbox);
+
+  dialog->view = g_object_new (THUNAR_TYPE_DESKTOP_BACKGROUND_ICON_VIEW, NULL);
+  g_signal_connect_swapped (G_OBJECT (dialog->view), "notify::selected-files",
+      G_CALLBACK (thunar_desktop_preferences_dialog_background_changed), dialog);
+  gtk_box_pack_start (GTK_BOX (vbox), dialog->view, TRUE, TRUE, 0);
+  gtk_widget_show (dialog->view);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+  hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic ("_Folder:");
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+  gtk_size_group_add_widget (size_group, label);
+  gtk_widget_show (label);
+
+  dialog->folder_chooser = gtk_file_chooser_button_new (_("Choose a background images folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->folder_chooser, FALSE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->folder_chooser);
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog->folder_chooser), TRUE);
+  g_signal_connect_swapped (G_OBJECT (dialog->folder_chooser), "current-folder-changed",
+      G_CALLBACK (thunar_desktop_preferences_dialog_folder_changed), dialog);
+  gtk_widget_show (dialog->folder_chooser);
+
+  /* spacer */
+  label = g_object_new (GTK_TYPE_LABEL, NULL);
+  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+  gtk_widget_show (label);
+
+  label = gtk_label_new_with_mnemonic ("_Style:");
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+  gtk_widget_show (label);
+
+  dialog->image_style = combo = gtk_combo_box_text_new ();
+  gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+  gtk_widget_show (combo);
+
+  klass = g_type_class_ref (THUNAR_TYPE_BACKGROUND_STYLE);
+  for (n = 0; n < klass->n_values; ++n)
+    gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
+  g_type_class_unref (klass);
+
+  g_signal_connect_swapped (G_OBJECT (combo), "changed",
+      G_CALLBACK (thunar_desktop_preferences_dialog_style_changed), dialog);
+
+  hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic ("C_olors:");
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+  gtk_size_group_add_widget (size_group, label);
+  gtk_widget_show (label);
+
+  g_object_unref (size_group);
+
+  dialog->color_style = combo = gtk_combo_box_text_new ();
+  gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+  gtk_widget_show (combo);
+
+  klass = g_type_class_ref (THUNAR_TYPE_BACKGROUND_COLOR_STYLE);
+  for (n = 0; n < klass->n_values; ++n)
+    gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
+  g_type_class_unref (klass);
+
+  g_signal_connect_swapped (G_OBJECT (combo), "changed",
+      G_CALLBACK (thunar_desktop_preferences_dialog_color_style_changed), dialog);
+
+  dialog->color_start = button = gtk_color_button_new ();
+  gtk_color_button_set_title (GTK_COLOR_BUTTON (button), _("Start background color"));
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+  g_signal_connect (G_OBJECT (button), "color-set",
+      G_CALLBACK (thunar_desktop_preferences_dialog_color_changed), dialog);
+  gtk_widget_show (button);
+
+  dialog->color_end = button = gtk_color_button_new ();
+  gtk_color_button_set_title (GTK_COLOR_BUTTON (button), _("End background color"));
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+  g_object_bind_property (combo, "active", button, "sensitive", G_BINDING_SYNC_CREATE);
+  g_signal_connect (G_OBJECT (button), "color-set",
+      G_CALLBACK (thunar_desktop_preferences_dialog_color_changed), dialog);
+  gtk_widget_show (button);
+
+  seperator = gtk_hseparator_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), seperator, FALSE, TRUE, 0);
+  gtk_widget_show (seperator);
+
+  hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+  gtk_widget_show (hbox);
+
+  button = gtk_check_button_new_with_mnemonic (_("Ch_ange background(s)"));
+  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+  xfconf_g_property_bind (dialog->settings, "/background/cycle/enabled",
+                          G_TYPE_BOOLEAN, button, "active");
+  gtk_widget_show (button);
+
+  combo = dialog->cycle_time = gtk_combo_box_text_new ();
+  g_object_bind_property (button, "active", combo, "sensitive", G_BINDING_SYNC_CREATE);
+  gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0);
+  for (n = 0; n < G_N_ELEMENTS (default_cycle_times); ++n)
+    {
+      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(default_cycle_times[n].name));
+      if (default_cycle_times[n].seconds == DEFAULT_BACKGROUND_CYCLE_TIME)
+        gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n);
+    }
+  g_signal_connect_swapped (G_OBJECT (combo), "changed",
+      G_CALLBACK (thunar_desktop_preferences_dialog_cycle_time_changed), dialog);
+  gtk_widget_show (combo);
+
+  xfconf_g_property_bind (dialog->settings, "/background/cycle/time",
+                          G_TYPE_UINT, dialog, "cycle-time");
+
+  button2 = gtk_check_button_new_with_mnemonic (_("_Random order"));
+  g_object_bind_property (button, "active", button2, "sensitive", G_BINDING_SYNC_CREATE);
+  gtk_box_pack_start (GTK_BOX (hbox), button2, FALSE, TRUE, 0);
+  xfconf_g_property_bind (dialog->settings, "/background/cycle/random",
+                          G_TYPE_BOOLEAN, button2, "active");
+  gtk_widget_show (button2);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_get_property (GObject    *object,
+                                                guint       prop_id,
+                                                GValue     *value,
+                                                GParamSpec *pspec)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (object);
+
+  switch (prop_id)
+    {
+    case PROP_CYCLE_TIME:
+      g_value_set_uint (value, dialog->cycle_time_sec);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_set_property (GObject      *object,
+                                                guint         prop_id,
+                                                const GValue *value,
+                                                GParamSpec   *pspec)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (object);
+  guint                           n;
+  guint                           cycle_time;
+
+  switch (prop_id)
+    {
+    case PROP_CYCLE_TIME:
+      /* find closest item in list */
+      cycle_time = g_value_get_uint (value);
+      for (n = 0; n < G_N_ELEMENTS (default_cycle_times); n++)
+        if (default_cycle_times[n].seconds >= cycle_time)
+          break;
+
+       /* update the widget */
+       g_signal_handlers_block_by_func (dialog->cycle_time,
+           thunar_desktop_preferences_dialog_cycle_time_changed, dialog);
+       gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->cycle_time), n);
+       g_signal_handlers_unblock_by_func (dialog->cycle_time,
+           thunar_desktop_preferences_dialog_cycle_time_changed, dialog);
+
+       /* store last value */
+       dialog->cycle_time_sec = default_cycle_times[n].seconds;
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_finalize (GObject *object)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (object);
+
+  if (dialog->dialog_update_timeout != 0)
+    g_source_remove (dialog->dialog_update_timeout);
+
+  g_free (dialog->background_prop);
+
+  (*G_OBJECT_CLASS (thunar_desktop_preferences_dialog_parent_class)->finalize) (object);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_realize (GtkWidget *widget)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (widget);
+
+  GTK_WIDGET_CLASS (thunar_desktop_preferences_dialog_parent_class)->realize (widget);
+
+  if (dialog->dialog_update_timeout == 0)
+    {
+      /* make sure the dialog hold data */
+      dialog->dialog_update_timeout =
+          g_idle_add (thunar_desktop_preferences_dialog_update, dialog);
+    }
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_screen_changed (GtkWidget *widget,
+                                                  GdkScreen *old_screen)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (widget);
+
+  if (dialog->dialog_update_timeout == 0)
+    {
+      dialog->dialog_update_timeout =
+          g_idle_add (thunar_desktop_preferences_dialog_update, dialog);
+    }
+}
+
+
+
+static gboolean
+thunar_desktop_preferences_dialog_configure_event (GtkWidget         *widget,
+                                                   GdkEventConfigure *event)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (widget);
+  static gint                     old_x = G_MAXINT;
+  static gint                     old_y = G_MAXINT;
+
+  /* quick check if the window moved */
+  if (old_x == G_MAXINT && old_y == G_MAXINT)
+    {
+      /* we're about the get the initial position */
+      if (dialog->dialog_update_timeout == 0)
+        {
+          dialog->dialog_update_timeout =
+            g_idle_add (thunar_desktop_preferences_dialog_update, dialog);
+        }
+    }
+  else if (old_x != event->x || old_y != event->y)
+    {
+      /* reschedule update */
+      if (dialog->dialog_update_timeout != 0)
+        g_source_remove (dialog->dialog_update_timeout);
+
+      dialog->dialog_update_timeout =
+        g_timeout_add (250, thunar_desktop_preferences_dialog_update, dialog);
+    }
+
+  /* update coordinates */
+  old_x = event->x;
+  old_y = event->y;
+
+  /* let Gtk+ handle the configure event */
+  return (*GTK_WIDGET_CLASS (thunar_desktop_preferences_dialog_parent_class)->configure_event) (widget, event);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_response (GtkDialog *dialog,
+                                            gint       response)
+{
+  if (G_UNLIKELY (response == GTK_RESPONSE_HELP))
+    {
+      /* open the preferences section of the user manual */
+      xfce_dialog_show_help (GTK_WINDOW (dialog), "thunar",
+                             "desktop", NULL);
+    }
+  else
+    {
+      /* close the preferences dialog */
+      gtk_widget_destroy (GTK_WIDGET (dialog));
+    }
+}
+
+
+
+static gboolean
+thunar_desktop_preferences_dialog_update (gpointer data)
+{
+  ThunarDesktopPreferencesDialog *dialog = THUNAR_DESKTOP_PREFERENCES_DIALOG (data);
+  gchar                           prop[128];
+  gchar                          *uri;
+  ThunarFile                     *file;
+  GFile                          *gfile;
+  ThunarFile                     *file_parent;
+  GList                           fake;
+  guint                           style;
+  GdkColor                        color;
+
+  /* for timeout */
+  dialog->dialog_update_timeout = 0;
+
+  /* update the base property */
+  thunar_desktop_preferences_dialog_background_prop (dialog);
+
+  /* background image */
+  g_snprintf (prop, sizeof (prop), "%s/uri", dialog->background_prop);
+  uri = xfconf_channel_get_string (dialog->settings, prop, DEFAULT_BACKGROUND_URI);
+  gfile = g_file_new_for_uri (uri);
+  g_free (uri);
+
+  /* select the file in the view */
+  file = thunar_file_get (gfile, NULL);
+  if (file != NULL)
+    {
+      file_parent = thunar_file_get_parent (file, NULL);
+      if (file_parent != NULL)
+        {
+          /* set view of the directory */
+          thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (dialog->view), file_parent);
+          gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog->folder_chooser),
+                                                    thunar_file_get_file (file_parent), NULL);
+          g_object_unref (file_parent);
+
+          /* create a fake list and select the file */
+          fake.prev = fake.next = NULL;
+          fake.data = file;
+          thunar_component_set_selected_files (THUNAR_COMPONENT (dialog->view), &fake);
+        }
+
+      g_object_unref (file);
+    }
+
+  /* image style */
+  g_snprintf (prop, sizeof (prop), "%s/style", dialog->background_prop);
+  style = thunar_desktop_background_settings_enum (dialog->settings,
+                                                   THUNAR_TYPE_BACKGROUND_STYLE,
+                                                   prop, DEFAULT_BACKGROUND_STYLE);
+  gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->image_style), style);
+
+  /* color style */
+  g_snprintf (prop, sizeof (prop), "%s/color-style", dialog->background_prop);
+  style = thunar_desktop_background_settings_enum (dialog->settings,
+                                                   THUNAR_TYPE_BACKGROUND_COLOR_STYLE,
+                                                   prop, DEFAULT_BACKGROUND_COLOR_STYLE);
+  gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->color_style), style);
+
+  /* colors */
+  g_snprintf (prop, sizeof (prop), "%s/color-start", dialog->background_prop);
+  thunar_desktop_background_settings_color (dialog->settings, prop, &color,
+                                            DEFAULT_BACKGROUND_COLOR_START);
+  gtk_color_button_set_color (GTK_COLOR_BUTTON (dialog->color_start), &color);
+
+  g_snprintf (prop, sizeof (prop), "%s/color-end", dialog->background_prop);
+  thunar_desktop_background_settings_color (dialog->settings, prop, &color,
+                                            DEFAULT_BACKGROUND_COLOR_END);
+  gtk_color_button_set_color (GTK_COLOR_BUTTON (dialog->color_end), &color);
+
+  return FALSE;
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_background_changed (ThunarDesktopPreferencesDialog *dialog)
+{
+  GList      *files;
+  ThunarFile *file;
+  gchar      *uri;
+  gchar      *prop;
+
+  /* get the selected file */
+  files = thunar_component_get_selected_files (THUNAR_COMPONENT (dialog->view));
+  _thunar_assert (files == NULL || files->next == NULL);
+  file = g_list_nth_data (files, 0);
+
+  if (file != NULL)
+    {
+      /* save uri */
+      uri = thunar_file_dup_uri (file);
+      prop = g_strconcat (dialog->background_prop, "/uri", NULL);
+      xfconf_channel_set_string (dialog->settings, prop, uri);
+      g_free (uri);
+      g_free (prop);
+    }
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_background_prop (ThunarDesktopPreferencesDialog *dialog)
+{
+  GdkScreen *screen;
+  gint       monitor_num;
+  gchar     *monitor_name;
+  gchar     *nice_name;
+  gchar     *title;
+
+  g_free (dialog->background_prop);
+
+  screen = gtk_window_get_screen (GTK_WINDOW (dialog));
+  monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (GTK_WIDGET (dialog)));
+  monitor_name = gdk_screen_get_monitor_plug_name (screen, monitor_num);
+
+  if (gdk_display_get_n_screens (gdk_screen_get_display (screen)) > 1)
+    {
+      /* I18N: multiple screen setup, use this as part of the title */
+      nice_name = g_strdup_printf (_("Screen %d"), gdk_screen_get_number (screen));
+    }
+  else
+    {
+      if (monitor_name == NULL)
+        {
+          /* I18N: no output name for title, use number */
+          nice_name = g_strdup_printf (_("Monitor %d"), monitor_num);
+        }
+      else
+        {
+          nice_name = g_strdup (monitor_name);
+        }
+    }
+
+  /* I18N: last part is the screen or monitor name */
+  title = g_strdup_printf (_("Desktop Preferences - %s"), nice_name);
+  gtk_window_set_title (GTK_WINDOW (dialog), title);
+  g_free (title);
+
+  /* make sure there is a technical name */
+  if (monitor_name == NULL)
+    monitor_name = g_strdup_printf ("monitor-%d", monitor_num);
+
+  /* xfconf base property for settings in this dialog */
+  dialog->background_prop = g_strdup_printf ("/background/screen-%d/%s",
+                                             gdk_screen_get_number (screen),
+                                             monitor_name);
+
+  g_free (monitor_name);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_folder_changed (ThunarDesktopPreferencesDialog *dialog)
+{
+  GFile      *gfile;
+  ThunarFile *current_directory;
+
+  gfile = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (dialog->folder_chooser));
+  if (G_LIKELY (gfile != NULL))
+    {
+      current_directory = thunar_file_get (gfile, NULL);
+      g_object_unref (gfile);
+
+      thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (dialog->view), current_directory);
+      g_object_unref (current_directory);
+    }
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_style_changed (ThunarDesktopPreferencesDialog *dialog)
+{
+  guint       active;
+  GEnumClass *klass;
+  gchar      *prop;
+
+  active = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->image_style));
+
+  prop = g_strconcat (dialog->background_prop, "/style", NULL);
+  klass = g_type_class_ref (THUNAR_TYPE_BACKGROUND_STYLE);
+  xfconf_channel_set_string (dialog->settings, prop, klass->values[active].value_name);
+  g_type_class_unref (klass);
+  g_free (prop);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_color_style_changed (ThunarDesktopPreferencesDialog *dialog)
+{
+  guint       active;
+  GEnumClass *klass;
+  gchar      *prop;
+
+  active = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->color_style));
+
+  prop = g_strconcat (dialog->background_prop, "/color-style", NULL);
+  klass = g_type_class_ref (THUNAR_TYPE_BACKGROUND_COLOR_STYLE);
+  xfconf_channel_set_string (dialog->settings, prop, klass->values[active].value_name);
+  g_type_class_unref (klass);
+  g_free (prop);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_color_changed (GtkWidget                      *button,
+                                                 ThunarDesktopPreferencesDialog *dialog)
+{
+  GdkColor     color;
+  gchar       *prop;
+  gchar       *color_str;
+  const gchar *name = button == dialog->color_start ? "start" : "end";
+
+  gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &color);
+  color_str = gdk_color_to_string (&color);
+
+  prop = g_strconcat (dialog->background_prop, "/color-", name, NULL);
+  xfconf_channel_set_string (dialog->settings, prop, color_str);
+  g_free (prop);
+  g_free (color_str);
+}
+
+
+
+static void
+thunar_desktop_preferences_dialog_cycle_time_changed (ThunarDesktopPreferencesDialog *dialog)
+{
+  gint active;
+
+  active = CLAMP (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->cycle_time)),
+                  0, (gint) G_N_ELEMENTS (default_cycle_times));
+  dialog->cycle_time_sec = default_cycle_times[active].seconds;
+
+  /* save xfconf setting */
+  g_object_notify (G_OBJECT (dialog), "cycle-time");
+}
+
+
+
+/**
+ * thunar_desktop_preferences_dialog_new:
+ * @parent : a #GtkWindow or %NULL.
+ *
+ * Allocates a new #ThunarDesktopPreferencesDialog widget.
+ *
+ * Return value: the newly allocated #ThunarDesktopPreferencesDialog.
+ **/
+GtkWidget *
+thunar_desktop_preferences_dialog_new (GtkWindow *parent)
+{
+  return g_object_new (THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG,
+                       "screen", gtk_window_get_screen (parent), NULL);
+}
+
diff --git a/thunar/thunar-desktop-preferences-dialog.h b/thunar/thunar-desktop-preferences-dialog.h
new file mode 100644
index 0000000..12b990b
--- /dev/null
+++ b/thunar/thunar-desktop-preferences-dialog.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2013 Nick Schermer <nick 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 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
+ */
+
+#ifndef __THUNAR_DESKTOP_PREFERENCES_DIALOG_H__
+#define __THUNAR_DESKTOP_PREFERENCES_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ThunarDesktopPreferencesDialogClass ThunarDesktopPreferencesDialogClass;
+typedef struct _ThunarDesktopPreferencesDialog      ThunarDesktopPreferencesDialog;
+
+#define THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG            (thunar_desktop_preferences_dialog_get_type ())
+#define THUNAR_DESKTOP_PREFERENCES_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG, ThunarDesktopPreferencesDialog))
+#define THUNAR_DESKTOP_PREFERENCES_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG, ThunarDesktopPreferencesDialogClass))
+#define THUNAR_IS_DESKTOP_PREFERENCES_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG))
+#define THUNAR_IS_DESKTOP_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG))
+#define THUNAR_DESKTOP_PREFERENCES_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DESKTOP_PREFERENCES_DIALOG, ThunarDesktopPreferencesDialogClass))
+
+GType      thunar_desktop_preferences_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *thunar_desktop_preferences_dialog_new      (GtkWindow *parent) G_GNUC_MALLOC;
+
+G_END_DECLS;
+
+#endif /* !__THUNAR_DESKTOP_PREFERENCES_DIALOG_H__ */
diff --git a/thunar/thunar-desktop-window.c b/thunar/thunar-desktop-window.c
new file mode 100644
index 0000000..dfa48da
--- /dev/null
+++ b/thunar/thunar-desktop-window.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 2006 Benedikt Meurer <benny at xfce.org>
+ * Copyright (c) 2012 Nick Schermer <nick 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 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <thunar/thunar-private.h>
+#include <thunar/thunar-desktop-window.h>
+#include <thunar/thunar-desktop-background.h>
+#include <thunar/thunar-desktop-preferences-dialog.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+
+
+static void     thunar_desktop_window_size_request              (GtkWidget                *widget,
+                                                                 GtkRequisition           *requisition);
+static void     thunar_desktop_window_realize                   (GtkWidget                *widget);
+static void     thunar_desktop_window_unrealize                 (GtkWidget                *widget);
+static gboolean thunar_desktop_window_expose_event              (GtkWidget                *widget,
+                                                                 GdkEventExpose           *event);
+static gboolean thunar_desktop_window_button_press_event        (GtkWidget                *widget,
+                                                                 GdkEventButton           *event);
+
+
+
+struct _ThunarDesktopWindowClass
+{
+  GtkWindowClass __parent__;
+};
+
+struct _ThunarDesktopWindow
+{
+  GtkWindow __parent__;
+
+  ThunarDesktopBackground *background;
+};
+
+
+
+G_DEFINE_TYPE (ThunarDesktopWindow, thunar_desktop_window, GTK_TYPE_WINDOW)
+
+
+
+static void
+thunar_desktop_window_class_init (ThunarDesktopWindowClass *klass)
+{
+  GtkWidgetClass *gtkwidget_class;
+
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->size_request = thunar_desktop_window_size_request;
+  gtkwidget_class->realize = thunar_desktop_window_realize;
+  gtkwidget_class->unrealize = thunar_desktop_window_unrealize;
+  gtkwidget_class->expose_event = thunar_desktop_window_expose_event;
+  gtkwidget_class->button_press_event = thunar_desktop_window_button_press_event;
+}
+
+
+
+static void
+thunar_desktop_window_init (ThunarDesktopWindow *window)
+{
+  gtk_widget_add_events (GTK_WIDGET (window),
+                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+
+  gtk_window_move (GTK_WINDOW (window), 0, 0);
+  gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DESKTOP);
+  gtk_widget_set_double_buffered (GTK_WIDGET (window), FALSE);
+}
+
+
+
+static void
+thunar_desktop_window_size_request (GtkWidget      *widget,
+                                    GtkRequisition *requisition)
+{
+  GdkScreen *screen = gtk_widget_get_screen (widget);
+
+  requisition->width = gdk_screen_get_width (screen);
+  requisition->height = gdk_screen_get_height (screen);
+}
+
+
+
+static void
+thunar_desktop_window_screen_changed (GdkScreen           *screen,
+                                      ThunarDesktopWindow *window)
+{
+  GdkWindow *gdk_window;
+
+  /* release background */
+  if (window->background != NULL)
+    {
+      g_object_unref (G_OBJECT (window->background));
+      window->background = NULL;
+    }
+
+  /* allocate bg and set it on the window */
+  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+  window->background = thunar_desktop_background_new (gdk_window);
+}
+
+
+
+static void
+thunar_desktop_window_realize (GtkWidget *widget)
+{
+  GdkScreen *screen;
+  GdkWindow *root;
+  Window     xid;
+
+  GTK_WIDGET_CLASS (thunar_desktop_window_parent_class)->realize (widget);
+
+  screen = gtk_widget_get_screen (widget);
+  root = gdk_screen_get_root_window (screen);
+
+  /* tell the root window that we have a new "desktop" window */
+  xid = GDK_DRAWABLE_XID (gtk_widget_get_window (widget));
+  gdk_property_change (root,
+                       gdk_atom_intern ("NAUTILUS_DESKTOP_WINDOW_ID", FALSE),
+                       gdk_atom_intern ("WINDOW", FALSE), 32,
+                       GDK_PROP_MODE_REPLACE, (gpointer) &xid, 1);
+  gdk_property_change (root,
+                       gdk_atom_intern ("XFCE_DESKTOP_WINDOW", FALSE),
+                       gdk_atom_intern ("WINDOW", FALSE), 32,
+                       GDK_PROP_MODE_REPLACE, (gpointer) &xid, 1);
+
+  /* watch screen changes */
+  g_signal_connect_swapped (G_OBJECT (screen), "size-changed",
+    G_CALLBACK (thunar_desktop_window_screen_changed), widget);
+  g_signal_connect_swapped (G_OBJECT (screen), "monitors-changed",
+    G_CALLBACK (thunar_desktop_window_screen_changed), widget);
+
+  /* prepare bg */
+  thunar_desktop_window_screen_changed (screen, THUNAR_DESKTOP_WINDOW (widget));
+}
+
+
+
+static void
+thunar_desktop_window_unrealize (GtkWidget *widget)
+{
+  ThunarDesktopWindow *window = THUNAR_DESKTOP_WINDOW (widget);
+  GdkScreen           *screen;
+  GdkWindow           *root;
+
+  /* drop background */
+  if (window->background != NULL)
+    {
+      g_object_unref (G_OBJECT (window->background));
+      window->background = NULL;
+    }
+  gdk_window_set_back_pixmap (gtk_widget_get_window (widget), NULL, FALSE);
+
+  /* disconnect the XRandR support handler */
+  screen = gtk_widget_get_screen (widget);
+  g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
+      thunar_desktop_window_screen_changed, widget);
+
+  /* unset root window properties */
+  root = gdk_screen_get_root_window (screen);
+  gdk_property_delete (root, gdk_atom_intern ("NAUTILUS_DESKTOP_WINDOW_ID", FALSE));
+  gdk_property_delete (root, gdk_atom_intern ("XFCE_DESKTOP_WINDOW", FALSE));
+
+  GTK_WIDGET_CLASS (thunar_desktop_window_parent_class)->unrealize (widget);
+}
+
+
+
+static gboolean
+thunar_desktop_window_expose_event (GtkWidget      *widget,
+                                    GdkEventExpose *event)
+{
+  /* leave on multiple events */
+  if (event->count != 0)
+    return FALSE;
+
+  /* paint background */
+  gdk_window_clear_area (gtk_widget_get_window (widget),
+                         event->area.x, event->area.y,
+                         event->area.width, event->area.height);
+
+  return FALSE;
+}
+
+
+
+static gboolean
+thunar_desktop_window_button_press_event (GtkWidget      *widget,
+                                          GdkEventButton *event)
+{
+  ThunarDesktopWindow *window = THUNAR_DESKTOP_WINDOW (widget);
+  GtkWidget           *dialog;
+
+  if (event->type == GDK_BUTTON_PRESS)
+    {
+      if (event->button == 3
+          || (event->button == 1 && (event->state & GDK_SHIFT_MASK) != 0))
+        {
+          dialog = thunar_desktop_preferences_dialog_new (GTK_WINDOW (window));
+          gtk_widget_show (dialog);
+        }
+    }
+
+  return FALSE;
+}
+
+
+
+/**
+ * thunar_desktop_window_new:
+ *
+ * Allocates a new #ThunarDesktopWindow instance.
+ *
+ * Return value: the newly allocated #ThunarDesktopWindow.
+ **/
+GtkWidget*
+thunar_desktop_window_new (void)
+{
+  return thunar_desktop_window_new_with_screen (gdk_screen_get_default ());
+}
+
+
+
+/**
+ * thunar_desktop_window_new_with_screen:
+ * @screen : a #GdkScreen.
+ *
+ * Allocates a new #ThunarDesktopWindow instance and
+ * associates it with the given @screen.
+ *
+ * Return value: the newly allocated #ThunarDesktopWindow.
+ **/
+GtkWidget*
+thunar_desktop_window_new_with_screen (GdkScreen *screen)
+{
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+  return g_object_new (THUNAR_TYPE_DESKTOP_WINDOW,
+                       "screen", screen,
+                       NULL);
+}
diff --git a/thunar/thunar-desktop-window.h b/thunar/thunar-desktop-window.h
new file mode 100644
index 0000000..bcfb1d3
--- /dev/null
+++ b/thunar/thunar-desktop-window.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2006 Benedikt Meurer <benny 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 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
+ */
+
+#ifndef __THUNAR_DESKTOP_WINDOW_H__
+#define __THUNAR_DESKTOP_WINDOW_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ThunarDesktopWindowClass ThunarDesktopWindowClass;
+typedef struct _ThunarDesktopWindow      ThunarDesktopWindow;
+
+#define THUNAR_TYPE_DESKTOP_WINDOW            (thunar_desktop_window_get_type ())
+#define THUNAR_DESKTOP_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DESKTOP_WINDOW, ThunarDesktopWindow))
+#define THUNAR_DESKTOP_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DESKTOP_WINDOW, ThunarDesktopWindowClass))
+#define THUNAR_IS_DESKTOP_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DESKTOP_WINDOW))
+#define THUNAR_IS_DESKTOP_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_DESKTOP_WINDOW))
+#define THUNAR_DESKTOP_WINDOW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DESKTOP_WINDOW, ThunarDesktopWindowClass))
+
+GType      thunar_desktop_window_get_type         (void) G_GNUC_CONST;
+
+GtkWidget *thunar_desktop_window_new              (void);
+
+GtkWidget *thunar_desktop_window_new_with_screen  (GdkScreen *screen);
+
+G_END_DECLS
+
+#endif /* !__THUNAR_DESKTOP_WINDOW_H__ */
diff --git a/thunar/thunar-enum-types.c b/thunar/thunar-enum-types.c
index 1881598..a0793f8 100644
--- a/thunar/thunar-enum-types.c
+++ b/thunar/thunar-enum-types.c
@@ -252,26 +252,26 @@ thunar_icon_size_from_zoom_level (const GValue *src_value,
 GType
 thunar_job_response_get_type (void)
 {
-	static GType type = G_TYPE_INVALID;
+  static GType type = G_TYPE_INVALID;
 
   if (G_UNLIKELY (type == G_TYPE_INVALID))
     {
-	    static const GFlagsValue values[] = 
+      static const GFlagsValue values[] =
       {
-	      { THUNAR_JOB_RESPONSE_YES,     "THUNAR_JOB_RESPONSE_YES",     "yes"     },
-	      { THUNAR_JOB_RESPONSE_YES_ALL, "THUNAR_JOB_RESPONSE_YES_ALL", "yes-all" },
-	      { THUNAR_JOB_RESPONSE_NO,      "THUNAR_JOB_RESPONSE_NO",      "no"      },
-	      { THUNAR_JOB_RESPONSE_CANCEL,  "THUNAR_JOB_RESPONSE_CANCEL",  "cancel"  },
-	      { THUNAR_JOB_RESPONSE_NO_ALL,  "THUNAR_JOB_RESPONSE_NO_ALL",  "no-all"  },
-	      { THUNAR_JOB_RESPONSE_RETRY,   "THUNAR_JOB_RESPONSE_RETRY",   "retry"   },
-	      { THUNAR_JOB_RESPONSE_FORCE,   "THUNAR_JOB_RESPONSE_FORCE",   "force"   },
-	      { 0,                           NULL,                          NULL      }
-	    };
-
-	    type = g_flags_register_static (I_("ThunarJobResponse"), values);
+        { THUNAR_JOB_RESPONSE_YES,     "THUNAR_JOB_RESPONSE_YES",     "yes"     },
+        { THUNAR_JOB_RESPONSE_YES_ALL, "THUNAR_JOB_RESPONSE_YES_ALL", "yes-all" },
+        { THUNAR_JOB_RESPONSE_NO,      "THUNAR_JOB_RESPONSE_NO",      "no"      },
+        { THUNAR_JOB_RESPONSE_CANCEL,  "THUNAR_JOB_RESPONSE_CANCEL",  "cancel"  },
+        { THUNAR_JOB_RESPONSE_NO_ALL,  "THUNAR_JOB_RESPONSE_NO_ALL",  "no-all"  },
+        { THUNAR_JOB_RESPONSE_RETRY,   "THUNAR_JOB_RESPONSE_RETRY",   "retry"   },
+        { THUNAR_JOB_RESPONSE_FORCE,   "THUNAR_JOB_RESPONSE_FORCE",   "force"   },
+        { 0,                           NULL,                          NULL      }
+      };
+
+      type = g_flags_register_static (I_("ThunarJobResponse"), values);
     }
 
-	return type;
+  return type;
 }
 
 
@@ -279,31 +279,80 @@ thunar_job_response_get_type (void)
 GType
 thunar_file_mode_get_type (void)
 {
-	static GType type = G_TYPE_INVALID;
+  static GType type = G_TYPE_INVALID;
 
-	if (type == G_TYPE_INVALID) 
+  if (type == G_TYPE_INVALID)
     {
-	    static const GFlagsValue values[] = 
+      static const GFlagsValue values[] =
       {
-	      { THUNAR_FILE_MODE_SUID,      "THUNAR_FILE_MODE_SUID",      "suid"      },
-	      { THUNAR_FILE_MODE_SGID,      "THUNAR_FILE_MODE_SGID",      "sgid"      },
-	      { THUNAR_FILE_MODE_STICKY,    "THUNAR_FILE_MODE_STICKY",    "sticky"    },
-	      { THUNAR_FILE_MODE_USR_ALL,   "THUNAR_FILE_MODE_USR_ALL",   "usr-all"   },
-	      { THUNAR_FILE_MODE_USR_READ,  "THUNAR_FILE_MODE_USR_READ",  "usr-read"  },
-	      { THUNAR_FILE_MODE_USR_WRITE, "THUNAR_FILE_MODE_USR_WRITE", "usr-write" },
-	      { THUNAR_FILE_MODE_USR_EXEC,  "THUNAR_FILE_MODE_USR_EXEC",  "usr-exec"  },
-	      { THUNAR_FILE_MODE_GRP_ALL,   "THUNAR_FILE_MODE_GRP_ALL",   "grp-all"   },
-	      { THUNAR_FILE_MODE_GRP_READ,  "THUNAR_FILE_MODE_GRP_READ",  "grp-read"  },
-	      { THUNAR_FILE_MODE_GRP_WRITE, "THUNAR_FILE_MODE_GRP_WRITE", "grp-write" },
-	      { THUNAR_FILE_MODE_GRP_EXEC,  "THUNAR_FILE_MODE_GRP_EXEC",  "grp-exec"  },
-	      { THUNAR_FILE_MODE_OTH_ALL,   "THUNAR_FILE_MODE_OTH_ALL",   "oth-all"   },
-	      { THUNAR_FILE_MODE_OTH_READ,  "THUNAR_FILE_MODE_OTH_READ",  "oth-read"  },
-	      { THUNAR_FILE_MODE_OTH_WRITE, "THUNAR_FILE_MODE_OTH_WRITE", "oth-write" },
-	      { THUNAR_FILE_MODE_OTH_EXEC,  "THUNAR_FILE_MODE_OTH_EXEC",  "oth-exec"  },
-	      { 0,                          NULL,                         NULL        }
-	    };
-	    
+        { THUNAR_FILE_MODE_SUID,      "THUNAR_FILE_MODE_SUID",      "suid"      },
+        { THUNAR_FILE_MODE_SGID,      "THUNAR_FILE_MODE_SGID",      "sgid"      },
+        { THUNAR_FILE_MODE_STICKY,    "THUNAR_FILE_MODE_STICKY",    "sticky"    },
+        { THUNAR_FILE_MODE_USR_ALL,   "THUNAR_FILE_MODE_USR_ALL",   "usr-all"   },
+        { THUNAR_FILE_MODE_USR_READ,  "THUNAR_FILE_MODE_USR_READ",  "usr-read"  },
+        { THUNAR_FILE_MODE_USR_WRITE, "THUNAR_FILE_MODE_USR_WRITE", "usr-write" },
+        { THUNAR_FILE_MODE_USR_EXEC,  "THUNAR_FILE_MODE_USR_EXEC",  "usr-exec"  },
+        { THUNAR_FILE_MODE_GRP_ALL,   "THUNAR_FILE_MODE_GRP_ALL",   "grp-all"   },
+        { THUNAR_FILE_MODE_GRP_READ,  "THUNAR_FILE_MODE_GRP_READ",  "grp-read"  },
+        { THUNAR_FILE_MODE_GRP_WRITE, "THUNAR_FILE_MODE_GRP_WRITE", "grp-write" },
+        { THUNAR_FILE_MODE_GRP_EXEC,  "THUNAR_FILE_MODE_GRP_EXEC",  "grp-exec"  },
+        { THUNAR_FILE_MODE_OTH_ALL,   "THUNAR_FILE_MODE_OTH_ALL",   "oth-all"   },
+        { THUNAR_FILE_MODE_OTH_READ,  "THUNAR_FILE_MODE_OTH_READ",  "oth-read"  },
+        { THUNAR_FILE_MODE_OTH_WRITE, "THUNAR_FILE_MODE_OTH_WRITE", "oth-write" },
+        { THUNAR_FILE_MODE_OTH_EXEC,  "THUNAR_FILE_MODE_OTH_EXEC",  "oth-exec"  },
+        { 0,                          NULL,                         NULL        }
+      };
+
       type = g_flags_register_static ("ThunarFileMode", values);
     }
-	return type;
+  return type;
+}
+
+
+
+GType
+thunar_background_style_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (type == G_TYPE_INVALID)
+    {
+      static const GEnumValue values[] =
+      {
+        { THUNAR_BACKGROUND_STYLE_NONE,      "THUNAR_BACKGROUND_STYLE_NONE",      N_ ("None")      },
+        { THUNAR_BACKGROUND_STYLE_TILED,     "THUNAR_BACKGROUND_STYLE_TILED",     N_ ("Titled")    },
+        { THUNAR_BACKGROUND_STYLE_CENTERED,  "THUNAR_BACKGROUND_STYLE_CENTERED",  N_ ("Centered")  },
+        { THUNAR_BACKGROUND_STYLE_STRETCHED, "THUNAR_BACKGROUND_STYLE_STRETCHED", N_ ("Stretched") },
+        { THUNAR_BACKGROUND_STYLE_SCALED,    "THUNAR_BACKGROUND_STYLE_SCALED",    N_ ("Scaled")    },
+        { THUNAR_BACKGROUND_STYLE_ZOOMED,    "THUNAR_BACKGROUND_STYLE_ZOOMED",    N_ ("Zoomed")    },
+        { THUNAR_BACKGROUND_STYLE_SPANNED,   "THUNAR_BACKGROUND_STYLE_SPANNED",   N_ ("Spanned")   },
+        { 0,                                 NULL,                                NULL           }
+      };
+
+      type = g_enum_register_static ("ThunarBackgroundStyle", values);
+    }
+  return type;
+}
+
+
+
+GType
+thunar_background_color_style_get_type (void)
+{
+  static GType type = G_TYPE_INVALID;
+
+  if (type == G_TYPE_INVALID)
+    {
+      static const GEnumValue values[] =
+      {
+        { THUNAR_BACKGROUND_COLOR_STYLE_SOLID,      "THUNAR_BACKGROUND_COLOR_STYLE_SOLID",      N_ ("Solid")      },
+        { THUNAR_BACKGROUND_COLOR_STYLE_HORIZONTAL, "THUNAR_BACKGROUND_COLOR_STYLE_HORIZONTAL", N_ ("Horizontal") },
+        { THUNAR_BACKGROUND_COLOR_STYLE_VERTICAL,   "THUNAR_BACKGROUND_COLOR_STYLE_VERTICAL",   N_ ("Vertical")   },
+        { THUNAR_BACKGROUND_COLOR_STYLE_RADIAL,     "THUNAR_BACKGROUND_COLOR_STYLE_RADIAL",     N_ ("Radial")     },
+        { 0,                                       NULL,                                        NULL              }
+      };
+
+      type = g_enum_register_static ("ThunarBackgroundColorStyle", values);
+    }
+  return type;
 }
diff --git a/thunar/thunar-enum-types.h b/thunar/thunar-enum-types.h
index f1f2193..c787d51 100644
--- a/thunar/thunar-enum-types.h
+++ b/thunar/thunar-enum-types.h
@@ -270,6 +270,45 @@ typedef enum /*< flags >*/
 
 GType thunar_file_mode_get_type (void) G_GNUC_CONST;
 
+
+#define THUNAR_TYPE_BACKGROUND_STYLE (thunar_background_style_get_type ())
+
+/**
+ * ThunarBackgroundStyle:
+ *
+ * Transformations of the background image on the background
+ **/
+typedef enum /*< flags >*/
+{
+  THUNAR_BACKGROUND_STYLE_NONE,
+  THUNAR_BACKGROUND_STYLE_TILED,     /* no scaling, fill screen from 0,0 */
+  THUNAR_BACKGROUND_STYLE_CENTERED,  /* center on monitor once, no resizing */
+  THUNAR_BACKGROUND_STYLE_STRETCHED, /* scale to fill screen */
+  THUNAR_BACKGROUND_STYLE_SCALED,    /* scale to monitor, center */
+  THUNAR_BACKGROUND_STYLE_ZOOMED,    /* scale to monitor, crop + center */
+  THUNAR_BACKGROUND_STYLE_SPANNED,   /* scale to fit screen */
+} ThunarBackgroundStyle;
+
+GType thunar_background_style_get_type (void) G_GNUC_CONST;
+
+
+#define THUNAR_TYPE_BACKGROUND_COLOR_STYLE (thunar_background_color_style_get_type ())
+
+/**
+ * ThunarBackgroundColorStyle:
+ *
+ * Fill of the background color
+ **/
+typedef enum /*< flags >*/
+{
+  THUNAR_BACKGROUND_COLOR_STYLE_SOLID,
+  THUNAR_BACKGROUND_COLOR_STYLE_HORIZONTAL,
+  THUNAR_BACKGROUND_COLOR_STYLE_VERTICAL,
+  THUNAR_BACKGROUND_COLOR_STYLE_RADIAL,
+} ThunarBackgroundColorStyle;
+
+GType thunar_background_color_style_get_type (void) G_GNUC_CONST;
+
 G_END_DECLS;
 
 #endif /* !__THUNAR_ENUM_TYPES_H__ */
diff --git a/thunar/thunar-gtk-extensions.c b/thunar/thunar-gtk-extensions.c
index 1938a95..f158a33 100644
--- a/thunar/thunar-gtk-extensions.c
+++ b/thunar/thunar-gtk-extensions.c
@@ -199,8 +199,11 @@ thunar_gtk_ui_manager_get_action_by_name (GtkUIManager *ui_manager,
   GtkAction *action;
   GList     *lp;
 
-  _thunar_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), NULL);
+  _thunar_return_val_if_fail (ui_manager == NULL || GTK_IS_UI_MANAGER (ui_manager), NULL);
   _thunar_return_val_if_fail (action_name != NULL, NULL);
+  
+  if (ui_manager == NULL)
+    return NULL;
 
   /* check all action groups associated with the ui manager */
   for (lp = gtk_ui_manager_get_action_groups (ui_manager); lp != NULL; lp = lp->next)
diff --git a/thunar/thunar-list-model.c b/thunar/thunar-list-model.c
index 33b36fd..be87010 100644
--- a/thunar/thunar-list-model.c
+++ b/thunar/thunar-list-model.c
@@ -228,6 +228,9 @@ struct _ThunarListModel
   gboolean       sort_folders_first : 1;
   gint           sort_sign;   /* 1 = ascending, -1 descending */
   ThunarSortFunc sort_func;
+  
+  ThunarListModelVisibleFunc visible_func;
+  gpointer                   visible_data;
 };
 
 
@@ -1223,9 +1226,15 @@ thunar_list_model_files_added (ThunarFolder    *folder,
   /* process all added files */
   for (lp = files; lp != NULL; lp = lp->next)
     {
+      _thunar_return_if_fail (THUNAR_IS_FILE (lp->data));
+
+      /* skip files that do not pass the visible function */
+      if (store->visible_func != NULL
+          && !store->visible_func (lp->data, store->visible_data))
+        continue;
+
       /* take a reference on that file */
       file = g_object_ref (G_OBJECT (lp->data));
-      _thunar_return_if_fail (THUNAR_IS_FILE (file));
 
       /* check if the file should be hidden */
       if (!store->show_hidden && thunar_file_is_hidden (file))
@@ -1822,6 +1831,24 @@ thunar_list_model_set_folder (ThunarListModel *store,
 
 
 /**
+ * thunar_list_model_set_visible_func:
+ * @store: a #ThunarListModel.
+ * @func : filter function.
+ * @data: user data.
+ **/
+void
+thunar_list_model_set_visible_func (ThunarListModel            *store,
+                                    ThunarListModelVisibleFunc  func,
+                                    gpointer                    data)
+{
+  _thunar_return_if_fail (THUNAR_IS_LIST_MODEL (store));
+  store->visible_func = func;
+  store->visible_data = data;
+}
+
+
+
+/**
  * thunar_list_model_get_folders_first:
  * @store : a #ThunarListModel.
  *
diff --git a/thunar/thunar-list-model.h b/thunar/thunar-list-model.h
index 9392a02..57804bf 100644
--- a/thunar/thunar-list-model.h
+++ b/thunar/thunar-list-model.h
@@ -27,6 +27,10 @@ G_BEGIN_DECLS;
 typedef struct _ThunarListModelClass ThunarListModelClass;
 typedef struct _ThunarListModel      ThunarListModel;
 
+/* filter for removing files before they are added into the model */
+typedef gboolean (*ThunarListModelVisibleFunc) (ThunarFile *file,
+                                                gpointer    data);
+
 #define THUNAR_TYPE_LIST_MODEL            (thunar_list_model_get_type ())
 #define THUNAR_LIST_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_LIST_MODEL, ThunarListModel))
 #define THUNAR_LIST_MODEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_LIST_MODEL, ThunarListModelClass))
@@ -42,6 +46,10 @@ ThunarFolder    *thunar_list_model_get_folder             (ThunarListModel  *sto
 void             thunar_list_model_set_folder             (ThunarListModel  *store,
                                                            ThunarFolder     *folder);
 
+void             thunar_list_model_set_visible_func       (ThunarListModel            *store,
+                                                           ThunarListModelVisibleFunc  func,
+                                                           gpointer                    data);
+
 void             thunar_list_model_set_folders_first      (ThunarListModel  *store,
                                                            gboolean          folders_first);
 
diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c
index 69531b4..3f59a19 100644
--- a/thunar/thunar-standard-view.c
+++ b/thunar/thunar-standard-view.c
@@ -731,6 +731,7 @@ thunar_standard_view_constructor (GType                  type,
   GtkSortType         sort_order;
   GtkWidget          *view;
   GObject            *object;
+  const gchar        *zoom_level_property_name;
 
   /* let the GObject constructor create the instance */
   object = G_OBJECT_CLASS (thunar_standard_view_parent_class)->constructor (type,
@@ -741,11 +742,15 @@ thunar_standard_view_constructor (GType                  type,
   standard_view = THUNAR_STANDARD_VIEW (object);
 
   /* setup the default zoom-level, determined from the "last-<view>-zoom-level" preference */
-  g_object_get (G_OBJECT (standard_view->preferences), THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->zoom_level_property_name, &zoom_level, NULL);
-  thunar_view_set_zoom_level (THUNAR_VIEW (standard_view), zoom_level);
+  zoom_level_property_name = THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->zoom_level_property_name;
+  if (G_LIKELY (zoom_level_property_name != NULL))
+    {
+      g_object_get (G_OBJECT (standard_view->preferences), zoom_level_property_name, &zoom_level, NULL);
+      thunar_view_set_zoom_level (THUNAR_VIEW (standard_view), zoom_level);
 
-  /* save the "zoom-level" as "last-<view>-zoom-level" whenever the user changes the zoom level */
-  g_object_bind_property (object, "zoom-level", G_OBJECT (standard_view->preferences), THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->zoom_level_property_name, G_BINDING_DEFAULT);
+      /* save the "zoom-level" as "last-<view>-zoom-level" whenever the user changes the zoom level */
+      g_object_bind_property (object, "zoom-level", G_OBJECT (standard_view->preferences), zoom_level_property_name, G_BINDING_DEFAULT);
+    }
 
   /* determine the real view widget (treeview or iconview) */
   view = GTK_BIN (object)->child;
@@ -1690,6 +1695,9 @@ thunar_standard_view_reset_zoom_level (ThunarView *view)
 
   /* determine the default zoom level from the preferences */
   property_name = THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->zoom_level_property_name;
+  if (property_name == NULL)
+    return;
+
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (standard_view->preferences), property_name);
   g_value_init (&value, THUNAR_TYPE_ZOOM_LEVEL);
   g_param_value_set_default (pspec, &value);
@@ -4128,6 +4136,9 @@ thunar_standard_view_context_menu (ThunarStandardView *standard_view,
 
   _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view));
 
+  if (standard_view->ui_manager == NULL)
+    return;
+
   /* merge the custom menu actions for the selected items */
   selected_items = (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_selected_items) (standard_view);
   thunar_standard_view_merge_custom_actions (standard_view, selected_items);


More information about the Xfce4-commits mailing list