[Xfce4-commits] <thunar:nick/desktop> Make background basics work.
Nick Schermer
noreply at xfce.org
Wed Jul 31 19:32:03 CEST 2013
Updating branch refs/heads/nick/desktop
to 6e68c327798e83763c874146cf5c4af8e9e410cf (commit)
from 3d20b81250eddcd3df0b027478831b0f38817a1c (commit)
commit 6e68c327798e83763c874146cf5c4af8e9e410cf
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 cc5bcba..8e9989e 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 04846de..0a8c1b0 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 4c31953..f9c393e 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");
@@ -208,6 +212,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 92dcf09..0eed66e 100644
--- a/thunar/thunar-enum-types.c
+++ b/thunar/thunar-enum-types.c
@@ -251,26 +251,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;
}
@@ -278,31 +278,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 c372436..eb426cb 100644
--- a/thunar/thunar-enum-types.h
+++ b/thunar/thunar-enum-types.h
@@ -268,6 +268,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 6d5dd48..ffb66b4 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);
@@ -4152,6 +4160,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