From f28c52f3f39e32cb9c14900e687dc04e6bb92f55 Mon Sep 17 00:00:00 2001 From: Jonathan Blandford Date: Mon, 13 Jan 2003 20:21:28 +0000 Subject: [PATCH] capplets/common/gnome-theme-info.[ch]: Total rewrite. Fam now works correctly. capplets/keybindings/*: use new theme code. capplets/theme-switcher/*: use new theme code. --- capplets/common/ChangeLog | 5 + capplets/common/gnome-theme-info.c | 1652 +++++++++++++---- capplets/common/gnome-theme-info.h | 42 +- capplets/keybindings/ChangeLog | 4 + capplets/keybindings/eggcellrendererkeys.c | 2 +- .../keybindings/gnome-keybinding-properties.c | 2 + capplets/theme-switcher/ChangeLog | 4 + capplets/theme-switcher/gnome-theme-manager.c | 2 + 8 files changed, 1332 insertions(+), 381 deletions(-) diff --git a/capplets/common/ChangeLog b/capplets/common/ChangeLog index fd926a3fd..4cdc59f09 100644 --- a/capplets/common/ChangeLog +++ b/capplets/common/ChangeLog @@ -1,3 +1,8 @@ +Mon Jan 13 15:04:47 2003 Jonathan Blandford + + * gnome-theme-info.c: rewrote to handle fam more correctly. Still + a little broken in places, but much, much better than before. + 2003-01-10 Jody Goldberg * Release 2.1.6 diff --git a/capplets/common/gnome-theme-info.c b/capplets/common/gnome-theme-info.c index 0d4ae2d85..498ce1eb2 100644 --- a/capplets/common/gnome-theme-info.c +++ b/capplets/common/gnome-theme-info.c @@ -1,15 +1,9 @@ -#include - -#include -#include -#include - -#include -#include -#include +#include +#include #include #include -#include +#include +#include #include #include "gnome-theme-info.h" @@ -21,6 +15,14 @@ #define APPLICATION_FONT_KEY "X-GNOME-Metatheme/ApplicationFont" #define BACKGROUND_IMAGE_KEY "X-GNOME-Metatheme/BackgroundImage" +/* 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 { @@ -28,35 +30,292 @@ typedef struct _ThemeCallbackData gpointer data; } ThemeCallbackData; +typedef struct { + GnomeVFSMonitorHandle *common_theme_dir_handle; + GnomeVFSMonitorHandle *gtk2_dir_handle; + GnomeVFSMonitorHandle *keybinding_dir_handle; + GnomeVFSMonitorHandle *metacity_dir_handle; + gint priority; +} CommonThemeDirMonitorData; -static GHashTable *theme_hash = NULL; -static GHashTable *icon_theme_hash = NULL; -static GHashTable *meta_theme_hash = NULL; +typedef struct { + GnomeVFSMonitorHandle *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 + * GnomeTheme{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 GnomeTheme{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 gboolean initted = FALSE; + +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 *theme_hash_by_uri; +static GHashTable *theme_hash_by_name; static gboolean initting = FALSE; -const gchar *gtk2_suffix = "gtk-2.0"; -const gchar *key_suffix = "gtk-2.0-key"; -const gchar *metacity_suffix = "metacity-1"; -const gchar *icon_theme_file_id = "index.theme"; -const gchar *meta_theme_file_id = "index.theme"; +/* prototypes */ +static gint safe_strcmp (gchar *a_str, + gchar *b_str); +static gint get_priority_from_data_by_hash (GHashTable *hash_table, + gpointer data); +static void add_data_to_hash_by_name (GHashTable *hash_table, + gchar *name, + gpointer data); +static void remove_data_from_hash_by_name (GHashTable *hash_table, + const gchar *name, + gpointer data); +static gpointer get_data_from_hash_by_name (GHashTable *hash_table, + const gchar *name, + gint priority); +static GnomeThemeMetaInfo *read_meta_theme (GnomeVFSURI *meta_theme_uri); +static GnomeThemeIconInfo *read_icon_theme (GnomeVFSURI *icon_theme_uri); +static void handle_change_signal (GnomeThemeType type, + gpointer theme, + GnomeThemeChangeType change_type, + GnomeThemeElement element); +static void update_theme_index (GnomeVFSURI *index_uri, + GnomeThemeElement key_element, + gint priority); +static void update_gtk2_index (GnomeVFSURI *gtk2_index_uri, + gint priority); +static void update_keybinding_index (GnomeVFSURI *keybinding_index_uri, + gint priority); +static void update_metacity_index (GnomeVFSURI *metacity_index_uri, + gint priority); +static void update_common_theme_dir_index (GnomeVFSURI *theme_index_uri, + gboolean icon_theme, + gint priority); +static void update_meta_theme_index (GnomeVFSURI *meta_theme_index_uri, + gint priority); +static void update_icon_theme_index (GnomeVFSURI *icon_theme_index_uri, + gint priority); +static void gtk2_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void keybinding_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void metacity_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void common_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void common_icon_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void top_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static void top_icon_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data); +static GnomeVFSResult add_common_theme_dir_monitor (GnomeVFSURI *theme_dir_uri, + gboolean *monitor_not_added, + CommonThemeDirMonitorData *monitor_data, + GError **error); +static GnomeVFSResult add_common_icon_theme_dir_monitor (GnomeVFSURI *theme_dir_uri, + gboolean *monitor_not_added, + CommonIconThemeDirMonitorData *monitor_data, + GError **error); +static void remove_common_theme_dir_monitor (CommonThemeDirMonitorData *monitor_data); +static void remove_common_icon_theme_dir_monitor (CommonIconThemeDirMonitorData *monitor_data); +static GnomeVFSResult real_add_top_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + gboolean icon_theme, + GError **error); +static GnomeVFSResult add_top_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + GError **error); +static GnomeVFSResult add_top_icon_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + GError **error); +/* private functions */ +static gint +safe_strcmp (gchar *a_str, + gchar *b_str) +{ + if (a_str == NULL && b_str != NULL) + return -1; + if (a_str != NULL && b_str == NULL) + return 1; + if (a_str == NULL && b_str == NULL) + return 0; + return strcmp (a_str, b_str); +} + +static gint +get_priority_from_data_by_hash (GHashTable *hash_table, + gpointer data) +{ + gint theme_priority = 0; + if (hash_table == meta_theme_hash_by_name) + theme_priority = ((GnomeThemeMetaInfo *)data)->priority; + else if (hash_table == icon_theme_hash_by_name) + theme_priority = ((GnomeThemeIconInfo *)data)->priority; + else if (hash_table == theme_hash_by_name) + theme_priority = ((GnomeThemeInfo *)data)->priority; + else + g_assert_not_reached (); + + return theme_priority; +} + + +static void +add_data_to_hash_by_name (GHashTable *hash_table, + gchar *name, + gpointer data) +{ + GList *list; + + list = g_hash_table_lookup (hash_table, name); + if (list == NULL) + { + list = g_list_append (list, data); + } + else + { + GList *list_ptr = list; + gboolean added = FALSE; + gint priority; + + priority = get_priority_from_data_by_hash (hash_table, data); + while (list_ptr) + { + gint theme_priority; + + theme_priority = get_priority_from_data_by_hash (hash_table, list_ptr->data); + + if (theme_priority == priority) + { + /* Swap it in */ + list_ptr->data = data; + added = TRUE; + break; + } + if (theme_priority > priority) + { + list = g_list_insert_before (list, list_ptr, data); + added = TRUE; + break; + } + list_ptr = list_ptr->next; + } + if (! added) + list = g_list_append (list, data); + } + g_hash_table_insert (hash_table, g_strdup (name), list); +} + +static void +remove_data_from_hash_by_name (GHashTable *hash_table, + const gchar *name, + gpointer data) +{ + GList *list; + + list = g_hash_table_lookup (hash_table, name); + + list = g_list_remove (list, data); + if (list == NULL) + g_hash_table_remove (hash_table, name); + else + g_hash_table_insert (hash_table, g_strdup (name), list); + +} + +static gpointer +get_data_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) + { + if (list) + return list->data; + + return NULL; + } + while (list) + { + gint theme_priority ; + + theme_priority = get_priority_from_data_by_hash (hash_table, list->data); + + if (theme_priority == priority) + return list->data; + + list = list->next; + } + return NULL; +} + static GnomeThemeMetaInfo * -read_meta_theme (const gchar *theme_name, - const gchar *meta_theme_file) +read_meta_theme (GnomeVFSURI *meta_theme_uri) { GnomeThemeMetaInfo *meta_theme_info; + GnomeVFSURI *common_theme_dir_uri; GnomeDesktopItem *meta_theme_ditem; + gchar *meta_theme_file; const gchar *str; - meta_theme_ditem = gnome_desktop_item_new_from_file (meta_theme_file, 0, NULL); + meta_theme_file = gnome_vfs_uri_to_string (meta_theme_uri, GNOME_VFS_URI_HIDE_NONE); + meta_theme_ditem = gnome_desktop_item_new_from_uri (meta_theme_file, 0, NULL); if (meta_theme_ditem == NULL) - return NULL; - + { + g_free (meta_theme_file); + return NULL; + } + common_theme_dir_uri = gnome_vfs_uri_get_parent (meta_theme_uri); + meta_theme_info = gnome_theme_meta_info_new (); - meta_theme_info->path = g_strdup (meta_theme_file); - meta_theme_info->name = g_strdup (theme_name); + meta_theme_info->path = meta_theme_file; + meta_theme_info->name = gnome_vfs_uri_extract_short_name (common_theme_dir_uri); + gnome_vfs_uri_unref (common_theme_dir_uri); str = gnome_desktop_item_get_string (meta_theme_ditem, GNOME_DESKTOP_ITEM_NAME); if (str == NULL) @@ -110,329 +369,882 @@ read_meta_theme (const gchar *theme_name, } static GnomeThemeIconInfo * -read_icon_theme (const gchar *icon_theme_file) +read_icon_theme (GnomeVFSURI *icon_theme_uri) { GnomeThemeIconInfo *icon_theme_info; GnomeDesktopItem *icon_theme_ditem; + char *icon_theme_file; const gchar *name; - icon_theme_ditem = gnome_desktop_item_new_from_file (icon_theme_file, 0, NULL); + icon_theme_file = gnome_vfs_uri_to_string (icon_theme_uri, GNOME_VFS_URI_HIDE_NONE); + icon_theme_ditem = gnome_desktop_item_new_from_uri (icon_theme_file, 0, NULL); if (icon_theme_ditem == NULL) - return NULL; + { + g_free (icon_theme_file); + return NULL; + } name = gnome_desktop_item_get_string (icon_theme_ditem, "Icon Theme/Name"); if (name == NULL) - return NULL; + { + gnome_desktop_item_unref (icon_theme_ditem); + g_free (icon_theme_file); + return NULL; + } icon_theme_info = gnome_theme_icon_info_new (); icon_theme_info->name = g_strdup (name); - icon_theme_info->path = g_strdup (icon_theme_file); + icon_theme_info->path = icon_theme_file; + + gnome_desktop_item_unref (icon_theme_ditem); return icon_theme_info; } static void -update_theme_dir (const gchar *theme_dir) +handle_change_signal (GnomeThemeType type, + gpointer theme, + GnomeThemeChangeType change_type, + GnomeThemeElement element) { - GnomeThemeInfo *info = NULL; - gboolean changed = FALSE; - gboolean has_gtk = FALSE; - gboolean has_keybinding = FALSE; - gboolean has_metacity = FALSE; - gchar *tmp; +#if DEBUG + gchar *type_str = NULL; + gchar *element_str = NULL; +#endif + gchar *uri = NULL; + GList *list; - tmp = g_build_filename (theme_dir, meta_theme_file_id, NULL); - if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) + if (initting) + return; + + if (type == GNOME_THEME_TYPE_REGULAR) + uri = g_strdup (((GnomeThemeInfo *)theme)->path); + else if (type == GNOME_THEME_TYPE_METATHEME) + uri = g_strdup (((GnomeThemeMetaInfo *)theme)->path); + else if (type == GNOME_THEME_TYPE_ICON) + uri = g_strdup (((GnomeThemeIconInfo *)theme)->path); + + for (list = callbacks; list; list = list->next) + { + ThemeCallbackData *callback_data = list->data; + (* callback_data->func) (uri, callback_data->data); + } + +#if DEBUG + if (change_type == GNOME_THEME_CHANGE_CREATED) + type_str = "created"; + else if (change_type == GNOME_THEME_CHANGE_CHANGED) + type_str = "changed"; + else if (change_type == GNOME_THEME_CHANGE_DELETED) + type_str = "deleted"; + + if (element & GNOME_THEME_GTK_2) + element_str = "gtk-2"; + else if (element & GNOME_THEME_GTK_2_KEYBINDING) + element_str = "keybinding"; + if (element & GNOME_THEME_METACITY) + element_str = "metacity"; + + if (type == GNOME_THEME_TYPE_REGULAR) { - GnomeThemeMetaInfo *old_meta_theme_info; - GnomeThemeMetaInfo *meta_theme_info; - - old_meta_theme_info = gnome_theme_meta_info_find_by_filename (tmp); - meta_theme_info = read_meta_theme (strrchr (theme_dir, '/')+1, tmp); - - /* this is hardly efficient. We always remove the old meta_theme and - * rereplace it, even if nothing has changed. We can add a check in the - * future. */ - if (old_meta_theme_info != NULL) - { - g_hash_table_remove (meta_theme_hash, old_meta_theme_info->name); - gnome_theme_meta_info_free (old_meta_theme_info); - changed = TRUE; - } - if (meta_theme_info != NULL) - { - g_hash_table_insert (meta_theme_hash, meta_theme_info->name, meta_theme_info); - changed = TRUE; - } + g_print ("theme \"%s\" has a theme of type %s (priority %d) has been %s\n", + ((GnomeThemeInfo *) theme)->name, + element_str, + ((GnomeThemeInfo *) theme)->priority, + type_str); } - g_free (tmp); - - tmp = g_build_filename (theme_dir, gtk2_suffix, NULL); - if (g_file_test (tmp, G_FILE_TEST_IS_DIR)) + else if (type == GNOME_THEME_TYPE_METATHEME) { - has_gtk = TRUE; + g_print ("meta theme \"%s\" (priority %d) has been %s\n", + ((GnomeThemeMetaInfo *) theme)->name, + ((GnomeThemeMetaInfo *) theme)->priority, + type_str); } - g_free (tmp); - - tmp = g_build_filename (theme_dir, key_suffix, NULL); - if (g_file_test (tmp, G_FILE_TEST_IS_DIR)) + else if (type == GNOME_THEME_TYPE_ICON) { - has_keybinding = TRUE; + g_print ("icon theme \"%s\" (priority %d) has been %s\n", + ((GnomeThemeIconInfo *) theme)->name, + ((GnomeThemeIconInfo *) theme)->priority, + type_str); } - g_free (tmp); +#endif +} - tmp = g_build_filename (theme_dir, metacity_suffix, NULL); - if (g_file_test (tmp, G_FILE_TEST_IS_DIR)) +/* gtk2_index_uri should point to the gtkrc file that was modified */ +static void +update_theme_index (GnomeVFSURI *index_uri, + GnomeThemeElement key_element, + gint priority) +{ + GnomeVFSFileInfo file_info = {0,}; + GnomeVFSResult result; + gboolean theme_exists; + GnomeThemeInfo *theme_info; + GnomeVFSURI *parent; + GnomeVFSURI *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" */ + result = gnome_vfs_get_file_info_uri (index_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_REGULAR) + theme_exists = TRUE; + else + theme_exists = FALSE; + + /* Next, we see what currently exists */ + parent = gnome_vfs_uri_get_parent (index_uri); + common_theme_dir_uri = gnome_vfs_uri_get_parent (parent); + common_theme_dir = gnome_vfs_uri_to_string (common_theme_dir_uri, GNOME_VFS_URI_HIDE_NONE); + + theme_info = g_hash_table_lookup (theme_hash_by_uri, common_theme_dir); + if (theme_info == NULL) { - has_metacity = TRUE; - } - g_free (tmp); - - info = gnome_theme_info_find_by_dir (theme_dir); - - if (info) - { - if (!has_gtk && ! has_keybinding && ! has_metacity) + if (theme_exists) { - g_hash_table_remove (theme_hash, info->name); - gnome_theme_info_free (info); - changed = TRUE; - } - else if ((info->has_keybinding != has_keybinding) || - (info->has_gtk != has_gtk) || - (info->has_metacity != has_metacity)) - { - info->has_keybinding = has_keybinding; - info->has_gtk = has_gtk; - info->has_metacity = has_metacity; - changed = TRUE; + theme_info = gnome_theme_info_new (); + theme_info->path = g_strdup (common_theme_dir); + theme_info->name = gnome_vfs_uri_extract_short_name (common_theme_dir_uri); + theme_info->priority = priority; + if (key_element & GNOME_THEME_GTK_2) + theme_info->has_gtk = TRUE; + else if (key_element & GNOME_THEME_GTK_2_KEYBINDING) + theme_info->has_keybinding = TRUE; + else if (key_element & GNOME_THEME_METACITY) + theme_info->has_metacity = TRUE; + + g_hash_table_insert (theme_hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_data_to_hash_by_name (theme_hash_by_name, theme_info->name, theme_info); + handle_change_signal (GNOME_THEME_TYPE_REGULAR, theme_info, GNOME_THEME_CHANGE_CREATED, key_element); } } else { - if (has_gtk || has_keybinding || has_metacity) + gboolean theme_used_to_exist = FALSE; + + if (key_element & GNOME_THEME_GTK_2) { - info = gnome_theme_info_new (); - info->path = g_strdup (theme_dir); - info->name = g_strdup (strrchr (theme_dir, '/') + 1); - info->has_gtk = has_gtk; - info->has_keybinding = has_keybinding; - info->has_metacity = has_metacity; - - g_hash_table_insert (theme_hash, info->name, info); - changed = TRUE; + theme_used_to_exist = theme_info->has_gtk; + theme_info->has_gtk = theme_exists; } - } - if (changed && !initting) - { - GList *list; - - for (list = callbacks; list; list = list->next) + else if (key_element & GNOME_THEME_GTK_2_KEYBINDING) { - ThemeCallbackData *callback_data = list->data; + theme_used_to_exist = theme_info->has_keybinding; + theme_info->has_keybinding = theme_exists; + } + else if (key_element & GNOME_THEME_METACITY) + { + theme_used_to_exist = theme_info->has_metacity; + theme_info->has_metacity = theme_exists; + } - (* callback_data->func) ((gpointer)theme_dir, callback_data->data); + if (theme_exists && theme_used_to_exist) + { + handle_change_signal (GNOME_THEME_TYPE_REGULAR, theme_info, GNOME_THEME_CHANGE_CHANGED, key_element); + } + else if (theme_exists && !theme_used_to_exist) + { + handle_change_signal (GNOME_THEME_TYPE_REGULAR, theme_info, GNOME_THEME_CHANGE_CREATED, key_element); + } + else if (! theme_exists && theme_used_to_exist) + { + handle_change_signal (GNOME_THEME_TYPE_REGULAR, theme_info, GNOME_THEME_CHANGE_DELETED, key_element); + } + + if (!theme_info->has_metacity && !theme_info->has_keybinding && !theme_info->has_gtk) + { + g_hash_table_remove (theme_hash_by_uri, common_theme_dir); + remove_data_from_hash_by_name (theme_hash_by_name, theme_info->name, theme_info); + gnome_theme_info_free (theme_info); } } + g_free (common_theme_dir); + gnome_vfs_uri_unref (parent); + gnome_vfs_uri_unref (common_theme_dir_uri); } static void -update_icon_theme_dir (const gchar *theme_dir) +update_gtk2_index (GnomeVFSURI *gtk2_index_uri, + gint priority) { - GnomeThemeIconInfo *icon_theme_info = NULL; - gboolean changed = FALSE; - gchar *tmp; - - tmp = g_build_filename (theme_dir, icon_theme_file_id, NULL); - if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) - { - icon_theme_info = read_icon_theme (tmp); - } - - g_free (tmp); - - if (icon_theme_info) - { - g_hash_table_insert (icon_theme_hash, icon_theme_info->name, icon_theme_info); - changed = TRUE; - } - - if (changed && !initting) - { - GList *list; - - for (list = callbacks; list; list = list->next) - { - ThemeCallbackData *callback_data = list->data; - - (* callback_data->func) ((gpointer)theme_dir, callback_data->data); - } - } + update_theme_index (gtk2_index_uri, GNOME_THEME_GTK_2, priority); } static void -top_theme_dir_changed_callback (GnomeVFSMonitorHandle *handle, - const gchar *monitor_uri, - const gchar *info_uri, - GnomeVFSMonitorEventType event_type, - gpointer user_data) +update_keybinding_index (GnomeVFSURI *keybinding_index_uri, + gint priority) { - typedef void (*ThemeChangedFunc) (const gchar *uri); - char *uri; - ThemeChangedFunc func; + update_theme_index (keybinding_index_uri, GNOME_THEME_GTK_2_KEYBINDING, priority); +} + +static void +update_metacity_index (GnomeVFSURI *metacity_index_uri, + gint priority) +{ + update_theme_index (metacity_index_uri, GNOME_THEME_METACITY, priority); +} + +static void +update_common_theme_dir_index (GnomeVFSURI *theme_index_uri, + gboolean icon_theme, + gint priority) +{ + GnomeVFSFileInfo file_info = {0,}; + GnomeVFSResult result; + gboolean theme_exists; + gpointer theme_info; + gpointer old_theme_info; + GnomeVFSURI *common_theme_dir_uri; + gchar *common_theme_dir; + GHashTable *hash_by_uri; + GHashTable *hash_by_name; + gchar *name = NULL; + + if (icon_theme) + { + hash_by_uri = icon_theme_hash_by_uri; + hash_by_name = icon_theme_hash_by_name; + } + else + { + hash_by_uri = meta_theme_hash_by_uri; + hash_by_name = meta_theme_hash_by_name; + } + /* First, we determine the new state of the file. */ + result = gnome_vfs_get_file_info_uri (theme_index_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_REGULAR) + { + /* It's an interesting file. Lets try to load it. */ + if (icon_theme) + { + theme_info = read_icon_theme (theme_index_uri); + if (theme_info) + { + ((GnomeThemeIconInfo *) theme_info)->priority = priority; + theme_exists = TRUE; + } + else + { + theme_exists = FALSE; + } + } + else + { + theme_info = read_meta_theme (theme_index_uri); + if (theme_info) + { + ((GnomeThemeMetaInfo *) theme_info)->priority = priority; + theme_exists = TRUE; + } + else + { + theme_exists = FALSE; + } + } + } + else + { + theme_info = NULL; + theme_exists = FALSE; + } + + /* Next, we see what currently exists */ + common_theme_dir_uri = gnome_vfs_uri_get_parent (theme_index_uri); + common_theme_dir = gnome_vfs_uri_to_string (common_theme_dir_uri, GNOME_VFS_URI_HIDE_NONE); + + old_theme_info = g_hash_table_lookup (hash_by_uri, common_theme_dir); + + if (theme_exists) + { + if (icon_theme) + name = ((GnomeThemeIconInfo *)theme_info)->name; + else + name = ((GnomeThemeMetaInfo *)theme_info)->name; + } + if (old_theme_info == NULL) + { + if (theme_exists) + { + g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_data_to_hash_by_name (hash_by_name, g_strdup (name), theme_info); + handle_change_signal (icon_theme?GNOME_THEME_TYPE_ICON:GNOME_THEME_TYPE_METATHEME, + theme_info, GNOME_THEME_CHANGE_CREATED, 0); + } + } + else + { + if (theme_exists) + { + gint cmp; + + if (icon_theme) + cmp = gnome_theme_icon_info_compare (theme_info, old_theme_info); + else + cmp = gnome_theme_meta_info_compare (theme_info, old_theme_info); + if (cmp != 0) + { + g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_data_to_hash_by_name (hash_by_name, g_strdup (name), theme_info); + handle_change_signal (icon_theme?GNOME_THEME_TYPE_ICON:GNOME_THEME_TYPE_METATHEME, + theme_info, GNOME_THEME_CHANGE_CHANGED, 0); + if (icon_theme) + gnome_theme_icon_info_free (old_theme_info); + else + gnome_theme_meta_info_free (old_theme_info); + } + else + { + if (icon_theme) + gnome_theme_icon_info_free (theme_info); + else + gnome_theme_meta_info_free (theme_info); + } + } + else + { + if (icon_theme) + name = ((GnomeThemeIconInfo *)old_theme_info)->name; + else + name = ((GnomeThemeMetaInfo *)old_theme_info)->name; + + g_hash_table_remove (hash_by_uri, common_theme_dir); + remove_data_from_hash_by_name (hash_by_name, name, common_theme_dir); + handle_change_signal (icon_theme?GNOME_THEME_TYPE_ICON:GNOME_THEME_TYPE_METATHEME, + old_theme_info, GNOME_THEME_CHANGE_DELETED, 0); + if (icon_theme) + gnome_theme_icon_info_free (old_theme_info); + else + gnome_theme_meta_info_free (old_theme_info); + } + } + + g_free (common_theme_dir); + gnome_vfs_uri_unref (common_theme_dir_uri); +} + +static void +update_meta_theme_index (GnomeVFSURI *meta_theme_index_uri, + gint priority) +{ + update_common_theme_dir_index (meta_theme_index_uri, FALSE, priority); +} +static void +update_icon_theme_index (GnomeVFSURI *icon_theme_index_uri, + gint priority) +{ + update_common_theme_dir_index (icon_theme_index_uri, TRUE, priority); +} + +static void +gtk2_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + GnomeVFSURI *gtk2_dir_uri; + gchar *affected_file; + CommonThemeDirMonitorData *monitor_data; + + monitor_data = user_data; + + gtk2_dir_uri = gnome_vfs_uri_new (info_uri); + affected_file = gnome_vfs_uri_extract_short_name (gtk2_dir_uri); + + /* The only file we care about is gtkrc */ + if (strcmp (affected_file, "gtkrc")) + { + g_free (affected_file); + gnome_vfs_uri_unref (gtk2_dir_uri); + return; + } + + update_gtk2_index (gtk2_dir_uri, monitor_data->priority); + + g_free (affected_file); + gnome_vfs_uri_unref (gtk2_dir_uri); +} + + +static void +keybinding_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + GnomeVFSURI *keybinding_dir_uri; + gchar *affected_file; + CommonThemeDirMonitorData *monitor_data; + + monitor_data = user_data; + + keybinding_dir_uri = gnome_vfs_uri_new (info_uri); + affected_file = gnome_vfs_uri_extract_short_name (keybinding_dir_uri); + + /* The only file we care about is gtkrc */ + if (strcmp (affected_file, "gtkrc")) + { + g_free (affected_file); + gnome_vfs_uri_unref (keybinding_dir_uri); + return; + } + + update_keybinding_index (keybinding_dir_uri, monitor_data->priority); + + g_free (affected_file); + gnome_vfs_uri_unref (keybinding_dir_uri); +} + +static void +metacity_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + GnomeVFSURI *metacity_dir_uri; + gchar *affected_file; + CommonThemeDirMonitorData *monitor_data; + + monitor_data = user_data; + metacity_dir_uri = gnome_vfs_uri_new (info_uri); + affected_file = gnome_vfs_uri_extract_short_name (metacity_dir_uri); - func = user_data; - uri = gnome_vfs_get_local_path_from_uri (info_uri); - if (uri == NULL) - uri = g_strdup (info_uri); - - switch (event_type) + /* The only file we care about is gtkrc */ + if (strcmp (affected_file, "metacity-theme-1.xml")) { - case GNOME_VFS_MONITOR_EVENT_CHANGED: - case GNOME_VFS_MONITOR_EVENT_CREATED: - case GNOME_VFS_MONITOR_EVENT_DELETED: - func (uri); - break; - default: - break; + g_free (affected_file); + gnome_vfs_uri_unref (metacity_dir_uri); + return; } - g_free (uri); -} + update_metacity_index (metacity_dir_uri, monitor_data->priority); -static void -themes_common_list_add_dir (const char *dirname) -{ - GnomeVFSMonitorHandle *handle = NULL; - DIR *dir; - struct dirent *de; - - g_return_if_fail (dirname != NULL); - - dir = opendir (dirname); - - gnome_vfs_monitor_add (&handle, - dirname, - GNOME_VFS_MONITOR_DIRECTORY, - top_theme_dir_changed_callback, - update_theme_dir); - - if (!dir) - return; - - while ((de = readdir (dir))) - { - char *tmp; - - if (de->d_name[0] == '.') - continue; - - tmp = g_build_filename (dirname, de->d_name, NULL); - update_theme_dir (tmp); - g_free (tmp); - } - closedir (dir); + g_free (affected_file); + gnome_vfs_uri_unref (metacity_dir_uri); } static void -icon_themes_add_dir (const char *dirname) +common_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) { - GnomeVFSMonitorHandle *handle = NULL; - DIR *dir; - struct dirent *de; + GnomeVFSURI *meta_theme_dir_uri; + gchar *affected_file; + CommonThemeDirMonitorData *monitor_data; - g_return_if_fail (dirname != NULL); + monitor_data = user_data; - dir = opendir (dirname); + meta_theme_dir_uri = gnome_vfs_uri_new (info_uri); + affected_file = gnome_vfs_uri_extract_short_name (meta_theme_dir_uri); - gnome_vfs_monitor_add (&handle, - dirname, - GNOME_VFS_MONITOR_DIRECTORY, - top_theme_dir_changed_callback, - update_icon_theme_dir); - - if (!dir) - return; - - while ((de = readdir (dir))) + /* The only file we care about is index.theme */ + if (strcmp (affected_file, "index.theme")) { - char *tmp; - - if (de->d_name[0] == '.') - continue; - - tmp = g_build_filename (dirname, de->d_name, NULL); - update_icon_theme_dir (tmp); - g_free (tmp); + gnome_vfs_uri_unref (meta_theme_dir_uri); + g_free (affected_file); + return; } - closedir (dir); + + update_meta_theme_index (meta_theme_dir_uri, monitor_data->priority); + + g_free (affected_file); + gnome_vfs_uri_unref (meta_theme_dir_uri); } static void -gnome_theme_info_init (void) +common_icon_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) { - gchar *dir; - const gchar *gtk_data_dir; - GnomeVFSURI *uri; - + GnomeVFSURI *icon_theme_dir_uri; + gchar *affected_file; + CommonIconThemeDirMonitorData *monitor_data; - if (initted) - return; - initted = TRUE; - initting = TRUE; + monitor_data = user_data; - theme_hash = g_hash_table_new (g_str_hash, g_str_equal); - icon_theme_hash = g_hash_table_new (g_str_hash, g_str_equal); - meta_theme_hash = g_hash_table_new (g_str_hash, g_str_equal); + icon_theme_dir_uri = gnome_vfs_uri_new (info_uri); + affected_file = gnome_vfs_uri_extract_short_name (icon_theme_dir_uri); - dir = g_build_filename (g_get_home_dir (), ".themes", NULL); + /* The only file we care about is index.theme*/ + if (strcmp (affected_file, "index.theme")) + { + gnome_vfs_uri_unref (icon_theme_dir_uri); + g_free (affected_file); + return; + } + update_icon_theme_index (icon_theme_dir_uri, monitor_data->priority); - /* Make sure ~/.themes exists */ - uri = gnome_vfs_uri_new (dir); - if (!gnome_vfs_uri_exists (uri)) - gnome_vfs_make_directory_for_uri (uri, 0775); - gnome_vfs_uri_unref (uri); - - themes_common_list_add_dir (dir); - g_free (dir); - - dir = gtk_rc_get_theme_dir (); - themes_common_list_add_dir (dir); - g_free (dir); - - /* handle icon themes */ - dir = g_build_filename (g_get_home_dir (), ".icons", NULL); - - /* Make sure ~/.themes exists */ - uri = gnome_vfs_uri_new (dir); - if (!gnome_vfs_uri_exists (uri)) - gnome_vfs_make_directory_for_uri (uri, 0775); - gnome_vfs_uri_unref (uri); - - icon_themes_add_dir (dir); - g_free (dir); - - gtk_data_dir = g_getenv ("GTK_DATA_PREFIX"); - if (gtk_data_dir) { - dir = g_build_filename (gtk_data_dir, "share", "icons", NULL); - } else { - dir = g_build_filename (INSTALL_PREFIX, "share", "icons", NULL); - } - - icon_themes_add_dir (dir); - g_free (dir); - - /* Finally, the weird backup for icon themes */ - icon_themes_add_dir ("/usr/share/icons"); - initting = FALSE; + g_free (affected_file); + gnome_vfs_uri_unref (icon_theme_dir_uri); } +static void +top_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + GnomeVFSResult result; + CallbackTuple *tuple; + GHashTable *handle_hash; + CommonThemeDirMonitorData *monitor_data; + GnomeVFSURI *common_theme_dir_uri; + gint priority; -/* Public functions + common_theme_dir_uri = gnome_vfs_uri_new (info_uri); + tuple = user_data; + handle_hash = tuple->handle_hash; + priority = tuple->priority; + + if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED) + { + GnomeVFSFileInfo file_info = {0,}; + + monitor_data = g_new0 (CommonThemeDirMonitorData, 1); + monitor_data->priority = priority; + result = gnome_vfs_get_file_info_uri (common_theme_dir_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) + { + add_common_theme_dir_monitor (common_theme_dir_uri, NULL, monitor_data, NULL); + g_hash_table_insert (handle_hash, file_info.name, monitor_data); + } + } + else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) + { + gchar *name; + CommonThemeDirMonitorData *monitor_data; + + name = gnome_vfs_uri_extract_short_name (common_theme_dir_uri); + 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 (monitor_data); + } + g_free (name); + } + gnome_vfs_uri_unref (common_theme_dir_uri); +} + +static void +top_icon_theme_dir_changed (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + GnomeVFSResult result; + GHashTable *handle_hash; + CallbackTuple *tuple; + CommonIconThemeDirMonitorData *monitor_data; + GnomeVFSURI *common_icon_theme_dir_uri; + gint priority; + + common_icon_theme_dir_uri = gnome_vfs_uri_new (info_uri); + tuple = user_data; + handle_hash = tuple->handle_hash; + priority = tuple->priority; + + if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED) + { + GnomeVFSFileInfo file_info = {0,}; + + monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1); + monitor_data->priority = priority; + result = gnome_vfs_get_file_info_uri (common_icon_theme_dir_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) + { + add_common_icon_theme_dir_monitor (common_icon_theme_dir_uri, NULL, monitor_data, NULL); + g_hash_table_insert (handle_hash, file_info.name, monitor_data); + } + } + else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) + { + gchar *name; + CommonIconThemeDirMonitorData *monitor_data; + + name = gnome_vfs_uri_extract_short_name (common_icon_theme_dir_uri); + 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 (monitor_data); + } + g_free (name); + } + gnome_vfs_uri_unref (common_icon_theme_dir_uri); +} + +/* Add a monitor to a common_theme_dir. */ +static GnomeVFSResult +add_common_theme_dir_monitor (GnomeVFSURI *theme_dir_uri, + gboolean *monitor_not_added, + CommonThemeDirMonitorData *monitor_data, + GError **error) +{ + GnomeVFSResult result; + gchar *uri_string; + gboolean real_monitor_not_added = FALSE; + GnomeVFSURI *subdir; + GnomeVFSURI *index_uri; + GnomeVFSFileInfo file_info = {0,}; + + index_uri = gnome_vfs_uri_append_file_name (theme_dir_uri, "index.theme"); + update_meta_theme_index (index_uri, monitor_data->priority); + gnome_vfs_uri_unref (index_uri); + + /* Add the handle for this directory */ + uri_string = gnome_vfs_uri_to_string (theme_dir_uri, GNOME_VFS_URI_HIDE_NONE); + result = gnome_vfs_monitor_add (& (monitor_data->common_theme_dir_handle), + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + common_theme_dir_changed, + monitor_data); + g_free (uri_string); + + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + real_monitor_not_added = TRUE; + else if (result != GNOME_VFS_OK) + return result; + + /* gtk-2 theme subdir */ + subdir = gnome_vfs_uri_append_path (theme_dir_uri, "gtk-2.0"); + result = gnome_vfs_get_file_info_uri (theme_dir_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) + { + index_uri = gnome_vfs_uri_append_file_name (subdir, "gtkrc"); + update_gtk2_index (index_uri, monitor_data->priority); + gnome_vfs_uri_unref (index_uri); + } + uri_string = gnome_vfs_uri_to_string (subdir, GNOME_VFS_URI_HIDE_NONE); + result = gnome_vfs_monitor_add (& (monitor_data->gtk2_dir_handle), + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + gtk2_dir_changed, + monitor_data); + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + real_monitor_not_added = TRUE; + g_free (uri_string); + gnome_vfs_uri_unref (subdir); + + /* keybinding theme subdir */ + subdir = gnome_vfs_uri_append_path (theme_dir_uri, "gtk-2.0-key"); + result = gnome_vfs_get_file_info_uri (theme_dir_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK && file_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) + { + index_uri = gnome_vfs_uri_append_file_name (subdir, "gtkrc"); + update_keybinding_index (index_uri, monitor_data->priority); + gnome_vfs_uri_unref (index_uri); + } + uri_string = gnome_vfs_uri_to_string (subdir, GNOME_VFS_URI_HIDE_NONE); + result = gnome_vfs_monitor_add (& (monitor_data->keybinding_dir_handle), + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + keybinding_dir_changed, + monitor_data); + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + real_monitor_not_added = TRUE; + g_free (uri_string); + gnome_vfs_uri_unref (subdir); + + /* metacity theme subdir */ + subdir = gnome_vfs_uri_append_path (theme_dir_uri, "metacity-1"); + result = gnome_vfs_get_file_info_uri (theme_dir_uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (file_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) + { + index_uri = gnome_vfs_uri_append_file_name (subdir, "metacity-theme-1.xml"); + update_metacity_index (index_uri, monitor_data->priority); + gnome_vfs_uri_unref (index_uri); + } + uri_string = gnome_vfs_uri_to_string (subdir, GNOME_VFS_URI_HIDE_NONE); + result = gnome_vfs_monitor_add (& (monitor_data->metacity_dir_handle), + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + metacity_dir_changed, + monitor_data); + g_free (uri_string); + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + real_monitor_not_added = TRUE; + gnome_vfs_uri_unref (subdir); + + if (monitor_not_added) + *monitor_not_added = real_monitor_not_added; + + return GNOME_VFS_OK; +} + +static GnomeVFSResult +add_common_icon_theme_dir_monitor (GnomeVFSURI *theme_dir_uri, + gboolean *monitor_not_added, + CommonIconThemeDirMonitorData *monitor_data, + GError **error) +{ + GnomeVFSResult result; + gchar *uri_string; + gboolean real_monitor_not_added = FALSE; + GnomeVFSURI *index_uri; + + /* Add the handle for this directory */ + index_uri = gnome_vfs_uri_append_file_name (theme_dir_uri, "index.theme"); + update_icon_theme_index (index_uri, monitor_data->priority); + gnome_vfs_uri_unref (index_uri); + + uri_string = gnome_vfs_uri_to_string (theme_dir_uri, GNOME_VFS_URI_HIDE_NONE); + result = gnome_vfs_monitor_add (& (monitor_data->common_icon_theme_dir_handle), + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + common_icon_theme_dir_changed, + monitor_data); + g_free (uri_string); + + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + real_monitor_not_added = TRUE; + else if (result != GNOME_VFS_OK) + return result; + + if (monitor_not_added) + *monitor_not_added = real_monitor_not_added; + + return GNOME_VFS_OK; +} + +static void +remove_common_theme_dir_monitor (CommonThemeDirMonitorData *monitor_data) +{ + /* None of the possible errors here are interesting */ + gnome_vfs_monitor_cancel (monitor_data->common_theme_dir_handle); + gnome_vfs_monitor_cancel (monitor_data->gtk2_dir_handle); + gnome_vfs_monitor_cancel (monitor_data->keybinding_dir_handle); + gnome_vfs_monitor_cancel (monitor_data->metacity_dir_handle); + +} + +static void +remove_common_icon_theme_dir_monitor (CommonIconThemeDirMonitorData *monitor_data) +{ + /* None of the possible errors here are interesting */ + gnome_vfs_monitor_cancel (monitor_data->common_icon_theme_dir_handle); +} + +/* Add a monitor to a top dir. These monitors persist for the duration of the + * lib. + */ +static GnomeVFSResult +real_add_top_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + gboolean icon_theme, + GError **error) +{ + GnomeVFSMonitorHandle *monitor_handle = NULL; + GnomeVFSDirectoryHandle *directory_handle = NULL; + GnomeVFSResult result; + GnomeVFSFileInfo file_info = {0,}; + gchar *uri_string; + CallbackTuple *tuple; + + /* 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_new0 (CallbackTuple, 1); + tuple->handle_hash = g_hash_table_new (g_str_hash, g_str_equal); + tuple->priority = priority; + + /* Check the URI */ + gnome_vfs_get_file_info_uri (uri, &file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (file_info.type != GNOME_VFS_FILE_TYPE_DIRECTORY) + return GNOME_VFS_ERROR_NOT_A_DIRECTORY; + /* Monitor the top directory */ + uri_string = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE); + + result = gnome_vfs_monitor_add (&monitor_handle, + uri_string, + GNOME_VFS_MONITOR_DIRECTORY, + icon_theme?top_icon_theme_dir_changed:top_theme_dir_changed, + tuple); + + g_free (uri_string); + + /* We can deal with NOT_SUPPORTED manually */ + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) + *monitor_not_added = TRUE; + else if (result != GNOME_VFS_OK) + return result; + + /* Go through the directory to add monitoring */ + result = gnome_vfs_directory_open_from_uri (&directory_handle, uri, GNOME_VFS_FILE_INFO_DEFAULT); + if (result != GNOME_VFS_OK) + return result; + + while (gnome_vfs_directory_read_next (directory_handle, &file_info) == GNOME_VFS_OK) + { + GnomeVFSURI *theme_dir_uri; + gpointer monitor_data; + + if (file_info.type != GNOME_VFS_FILE_TYPE_DIRECTORY) + continue; + if (file_info.name[0] == '.') + continue; + + /* Add the directory */ + theme_dir_uri = gnome_vfs_uri_append_path (uri, file_info.name); + if (icon_theme) + { + monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1); + ((CommonIconThemeDirMonitorData *)monitor_data)->priority = priority; + add_common_icon_theme_dir_monitor (theme_dir_uri, monitor_not_added, monitor_data, error); + } + else + { + monitor_data = g_new0 (CommonThemeDirMonitorData, 1); + ((CommonThemeDirMonitorData *)monitor_data)->priority = priority; + add_common_theme_dir_monitor (theme_dir_uri, monitor_not_added, monitor_data, error); + } + + + g_hash_table_insert (tuple->handle_hash, file_info.name, monitor_data); + gnome_vfs_uri_unref (theme_dir_uri); + } + + if (result != GNOME_VFS_ERROR_EOF) + return result; + + return GNOME_VFS_OK; +} + +static GnomeVFSResult +add_top_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + GError **error) +{ + return real_add_top_theme_dir_monitor (uri, monitor_not_added, priority, FALSE, error); +} + +static GnomeVFSResult +add_top_icon_theme_dir_monitor (GnomeVFSURI *uri, + gboolean *monitor_not_added, + gint priority, + GError **error) +{ + return real_add_top_theme_dir_monitor (uri, monitor_not_added, priority, TRUE, error); +} + +/* Public functions */ + /* Generic Themes */ GnomeThemeInfo * @@ -456,9 +1268,7 @@ gnome_theme_info_free (GnomeThemeInfo *theme_info) GnomeThemeInfo * gnome_theme_info_find (const gchar *theme_name) { - gnome_theme_info_init (); - - return g_hash_table_lookup (theme_hash, theme_name); + return get_data_from_hash_by_name (theme_hash_by_name, theme_name, -1); } @@ -473,11 +1283,15 @@ gnome_theme_info_find_by_type_helper (gpointer key, gpointer value, gpointer user_data) { - GnomeThemeInfo *theme_info = value; + GList *list; + GnomeThemeInfo *theme_info; struct GnomeThemeInfoHashData *hash_data = user_data; guint elements = GPOINTER_TO_INT (hash_data->user_data); gboolean add_theme = FALSE; + list = value; + theme_info = list->data; + if (elements & GNOME_THEME_METACITY && theme_info->has_metacity) add_theme = TRUE; @@ -492,19 +1306,6 @@ gnome_theme_info_find_by_type_helper (gpointer key, hash_data->list = g_list_prepend (hash_data->list, theme_info); } - -static void -gnome_theme_info_find_by_dir_helper (gpointer key, - gpointer value, - gpointer user_data) -{ - GnomeThemeInfo *theme_info = value; - struct GnomeThemeInfoHashData *hash_data = user_data; - - if (! strcmp (hash_data->user_data, theme_info->path)) - hash_data->list = g_list_prepend (hash_data->list, theme_info); -} - GList * gnome_theme_info_find_by_type (guint elements) { @@ -512,40 +1313,22 @@ gnome_theme_info_find_by_type (guint elements) data.user_data = GINT_TO_POINTER (elements); data.list = NULL; - gnome_theme_info_init (); - - g_hash_table_foreach (theme_hash, + g_hash_table_foreach (theme_hash_by_name, gnome_theme_info_find_by_type_helper, &data); return data.list; } - GnomeThemeInfo * -gnome_theme_info_find_by_dir (const gchar *theme_dir) +gnome_theme_info_find_by_uri (const gchar *theme_uri) { - struct GnomeThemeInfoHashData data; - GnomeThemeInfo *retval = NULL; + g_return_val_if_fail (theme_uri != NULL, NULL); - data.user_data = theme_dir; - data.list = NULL; - - gnome_theme_info_init (); - - g_hash_table_foreach (theme_hash, - gnome_theme_info_find_by_dir_helper, - &data); - - if (data.list) - { - retval = data.list->data; - g_list_free (data.list); - } - - return retval; + return g_hash_table_lookup (theme_hash_by_uri, theme_uri); } + /* Icon themes */ GnomeThemeIconInfo * gnome_theme_icon_info_new (void) @@ -568,9 +1351,7 @@ gnome_theme_icon_info_find (const gchar *icon_theme_name) { g_return_val_if_fail (icon_theme_name != NULL, NULL); - gnome_theme_info_init (); - - return g_hash_table_lookup (icon_theme_hash, icon_theme_name); + return get_data_from_hash_by_name (icon_theme_hash_by_name, icon_theme_name, -1); } @@ -579,10 +1360,13 @@ gnome_theme_icon_info_find_all_helper (gpointer key, gpointer value, gpointer user_data) { - GnomeThemeIconInfo *theme_info = value; - struct GnomeThemeInfoHashData *hash_data = user_data; + GList *list = value; + struct GnomeThemeInfoHashData *hash_data; - hash_data->list = g_list_prepend (hash_data->list, theme_info); + list = value; + hash_data = user_data; + + hash_data->list = g_list_prepend (hash_data->list, list->data); } GList * @@ -592,9 +1376,7 @@ gnome_theme_icon_info_find_all (void) struct GnomeThemeInfoHashData data; data.list = NULL; - gnome_theme_info_init (); - - g_hash_table_foreach (icon_theme_hash, + g_hash_table_foreach (icon_theme_hash_by_name, gnome_theme_icon_info_find_all_helper, &data); @@ -602,6 +1384,20 @@ gnome_theme_icon_info_find_all (void) } +gint +gnome_theme_icon_info_compare (GnomeThemeIconInfo *a, + GnomeThemeIconInfo *b) +{ + gint cmp = 0; + + cmp = safe_strcmp (a->path, b->path); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->name, b->name); + return cmp; +} + + /* Meta themes*/ GnomeThemeMetaInfo * gnome_theme_meta_info_new (void) @@ -629,69 +1425,48 @@ gnome_theme_meta_info_free (GnomeThemeMetaInfo *meta_theme_info) g_free (meta_theme_info); } +void +gnome_theme_meta_info_print (GnomeThemeMetaInfo *meta_theme_info) +{ + g_print ("path: %s\n", meta_theme_info->path); + g_print ("readable_name: %s\n", meta_theme_info->readable_name); + g_print ("name: %s\n", meta_theme_info->name); + g_print ("comment: %s\n", meta_theme_info->comment); + g_print ("icon_file: %s\n", meta_theme_info->icon_file); + g_print ("gtk_theme_name: %s\n", meta_theme_info->gtk_theme_name); + g_print ("metacity_theme_name: %s\n", meta_theme_info->metacity_theme_name); + g_print ("icon_theme_name: %s\n", meta_theme_info->icon_theme_name); + g_print ("sawfish_theme_name: %s\n", meta_theme_info->sawfish_theme_name); + g_print ("sound_theme_name: %s\n", meta_theme_info->sound_theme_name); + g_print ("application_font: %s\n", meta_theme_info->application_font); + g_print ("background_image: %s\n", meta_theme_info->background_image); +} + GnomeThemeMetaInfo * gnome_theme_meta_info_find (const char *meta_theme_name) { g_return_val_if_fail (meta_theme_name != NULL, NULL); - gnome_theme_info_init (); - - return g_hash_table_lookup (meta_theme_hash, meta_theme_name); -} - -static void -gnome_theme_meta_info_find_by_filename_helper (gpointer key, - gpointer value, - gpointer user_data) -{ - GnomeThemeMetaInfo *theme_info = value; - struct GnomeThemeInfoHashData *hash_data = user_data; - - g_assert (theme_info->path); - - if (!strcmp (hash_data->user_data, theme_info->path)) - hash_data->list = g_list_prepend (hash_data->list, theme_info); + return get_data_from_hash_by_name (meta_theme_hash_by_name, meta_theme_name, -1); } GnomeThemeMetaInfo * -gnome_theme_meta_info_find_by_filename (const char *file_name) +gnome_theme_meta_info_find_by_uri (const char *theme_uri) { - struct GnomeThemeInfoHashData hash_data; - GnomeThemeMetaInfo *retval; + g_return_val_if_fail (theme_uri != NULL, NULL); - g_return_val_if_fail (file_name != NULL, NULL); - - gnome_theme_info_init (); - hash_data.list = NULL; - hash_data.user_data = file_name; - - g_hash_table_foreach (meta_theme_hash, - gnome_theme_meta_info_find_by_filename_helper, - &hash_data); - - if (hash_data.list) - { - retval = hash_data.list->data; - g_list_free (hash_data.list); - } - else - retval = NULL; - - return retval; + return g_hash_table_lookup (meta_theme_hash_by_uri, theme_uri); } - - - static void gnome_theme_meta_info_find_all_helper (gpointer key, gpointer value, gpointer user_data) { - GnomeThemeMetaInfo *theme_info = value; + GList *list = value; struct GnomeThemeInfoHashData *hash_data = user_data; - hash_data->list = g_list_prepend (hash_data->list, theme_info); + hash_data->list = g_list_prepend (hash_data->list, list->data); } GList * @@ -701,15 +1476,55 @@ gnome_theme_meta_info_find_all (void) struct GnomeThemeInfoHashData data; data.list = NULL; - gnome_theme_info_init (); - - g_hash_table_foreach (meta_theme_hash, + g_hash_table_foreach (meta_theme_hash_by_name, gnome_theme_meta_info_find_all_helper, &data); return data.list; } +gint +gnome_theme_meta_info_compare (GnomeThemeMetaInfo *a, + GnomeThemeMetaInfo *b) +{ + gint cmp = 0; + + 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->metacity_theme_name, b->metacity_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->sawfish_theme_name, b->sawfish_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->background_image, b->background_image); + return cmp; +} void gnome_theme_info_register_theme_change (GFunc func, @@ -726,3 +1541,100 @@ gnome_theme_info_register_theme_change (GFunc func, callbacks = g_list_prepend (callbacks, callback_data); } +void +gnome_theme_init (gboolean *monitor_not_added) +{ + GnomeVFSURI *top_theme_dir_uri; + gchar *top_theme_dir_string; + gboolean real_monitor_not_added = FALSE; + static gboolean initted = FALSE; + GnomeVFSResult result; + const gchar *gtk_data_dir; + 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); + 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_uri = gnome_vfs_uri_new (top_theme_dir_string); + result = add_top_theme_dir_monitor (top_theme_dir_uri, &real_monitor_not_added, 1, NULL); + g_free (top_theme_dir_string); + gnome_vfs_uri_unref (top_theme_dir_uri); + + /* ~/.themes */ + top_theme_dir_string = g_build_filename (g_get_home_dir (), ".themes", NULL); + top_theme_dir_uri = gnome_vfs_uri_new (top_theme_dir_string); + g_free (top_theme_dir_string); + if (!gnome_vfs_uri_exists (top_theme_dir_uri)) + gnome_vfs_make_directory_for_uri (top_theme_dir_uri, 0775); + result = add_top_theme_dir_monitor (top_theme_dir_uri, &real_monitor_not_added, 0, NULL); + gnome_vfs_uri_unref (top_theme_dir_uri); + + /* The weird /usr/share/icons */ + top_theme_dir_uri = gnome_vfs_uri_new ("/usr/share/icons"); + if (!gnome_vfs_uri_exists (top_theme_dir_uri)) + gnome_vfs_make_directory_for_uri (top_theme_dir_uri, 0775); + result = add_top_icon_theme_dir_monitor (top_theme_dir_uri, &real_monitor_not_added, 2, NULL); + gnome_vfs_uri_unref (top_theme_dir_uri); + + /* $datadir/icons */ + gtk_data_dir = g_getenv ("GTK_DATA_PREFIX"); + if (gtk_data_dir) + { + top_theme_dir_string = g_build_filename (gtk_data_dir, "share", "icons", NULL); + } + else + { + top_theme_dir_string = g_build_filename (INSTALL_PREFIX, "share", "icons", NULL); + } + top_theme_dir_uri = gnome_vfs_uri_new (top_theme_dir_string); + g_free (top_theme_dir_string); + + if (!gnome_vfs_uri_exists (top_theme_dir_uri)) + gnome_vfs_make_directory_for_uri (top_theme_dir_uri, 0775); + result = add_top_icon_theme_dir_monitor (top_theme_dir_uri, &real_monitor_not_added, 1, NULL); + gnome_vfs_uri_unref (top_theme_dir_uri); + + /* ~/.icons */ + top_theme_dir_string = g_build_filename (g_get_home_dir (), ".icons", NULL); + top_theme_dir_uri = gnome_vfs_uri_new (top_theme_dir_string); + g_free (top_theme_dir_string); + + if (!gnome_vfs_uri_exists (top_theme_dir_uri)) + gnome_vfs_make_directory_for_uri (top_theme_dir_uri, 0775); + result = add_top_icon_theme_dir_monitor (top_theme_dir_uri, &real_monitor_not_added, 0, NULL); + gnome_vfs_uri_unref (top_theme_dir_uri); + + /* done */ + initted = TRUE; + initting = FALSE; + + if (monitor_not_added) + *monitor_not_added = real_monitor_not_added; +} + +#if 0 +int +main (int argc, char *argv[]) +{ + gtk_init (&argc, &argv); + gnome_vfs_init (); + gboolean monitor_not_added = FALSE; + + gnome_theme_init (&monitor_not_added); + + gtk_main (); + + return 0; +} +#endif diff --git a/capplets/common/gnome-theme-info.h b/capplets/common/gnome-theme-info.h index 08e069179..d0228e36b 100644 --- a/capplets/common/gnome-theme-info.h +++ b/capplets/common/gnome-theme-info.h @@ -9,12 +9,12 @@ 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 Gnome 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 Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, @@ -29,17 +29,32 @@ #include +typedef enum { + GNOME_THEME_TYPE_METATHEME, + GNOME_THEME_TYPE_ICON, + GNOME_THEME_TYPE_REGULAR, +} GnomeThemeType; + +typedef enum { + GNOME_THEME_CHANGE_CREATED, + GNOME_THEME_CHANGE_DELETED, + GNOME_THEME_CHANGE_CHANGED +} GnomeThemeChangeType; + + typedef enum { GNOME_THEME_METACITY = 1 << 0, GNOME_THEME_GTK_2 = 1 << 1, GNOME_THEME_GTK_2_KEYBINDING = 1 << 2, } GnomeThemeElement; + typedef struct _GnomeThemeInfo GnomeThemeInfo; struct _GnomeThemeInfo { gchar *path; gchar *name; + gint priority; guint has_gtk : 1; guint has_keybinding : 1; guint has_metacity : 1; @@ -51,14 +66,16 @@ struct _GnomeThemeIconInfo { gchar *path; gchar *name; + gint priority; }; typedef struct _GnomeThemeMetaInfo GnomeThemeMetaInfo; struct _GnomeThemeMetaInfo { gchar *path; - gchar *readable_name; gchar *name; + gint priority; + gchar *readable_name; gchar *comment; gchar *icon_file; @@ -67,7 +84,7 @@ struct _GnomeThemeMetaInfo gchar *icon_theme_name; gchar *sawfish_theme_name; gchar *sound_theme_name; - + gchar *application_font; gchar *background_image; }; @@ -78,8 +95,8 @@ GnomeThemeInfo *gnome_theme_info_new (void); void gnome_theme_info_free (GnomeThemeInfo *theme_info); GnomeThemeInfo *gnome_theme_info_find (const gchar *theme_name); GList *gnome_theme_info_find_by_type (guint elements); -GnomeThemeInfo *gnome_theme_info_find_by_dir (const gchar *theme_dir); - +/* Expected to be in the form "file:///usr/share/..." */ +GnomeThemeInfo *gnome_theme_info_find_by_uri (const gchar *theme_uri); /* Icon Themes */ @@ -87,18 +104,23 @@ GnomeThemeIconInfo *gnome_theme_icon_info_new (void); void gnome_theme_icon_info_free (GnomeThemeIconInfo *icon_theme_info); GnomeThemeInfo *gnome_theme_icon_info_find (const gchar *icon_theme_name); GList *gnome_theme_icon_info_find_all (void); +gint gnome_theme_icon_info_compare (GnomeThemeIconInfo *a, + GnomeThemeIconInfo *b); + - /* Meta themes*/ GnomeThemeMetaInfo *gnome_theme_meta_info_new (void); void gnome_theme_meta_info_free (GnomeThemeMetaInfo *meta_theme_info); +void gnome_theme_meta_info_print (GnomeThemeMetaInfo *meta_theme_info); GnomeThemeMetaInfo *gnome_theme_meta_info_find (const char *meta_theme_name); -GnomeThemeMetaInfo *gnome_theme_meta_info_find_by_filename (const char *file_name); +GnomeThemeMetaInfo *gnome_theme_meta_info_find_by_uri (const char *uri); GList *gnome_theme_meta_info_find_all (void); +gint gnome_theme_meta_info_compare (GnomeThemeMetaInfo *a, + GnomeThemeMetaInfo *b); - -/* Theme monitoring */ +/* Other */ +void gnome_theme_init (gboolean *monitor_not_added); void gnome_theme_info_register_theme_change (GFunc func, gpointer data); diff --git a/capplets/keybindings/ChangeLog b/capplets/keybindings/ChangeLog index a807b94ca..337d7d900 100644 --- a/capplets/keybindings/ChangeLog +++ b/capplets/keybindings/ChangeLog @@ -1,3 +1,7 @@ +Mon Jan 13 15:14:22 2003 Jonathan Blandford + + * gnome-keybinding-properties.c (main): gnome_theme_init() added. + 2003-01-10 Jody Goldberg * Release 2.1.6 diff --git a/capplets/keybindings/eggcellrendererkeys.c b/capplets/keybindings/eggcellrendererkeys.c index 4e6401bf8..5d5c1df11 100644 --- a/capplets/keybindings/eggcellrendererkeys.c +++ b/capplets/keybindings/eggcellrendererkeys.c @@ -347,7 +347,7 @@ grab_key_callback (GtkWidget *widget, GdkEventKey *event, void *data) { - GdkModifierType accel_mods; + GdkModifierType accel_mods = 0; guint accel_keyval; EggCellRendererKeys *keys; char *path; diff --git a/capplets/keybindings/gnome-keybinding-properties.c b/capplets/keybindings/gnome-keybinding-properties.c index cb4022495..7b5156542 100644 --- a/capplets/keybindings/gnome-keybinding-properties.c +++ b/capplets/keybindings/gnome-keybinding-properties.c @@ -959,6 +959,8 @@ main (int argc, char *argv[]) GNOME_PARAM_APP_DATADIR, GNOMECC_DATA_DIR, NULL); + gnome_theme_init (NULL); + activate_settings_daemon (); dialog = create_dialog (); diff --git a/capplets/theme-switcher/ChangeLog b/capplets/theme-switcher/ChangeLog index 79bcaae9a..ee4573af3 100644 --- a/capplets/theme-switcher/ChangeLog +++ b/capplets/theme-switcher/ChangeLog @@ -1,3 +1,7 @@ +Mon Jan 13 15:20:17 2003 Jonathan Blandford + + * gnome-theme-manager.c (main): gnome_theme_init () + 2003-01-10 Jody Goldberg * Release 2.1.6 diff --git a/capplets/theme-switcher/gnome-theme-manager.c b/capplets/theme-switcher/gnome-theme-manager.c index 22f76fe88..505e55afd 100644 --- a/capplets/theme-switcher/gnome-theme-manager.c +++ b/capplets/theme-switcher/gnome-theme-manager.c @@ -1404,6 +1404,8 @@ main (int argc, char *argv[]) GNOME_PARAM_APP_DATADIR, GNOMECC_DATA_DIR, NULL); + gnome_theme_init (NULL); + gnome_wm_manager_init (); activate_settings_daemon ();