summaryrefslogtreecommitdiff
path: root/capplets/common
diff options
context:
space:
mode:
Diffstat (limited to 'capplets/common')
-rw-r--r--capplets/common/Makefile.am63
-rw-r--r--capplets/common/activate-settings-daemon.c60
-rw-r--r--capplets/common/activate-settings-daemon.h9
-rw-r--r--capplets/common/capplet-stock-icons.c101
-rw-r--r--capplets/common/capplet-stock-icons.h66
-rw-r--r--capplets/common/capplet-util.c205
-rw-r--r--capplets/common/capplet-util.h46
-rw-r--r--capplets/common/file-transfer-dialog.c608
-rw-r--r--capplets/common/file-transfer-dialog.h73
-rw-r--r--capplets/common/gtkrc-utils.c256
-rw-r--r--capplets/common/gtkrc-utils.h25
-rw-r--r--capplets/common/mate-theme-apply.c136
-rw-r--r--capplets/common/mate-theme-apply.h33
-rw-r--r--capplets/common/mate-theme-info.c1988
-rw-r--r--capplets/common/mate-theme-info.h190
-rw-r--r--capplets/common/mate-theme-test.c134
-rw-r--r--capplets/common/mateconf-property-editor-marshal.c41
-rw-r--r--capplets/common/mateconf-property-editor-marshal.h19
-rw-r--r--capplets/common/mateconf-property-editor.c1801
-rw-r--r--capplets/common/mateconf-property-editor.h162
-rw-r--r--capplets/common/theme-thumbnail.c1144
-rw-r--r--capplets/common/theme-thumbnail.h37
-rw-r--r--capplets/common/wm-common.c184
-rw-r--r--capplets/common/wm-common.h17
24 files changed, 7398 insertions, 0 deletions
diff --git a/capplets/common/Makefile.am b/capplets/common/Makefile.am
new file mode 100644
index 00000000..127af950
--- /dev/null
+++ b/capplets/common/Makefile.am
@@ -0,0 +1,63 @@
+EXTRA_DIST =
+
+INCLUDES = \
+ -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \
+ -DMATELOCALEDIR="\"$(datadir)/locale\"" \
+ -DGTK_ENGINE_DIR="\"$(GTK_ENGINE_DIR)\"" \
+ -DG_LOG_DOMAIN=\"capplet-common\" \
+ -DINSTALL_PREFIX=\"$(prefix)\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libwindow-settings \
+ -DPIXMAP_DIR=\""$(datadir)/mate-control-center/pixmaps"\" \
+ $(CAPPLET_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(MATE_DESKTOP_CFLAGS) \
+ $(MARCO_CFLAGS) \
+ $(GSD_DBUS_CFLAGS) \
+ $(GIO_CFLAGS)
+
+
+noinst_LTLIBRARIES = libcommon.la
+
+libcommon_la_SOURCES = \
+ activate-settings-daemon.c \
+ activate-settings-daemon.h \
+ capplet-stock-icons.c \
+ capplet-stock-icons.h \
+ capplet-util.c \
+ capplet-util.h \
+ file-transfer-dialog.c \
+ file-transfer-dialog.h \
+ mateconf-property-editor.c \
+ mateconf-property-editor.h \
+ mateconf-property-editor-marshal.c \
+ mateconf-property-editor-marshal.h \
+ mate-theme-apply.c \
+ mate-theme-apply.h \
+ mate-theme-info.c \
+ mate-theme-info.h \
+ gtkrc-utils.c \
+ gtkrc-utils.h \
+ theme-thumbnail.c \
+ theme-thumbnail.h \
+ wm-common.c \
+ wm-common.h
+
+libcommon_la_LIBADD = \
+ $(top_builddir)/libwindow-settings/libmate-window-settings.la \
+ $(MARCO_LIBS) \
+ $(DBUS_LIBS) \
+ $(MATE_DESKTOP_LIBS) \
+ $(GIO_LIBS)
+
+mate_theme_test_SOURCES = \
+ mate-theme-test.c
+
+mate_theme_test_LDADD = \
+ libcommon.la \
+ $(MATECC_CAPPLETS_LIBS)
+
+noinst_PROGRAMS = \
+ mate-theme-test
+
+-include $(top_srcdir)/git.mk
diff --git a/capplets/common/activate-settings-daemon.c b/capplets/common/activate-settings-daemon.c
new file mode 100644
index 00000000..794f1098
--- /dev/null
+++ b/capplets/common/activate-settings-daemon.c
@@ -0,0 +1,60 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mate-settings-daemon/mate-settings-client.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "activate-settings-daemon.h"
+
+static void popup_error_message (void)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_OK, _("Unable to start the settings manager 'mate-settings-daemon'.\n"
+ "Without the MATE settings manager running, some preferences may not take effect. This could "
+ "indicate a problem with DBus, or a non-MATE (e.g. KDE) settings manager may already "
+ "be active and conflicting with the MATE settings manager."));
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+/* Returns FALSE if activation failed, else TRUE */
+gboolean
+activate_settings_daemon (void)
+{
+ DBusGConnection *connection = NULL;
+ DBusGProxy *proxy = NULL;
+ GError *error = NULL;
+
+ connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (connection == NULL)
+ {
+ popup_error_message ();
+ g_error_free (error);
+ return FALSE;
+ }
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ "org.mate.SettingsDaemon",
+ "/org/mate/SettingsDaemon",
+ "org.mate.SettingsDaemon");
+
+ if (proxy == NULL)
+ {
+ popup_error_message ();
+ return FALSE;
+ }
+
+ if (!org_mate_SettingsDaemon_awake(proxy, &error))
+ {
+ popup_error_message ();
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/capplets/common/activate-settings-daemon.h b/capplets/common/activate-settings-daemon.h
new file mode 100644
index 00000000..fc1558d8
--- /dev/null
+++ b/capplets/common/activate-settings-daemon.h
@@ -0,0 +1,9 @@
+#ifndef ACTIVATE_SETINGS_DAEMON
+#define ACTIVATE_SETINGS_DAEMON
+
+#include <glib.h>
+
+/* Returns FALSE if activation failed, else TRUE */
+gboolean activate_settings_daemon (void);
+
+#endif
diff --git a/capplets/common/capplet-stock-icons.c b/capplets/common/capplet-stock-icons.c
new file mode 100644
index 00000000..29b440f5
--- /dev/null
+++ b/capplets/common/capplet-stock-icons.c
@@ -0,0 +1,101 @@
+/*
+ * capplet-stock-icons.c
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Rajkumar Sivasamy <[email protected]>
+ * Taken bits of code from panel-stock-icons.c, Thanks Mark <[email protected]>
+ */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "capplet-stock-icons.h"
+
+static GtkIconSize mouse_capplet_dblclck_icon_size = 0;
+
+GtkIconSize
+mouse_capplet_dblclck_icon_get_size (void)
+{
+ return mouse_capplet_dblclck_icon_size;
+}
+
+typedef struct
+{
+ char *stock_id;
+ char *name;
+} CappletStockIcon;
+
+
+static CappletStockIcon items [] = {
+ { MOUSE_DBLCLCK_MAYBE, "double-click-maybe.png"},
+ { MOUSE_DBLCLCK_ON, "double-click-on.png"},
+ { MOUSE_DBLCLCK_OFF, "double-click-off.png"}
+};
+
+static void
+capplet_register_stock_icons (GtkIconFactory *factory)
+{
+ gint i;
+ GtkIconSource *source;
+
+ source = gtk_icon_source_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (items); ++i) {
+ GtkIconSet *icon_set;
+ char *filename;
+ filename = g_build_filename (PIXMAP_DIR, items[i].name, NULL);
+
+ if (!filename) {
+ g_warning (_("Unable to load stock icon '%s'\n"), items[i].name);
+ icon_set = gtk_icon_factory_lookup_default (GTK_STOCK_MISSING_IMAGE);
+ gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
+ continue;
+ }
+
+ gtk_icon_source_set_filename (source, filename);
+ g_free (filename);
+
+ icon_set = gtk_icon_set_new ();
+ gtk_icon_set_add_source (icon_set, source);
+ gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
+ gtk_icon_set_unref (icon_set);
+ }
+ gtk_icon_source_free (source);
+}
+
+void
+capplet_init_stock_icons (void)
+{
+ GtkIconFactory *factory;
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+ initialized = TRUE;
+
+ factory = gtk_icon_factory_new ();
+ gtk_icon_factory_add_default (factory);
+ capplet_register_stock_icons (factory);
+
+ mouse_capplet_dblclck_icon_size = gtk_icon_size_register ("mouse-capplet-dblclck-icon",
+ MOUSE_CAPPLET_DBLCLCK_ICON_SIZE,
+ MOUSE_CAPPLET_DBLCLCK_ICON_SIZE);
+ g_object_unref (factory);
+}
diff --git a/capplets/common/capplet-stock-icons.h b/capplets/common/capplet-stock-icons.h
new file mode 100644
index 00000000..8c954cf4
--- /dev/null
+++ b/capplets/common/capplet-stock-icons.h
@@ -0,0 +1,66 @@
+/*
+ * capplet-stock-icons.h
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Rajkumar Sivasamy <[email protected]>
+ * Taken bits of code from panel-stock-icons.h, Thanks Mark <[email protected]>
+ */
+
+#ifndef __CAPPLET_STOCK_ICONS_H__
+#define __CAPPLET_STOCK_ICONS_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KEYBOARD_CAPPLET_DEFAULT_ICON_SIZE 48
+#define MOUSE_CAPPLET_DEFAULT_WIDTH 120
+#define MOUSE_CAPPLET_DEFAULT_HEIGHT 100
+#define MOUSE_CAPPLET_DBLCLCK_ICON_SIZE 100
+
+/* stock icons */
+#define KEYBOARD_REPEAT "keyboard-repeat"
+#define KEYBOARD_CURSOR "keyboard-cursor"
+#define KEYBOARD_VOLUME "keyboard-volume"
+#define KEYBOARD_BELL "keyboard-bell"
+#define ACCESSX_KEYBOARD_BOUNCE "accessibility-keyboard-bouncekey"
+#define ACCESSX_KEYBOARD_SLOW "accessibility-keyboard-slowkey"
+#define ACCESSX_KEYBOARD_MOUSE "accessibility-keyboard-mousekey"
+#define ACCESSX_KEYBOARD_STICK "accessibility-keyboard-stickykey"
+#define ACCESSX_KEYBOARD_TOGGLE "accessibility-keyboard-togglekey"
+#define MOUSE_DBLCLCK_MAYBE "mouse-dblclck-maybe"
+#define MOUSE_DBLCLCK_ON "mouse-dblclck-on"
+#define MOUSE_DBLCLCK_OFF "mouse-dblclck-off"
+#define MOUSE_RIGHT_HANDED "mouse-right-handed"
+#define MOUSE_LEFT_HANDED "mouse-left-handed"
+
+void capplet_init_stock_icons (void);
+GtkIconSize keyboard_capplet_icon_get_size (void);
+GtkIconSize mouse_capplet_icon_get_size (void);
+GtkIconSize mouse_capplet_dblclck_icon_get_size (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CAPPLET_STOCK_ICONS_H__ */
diff --git a/capplets/common/capplet-util.c b/capplets/common/capplet-util.c
new file mode 100644
index 00000000..43d9485f
--- /dev/null
+++ b/capplets/common/capplet-util.c
@@ -0,0 +1,205 @@
+/* -*- mode: c; style: linux -*- */
+
+/* capplet-util.c
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Written by Bradford Hovinen <[email protected]>
+ *
+ * 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, 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 <ctype.h>
+
+/* For stat */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "capplet-util.h"
+
+static void
+capplet_error_dialog (GtkWindow *parent, char const *msg, GError *err)
+{
+ if (err != NULL) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ msg, err->message);
+
+ g_signal_connect (G_OBJECT (dialog),
+ "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_widget_show (dialog);
+ g_error_free (err);
+ }
+}
+
+/**
+ * capplet_help :
+ * @parent :
+ * @helpfile :
+ * @section :
+ *
+ * A quick utility routine to display help for capplets, and handle errors in a
+ * Havoc happy way.
+ **/
+void
+capplet_help (GtkWindow *parent, char const *section)
+{
+ GError *error = NULL;
+ char *uri;
+ GdkScreen *screen;
+
+ g_return_if_fail (section != NULL);
+
+ if (!parent)
+ screen = gdk_screen_get_default ();
+ else
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+
+ uri = g_strdup_printf ("ghelp:user-guide#%s", section);
+
+ if (!gtk_show_uri (screen, uri, gtk_get_current_event_time (), &error)) {
+ capplet_error_dialog (
+ parent,
+ _("There was an error displaying help: %s"),
+ error);
+ }
+
+ g_free (uri);
+}
+
+/**
+ * capplet_set_icon :
+ * @window :
+ * @file_name :
+ *
+ * A quick utility routine to avoid the cut-n-paste of bogus code
+ * that caused several bugs.
+ **/
+void
+capplet_set_icon (GtkWidget *window, char const *icon_file_name)
+{
+ /* Make sure that every window gets an icon */
+ gtk_window_set_default_icon_name (icon_file_name);
+ gtk_window_set_icon_name (GTK_WINDOW (window), icon_file_name);
+}
+
+static gboolean
+directory_delete_recursive (GFile *directory, GError **error)
+{
+ GFileEnumerator *enumerator;
+ GFileInfo *info;
+ gboolean success = TRUE;
+
+ enumerator = g_file_enumerate_children (directory,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, error);
+ if (enumerator == NULL)
+ return FALSE;
+
+ while (success &&
+ (info = g_file_enumerator_next_file (enumerator, NULL, NULL))) {
+ GFile *child;
+
+ child = g_file_get_child (directory, g_file_info_get_name (info));
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ success = directory_delete_recursive (child, error);
+ } else {
+ success = g_file_delete (child, NULL, error);
+ }
+ g_object_unref (info);
+ }
+ g_file_enumerator_close (enumerator, NULL, NULL);
+
+ if (success)
+ success = g_file_delete (directory, NULL, error);
+
+ return success;
+}
+
+/**
+ * capplet_file_delete_recursive :
+ * @file :
+ * @error :
+ *
+ * A utility routine to delete files and/or directories,
+ * including non-empty directories.
+ **/
+gboolean
+capplet_file_delete_recursive (GFile *file, GError **error)
+{
+ GFileInfo *info;
+ GFileType type;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, error);
+ if (info == NULL)
+ return FALSE;
+
+ type = g_file_info_get_file_type (info);
+ g_object_unref (info);
+
+ if (type == G_FILE_TYPE_DIRECTORY)
+ return directory_delete_recursive (file, error);
+ else
+ return g_file_delete (file, NULL, error);
+}
+
+void
+capplet_init (GOptionContext *context,
+ int *argc,
+ char ***argv)
+{
+ GError *err = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (context) {
+#if GLIB_CHECK_VERSION (2, 12, 0)
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+#endif
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+
+ if (!g_option_context_parse (context, argc, argv, &err)) {
+ g_printerr ("%s\n", err->message);
+ exit (1);
+ }
+ }
+
+ gtk_init (argc, argv);
+}
diff --git a/capplets/common/capplet-util.h b/capplets/common/capplet-util.h
new file mode 100644
index 00000000..b7caf4f0
--- /dev/null
+++ b/capplets/common/capplet-util.h
@@ -0,0 +1,46 @@
+/* -*- mode: c; style: linux -*- */
+
+/* capplet-util.h
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Written by Bradford Hovinen <[email protected]>
+ *
+ * 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, 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 __CAPPLET_UTIL_H
+#define __CAPPLET_UTIL_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <mateconf/mateconf.h>
+#include <mateconf/mateconf-changeset.h>
+
+/* Macros to make certain repetitive tasks a bit easier */
+
+/* Retrieve a widget from the UI object */
+
+#define WID(s) GTK_WIDGET (gtk_builder_get_object (dialog, s))
+
+/* Some miscellaneous functions useful to all capplets */
+
+void capplet_help (GtkWindow *parent, char const *section);
+void capplet_set_icon (GtkWidget *window, char const *icon_file_name);
+gboolean capplet_file_delete_recursive (GFile *directory, GError **error);
+void capplet_init (GOptionContext *context, int *argc, char ***argv);
+
+#endif /* __CAPPLET_UTIL_H */
diff --git a/capplets/common/file-transfer-dialog.c b/capplets/common/file-transfer-dialog.c
new file mode 100644
index 00000000..a698520f
--- /dev/null
+++ b/capplets/common/file-transfer-dialog.c
@@ -0,0 +1,608 @@
+/* file-transfer-dialog.c
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * Written by Rachel Hestilow <[email protected]>
+ * Jens Granseuer <[email protected]>
+ *
+ * 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, 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 <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <limits.h>
+
+#include "file-transfer-dialog.h"
+
+enum
+{
+ PROP_0,
+ PROP_FROM_URI,
+ PROP_TO_URI,
+ PROP_FRACTION_COMPLETE,
+ PROP_NTH_URI,
+ PROP_TOTAL_URIS,
+ PROP_PARENT
+};
+
+enum
+{
+ CANCEL,
+ DONE,
+ LAST_SIGNAL
+};
+
+guint file_transfer_dialog_signals[LAST_SIGNAL] = {0, };
+
+struct _FileTransferDialogPrivate
+{
+ GtkWidget *progress;
+ GtkWidget *status;
+ guint nth;
+ guint total;
+ GCancellable *cancellable;
+};
+
+#define FILE_TRANSFER_DIALOG_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), file_transfer_dialog_get_type (), FileTransferDialogPrivate))
+
+typedef struct _FileTransferJob
+{
+ FileTransferDialog *dialog;
+ GtkDialog *overwrite_dialog;
+ GSList *source_files;
+ GSList *target_files;
+ FileTransferDialogOptions options;
+} FileTransferJob;
+
+/* structure passed to the various callbacks */
+typedef struct {
+ FileTransferDialog *dialog;
+ gchar *source;
+ gchar *target;
+ guint current_file;
+ guint total_files;
+ goffset current_bytes;
+ goffset total_bytes;
+ gint response;
+ GtkDialog *overwrite_dialog;
+} FileTransferData;
+
+G_DEFINE_TYPE (FileTransferDialog, file_transfer_dialog, GTK_TYPE_DIALOG)
+
+static void
+file_transfer_dialog_update_num_files (FileTransferDialog *dlg)
+{
+ gchar *str;
+
+ if (dlg->priv->total <= 1)
+ return;
+
+ str = g_strdup_printf (_("Copying file: %u of %u"),
+ dlg->priv->nth, dlg->priv->total);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dlg->priv->progress), str);
+ g_free (str);
+}
+
+static void
+file_transfer_dialog_response (GtkDialog *dlg, gint response_id)
+{
+ FileTransferDialog *dialog = FILE_TRANSFER_DIALOG (dlg);
+
+ g_cancellable_cancel (dialog->priv->cancellable);
+}
+
+static void
+file_transfer_dialog_finalize (GObject *object)
+{
+ FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object);
+
+ if (dlg->priv->cancellable)
+ {
+ g_object_unref (dlg->priv->cancellable);
+ dlg->priv->cancellable = NULL;
+ }
+
+ G_OBJECT_CLASS (file_transfer_dialog_parent_class)->finalize (object);
+}
+
+static void
+file_transfer_dialog_set_prop (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object);
+ GFile *file;
+ gchar *str;
+ gchar *str2;
+ gchar *base;
+ gchar *escaped;
+ GtkWindow *parent;
+ guint n;
+
+ switch (prop_id)
+ {
+ case PROP_FROM_URI:
+ file = g_file_new_for_uri (g_value_get_string (value));
+ base = g_file_get_basename (file);
+ escaped = g_uri_escape_string (base, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+
+ str = g_strdup_printf (_("Copying '%s'"), escaped);
+ str2 = g_strdup_printf ("<big><b>%s</b></big>", str);
+ gtk_label_set_markup (GTK_LABEL (dlg->priv->status), str2);
+
+ g_free (base);
+ g_free (escaped);
+ g_free (str);
+ g_free (str2);
+ g_object_unref (file);
+ break;
+ case PROP_TO_URI:
+ break;
+ case PROP_FRACTION_COMPLETE:
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dlg->priv->progress), g_value_get_double (value));
+ break;
+ case PROP_NTH_URI:
+ n = g_value_get_uint (value);
+ if (n != dlg->priv->nth)
+ {
+ dlg->priv->nth = g_value_get_uint (value);
+ file_transfer_dialog_update_num_files (dlg);
+ }
+ break;
+ case PROP_TOTAL_URIS:
+ n = g_value_get_uint (value);
+ if (n != dlg->priv->nth)
+ {
+ dlg->priv->total = g_value_get_uint (value);
+ file_transfer_dialog_update_num_files (dlg);
+ }
+ break;
+ case PROP_PARENT:
+ parent = g_value_get_pointer (value);
+ if (parent)
+ {
+ gtk_window_set_title (GTK_WINDOW (dlg), gtk_window_get_title (parent));
+ gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
+ }
+ else
+ gtk_window_set_title (GTK_WINDOW (dlg),
+ _("Copying files"));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+file_transfer_dialog_get_prop (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object);
+
+ switch (prop_id)
+ {
+ case PROP_NTH_URI:
+ g_value_set_uint (value, dlg->priv->nth);
+ break;
+ case PROP_TOTAL_URIS:
+ g_value_set_uint (value, dlg->priv->total);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+file_transfer_dialog_class_init (FileTransferDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = file_transfer_dialog_finalize;
+ object_class->get_property = file_transfer_dialog_get_prop;
+ object_class->set_property = file_transfer_dialog_set_prop;
+
+ GTK_DIALOG_CLASS (klass)->response = file_transfer_dialog_response;
+
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_pointer ("parent",
+ _("Parent Window"),
+ _("Parent window of the dialog"),
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_FROM_URI,
+ g_param_spec_string ("from_uri",
+ _("From URI"),
+ _("URI currently transferring from"),
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_TO_URI,
+ g_param_spec_string ("to_uri",
+ _("To URI"),
+ _("URI currently transferring to"),
+ NULL,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_FRACTION_COMPLETE,
+ g_param_spec_double ("fraction_complete",
+ _("Fraction completed"),
+ _("Fraction of transfer currently completed"),
+ 0, 1, 0,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_NTH_URI,
+ g_param_spec_uint ("nth_uri",
+ _("Current URI index"),
+ _("Current URI index - starts from 1"),
+ 1, INT_MAX, 1,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_TOTAL_URIS,
+ g_param_spec_uint ("total_uris",
+ _("Total URIs"),
+ _("Total number of URIs"),
+ 1, INT_MAX, 1,
+ G_PARAM_READWRITE));
+
+ file_transfer_dialog_signals[CANCEL] =
+ g_signal_new ("cancel",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ file_transfer_dialog_signals[DONE] =
+ g_signal_new ("done",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (FileTransferDialogPrivate));
+}
+
+static void
+file_transfer_dialog_init (FileTransferDialog *dlg)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *progress_vbox;
+ GtkWidget *table;
+ char *markup;
+ GtkWidget *content_area;
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
+ dlg->priv = FILE_TRANSFER_DIALOG_GET_PRIVATE (dlg);
+ dlg->priv->cancellable = g_cancellable_new ();
+
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 4);
+ gtk_box_set_spacing (GTK_BOX (content_area), 4);
+
+ gtk_widget_set_size_request (GTK_WIDGET (dlg), 350, -1);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
+
+ dlg->priv->status = gtk_label_new (NULL);
+ markup = g_strconcat ("<big><b>", _("Copying files"), "</b></big>", NULL);
+ gtk_label_set_markup (GTK_LABEL (dlg->priv->status), markup);
+ g_free (markup);
+
+ gtk_misc_set_alignment (GTK_MISC (dlg->priv->status), 0.0, 0.0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), dlg->priv->status, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (table), FALSE, FALSE, 0);
+
+ progress_vbox = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), progress_vbox, FALSE, FALSE, 0);
+
+ dlg->priv->progress = gtk_progress_bar_new ();
+ gtk_box_pack_start (GTK_BOX (progress_vbox),
+ dlg->priv->progress, FALSE, FALSE, 0);
+
+ gtk_dialog_add_button (GTK_DIALOG (dlg),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), 6);
+
+ gtk_widget_show_all (content_area);
+}
+
+GtkWidget*
+file_transfer_dialog_new (void)
+{
+ return GTK_WIDGET (g_object_new (file_transfer_dialog_get_type (),
+ NULL));
+}
+
+GtkWidget*
+file_transfer_dialog_new_with_parent (GtkWindow *parent)
+{
+ return GTK_WIDGET (g_object_new (file_transfer_dialog_get_type (),
+ "parent", parent, NULL));
+}
+
+static gboolean
+file_transfer_job_update (gpointer user_data)
+{
+ FileTransferData *data = user_data;
+ gdouble fraction;
+ gdouble current_fraction;
+
+ if (data->total_bytes == 0)
+ current_fraction = 0.0;
+ else
+ current_fraction = ((gdouble) data->current_bytes) / data->total_bytes;
+
+ fraction = ((gdouble) data->current_file - 1) / data->total_files +
+ (1.0 / data->total_files) * current_fraction;
+
+ g_object_set (data->dialog,
+ "from_uri", data->source,
+ "to_uri", data->target,
+ "nth_uri", data->current_file,
+ "fraction_complete", fraction,
+ NULL);
+ return FALSE;
+}
+
+static void
+file_transfer_job_progress (goffset current_bytes,
+ goffset total_bytes,
+ gpointer user_data)
+{
+ FileTransferData *data = user_data;
+
+ data->current_bytes = current_bytes;
+ data->total_bytes = total_bytes;
+
+ gdk_threads_enter ();
+ file_transfer_job_update (data);
+ gdk_threads_leave ();
+}
+
+static void
+file_transfer_job_destroy (FileTransferJob *job)
+{
+ g_object_unref (job->dialog);
+ g_slist_foreach (job->source_files, (GFunc) g_object_unref, NULL);
+ g_slist_foreach (job->target_files, (GFunc) g_object_unref, NULL);
+ g_slist_free (job->source_files);
+ g_slist_free (job->target_files);
+ if (job->overwrite_dialog != NULL)
+ gtk_widget_destroy (GTK_WIDGET (job->overwrite_dialog));
+ g_free (job);
+}
+
+static gboolean
+file_transfer_dialog_done (FileTransferDialog *dialog)
+{
+ g_signal_emit (dialog,
+ file_transfer_dialog_signals[DONE],
+ 0, NULL);
+ return FALSE;
+}
+
+static gboolean
+file_transfer_dialog_cancel (FileTransferDialog *dialog)
+{
+ g_signal_emit (dialog,
+ file_transfer_dialog_signals[CANCEL],
+ 0, NULL);
+ return FALSE;
+}
+
+static gboolean
+file_transfer_dialog_overwrite (gpointer user_data)
+{
+ FileTransferData *data = user_data;
+ GtkDialog *dialog;
+
+ dialog = data->overwrite_dialog;
+
+ if (dialog != NULL) {
+ } else {
+ GtkWidget *button;
+
+ dialog = GTK_DIALOG (gtk_message_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("File '%s' already exists. Do you want to overwrite it?"),
+ data->target));
+
+ gtk_dialog_add_button (dialog, _("_Skip"), GTK_RESPONSE_NO);
+ gtk_dialog_add_button (dialog, _("Overwrite _All"), GTK_RESPONSE_APPLY);
+
+ button = gtk_button_new_with_label (_("_Overwrite"));
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_stock (GTK_STOCK_APPLY,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_dialog_add_action_widget (dialog, button, GTK_RESPONSE_YES);
+ gtk_widget_show (button);
+
+ data->overwrite_dialog = dialog;
+ }
+
+ data->response = gtk_dialog_run (dialog);
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ return FALSE;
+}
+
+/* TODO: support transferring directories recursively? */
+static gboolean
+file_transfer_job_schedule (GIOSchedulerJob *io_job,
+ GCancellable *cancellable,
+ FileTransferJob *job)
+{
+ GFile *source, *target;
+ gboolean success;
+ GFileCopyFlags copy_flags = G_FILE_COPY_NONE;
+ FileTransferData data;
+ GError *error;
+ gboolean retry;
+
+ /* take the first file from the list and copy it */
+ source = job->source_files->data;
+ job->source_files = g_slist_delete_link (job->source_files, job->source_files);
+
+ target = job->target_files->data;
+ job->target_files = g_slist_delete_link (job->target_files, job->target_files);
+
+ data.dialog = job->dialog;
+ data.overwrite_dialog = job->overwrite_dialog;
+ data.current_file = job->dialog->priv->nth + 1;
+ data.total_files = job->dialog->priv->total;
+ data.current_bytes = data.total_bytes = 0;
+ data.source = g_file_get_basename (source);
+ data.target = g_file_get_basename (target);
+
+ g_io_scheduler_job_send_to_mainloop (io_job,
+ file_transfer_job_update,
+ &data,
+ NULL);
+
+ if (job->options & FILE_TRANSFER_DIALOG_OVERWRITE)
+ copy_flags |= G_FILE_COPY_OVERWRITE;
+
+ do {
+ retry = FALSE;
+ error = NULL;
+ success = g_file_copy (source, target,
+ copy_flags,
+ job->dialog->priv->cancellable,
+ file_transfer_job_progress,
+ &data,
+ &error);
+
+ if (error != NULL)
+ {
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_EXISTS)
+ {
+ /* since the job is run in a thread, we cannot simply run
+ * a dialog here and need to defer it to the mainloop */
+ data.response = GTK_RESPONSE_NONE;
+ g_io_scheduler_job_send_to_mainloop (io_job,
+ file_transfer_dialog_overwrite,
+ &data,
+ NULL);
+
+ if (data.response == GTK_RESPONSE_YES) {
+ retry = TRUE;
+ copy_flags |= G_FILE_COPY_OVERWRITE;
+ } else if (data.response == GTK_RESPONSE_APPLY) {
+ retry = TRUE;
+ job->options |= FILE_TRANSFER_DIALOG_OVERWRITE;
+ copy_flags |= G_FILE_COPY_OVERWRITE;
+ } else {
+ success = TRUE;
+ }
+
+ job->overwrite_dialog = data.overwrite_dialog;
+ }
+ g_error_free (error);
+ }
+ } while (retry);
+
+ g_object_unref (source);
+ g_object_unref (target);
+
+ g_free (data.source);
+ g_free (data.target);
+
+ if (success)
+ {
+ if (job->source_files == NULL)
+ {
+ g_io_scheduler_job_send_to_mainloop_async (io_job,
+ (GSourceFunc) file_transfer_dialog_done,
+ g_object_ref (job->dialog),
+ g_object_unref);
+ return FALSE;
+ }
+ }
+ else /* error on copy or cancelled */
+ {
+ g_io_scheduler_job_send_to_mainloop_async (io_job,
+ (GSourceFunc) file_transfer_dialog_cancel,
+ g_object_ref (job->dialog),
+ g_object_unref);
+ return FALSE;
+ }
+
+ /* more work to do... */
+ return TRUE;
+}
+
+void
+file_transfer_dialog_copy_async (FileTransferDialog *dlg,
+ GList *source_files,
+ GList *target_files,
+ FileTransferDialogOptions options,
+ int priority)
+{
+ FileTransferJob *job;
+ GList *l;
+ guint n;
+
+ job = g_new0 (FileTransferJob, 1);
+ job->dialog = g_object_ref (dlg);
+ job->options = options;
+
+ /* we need to copy the list contents for private use */
+ n = 0;
+ for (l = g_list_last (source_files); l; l = l->prev, ++n)
+ {
+ job->source_files = g_slist_prepend (job->source_files,
+ g_object_ref (l->data));
+ }
+ for (l = g_list_last (target_files); l; l = l->prev)
+ {
+ job->target_files = g_slist_prepend (job->target_files,
+ g_object_ref (l->data));
+ }
+
+ g_object_set (dlg, "total_uris", n, NULL);
+
+ g_io_scheduler_push_job ((GIOSchedulerJobFunc) file_transfer_job_schedule,
+ job,
+ (GDestroyNotify) file_transfer_job_destroy,
+ priority,
+ dlg->priv->cancellable);
+}
diff --git a/capplets/common/file-transfer-dialog.h b/capplets/common/file-transfer-dialog.h
new file mode 100644
index 00000000..0b867005
--- /dev/null
+++ b/capplets/common/file-transfer-dialog.h
@@ -0,0 +1,73 @@
+/* -*- mode: c; style: linux -*- */
+
+/* file-transfer-dialog.h
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * Written by Rachel Hestilow <[email protected]>
+ *
+ * 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, 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 __FILE_TRANSFER_DIALOG_H__
+#define __FILE_TRANSFER_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FILE_TRANSFER_DIALOG(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, file_transfer_dialog_get_type (), FileTransferDialog)
+#define FILE_TRANSFER_DIALOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, file_transfer_dialog_get_type (), FileTransferDialogClass)
+#define IS_FILE_TRANSFER_DIALOG(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, file_transfer_dialog_get_type ())
+
+typedef struct _FileTransferDialog FileTransferDialog;
+typedef struct _FileTransferDialogClass FileTransferDialogClass;
+typedef struct _FileTransferDialogPrivate FileTransferDialogPrivate;
+
+typedef enum {
+ FILE_TRANSFER_DIALOG_DEFAULT = 1 << 0,
+ FILE_TRANSFER_DIALOG_OVERWRITE = 1 << 1
+} FileTransferDialogOptions;
+
+struct _FileTransferDialog
+{
+ GtkDialog dialog;
+
+ FileTransferDialogPrivate *priv;
+};
+
+struct _FileTransferDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+GType file_transfer_dialog_get_type (void);
+GtkWidget* file_transfer_dialog_new (void);
+GtkWidget* file_transfer_dialog_new_with_parent (GtkWindow *parent);
+
+void file_transfer_dialog_copy_async (FileTransferDialog *dlg,
+ GList *source_files,
+ GList *target_files,
+ FileTransferDialogOptions options,
+ int priority);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FILE_TRANSFER_DIALOG_H__ */
diff --git a/capplets/common/gtkrc-utils.c b/capplets/common/gtkrc-utils.c
new file mode 100644
index 00000000..be03faa5
--- /dev/null
+++ b/capplets/common/gtkrc-utils.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2007 The MATE Foundation
+ * Written by Thomas Wood <[email protected]>
+ * Jens Granseuer <[email protected]>
+ * All Rights Reserved
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <fcntl.h>
+#include <gtk/gtk.h>
+
+#define INCLUDE_SYMBOL ((gpointer) 1)
+#define ENGINE_SYMBOL ((gpointer) 2)
+#define COLOR_SCHEME_SYMBOL ((gpointer) 3)
+
+gchar* gtkrc_find_named(const gchar* name)
+{
+ /* find the gtkrc of the named theme
+ * taken from gtkrc.c (gtk_rc_parse_named)
+ */
+ gchar* path = NULL;
+ const gchar* home_dir;
+ const gchar* subpath = "gtk-2.0" G_DIR_SEPARATOR_S "gtkrc";
+
+ /* First look in the users home directory
+ */
+ home_dir = g_get_home_dir();
+
+ if (home_dir)
+ {
+ path = g_build_filename(home_dir, ".themes", name, subpath, NULL);
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (!path)
+ {
+ gchar* theme_dir = gtk_rc_get_theme_dir();
+ path = g_build_filename(theme_dir, name, subpath, NULL);
+ g_free(theme_dir);
+
+ if (!g_file_test(path, G_FILE_TEST_EXISTS))
+ {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ return path;
+
+}
+
+void gtkrc_get_details(gchar* filename, GSList** engines, GSList** symbolic_colors)
+{
+ gint file = -1;
+ GSList* files = NULL;
+ GSList* read_files = NULL;
+ GTokenType token;
+ GScanner *scanner = g_scanner_new (NULL);
+
+ g_scanner_scope_add_symbol (scanner, 0, "include", INCLUDE_SYMBOL);
+
+ if (engines != NULL)
+ {
+ g_scanner_scope_add_symbol (scanner, 0, "engine", ENGINE_SYMBOL);
+ }
+
+ files = g_slist_prepend (files, g_strdup (filename));
+
+ while (files != NULL)
+ {
+ filename = files->data;
+ files = g_slist_delete_link (files, files);
+
+ if (filename == NULL)
+ continue;
+
+ if (g_slist_find_custom (read_files, filename, (GCompareFunc) strcmp))
+ {
+ g_warning ("Recursion in the gtkrc detected!");
+ g_free (filename);
+ continue; /* skip this file since we've done it before... */
+ }
+
+ read_files = g_slist_prepend (read_files, filename);
+
+ file = g_open (filename, O_RDONLY);
+ if (file == -1)
+ {
+ g_warning ("Could not open file \"%s\"", filename);
+ }
+ else
+ {
+ g_scanner_input_file (scanner, file);
+ while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_EOF)
+ {
+ GTokenType string_token;
+ if (token == '@')
+ {
+ if (symbolic_colors == NULL)
+ continue;
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_IDENTIFIER)
+ continue;
+ if (!g_slist_find_custom (*symbolic_colors, scanner->value.v_identifier, (GCompareFunc) strcmp))
+ *symbolic_colors = g_slist_append (*symbolic_colors, g_strdup (scanner->value.v_identifier));
+ continue;
+ }
+
+ if (token != G_TOKEN_SYMBOL)
+ continue;
+
+ if (scanner->value.v_symbol == INCLUDE_SYMBOL)
+ {
+ string_token = g_scanner_get_next_token (scanner);
+ if (string_token != G_TOKEN_STRING)
+ continue;
+ if (g_path_is_absolute (scanner->value.v_string))
+ {
+ files = g_slist_prepend (files, g_strdup (scanner->value.v_string));
+ }
+ else
+ {
+ gchar *basedir = g_path_get_dirname (filename);
+ files = g_slist_prepend (files, g_build_path (G_DIR_SEPARATOR_S, basedir, scanner->value.v_string, NULL));
+ g_free (basedir);
+ }
+ }
+ else if (scanner->value.v_symbol == ENGINE_SYMBOL)
+ {
+ string_token = g_scanner_get_next_token (scanner);
+ if (string_token != G_TOKEN_STRING || scanner->value.v_string[0] == '\0')
+ continue;
+ if (!g_slist_find_custom (*engines, scanner->value.v_string, (GCompareFunc) strcmp))
+ *engines = g_slist_append (*engines, g_strdup (scanner->value.v_string));
+ }
+
+ }
+ close (file);
+ }
+ }
+
+ g_slist_foreach (read_files, (GFunc) g_free, NULL);
+ g_slist_free (read_files);
+
+ g_scanner_destroy (scanner);
+}
+
+
+gchar *
+gtkrc_get_color_scheme (const gchar *gtkrc_file)
+{
+ gint file = -1;
+ gchar *result = NULL;
+ GSList *files = NULL;
+ GSList *read_files = NULL;
+ GTokenType token;
+ GScanner *scanner = gtk_rc_scanner_new ();
+
+ g_scanner_scope_add_symbol (scanner, 0, "include", INCLUDE_SYMBOL);
+ g_scanner_scope_add_symbol (scanner, 0, "gtk_color_scheme", COLOR_SCHEME_SYMBOL);
+ g_scanner_scope_add_symbol (scanner, 0, "gtk-color-scheme", COLOR_SCHEME_SYMBOL);
+
+ files = g_slist_prepend (files, g_strdup (gtkrc_file));
+ while (files != NULL)
+ {
+ gchar *filename = files->data;
+ files = g_slist_delete_link (files, files);
+
+ if (filename == NULL)
+ continue;
+
+ if (g_slist_find_custom (read_files, filename, (GCompareFunc) strcmp))
+ {
+ g_warning ("Recursion in the gtkrc detected!");
+ g_free (filename);
+ continue; /* skip this file since we've done it before... */
+ }
+
+ read_files = g_slist_prepend (read_files, filename);
+
+ file = g_open (filename, O_RDONLY);
+ if (file == -1)
+ {
+ g_warning ("Could not open file \"%s\"", filename);
+ }
+ else
+ {
+ g_scanner_input_file (scanner, file);
+ while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_EOF)
+ {
+ if (GINT_TO_POINTER (token) == COLOR_SCHEME_SYMBOL)
+ {
+ if (g_scanner_get_next_token (scanner) == '=')
+ {
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_STRING)
+ {
+ g_free (result);
+ result = g_strdup (scanner->value.v_string);
+ }
+ }
+ }
+ }
+ close (file);
+ }
+ }
+
+ g_slist_foreach (read_files, (GFunc) g_free, NULL);
+ g_slist_free (read_files);
+
+ g_scanner_destroy (scanner);
+ return result;
+}
+
+gchar* gtkrc_get_color_scheme_for_theme(const gchar* theme_name)
+{
+ /* try to find the color scheme from the gtkrc */
+ gchar* gtkrc_file;
+ gchar* scheme = NULL;
+
+ gtkrc_file = gtkrc_find_named(theme_name);
+
+ if (gtkrc_file)
+ {
+ scheme = gtkrc_get_color_scheme(gtkrc_file);
+ g_free(gtkrc_file);
+ }
+
+ return scheme;
+}
diff --git a/capplets/common/gtkrc-utils.h b/capplets/common/gtkrc-utils.h
new file mode 100644
index 00000000..1023fe14
--- /dev/null
+++ b/capplets/common/gtkrc-utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The MATE Foundation
+ * Written by Thomas Wood <[email protected]>
+ * Jens Granseuer <[email protected]>
+ * All Rights Reserved
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+void gtkrc_get_details (gchar *filename, GSList **engines, GSList **symbolic_colors);
+gchar * gtkrc_find_named (const gchar *name);
+gchar * gtkrc_get_color_scheme (const gchar *filename);
+gchar * gtkrc_get_color_scheme_for_theme (const gchar *theme_name);
diff --git a/capplets/common/mate-theme-apply.c b/capplets/common/mate-theme-apply.c
new file mode 100644
index 00000000..b1c772e8
--- /dev/null
+++ b/capplets/common/mate-theme-apply.c
@@ -0,0 +1,136 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * themus - utilities for MATE themes
+ * Copyright (C) 2002 Jonathan Blandford <[email protected]>
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; 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 <string.h>
+#include <mateconf/mateconf-client.h>
+#include <mate-wm-manager.h>
+#include "mate-theme-apply.h"
+#include "gtkrc-utils.h"
+
+#define GTK_THEME_KEY "/desktop/mate/interface/gtk_theme"
+#define COLOR_SCHEME_KEY "/desktop/mate/interface/gtk_color_scheme"
+#define ICON_THEME_KEY "/desktop/mate/interface/icon_theme"
+#define FONT_KEY "/desktop/mate/interface/font_name"
+#define CURSOR_FONT_KEY "/desktop/mate/peripherals/mouse/cursor_font"
+#define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_theme"
+#define CURSOR_SIZE_KEY "/desktop/mate/peripherals/mouse/cursor_size"
+#define NOTIFICATION_THEME_KEY "/apps/notification-daemon/theme"
+
+#define compare(x,y) (!x && y) || (x && !y) || (x && y && strcmp (x, y))
+
+void
+mate_meta_theme_set (MateThemeMetaInfo *meta_theme_info)
+{
+ MateConfClient *client;
+ gchar *old_key;
+ gint old_key_int;
+ MateWindowManager *window_manager;
+ MateWMSettings wm_settings;
+
+ mate_wm_manager_init ();
+
+ window_manager = mate_wm_manager_get_current (gdk_display_get_default_screen (gdk_display_get_default ()));
+
+ client = mateconf_client_get_default ();
+
+ /* Set the gtk+ key */
+ old_key = mateconf_client_get_string (client, GTK_THEME_KEY, NULL);
+ if (compare (old_key, meta_theme_info->gtk_theme_name))
+ {
+ mateconf_client_set_string (client, GTK_THEME_KEY, meta_theme_info->gtk_theme_name, NULL);
+ }
+ g_free (old_key);
+
+ /* Set the color scheme key */
+ old_key = mateconf_client_get_string (client, COLOR_SCHEME_KEY, NULL);
+ if (compare (old_key, meta_theme_info->gtk_color_scheme))
+ {
+ /* only save the color scheme if it differs from the default
+ scheme for the selected gtk theme */
+ gchar *newval, *gtkcols;
+
+ newval = meta_theme_info->gtk_color_scheme;
+ gtkcols = gtkrc_get_color_scheme_for_theme (meta_theme_info->gtk_theme_name);
+
+ if (newval == NULL || !strcmp (newval, "") ||
+ mate_theme_color_scheme_equal (newval, gtkcols))
+ {
+ mateconf_client_unset (client, COLOR_SCHEME_KEY, NULL);
+ }
+ else
+ {
+ mateconf_client_set_string (client, COLOR_SCHEME_KEY, newval, NULL);
+ }
+ g_free (gtkcols);
+ }
+ g_free (old_key);
+
+ /* Set the wm key */
+ wm_settings.flags = MATE_WM_SETTING_THEME;
+ wm_settings.theme = meta_theme_info->marco_theme_name;
+ if (window_manager)
+ mate_window_manager_change_settings (window_manager, &wm_settings);
+
+ /* set the icon theme */
+ old_key = mateconf_client_get_string (client, ICON_THEME_KEY, NULL);
+ if (compare (old_key, meta_theme_info->icon_theme_name))
+ {
+ mateconf_client_set_string (client, ICON_THEME_KEY, meta_theme_info->icon_theme_name, NULL);
+ }
+ g_free (old_key);
+
+ /* set the notification theme */
+ if (meta_theme_info->notification_theme_name != NULL)
+ {
+ old_key = mateconf_client_get_string (client, NOTIFICATION_THEME_KEY, NULL);
+ if (compare (old_key, meta_theme_info->notification_theme_name))
+ {
+ mateconf_client_set_string (client, NOTIFICATION_THEME_KEY, meta_theme_info->notification_theme_name, NULL);
+ }
+ g_free (old_key);
+ }
+
+ /* Set the cursor theme key */
+#ifdef HAVE_XCURSOR
+ old_key = mateconf_client_get_string (client, CURSOR_THEME_KEY, NULL);
+ if (compare (old_key, meta_theme_info->cursor_theme_name))
+ {
+ mateconf_client_set_string (client, CURSOR_THEME_KEY, meta_theme_info->cursor_theme_name, NULL);
+ }
+
+ old_key_int = mateconf_client_get_int (client, CURSOR_SIZE_KEY, NULL);
+ if (old_key_int != meta_theme_info->cursor_size)
+ {
+ mateconf_client_set_int (client, CURSOR_SIZE_KEY, meta_theme_info->cursor_size, NULL);
+ }
+#else
+ old_key = mateconf_client_get_string (client, CURSOR_FONT_KEY, NULL);
+ if (compare (old_key, meta_theme_info->cursor_theme_name))
+ {
+ mateconf_client_set_string (client, CURSOR_FONT_KEY, meta_theme_info->cursor_theme_name, NULL);
+ }
+#endif
+
+ g_free (old_key);
+ g_object_unref (client);
+}
diff --git a/capplets/common/mate-theme-apply.h b/capplets/common/mate-theme-apply.h
new file mode 100644
index 00000000..d89d8d06
--- /dev/null
+++ b/capplets/common/mate-theme-apply.h
@@ -0,0 +1,33 @@
+/* mate-theme-info.h - MATE Theme information
+
+ Copyright (C) 2002 Jonathan Blandford <[email protected]>
+ All rights reserved.
+
+ This file is part of the Mate Library.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+/*
+ @NOTATION@
+ */
+
+#ifndef THEME_APPLY_H
+#define THEME_APPLY_H
+
+#include "mate-theme-info.h"
+
+void mate_meta_theme_set (MateThemeMetaInfo *meta_theme_info);
+
+#endif /* THEME_APPLY_H */
diff --git a/capplets/common/mate-theme-info.c b/capplets/common/mate-theme-info.c
new file mode 100644
index 00000000..aead0b34
--- /dev/null
+++ b/capplets/common/mate-theme-info.c
@@ -0,0 +1,1988 @@
+/* mate-theme-info.c - MATE Theme information
+ *
+ * Copyright (C) 2002 Jonathan Blandford <[email protected]>
+ * Copyright (C) 2011 Perberos
+ * All rights reserved.
+ *
+ * This file is part of the Mate Library.
+ *
+ * The Mate Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Mate Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Mate Library; see the file COPYING.LIB. 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 <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <glib/gi18n.h>
+#include <gmodule.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <libmate/mate-desktop-item.h>
+#include "mate-theme-info.h"
+#include "gtkrc-utils.h"
+
+#ifdef HAVE_XCURSOR
+ #include <X11/Xcursor/Xcursor.h>
+#endif
+
+#define THEME_NAME "X-GNOME-Metatheme/Name"
+#define THEME_COMMENT "X-GNOME-Metatheme/Comment"
+#define GTK_THEME_KEY "X-GNOME-Metatheme/GtkTheme"
+#define GTK_COLOR_SCHEME_KEY "X-GNOME-Metatheme/GtkColorScheme"
+#define MARCO_THEME_KEY "X-GNOME-Metatheme/MetacityTheme"
+#define ICON_THEME_KEY "X-GNOME-Metatheme/IconTheme"
+#define CURSOR_THEME_KEY "X-GNOME-Metatheme/CursorTheme"
+#define NOTIFICATION_THEME_KEY "X-GNOME-Metatheme/NotificationTheme"
+#define CURSOR_SIZE_KEY "X-GNOME-Metatheme/CursorSize"
+#define SOUND_THEME_KEY "X-GNOME-Metatheme/SoundTheme"
+#define APPLICATION_FONT_KEY "X-GNOME-Metatheme/ApplicationFont"
+#define DOCUMENTS_FONT_KEY "X-GNOME-Metatheme/DocumentsFont"
+#define DESKTOP_FONT_KEY "X-GNOME-Metatheme/DesktopFont"
+#define WINDOWTITLE_FONT_KEY "X-GNOME-Metatheme/WindowTitleFont"
+#define MONOSPACE_FONT_KEY "X-GNOME-Metatheme/MonospaceFont"
+#define BACKGROUND_IMAGE_KEY "X-GNOME-Metatheme/BackgroundImage"
+#define HIDDEN_KEY "X-GNOME-Metatheme/Hidden"
+
+/* Terminology used in this lib:
+ *
+ * /usr/share/themes, ~/.themes -- top_theme_dir
+ * top_theme_dir/theme_name/ -- common_theme_dir
+ * /usr/share/icons, ~/.icons -- top_icon_theme_dir
+ * top_icon_theme_dir/theme_name/ -- icon_common_theme_dir
+ *
+ */
+
+typedef struct _ThemeCallbackData {
+ ThemeChangedCallback func;
+ gpointer data;
+} ThemeCallbackData;
+
+typedef struct {
+ GFileMonitor* common_theme_dir_handle;
+ GFileMonitor* gtk2_dir_handle;
+ GFileMonitor* keybinding_dir_handle;
+ GFileMonitor* marco_dir_handle;
+ gint priority;
+} CommonThemeDirMonitorData;
+
+typedef struct {
+ GFileMonitor* common_icon_theme_dir_handle;
+ gint priority;
+} CommonIconThemeDirMonitorData;
+
+typedef struct {
+ GHashTable* handle_hash;
+ gint priority;
+} CallbackTuple;
+
+
+/* Hash tables */
+
+/* The hashes_by_dir are indexed by an escaped uri of the common_theme_dir that
+ * that particular theme is part of. The data pointed to by them is a
+ * MateTheme{Meta,Icon,}Info struct. Note that the uri is of the form
+ * "file:///home/username/.themes/foo", and not "/home/username/.themes/foo"
+ */
+
+/* The hashes_by_name are hashed by the index of the theme. The data pointed to
+ * by them is a GList whose data elements are MateTheme{Meta,Icon,}Info
+ * structs. This is because a theme can be found both in the users ~/.theme as
+ * well as globally in $prefix. All access to them must be done via helper
+ * functions.
+ */
+static GList* callbacks = NULL;
+
+static GHashTable* meta_theme_hash_by_uri;
+static GHashTable* meta_theme_hash_by_name;
+static GHashTable* icon_theme_hash_by_uri;
+static GHashTable* icon_theme_hash_by_name;
+static GHashTable* cursor_theme_hash_by_uri;
+static GHashTable* cursor_theme_hash_by_name;
+static GHashTable* theme_hash_by_uri;
+static GHashTable* theme_hash_by_name;
+static gboolean initting = FALSE;
+
+/* private functions */
+static gint safe_strcmp(const gchar* a_str, const gchar* b_str)
+{
+ if (a_str && b_str)
+ {
+ return strcmp(a_str, b_str);
+ }
+ else
+ {
+ return a_str - b_str;
+ }
+}
+
+static GFileType
+get_file_type (GFile *file)
+{
+ GFileType file_type = G_FILE_TYPE_UNKNOWN;
+ GFileInfo *file_info;
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (file_info != NULL) {
+ file_type = g_file_info_get_file_type (file_info);
+ g_object_unref (file_info);
+ }
+
+ return file_type;
+}
+
+static void
+add_theme_to_hash_by_name (GHashTable *hash_table,
+ gpointer data)
+{
+ MateThemeCommonInfo *info = data;
+ GList *list;
+
+ list = g_hash_table_lookup (hash_table, info->name);
+ if (list == NULL) {
+ list = g_list_append (list, info);
+ } else {
+ GList *list_ptr = list;
+ gboolean added = FALSE;
+
+ while (list_ptr) {
+ gint theme_priority;
+
+ theme_priority = ((MateThemeCommonInfo *) list_ptr->data)->priority;
+
+ if (theme_priority == info->priority) {
+ /* Swap it in */
+ list_ptr->data = info;
+ added = TRUE;
+ break;
+ } else if (theme_priority > info->priority) {
+ list = g_list_insert_before (list, list_ptr, info);
+ added = TRUE;
+ break;
+ }
+ list_ptr = list_ptr->next;
+ }
+ if (!added)
+ list = g_list_append (list, info);
+ }
+ g_hash_table_insert (hash_table, g_strdup (info->name), list);
+}
+
+static void
+remove_theme_from_hash_by_name (GHashTable *hash_table,
+ gpointer data)
+{
+ MateThemeCommonInfo *info = data;
+ GList *list;
+
+ list = g_hash_table_lookup (hash_table, info->name);
+
+ list = g_list_remove (list, info);
+ if (list == NULL)
+ g_hash_table_remove (hash_table, info->name);
+ else
+ g_hash_table_insert (hash_table, g_strdup (info->name), list);
+}
+
+static MateThemeCommonInfo *
+get_theme_from_hash_by_name (GHashTable *hash_table,
+ const gchar *name,
+ gint priority)
+{
+ GList *list;
+
+ list = g_hash_table_lookup (hash_table, name);
+
+ /* -1 implies return the first one */
+ if (priority == -1) {
+ return list ? list->data : NULL;
+ }
+
+ while (list) {
+ MateThemeCommonInfo *info = (MateThemeCommonInfo *) list->data;
+
+ if (info->priority == priority)
+ return info;
+
+ list = list->next;
+ }
+ return NULL;
+}
+
+static gint
+theme_compare (MateThemeCommonInfo *a,
+ MateThemeCommonInfo *b)
+{
+ gint cmp;
+
+ g_return_val_if_fail (a->type == b->type, a->type - b->type);
+
+ switch (a->type) {
+ case MATE_THEME_TYPE_METATHEME:
+ cmp = mate_theme_meta_info_compare (
+ (MateThemeMetaInfo *) a, (MateThemeMetaInfo *) b);
+ break;
+ case MATE_THEME_TYPE_ICON:
+ cmp = mate_theme_icon_info_compare (
+ (MateThemeIconInfo *) a, (MateThemeIconInfo *) b);
+ break;
+ case MATE_THEME_TYPE_CURSOR:
+ cmp = mate_theme_cursor_info_compare (
+ (MateThemeCursorInfo *) a, (MateThemeCursorInfo *) b);
+ break;
+ default:
+ /* not supported at this time */
+ g_assert_not_reached ();
+ }
+
+ return cmp;
+}
+
+static void
+theme_free (MateThemeCommonInfo *info)
+{
+ switch (info->type) {
+ case MATE_THEME_TYPE_METATHEME:
+ mate_theme_meta_info_free ((MateThemeMetaInfo *) info);
+ break;
+ case MATE_THEME_TYPE_ICON:
+ mate_theme_icon_info_free ((MateThemeIconInfo *) info);
+ break;
+ case MATE_THEME_TYPE_REGULAR:
+ mate_theme_info_free ((MateThemeInfo *) info);
+ break;
+ case MATE_THEME_TYPE_CURSOR:
+ mate_theme_cursor_info_free ((MateThemeCursorInfo *) info);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+GQuark mate_theme_info_error_quark(void)
+{
+ return g_quark_from_static_string("mate-theme-info-error-quark");
+}
+
+MateThemeMetaInfo* mate_theme_read_meta_theme(GFile* meta_theme_uri)
+{
+ MateThemeMetaInfo* meta_theme_info;
+ GFile* common_theme_dir_uri;
+ MateDesktopItem* meta_theme_ditem;
+ gchar* meta_theme_file;
+ const gchar* str;
+ gchar* scheme;
+
+ meta_theme_file = g_file_get_uri(meta_theme_uri);
+ meta_theme_ditem = mate_desktop_item_new_from_uri(meta_theme_file, 0, NULL);
+ g_free(meta_theme_file);
+
+ if (meta_theme_ditem == NULL)
+ return NULL;
+
+ common_theme_dir_uri = g_file_get_parent(meta_theme_uri);
+ meta_theme_info = mate_theme_meta_info_new();
+ meta_theme_info->path = g_file_get_path(meta_theme_uri);
+ meta_theme_info->name = g_file_get_basename(common_theme_dir_uri);
+ g_object_unref(common_theme_dir_uri);
+
+ str = mate_desktop_item_get_localestring(meta_theme_ditem, THEME_NAME);
+
+ if (!str)
+ {
+ str = mate_desktop_item_get_localestring(meta_theme_ditem, MATE_DESKTOP_ITEM_NAME);
+ if (!str)
+ { /* shouldn't reach */
+ mate_theme_meta_info_free(meta_theme_info);
+ return NULL;
+ }
+ }
+
+ meta_theme_info->readable_name = g_strdup(str);
+
+ str = mate_desktop_item_get_localestring(meta_theme_ditem, THEME_COMMENT);
+
+ if (str == NULL)
+ str = mate_desktop_item_get_localestring(meta_theme_ditem, MATE_DESKTOP_ITEM_COMMENT);
+
+ if (str != NULL)
+ meta_theme_info->comment = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, MATE_DESKTOP_ITEM_ICON);
+
+ if (str != NULL)
+ meta_theme_info->icon_file = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, GTK_THEME_KEY);
+
+ if (str == NULL)
+ {
+ mate_theme_meta_info_free(meta_theme_info);
+ return NULL;
+ }
+ meta_theme_info->gtk_theme_name = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, GTK_COLOR_SCHEME_KEY);
+
+ if (str == NULL || str[0] == '\0')
+ scheme = gtkrc_get_color_scheme_for_theme(meta_theme_info->gtk_theme_name);
+ else
+ scheme = g_strdup(str);
+
+ if (scheme != NULL)
+ {
+ meta_theme_info->gtk_color_scheme = scheme;
+
+ for (; *scheme != '\0'; scheme++)
+ if (*scheme == ',')
+ *scheme = '\n';
+ }
+
+ str = mate_desktop_item_get_string (meta_theme_ditem, MARCO_THEME_KEY);
+
+ if (str == NULL)
+ {
+ mate_theme_meta_info_free (meta_theme_info);
+ return NULL;
+ }
+
+ meta_theme_info->marco_theme_name = g_strdup (str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, ICON_THEME_KEY);
+
+ if (str == NULL)
+ {
+ mate_theme_meta_info_free(meta_theme_info);
+ return NULL;
+ }
+
+ meta_theme_info->icon_theme_name = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, NOTIFICATION_THEME_KEY);
+
+ if (str != NULL)
+ meta_theme_info->notification_theme_name = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, CURSOR_THEME_KEY);
+
+ if (str != NULL)
+ {
+ meta_theme_info->cursor_theme_name = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, CURSOR_SIZE_KEY);
+
+ if (str)
+ meta_theme_info->cursor_size = (int) g_ascii_strtoll(str, NULL, 10);
+ else
+ meta_theme_info->cursor_size = 18;
+ }
+ else
+ {
+ meta_theme_info->cursor_theme_name = g_strdup("default");
+ meta_theme_info->cursor_size = 18;
+ }
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, APPLICATION_FONT_KEY);
+
+ if (str != NULL)
+ meta_theme_info->application_font = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, DOCUMENTS_FONT_KEY);
+
+ if (str != NULL)
+ meta_theme_info->documents_font = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, DESKTOP_FONT_KEY);
+
+ if (str != NULL)
+ meta_theme_info->desktop_font = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, WINDOWTITLE_FONT_KEY);
+
+ if (str != NULL)
+ meta_theme_info->windowtitle_font = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, MONOSPACE_FONT_KEY);
+
+ if (str != NULL)
+ meta_theme_info->monospace_font = g_strdup(str);
+
+ str = mate_desktop_item_get_string(meta_theme_ditem, BACKGROUND_IMAGE_KEY);
+
+ if (str != NULL)
+ meta_theme_info->background_image = g_strdup(str);
+
+ meta_theme_info->hidden = mate_desktop_item_get_boolean(meta_theme_ditem, HIDDEN_KEY);
+
+ mate_desktop_item_unref(meta_theme_ditem);
+
+ return meta_theme_info;
+}
+
+static MateThemeIconInfo *
+read_icon_theme (GFile *icon_theme_uri)
+{
+ MateThemeIconInfo *icon_theme_info;
+ MateDesktopItem *icon_theme_ditem;
+ gchar *icon_theme_file;
+ gchar *dir_name;
+ const gchar *name;
+ const gchar *directories;
+
+ icon_theme_file = g_file_get_uri (icon_theme_uri);
+ icon_theme_ditem = mate_desktop_item_new_from_uri (icon_theme_file, 0, NULL);
+ g_free (icon_theme_file);
+
+ if (icon_theme_ditem == NULL)
+ return NULL;
+
+ name = mate_desktop_item_get_localestring (icon_theme_ditem, "Icon Theme/Name");
+ if (!name) {
+ name = mate_desktop_item_get_localestring (icon_theme_ditem, MATE_DESKTOP_ITEM_NAME);
+ if (!name) {
+ mate_desktop_item_unref (icon_theme_ditem);
+ return NULL;
+ }
+ }
+
+ /* If index.theme has no Directories entry, it is only a cursor theme */
+ directories = mate_desktop_item_get_string (icon_theme_ditem, "Icon Theme/Directories");
+ if (directories == NULL) {
+ mate_desktop_item_unref (icon_theme_ditem);
+ return NULL;
+ }
+
+ icon_theme_info = mate_theme_icon_info_new ();
+ icon_theme_info->readable_name = g_strdup (name);
+ icon_theme_info->path = g_file_get_path (icon_theme_uri);
+ icon_theme_info->hidden = mate_desktop_item_get_boolean (icon_theme_ditem, "Icon Theme/Hidden");
+ dir_name = g_path_get_dirname (icon_theme_info->path);
+ icon_theme_info->name = g_path_get_basename (dir_name);
+ g_free (dir_name);
+
+ mate_desktop_item_unref (icon_theme_ditem);
+
+ return icon_theme_info;
+}
+
+#ifdef HAVE_XCURSOR
+static void
+add_default_cursor_theme ()
+{
+ MateThemeCursorInfo *theme_info;
+
+ theme_info = mate_theme_cursor_info_new ();
+ theme_info->path = g_strdup ("builtin");
+ theme_info->name = g_strdup ("default");
+ theme_info->readable_name = g_strdup (_("Default Pointer"));
+ theme_info->sizes = g_array_sized_new (FALSE, FALSE, sizeof (gint), 0);
+
+ g_hash_table_insert (cursor_theme_hash_by_uri, theme_info->path, theme_info);
+ add_theme_to_hash_by_name (cursor_theme_hash_by_name, theme_info);
+}
+
+static GdkPixbuf *
+gdk_pixbuf_from_xcursor_image (XcursorImage *cursor)
+{
+ GdkPixbuf *pixbuf;
+#define BUF_SIZE sizeof(guint32) * cursor->width * cursor->height
+ guchar *buf = g_malloc0 (BUF_SIZE);
+ guchar *it;
+
+ for (it = buf; it < (buf + BUF_SIZE); it += 4) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ /* on little endianess it's BGRA to RGBA */
+ it[0] = ((guchar *) (cursor->pixels))[it - buf + 2];
+ it[1] = ((guchar *) (cursor->pixels))[it - buf + 1];
+ it[2] = ((guchar *) (cursor->pixels))[it - buf + 0];
+ it[3] = ((guchar *) (cursor->pixels))[it - buf + 3];
+#else
+ /* on big endianess it's ARGB to RGBA */
+ it[0] = ((guchar *) cursor->pixels)[it - buf + 1];
+ it[1] = ((guchar *) cursor->pixels)[it - buf + 2];
+ it[2] = ((guchar *) cursor->pixels)[it - buf + 3];
+ it[3] = ((guchar *) cursor->pixels)[it - buf + 0];
+#endif
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data ((const guchar *) buf,
+ GDK_COLORSPACE_RGB, TRUE, 8,
+ cursor->width, cursor->height,
+ cursor->width * 4,
+ (GdkPixbufDestroyNotify) g_free,
+ NULL);
+
+ if (!pixbuf)
+ g_free (buf);
+
+ return pixbuf;
+}
+
+static MateThemeCursorInfo *
+read_cursor_theme (GFile *cursor_theme_uri)
+{
+ MateThemeCursorInfo *cursor_theme_info = NULL;
+ GFile *parent_uri, *cursors_uri;
+
+ const gint filter_sizes[] = { 12, 16, 24, 32, 36, 40, 48, 64 };
+ const gint num_sizes = G_N_ELEMENTS (filter_sizes);
+
+ parent_uri = g_file_get_parent (cursor_theme_uri);
+ cursors_uri = g_file_get_child (parent_uri, "cursors");
+
+ if (get_file_type (cursors_uri) == G_FILE_TYPE_DIRECTORY) {
+ GArray *sizes;
+ XcursorImage *cursor;
+ GdkPixbuf *thumbnail = NULL;
+ gchar *name;
+ gint i;
+
+ name = g_file_get_basename (parent_uri);
+
+ sizes = g_array_sized_new (FALSE, FALSE, sizeof (gint), num_sizes);
+
+ for (i = 0; i < num_sizes; ++i) {
+ cursor = XcursorLibraryLoadImage ("left_ptr", name, filter_sizes[i]);
+
+ if (cursor) {
+ if (cursor->size == filter_sizes[i]) {
+ g_array_append_val (sizes, filter_sizes[i]);
+
+ if (thumbnail == NULL && i >= 1)
+ thumbnail = gdk_pixbuf_from_xcursor_image (cursor);
+ }
+
+ XcursorImageDestroy (cursor);
+ }
+ }
+
+ if (sizes->len == 0) {
+ g_array_free (sizes, TRUE);
+ g_free (name);
+ } else {
+ MateDesktopItem *cursor_theme_ditem;
+ gchar *cursor_theme_file;
+
+ if (!thumbnail) {
+ cursor = XcursorLibraryLoadImage ("left_ptr", name,
+ g_array_index (sizes, gint, 0));
+ if (cursor) {
+ thumbnail = gdk_pixbuf_from_xcursor_image (cursor);
+ XcursorImageDestroy (cursor);
+ }
+ }
+
+ cursor_theme_info = mate_theme_cursor_info_new ();
+ cursor_theme_info->path = g_file_get_path (parent_uri);
+ cursor_theme_info->name = name;
+ cursor_theme_info->sizes = sizes;
+ cursor_theme_info->thumbnail = thumbnail;
+
+ cursor_theme_file = g_file_get_path (cursor_theme_uri);
+ cursor_theme_ditem = mate_desktop_item_new_from_file (cursor_theme_file, 0, NULL);
+ g_free (cursor_theme_file);
+
+ if (cursor_theme_ditem != NULL) {
+ const gchar *readable_name;
+
+ readable_name = mate_desktop_item_get_string (cursor_theme_ditem,
+ "Icon Theme/Name");
+ if (readable_name)
+ cursor_theme_info->readable_name = g_strdup (readable_name);
+ else
+ cursor_theme_info->readable_name = g_strdup (name);
+
+ cursor_theme_info->hidden = mate_desktop_item_get_boolean (cursor_theme_ditem,
+ "Icon Theme/Hidden");
+
+ mate_desktop_item_unref (cursor_theme_ditem);
+ } else {
+ cursor_theme_info->readable_name = g_strdup (name);
+ }
+ }
+ }
+
+ g_object_unref (cursors_uri);
+ g_object_unref (parent_uri);
+
+ return cursor_theme_info;
+}
+
+#else /* !HAVE_XCURSOR */
+
+static gchar *
+read_current_cursor_font (void)
+{
+ DIR *dir;
+ gchar *dir_name;
+ struct dirent *file_dirent;
+
+ dir_name = g_build_filename (g_get_home_dir (), ".mate2/share/cursor-fonts", NULL);
+ if (! g_file_test (dir_name, G_FILE_TEST_EXISTS)) {
+ g_free (dir_name);
+ return NULL;
+ }
+
+ dir = opendir (dir_name);
+
+ while ((file_dirent = readdir (dir)) != NULL) {
+ struct stat st;
+ gchar *link_name;
+
+ link_name = g_build_filename (dir_name, file_dirent->d_name, NULL);
+ if (lstat (link_name, &st)) {
+ g_free (link_name);
+ continue;
+ }
+
+ if (S_ISLNK (st.st_mode)) {
+ gint length;
+ gchar target[256];
+
+ length = readlink (link_name, target, 255);
+ if (length > 0) {
+ gchar *retval;
+ target[length] = '\0';
+ retval = g_strdup (target);
+ g_free (link_name);
+ closedir (dir);
+ return retval;
+ }
+
+ }
+ g_free (link_name);
+ }
+ g_free (dir_name);
+ closedir (dir);
+ return NULL;
+}
+
+static void
+read_cursor_fonts (void)
+{
+ gchar *cursor_font;
+ gint i;
+
+ const gchar *builtins[][4] = {
+ {
+ "mate/cursor-fonts/cursor-normal.pcf",
+ N_("Default Pointer"),
+ N_("Default Pointer - Current"),
+ "mouse-cursor-normal.png"
+ }, {
+ "mate/cursor-fonts/cursor-white.pcf",
+ N_("White Pointer"),
+ N_("White Pointer - Current"),
+ "mouse-cursor-white.png"
+ }, {
+ "mate/cursor-fonts/cursor-large.pcf",
+ N_("Large Pointer"),
+ N_("Large Pointer - Current"),
+ "mouse-cursor-normal-large.png"
+ }, {
+ "mate/cursor-fonts/cursor-large-white.pcf",
+ N_("Large White Pointer - Current"),
+ N_("Large White Pointer"),
+ "mouse-cursor-white-large.png"
+ }
+ };
+
+ cursor_font = read_current_cursor_font();
+
+ if (!cursor_font)
+ cursor_font = g_strdup (builtins[0][0]);
+
+ for (i = 0; i < G_N_ELEMENTS (builtins); i++) {
+ MateThemeCursorInfo *theme_info;
+ gchar *filename;
+
+ theme_info = mate_theme_cursor_info_new ();
+
+ filename = g_build_filename (MATECC_DATA_DIR, "pixmaps", builtins[i][3], NULL);
+ theme_info->thumbnail = gdk_pixbuf_new_from_file (filename, NULL);
+ g_free (filename);
+
+ theme_info->path = g_build_filename (MATECC_DATA_DIR, builtins[i][0], NULL);
+ theme_info->name = g_strdup (theme_info->path);
+
+ if (!strcmp (theme_info->path, cursor_font))
+ theme_info->readable_name = g_strdup (_(builtins[i][2]));
+ else
+ theme_info->readable_name = g_strdup (_(builtins[i][1]));
+
+ g_hash_table_insert (cursor_theme_hash_by_uri, theme_info->path, theme_info);
+ add_theme_to_hash_by_name (cursor_theme_hash_by_name, theme_info);
+ }
+
+ g_free (cursor_font);
+}
+#endif /* HAVE_XCURSOR */
+
+static void
+handle_change_signal (gpointer data,
+ MateThemeChangeType change_type,
+ MateThemeElement element_type)
+{
+#ifdef DEBUG
+ gchar *type_str = NULL;
+ gchar *change_str = NULL;
+ gchar *element_str = NULL;
+#endif
+ MateThemeCommonInfo *theme = data;
+ GList *list;
+
+ if (initting)
+ return;
+
+ for (list = callbacks; list; list = list->next) {
+ ThemeCallbackData *callback_data = list->data;
+ (* callback_data->func) (theme, change_type, element_type, callback_data->data);
+ }
+
+#ifdef DEBUG
+ if (theme->type == MATE_THEME_TYPE_METATHEME)
+ type_str = "meta";
+ else if (theme->type == MATE_THEME_TYPE_ICON)
+ type_str = "icon";
+ else if (theme->type == MATE_THEME_TYPE_CURSOR)
+ type_str = "cursor";
+ else if (theme->type == MATE_THEME_TYPE_REGULAR) {
+ if (element_type & MATE_THEME_GTK_2)
+ element_str = "gtk-2";
+ else if (element_type & MATE_THEME_GTK_2_KEYBINDING)
+ element_str = "keybinding";
+ else if (element_type & MATE_THEME_MARCO)
+ element_str = "marco";
+ }
+
+ if (change_type == MATE_THEME_CHANGE_CREATED)
+ change_str = "created";
+ else if (change_type == MATE_THEME_CHANGE_CHANGED)
+ change_str = "changed";
+ else if (change_type == MATE_THEME_CHANGE_DELETED)
+ change_str = "deleted";
+
+ if (type == MATE_THEME_TYPE_REGULAR) {
+ g_print ("theme \"%s\" has a theme of type %s (priority %d) has been %s\n",
+ theme->name,
+ element_str,
+ theme->priority,
+ type_str);
+ } else if (type_str != NULL) {
+ g_print ("%s theme \"%s\" (priority %d) has been %s\n",
+ type_str,
+ theme->name,
+ theme->priority,
+ type_str);
+ }
+#endif
+}
+
+/* index_uri should point to the gtkrc file that was modified */
+static void
+update_theme_index (GFile *index_uri,
+ MateThemeElement key_element,
+ gint priority)
+{
+ gboolean theme_exists;
+ MateThemeInfo *theme_info;
+ GFile *parent;
+ GFile *common_theme_dir_uri;
+ gchar *common_theme_dir;
+
+ /* First, we determine the new state of the file. We do no more
+ * sophisticated a test than "files exists and is a file" */
+ theme_exists = (get_file_type (index_uri) == G_FILE_TYPE_REGULAR);
+
+ /* Next, we see what currently exists */
+ parent = g_file_get_parent (index_uri);
+ common_theme_dir_uri = g_file_get_parent (parent);
+ common_theme_dir = g_file_get_path (common_theme_dir_uri);
+
+ theme_info = g_hash_table_lookup (theme_hash_by_uri, common_theme_dir);
+ if (theme_info == NULL) {
+ if (theme_exists) {
+ theme_info = mate_theme_info_new ();
+ theme_info->path = g_strdup (common_theme_dir);
+ theme_info->name = g_file_get_basename (common_theme_dir_uri);
+ theme_info->readable_name = g_strdup (theme_info->name);
+ theme_info->priority = priority;
+ if (key_element & MATE_THEME_GTK_2)
+ theme_info->has_gtk = TRUE;
+ else if (key_element & MATE_THEME_GTK_2_KEYBINDING)
+ theme_info->has_keybinding = TRUE;
+ else if (key_element & MATE_THEME_MARCO)
+ theme_info->has_marco = TRUE;
+
+ g_hash_table_insert (theme_hash_by_uri, g_strdup (common_theme_dir), theme_info);
+ add_theme_to_hash_by_name (theme_hash_by_name, theme_info);
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, key_element);
+ }
+ } else {
+ gboolean theme_used_to_exist = FALSE;
+
+ if (key_element & MATE_THEME_GTK_2) {
+ theme_used_to_exist = theme_info->has_gtk;
+ theme_info->has_gtk = theme_exists;
+ } else if (key_element & MATE_THEME_GTK_2_KEYBINDING) {
+ theme_used_to_exist = theme_info->has_keybinding;
+ theme_info->has_keybinding = theme_exists;
+ } else if (key_element & MATE_THEME_MARCO) {
+ theme_used_to_exist = theme_info->has_marco;
+ theme_info->has_marco = theme_exists;
+ }
+
+ if (!theme_info->has_marco && !theme_info->has_keybinding && !theme_info->has_gtk) {
+ g_hash_table_remove (theme_hash_by_uri, common_theme_dir);
+ remove_theme_from_hash_by_name (theme_hash_by_name, theme_info);
+ }
+
+ if (theme_exists && theme_used_to_exist) {
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_CHANGED, key_element);
+ } else if (theme_exists && !theme_used_to_exist) {
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, key_element);
+ } else if (!theme_exists && theme_used_to_exist) {
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_DELETED, key_element);
+ }
+
+ if (!theme_info->has_marco && !theme_info->has_keybinding && !theme_info->has_gtk) {
+ mate_theme_info_free (theme_info);
+ }
+ }
+
+ g_free (common_theme_dir);
+ g_object_unref (parent);
+ g_object_unref (common_theme_dir_uri);
+}
+
+static void
+update_gtk2_index (GFile *gtk2_index_uri,
+ gint priority)
+{
+ update_theme_index (gtk2_index_uri, MATE_THEME_GTK_2, priority);
+}
+
+static void
+update_keybinding_index (GFile *keybinding_index_uri,
+ gint priority)
+{
+ update_theme_index (keybinding_index_uri, MATE_THEME_GTK_2_KEYBINDING, priority);
+}
+
+static void
+update_marco_index (GFile *marco_index_uri,
+ gint priority)
+{
+ update_theme_index (marco_index_uri, MATE_THEME_MARCO, priority);
+}
+
+static void
+update_common_theme_dir_index (GFile *theme_index_uri,
+ MateThemeType type,
+ gint priority)
+{
+ gboolean theme_exists;
+ MateThemeCommonInfo *theme_info = NULL;
+ MateThemeCommonInfo *old_theme_info;
+ GFile *common_theme_dir_uri;
+ gchar *common_theme_dir;
+ GHashTable *hash_by_uri;
+ GHashTable *hash_by_name;
+
+ if (type == MATE_THEME_TYPE_ICON) {
+ hash_by_uri = icon_theme_hash_by_uri;
+ hash_by_name = icon_theme_hash_by_name;
+ } else if (type == MATE_THEME_TYPE_CURSOR) {
+ hash_by_uri = cursor_theme_hash_by_uri;
+ hash_by_name = cursor_theme_hash_by_name;
+ } else {
+ hash_by_uri = meta_theme_hash_by_uri;
+ hash_by_name = meta_theme_hash_by_name;
+ }
+
+ if (type != MATE_THEME_TYPE_CURSOR) {
+ /* First, we determine the new state of the file. */
+ if (get_file_type (theme_index_uri) == G_FILE_TYPE_REGULAR) {
+ /* It's an interesting file. Let's try to load it. */
+ if (type == MATE_THEME_TYPE_ICON)
+ theme_info = (MateThemeCommonInfo *) read_icon_theme (theme_index_uri);
+ else
+ theme_info = (MateThemeCommonInfo *) mate_theme_read_meta_theme (theme_index_uri);
+ } else {
+ theme_info = NULL;
+ }
+
+ }
+#ifdef HAVE_XCURSOR
+ /* cursor themes don't necessarily have an index file, so try those in any case */
+ else {
+ theme_info = (MateThemeCommonInfo *) read_cursor_theme (theme_index_uri);
+ }
+#endif
+
+ if (theme_info) {
+ theme_info->priority = priority;
+ theme_exists = TRUE;
+ } else {
+ theme_exists = FALSE;
+ }
+
+ /* Next, we see what currently exists */
+ common_theme_dir_uri = g_file_get_parent (theme_index_uri);
+ common_theme_dir = g_file_get_path (common_theme_dir_uri);
+ g_object_unref (common_theme_dir_uri);
+
+ old_theme_info = (MateThemeCommonInfo *) g_hash_table_lookup (hash_by_uri, common_theme_dir);
+
+ if (old_theme_info == NULL) {
+ if (theme_exists) {
+ g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info);
+ add_theme_to_hash_by_name (hash_by_name, theme_info);
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, 0);
+ }
+ } else {
+ if (theme_exists) {
+ if (theme_compare (theme_info, old_theme_info) != 0) {
+ /* Remove old theme */
+ g_hash_table_remove (hash_by_uri, common_theme_dir);
+ remove_theme_from_hash_by_name (hash_by_name, old_theme_info);
+ g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info);
+ add_theme_to_hash_by_name (hash_by_name, theme_info);
+ handle_change_signal (theme_info, MATE_THEME_CHANGE_CHANGED, 0);
+ theme_free (old_theme_info);
+ } else {
+ theme_free (theme_info);
+ }
+ } else {
+ g_hash_table_remove (hash_by_uri, common_theme_dir);
+ remove_theme_from_hash_by_name (hash_by_name, old_theme_info);
+
+ handle_change_signal (old_theme_info, MATE_THEME_CHANGE_DELETED, 0);
+ theme_free (old_theme_info);
+ }
+ }
+
+ g_free (common_theme_dir);
+}
+
+static void
+update_meta_theme_index (GFile *meta_theme_index_uri,
+ gint priority)
+{
+ update_common_theme_dir_index (meta_theme_index_uri, MATE_THEME_TYPE_METATHEME, priority);
+}
+
+static void
+update_icon_theme_index (GFile *icon_theme_index_uri,
+ gint priority)
+{
+ update_common_theme_dir_index (icon_theme_index_uri, MATE_THEME_TYPE_ICON, priority);
+}
+
+static void
+update_cursor_theme_index (GFile *cursor_theme_index_uri,
+ gint priority)
+{
+#ifdef HAVE_XCURSOR
+ update_common_theme_dir_index (cursor_theme_index_uri, MATE_THEME_TYPE_CURSOR, priority);
+#endif
+}
+
+static void
+gtk2_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CommonThemeDirMonitorData *monitor_data)
+{
+ gchar *affected_file;
+
+ affected_file = g_file_get_basename (file);
+
+ /* The only file we care about is gtkrc */
+ if (!strcmp (affected_file, "gtkrc")) {
+ update_gtk2_index (file, monitor_data->priority);
+ }
+
+ g_free (affected_file);
+}
+
+static void
+keybinding_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CommonThemeDirMonitorData *monitor_data)
+{
+ gchar *affected_file;
+
+ affected_file = g_file_get_basename (file);
+
+ /* The only file we care about is gtkrc */
+ if (!strcmp (affected_file, "gtkrc")) {
+ update_keybinding_index (file, monitor_data->priority);
+ }
+
+ g_free (affected_file);
+}
+
+static void
+marco_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CommonThemeDirMonitorData *monitor_data)
+{
+ gchar *affected_file;
+
+ affected_file = g_file_get_basename (file);
+
+ /* The only file we care about is marco-theme-1.xml */
+ if (!strcmp (affected_file, "metacity-theme-1.xml")) {
+ update_marco_index (file, monitor_data->priority);
+ }
+
+ g_free (affected_file);
+}
+
+static void
+common_theme_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CommonThemeDirMonitorData *monitor_data)
+{
+ gchar *affected_file;
+
+ affected_file = g_file_get_basename (file);
+
+ /* The only file we care about is index.theme */
+ if (!strcmp (affected_file, "index.theme")) {
+ update_meta_theme_index (file, monitor_data->priority);
+ }
+
+ g_free (affected_file);
+}
+
+static void
+common_icon_theme_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CommonIconThemeDirMonitorData *monitor_data)
+{
+ gchar *affected_file;
+
+ affected_file = g_file_get_basename (file);
+
+ /* The only file we care about is index.theme */
+ if (!strcmp (affected_file, "index.theme")) {
+ update_icon_theme_index (file, monitor_data->priority);
+ update_cursor_theme_index (file, monitor_data->priority);
+ }
+ /* and the cursors subdir for cursor themes */
+ else if (!strcmp (affected_file, "cursors")) {
+ /* always call update_cursor_theme_index with the index.theme URI */
+ GFile *parent, *index;
+
+ parent = g_file_get_parent (file);
+ index = g_file_get_child (parent, "index.theme");
+ g_object_unref (parent);
+
+ update_cursor_theme_index (index, monitor_data->priority);
+
+ g_object_unref (index);
+ }
+
+ g_free (affected_file);
+}
+
+/* Add a monitor to a common_theme_dir. */
+static gboolean
+add_common_theme_dir_monitor (GFile *theme_dir_uri,
+ CommonThemeDirMonitorData *monitor_data,
+ GError **error)
+{
+ GFile *uri, *subdir;
+ GFileMonitor *monitor;
+
+ uri = g_file_get_child (theme_dir_uri, "index.theme");
+ update_meta_theme_index (uri, monitor_data->priority);
+ g_object_unref (uri);
+
+ /* Add the handle for this directory */
+ monitor = g_file_monitor_file (theme_dir_uri, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor == NULL)
+ return FALSE;
+
+ g_signal_connect (monitor, "changed",
+ (GCallback) common_theme_dir_changed, monitor_data);
+
+ monitor_data->common_theme_dir_handle = monitor;
+
+
+ /* gtk-2 theme subdir */
+ subdir = g_file_get_child (theme_dir_uri, "gtk-2.0");
+ uri = g_file_get_child (subdir, "gtkrc");
+ if (g_file_query_exists (uri, NULL)) {
+ update_gtk2_index (uri, monitor_data->priority);
+ }
+ g_object_unref (uri);
+
+ monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL) {
+ g_signal_connect (monitor, "changed",
+ (GCallback) gtk2_dir_changed, monitor_data);
+ }
+ monitor_data->gtk2_dir_handle = monitor;
+ g_object_unref (subdir);
+
+ /* keybinding theme subdir */
+ subdir = g_file_get_child (theme_dir_uri, "gtk-2.0-key");
+ uri = g_file_get_child (subdir, "gtkrc");
+ if (g_file_query_exists (uri, NULL)) {
+ update_keybinding_index (uri, monitor_data->priority);
+ }
+ g_object_unref (uri);
+
+ monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL) {
+ g_signal_connect (monitor, "changed",
+ (GCallback) keybinding_dir_changed, monitor_data);
+ }
+ monitor_data->keybinding_dir_handle = monitor;
+ g_object_unref (subdir);
+
+ /* marco theme subdir */
+ subdir = g_file_get_child (theme_dir_uri, "metacity-1");
+ uri = g_file_get_child (subdir, "metacity-theme-1.xml");
+ if (g_file_query_exists (uri, NULL)) {
+ update_marco_index (uri, monitor_data->priority);
+ }
+ g_object_unref (uri);
+
+ monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL) {
+ g_signal_connect (monitor, "changed",
+ (GCallback) marco_dir_changed, monitor_data);
+ }
+ monitor_data->marco_dir_handle = monitor;
+ g_object_unref (subdir);
+
+ return TRUE;
+}
+
+static gboolean
+add_common_icon_theme_dir_monitor (GFile *theme_dir_uri,
+ CommonIconThemeDirMonitorData *monitor_data,
+ GError **error)
+{
+ GFile *index_uri;
+ GFileMonitor *monitor;
+
+ /* Add the handle for this directory */
+ index_uri = g_file_get_child (theme_dir_uri, "index.theme");
+ update_icon_theme_index (index_uri, monitor_data->priority);
+ update_cursor_theme_index (index_uri, monitor_data->priority);
+ g_object_unref (index_uri);
+
+ monitor = g_file_monitor_file (theme_dir_uri, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor == NULL)
+ return FALSE;
+
+ g_signal_connect (monitor, "changed",
+ (GCallback) common_icon_theme_dir_changed, monitor_data);
+
+ monitor_data->common_icon_theme_dir_handle = monitor;
+ return TRUE;
+}
+
+static void
+remove_common_theme_dir_monitor (CommonThemeDirMonitorData *monitor_data)
+{
+ g_file_monitor_cancel (monitor_data->common_theme_dir_handle);
+ g_file_monitor_cancel (monitor_data->gtk2_dir_handle);
+ g_file_monitor_cancel (monitor_data->keybinding_dir_handle);
+ g_file_monitor_cancel (monitor_data->marco_dir_handle);
+}
+
+static void
+remove_common_icon_theme_dir_monitor (CommonIconThemeDirMonitorData *monitor_data)
+{
+ g_file_monitor_cancel (monitor_data->common_icon_theme_dir_handle);
+}
+
+static void
+top_theme_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CallbackTuple *tuple)
+{
+ GHashTable *handle_hash;
+ CommonThemeDirMonitorData *monitor_data;
+ gint priority;
+
+ handle_hash = tuple->handle_hash;
+ priority = tuple->priority;
+
+ if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
+ if (get_file_type (file) == G_FILE_TYPE_DIRECTORY) {
+ monitor_data = g_new0 (CommonThemeDirMonitorData, 1);
+ monitor_data->priority = priority;
+ add_common_theme_dir_monitor (file, monitor_data, NULL);
+ g_hash_table_insert (handle_hash, g_file_get_basename (file), monitor_data);
+ }
+
+ } else if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
+ gchar *name;
+
+ name = g_file_get_basename (file);
+ monitor_data = g_hash_table_lookup (handle_hash, name);
+ if (monitor_data != NULL) {
+ remove_common_theme_dir_monitor (monitor_data);
+ g_hash_table_remove (handle_hash, name);
+ }
+ g_free (name);
+ }
+}
+
+static void
+top_icon_theme_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CallbackTuple *tuple)
+{
+ GHashTable *handle_hash;
+ CommonIconThemeDirMonitorData *monitor_data;
+ gint priority;
+
+ handle_hash = tuple->handle_hash;
+ priority = tuple->priority;
+
+ if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
+ if (get_file_type (file) == G_FILE_TYPE_DIRECTORY) {
+ monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1);
+ monitor_data->priority = priority;
+ add_common_icon_theme_dir_monitor (file, monitor_data, NULL);
+ g_hash_table_insert (handle_hash, g_file_get_basename (file), monitor_data);
+ }
+
+ } else if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
+ gchar *name;
+
+ name = g_file_get_basename (file);
+ monitor_data = g_hash_table_lookup (handle_hash, name);
+ if (monitor_data != NULL) {
+ remove_common_icon_theme_dir_monitor (monitor_data);
+ g_hash_table_remove (handle_hash, name);
+ }
+ g_free (name);
+ }
+}
+
+/* Add a monitor to a top dir. These monitors persist for the duration of the
+ * lib.
+ */
+static gboolean
+real_add_top_theme_dir_monitor (GFile *uri,
+ gint priority,
+ gboolean icon_theme,
+ GError **error)
+{
+ GFileInfo *file_info;
+ GFileMonitor *monitor;
+ GFileEnumerator *enumerator;
+ CallbackTuple *tuple;
+
+ /* Check the URI */
+ if (get_file_type (uri) != G_FILE_TYPE_DIRECTORY)
+ return FALSE;
+
+ /* handle_hash is a hash of common_theme_dir names to their monitor_data. We
+ * use it to remove the monitor handles when a dir is removed.
+ */
+ tuple = g_new (CallbackTuple, 1);
+ tuple->handle_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_free);
+ tuple->priority = priority;
+
+ /* Monitor the top directory */
+ monitor = g_file_monitor_directory (uri, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL) {
+ g_signal_connect (monitor, "changed",
+ (GCallback) (icon_theme ? top_icon_theme_dir_changed : top_theme_dir_changed),
+ tuple);
+ }
+
+ /* Go through the directory to add monitoring */
+ enumerator = g_file_enumerate_children (uri,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (enumerator == NULL)
+ return FALSE;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, NULL))) {
+ GFileType type = g_file_info_get_file_type (file_info);
+
+ if (type == G_FILE_TYPE_DIRECTORY || type == G_FILE_TYPE_SYMBOLIC_LINK) {
+ GFile *child;
+ const gchar *name;
+ gpointer data;
+
+ /* Add the directory */
+ name = g_file_info_get_name (file_info);
+ child = g_file_get_child (uri, name);
+
+ if (icon_theme) {
+ CommonIconThemeDirMonitorData *monitor_data;
+ monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1);
+ monitor_data->priority = priority;
+ add_common_icon_theme_dir_monitor (child, monitor_data, error);
+ data = monitor_data;
+ } else {
+ CommonThemeDirMonitorData *monitor_data;
+ monitor_data = g_new0 (CommonThemeDirMonitorData, 1);
+ monitor_data->priority = priority;
+ add_common_theme_dir_monitor (child, monitor_data, error);
+ data = monitor_data;
+ }
+ g_object_unref (child);
+
+ g_hash_table_insert (tuple->handle_hash, g_strdup (name), data);
+ }
+ g_object_unref (file_info);
+ }
+ g_file_enumerator_close (enumerator, NULL, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+add_top_theme_dir_monitor (GFile *uri,
+ gint priority,
+ GError **error)
+{
+ return real_add_top_theme_dir_monitor (uri, priority, FALSE, error);
+}
+
+static gboolean
+add_top_icon_theme_dir_monitor (GFile *uri,
+ gint priority,
+ GError **error)
+{
+ return real_add_top_theme_dir_monitor (uri, priority, TRUE, error);
+}
+
+/* Public functions */
+
+/* GTK/Marco/keybinding Themes */
+MateThemeInfo *
+mate_theme_info_new (void)
+{
+ MateThemeInfo *theme_info;
+
+ theme_info = g_new0 (MateThemeInfo, 1);
+ theme_info->type = MATE_THEME_TYPE_REGULAR;
+
+ return theme_info;
+}
+
+void
+mate_theme_info_free (MateThemeInfo *theme_info)
+{
+ g_free (theme_info->path);
+ g_free (theme_info->name);
+ g_free (theme_info->readable_name);
+ g_free (theme_info);
+}
+
+MateThemeInfo *
+mate_theme_info_find (const gchar *theme_name)
+{
+ return (MateThemeInfo *)
+ get_theme_from_hash_by_name (theme_hash_by_name, theme_name, -1);
+}
+
+struct MateThemeInfoHashData
+{
+ gconstpointer user_data;
+ GList *list;
+};
+
+static void
+mate_theme_info_find_by_type_helper (gpointer key,
+ GList *list,
+ struct MateThemeInfoHashData *hash_data)
+{
+ guint elements = GPOINTER_TO_INT (hash_data->user_data);
+
+ do {
+ MateThemeInfo *theme_info = list->data;
+
+ if ((elements & MATE_THEME_MARCO && theme_info->has_marco) ||
+ (elements & MATE_THEME_GTK_2 && theme_info->has_gtk) ||
+ (elements & MATE_THEME_GTK_2_KEYBINDING && theme_info->has_keybinding)) {
+ hash_data->list = g_list_prepend (hash_data->list, theme_info);
+ return;
+ }
+
+ list = list->next;
+ } while (list);
+}
+
+GList *
+mate_theme_info_find_by_type (guint elements)
+{
+ struct MateThemeInfoHashData data;
+ data.user_data = GINT_TO_POINTER (elements);
+ data.list = NULL;
+
+ g_hash_table_foreach (theme_hash_by_name,
+ (GHFunc) mate_theme_info_find_by_type_helper,
+ &data);
+
+ return data.list;
+}
+
+static void mate_theme_info_find_all_helper(const gchar* key, GList* list, GList** themes)
+{
+ /* only return visible themes */
+ if (!((MateThemeCommonInfo*) list->data)->hidden)
+ {
+ *themes = g_list_prepend(*themes, list->data);
+ }
+}
+
+gchar* gtk_theme_info_missing_engine(const gchar* gtk_theme, gboolean nameOnly)
+{
+ gchar* engine = NULL;
+ gchar* gtkrc;
+
+ gtkrc = gtkrc_find_named(gtk_theme);
+
+ if (gtkrc)
+ {
+ GSList* engines = NULL;
+ GSList* l;
+
+ gtkrc_get_details(gtkrc, &engines, NULL);
+
+ g_free(gtkrc);
+
+ for (l = engines; l; l = l->next)
+ {
+ #if 1 // set to 0 if you can not compile with the follow code
+ GtkThemeEngine* a = gtk_theme_engine_get((const gchar*) l->data);
+
+ if (!a)
+ {
+ if (nameOnly)
+ {
+ engine = g_strdup(l->data);
+ }
+ else
+ {
+ // esto necesita más trabajo, pero creo que debian no se
+ // salva ni con el anterior fix.
+ // GTK_ENGINE_DIR aún sigue conteniendo un path erroneo.
+ engine = g_module_build_path(GTK_ENGINE_DIR, l->data);
+ }
+
+ break;
+ }
+
+ #else
+
+ /* This code do not work on distros with more of one gtk theme
+ * engine path. Like debian. But yes on others like Archlinux.
+ * Example, debian use:
+ * /usr/lib/i386-linux-gnu/2.10.0/engines/
+ * and /usr/lib/2.10.0/engines/
+ *
+ * some links
+ * http://forums.linuxmint.com/viewtopic.php?f=190&t=85015
+ */
+ gchar* full = g_module_build_path(GTK_ENGINE_DIR, l->data);
+
+ gboolean found = g_file_test(full, G_FILE_TEST_EXISTS);
+
+ if (!found)
+ {
+ if (nameOnly)
+ {
+ engine = g_strdup(l->data);
+ g_free(full);
+ }
+ else
+ {
+ engine = full;
+ }
+
+ break;
+ }
+
+ g_free(full);
+ #endif
+ }
+
+ g_slist_foreach(engines, (GFunc) g_free, NULL);
+ g_slist_free(engines);
+ }
+
+ return engine;
+}
+
+/* Icon themes */
+MateThemeIconInfo *
+mate_theme_icon_info_new (void)
+{
+ MateThemeIconInfo *icon_theme_info;
+
+ icon_theme_info = g_new0 (MateThemeIconInfo, 1);
+ icon_theme_info->type = MATE_THEME_TYPE_ICON;
+
+ return icon_theme_info;
+}
+
+void
+mate_theme_icon_info_free (MateThemeIconInfo *icon_theme_info)
+{
+ g_free (icon_theme_info->name);
+ g_free (icon_theme_info->readable_name);
+ g_free (icon_theme_info->path);
+ g_free (icon_theme_info);
+}
+
+MateThemeIconInfo *
+mate_theme_icon_info_find (const gchar *icon_theme_name)
+{
+ g_return_val_if_fail (icon_theme_name != NULL, NULL);
+
+ return (MateThemeIconInfo *)
+ get_theme_from_hash_by_name (icon_theme_hash_by_name, icon_theme_name, -1);
+}
+
+GList *
+mate_theme_icon_info_find_all (void)
+{
+ GList *list = NULL;
+
+ g_hash_table_foreach (icon_theme_hash_by_name,
+ (GHFunc) mate_theme_info_find_all_helper,
+ &list);
+
+ return list;
+}
+
+gint
+mate_theme_icon_info_compare (MateThemeIconInfo *a,
+ MateThemeIconInfo *b)
+{
+ gint cmp;
+
+ cmp = safe_strcmp (a->path, b->path);
+ if (cmp != 0) return cmp;
+
+ return safe_strcmp (a->name, b->name);
+}
+
+/* Cursor themes */
+MateThemeCursorInfo *
+mate_theme_cursor_info_new (void)
+{
+ MateThemeCursorInfo *theme_info;
+
+ theme_info = g_new0 (MateThemeCursorInfo, 1);
+ theme_info->type = MATE_THEME_TYPE_CURSOR;
+
+ return theme_info;
+}
+
+void
+mate_theme_cursor_info_free (MateThemeCursorInfo *cursor_theme_info)
+{
+ g_free (cursor_theme_info->name);
+ g_free (cursor_theme_info->readable_name);
+ g_free (cursor_theme_info->path);
+ g_array_free (cursor_theme_info->sizes, TRUE);
+ if (cursor_theme_info->thumbnail != NULL)
+ g_object_unref (cursor_theme_info->thumbnail);
+ g_free (cursor_theme_info);
+}
+
+MateThemeCursorInfo *
+mate_theme_cursor_info_find (const gchar *cursor_theme_name)
+{
+ g_return_val_if_fail (cursor_theme_name != NULL, NULL);
+
+ return (MateThemeCursorInfo *)
+ get_theme_from_hash_by_name (cursor_theme_hash_by_name, cursor_theme_name, -1);
+}
+
+GList *
+mate_theme_cursor_info_find_all (void)
+{
+ GList *list = NULL;
+
+ g_hash_table_foreach (cursor_theme_hash_by_name,
+ (GHFunc) mate_theme_info_find_all_helper,
+ &list);
+
+ return list;
+}
+
+gint
+mate_theme_cursor_info_compare (MateThemeCursorInfo *a,
+ MateThemeCursorInfo *b)
+{
+ gint cmp;
+
+ cmp = safe_strcmp (a->path, b->path);
+ if (cmp != 0) return cmp;
+
+ return safe_strcmp (a->name, b->name);
+}
+
+/* Meta themes */
+MateThemeMetaInfo* mate_theme_meta_info_new(void)
+{
+ MateThemeMetaInfo* theme_info;
+
+ theme_info = g_new0(MateThemeMetaInfo, 1);
+ theme_info->type = MATE_THEME_TYPE_METATHEME;
+
+ return theme_info;
+}
+
+void mate_theme_meta_info_free(MateThemeMetaInfo* meta_theme_info)
+{
+ g_free(meta_theme_info->path);
+ g_free(meta_theme_info->readable_name);
+ g_free(meta_theme_info->name);
+ g_free(meta_theme_info->comment);
+ g_free(meta_theme_info->application_font);
+ g_free(meta_theme_info->documents_font);
+ g_free(meta_theme_info->desktop_font);
+ g_free(meta_theme_info->windowtitle_font);
+ g_free(meta_theme_info->monospace_font);
+ g_free(meta_theme_info->background_image);
+ g_free(meta_theme_info->gtk_theme_name);
+ g_free(meta_theme_info->gtk_color_scheme);
+ g_free(meta_theme_info->icon_theme_name);
+ g_free(meta_theme_info->marco_theme_name);
+ g_free(meta_theme_info->notification_theme_name);
+ g_free(meta_theme_info);
+}
+
+gboolean mate_theme_meta_info_validate(const MateThemeMetaInfo* info, GError** error)
+{
+ MateThemeInfo* theme;
+ gchar* engine;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ theme = mate_theme_info_find (info->gtk_theme_name);
+
+ if (!theme || !theme->has_gtk)
+ {
+ g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_THEME_NOT_AVAILABLE,
+ _("This theme will not look as intended because the required GTK+ theme '%s' is not installed."),
+ info->gtk_theme_name);
+ return FALSE;
+ }
+
+ theme = mate_theme_info_find (info->marco_theme_name);
+
+ if (!theme || !theme->has_marco)
+ {
+ g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_WM_THEME_NOT_AVAILABLE,
+ _("This theme will not look as intended because the required window manager theme '%s' is not installed."),
+ info->marco_theme_name);
+ return FALSE;
+ }
+
+ if (!mate_theme_icon_info_find (info->icon_theme_name))
+ {
+ g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_ICON_THEME_NOT_AVAILABLE,
+ _("This theme will not look as intended because the required icon theme '%s' is not installed."),
+ info->icon_theme_name);
+ return FALSE;
+ }
+
+ /* check for gtk theme engines */
+ engine = gtk_theme_info_missing_engine(info->gtk_theme_name, TRUE);
+
+ if (engine != NULL)
+ {
+ g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE,
+ _("This theme will not look as intended because the required GTK+ theme engine '%s' is not installed."),
+ engine);
+ g_free (engine);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+MateThemeMetaInfo* mate_theme_meta_info_find(const char* meta_theme_name)
+{
+ g_return_val_if_fail(meta_theme_name != NULL, NULL);
+
+ return (MateThemeMetaInfo*) get_theme_from_hash_by_name (meta_theme_hash_by_name, meta_theme_name, -1);
+}
+
+GList* mate_theme_meta_info_find_all(void)
+{
+ GList* list = NULL;
+
+ g_hash_table_foreach (meta_theme_hash_by_name, (GHFunc) mate_theme_info_find_all_helper, &list);
+
+ return list;
+}
+
+gint
+mate_theme_meta_info_compare (MateThemeMetaInfo *a,
+ MateThemeMetaInfo *b)
+{
+ gint cmp;
+
+ cmp = safe_strcmp (a->path, b->path);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->readable_name, b->readable_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->name, b->name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->comment, b->comment);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->icon_file, b->icon_file);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->gtk_theme_name, b->gtk_theme_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->gtk_color_scheme, b->gtk_color_scheme);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->marco_theme_name, b->marco_theme_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->icon_theme_name, b->icon_theme_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->notification_theme_name, b->notification_theme_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->sound_theme_name, b->sound_theme_name);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->application_font, b->application_font);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->documents_font, b->documents_font);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->desktop_font, b->desktop_font);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->windowtitle_font, b->windowtitle_font);
+ if (cmp != 0) return cmp;
+
+ cmp = safe_strcmp (a->monospace_font, b->monospace_font);
+ if (cmp != 0) return cmp;
+
+ return safe_strcmp (a->background_image, b->background_image);
+}
+
+void
+mate_theme_info_register_theme_change (ThemeChangedCallback func,
+ gpointer data)
+{
+ ThemeCallbackData *callback_data;
+
+ g_return_if_fail (func != NULL);
+
+ callback_data = g_new (ThemeCallbackData, 1);
+ callback_data->func = func;
+ callback_data->data = data;
+
+ callbacks = g_list_prepend (callbacks, callback_data);
+}
+
+gboolean
+mate_theme_color_scheme_parse (const gchar *scheme, GdkColor *colors)
+{
+ gchar **color_scheme_strings, **color_scheme_pair, *current_string;
+ gint i;
+
+ if (!scheme || !strcmp (scheme, ""))
+ return FALSE;
+
+ /* initialise the array */
+ for (i = 0; i < NUM_SYMBOLIC_COLORS; i++)
+ colors[i].red = colors[i].green = colors[i].blue = 0;
+
+ /* The color scheme string consists of name:color pairs, separated by
+ * newlines, so first we split the string up by new line */
+
+ color_scheme_strings = g_strsplit (scheme, "\n", 0);
+
+ /* loop through the name:color pairs, and save the color if we recognise the name */
+ i = 0;
+ while ((current_string = color_scheme_strings[i++])) {
+ color_scheme_pair = g_strsplit (current_string, ":", 0);
+
+ if (color_scheme_pair[0] != NULL && color_scheme_pair[1] != NULL) {
+ g_strstrip (color_scheme_pair[0]);
+ g_strstrip (color_scheme_pair[1]);
+
+ if (!strcmp ("fg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_FG]);
+ else if (!strcmp ("bg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_BG]);
+ else if (!strcmp ("text_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TEXT]);
+ else if (!strcmp ("base_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_BASE]);
+ else if (!strcmp ("selected_fg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_SELECTED_FG]);
+ else if (!strcmp ("selected_bg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_SELECTED_BG]);
+ else if (!strcmp ("tooltip_fg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TOOLTIP_FG]);
+ else if (!strcmp ("tooltip_bg_color", color_scheme_pair[0]))
+ gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TOOLTIP_BG]);
+ }
+
+ g_strfreev (color_scheme_pair);
+ }
+
+ g_strfreev (color_scheme_strings);
+
+ return TRUE;
+}
+
+gboolean
+mate_theme_color_scheme_equal (const gchar *s1, const gchar *s2)
+{
+ GdkColor c1[NUM_SYMBOLIC_COLORS], c2[NUM_SYMBOLIC_COLORS];
+ int i;
+
+ if (!mate_theme_color_scheme_parse (s1, c1) ||
+ !mate_theme_color_scheme_parse (s2, c2))
+ return FALSE;
+
+ for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) {
+ if (!gdk_color_equal (&c1[i], &c2[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+mate_theme_init ()
+{
+ GFile *top_theme_dir;
+ gchar *top_theme_dir_string;
+ static gboolean initted = FALSE;
+ gchar **search_path;
+ gint i, n;
+
+ if (initted)
+ return;
+
+ initting = TRUE;
+
+ meta_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ meta_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ icon_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ icon_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ cursor_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ cursor_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ /* Add all the toplevel theme dirs. */
+ /* $datadir/themes */
+ top_theme_dir_string = gtk_rc_get_theme_dir ();
+ top_theme_dir = g_file_new_for_path (top_theme_dir_string);
+ g_free (top_theme_dir_string);
+ add_top_theme_dir_monitor (top_theme_dir, 1, NULL);
+ g_object_unref (top_theme_dir);
+
+ /* ~/.themes */
+ top_theme_dir_string = g_build_filename (g_get_home_dir (), ".themes", NULL);
+ top_theme_dir = g_file_new_for_path (top_theme_dir_string);
+ g_free (top_theme_dir_string);
+ if (!g_file_query_exists (top_theme_dir, NULL))
+ g_file_make_directory (top_theme_dir, NULL, NULL);
+ add_top_theme_dir_monitor (top_theme_dir, 0, NULL);
+ g_object_unref (top_theme_dir);
+
+ /* ~/.icons */
+ top_theme_dir_string = g_build_filename (g_get_home_dir (), ".icons", NULL);
+ top_theme_dir = g_file_new_for_path (top_theme_dir_string);
+ g_free (top_theme_dir_string);
+ if (!g_file_query_exists (top_theme_dir, NULL))
+ g_file_make_directory (top_theme_dir, NULL, NULL);
+ g_object_unref (top_theme_dir);
+
+ /* icon theme search path */
+ gtk_icon_theme_get_search_path (gtk_icon_theme_get_default (), &search_path, &n);
+ for (i = 0; i < n; ++i) {
+ top_theme_dir = g_file_new_for_path (search_path[i]);
+ add_top_icon_theme_dir_monitor (top_theme_dir, i, NULL);
+ g_object_unref (top_theme_dir);
+ }
+ g_strfreev (search_path);
+
+#ifdef XCURSOR_ICONDIR
+ /* if there's a separate xcursors dir, add that as well */
+ if (strcmp (XCURSOR_ICONDIR, top_theme_dir_string) &&
+ strcmp (XCURSOR_ICONDIR, "/usr/share/icons")) {
+ top_theme_dir = g_file_new_for_path (XCURSOR_ICONDIR);
+ add_top_icon_theme_dir_monitor (top_theme_dir, 1, NULL);
+ g_object_unref (top_theme_dir);
+ }
+#endif
+
+#ifdef HAVE_XCURSOR
+ /* make sure we have the default theme */
+ if (!mate_theme_cursor_info_find ("default"))
+ add_default_cursor_theme ();
+#else
+ /* If we don't have Xcursor, use the built-in cursor fonts instead */
+ read_cursor_fonts ();
+#endif
+
+ /* done */
+ initted = TRUE;
+ initting = FALSE;
+}
diff --git a/capplets/common/mate-theme-info.h b/capplets/common/mate-theme-info.h
new file mode 100644
index 00000000..095e98ae
--- /dev/null
+++ b/capplets/common/mate-theme-info.h
@@ -0,0 +1,190 @@
+/* mate-theme-info.h - MATE Theme information
+
+ Copyright (C) 2002 Jonathan Blandford <[email protected]>
+ All rights reserved.
+
+ This file is part of the Mate Library.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef MATE_THEME_INFO_H
+#define MATE_THEME_INFO_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdk.h>
+
+typedef enum {
+ MATE_THEME_TYPE_METATHEME,
+ MATE_THEME_TYPE_ICON,
+ MATE_THEME_TYPE_CURSOR,
+ MATE_THEME_TYPE_REGULAR
+} MateThemeType;
+
+typedef enum {
+ MATE_THEME_CHANGE_CREATED,
+ MATE_THEME_CHANGE_DELETED,
+ MATE_THEME_CHANGE_CHANGED
+} MateThemeChangeType;
+
+typedef enum {
+ MATE_THEME_MARCO = 1 << 0,
+ MATE_THEME_GTK_2 = 1 << 1,
+ MATE_THEME_GTK_2_KEYBINDING = 1 << 2
+} MateThemeElement;
+
+typedef struct _MateThemeCommonInfo MateThemeCommonInfo;
+typedef struct _MateThemeCommonInfo MateThemeIconInfo;
+struct _MateThemeCommonInfo
+{
+ MateThemeType type;
+ gchar* path;
+ gchar* name;
+ gchar* readable_name;
+ gint priority;
+ gboolean hidden;
+};
+
+typedef struct _MateThemeInfo MateThemeInfo;
+struct _MateThemeInfo
+{
+ MateThemeType type;
+ gchar* path;
+ gchar* name;
+ gchar* readable_name;
+ gint priority;
+ gboolean hidden;
+
+ guint has_gtk : 1;
+ guint has_keybinding : 1;
+ guint has_marco : 1;
+};
+
+typedef struct _MateThemeCursorInfo MateThemeCursorInfo;
+struct _MateThemeCursorInfo {
+ MateThemeType type;
+ gchar* path;
+ gchar* name;
+ gchar* readable_name;
+ gint priority;
+ gboolean hidden;
+
+ GArray* sizes;
+ GdkPixbuf* thumbnail;
+};
+
+typedef struct _MateThemeMetaInfo MateThemeMetaInfo;
+struct _MateThemeMetaInfo {
+ MateThemeType type;
+ gchar* path;
+ gchar* name;
+ gchar* readable_name;
+ gint priority;
+ gboolean hidden;
+
+ gchar* comment;
+ gchar* icon_file;
+
+ gchar* gtk_theme_name;
+ gchar* gtk_color_scheme;
+ gchar* marco_theme_name;
+ gchar* icon_theme_name;
+ gchar* notification_theme_name;
+ gchar* sound_theme_name;
+ gchar* cursor_theme_name;
+ guint cursor_size;
+
+ gchar* application_font;
+ gchar* documents_font;
+ gchar* desktop_font;
+ gchar* windowtitle_font;
+ gchar* monospace_font;
+ gchar* background_image;
+};
+
+enum {
+ COLOR_FG,
+ COLOR_BG,
+ COLOR_TEXT,
+ COLOR_BASE,
+ COLOR_SELECTED_FG,
+ COLOR_SELECTED_BG,
+ COLOR_TOOLTIP_FG,
+ COLOR_TOOLTIP_BG,
+ NUM_SYMBOLIC_COLORS
+};
+
+typedef void (*ThemeChangedCallback) (MateThemeCommonInfo* theme, MateThemeChangeType change_type, MateThemeElement element_type, gpointer user_data);
+
+#define MATE_THEME_ERROR mate_theme_info_error_quark()
+
+enum {
+ MATE_THEME_ERROR_GTK_THEME_NOT_AVAILABLE = 1,
+ MATE_THEME_ERROR_WM_THEME_NOT_AVAILABLE,
+ MATE_THEME_ERROR_ICON_THEME_NOT_AVAILABLE,
+ MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE,
+ MATE_THEME_ERROR_UNKNOWN
+};
+
+
+/* GTK/Marco/keybinding Themes */
+MateThemeInfo *mate_theme_info_new (void);
+void mate_theme_info_free (MateThemeInfo *theme_info);
+MateThemeInfo *mate_theme_info_find (const gchar *theme_name);
+GList *mate_theme_info_find_by_type (guint elements);
+GQuark mate_theme_info_error_quark (void);
+gchar *gtk_theme_info_missing_engine (const gchar *gtk_theme,
+ gboolean nameOnly);
+
+/* Icon Themes */
+MateThemeIconInfo *mate_theme_icon_info_new (void);
+void mate_theme_icon_info_free (MateThemeIconInfo *icon_theme_info);
+MateThemeIconInfo *mate_theme_icon_info_find (const gchar *icon_theme_name);
+GList *mate_theme_icon_info_find_all (void);
+gint mate_theme_icon_info_compare (MateThemeIconInfo *a,
+ MateThemeIconInfo *b);
+
+/* Cursor Themes */
+MateThemeCursorInfo *mate_theme_cursor_info_new (void);
+void mate_theme_cursor_info_free (MateThemeCursorInfo *info);
+MateThemeCursorInfo *mate_theme_cursor_info_find (const gchar *name);
+GList *mate_theme_cursor_info_find_all (void);
+gint mate_theme_cursor_info_compare (MateThemeCursorInfo *a,
+ MateThemeCursorInfo *b);
+
+/* Meta themes*/
+MateThemeMetaInfo *mate_theme_meta_info_new (void);
+void mate_theme_meta_info_free (MateThemeMetaInfo *meta_theme_info);
+MateThemeMetaInfo *mate_theme_meta_info_find (const gchar *meta_theme_name);
+GList *mate_theme_meta_info_find_all (void);
+gint mate_theme_meta_info_compare (MateThemeMetaInfo *a,
+ MateThemeMetaInfo *b);
+gboolean mate_theme_meta_info_validate (const MateThemeMetaInfo *info,
+ GError **error);
+MateThemeMetaInfo *mate_theme_read_meta_theme (GFile *meta_theme_uri);
+
+/* Other */
+void mate_theme_init (void);
+void mate_theme_info_register_theme_change (ThemeChangedCallback func,
+ gpointer data);
+
+gboolean mate_theme_color_scheme_parse (const gchar *scheme,
+ GdkColor *colors);
+gboolean mate_theme_color_scheme_equal (const gchar *s1,
+ const gchar *s2);
+
+#endif /* MATE_THEME_INFO_H */
diff --git a/capplets/common/mate-theme-test.c b/capplets/common/mate-theme-test.c
new file mode 100644
index 00000000..0c3c51f9
--- /dev/null
+++ b/capplets/common/mate-theme-test.c
@@ -0,0 +1,134 @@
+#include <config.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <libmate/mate-desktop-item.h>
+#include "mate-theme-info.h"
+
+int
+main (int argc, char *argv[])
+{
+ GList *themes, *list;
+
+ g_thread_init (NULL);
+ gtk_init (&argc, &argv);
+ mate_theme_init ();
+
+ themes = mate_theme_meta_info_find_all ();
+ if (themes == NULL)
+ {
+ g_print ("No meta themes were found.\n");
+ }
+ else
+ {
+ g_print ("%d meta themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeMetaInfo *meta_theme_info;
+
+ meta_theme_info = list->data;
+ g_print ("\t%s\n", meta_theme_info->readable_name);
+ }
+ }
+ g_list_free (themes);
+
+ themes = mate_theme_icon_info_find_all ();
+ if (themes == NULL)
+ {
+ g_print ("No icon themes were found.\n");
+ }
+ else
+ {
+ g_print ("%d icon themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeIconInfo *icon_theme_info;
+
+ icon_theme_info = list->data;
+ g_print ("\t%s\n", icon_theme_info->name);
+ }
+ }
+ g_list_free (themes);
+
+ themes = mate_theme_info_find_by_type (MATE_THEME_MARCO);
+ if (themes == NULL)
+ {
+ g_print ("No marco themes were found.\n");
+ }
+ else
+ {
+ g_print ("%d marco themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeInfo *theme_info;
+
+ theme_info = list->data;
+ g_print ("\t%s\n", theme_info->name);
+ }
+ }
+ g_list_free (themes);
+
+ themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2);
+ if (themes == NULL)
+ {
+ gchar *str;
+
+ g_print ("No gtk-2 themes were found. The following directories were tested:\n");
+ str = gtk_rc_get_theme_dir ();
+ g_print ("\t%s\n", str);
+ g_free (str);
+ str = g_build_filename (g_get_home_dir (), ".themes", NULL);
+ g_print ("\t%s\n", str);
+ g_free (str);
+ }
+ else
+ {
+ g_print ("%d gtk-2 themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeInfo *theme_info;
+
+ theme_info = list->data;
+ g_print ("\t%s\n", theme_info->name);
+ }
+ }
+ g_list_free (themes);
+
+ themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2_KEYBINDING);
+ if (themes == NULL)
+ {
+ g_print ("No keybinding themes were found.\n");
+ }
+ else
+ {
+ g_print ("%d keybinding themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeInfo *theme_info;
+
+ theme_info = list->data;
+ g_print ("\t%s\n", theme_info->name);
+ }
+ }
+ g_list_free (themes);
+
+ themes = mate_theme_cursor_info_find_all ();
+ if (themes == NULL)
+ {
+ g_print ("No cursor themes were found.\n");
+ }
+ else
+ {
+ g_print ("%d cursor themes were found:\n", g_list_length (themes));
+ for (list = themes; list; list = list->next)
+ {
+ MateThemeCursorInfo *cursor_theme_info;
+
+ cursor_theme_info = list->data;
+ g_print ("\t%s\n", cursor_theme_info->name);
+ }
+ }
+ g_list_free (themes);
+
+ return 0;
+}
+
diff --git a/capplets/common/mateconf-property-editor-marshal.c b/capplets/common/mateconf-property-editor-marshal.c
new file mode 100644
index 00000000..9bead349
--- /dev/null
+++ b/capplets/common/mateconf-property-editor-marshal.c
@@ -0,0 +1,41 @@
+#include <glib.h>
+#include <glib-object.h>
+#include "mateconf-property-editor-marshal.h"
+
+/* VOID:STRING,POINTER (peditor-marshal.list:25) */
+void
+mateconf_property_editor_marshal_VOID__STRING_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ (char*) g_value_get_string (param_values + 1),
+ g_value_get_pointer (param_values + 2),
+ data2);
+}
+
diff --git a/capplets/common/mateconf-property-editor-marshal.h b/capplets/common/mateconf-property-editor-marshal.h
new file mode 100644
index 00000000..e26505cf
--- /dev/null
+++ b/capplets/common/mateconf-property-editor-marshal.h
@@ -0,0 +1,19 @@
+
+#include <gobject/gmarshal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* VOID:STRING,POINTER (peditor-marshal.list:25) */
+extern void mateconf_property_editor_marshal_VOID__STRING_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/capplets/common/mateconf-property-editor.c b/capplets/common/mateconf-property-editor.c
new file mode 100644
index 00000000..f565a168
--- /dev/null
+++ b/capplets/common/mateconf-property-editor.c
@@ -0,0 +1,1801 @@
+/* -*- mode: c; style: linux -*- */
+
+/* mateconf-property-editor.c
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Written by Bradford Hovinen <[email protected]>
+ *
+ * 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, 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 <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "mateconf-property-editor.h"
+#include "mateconf-property-editor-marshal.h"
+
+enum {
+ VALUE_CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_KEY,
+ PROP_CALLBACK,
+ PROP_CHANGESET,
+ PROP_CONV_TO_WIDGET_CB,
+ PROP_CONV_FROM_WIDGET_CB,
+ PROP_UI_CONTROL,
+ PROP_DATA,
+ PROP_DATA_FREE_CB
+};
+
+struct _MateConfPropertyEditorPrivate
+{
+ gchar *key;
+ guint handler_id;
+ MateConfChangeSet *changeset;
+ GObject *ui_control;
+ MateConfPEditorValueConvFn conv_to_widget_cb;
+ MateConfPEditorValueConvFn conv_from_widget_cb;
+ MateConfClientNotifyFunc callback;
+ gboolean inited;
+
+ gpointer data;
+ GFreeFunc data_free_cb;
+};
+
+typedef struct
+{
+ GType enum_type;
+ MateConfPEditorGetValueFn enum_val_true_fn;
+ gpointer enum_val_true_fn_data;
+ guint enum_val_false;
+ gboolean use_nick;
+} MateConfPropertyEditorEnumData;
+
+static guint peditor_signals[LAST_SIGNAL];
+
+static void mateconf_property_editor_set_prop (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void mateconf_property_editor_get_prop (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void mateconf_property_editor_finalize (GObject *object);
+
+static GObject *mateconf_peditor_new (const gchar *key,
+ MateConfClientNotifyFunc cb,
+ MateConfChangeSet *changeset,
+ GObject *ui_control,
+ const gchar *first_prop_name,
+ va_list var_args,
+ const gchar *first_custom,
+ ...);
+
+G_DEFINE_TYPE (MateConfPropertyEditor, mateconf_property_editor, G_TYPE_OBJECT)
+
+#define MATECONF_PROPERTY_EDITOR_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), mateconf_property_editor_get_type (), MateConfPropertyEditorPrivate))
+
+
+static MateConfValue*
+mateconf_property_editor_conv_default (MateConfPropertyEditor *peditor,
+ const MateConfValue *value)
+{
+ return mateconf_value_copy (value);
+}
+
+static void
+mateconf_property_editor_init (MateConfPropertyEditor *mateconf_property_editor)
+{
+ mateconf_property_editor->p = MATECONF_PROPERTY_EDITOR_GET_PRIVATE (mateconf_property_editor);
+ mateconf_property_editor->p->conv_to_widget_cb = mateconf_property_editor_conv_default;
+ mateconf_property_editor->p->conv_from_widget_cb = mateconf_property_editor_conv_default;
+ mateconf_property_editor->p->inited = FALSE;
+}
+
+static void
+mateconf_property_editor_class_init (MateConfPropertyEditorClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = mateconf_property_editor_finalize;
+ object_class->set_property = mateconf_property_editor_set_prop;
+ object_class->get_property = mateconf_property_editor_get_prop;
+
+ g_object_class_install_property
+ (object_class, PROP_KEY,
+ g_param_spec_string ("key",
+ _("Key"),
+ _("MateConf key to which this property editor is attached"),
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (object_class, PROP_CALLBACK,
+ g_param_spec_pointer ("callback",
+ _("Callback"),
+ _("Issue this callback when the value associated with key gets changed"),
+ G_PARAM_WRITABLE));
+ g_object_class_install_property
+ (object_class, PROP_CHANGESET,
+ g_param_spec_pointer ("changeset",
+ _("Change set"),
+ _("MateConf change set containing data to be forwarded to the mateconf client on apply"),
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (object_class, PROP_CONV_TO_WIDGET_CB,
+ g_param_spec_pointer ("conv-to-widget-cb",
+ _("Conversion to widget callback"),
+ _("Callback to be issued when data are to be converted from MateConf to the widget"),
+ G_PARAM_WRITABLE));
+ g_object_class_install_property
+ (object_class, PROP_CONV_FROM_WIDGET_CB,
+ g_param_spec_pointer ("conv-from-widget-cb",
+ _("Conversion from widget callback"),
+ _("Callback to be issued when data are to be converted to MateConf from the widget"),
+ G_PARAM_WRITABLE));
+ g_object_class_install_property
+ (object_class, PROP_UI_CONTROL,
+ g_param_spec_object ("ui-control",
+ _("UI Control"),
+ _("Object that controls the property (normally a widget)"),
+ G_TYPE_OBJECT,
+ G_PARAM_WRITABLE));
+
+ peditor_signals[VALUE_CHANGED] =
+ g_signal_new ("value-changed",
+ G_TYPE_FROM_CLASS (object_class), 0,
+ G_STRUCT_OFFSET (MateConfPropertyEditorClass, value_changed),
+ NULL, NULL,
+ (GSignalCMarshaller) mateconf_property_editor_marshal_VOID__STRING_POINTER,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+
+ g_object_class_install_property
+ (object_class, PROP_DATA,
+ g_param_spec_pointer ("data",
+ _("Property editor object data"),
+ _("Custom data required by the specific property editor"),
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_DATA_FREE_CB,
+ g_param_spec_pointer ("data-free-cb",
+ _("Property editor data freeing callback"),
+ _("Callback to be issued when property editor object data is to be freed"),
+ G_PARAM_WRITABLE));
+
+ g_type_class_add_private (class, sizeof (MateConfPropertyEditorPrivate));
+}
+
+static void
+mateconf_property_editor_set_prop (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MateConfPropertyEditor *peditor;
+ MateConfClient *client;
+ MateConfNotifyFunc cb;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object));
+
+ peditor = MATECONF_PROPERTY_EDITOR (object);
+
+ switch (prop_id) {
+ case PROP_KEY:
+ peditor->p->key = g_value_dup_string (value);
+ break;
+
+ case PROP_CALLBACK:
+ client = mateconf_client_get_default ();
+ cb = g_value_get_pointer (value);
+ peditor->p->callback = (MateConfClientNotifyFunc) cb;
+ if (peditor->p->handler_id != 0) {
+ mateconf_client_notify_remove (client,
+ peditor->p->handler_id);
+ }
+ peditor->p->handler_id =
+ mateconf_client_notify_add (client, peditor->p->key,
+ peditor->p->callback,
+ peditor, NULL, NULL);
+ g_object_unref (client);
+ break;
+
+ case PROP_CHANGESET:
+ peditor->p->changeset = g_value_get_pointer (value);
+ break;
+
+ case PROP_CONV_TO_WIDGET_CB:
+ peditor->p->conv_to_widget_cb = g_value_get_pointer (value);
+ break;
+
+ case PROP_CONV_FROM_WIDGET_CB:
+ peditor->p->conv_from_widget_cb = g_value_get_pointer (value);
+ break;
+
+ case PROP_UI_CONTROL:
+ peditor->p->ui_control = g_value_get_object (value);
+ g_object_weak_ref (peditor->p->ui_control, (GWeakNotify) g_object_unref, object);
+ break;
+ case PROP_DATA:
+ peditor->p->data = g_value_get_pointer (value);
+ break;
+ case PROP_DATA_FREE_CB:
+ peditor->p->data_free_cb = g_value_get_pointer (value);
+ break;
+ default:
+ g_warning ("Bad argument set");
+ break;
+ }
+}
+
+static void
+mateconf_property_editor_get_prop (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MateConfPropertyEditor *peditor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object));
+
+ peditor = MATECONF_PROPERTY_EDITOR (object);
+
+ switch (prop_id) {
+ case PROP_KEY:
+ g_value_set_string (value, peditor->p->key);
+ break;
+
+ case PROP_CHANGESET:
+ g_value_set_pointer (value, peditor->p->changeset);
+ break;
+
+ case PROP_DATA:
+ g_value_set_pointer (value, peditor->p->data);
+ break;
+ default:
+ g_warning ("Bad argument get");
+ break;
+ }
+}
+
+static void
+mateconf_property_editor_finalize (GObject *object)
+{
+ MateConfPropertyEditor *mateconf_property_editor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object));
+
+ mateconf_property_editor = MATECONF_PROPERTY_EDITOR (object);
+
+ g_free (mateconf_property_editor->p->key);
+
+ if (mateconf_property_editor->p->data_free_cb)
+ mateconf_property_editor->p->data_free_cb (mateconf_property_editor->p->data);
+
+ if (mateconf_property_editor->p->handler_id != 0) {
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+ mateconf_client_notify_remove (client,
+ mateconf_property_editor->p->handler_id);
+ g_object_unref (client);
+ }
+
+ G_OBJECT_CLASS (mateconf_property_editor_parent_class)->finalize (object);
+}
+
+static GObject *
+mateconf_peditor_new (const gchar *key,
+ MateConfClientNotifyFunc cb,
+ MateConfChangeSet *changeset,
+ GObject *ui_control,
+ const gchar *first_prop_name,
+ va_list var_args,
+ const gchar *first_custom,
+ ...)
+{
+ GObject *obj;
+ MateConfClient *client;
+ MateConfEntry *mateconf_entry;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (cb != NULL, NULL);
+
+ obj = g_object_new (mateconf_property_editor_get_type (),
+ "key", key,
+ "callback", cb,
+ "changeset", changeset,
+ "ui-control", ui_control,
+ NULL);
+
+ g_object_set_valist (obj, first_prop_name, var_args);
+
+ if (first_custom)
+ {
+ va_list custom_args;
+ va_start (custom_args, first_custom);
+ g_object_set_valist (obj, first_custom, custom_args);
+ va_end (custom_args);
+ }
+
+ client = mateconf_client_get_default ();
+ mateconf_entry = mateconf_client_get_entry (client, MATECONF_PROPERTY_EDITOR (obj)->p->key, NULL, TRUE, NULL);
+ MATECONF_PROPERTY_EDITOR (obj)->p->callback (client, 0, mateconf_entry, obj);
+ MATECONF_PROPERTY_EDITOR (obj)->p->inited = TRUE;
+ if (mateconf_entry)
+ mateconf_entry_free (mateconf_entry);
+ g_object_unref (client);
+
+ return obj;
+}
+
+const gchar *
+mateconf_property_editor_get_key (MateConfPropertyEditor *peditor)
+{
+ return peditor->p->key;
+}
+
+GObject *
+mateconf_property_editor_get_ui_control (MateConfPropertyEditor *peditor)
+{
+ return peditor->p->ui_control;
+}
+
+static void
+peditor_set_mateconf_value (MateConfPropertyEditor *peditor,
+ const gchar *key,
+ MateConfValue *value)
+{
+
+ if (peditor->p->changeset != NULL) {
+ if (value)
+ mateconf_change_set_set (peditor->p->changeset, peditor->p->key, value);
+ else
+ mateconf_change_set_unset (peditor->p->changeset, peditor->p->key);
+ } else {
+ MateConfClient *client = mateconf_client_get_default();
+
+ if (value)
+ mateconf_client_set (client, peditor->p->key, value, NULL);
+ else
+ mateconf_client_unset (client, peditor->p->key, NULL);
+
+ g_object_unref (client);
+ }
+
+}
+
+static void
+peditor_boolean_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (peditor->p->ui_control), mateconf_value_get_bool (value_wid));
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_boolean_widget_changed (MateConfPropertyEditor *peditor,
+ GtkToggleButton *tb)
+{
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+ value_wid = mateconf_value_new (MATECONF_VALUE_BOOL);
+ mateconf_value_set_bool (value_wid, gtk_toggle_button_get_active (tb));
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_boolean (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *checkbox,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (checkbox != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_boolean_value_changed,
+ changeset,
+ G_OBJECT (checkbox),
+ first_property_name,
+ var_args,
+ NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (checkbox, "toggled",
+ (GCallback) peditor_boolean_widget_changed, peditor);
+
+ return peditor;
+}
+
+static void
+peditor_integer_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+ const char *entry_current_text;
+ int entry_current_integer;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control));
+ entry_current_integer = strtol (entry_current_text, NULL, 10);
+ if (entry_current_integer != mateconf_value_get_int (value)) {
+ char *buf = g_strdup_printf ("%d", mateconf_value_get_int (value_wid));
+ gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), buf);
+ g_free (buf);
+ }
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_integer_widget_changed (MateConfPropertyEditor *peditor,
+ GtkEntry *entry)
+{
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_INT);
+
+ mateconf_value_set_int (value_wid, strtol (gtk_entry_get_text (entry), NULL, 10));
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+static GObject *
+mateconf_peditor_new_integer_valist (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ va_list var_args)
+{
+ GObject *peditor;
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_integer_value_changed,
+ changeset,
+ G_OBJECT (entry),
+ first_property_name,
+ var_args, NULL);
+
+ g_signal_connect_swapped (entry, "changed",
+ (GCallback) peditor_integer_widget_changed, peditor);
+
+ return peditor;
+}
+
+GObject *
+mateconf_peditor_new_integer (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (entry != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new_integer_valist
+ (changeset, key, entry,
+ first_property_name, var_args);
+
+ va_end (var_args);
+
+ return peditor;
+}
+
+static void
+peditor_string_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ const char *entry_current_text;
+ const char *mateconf_text;
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ mateconf_text = mateconf_value_get_string (value_wid);
+ entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control));
+
+ if (mateconf_text && strcmp (entry_current_text, mateconf_text) != 0) {
+ gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), mateconf_value_get_string (value_wid));
+ }
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_string_widget_changed (MateConfPropertyEditor *peditor,
+ GtkEntry *entry)
+{
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_STRING);
+
+ mateconf_value_set_string (value_wid, gtk_entry_get_text (entry));
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+static GObject *
+mateconf_peditor_new_string_valist (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ va_list var_args)
+{
+ GObject *peditor;
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_string_value_changed,
+ changeset,
+ G_OBJECT (entry),
+ first_property_name,
+ var_args, NULL);
+
+ g_signal_connect_swapped (entry, "changed",
+ (GCallback) peditor_string_widget_changed, peditor);
+
+ return peditor;
+}
+
+GObject *
+mateconf_peditor_new_string (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (entry != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new_string_valist
+ (changeset, key, entry,
+ first_property_name, var_args);
+
+ va_end (var_args);
+
+ return peditor;
+}
+
+static void
+peditor_color_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ const gchar *spec;
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ spec = mateconf_value_get_string (value_wid);
+ if (spec) {
+ GdkColor color;
+ gdk_color_parse (mateconf_value_get_string (value_wid), &color);
+ gtk_color_button_set_color (
+ GTK_COLOR_BUTTON (peditor->p->ui_control), &color);
+ }
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_color_widget_changed (MateConfPropertyEditor *peditor,
+ GtkColorButton *cb)
+{
+ gchar *str;
+ MateConfValue *value, *value_wid;
+ GdkColor color;
+
+ if (!peditor->p->inited) return;
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_STRING);
+ gtk_color_button_get_color (cb, &color);
+ str = g_strdup_printf ("#%02x%02x%02x", color.red >> 8,
+ color.green >> 8, color.blue >> 8);
+ mateconf_value_set_string (value_wid, str);
+ g_free (str);
+
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_color (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *cb,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (cb != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_COLOR_BUTTON (cb), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_color_value_changed,
+ changeset,
+ G_OBJECT (cb),
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (cb, "color_set",
+ (GCallback) peditor_color_widget_changed, peditor);
+
+ return peditor;
+}
+
+static int
+peditor_enum_int_from_string (GType type, const gchar *str, gboolean use_nick)
+{
+ GEnumClass *klass;
+ GEnumValue *val;
+ int ret = -1;
+
+ klass = g_type_class_ref (type);
+ if (use_nick)
+ val = g_enum_get_value_by_nick (klass, str);
+ else
+ val = g_enum_get_value_by_name (klass, str);
+
+ g_type_class_unref (klass);
+
+ if (val)
+ ret = val->value;
+
+ return ret;
+}
+
+static gchar*
+peditor_enum_string_from_int (GType type, const int index, gboolean use_nick)
+{
+ GEnumClass *klass;
+ GEnumValue *val;
+ gchar *ret = NULL;
+
+ klass = g_type_class_ref (type);
+ val = g_enum_get_value (klass, index);
+ if (val)
+ {
+ if (val->value_nick && use_nick)
+ ret = g_strdup (val->value_nick);
+ else
+ ret = g_strdup (val->value_name);
+ }
+
+ g_type_class_unref (klass);
+
+ return ret;
+}
+
+static MateConfValue*
+peditor_enum_conv_to_widget (MateConfPropertyEditor *peditor,
+ const MateConfValue *value)
+{
+ MateConfValue *ret;
+ MateConfPropertyEditorEnumData *data = peditor->p->data;
+ int index;
+
+ if (value->type == MATECONF_VALUE_INT)
+ return mateconf_value_copy (value);
+
+ ret = mateconf_value_new (MATECONF_VALUE_INT);
+
+ index = peditor_enum_int_from_string (data->enum_type,
+ mateconf_value_get_string (value),
+ data->use_nick);
+
+ mateconf_value_set_int (ret, index);
+
+ return ret;
+}
+
+static MateConfValue*
+peditor_enum_conv_from_widget (MateConfPropertyEditor *peditor,
+ const MateConfValue *value)
+{
+ MateConfValue *ret;
+ MateConfPropertyEditorEnumData *data = peditor->p->data;
+ gchar *str;
+
+ if (value->type == MATECONF_VALUE_STRING)
+ return mateconf_value_copy (value);
+
+ ret = mateconf_value_new (MATECONF_VALUE_STRING);
+ str = peditor_enum_string_from_int (data->enum_type,
+ mateconf_value_get_int (value),
+ data->use_nick);
+ mateconf_value_set_string (ret, str);
+ g_free (str);
+
+ return ret;
+}
+
+static void
+peditor_combo_box_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (peditor->p->ui_control), mateconf_value_get_int (value_wid));
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_combo_box_widget_changed (MateConfPropertyEditor *peditor,
+ GtkComboBox *combo_box)
+{
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+ value_wid = mateconf_value_new (MATECONF_VALUE_INT);
+ mateconf_value_set_int (value_wid, gtk_combo_box_get_active (combo_box));
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+ mateconf_value_free (value_wid);
+ if (value)
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_combo_box (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *combo_box,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (combo_box != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_combo_box_value_changed,
+ changeset,
+ G_OBJECT (combo_box),
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (combo_box, "changed",
+ (GCallback) peditor_combo_box_widget_changed, peditor);
+
+ return peditor;
+}
+
+GObject *
+mateconf_peditor_new_combo_box_with_enum (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *combo_box,
+ GType enum_type,
+ gboolean use_nick,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ MateConfPropertyEditorEnumData *data;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (combo_box != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
+ g_return_val_if_fail (enum_type != G_TYPE_NONE, NULL);
+
+ data = g_new0 (MateConfPropertyEditorEnumData, 1);
+ data->enum_type = enum_type;
+ data->use_nick = use_nick;
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_combo_box_value_changed,
+ changeset,
+ G_OBJECT (combo_box),
+ first_property_name,
+ var_args,
+ "conv-to-widget-cb",
+ peditor_enum_conv_to_widget,
+ "conv-from-widget-cb",
+ peditor_enum_conv_from_widget,
+ "data",
+ data,
+ "data-free-cb",
+ g_free,
+ NULL
+ );
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (combo_box, "changed",
+ (GCallback) peditor_combo_box_widget_changed, peditor);
+
+ return peditor;
+}
+
+static void
+peditor_select_radio_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ GSList *group, *link;
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control)));
+ group = g_slist_reverse (group);
+ link = g_slist_nth (group, mateconf_value_get_int (value_wid));
+ if (link && link->data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (link->data), TRUE);
+ mateconf_value_free (value_wid);
+ g_slist_free (group);
+ }
+}
+
+static void
+peditor_select_radio_widget_changed (MateConfPropertyEditor *peditor,
+ GtkToggleButton *tb)
+{
+ GSList *group;
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+ if (!gtk_toggle_button_get_active (tb)) return;
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_INT);
+ group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control)));
+ group = g_slist_reverse (group);
+
+ mateconf_value_set_int (value_wid, g_slist_index (group, tb));
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+ g_slist_free (group);
+}
+
+GObject *
+mateconf_peditor_new_select_radio (MateConfChangeSet *changeset,
+ const gchar *key,
+ GSList *radio_group,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ GtkRadioButton *first_button;
+ GSList *item;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (radio_group != NULL, NULL);
+ g_return_val_if_fail (radio_group->data != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL);
+
+ first_button = GTK_RADIO_BUTTON (radio_group->data);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_select_radio_value_changed,
+ changeset,
+ G_OBJECT (first_button),
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ for (item = radio_group; item != NULL; item = item->next)
+ g_signal_connect_swapped (item->data, "toggled",
+ (GCallback) peditor_select_radio_widget_changed, peditor);
+
+ return peditor;
+}
+
+static void
+peditor_numeric_range_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+
+ switch (value_wid->type) {
+ case MATECONF_VALUE_FLOAT:
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), mateconf_value_get_float (value_wid));
+ break;
+ case MATECONF_VALUE_INT:
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), mateconf_value_get_int (value_wid));
+ break;
+ default:
+ g_warning ("Unknown type in range peditor: %d\n", value_wid->type);
+ }
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_numeric_range_widget_changed (MateConfPropertyEditor *peditor,
+ GtkAdjustment *adjustment)
+{
+ MateConfValue *value, *value_wid, *default_value;
+ MateConfClient *client;
+
+ if (!peditor->p->inited) return;
+
+ /* We try to get the default type from the schemas. if not, we default
+ * to an int.
+ */
+ client = mateconf_client_get_default();
+
+ default_value = mateconf_client_get_default_from_schema (client,
+ peditor->p->key,
+ NULL);
+ g_object_unref (client);
+
+ if (default_value) {
+ value_wid = mateconf_value_new (default_value->type);
+ mateconf_value_free (default_value);
+ } else {
+ g_warning ("Unable to find a default value for key for %s.\n"
+ "I'll assume it is an integer, but that may break things.\n"
+ "Please be sure that the associated schema is installed",
+ peditor->p->key);
+ value_wid = mateconf_value_new (MATECONF_VALUE_INT);
+ }
+
+ g_assert (value_wid);
+
+ if (value_wid->type == MATECONF_VALUE_INT)
+ mateconf_value_set_int (value_wid, gtk_adjustment_get_value (adjustment));
+ else if (value_wid->type == MATECONF_VALUE_FLOAT)
+ mateconf_value_set_float (value_wid, gtk_adjustment_get_value (adjustment));
+ else {
+ g_warning ("unable to set a mateconf key for %s of type %d",
+ peditor->p->key,
+ value_wid->type);
+ mateconf_value_free (value_wid);
+ return;
+ }
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_numeric_range (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *range,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ GObject *adjustment = NULL;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (range != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_RANGE (range)||GTK_IS_SPIN_BUTTON (range), NULL);
+
+ if (GTK_IS_RANGE (range))
+ adjustment = G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (range)));
+ else if (GTK_IS_SPIN_BUTTON (range))
+ adjustment = G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (range)));
+ else
+ g_assert_not_reached ();
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_numeric_range_value_changed,
+ changeset,
+ adjustment,
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (adjustment, "value_changed",
+ (GCallback) peditor_numeric_range_widget_changed, peditor);
+
+ return peditor;
+}
+
+static gboolean
+guard_get_bool (MateConfPropertyEditor *peditor, const MateConfValue *value)
+{
+ if (value->type == MATECONF_VALUE_BOOL)
+ return mateconf_value_get_bool (value);
+ else
+ {
+ MateConfPropertyEditorEnumData *data = peditor->p->data;
+ int index = peditor_enum_int_from_string (data->enum_type, mateconf_value_get_string (value), data->use_nick);
+ return (index != data->enum_val_false);
+ }
+}
+
+static void
+guard_value_changed (MateConfPropertyEditor *peditor,
+ const gchar *key,
+ const MateConfValue *value,
+ GtkWidget *widget)
+{
+ gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value));
+}
+
+void
+mateconf_peditor_widget_set_guard (MateConfPropertyEditor *peditor,
+ GtkWidget *widget)
+{
+ MateConfClient *client;
+ MateConfValue *value;
+
+ g_return_if_fail (peditor != NULL);
+ g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (peditor));
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ client = mateconf_client_get_default ();
+ value = mateconf_client_get (client, peditor->p->key, NULL);
+ g_object_unref (client);
+
+ if (value) {
+ gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value));
+ mateconf_value_free (value);
+ } else {
+ g_warning ("NULL MateConf value: %s: possibly incomplete setup", peditor->p->key);
+ }
+
+ g_signal_connect (peditor, "value-changed", (GCallback) guard_value_changed, widget);
+}
+
+MateConfValue *
+mateconf_value_int_to_float (MateConfPropertyEditor *ignored, const MateConfValue *value)
+{
+ MateConfValue *new_value;
+
+ new_value = mateconf_value_new (MATECONF_VALUE_FLOAT);
+ mateconf_value_set_float (new_value, mateconf_value_get_int (value));
+ return new_value;
+}
+
+MateConfValue *
+mateconf_value_float_to_int (MateConfPropertyEditor *ignored, const MateConfValue *value)
+{
+ MateConfValue *new_value;
+
+ new_value = mateconf_value_new (MATECONF_VALUE_INT);
+ mateconf_value_set_int (new_value, mateconf_value_get_float (value));
+ return new_value;
+}
+
+static void
+peditor_font_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ const gchar *font;
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ font = mateconf_value_get_string (value_wid);
+ gtk_font_button_set_font_name (GTK_FONT_BUTTON (peditor->p->ui_control),
+ font);
+ mateconf_value_free (value_wid);
+ }
+}
+
+static void
+peditor_font_widget_changed (MateConfPropertyEditor *peditor,
+ GtkFontButton *font_button)
+{
+ const gchar *font_name;
+ MateConfValue *value, *value_wid = NULL;
+
+ if (!peditor->p->inited)
+ return;
+
+ font_name = gtk_font_button_get_font_name (font_button);
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_STRING);
+ mateconf_value_set_string (value_wid, font_name);
+
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_font (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *font_button,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_FONT_BUTTON (font_button), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new (key,
+ (MateConfClientNotifyFunc) peditor_font_value_changed,
+ changeset,
+ G_OBJECT (font_button),
+ first_property_name,
+ var_args,
+ NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (font_button, "font_set",
+ (GCallback) peditor_font_widget_changed, peditor);
+
+ return peditor;
+}
+
+static MateConfValue*
+peditor_enum_toggle_conv_to_widget (MateConfPropertyEditor *peditor,
+ const MateConfValue *value)
+{
+ MateConfValue *ret;
+ MateConfPropertyEditorEnumData *data = peditor->p->data;
+ int index;
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ return mateconf_value_copy (value);
+
+ ret = mateconf_value_new (MATECONF_VALUE_BOOL);
+
+ index = peditor_enum_int_from_string (data->enum_type,
+ mateconf_value_get_string (value),
+ data->use_nick);
+ mateconf_value_set_bool (ret, (index != data->enum_val_false));
+
+ return ret;
+}
+
+static MateConfValue*
+peditor_enum_toggle_conv_from_widget (MateConfPropertyEditor *peditor,
+ const MateConfValue *value)
+{
+ MateConfValue *ret;
+ MateConfPropertyEditorEnumData *data = peditor->p->data;
+ gchar *str;
+ int index;
+
+ if (value->type == MATECONF_VALUE_STRING)
+ return mateconf_value_copy (value);
+
+ ret = mateconf_value_new (MATECONF_VALUE_STRING);
+ if (mateconf_value_get_bool (value))
+ index = data->enum_val_true_fn (peditor, data->enum_val_true_fn_data);
+ else
+ index = data->enum_val_false;
+
+ str = peditor_enum_string_from_int (data->enum_type, index, data->use_nick);
+ mateconf_value_set_string (ret, str);
+ g_free (str);
+
+ return ret;
+}
+
+GObject *
+mateconf_peditor_new_enum_toggle (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *checkbox,
+ GType enum_type,
+ MateConfPEditorGetValueFn val_true_fn,
+ guint val_false,
+ gboolean use_nick,
+ gpointer data,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ MateConfPropertyEditorEnumData *enum_data;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (checkbox != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL);
+
+ enum_data = g_new0 (MateConfPropertyEditorEnumData, 1);
+ enum_data->enum_type = enum_type;
+ enum_data->enum_val_true_fn = val_true_fn;
+ enum_data->enum_val_true_fn_data = data;
+ enum_data->enum_val_false = val_false;
+ enum_data->use_nick = use_nick;
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_boolean_value_changed,
+ changeset,
+ G_OBJECT (checkbox),
+ first_property_name,
+ var_args,
+ "conv-to-widget-cb",
+ peditor_enum_toggle_conv_to_widget,
+ "conv-from-widget-cb",
+ peditor_enum_toggle_conv_from_widget,
+ "data",
+ enum_data,
+ "data-free-cb",
+ g_free,
+ NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (checkbox, "toggled",
+ (GCallback) peditor_boolean_widget_changed, peditor);
+
+ return peditor;
+}
+
+static gboolean
+peditor_image_set_filename (MateConfPropertyEditor *peditor, const gchar *filename)
+{
+ GdkPixbuf *pixbuf = NULL;
+ GtkImage *image = NULL;
+ const int scale = 100;
+ gchar *message = NULL;
+ GtkWidget *ui_control_child;
+ GList *l;
+
+ /* NULL is not valid, however "" is, but not an error (it's
+ * the default) */
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ {
+ message = g_strdup_printf (_("Couldn't find the file '%s'.\n\nPlease make "
+ "sure it exists and try again, "
+ "or choose a different background picture."),
+ filename);
+
+ }
+ else if (!(pixbuf = gdk_pixbuf_new_from_file_at_size (filename, scale, scale, NULL)))
+ {
+ message = g_strdup_printf (_("I don't know how to open the file '%s'.\n"
+ "Perhaps it's "
+ "a kind of picture that is not yet supported.\n\n"
+ "Please select a different picture instead."),
+ filename);
+ }
+
+ ui_control_child = gtk_bin_get_child (GTK_BIN (peditor->p->ui_control));
+
+ if (GTK_IS_IMAGE (ui_control_child))
+ image = GTK_IMAGE (ui_control_child);
+ else
+ {
+ for (l = gtk_container_get_children (GTK_CONTAINER (ui_control_child)); l != NULL; l = l->next)
+ {
+ if (GTK_IS_IMAGE (l->data))
+ image = GTK_IMAGE (l->data);
+ else if (GTK_IS_LABEL (l->data) && message == NULL)
+ {
+ gchar *base = g_path_get_basename (filename);
+ gtk_label_set_text (GTK_LABEL (l->data), base);
+ g_free (base);
+ }
+ }
+ }
+
+ if (message)
+ {
+ if (peditor->p->inited)
+ {
+ GtkWidget *box;
+
+ box = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ message);
+ gtk_dialog_run (GTK_DIALOG (box));
+ gtk_widget_destroy (box);
+ } else {
+ gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE,
+ GTK_ICON_SIZE_BUTTON);
+ }
+ g_free (message);
+
+ return FALSE;
+ }
+
+ gtk_image_set_from_pixbuf (image, pixbuf);
+ g_object_unref (pixbuf);
+
+ return TRUE;
+}
+
+static void
+peditor_image_chooser_response_cb (GtkWidget *chooser,
+ gint response,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+ gchar *filename;
+
+ if (response == GTK_RESPONSE_CANCEL ||
+ response == GTK_RESPONSE_DELETE_EVENT)
+ {
+ gtk_widget_destroy (chooser);
+ return;
+ }
+
+ if (!peditor->p->inited)
+ return;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+ if (!(filename && peditor_image_set_filename (peditor, filename)))
+ {
+ g_free (filename);
+ return;
+ }
+
+ value_wid = mateconf_value_new (MATECONF_VALUE_STRING);
+ mateconf_value_set_string (value_wid, filename);
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+
+ mateconf_value_free (value_wid);
+ mateconf_value_free (value);
+ g_free (filename);
+ gtk_widget_destroy (chooser);
+}
+
+static void
+peditor_image_chooser_update_preview_cb (GtkFileChooser *chooser,
+ GtkImage *preview)
+{
+ char *filename;
+ GdkPixbuf *pixbuf = NULL;
+ const int scale = 100;
+
+ filename = gtk_file_chooser_get_preview_filename (chooser);
+
+ if (filename != NULL && g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ pixbuf = gdk_pixbuf_new_from_file_at_size (filename, scale, scale, NULL);
+
+ gtk_image_set_from_pixbuf (preview, pixbuf);
+
+ g_free (filename);
+
+ if (pixbuf != NULL)
+ g_object_unref (pixbuf);
+}
+
+static void
+peditor_image_clicked_cb (MateConfPropertyEditor *peditor, GtkButton *button)
+{
+ MateConfValue *value = NULL, *value_wid;
+ const gchar *filename;
+ GtkWidget *chooser, *toplevel, *preview, *preview_box;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ chooser = gtk_file_chooser_dialog_new (_("Please select an image."),
+ GTK_IS_WINDOW (toplevel) ? GTK_WINDOW (toplevel)
+ : NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Select"), GTK_RESPONSE_OK,
+ NULL);
+
+ preview = gtk_image_new ();
+
+ preview_box = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (preview_box), preview, FALSE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (preview_box), 6);
+
+ gtk_widget_show_all (preview_box);
+ gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (chooser),
+ preview_box);
+ gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (chooser),
+ TRUE);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
+ gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
+
+ /* need the current filename */
+ if (peditor->p->changeset)
+ mateconf_change_set_check_value (peditor->p->changeset, peditor->p->key, &value);
+
+ if (value)
+ {
+ /* the one we got is not a copy */
+ value = mateconf_value_copy (value);
+ }
+ else
+ {
+ MateConfClient *client = mateconf_client_get_default ();
+ value = mateconf_client_get (client, peditor->p->key, NULL);
+ g_object_unref (client);
+ }
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ filename = mateconf_value_get_string (value_wid);
+
+ if (filename && strcmp (filename, ""))
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), filename);
+
+ g_signal_connect (chooser, "update-preview",
+ G_CALLBACK (peditor_image_chooser_update_preview_cb),
+ preview);
+ g_signal_connect (chooser, "response",
+ G_CALLBACK (peditor_image_chooser_response_cb),
+ peditor);
+
+ if (gtk_grab_get_current ())
+ gtk_grab_add (chooser);
+
+ gtk_widget_show (chooser);
+
+ mateconf_value_free (value);
+ mateconf_value_free (value_wid);
+}
+
+static void
+peditor_image_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value, *value_wid;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ const gchar *filename;
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+ filename = mateconf_value_get_string (value_wid);
+ peditor_image_set_filename (peditor, filename);
+ mateconf_value_free (value_wid);
+ }
+}
+
+GObject *
+mateconf_peditor_new_image (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *button,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (button != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_image_value_changed,
+ changeset,
+ G_OBJECT (button),
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (button, "clicked",
+ (GCallback) peditor_image_clicked_cb, peditor);
+
+ return peditor;
+}
+
+GObject *
+mateconf_peditor_new_select_radio_with_enum (MateConfChangeSet *changeset,
+ const gchar *key,
+ GSList *radio_group,
+ GType enum_type,
+ gboolean use_nick,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ MateConfPropertyEditorEnumData *enum_data;
+ GtkRadioButton *first_button;
+ GSList *item;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (radio_group != NULL, NULL);
+ g_return_val_if_fail (radio_group->data != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL);
+
+ enum_data = g_new0 (MateConfPropertyEditorEnumData, 1);
+ enum_data->enum_type = enum_type;
+ enum_data->use_nick = use_nick;
+
+ first_button = GTK_RADIO_BUTTON (radio_group->data);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_select_radio_value_changed,
+ changeset,
+ G_OBJECT (first_button),
+ first_property_name,
+ var_args,
+ "conv-to-widget-cb",
+ peditor_enum_conv_to_widget,
+ "conv-from-widget-cb",
+ peditor_enum_conv_from_widget,
+ "data",
+ enum_data,
+ "data-free-cb",
+ g_free,
+ NULL);
+
+ va_end (var_args);
+
+ for (item = radio_group; item != NULL; item = item->next)
+ g_signal_connect_swapped (item->data, "toggled",
+ (GCallback) peditor_select_radio_widget_changed, peditor);
+
+ return peditor;
+}
+
+static void
+peditor_tree_view_value_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MateConfPropertyEditor *peditor)
+{
+ MateConfValue *value;
+
+ if (peditor->p->changeset != NULL)
+ mateconf_change_set_remove (peditor->p->changeset, peditor->p->key);
+
+ if (entry && (value = mateconf_entry_get_value (entry))) {
+ GtkTreeView *treeview;
+ GtkTreeSelection *selection;
+ MateConfValue *value_wid;
+
+ treeview = GTK_TREE_VIEW (peditor->p->ui_control);
+ selection = gtk_tree_view_get_selection (treeview);
+
+ value_wid = peditor->p->conv_to_widget_cb (peditor, value);
+
+ if (value_wid != NULL) {
+ GtkTreePath *path = gtk_tree_path_new_from_string (
+ mateconf_value_get_string (value_wid));
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+ mateconf_value_free (value_wid);
+ } else {
+ gtk_tree_selection_unselect_all (selection);
+ }
+ }
+}
+
+static void
+peditor_tree_view_widget_changed (MateConfPropertyEditor *peditor,
+ GtkTreeSelection *selection)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ MateConfValue *value, *value_wid;
+
+ if (!peditor->p->inited) return;
+
+ /* we don't support GTK_SELECTION_MULTIPLE */
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gchar *path;
+
+ path = gtk_tree_model_get_string_from_iter (model, &iter);
+ value_wid = mateconf_value_new (MATECONF_VALUE_STRING);
+ mateconf_value_set_string (value_wid, path);
+ g_free (path);
+ } else
+ value_wid = NULL;
+
+ value = peditor->p->conv_from_widget_cb (peditor, value_wid);
+ peditor_set_mateconf_value (peditor, peditor->p->key, value);
+ g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
+
+ if (value_wid)
+ mateconf_value_free (value_wid);
+ if (value)
+ mateconf_value_free (value);
+}
+
+GObject *
+mateconf_peditor_new_tree_view (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *tree_view,
+ const gchar *first_property_name,
+ ...)
+{
+ GObject *peditor;
+ va_list var_args;
+
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ va_start (var_args, first_property_name);
+
+ peditor = mateconf_peditor_new
+ (key,
+ (MateConfClientNotifyFunc) peditor_tree_view_value_changed,
+ changeset,
+ G_OBJECT (tree_view),
+ first_property_name,
+ var_args, NULL);
+
+ va_end (var_args);
+
+ g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)),
+ "changed",
+ (GCallback) peditor_tree_view_widget_changed, peditor);
+
+ return peditor;
+}
diff --git a/capplets/common/mateconf-property-editor.h b/capplets/common/mateconf-property-editor.h
new file mode 100644
index 00000000..1f15b9fd
--- /dev/null
+++ b/capplets/common/mateconf-property-editor.h
@@ -0,0 +1,162 @@
+/* -*- mode: c; style: linux -*- */
+
+/* mateconf-property-editor.h
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Written by Bradford Hovinen <[email protected]>
+ *
+ * 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, 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 __MATECONF_PROPERTY_EDITOR_H
+#define __MATECONF_PROPERTY_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <mateconf/mateconf-client.h>
+#include <mateconf/mateconf-changeset.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MATECONF_PROPERTY_EDITOR(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, mateconf_property_editor_get_type (), MateConfPropertyEditor)
+#define MATECONF_PROPERTY_EDITOR_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, mateconf_property_editor_get_type (), MateConfPropertyEditorClass)
+#define IS_MATECONF_PROPERTY_EDITOR(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, mateconf_property_editor_get_type ())
+
+typedef struct _MateConfPropertyEditor MateConfPropertyEditor;
+typedef struct _MateConfPropertyEditorClass MateConfPropertyEditorClass;
+typedef struct _MateConfPropertyEditorPrivate MateConfPropertyEditorPrivate;
+
+typedef MateConfValue *(*MateConfPEditorValueConvFn) (MateConfPropertyEditor *peditor, const MateConfValue *);
+typedef int (*MateConfPEditorGetValueFn) (MateConfPropertyEditor *peditor, gpointer data);
+
+struct _MateConfPropertyEditor
+{
+ GObject parent;
+
+ MateConfPropertyEditorPrivate *p;
+};
+
+struct _MateConfPropertyEditorClass
+{
+ GObjectClass g_object_class;
+
+ void (*value_changed) (MateConfPropertyEditor *peditor, gchar *key, const MateConfValue *value);
+};
+
+GType mateconf_property_editor_get_type (void);
+
+const gchar *mateconf_property_editor_get_key (MateConfPropertyEditor *peditor);
+GObject *mateconf_property_editor_get_ui_control (MateConfPropertyEditor *peditor);
+
+GObject *mateconf_peditor_new_boolean (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *checkbox,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_enum_toggle (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *checkbox,
+ GType enum_type,
+ MateConfPEditorGetValueFn val_true_fn,
+ guint val_false,
+ gboolean use_nick,
+ gpointer data,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_integer (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ ...);
+GObject *mateconf_peditor_new_string (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *entry,
+ const gchar *first_property_name,
+ ...);
+GObject *mateconf_peditor_new_color (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *color_entry,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_combo_box (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *combo_box,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_combo_box_with_enum (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *combo_box,
+ GType enum_type,
+ gboolean use_nick,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_select_radio (MateConfChangeSet *changeset,
+ const gchar *key,
+ GSList *radio_group,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_select_radio_with_enum (MateConfChangeSet *changeset,
+ const gchar *key,
+ GSList *radio_group,
+ GType enum_type,
+ gboolean use_nick,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_numeric_range (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *range,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_font (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *font_button,
+ const gchar *first_property_name,
+ ...);
+
+GObject *mateconf_peditor_new_image (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *button,
+ const gchar *first_property,
+ ...);
+
+GObject *mateconf_peditor_new_tree_view (MateConfChangeSet *changeset,
+ const gchar *key,
+ GtkWidget *tree_view,
+ const gchar *first_property_name,
+ ...);
+
+void mateconf_peditor_widget_set_guard (MateConfPropertyEditor *peditor,
+ GtkWidget *widget);
+
+/* some convenience callbacks to map int <-> float */
+MateConfValue *mateconf_value_int_to_float (MateConfPropertyEditor *ignored, MateConfValue const *value);
+MateConfValue *mateconf_value_float_to_int (MateConfPropertyEditor *ignored, MateConfValue const *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MATECONF_PROPERTY_EDITOR_H */
diff --git a/capplets/common/theme-thumbnail.c b/capplets/common/theme-thumbnail.c
new file mode 100644
index 00000000..4afa4454
--- /dev/null
+++ b/capplets/common/theme-thumbnail.c
@@ -0,0 +1,1144 @@
+#include <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <marco-private/util.h>
+#include <marco-private/theme.h>
+#include <marco-private/theme-parser.h>
+#include <marco-private/preview-widget.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h>
+
+/* We have to #undef this as marco #defines these. */
+#undef _
+#undef N_
+
+#include <glib.h>
+
+#include "theme-thumbnail.h"
+#include "gtkrc-utils.h"
+#include "capplet-util.h"
+
+typedef struct {
+ gboolean set;
+ gint thumbnail_width;
+ gint thumbnail_height;
+ GByteArray* data;
+ gchar* theme_name;
+ ThemeThumbnailFunc func;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ GIOChannel* channel;
+ guint watch_id;
+} ThemeThumbnailAsyncData;
+
+
+static ThemeThumbnailAsyncData async_data;
+
+/* Protocol */
+
+/* Our protocol is pretty simple. The parent process will write several strings
+ * (separated by a '\000'). They are the widget theme, the wm theme, the icon
+ * theme, etc. Then, it will wait for the child to write back the data. The
+ * parent expects ICON_SIZE_WIDTH * ICON_SIZE_HEIGHT * 4 bytes of information.
+ * After that, the child is ready for the next theme to render.
+ */
+
+enum {
+ READY_FOR_THEME,
+ READING_TYPE,
+ READING_CONTROL_THEME_NAME,
+ READING_GTK_COLOR_SCHEME,
+ READING_WM_THEME_NAME,
+ READING_ICON_THEME_NAME,
+ READING_APPLICATION_FONT,
+ WRITING_PIXBUF_DATA
+};
+
+typedef struct {
+ gint status;
+ GByteArray* type;
+ GByteArray* control_theme_name;
+ GByteArray* gtk_color_scheme;
+ GByteArray* wm_theme_name;
+ GByteArray* icon_theme_name;
+ GByteArray* application_font;
+} ThemeThumbnailData;
+
+typedef struct {
+ gchar* thumbnail_type;
+ gpointer theme_info;
+ ThemeThumbnailFunc func;
+ gpointer user_data;
+ GDestroyNotify destroy;
+} ThemeQueueItem;
+
+static GList* theme_queue = NULL;
+
+static int pipe_to_factory_fd[2];
+static int pipe_from_factory_fd[2];
+
+#define THUMBNAIL_TYPE_META "meta"
+#define THUMBNAIL_TYPE_GTK "gtk"
+#define THUMBNAIL_TYPE_MARCO "marco"
+#define THUMBNAIL_TYPE_ICON "icon"
+
+#define META_THUMBNAIL_SIZE 128
+#define GTK_THUMBNAIL_SIZE 96
+#define MARCO_THUMBNAIL_WIDTH 120
+#define MARCO_THUMBNAIL_HEIGHT 60
+
+/* This draw the thumbnail of gtk
+ */
+static GdkPixmap* draw_window_on_pixbuf(GtkWidget* widget)
+{
+ GdkVisual* visual;
+ GdkPixmap* pixmap;
+ GtkStyle* style;
+ GdkScreen* screen = gdk_screen_get_default();
+ GdkWindow* window;
+ gint width, height;
+
+ gtk_widget_ensure_style(widget);
+
+ style = gtk_widget_get_style(widget);
+
+ g_assert(style);
+ g_assert(style->font_desc);
+
+ gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
+
+ visual = gtk_widget_get_visual(widget);
+ pixmap = gdk_pixmap_new(NULL, width, height, visual->depth);
+ gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), gtk_widget_get_colormap(widget));
+
+ window = gtk_widget_get_window(widget);
+
+ /* This is a hack for the default resize grip on Ubuntu.
+ * Once again, thank you Ubuntu.
+ *
+ * We need to add a --enable-ubuntu for this.
+ */
+ #ifdef UBUNTU
+ gtk_window_set_has_resize_grip(GTK_WINDOW(widget), FALSE);
+ #endif
+
+ gdk_window_redirect_to_drawable(window, pixmap, 0, 0, 0, 0, width, height);
+ gdk_window_set_override_redirect(window, TRUE);
+ gtk_window_move(GTK_WINDOW(widget), gdk_screen_get_width(screen), gdk_screen_get_height(screen));
+ gtk_widget_show(widget);
+
+ gdk_window_process_updates(window, TRUE);
+ gtk_widget_hide(widget);
+
+ return pixmap;
+}
+
+static void pixbuf_apply_mask_region(GdkPixbuf* pixbuf, GdkRegion* region)
+{
+ gint nchannels, rowstride, w, h;
+ guchar *pixels, *p;
+
+ g_return_if_fail (pixbuf);
+ g_return_if_fail (region);
+
+ nchannels = gdk_pixbuf_get_n_channels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+
+ /* we need an alpha channel ... */
+ if (!gdk_pixbuf_get_has_alpha (pixbuf) || nchannels != 4)
+ return;
+
+ for (w = 0; w < gdk_pixbuf_get_width (pixbuf); ++w)
+ for (h = 0; h < gdk_pixbuf_get_height (pixbuf); ++h)
+ {
+ if (!gdk_region_point_in (region, w, h))
+ {
+ p = pixels + h * rowstride + w * nchannels;
+ if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ p[0] = 0x0;
+ else
+ p[3] = 0x0;
+ }
+ }
+
+}
+
+static GdkPixbuf *
+create_folder_icon (char *icon_theme_name)
+{
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *folder_icon = NULL;
+ GtkIconInfo *folder_icon_info;
+ gchar *example_icon_name;
+ const gchar *icon_names[5];
+ gint i;
+
+ icon_theme = gtk_icon_theme_new ();
+ gtk_icon_theme_set_custom_theme (icon_theme, icon_theme_name);
+
+ i = 0;
+ /* Get the Example icon name in the theme if specified */
+ example_icon_name = gtk_icon_theme_get_example_icon_name (icon_theme);
+ if (example_icon_name != NULL)
+ icon_names[i++] = example_icon_name;
+ icon_names[i++] = "x-directory-normal";
+ icon_names[i++] = "mate-fs-directory";
+ icon_names[i++] = "folder";
+ icon_names[i++] = NULL;
+
+ folder_icon_info = gtk_icon_theme_choose_icon (icon_theme, icon_names, 48, GTK_ICON_LOOKUP_FORCE_SIZE);
+ if (folder_icon_info != NULL)
+ {
+ folder_icon = gtk_icon_info_load_icon (folder_icon_info, NULL);
+ gtk_icon_info_free (folder_icon_info);
+ }
+
+ g_object_unref (icon_theme);
+ g_free (example_icon_name);
+
+ /* render the icon to the thumbnail */
+ if (folder_icon == NULL)
+ {
+ GtkWidget *dummy;
+ dummy = gtk_label_new ("");
+
+ folder_icon = gtk_widget_render_icon (dummy,
+ GTK_STOCK_MISSING_IMAGE,
+ GTK_ICON_SIZE_DIALOG,
+ NULL);
+
+ gtk_widget_destroy (dummy);
+ }
+
+ return folder_icon;
+}
+
+static GdkPixbuf *
+create_meta_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data)
+{
+ GtkWidget *window;
+ GtkWidget *preview;
+ GtkWidget *vbox;
+ GtkWidget *align;
+ GtkWidget *box;
+ GtkWidget *stock_button;
+ GtkWidget *checkbox;
+ GtkWidget *radio;
+
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+ GtkAllocation vbox_allocation;
+ GdkPixmap *pixmap;
+ MetaFrameFlags flags;
+ MetaTheme *theme;
+ GdkPixbuf *pixbuf, *icon;
+ int icon_width, icon_height;
+ GdkRegion *region;
+
+ g_object_set (gtk_settings_get_default (),
+ "gtk-theme-name", (char *) theme_thumbnail_data->control_theme_name->data,
+ "gtk-font-name", (char *) theme_thumbnail_data->application_font->data,
+ "gtk-icon-theme-name", (char *) theme_thumbnail_data->icon_theme_name->data,
+ "gtk-color-scheme", (char *) theme_thumbnail_data->gtk_color_scheme->data,
+ NULL);
+
+ theme = meta_theme_load ((char *) theme_thumbnail_data->wm_theme_name->data, NULL);
+ if (theme == NULL)
+ return NULL;
+
+ /* Represent the icon theme */
+ icon = create_folder_icon ((char *) theme_thumbnail_data->icon_theme_name->data);
+ icon_width = gdk_pixbuf_get_width (icon);
+ icon_height = gdk_pixbuf_get_height (icon);
+
+ /* Create a fake window */
+ flags = META_FRAME_ALLOWS_DELETE |
+ META_FRAME_ALLOWS_MENU |
+ META_FRAME_ALLOWS_MINIMIZE |
+ META_FRAME_ALLOWS_MAXIMIZE |
+ META_FRAME_ALLOWS_VERTICAL_RESIZE |
+ META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
+ META_FRAME_HAS_FOCUS |
+ META_FRAME_ALLOWS_SHADE |
+ META_FRAME_ALLOWS_MOVE;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ preview = meta_preview_new ();
+ gtk_container_add (GTK_CONTAINER (window), preview);
+ gtk_widget_realize (window);
+ gtk_widget_realize (preview);
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_container_add (GTK_CONTAINER (preview), vbox);
+ align = gtk_alignment_new (0, 0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
+ stock_button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
+ gtk_container_add (GTK_CONTAINER (align), stock_button);
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new ();
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), checkbox, FALSE, FALSE, 0);
+ radio = gtk_radio_button_new (NULL);
+ gtk_box_pack_start (GTK_BOX (box), radio, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (preview);
+
+ meta_preview_set_frame_flags (META_PREVIEW (preview), flags);
+ meta_preview_set_theme (META_PREVIEW (preview), theme);
+ meta_preview_set_title (META_PREVIEW (preview), "");
+
+ gtk_window_set_default_size (GTK_WINDOW (window), META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE);
+
+ gtk_widget_size_request (window, &requisition);
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation.width = META_THUMBNAIL_SIZE;
+ allocation.height = META_THUMBNAIL_SIZE;
+ gtk_widget_size_allocate (window, &allocation);
+ gtk_widget_size_request (window, &requisition);
+
+ pixmap = draw_window_on_pixbuf (window);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE);
+ gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE);
+
+ gtk_widget_get_allocation (vbox, &vbox_allocation);
+
+ /* Add the icon theme to the pixbuf */
+ gdk_pixbuf_composite (icon, pixbuf,
+ vbox_allocation.x + vbox_allocation.width - icon_width - 5,
+ vbox_allocation.y + vbox_allocation.height - icon_height - 5,
+ icon_width, icon_height,
+ vbox_allocation.x + vbox_allocation.width - icon_width - 5,
+ vbox_allocation.y + vbox_allocation.height - icon_height - 5,
+ 1.0, 1.0, GDK_INTERP_BILINEAR, 255);
+ region = meta_preview_get_clip_region (META_PREVIEW (preview),
+ META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE);
+ pixbuf_apply_mask_region (pixbuf, region);
+ gdk_region_destroy (region);
+
+ g_object_unref (icon);
+ gtk_widget_destroy (window);
+ meta_theme_free (theme);
+ g_object_unref (pixmap);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+create_gtk_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data)
+{
+ GtkSettings *settings;
+ GtkWidget *window, *vbox, *box, *stock_button, *checkbox, *radio;
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+ GdkPixmap *pixmap;
+ GdkPixbuf *pixbuf, *retval;
+ gint width, height;
+
+ settings = gtk_settings_get_default ();
+ g_object_set (settings, "gtk-theme-name", (char *) theme_thumbnail_data->control_theme_name->data,
+ "gtk-color-scheme", (char *) theme_thumbnail_data->gtk_color_scheme->data,
+ NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ box = gtk_hbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
+ stock_button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
+ gtk_box_pack_start (GTK_BOX (box), stock_button, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new ();
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), checkbox, FALSE, FALSE, 0);
+ radio = gtk_radio_button_new_from_widget (NULL);
+ gtk_box_pack_start (GTK_BOX (box), radio, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (vbox);
+ gtk_widget_realize (stock_button);
+ gtk_widget_realize (gtk_bin_get_child (GTK_BIN (stock_button)));
+ gtk_widget_realize (checkbox);
+ gtk_widget_realize (radio);
+ gtk_widget_map (stock_button);
+ gtk_widget_map (gtk_bin_get_child (GTK_BIN (stock_button)));
+ gtk_widget_map (checkbox);
+ gtk_widget_map (radio);
+
+ gtk_widget_size_request (window, &requisition);
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation.width = requisition.width;
+ allocation.height = requisition.height;
+ gtk_widget_size_allocate (window, &allocation);
+ gtk_widget_size_request (window, &requisition);
+
+ gtk_window_get_size (GTK_WINDOW (window), &width, &height);
+
+ pixmap = draw_window_on_pixbuf (window);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+ gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, width, height);
+
+ retval = gdk_pixbuf_scale_simple (pixbuf,
+ GTK_THUMBNAIL_SIZE,
+ (int) GTK_THUMBNAIL_SIZE * (((double) height) / ((double) width)),
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ gtk_widget_destroy (window);
+ g_object_unref (pixmap);
+
+ return retval;
+}
+
+static GdkPixbuf *
+create_marco_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data)
+{
+ GtkWidget *window, *preview, *dummy;
+ MetaFrameFlags flags;
+ MetaTheme *theme;
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+ GdkPixmap *pixmap;
+ GdkPixbuf *pixbuf, *retval;
+ GdkRegion *region;
+
+ theme = meta_theme_load ((char *) theme_thumbnail_data->wm_theme_name->data, NULL);
+ if (theme == NULL)
+ return NULL;
+
+ flags = META_FRAME_ALLOWS_DELETE |
+ META_FRAME_ALLOWS_MENU |
+ META_FRAME_ALLOWS_MINIMIZE |
+ META_FRAME_ALLOWS_MAXIMIZE |
+ META_FRAME_ALLOWS_VERTICAL_RESIZE |
+ META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
+ META_FRAME_HAS_FOCUS |
+ META_FRAME_ALLOWS_SHADE |
+ META_FRAME_ALLOWS_MOVE;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2);
+
+ preview = meta_preview_new ();
+ meta_preview_set_frame_flags (META_PREVIEW (preview), flags);
+ meta_preview_set_theme (META_PREVIEW (preview), theme);
+ meta_preview_set_title (META_PREVIEW (preview), "");
+ gtk_container_add (GTK_CONTAINER (window), preview);
+
+ dummy = gtk_label_new ("");
+ gtk_container_add (GTK_CONTAINER (preview), dummy);
+
+ gtk_widget_realize (window);
+ gtk_widget_realize (preview);
+ gtk_widget_realize (dummy);
+ gtk_widget_show_all (preview);
+ gtk_widget_map (dummy);
+
+ gtk_widget_size_request (window, &requisition);
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation.width = (int) MARCO_THUMBNAIL_WIDTH * 1.2;
+ allocation.height = (int) MARCO_THUMBNAIL_HEIGHT * 1.2;
+ gtk_widget_size_allocate (window, &allocation);
+ gtk_widget_size_request (window, &requisition);
+
+ pixmap = draw_window_on_pixbuf (window);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2);
+ gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2);
+
+ region = meta_preview_get_clip_region (META_PREVIEW (preview),
+ MARCO_THUMBNAIL_WIDTH * 1.2, MARCO_THUMBNAIL_HEIGHT * 1.2);
+ pixbuf_apply_mask_region (pixbuf, region);
+ gdk_region_destroy (region);
+
+
+ retval = gdk_pixbuf_scale_simple (pixbuf,
+ MARCO_THUMBNAIL_WIDTH,
+ MARCO_THUMBNAIL_HEIGHT,
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+
+ gtk_widget_destroy (window);
+ meta_theme_free (theme);
+ g_object_unref (pixmap);
+
+ return retval;
+}
+
+static GdkPixbuf *
+create_icon_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data)
+{
+ return create_folder_icon ((char *) theme_thumbnail_data->icon_theme_name->data);
+}
+
+
+static void
+handle_bytes (const gchar *buffer,
+ gint bytes_read,
+ ThemeThumbnailData *theme_thumbnail_data)
+{
+ const gchar *ptr;
+ ptr = buffer;
+
+ while (bytes_read > 0)
+ {
+ char *nil;
+
+ switch (theme_thumbnail_data->status)
+ {
+ case READY_FOR_THEME:
+ theme_thumbnail_data->status = READING_TYPE;
+ /* fall through */
+ case READING_TYPE:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->type, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->type, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = READING_CONTROL_THEME_NAME;
+ }
+ break;
+
+ case READING_CONTROL_THEME_NAME:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->control_theme_name, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->control_theme_name, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = READING_GTK_COLOR_SCHEME;
+ }
+ break;
+
+ case READING_GTK_COLOR_SCHEME:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->gtk_color_scheme, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->gtk_color_scheme, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = READING_WM_THEME_NAME;
+ }
+ break;
+
+ case READING_WM_THEME_NAME:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->wm_theme_name, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->wm_theme_name, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = READING_ICON_THEME_NAME;
+ }
+ break;
+
+ case READING_ICON_THEME_NAME:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->icon_theme_name, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->icon_theme_name, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = READING_APPLICATION_FONT;
+ }
+ break;
+
+ case READING_APPLICATION_FONT:
+ nil = memchr (ptr, '\000', bytes_read);
+ if (nil == NULL)
+ {
+ g_byte_array_append (theme_thumbnail_data->application_font, ptr, bytes_read);
+ bytes_read = 0;
+ }
+ else
+ {
+ g_byte_array_append (theme_thumbnail_data->application_font, ptr, nil - ptr + 1);
+ bytes_read -= (nil - ptr + 1);
+ ptr = nil + 1;
+ theme_thumbnail_data->status = WRITING_PIXBUF_DATA;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+}
+
+static gboolean
+message_from_capplet (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gchar buffer[1024];
+ GIOStatus status;
+ gsize bytes_read;
+ ThemeThumbnailData *theme_thumbnail_data;
+
+ theme_thumbnail_data = (ThemeThumbnailData *) data;
+ status = g_io_channel_read_chars (source,
+ buffer,
+ 1024,
+ &bytes_read,
+ NULL);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ handle_bytes (buffer, bytes_read, theme_thumbnail_data);
+
+ if (theme_thumbnail_data->status == WRITING_PIXBUF_DATA)
+ {
+ GdkPixbuf *pixbuf = NULL;
+ gint i, rowstride;
+ guchar *pixels;
+ gint width, height;
+ const gchar *type = (const gchar *) theme_thumbnail_data->type->data;
+
+ if (!strcmp (type, THUMBNAIL_TYPE_META))
+ pixbuf = create_meta_theme_pixbuf (theme_thumbnail_data);
+ else if (!strcmp (type, THUMBNAIL_TYPE_GTK))
+ pixbuf = create_gtk_theme_pixbuf (theme_thumbnail_data);
+ else if (!strcmp (type, THUMBNAIL_TYPE_MARCO))
+ pixbuf = create_marco_theme_pixbuf (theme_thumbnail_data);
+ else if (!strcmp (type, THUMBNAIL_TYPE_ICON))
+ pixbuf = create_icon_theme_pixbuf (theme_thumbnail_data);
+ else
+ g_assert_not_reached ();
+
+ if (pixbuf == NULL) {
+ width = height = rowstride = 0;
+ pixels = NULL;
+ } else {
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ }
+
+ /* Write the pixbuf's size */
+ write (pipe_from_factory_fd[1], &width, sizeof (width));
+ write (pipe_from_factory_fd[1], &height, sizeof (height));
+
+ for (i = 0; i < height; i++)
+ {
+ write (pipe_from_factory_fd[1], pixels + rowstride * i, width * gdk_pixbuf_get_n_channels (pixbuf));
+ }
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+ g_byte_array_set_size (theme_thumbnail_data->type, 0);
+ g_byte_array_set_size (theme_thumbnail_data->control_theme_name, 0);
+ g_byte_array_set_size (theme_thumbnail_data->gtk_color_scheme, 0);
+ g_byte_array_set_size (theme_thumbnail_data->wm_theme_name, 0);
+ g_byte_array_set_size (theme_thumbnail_data->icon_theme_name, 0);
+ g_byte_array_set_size (theme_thumbnail_data->application_font, 0);
+ theme_thumbnail_data->status = READY_FOR_THEME;
+ }
+ return TRUE;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ case G_IO_STATUS_EOF:
+ case G_IO_STATUS_ERROR:
+ _exit (0);
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
+}
+
+static void
+generate_next_in_queue (void)
+{
+ ThemeQueueItem *item;
+
+ if (theme_queue == NULL)
+ return;
+
+ item = theme_queue->data;
+ theme_queue = g_list_delete_link (theme_queue, g_list_first (theme_queue));
+
+ if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_META))
+ generate_meta_theme_thumbnail_async ((MateThemeMetaInfo *) item->theme_info,
+ item->func,
+ item->user_data,
+ item->destroy);
+ else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_GTK))
+ generate_gtk_theme_thumbnail_async ((MateThemeInfo *) item->theme_info,
+ item->func,
+ item->user_data,
+ item->destroy);
+ else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_MARCO))
+ generate_marco_theme_thumbnail_async ((MateThemeInfo *) item->theme_info,
+ item->func,
+ item->user_data,
+ item->destroy);
+ else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_ICON))
+ generate_icon_theme_thumbnail_async ((MateThemeIconInfo *) item->theme_info,
+ item->func,
+ item->user_data,
+ item->destroy);
+
+ g_free (item);
+}
+
+static gboolean
+message_from_child (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gchar buffer[1024];
+ GIOStatus status;
+ gsize bytes_read;
+
+ if (async_data.set == FALSE)
+ return TRUE;
+
+ if (condition == G_IO_HUP)
+ return FALSE;
+
+ status = g_io_channel_read_chars (source,
+ buffer,
+ 1024,
+ &bytes_read,
+ NULL);
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ g_byte_array_append (async_data.data, (guchar *) buffer, bytes_read);
+
+ if (async_data.thumbnail_width == -1 && async_data.data->len >= 2 * sizeof (gint))
+ {
+ async_data.thumbnail_width = *((gint *) async_data.data->data);
+ async_data.thumbnail_height = *(((gint *) async_data.data->data) + 1);
+ g_byte_array_remove_range (async_data.data, 0, 2 * sizeof (gint));
+ }
+
+ if (async_data.thumbnail_width >= 0 && async_data.data->len == async_data.thumbnail_width * async_data.thumbnail_height * 4)
+ {
+ GdkPixbuf *pixbuf = NULL;
+
+ if (async_data.thumbnail_width > 0) {
+ gchar *pixels;
+ gint i, rowstride;
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, async_data.thumbnail_width, async_data.thumbnail_height);
+ pixels = (gchar *) gdk_pixbuf_get_pixels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+ for (i = 0; i < async_data.thumbnail_height; ++i)
+ memcpy (pixels + rowstride * i, async_data.data->data + 4 * async_data.thumbnail_width * i, async_data.thumbnail_width * 4);
+ }
+
+ /* callback function needs to ref the pixbuf if it wants to keep it */
+ (* async_data.func) (pixbuf, async_data.theme_name, async_data.user_data);
+
+ if (async_data.destroy)
+ (* async_data.destroy) (async_data.user_data);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ /* Clean up async_data */
+ g_free (async_data.theme_name);
+ g_source_remove (async_data.watch_id);
+ g_io_channel_unref (async_data.channel);
+
+ /* reset async_data */
+ async_data.thumbnail_width = -1;
+ async_data.thumbnail_height = -1;
+ async_data.theme_name = NULL;
+ async_data.channel = NULL;
+ async_data.func = NULL;
+ async_data.user_data = NULL;
+ async_data.destroy = NULL;
+ async_data.set = FALSE;
+ g_byte_array_set_size (async_data.data, 0);
+
+ generate_next_in_queue ();
+ }
+ return TRUE;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ case G_IO_STATUS_EOF:
+ case G_IO_STATUS_ERROR:
+ return FALSE;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
+}
+
+static void
+send_thumbnail_request (gchar *thumbnail_type,
+ gchar *gtk_theme_name,
+ gchar *gtk_color_scheme,
+ gchar *marco_theme_name,
+ gchar *icon_theme_name,
+ gchar *application_font)
+{
+ write (pipe_to_factory_fd[1], thumbnail_type, strlen (thumbnail_type) + 1);
+
+ if (gtk_theme_name)
+ write (pipe_to_factory_fd[1], gtk_theme_name, strlen (gtk_theme_name) + 1);
+ else
+ write (pipe_to_factory_fd[1], "", 1);
+
+ if (gtk_color_scheme)
+ write (pipe_to_factory_fd[1], gtk_color_scheme, strlen (gtk_color_scheme) + 1);
+ else
+ write (pipe_to_factory_fd[1], "", 1);
+
+ if (marco_theme_name)
+ write (pipe_to_factory_fd[1], marco_theme_name, strlen (marco_theme_name) + 1);
+ else
+ write (pipe_to_factory_fd[1], "", 1);
+
+ if (icon_theme_name)
+ write (pipe_to_factory_fd[1], icon_theme_name, strlen (icon_theme_name) + 1);
+ else
+ write (pipe_to_factory_fd[1], "", 1);
+
+ if (application_font)
+ write (pipe_to_factory_fd[1], application_font, strlen (application_font) + 1);
+ else
+ write (pipe_to_factory_fd[1], "Sans 10", strlen ("Sans 10") + 1);
+
+}
+
+static GdkPixbuf *
+read_pixbuf (void)
+{
+ gint bytes_read, i, j = 0;
+ gint size[2];
+ GdkPixbuf *pixbuf;
+ gint rowstride;
+ guchar *pixels;
+
+ do
+ {
+ bytes_read = read (pipe_from_factory_fd[0], ((guint8*) size) + j, 2 * sizeof (gint));
+ if (bytes_read == 0)
+ goto eof;
+ j += bytes_read;
+ }
+ while (j < 2 * sizeof (gint));
+
+ if (size[0] <= 0 || size[1] <= 0)
+ return NULL;
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size[0], size[1]);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ for (i = 0; i < size[1]; i++)
+ {
+ j = 0;
+
+ do
+ {
+ bytes_read = read (pipe_from_factory_fd[0], pixels + rowstride * i + j, size[0] * gdk_pixbuf_get_n_channels (pixbuf) - j);
+
+ if (bytes_read > 0)
+ j += bytes_read;
+ else if (bytes_read == 0)
+ {
+ g_object_unref (pixbuf);
+ goto eof;
+ }
+ }
+ while (j < size[0] * gdk_pixbuf_get_n_channels (pixbuf));
+ }
+
+ return pixbuf;
+
+eof:
+ g_warning ("Received EOF while reading thumbnail");
+ close (pipe_to_factory_fd[1]);
+ pipe_to_factory_fd[1] = 0;
+ close (pipe_from_factory_fd[0]);
+ pipe_from_factory_fd[0] = 0;
+ return NULL;
+}
+
+static GdkPixbuf *
+generate_theme_thumbnail (gchar *thumbnail_type,
+ gchar *gtk_theme_name,
+ gchar *gtk_color_scheme,
+ gchar *marco_theme_name,
+ gchar *icon_theme_name,
+ gchar *application_font)
+{
+ if (async_data.set || !pipe_to_factory_fd[1] || !pipe_from_factory_fd[0])
+ return NULL;
+
+ send_thumbnail_request (thumbnail_type,
+ gtk_theme_name,
+ gtk_color_scheme,
+ marco_theme_name,
+ icon_theme_name,
+ application_font);
+
+ return read_pixbuf ();
+}
+
+GdkPixbuf *
+generate_meta_theme_thumbnail (MateThemeMetaInfo *theme_info)
+{
+ return generate_theme_thumbnail (THUMBNAIL_TYPE_META,
+ theme_info->gtk_theme_name,
+ theme_info->gtk_color_scheme,
+ theme_info->marco_theme_name,
+ theme_info->icon_theme_name,
+ theme_info->application_font);
+}
+
+GdkPixbuf *
+generate_gtk_theme_thumbnail (MateThemeInfo *theme_info)
+{
+ gchar *scheme;
+
+ scheme = gtkrc_get_color_scheme_for_theme (theme_info->name);
+
+ return generate_theme_thumbnail (THUMBNAIL_TYPE_GTK,
+ theme_info->name,
+ scheme,
+ NULL,
+ NULL,
+ NULL);
+ g_free (scheme);
+}
+
+GdkPixbuf *
+generate_marco_theme_thumbnail (MateThemeInfo *theme_info)
+{
+ return generate_theme_thumbnail (THUMBNAIL_TYPE_MARCO,
+ NULL,
+ NULL,
+ theme_info->name,
+ NULL,
+ NULL);
+}
+
+GdkPixbuf *
+generate_icon_theme_thumbnail (MateThemeIconInfo *theme_info)
+{
+ return generate_theme_thumbnail (THUMBNAIL_TYPE_ICON,
+ NULL,
+ NULL,
+ NULL,
+ theme_info->name,
+ NULL);
+}
+
+static void generate_theme_thumbnail_async(gpointer theme_info, gchar* theme_name, gchar* thumbnail_type, gchar* gtk_theme_name, gchar* gtk_color_scheme, gchar* marco_theme_name, gchar* icon_theme_name, gchar* application_font, ThemeThumbnailFunc func, gpointer user_data, GDestroyNotify destroy)
+{
+ if (async_data.set)
+ {
+ ThemeQueueItem* item = g_new0 (ThemeQueueItem, 1);
+
+ item->thumbnail_type = thumbnail_type;
+ item->theme_info = theme_info;
+ item->func = func;
+ item->user_data = user_data;
+ item->destroy = destroy;
+
+ theme_queue = g_list_append(theme_queue, item);
+
+ return;
+ }
+
+ if (!pipe_to_factory_fd[1] || !pipe_from_factory_fd[0])
+ {
+ (*func)(NULL, theme_name, user_data);
+
+ if (destroy)
+ {
+ (*destroy)(user_data);
+ }
+
+ return;
+ }
+
+ if (async_data.channel == NULL)
+ {
+ async_data.channel = g_io_channel_unix_new(pipe_from_factory_fd[0]);
+
+ g_io_channel_set_flags(async_data.channel, g_io_channel_get_flags (async_data.channel) | G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_encoding(async_data.channel, NULL, NULL);
+
+ async_data.watch_id = g_io_add_watch(async_data.channel, G_IO_IN | G_IO_HUP, message_from_child, NULL);
+ }
+
+ async_data.set = TRUE;
+ async_data.thumbnail_width = -1;
+ async_data.thumbnail_height = -1;
+ async_data.theme_name = g_strdup(theme_name);
+ async_data.func = func;
+ async_data.user_data = user_data;
+ async_data.destroy = destroy;
+
+ send_thumbnail_request(thumbnail_type, gtk_theme_name, gtk_color_scheme, marco_theme_name, icon_theme_name, application_font);
+}
+
+void
+generate_meta_theme_thumbnail_async (MateThemeMetaInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ generate_theme_thumbnail_async (theme_info,
+ theme_info->name,
+ THUMBNAIL_TYPE_META,
+ theme_info->gtk_theme_name,
+ theme_info->gtk_color_scheme,
+ theme_info->marco_theme_name,
+ theme_info->icon_theme_name,
+ theme_info->application_font,
+ func, user_data, destroy);
+}
+
+void generate_gtk_theme_thumbnail_async (MateThemeInfo* theme_info, ThemeThumbnailFunc func, gpointer user_data, GDestroyNotify destroy)
+{
+ gchar* scheme = gtkrc_get_color_scheme_for_theme(theme_info->name);
+
+ generate_theme_thumbnail_async(theme_info, theme_info->name, THUMBNAIL_TYPE_GTK, theme_info->name, scheme, NULL, NULL, NULL, func, user_data, destroy);
+
+ g_free(scheme);
+}
+
+void
+generate_marco_theme_thumbnail_async (MateThemeInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ generate_theme_thumbnail_async (theme_info,
+ theme_info->name,
+ THUMBNAIL_TYPE_MARCO,
+ NULL,
+ NULL,
+ theme_info->name,
+ NULL,
+ NULL,
+ func, user_data, destroy);
+}
+
+void
+generate_icon_theme_thumbnail_async (MateThemeIconInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ generate_theme_thumbnail_async (theme_info,
+ theme_info->name,
+ THUMBNAIL_TYPE_ICON,
+ NULL,
+ NULL,
+ NULL,
+ theme_info->name,
+ NULL,
+ func, user_data, destroy);
+}
+
+void
+theme_thumbnail_factory_init (int argc, char *argv[])
+{
+#ifndef __APPLE__
+ gint child_pid;
+#endif
+
+ pipe (pipe_to_factory_fd);
+ pipe (pipe_from_factory_fd);
+
+/* Apple's CoreFoundation classes must not be used from forked
+ * processes. Since freetype (and thus GTK) uses them, we simply
+ * disable the thumbnailer on MacOS for now. That means no thumbs
+ * until the thumbnailing process is rewritten, but at least we won't
+ * make apps crash. */
+#ifndef __APPLE__
+ child_pid = fork ();
+ if (child_pid == 0)
+ {
+ ThemeThumbnailData data;
+ GIOChannel *channel;
+
+ /* Child */
+ gtk_init (&argc, &argv);
+
+ close (pipe_to_factory_fd[1]);
+ pipe_to_factory_fd[1] = 0;
+ close (pipe_from_factory_fd[0]);
+ pipe_from_factory_fd[0] = 0;
+
+ data.status = READY_FOR_THEME;
+ data.type = g_byte_array_new ();
+ data.control_theme_name = g_byte_array_new ();
+ data.gtk_color_scheme = g_byte_array_new ();
+ data.wm_theme_name = g_byte_array_new ();
+ data.icon_theme_name = g_byte_array_new ();
+ data.application_font = g_byte_array_new ();
+
+ channel = g_io_channel_unix_new (pipe_to_factory_fd[0]);
+ g_io_channel_set_flags (channel, g_io_channel_get_flags (channel) |
+ G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_encoding (channel, NULL, NULL);
+ g_io_add_watch (channel, G_IO_IN | G_IO_HUP, message_from_capplet, &data);
+ g_io_channel_unref (channel);
+
+ gtk_main ();
+ _exit (0);
+ }
+
+ g_assert (child_pid > 0);
+
+ /* Parent */
+ close (pipe_to_factory_fd[0]);
+ close (pipe_from_factory_fd[1]);
+#endif /* __APPLE__ */
+
+ async_data.set = FALSE;
+ async_data.theme_name = NULL;
+ async_data.data = g_byte_array_new ();
+}
diff --git a/capplets/common/theme-thumbnail.h b/capplets/common/theme-thumbnail.h
new file mode 100644
index 00000000..fe25f706
--- /dev/null
+++ b/capplets/common/theme-thumbnail.h
@@ -0,0 +1,37 @@
+#ifndef __THEME_THUMBNAIL_H__
+#define __THEME_THUMBNAIL_H__
+
+
+#include <gtk/gtk.h>
+#include "mate-theme-info.h"
+
+typedef void (* ThemeThumbnailFunc) (GdkPixbuf *pixbuf,
+ gchar *theme_name,
+ gpointer data);
+
+GdkPixbuf *generate_meta_theme_thumbnail (MateThemeMetaInfo *theme_info);
+GdkPixbuf *generate_gtk_theme_thumbnail (MateThemeInfo *theme_info);
+GdkPixbuf *generate_marco_theme_thumbnail (MateThemeInfo *theme_info);
+GdkPixbuf *generate_icon_theme_thumbnail (MateThemeIconInfo *theme_info);
+
+void generate_meta_theme_thumbnail_async (MateThemeMetaInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+void generate_gtk_theme_thumbnail_async (MateThemeInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+void generate_marco_theme_thumbnail_async (MateThemeInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+void generate_icon_theme_thumbnail_async (MateThemeIconInfo *theme_info,
+ ThemeThumbnailFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+
+void theme_thumbnail_factory_init (int argc,
+ char *argv[]);
+
+#endif /* __THEME_THUMBNAIL_H__ */
diff --git a/capplets/common/wm-common.c b/capplets/common/wm-common.c
new file mode 100644
index 00000000..31cd503d
--- /dev/null
+++ b/capplets/common/wm-common.c
@@ -0,0 +1,184 @@
+#include <X11/Xatom.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+#include "wm-common.h"
+
+typedef struct _WMCallbackData
+{
+ GFunc func;
+ gpointer data;
+} WMCallbackData;
+
+/* Our WM Window */
+static Window wm_window = None;
+
+static char *
+wm_common_get_window_manager_property (Atom atom)
+{
+ Atom utf8_string, type;
+ int result;
+ char *retval;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gchar *val;
+
+ if (wm_window == None)
+ return NULL;
+
+ utf8_string = XInternAtom (GDK_DISPLAY (), "UTF8_STRING", False);
+
+ gdk_error_trap_push ();
+
+ val = NULL;
+ result = XGetWindowProperty (GDK_DISPLAY (),
+ wm_window,
+ atom,
+ 0, G_MAXLONG,
+ False, utf8_string,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **) &val);
+
+ if (gdk_error_trap_pop () || result != Success ||
+ type != utf8_string || format != 8 || nitems == 0 ||
+ !g_utf8_validate (val, nitems, NULL))
+ {
+ retval = NULL;
+ }
+ else
+ {
+ retval = g_strndup (val, nitems);
+ }
+
+ if (val)
+ XFree (val);
+
+ return retval;
+}
+
+char*
+wm_common_get_current_window_manager (void)
+{
+ Atom atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False);
+ char *result;
+
+ result = wm_common_get_window_manager_property (atom);
+ if (result)
+ return result;
+ else
+ return g_strdup (WM_COMMON_UNKNOWN);
+}
+
+char**
+wm_common_get_current_keybindings (void)
+{
+ Atom keybindings_atom = XInternAtom (GDK_DISPLAY (), "_MATE_WM_KEYBINDINGS", False);
+ char *keybindings = wm_common_get_window_manager_property (keybindings_atom);
+ char **results;
+
+ if (keybindings)
+ {
+ char **p;
+ results = g_strsplit(keybindings, ",", -1);
+ for (p = results; *p; p++)
+ g_strstrip (*p);
+ g_free (keybindings);
+ }
+ else
+ {
+ Atom wm_atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False);
+ char *wm_name = wm_common_get_window_manager_property (wm_atom);
+ char *to_copy[] = { NULL, NULL };
+
+ to_copy[0] = wm_name ? wm_name : WM_COMMON_UNKNOWN;
+
+ results = g_strdupv (to_copy);
+ g_free (wm_name);
+ }
+
+ return results;
+}
+
+static void
+update_wm_window (void)
+{
+ Window *xwindow;
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+
+ XGetWindowProperty (GDK_DISPLAY (), GDK_ROOT_WINDOW (),
+ XInternAtom (GDK_DISPLAY (), "_NET_SUPPORTING_WM_CHECK", False),
+ 0, G_MAXLONG, False, XA_WINDOW, &type, &format,
+ &nitems, &bytes_after, (guchar **) &xwindow);
+
+ if (type != XA_WINDOW)
+ {
+ wm_window = None;
+ return;
+ }
+
+ gdk_error_trap_push ();
+ XSelectInput (GDK_DISPLAY (), *xwindow, StructureNotifyMask | PropertyChangeMask);
+ XSync (GDK_DISPLAY (), False);
+
+ if (gdk_error_trap_pop ())
+ {
+ XFree (xwindow);
+ wm_window = None;
+ return;
+ }
+
+ wm_window = *xwindow;
+ XFree (xwindow);
+}
+
+static GdkFilterReturn
+wm_window_event_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ WMCallbackData *ncb_data = (WMCallbackData*) data;
+ XEvent *xevent = (XEvent *)xev;
+
+ if ((xevent->type == DestroyNotify &&
+ wm_window != None && xevent->xany.window == wm_window) ||
+ (xevent->type == PropertyNotify &&
+ xevent->xany.window == GDK_ROOT_WINDOW () &&
+ xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY (), "_NET_SUPPORTING_WM_CHECK", False))) ||
+ (xevent->type == PropertyNotify &&
+ wm_window != None && xevent->xany.window == wm_window &&
+ xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False))))
+ {
+ update_wm_window ();
+ (* ncb_data->func) ((gpointer)wm_common_get_current_window_manager(),
+ ncb_data->data);
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+void
+wm_common_register_window_manager_change (GFunc func,
+ gpointer data)
+{
+ WMCallbackData *ncb_data;
+
+ ncb_data = g_new0 (WMCallbackData, 1);
+
+ ncb_data->func = func;
+ ncb_data->data = data;
+
+ gdk_window_add_filter (NULL, wm_window_event_filter, ncb_data);
+
+ update_wm_window ();
+
+ XSelectInput (GDK_DISPLAY (), GDK_ROOT_WINDOW (), PropertyChangeMask);
+ XSync (GDK_DISPLAY (), False);
+}
+
+
diff --git a/capplets/common/wm-common.h b/capplets/common/wm-common.h
new file mode 100644
index 00000000..564c6b9e
--- /dev/null
+++ b/capplets/common/wm-common.h
@@ -0,0 +1,17 @@
+#ifndef WM_COMMON_H
+#define WM_COMMON_H
+
+#define WM_COMMON_MARCO "Marco"
+#define WM_COMMON_SAWFISH "Sawfish"
+#define WM_COMMON_UNKNOWN "Unknown"
+
+gchar *wm_common_get_current_window_manager (void);
+/* Returns a strv of keybinding names for the window manager;
+ * using _MATE_WM_KEYBINDINGS if available, _NET_WM_NAME otherwise. */
+char **wm_common_get_current_keybindings (void);
+
+void wm_common_register_window_manager_change (GFunc func,
+ gpointer data);
+
+#endif /* WM_COMMON_H */
+