diff options
Diffstat (limited to 'libmenu/entry-directories.c')
-rw-r--r-- | libmenu/entry-directories.c | 1105 |
1 files changed, 1105 insertions, 0 deletions
diff --git a/libmenu/entry-directories.c b/libmenu/entry-directories.c new file mode 100644 index 0000000..a442a65 --- /dev/null +++ b/libmenu/entry-directories.c @@ -0,0 +1,1105 @@ +/* + * Copyright (C) 2002 - 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + */ + +#include <config.h> + +#include "entry-directories.h" + +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <dirent.h> + +#include "menu-util.h" +#include "menu-monitor.h" +#include "canonicalize.h" + +typedef struct CachedDir CachedDir; +typedef struct CachedDirMonitor CachedDirMonitor; + +struct EntryDirectory { + CachedDir* dir; + char* legacy_prefix; + + guint entry_type: 2; + guint is_legacy: 1; + guint refcount: 24; +}; + +struct EntryDirectoryList { + int refcount; + int length; + GList* dirs; +}; + +struct CachedDir { + CachedDir* parent; + char* name; + + GSList* entries; + GSList* subdirs; + + MenuMonitor* dir_monitor; + GSList* monitors; + + guint have_read_entries: 1; + guint deleted: 1; + + guint references: 28; +}; + +struct CachedDirMonitor { + EntryDirectory* ed; + EntryDirectoryChangedFunc callback; + gpointer user_data; +}; + +static void cached_dir_free(CachedDir* dir); +static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname); + +static void handle_cached_dir_changed(MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir); + +/* + * Entry directory cache + */ + +static CachedDir* dir_cache = NULL; + +static CachedDir* cached_dir_new(const char *name) +{ + CachedDir* dir; + + dir = g_new0(CachedDir, 1); + + dir->name = g_strdup(name); + + return dir; +} + +static void cached_dir_free(CachedDir* dir) +{ + if (dir->dir_monitor) + { + menu_monitor_remove_notify (dir->dir_monitor, + (MenuMonitorNotifyFunc) handle_cached_dir_changed, + dir); + menu_monitor_unref (dir->dir_monitor); + dir->dir_monitor = NULL; + } + + g_slist_foreach (dir->monitors, (GFunc) g_free, NULL); + g_slist_free (dir->monitors); + dir->monitors = NULL; + + g_slist_foreach (dir->entries, + (GFunc) desktop_entry_unref, + NULL); + g_slist_free (dir->entries); + dir->entries = NULL; + + g_slist_foreach (dir->subdirs, + (GFunc) cached_dir_free, + NULL); + g_slist_free (dir->subdirs); + dir->subdirs = NULL; + + g_free (dir->name); + g_free (dir); +} + +static inline CachedDir* find_subdir(CachedDir* dir, const char* subdir) +{ + GSList *tmp; + + tmp = dir->subdirs; + while (tmp != NULL) + { + CachedDir *sub = tmp->data; + + if (strcmp (sub->name, subdir) == 0) + return sub; + + tmp = tmp->next; + } + + return NULL; +} + +static DesktopEntry* find_entry(CachedDir* dir, const char* basename) +{ + GSList *tmp; + + tmp = dir->entries; + while (tmp != NULL) + { + if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) + return tmp->data; + + tmp = tmp->next; + } + + return NULL; +} + +static DesktopEntry* cached_dir_find_relative_path(CachedDir* dir, const char* relative_path) +{ + DesktopEntry *retval = NULL; + char **split; + int i; + + split = g_strsplit (relative_path, "/", -1); + + i = 0; + while (split[i] != NULL) + { + if (split[i + 1] != NULL) + { + if ((dir = find_subdir (dir, split[i])) == NULL) + break; + } + else + { + retval = find_entry (dir, split[i]); + break; + } + + ++i; + } + + g_strfreev (split); + + return retval; +} + +static CachedDir* cached_dir_lookup(const char* canonical) +{ + CachedDir *dir; + char **split; + int i; + + if (dir_cache == NULL) + dir_cache = cached_dir_new ("/"); + dir = dir_cache; + + g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); + + menu_verbose ("Looking up cached dir \"%s\"\n", canonical); + + split = g_strsplit (canonical + 1, "/", -1); + + i = 0; + while (split[i] != NULL) + { + CachedDir *subdir; + + if ((subdir = find_subdir (dir, split[i])) == NULL) + { + subdir = cached_dir_new (split[i]); + dir->subdirs = g_slist_prepend (dir->subdirs, subdir); + subdir->parent = dir; + } + + dir = subdir; + + ++i; + } + + g_strfreev (split); + + g_assert (dir != NULL); + + return dir; +} + +static gboolean cached_dir_add_entry(CachedDir* dir, const char* basename, const char* path) +{ + DesktopEntry *entry; + + entry = desktop_entry_new (path); + if (entry == NULL) + return FALSE; + + dir->entries = g_slist_prepend (dir->entries, entry); + + return TRUE; +} + +static gboolean cached_dir_update_entry(CachedDir* dir, const char* basename, const char* path) +{ + GSList *tmp; + + tmp = dir->entries; + while (tmp != NULL) + { + if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) + { + if (!desktop_entry_reload (tmp->data)) + { + dir->entries = g_slist_delete_link (dir->entries, tmp); + } + + return TRUE; + } + + tmp = tmp->next; + } + + return cached_dir_add_entry (dir, basename, path); +} + +static gboolean cached_dir_remove_entry(CachedDir* dir, const char* basename) +{ + GSList *tmp; + + tmp = dir->entries; + while (tmp != NULL) + { + if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) + { + desktop_entry_unref (tmp->data); + dir->entries = g_slist_delete_link (dir->entries, tmp); + return TRUE; + } + + tmp = tmp->next; + } + + return FALSE; +} + +static gboolean cached_dir_add_subdir(CachedDir* dir, const char* basename, const char* path) +{ + CachedDir *subdir; + + subdir = find_subdir (dir, basename); + + if (subdir != NULL) + { + subdir->deleted = FALSE; + return TRUE; + } + + subdir = cached_dir_new (basename); + + if (!cached_dir_load_entries_recursive (subdir, path)) + { + cached_dir_free (subdir); + return FALSE; + } + + menu_verbose ("Caching dir \"%s\"\n", basename); + + subdir->parent = dir; + dir->subdirs = g_slist_prepend (dir->subdirs, subdir); + + return TRUE; +} + +static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename) +{ + CachedDir *subdir; + + subdir = find_subdir (dir, basename); + + if (subdir != NULL) + { + subdir->deleted = TRUE; + + if (subdir->references == 0) + { + cached_dir_free (subdir); + dir->subdirs = g_slist_remove (dir->subdirs, subdir); + } + + return TRUE; + } + + return FALSE; +} + +static void cached_dir_invoke_monitors(CachedDir* dir) +{ + GSList *tmp; + + tmp = dir->monitors; + while (tmp != NULL) + { + CachedDirMonitor *monitor = tmp->data; + GSList *next = tmp->next; + + monitor->callback (monitor->ed, monitor->user_data); + + tmp = next; + } + + if (dir->parent) + { + cached_dir_invoke_monitors (dir->parent); + } +} + +static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir) +{ + gboolean handled = FALSE; + char *basename; + char *dirname; + + menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", + dir->name, + path, + event == MENU_MONITOR_EVENT_CREATED ? ("created") : + event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); + + dirname = g_path_get_dirname (path); + basename = g_path_get_basename (path); + + dir = cached_dir_lookup (dirname); + + if (g_str_has_suffix (basename, ".desktop") || + g_str_has_suffix (basename, ".directory")) + { + switch (event) + { + case MENU_MONITOR_EVENT_CREATED: + case MENU_MONITOR_EVENT_CHANGED: + handled = cached_dir_update_entry (dir, basename, path); + break; + + case MENU_MONITOR_EVENT_DELETED: + handled = cached_dir_remove_entry (dir, basename); + break; + + default: + g_assert_not_reached (); + break; + } + } + else /* Try recursing */ + { + switch (event) + { + case MENU_MONITOR_EVENT_CREATED: + handled = cached_dir_add_subdir (dir, basename, path); + break; + + case MENU_MONITOR_EVENT_CHANGED: + break; + + case MENU_MONITOR_EVENT_DELETED: + handled = cached_dir_remove_subdir (dir, basename); + break; + + default: + g_assert_not_reached (); + break; + } + } + + g_free (basename); + g_free (dirname); + + if (handled) + { + /* CHANGED events don't change the set of desktop entries */ + if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED) + { + _entry_directory_list_empty_desktop_cache (); + } + + cached_dir_invoke_monitors (dir); + } +} + +static void cached_dir_ensure_monitor(CachedDir* dir, const char* dirname) +{ + if (dir->dir_monitor == NULL) + { + dir->dir_monitor = menu_get_directory_monitor (dirname); + menu_monitor_add_notify (dir->dir_monitor, + (MenuMonitorNotifyFunc) handle_cached_dir_changed, + dir); + } +} + +static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname) +{ + DIR *dp; + struct dirent *dent; + GString *fullpath; + gsize fullpath_len; + + g_assert (dir != NULL); + + if (dir->have_read_entries) + return TRUE; + + menu_verbose ("Attempting to read entries from %s (full path %s)\n", + dir->name, dirname); + + dp = opendir (dirname); + if (dp == NULL) + { + menu_verbose ("Unable to list directory \"%s\"\n", + dirname); + return FALSE; + } + + cached_dir_ensure_monitor (dir, dirname); + + fullpath = g_string_new (dirname); + if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR) + g_string_append_c (fullpath, G_DIR_SEPARATOR); + + fullpath_len = fullpath->len; + + while ((dent = readdir (dp)) != NULL) + { + /* ignore . and .. */ + if (dent->d_name[0] == '.' && + (dent->d_name[1] == '\0' || + (dent->d_name[1] == '.' && + dent->d_name[2] == '\0'))) + continue; + + g_string_append (fullpath, dent->d_name); + + if (g_str_has_suffix (dent->d_name, ".desktop") || + g_str_has_suffix (dent->d_name, ".directory")) + { + cached_dir_add_entry (dir, dent->d_name, fullpath->str); + } + else /* Try recursing */ + { + cached_dir_add_subdir (dir, dent->d_name, fullpath->str); + } + + g_string_truncate (fullpath, fullpath_len); + } + + closedir (dp); + + g_string_free (fullpath, TRUE); + + dir->have_read_entries = TRUE; + + return TRUE; +} + +static void cached_dir_add_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + CachedDirMonitor *monitor; + GSList *tmp; + + tmp = dir->monitors; + while (tmp != NULL) + { + monitor = tmp->data; + + if (monitor->ed == ed && + monitor->callback == callback && + monitor->user_data == user_data) + break; + + tmp = tmp->next; + } + + if (tmp == NULL) + { + monitor = g_new0 (CachedDirMonitor, 1); + monitor->ed = ed; + monitor->callback = callback; + monitor->user_data = user_data; + + dir->monitors = g_slist_append (dir->monitors, monitor); + } +} + +static void cached_dir_remove_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + GSList *tmp; + + tmp = dir->monitors; + while (tmp != NULL) + { + CachedDirMonitor *monitor = tmp->data; + GSList *next = tmp->next; + + if (monitor->ed == ed && + monitor->callback == callback && + monitor->user_data == user_data) + { + dir->monitors = g_slist_delete_link (dir->monitors, tmp); + g_free (monitor); + } + + tmp = next; + } +} + +static void cached_dir_add_reference(CachedDir* dir) +{ + dir->references++; + + if (dir->parent != NULL) + { + cached_dir_add_reference (dir->parent); + } +} + +static void cached_dir_remove_reference(CachedDir* dir) +{ + CachedDir *parent; + + parent = dir->parent; + + if (--dir->references == 0 && dir->deleted) + { + if (dir->parent != NULL) + { + GSList *tmp; + + tmp = parent->subdirs; + while (tmp != NULL) + { + CachedDir *subdir = tmp->data; + + if (!strcmp (subdir->name, dir->name)) + { + parent->subdirs = g_slist_delete_link (parent->subdirs, tmp); + break; + } + + tmp = tmp->next; + } + } + + cached_dir_free (dir); + } + + if (parent != NULL) + { + cached_dir_remove_reference (parent); + } +} + +/* + * Entry directories + */ + +static EntryDirectory* entry_directory_new_full(DesktopEntryType entry_type, const char* path, gboolean is_legacy, const char* legacy_prefix) +{ + EntryDirectory *ed; + char *canonical; + + menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n", + path, + is_legacy ? "<yes>" : "<no>"); + + canonical = menu_canonicalize_file_name (path, FALSE); + if (canonical == NULL) + { + menu_verbose ("Failed to canonicalize \"%s\": %s\n", + path, g_strerror (errno)); + return NULL; + } + + ed = g_new0 (EntryDirectory, 1); + + ed->dir = cached_dir_lookup (canonical); + g_assert (ed->dir != NULL); + + cached_dir_add_reference (ed->dir); + cached_dir_load_entries_recursive (ed->dir, canonical); + + ed->legacy_prefix = g_strdup (legacy_prefix); + ed->entry_type = entry_type; + ed->is_legacy = is_legacy != FALSE; + ed->refcount = 1; + + g_free (canonical); + + return ed; +} + +EntryDirectory* entry_directory_new(DesktopEntryType entry_type, const char* path) +{ + return entry_directory_new_full (entry_type, path, FALSE, NULL); +} + +EntryDirectory* entry_directory_new_legacy(DesktopEntryType entry_type, const char* path, const char* legacy_prefix) +{ + return entry_directory_new_full(entry_type, path, TRUE, legacy_prefix); +} + +EntryDirectory* entry_directory_ref(EntryDirectory* ed) +{ + g_return_val_if_fail(ed != NULL, NULL); + g_return_val_if_fail(ed->refcount > 0, NULL); + + ed->refcount++; + + return ed; +} + +void entry_directory_unref(EntryDirectory* ed) +{ + g_return_if_fail (ed != NULL); + g_return_if_fail (ed->refcount > 0); + + if (--ed->refcount == 0) + { + cached_dir_remove_reference (ed->dir); + + ed->dir = NULL; + ed->entry_type = DESKTOP_ENTRY_INVALID; + ed->is_legacy = FALSE; + + g_free (ed->legacy_prefix); + ed->legacy_prefix = NULL; + + g_free (ed); + } +} + +static void entry_directory_add_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + cached_dir_add_monitor (ed->dir, ed, callback, user_data); +} + +static void entry_directory_remove_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + cached_dir_remove_monitor (ed->dir, ed, callback, user_data); +} + +static DesktopEntry* entry_directory_get_directory(EntryDirectory* ed, const char* relative_path) +{ + DesktopEntry *entry; + + if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY) + return NULL; + + entry = cached_dir_find_relative_path (ed->dir, relative_path); + if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY) + return NULL; + + return desktop_entry_ref (entry); +} + +static char* get_desktop_file_id_from_path(EntryDirectory* ed, DesktopEntryType entry_type, const char* relative_path) +{ + char *retval; + + retval = NULL; + + if (entry_type == DESKTOP_ENTRY_DESKTOP) + { + if (!ed->is_legacy) + { + retval = g_strdelimit (g_strdup (relative_path), "/", '-'); + } + else + { + char *basename; + + basename = g_path_get_basename (relative_path); + + if (ed->legacy_prefix) + { + retval = g_strjoin ("-", ed->legacy_prefix, basename, NULL); + g_free (basename); + } + else + { + retval = basename; + } + } + } + else + { + retval = g_strdup (relative_path); + } + + return retval; +} + +typedef gboolean (*EntryDirectoryForeachFunc) (EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data); + +static gboolean entry_directory_foreach_recursive(EntryDirectory* ed, CachedDir* cd, GString* relative_path, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data) +{ + GSList *tmp; + int relative_path_len; + + if (cd->deleted) + return TRUE; + + relative_path_len = relative_path->len; + + tmp = cd->entries; + while (tmp != NULL) + { + DesktopEntry *entry = tmp->data; + + if (desktop_entry_get_type (entry) == ed->entry_type) + { + gboolean ret; + char *file_id; + + g_string_append (relative_path, + desktop_entry_get_basename (entry)); + + file_id = get_desktop_file_id_from_path (ed, + ed->entry_type, + relative_path->str); + + ret = func (ed, entry, file_id, set, user_data); + + g_free (file_id); + + g_string_truncate (relative_path, relative_path_len); + + if (!ret) + return FALSE; + } + + tmp = tmp->next; + } + + tmp = cd->subdirs; + while (tmp != NULL) + { + CachedDir *subdir = tmp->data; + + g_string_append (relative_path, subdir->name); + g_string_append_c (relative_path, G_DIR_SEPARATOR); + + if (!entry_directory_foreach_recursive (ed, + subdir, + relative_path, + func, + set, + user_data)) + return FALSE; + + g_string_truncate (relative_path, relative_path_len); + + tmp = tmp->next; + } + + return TRUE; +} + +static void entry_directory_foreach(EntryDirectory* ed, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data) +{ + GString *path; + + path = g_string_new (NULL); + + entry_directory_foreach_recursive (ed, + ed->dir, + path, + func, + set, + user_data); + + g_string_free (path, TRUE); +} + +void entry_directory_get_flat_contents(EntryDirectory* ed, DesktopEntrySet* desktop_entries, DesktopEntrySet* directory_entries, GSList** subdirs) +{ + GSList *tmp; + + if (subdirs) + *subdirs = NULL; + + tmp = ed->dir->entries; + while (tmp != NULL) + { + DesktopEntry *entry = tmp->data; + const char *basename; + + basename = desktop_entry_get_basename (entry); + + if (desktop_entries && + desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP) + { + char *file_id; + + file_id = get_desktop_file_id_from_path (ed, + DESKTOP_ENTRY_DESKTOP, + basename); + + desktop_entry_set_add_entry (desktop_entries, + entry, + file_id); + + g_free (file_id); + } + + if (directory_entries && + desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY) + { + desktop_entry_set_add_entry (directory_entries, + entry, + basename); + } + + tmp = tmp->next; + } + + if (subdirs) + { + tmp = ed->dir->subdirs; + while (tmp != NULL) + { + CachedDir *cd = tmp->data; + + if (!cd->deleted) + { + *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name)); + } + + tmp = tmp->next; + } + } + + if (subdirs) + *subdirs = g_slist_reverse (*subdirs); +} + +/* + * Entry directory lists + */ + +EntryDirectoryList* entry_directory_list_new(void) +{ + EntryDirectoryList *list; + + list = g_new0 (EntryDirectoryList, 1); + + list->refcount = 1; + list->dirs = NULL; + list->length = 0; + + return list; +} + +EntryDirectoryList* entry_directory_list_ref(EntryDirectoryList* list) +{ + g_return_val_if_fail (list != NULL, NULL); + g_return_val_if_fail (list->refcount > 0, NULL); + + list->refcount += 1; + + return list; +} + +void entry_directory_list_unref(EntryDirectoryList* list) +{ + g_return_if_fail (list != NULL); + g_return_if_fail (list->refcount > 0); + + list->refcount -= 1; + if (list->refcount == 0) + { + g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL); + g_list_free (list->dirs); + list->dirs = NULL; + list->length = 0; + g_free (list); + } +} + +void entry_directory_list_prepend(EntryDirectoryList* list, EntryDirectory* ed) +{ + list->length += 1; + list->dirs = g_list_prepend (list->dirs, + entry_directory_ref (ed)); +} + +int entry_directory_list_get_length(EntryDirectoryList* list) +{ + return list->length; +} + +void entry_directory_list_append_list(EntryDirectoryList* list, EntryDirectoryList* to_append) +{ + GList *tmp; + GList *new_dirs = NULL; + + if (to_append->length == 0) + return; + + tmp = to_append->dirs; + while (tmp != NULL) + { + list->length += 1; + new_dirs = g_list_prepend (new_dirs, + entry_directory_ref (tmp->data)); + + tmp = tmp->next; + } + + new_dirs = g_list_reverse (new_dirs); + list->dirs = g_list_concat (list->dirs, new_dirs); +} + +DesktopEntry* entry_directory_list_get_directory(EntryDirectoryList *list, const char* relative_path) +{ + DesktopEntry *retval = NULL; + GList *tmp; + + tmp = list->dirs; + while (tmp != NULL) + { + if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL) + break; + + tmp = tmp->next; + } + + return retval; +} + +gboolean _entry_directory_list_compare(const EntryDirectoryList* a, const EntryDirectoryList* b) +{ + GList *al, *bl; + + if (a == NULL && b == NULL) + return TRUE; + + if ((a == NULL || b == NULL)) + return FALSE; + + if (a->length != b->length) + return FALSE; + + al = a->dirs; bl = b->dirs; + while (al && bl && al->data == bl->data) + { + al = al->next; + bl = bl->next; + } + + return (al == NULL && bl == NULL); +} + +static gboolean get_all_func(EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data) +{ + if (ed->is_legacy && !desktop_entry_has_categories (entry)) + { + entry = desktop_entry_copy (entry); + desktop_entry_add_legacy_category (entry); + } + else + { + entry = desktop_entry_ref (entry); + } + + desktop_entry_set_add_entry (set, entry, file_id); + desktop_entry_unref (entry); + + return TRUE; +} + +static DesktopEntrySet* entry_directory_last_set = NULL; +static EntryDirectoryList* entry_directory_last_list = NULL; + +void _entry_directory_list_empty_desktop_cache(void) +{ + if (entry_directory_last_set != NULL) + desktop_entry_set_unref (entry_directory_last_set); + entry_directory_last_set = NULL; + + if (entry_directory_last_list != NULL) + entry_directory_list_unref (entry_directory_last_list); + entry_directory_last_list = NULL; +} + +DesktopEntrySet* _entry_directory_list_get_all_desktops(EntryDirectoryList* list) +{ + GList *tmp; + DesktopEntrySet *set; + + /* The only tricky thing here is that desktop files later + * in the search list with the same relative path + * are "hidden" by desktop files earlier in the path, + * so we have to do the earlier files first causing + * the later files to replace the earlier files + * in the DesktopEntrySet + * + * We go from the end of the list so we can just + * g_hash_table_replace and not have to do two + * hash lookups (check for existing entry, then insert new + * entry) + */ + + /* This method is -extremely- slow, so we have a simple + one-entry cache here */ + if (_entry_directory_list_compare (list, entry_directory_last_list)) + { + menu_verbose (" Hit desktop list (%p) cache\n", list); + return desktop_entry_set_ref (entry_directory_last_set); + } + + if (entry_directory_last_set != NULL) + desktop_entry_set_unref (entry_directory_last_set); + if (entry_directory_last_list != NULL) + entry_directory_list_unref (entry_directory_last_list); + + set = desktop_entry_set_new (); + menu_verbose (" Storing all of list %p in set %p\n", + list, set); + + tmp = g_list_last (list->dirs); + while (tmp != NULL) + { + entry_directory_foreach (tmp->data, get_all_func, set, NULL); + + tmp = tmp->prev; + } + + entry_directory_last_list = entry_directory_list_ref (list); + entry_directory_last_set = desktop_entry_set_ref (set); + + return set; +} + +void entry_directory_list_add_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + GList *tmp; + + tmp = list->dirs; + while (tmp != NULL) + { + entry_directory_add_monitor (tmp->data, callback, user_data); + tmp = tmp->next; + } +} + +void entry_directory_list_remove_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data) +{ + GList *tmp; + + tmp = list->dirs; + while (tmp != NULL) + { + entry_directory_remove_monitor (tmp->data, callback, user_data); + tmp = tmp->next; + } +} |