From 0b0e6bc987da4fd88a7854ebb12bde705e92c428 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 21:51:44 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- capplets/common/Makefile.am | 63 + capplets/common/activate-settings-daemon.c | 60 + capplets/common/activate-settings-daemon.h | 9 + capplets/common/capplet-stock-icons.c | 101 + capplets/common/capplet-stock-icons.h | 66 + capplets/common/capplet-util.c | 205 ++ capplets/common/capplet-util.h | 46 + capplets/common/file-transfer-dialog.c | 608 ++++++ capplets/common/file-transfer-dialog.h | 73 + capplets/common/gtkrc-utils.c | 256 +++ capplets/common/gtkrc-utils.h | 25 + capplets/common/mate-theme-apply.c | 136 ++ capplets/common/mate-theme-apply.h | 33 + capplets/common/mate-theme-info.c | 1988 ++++++++++++++++++++ capplets/common/mate-theme-info.h | 190 ++ capplets/common/mate-theme-test.c | 134 ++ capplets/common/mateconf-property-editor-marshal.c | 41 + capplets/common/mateconf-property-editor-marshal.h | 19 + capplets/common/mateconf-property-editor.c | 1801 ++++++++++++++++++ capplets/common/mateconf-property-editor.h | 162 ++ capplets/common/theme-thumbnail.c | 1144 +++++++++++ capplets/common/theme-thumbnail.h | 37 + capplets/common/wm-common.c | 184 ++ capplets/common/wm-common.h | 17 + 24 files changed, 7398 insertions(+) create mode 100644 capplets/common/Makefile.am create mode 100644 capplets/common/activate-settings-daemon.c create mode 100644 capplets/common/activate-settings-daemon.h create mode 100644 capplets/common/capplet-stock-icons.c create mode 100644 capplets/common/capplet-stock-icons.h create mode 100644 capplets/common/capplet-util.c create mode 100644 capplets/common/capplet-util.h create mode 100644 capplets/common/file-transfer-dialog.c create mode 100644 capplets/common/file-transfer-dialog.h create mode 100644 capplets/common/gtkrc-utils.c create mode 100644 capplets/common/gtkrc-utils.h create mode 100644 capplets/common/mate-theme-apply.c create mode 100644 capplets/common/mate-theme-apply.h create mode 100644 capplets/common/mate-theme-info.c create mode 100644 capplets/common/mate-theme-info.h create mode 100644 capplets/common/mate-theme-test.c create mode 100644 capplets/common/mateconf-property-editor-marshal.c create mode 100644 capplets/common/mateconf-property-editor-marshal.h create mode 100644 capplets/common/mateconf-property-editor.c create mode 100644 capplets/common/mateconf-property-editor.h create mode 100644 capplets/common/theme-thumbnail.c create mode 100644 capplets/common/theme-thumbnail.h create mode 100644 capplets/common/wm-common.c create mode 100644 capplets/common/wm-common.h (limited to 'capplets/common') 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 +#endif + +#include +#include +#include + +#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 + +/* 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 + * Taken bits of code from panel-stock-icons.c, Thanks Mark + */ + +#include +#include + +#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 + * Taken bits of code from panel-stock-icons.h, Thanks Mark + */ + +#ifndef __CAPPLET_STOCK_ICONS_H__ +#define __CAPPLET_STOCK_ICONS_H__ + +#include +#include + +#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 + * + * 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 +#endif + +#include + +/* For stat */ +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +/* 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 + * Jens Granseuer + * + * 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 +#include +#include +#include +#include + +#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 ("%s", 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 ("", _("Copying files"), "", 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 + * + * 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 + +#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 + * Jens Granseuer + * 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 +#endif + +#include +#include +#include +#include +#include +#include + +#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 + * Jens Granseuer + * 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 + * + * 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 +#endif + +#include +#include +#include +#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 + 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 + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mate-theme-info.h" +#include "gtkrc-utils.h" + +#ifdef HAVE_XCURSOR + #include +#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 + 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 +#include +#include +#include + +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 +#include +#include +#include +#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 +#include +#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 + +#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 + * + * 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 +#include +#include + +#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 + * + * 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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We have to #undef this as marco #defines these. */ +#undef _ +#undef N_ + +#include + +#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 +#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 +#include +#include +#include +#include +#include +#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 */ + -- cgit v1.2.1