/* * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include "entry-directories.h" #include #include #include #include #include #include "menu-util.h" #include "menu-monitor.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; GFunc notify; gpointer notify_data; }; struct CachedDirMonitor { EntryDirectory* ed; EntryDirectoryChangedFunc callback; gpointer user_data; }; static void cached_dir_add_reference (CachedDir *dir); static void cached_dir_remove_reference (CachedDir *dir); static void cached_dir_free (CachedDir *dir); static gboolean cached_dir_load_entries_recursive (CachedDir *dir, const char *dirname); static void cached_dir_unref (CachedDir *dir); static void cached_dir_unref_noparent (CachedDir *dir); static CachedDir * cached_dir_add_subdir (CachedDir *dir, const char *basename, const char *path); static gboolean cached_dir_remove_subdir (CachedDir *dir, const char *basename); static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir); /* * Entry directory cache */ static CachedDir* dir_cache = NULL; static void clear_cache (CachedDir *dir, gpointer *cache) { *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 CachedDir * cached_dir_new_full (const char *name, GFunc notify, gpointer notify_data) { CachedDir *dir; dir = cached_dir_new (name); dir->notify = notify; dir->notify_data = notify_data; 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_unref_noparent, NULL); g_slist_free (dir->subdirs); dir->subdirs = NULL; g_free (dir->name); g_free (dir); } static CachedDir * cached_dir_ref (CachedDir *dir) { dir->references++; return dir; } static void cached_dir_unref (CachedDir *dir) { if (--dir->references == 0) { CachedDir *parent; parent = dir->parent; if (parent != NULL) cached_dir_remove_subdir (parent, dir->name); if (dir->notify) dir->notify (dir, dir->notify_data); cached_dir_free (dir); } } static void cached_dir_unref_noparent (CachedDir *dir) { if (--dir->references == 0) { if (dir->notify) dir->notify (dir, dir->notify_data); cached_dir_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_full ("/", (GFunc) clear_cache, &dir_cache); 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; subdir = cached_dir_add_subdir (dir, split[i], NULL); 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 CachedDir * 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 subdir; } subdir = cached_dir_new (basename); if (path != NULL && !cached_dir_load_entries_recursive (subdir, path)) { cached_dir_free (subdir); return NULL; } menu_verbose ("Caching dir \"%s\"\n", basename); subdir->parent = dir; dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir)); return subdir; } static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename) { CachedDir *subdir; subdir = find_subdir (dir, basename); if (subdir != NULL) { subdir->deleted = TRUE; dir->subdirs = g_slist_remove (dir->subdirs, subdir); cached_dir_unref (subdir); return TRUE; } return FALSE; } static guint monitors_idle_handler = 0; static GSList *pending_monitors_dirs = NULL; 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; } /* we explicitly don't invoke monitors of the parent since an * event has been queued for it too */ } static gboolean emit_monitors_in_idle (void) { GSList *monitors_to_emit; GSList *tmp; monitors_to_emit = pending_monitors_dirs; pending_monitors_dirs = NULL; monitors_idle_handler = 0; tmp = monitors_to_emit; while (tmp != NULL) { CachedDir *dir = tmp->data; cached_dir_invoke_monitors (dir); cached_dir_remove_reference (dir); tmp = tmp->next; } g_slist_free (monitors_to_emit); return FALSE; } static void cached_dir_queue_monitor_event (CachedDir *dir) { GSList *tmp; tmp = pending_monitors_dirs; while (tmp != NULL) { CachedDir *d = tmp->data; GSList *next = tmp->next; if (dir->parent == d->parent && g_strcmp0 (dir->name, d->name) == 0) break; tmp = next; } /* not found, so let's queue it */ if (tmp == NULL) { cached_dir_add_reference (dir); pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir); } if (dir->parent) { cached_dir_queue_monitor_event (dir->parent); } if (monitors_idle_handler == 0) { monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL); } } static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir) { gboolean handled = FALSE; char *basename; char *dirname; dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); cached_dir_add_reference (dir); 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 if (g_file_test (path, G_FILE_TEST_IS_DIR)) /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; 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) { 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")); /* 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_queue_monitor_event (dir); } cached_dir_remove_reference (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) { cached_dir_ref (dir); if (dir->parent != NULL) { cached_dir_add_reference (dir->parent); } } static void cached_dir_remove_reference(CachedDir* dir) { CachedDir *parent; parent = dir->parent; cached_dir_unref (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 ? "" : ""); canonical = realpath (path, NULL); 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; } }