1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 * vfolder-info.c - Loading of .vfolder-info files. External interface
4 * defined in vfolder-common.h
6 * Copyright (C) 2002 Ximian, Inc.
8 * The Gnome Library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * The Gnome Library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with the Gnome Library; see the file COPYING.LIB. If not,
20 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
23 * Author: Alex Graveley <alex@ximian.com>
24 * Based on original code by George Lebl <jirka@5z.com>.
34 #include <libgnomevfs/gnome-vfs.h>
35 #include <libgnomevfs/gnome-vfs-monitor-private.h>
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 #include <libxml/xmlmemory.h>
41 #include "vfolder-common.h"
42 #include "vfolder-util.h"
44 #define DOT_GNOME ".gnome2"
59 /* .vfolder-info format example:
61 * <!-- Merge dirs optional -->
62 * <MergeDir>/etc/X11/applnk</MergeDir>
63 * <!-- Only specify if it should override standard location -->
64 * <ItemDir>/usr/share/applications</ItemDir>
65 * <!-- This is where the .directories are -->
66 * <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
67 * <!-- Root folder -->
71 * <Include>important.desktop</Include>
73 * <!-- Other folders -->
75 * <Name>SomeFolder</Name>
76 * <ParentLink>http:///mywebdav.com/homedir</ParentLink>
79 * <Name>Test_Folder</Name>
80 * <Parent>file:///a_readonly_path</Parent>
81 * <!-- could also be absolute -->
82 * <Desktop>Test_Folder.directory</Desktop>
86 * <Keyword>Application</Keyword>
87 * <Keyword>Game</Keyword>
89 * <Keyword>Clock</Keyword>
92 * <Include>somefile.desktop</Include>
93 * <Include>someotherfile.desktop</Include>
94 * <Exclude>yetanother.desktop</Exclude>
102 * XML VFolder description writing
105 add_xml_tree_from_query (xmlNode *parent, Query *query)
107 xmlNode *real_parent;
110 real_parent = xmlNewChild (parent /* parent */,
115 real_parent = parent;
117 if (query->type == QUERY_KEYWORD) {
118 const char *string = g_quark_to_string (query->val.keyword);
120 xmlNewChild (real_parent /* parent */,
122 "Keyword" /* name */,
123 string /* content */);
124 } else if (query->type == QUERY_FILENAME) {
125 xmlNewChild (real_parent /* parent */,
127 "Filename" /* name */,
128 query->val.filename /* content */);
129 } else if (query->type == QUERY_PARENT) {
130 xmlNewChild (real_parent /* parent */,
132 "ParentQuery" /* name */,
134 } else if (query->type == QUERY_OR ||
135 query->type == QUERY_AND) {
140 if (query->type == QUERY_OR)
145 node = xmlNewChild (real_parent /* parent */,
150 for (li = query->val.queries; li != NULL; li = li->next) {
151 Query *subquery = li->data;
152 add_xml_tree_from_query (node, subquery);
155 g_assert_not_reached ();
160 add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
162 const char *filename = key;
163 xmlNode *folder_node = user_data;
165 xmlNewChild (folder_node /* parent */,
167 "Exclude" /* name */,
168 filename /* content */);
172 add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
175 xmlNode *folder_node;
176 const gchar *extend_uri;
179 * return if this folder hasn't been modified by the user,
180 * and contains no modified subfolders.
182 if (!folder->user_private && !folder->has_user_private_subfolders)
185 folder_node = xmlNewChild (parent /* parent */,
190 xmlNewChild (folder_node /* parent */,
193 folder_get_name (folder) /* content */);
195 extend_uri = folder_get_extend_uri (folder);
197 xmlNewChild (folder_node /* parent */,
199 folder->is_link ? "ParentLink" : "Parent",
200 extend_uri /* content */);
203 if (folder->user_private) {
204 const gchar *desktop_file;
206 if (folder->read_only)
207 xmlNewChild (folder_node /* parent */,
209 "ReadOnly" /* name */,
211 if (folder->dont_show_if_empty)
212 xmlNewChild (folder_node /* parent */,
214 "DontShowIfEmpty" /* name */,
216 if (folder->only_unallocated)
217 xmlNewChild (folder_node /* parent */,
219 "OnlyUnallocated" /* name */,
222 if (folder->desktop_file != NULL) {
223 desktop_file = folder_get_desktop_file (folder);
225 xmlNewChild (folder_node /* parent */,
227 "Desktop" /* name */,
231 for (li = folder->includes; li != NULL; li = li->next) {
232 const char *include = li->data;
233 xmlNewChild (folder_node /* parent */,
235 "Include" /* name */,
236 include /* content */);
239 if (folder->excludes) {
240 g_hash_table_foreach (folder->excludes,
247 query_node = xmlNewChild (folder_node /* parent */,
251 add_xml_tree_from_query (query_node,
252 folder_get_query (folder));
256 for (li = folder_list_subfolders (folder); li != NULL; li = li->next) {
257 Folder *subfolder = li->data;
258 add_xml_tree_from_folder (folder_node, subfolder);
263 xml_tree_from_vfolder (VFolderInfo *info)
269 doc = xmlNewDoc ("1.0");
271 topnode = xmlNewDocNode (doc /* doc */,
273 "VFolderInfo" /* name */,
275 doc->xmlRootNode = topnode;
277 if (info->write_dir != NULL) {
278 xmlNewChild (topnode /* parent */,
280 "WriteDir" /* name */,
281 info->write_dir /* content */);
285 if (info->desktop_dir != NULL) {
286 xmlNewChild (topnode /* parent */,
288 "DesktopDir" /* name */,
289 info->desktop_dir /* content */);
292 for (li = info->item_dirs; li != NULL; li = li->next) {
293 ItemDir *item_dir = li->data;
295 switch (item_dir->type) {
297 xmlNewChild (topnode /* parent */,
299 "MergeDir" /* name */,
300 item_dir->uri /* content */);
303 xmlNewChild (topnode /* parent */,
305 "ItemDir" /* name */,
306 item_dir->uri /* content */);
311 if (info->root != NULL)
312 add_xml_tree_from_folder (topnode, info->root);
317 /* FIXME: what to do about errors */
319 vfolder_info_write_user (VFolderInfo *info)
322 GnomeVFSResult result;
326 if (info->loading || !info->dirty)
332 info->loading = TRUE;
334 /* FIXME: errors, anyone? */
335 result = vfolder_make_directory_and_parents (info->filename,
338 if (result != GNOME_VFS_OK) {
339 g_warning ("Unable to create parent directory for "
340 "vfolder-info file: %s",
345 doc = xml_tree_from_vfolder (info);
349 gettimeofday (&tv, NULL);
350 tmpfile = g_strdup_printf ("%s.tmp-%d",
352 (int) (tv.tv_sec ^ tv.tv_usec));
354 /* Write to temporary file */
355 xmlSaveFormatFile (tmpfile, doc, TRUE /* format */);
357 /* Avoid being notified of move, since we're performing it */
358 if (info->filename_monitor)
359 vfolder_monitor_freeze (info->filename_monitor);
361 /* Move temp file over to real filename */
362 result = gnome_vfs_move (tmpfile,
364 TRUE /*force_replace*/);
365 if (result != GNOME_VFS_OK) {
366 g_warning ("Error writing vfolder configuration "
369 gnome_vfs_result_to_string (result));
372 /* Start listening to changes again */
373 if (info->filename_monitor)
374 vfolder_monitor_thaw (info->filename_monitor);
379 info->modification_time = time (NULL);
381 info->loading = FALSE;
386 * XML VFolder description reading
389 single_query_read (xmlNode *qnode)
394 if (qnode->type != XML_ELEMENT_NODE || qnode->name == NULL)
399 if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
400 qnode->xmlChildrenNode != NULL) {
403 for (iter = qnode->xmlChildrenNode;
404 iter != NULL && query == NULL;
406 query = single_query_read (iter);
408 query->not = ! query->not;
412 else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
413 xmlChar *word = xmlNodeGetContent (qnode);
416 query = query_new (QUERY_KEYWORD);
417 query->val.keyword = g_quark_from_string (word);
422 else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
423 xmlChar *file = xmlNodeGetContent (qnode);
426 query = query_new (QUERY_FILENAME);
427 query->val.filename = g_strdup (file);
432 else if (g_ascii_strcasecmp (qnode->name, "ParentQuery") == 0) {
433 query = query_new (QUERY_PARENT);
435 else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
436 query = query_new (QUERY_AND);
438 else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
439 query = query_new (QUERY_OR);
442 /* We don't understand */
446 /* This must be OR or AND */
447 g_assert (query != NULL);
449 for (node = qnode->xmlChildrenNode; node; node = node->next) {
450 Query *new_query = single_query_read (node);
452 if (new_query != NULL)
454 g_slist_prepend (query->val.queries, new_query);
457 query->val.queries = g_slist_reverse (query->val.queries);
463 add_or_set_query (Query **query, Query *new_query)
465 if (*query == NULL) {
468 Query *old_query = *query;
469 *query = query_new (QUERY_OR);
470 (*query)->val.queries =
471 g_slist_append ((*query)->val.queries, old_query);
472 (*query)->val.queries =
473 g_slist_append ((*query)->val.queries, new_query);
478 query_read (xmlNode *qnode)
485 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
486 if (node->type != XML_ELEMENT_NODE ||
490 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
491 node->xmlChildrenNode != NULL) {
493 Query *new_query = NULL;
495 for (iter = node->xmlChildrenNode;
496 iter != NULL && new_query == NULL;
498 new_query = single_query_read (iter);
499 if (new_query != NULL) {
500 new_query->not = ! new_query->not;
501 add_or_set_query (&query, new_query);
504 Query *new_query = single_query_read (node);
505 if (new_query != NULL)
506 add_or_set_query (&query, new_query);
514 folder_read (VFolderInfo *info, gboolean user_private, xmlNode *fnode)
519 folder = folder_new (info, NULL, user_private);
521 for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
522 if (node->type != XML_ELEMENT_NODE ||
526 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
527 xmlChar *name = xmlNodeGetContent (node);
530 g_free (folder->name);
531 folder_set_name (folder, name);
535 else if (g_ascii_strcasecmp (node->name, "Parent") == 0) {
536 xmlChar *parent = xmlNodeGetContent (node);
541 esc_parent = vfolder_escape_home (parent);
542 folder_set_extend_uri (folder, esc_parent);
543 folder->is_link = FALSE;
549 else if (g_ascii_strcasecmp (node->name, "ParentLink") == 0) {
550 xmlChar *parent = xmlNodeGetContent (node);
555 esc_parent = vfolder_escape_home (parent);
556 folder_set_extend_uri (folder, esc_parent);
557 folder->is_link = TRUE;
563 else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
564 xmlChar *desktop = xmlNodeGetContent (node);
567 folder_set_desktop_file (folder, desktop);
571 else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
572 xmlChar *file = xmlNodeGetContent (node);
577 esc_file = vfolder_escape_home (file);
578 folder_add_include (folder, esc_file);
584 else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
585 xmlChar *file = xmlNodeGetContent (node);
590 esc_file = vfolder_escape_home (file);
591 folder_add_exclude (folder, esc_file);
597 else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
600 query = query_read (node);
602 folder_set_query (folder, query);
604 else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
605 Folder *new_folder = folder_read (info,
609 if (new_folder != NULL) {
610 folder_add_subfolder (folder, new_folder);
611 folder_unref (new_folder);
614 else if (g_ascii_strcasecmp (node->name,
615 "OnlyUnallocated") == 0) {
616 folder->only_unallocated = TRUE;
617 info->has_unallocated_folder = TRUE;
619 else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
620 folder->read_only = TRUE;
622 else if (g_ascii_strcasecmp (node->name,
623 "DontShowIfEmpty") == 0) {
624 folder->dont_show_if_empty = TRUE;
628 /* Name is required */
629 if (!folder_get_name (folder)) {
630 folder_unref (folder);
637 static void itemdir_monitor_cb (GnomeVFSMonitorHandle *handle,
638 const gchar *monitor_uri,
639 const gchar *info_uri,
640 GnomeVFSMonitorEventType event_type,
643 static void writedir_monitor_cb (GnomeVFSMonitorHandle *handle,
644 const gchar *monitor_uri,
645 const gchar *info_uri,
646 GnomeVFSMonitorEventType event_type,
649 static void desktopdir_monitor_cb (GnomeVFSMonitorHandle *handle,
650 const gchar *monitor_uri,
651 const gchar *info_uri,
652 GnomeVFSMonitorEventType event_type,
657 remove_double_slashes (const char *uri)
668 result = malloc (strlen (uri) + 1);
669 if (result == NULL) {
677 while (*src != '\0') {
678 /* Don't do anything if current char is a / and slash is TRUE*/
679 if ((*src == '/') && (slash != FALSE)) {
684 if ((*src == '/') && (slash == FALSE)) {
701 itemdir_new (VFolderInfo *info,
709 ret = g_new0 (ItemDir, 1);
711 ret->weight = weight;
712 tmp_uri = vfolder_escape_home (uri);
713 ret->uri = remove_double_slashes (tmp_uri);
717 info->item_dirs = g_slist_append (info->item_dirs, ret);
723 itemdir_free (ItemDir *itemdir)
727 for (iter = itemdir->monitors; iter; iter = iter->next) {
728 VFolderMonitor *monitor = iter->data;
729 vfolder_monitor_cancel (monitor);
732 g_slist_free (itemdir->monitors);
733 g_free (itemdir->uri);
738 read_vfolder_from_file (VFolderInfo *info,
739 const gchar *filename,
740 gboolean user_private,
741 GnomeVFSResult *result,
742 GnomeVFSContext *context)
746 GnomeVFSResult my_result;
752 /* Fail silently if filename does not exist */
753 if (access (filename, F_OK) != 0)
756 doc = xmlParseFile (filename);
758 || doc->xmlRootNode == NULL
759 || doc->xmlRootNode->name == NULL
760 || g_ascii_strcasecmp (doc->xmlRootNode->name,
761 "VFolderInfo") != 0) {
762 *result = GNOME_VFS_ERROR_WRONG_FORMAT;
767 if (context != NULL &&
768 gnome_vfs_context_check_cancellation (context)) {
770 *result = GNOME_VFS_ERROR_CANCELLED;
774 for (node = doc->xmlRootNode->xmlChildrenNode;
777 if (node->type != XML_ELEMENT_NODE ||
781 if (context != NULL &&
782 gnome_vfs_context_check_cancellation (context)) {
784 *result = GNOME_VFS_ERROR_CANCELLED;
788 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
789 xmlChar *dir = xmlNodeGetContent (node);
792 itemdir_new (info, dir, MERGE_DIR, weight--);
796 else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
797 xmlChar *dir = xmlNodeGetContent (node);
800 itemdir_new (info, dir, ITEM_DIR, weight--);
804 else if (g_ascii_strcasecmp (node->name, "WriteDir") == 0) {
805 xmlChar *dir = xmlNodeGetContent (node);
808 g_free (info->write_dir);
809 info->write_dir = vfolder_escape_home (dir);
813 else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
814 xmlChar *dir = xmlNodeGetContent (node);
817 g_free (info->desktop_dir);
818 info->desktop_dir = vfolder_escape_home (dir);
822 else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
823 Folder *folder = folder_read (info,
827 if (folder != NULL) {
828 if (info->root != NULL)
829 folder_unref (info->root);
834 else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
835 info->read_only = TRUE;
846 * MergeDir/ItemDir entry pool reading
849 const gchar *dirname;
850 const gchar *keyword;
851 } mergedir_keywords[] = {
852 /*Parent Dir*/ /*Keyword to add*/
855 { "Development", "Development" },
856 { "Editors", "TextEditor" },
858 { "Graphics", "Graphics" },
859 { "Internet", "Network" },
860 { "Multimedia", "AudioVideo" },
861 { "Office", "Office" },
862 { "Settings", "Settings" },
863 { "System", "System" },
864 { "Utilities", "Utility" },
867 { "Addressbook", "Office" },
868 { "Audio", "AudioVideo" },
869 { "Calendar", "Office" },
870 { "Finance", "Office" },
873 { "WordProcessing", "Office" },
874 { "Toys", "Utility" },
878 get_mergedir_keyword (const gchar *dirname)
882 for (i = 0; i < G_N_ELEMENTS (mergedir_keywords); i++) {
883 if (g_ascii_strcasecmp (mergedir_keywords [i].dirname,
885 return g_quark_from_static_string (
886 mergedir_keywords [i].keyword);
894 create_itemdir_entry (ItemDir *id,
895 const gchar *rel_path,
896 GnomeVFSFileInfo *file_info)
898 Entry *new_entry = NULL;
901 if (!vfolder_check_extension (file_info->name, ".desktop"))
904 if (vfolder_info_lookup_entry (id->info, file_info->name)) {
905 D (g_print ("EXCLUDING DUPLICATE ENTRY: %s\n",
910 file_uri = vfolder_build_uri (id->uri, rel_path, NULL);
912 /* Ref belongs to the VFolderInfo */
913 new_entry = entry_new (id->info,
914 file_uri /*filename*/,
915 file_info->name /*displayname*/,
916 FALSE /*user_private*/,
917 id->weight /*weight*/);
925 add_keywords_from_relative_path (Entry *new_entry, const gchar *rel_path)
931 pelems = g_strsplit (rel_path, "/", -1);
935 for (i = 0; pelems [i]; i++) {
936 keyword = get_mergedir_keyword (pelems [i]);
938 entry_add_implicit_keyword (new_entry, keyword);
945 set_mergedir_entry_keywords (Entry *new_entry, const gchar *rel_path)
947 static GQuark merged = 0, application = 0, core_quark = 0;
950 merged = g_quark_from_static_string ("Merged");
951 application = g_quark_from_static_string("Application");
952 core_quark = g_quark_from_static_string ("Core");
956 * Mergedirs have the 'Merged' and 'Appliction' keywords added.
958 entry_add_implicit_keyword (new_entry, merged);
959 entry_add_implicit_keyword (new_entry, application);
961 if (!strcmp (rel_path, entry_get_displayname (new_entry)))
962 entry_add_implicit_keyword (new_entry, core_quark);
964 add_keywords_from_relative_path (new_entry, rel_path);
968 create_mergedir_entry (ItemDir *id,
969 const gchar *rel_path,
970 GnomeVFSFileInfo *file_info)
974 new_entry = create_itemdir_entry (id, rel_path, file_info);
976 set_mergedir_entry_keywords (new_entry, rel_path);
982 create_entry_or_add_dir_monitor (ItemDir *id,
983 const gchar *rel_path,
984 GnomeVFSFileInfo *file_info)
986 VFolderMonitor *dir_monitor;
990 if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
991 /* Add monitor for subdirectory of this MergeDir/ItemDir */
992 file_uri = vfolder_build_uri (id->uri, rel_path, NULL);
993 dir_monitor = vfolder_monitor_dir_new (file_uri,
997 id->monitors = g_slist_prepend (id->monitors,
1004 ret = create_mergedir_entry (id, rel_path, file_info);
1007 ret = create_itemdir_entry (id, rel_path, file_info);
1016 create_entry_directory_visit_cb (const gchar *rel_path,
1017 GnomeVFSFileInfo *file_info,
1018 gboolean recursing_will_loop,
1022 ItemDir *id = user_data;
1024 create_entry_or_add_dir_monitor (id, rel_path, file_info);
1026 *recurse = !recursing_will_loop;
1031 vfolder_info_read_info (VFolderInfo *info,
1032 GnomeVFSResult *result,
1033 GnomeVFSContext *context)
1035 gboolean ret = FALSE;
1038 if (!info->filename)
1041 /* Don't let set_dirty write out the file */
1042 info->loading = TRUE;
1044 ret = read_vfolder_from_file (info,
1050 if (info->write_dir)
1051 info->write_dir_monitor =
1052 vfolder_monitor_dir_new (info->write_dir,
1053 writedir_monitor_cb,
1056 if (info->desktop_dir)
1057 info->desktop_dir_monitor =
1058 vfolder_monitor_dir_new (info->desktop_dir,
1059 desktopdir_monitor_cb,
1062 /* Load ItemDir/MergeDirs in order of appearance. */
1063 for (iter = info->item_dirs; iter; iter = iter->next) {
1064 ItemDir *id = iter->data;
1065 VFolderMonitor *dir_monitor;
1067 /* Add a monitor for the root directory */
1069 vfolder_monitor_dir_new (id->uri,
1073 id->monitors = g_slist_prepend (id->monitors,
1076 gnome_vfs_directory_visit (
1078 GNOME_VFS_FILE_INFO_DEFAULT,
1079 GNOME_VFS_DIRECTORY_VISIT_DEFAULT,
1080 create_entry_directory_visit_cb,
1085 /* Allow set_dirty to write config file again */
1086 info->loading = FALSE;
1092 vfolder_info_reset (VFolderInfo *info)
1096 info->loading = TRUE;
1098 if (info->filename_monitor) {
1099 vfolder_monitor_cancel (info->filename_monitor);
1100 info->filename_monitor = NULL;
1103 if (info->write_dir_monitor) {
1104 vfolder_monitor_cancel (info->write_dir_monitor);
1105 info->write_dir_monitor = NULL;
1108 for (iter = info->item_dirs; iter; iter = iter->next) {
1109 ItemDir *dir = iter->data;
1112 g_slist_free (info->item_dirs);
1113 info->item_dirs = NULL;
1115 g_free (info->filename);
1116 g_free (info->write_dir);
1117 g_free (info->desktop_dir);
1119 info->filename = NULL;
1120 info->desktop_dir = NULL;
1121 info->write_dir = NULL;
1123 folder_unref (info->root);
1126 g_slist_foreach (info->entries, (GFunc) entry_unref, NULL);
1127 g_slist_free (info->entries);
1128 info->entries = NULL;
1130 if (info->entries_ht) {
1131 g_hash_table_destroy (info->entries_ht);
1132 info->entries_ht = NULL;
1139 info->has_unallocated_folder = FALSE;
1144 * VFolder ItemDir/MergeDir/WriteDir/DesktopDir directory monitoring
1147 integrate_entry (Folder *folder, Entry *entry, gboolean do_add)
1152 gboolean matches = FALSE;
1154 for (subs = folder_list_subfolders (folder); subs; subs = subs->next) {
1155 Folder *asub = subs->data;
1156 integrate_entry (asub, entry, do_add);
1159 if (folder->only_unallocated)
1162 query = folder_get_query (folder);
1164 matches = query_try_match (query, folder, entry);
1166 existing = folder_get_entry (folder, entry_get_displayname (entry));
1169 * Do nothing if the existing entry has a higher weight than the
1170 * one we wish to add.
1172 if (entry_get_weight (existing) > entry_get_weight (entry))
1175 folder_remove_entry (folder, existing);
1177 if (do_add && matches) {
1178 folder_add_entry (folder, entry);
1180 folder_emit_changed (folder,
1181 entry_get_displayname (entry),
1182 GNOME_VFS_MONITOR_EVENT_CHANGED);
1184 folder_emit_changed (folder,
1185 entry_get_displayname (entry),
1186 GNOME_VFS_MONITOR_EVENT_DELETED);
1188 else if (do_add && matches) {
1189 folder_add_entry (folder, entry);
1191 folder_emit_changed (folder,
1192 entry_get_displayname (entry),
1193 GNOME_VFS_MONITOR_EVENT_CREATED);
1198 integrate_itemdir_entry_createupdate (ItemDir *id,
1199 GnomeVFSURI *full_uri,
1200 const gchar *full_uristr,
1201 const gchar *displayname,
1202 GnomeVFSMonitorEventType event_type)
1205 GnomeVFSURI *real_uri;
1206 const gchar *rel_path;
1208 rel_path = strstr (full_uristr, id->uri);
1209 g_assert (rel_path != NULL);
1210 rel_path += strlen (id->uri);
1212 /* Look for an existing entry with the same displayname */
1213 entry = vfolder_info_lookup_entry (id->info, displayname);
1215 real_uri = entry_get_real_uri (entry);
1217 if (gnome_vfs_uri_equal (full_uri, real_uri)) {
1219 entry_set_dirty (entry);
1221 else if (entry_get_weight (entry) < id->weight) {
1223 * Existing entry is less important than the new
1226 entry_set_filename (entry, full_uristr);
1227 entry_set_weight (entry, id->weight);
1229 if (id->type == MERGE_DIR) {
1230 /* Add keywords from relative path */
1231 set_mergedir_entry_keywords (entry, rel_path);
1235 gnome_vfs_uri_unref (real_uri);
1237 else if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED) {
1238 GnomeVFSFileInfo *file_info;
1239 GnomeVFSResult result;
1241 file_info = gnome_vfs_file_info_new ();
1244 gnome_vfs_get_file_info_uri (
1247 GNOME_VFS_FILE_INFO_DEFAULT);
1249 if (result == GNOME_VFS_OK)
1250 entry = create_entry_or_add_dir_monitor (id,
1254 gnome_vfs_file_info_unref (file_info);
1259 integrate_entry (id->info->root,
1262 entry_unref (entry);
1264 id->info->modification_time = time (NULL);
1269 find_replacement_for_delete (ItemDir *id, Entry *entry)
1271 GSList *iter, *miter;
1274 idx = g_slist_index (id->info->item_dirs, id);
1278 iter = g_slist_nth (id->info->item_dirs, idx + 1);
1280 for (; iter; iter = iter->next) {
1281 ItemDir *id_next = iter->data;
1283 for (miter = id_next->monitors; miter; miter = miter->next) {
1284 VFolderMonitor *monitor = miter->data;
1285 GnomeVFSURI *check_uri;
1286 gchar *uristr, *rel_path;
1292 entry_get_displayname (entry),
1295 check_uri = gnome_vfs_uri_new (uristr);
1296 exists = gnome_vfs_uri_exists (check_uri);
1297 gnome_vfs_uri_unref (check_uri);
1304 entry_set_filename (entry, uristr);
1305 entry_set_weight (entry, id_next->weight);
1307 if (id_next->type == MERGE_DIR) {
1308 rel_path = strstr (uristr, id_next->uri);
1309 rel_path += strlen (id_next->uri);
1311 /* Add keywords based on relative path */
1312 set_mergedir_entry_keywords (entry, rel_path);
1324 integrate_itemdir_entry_delete (ItemDir *id,
1325 GnomeVFSURI *full_uri,
1326 const gchar *displayname)
1329 GnomeVFSURI *real_uri;
1330 gboolean replaced, equal;
1332 entry = vfolder_info_lookup_entry (id->info, displayname);
1336 real_uri = entry_get_real_uri (entry);
1337 equal = gnome_vfs_uri_equal (full_uri, real_uri);
1338 gnome_vfs_uri_unref (real_uri);
1340 /* Only care if its the currently visible entry being deleted */
1344 replaced = find_replacement_for_delete (id, entry);
1347 integrate_entry (id->info->root, entry, replaced /* do_add */);
1348 entry_unref (entry);
1350 id->info->modification_time = time (NULL);
1354 itemdir_monitor_cb (GnomeVFSMonitorHandle *handle,
1355 const gchar *monitor_uri,
1356 const gchar *info_uri,
1357 GnomeVFSMonitorEventType event_type,
1360 ItemDir *id = user_data;
1364 D (g_print ("*** Itemdir '%s' monitor %s%s%s called! ***\n",
1366 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
1367 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
1368 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
1370 /* Operating on the whole directory, ignore */
1371 if (!strcmp (monitor_uri, info_uri) ||
1372 !vfolder_check_extension (info_uri, ".desktop"))
1375 uri = gnome_vfs_uri_new (info_uri);
1376 filename = gnome_vfs_uri_extract_short_name (uri);
1378 switch (event_type) {
1379 case GNOME_VFS_MONITOR_EVENT_CREATED:
1380 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1381 VFOLDER_INFO_WRITE_LOCK (id->info);
1382 integrate_itemdir_entry_createupdate (id,
1387 VFOLDER_INFO_WRITE_UNLOCK (id->info);
1389 case GNOME_VFS_MONITOR_EVENT_DELETED:
1390 VFOLDER_INFO_WRITE_LOCK (id->info);
1391 integrate_itemdir_entry_delete (id, uri, filename);
1392 VFOLDER_INFO_WRITE_UNLOCK (id->info);
1398 gnome_vfs_uri_unref (uri);
1403 integrate_writedir_entry_changed (Folder *folder,
1405 GnomeVFSURI *changed_uri)
1408 GnomeVFSURI *real_uri;
1411 entry = folder_get_entry (folder, displayname);
1413 real_uri = entry_get_real_uri (entry);
1415 if (gnome_vfs_uri_equal (real_uri, changed_uri)) {
1416 entry_set_dirty (entry);
1417 folder_emit_changed (folder,
1419 GNOME_VFS_MONITOR_EVENT_CHANGED);
1422 gnome_vfs_uri_unref (real_uri);
1425 for (subs = folder_list_subfolders (folder); subs; subs = subs->next) {
1426 Folder *asub = subs->data;
1427 integrate_writedir_entry_changed (asub,
1434 writedir_monitor_cb (GnomeVFSMonitorHandle *handle,
1435 const gchar *monitor_uri,
1436 const gchar *info_uri,
1437 GnomeVFSMonitorEventType event_type,
1440 VFolderInfo *info = user_data;
1442 gchar *filename, *filename_ts;
1444 /* Operating on the whole directory, ignore */
1445 if (!strcmp (monitor_uri, info_uri) ||
1446 (!vfolder_check_extension (info_uri, ".desktop") &&
1447 !vfolder_check_extension (info_uri, ".directory")))
1450 switch (event_type) {
1451 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1452 uri = gnome_vfs_uri_new (info_uri);
1453 filename_ts = gnome_vfs_uri_extract_short_name (uri);
1454 filename = vfolder_untimestamp_file_name (filename_ts);
1456 VFOLDER_INFO_WRITE_LOCK (info);
1457 integrate_writedir_entry_changed (info->root, filename, uri);
1458 VFOLDER_INFO_WRITE_UNLOCK (info);
1460 gnome_vfs_uri_unref (uri);
1461 g_free (filename_ts);
1464 case GNOME_VFS_MONITOR_EVENT_DELETED:
1465 case GNOME_VFS_MONITOR_EVENT_CREATED:
1472 desktopdir_monitor_cb (GnomeVFSMonitorHandle *handle,
1473 const gchar *monitor_uri,
1474 const gchar *info_uri,
1475 GnomeVFSMonitorEventType event_type,
1478 VFolderInfo *info = user_data;
1481 /* Operating on the whole directory, ignore */
1482 if (!strcmp (monitor_uri, info_uri) ||
1483 !vfolder_check_extension (info_uri, ".directory"))
1486 switch (event_type) {
1487 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1488 uri = gnome_vfs_uri_new (info_uri);
1490 VFOLDER_INFO_WRITE_LOCK (info);
1491 integrate_writedir_entry_changed (info->root,
1494 VFOLDER_INFO_WRITE_UNLOCK (info);
1496 gnome_vfs_uri_unref (uri);
1498 case GNOME_VFS_MONITOR_EVENT_DELETED:
1499 case GNOME_VFS_MONITOR_EVENT_CREATED:
1507 * .vfolder-info monitoring
1510 check_monitors_foreach (gpointer key, gpointer val, gpointer user_data)
1512 MonitorHandle *handle = key;
1513 GSList *children = val;
1514 GnomeVFSURI *uri, *curi;
1518 path = gnome_vfs_uri_get_path (handle->uri);
1520 if (handle->type == GNOME_VFS_MONITOR_DIRECTORY) {
1522 GSList *new_children, *iter, *found;
1524 folder = vfolder_info_get_folder (handle->info, path);
1526 gnome_vfs_monitor_callback (
1527 (GnomeVFSMethodHandle *) handle,
1529 GNOME_VFS_MONITOR_EVENT_DELETED);
1534 * FIXME: If someone has an <OnlyUnallocated> folder which also
1535 * has a <Query>, we won't receive change events for
1536 * children matching the query... I think this is corner
1537 * enough to ignore * though.
1539 if (folder->only_unallocated)
1542 new_children = folder_list_children (folder);
1544 for (iter = children; iter; iter = iter->next) {
1545 gchar *child_name = iter->data;
1547 /* Look for a child with the same name */
1548 found = g_slist_find_custom (new_children,
1550 (GCompareFunc) strcmp);
1552 g_free (found->data);
1554 g_slist_delete_link (new_children,
1558 gnome_vfs_uri_append_file_name (
1562 gnome_vfs_monitor_callback (
1563 (GnomeVFSMethodHandle *) handle,
1565 GNOME_VFS_MONITOR_EVENT_DELETED);
1567 gnome_vfs_uri_unref (curi);
1570 g_free (child_name);
1573 /* Whatever is left is new, send created events */
1574 for (iter = new_children; iter; iter = iter->next) {
1575 gchar *child_name = iter->data;
1577 curi = gnome_vfs_uri_append_file_name (handle->uri,
1580 gnome_vfs_monitor_callback (
1581 (GnomeVFSMethodHandle *) handle,
1583 GNOME_VFS_MONITOR_EVENT_CREATED);
1585 gnome_vfs_uri_unref (curi);
1586 g_free (child_name);
1589 g_slist_free (new_children);
1590 g_slist_free (children);
1595 found = vfolder_info_get_entry (handle->info, path) ||
1596 vfolder_info_get_folder (handle->info, path);
1598 gnome_vfs_monitor_callback (
1599 (GnomeVFSMethodHandle *) handle,
1602 GNOME_VFS_MONITOR_EVENT_CHANGED :
1603 GNOME_VFS_MONITOR_EVENT_DELETED);
1607 static gboolean vfolder_info_init (VFolderInfo *info);
1610 filename_monitor_handle (gpointer user_data)
1612 VFolderInfo *info = user_data;
1613 GHashTable *monitors;
1616 D (g_print ("*** PROCESSING .vfolder-info!!! ***\n"));
1618 monitors = g_hash_table_new (g_direct_hash, g_direct_equal);
1620 VFOLDER_INFO_WRITE_LOCK (info);
1622 /* Don't emit any events while we load */
1623 info->loading = TRUE;
1625 /* Compose a hash of all existing monitors and their children */
1626 for (iter = info->requested_monitors; iter; iter = iter->next) {
1627 MonitorHandle *mhandle = iter->data;
1628 GSList *monitored_paths = NULL;
1631 if (mhandle->type == GNOME_VFS_MONITOR_DIRECTORY) {
1633 vfolder_info_get_folder (
1635 gnome_vfs_uri_get_path (mhandle->uri));
1637 monitored_paths = folder_list_children (folder);
1640 g_hash_table_insert (monitors, mhandle, monitored_paths);
1643 vfolder_info_reset (info);
1644 vfolder_info_init (info);
1646 /* Start sending events again */
1647 info->loading = FALSE;
1649 /* Traverse monitor hash and diff with newly read folder structure */
1650 g_hash_table_foreach (monitors, check_monitors_foreach, info);
1652 VFOLDER_INFO_WRITE_UNLOCK (info);
1654 g_hash_table_destroy (monitors);
1656 info->filename_reload_tag = 0;
1661 filename_monitor_cb (GnomeVFSMonitorHandle *handle,
1662 const gchar *monitor_uri,
1663 const gchar *info_uri,
1664 GnomeVFSMonitorEventType event_type,
1667 VFolderInfo *info = user_data;
1669 D (g_print ("*** Filename '%s' monitor %s%s%s called! ***\n",
1671 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
1672 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
1673 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
1675 if (info->filename_reload_tag) {
1676 g_source_remove (info->filename_reload_tag);
1677 info->filename_reload_tag = 0;
1681 * Don't process the .vfolder-info for 2 seconds after a delete event or
1682 * .5 seconds after a create event. This allows files to be rewritten
1683 * before we start reading it and possibly copying the system default
1684 * file over top of it.
1686 switch (event_type) {
1687 case GNOME_VFS_MONITOR_EVENT_DELETED:
1688 info->filename_reload_tag =
1689 g_timeout_add (2000, filename_monitor_handle, info);
1691 case GNOME_VFS_MONITOR_EVENT_CREATED:
1692 info->filename_reload_tag =
1693 g_timeout_add (500, filename_monitor_handle, info);
1695 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1697 filename_monitor_handle (info);
1704 * VFolderInfo Implementation
1706 static VFolderInfo *
1707 vfolder_info_new (const char *scheme)
1711 info = g_new0 (VFolderInfo, 1);
1712 info->scheme = g_strdup (scheme);
1714 g_static_rw_lock_init (&info->rw_lock);
1720 vfolder_info_find_filenames (VFolderInfo *info)
1722 gchar *scheme = info->scheme;
1723 GnomeVFSURI *file_uri;
1727 * FIXME: load from gconf
1731 * 1st: Try mandatory system-global file located at
1732 * /etc/gnome-vfs-2.0/vfolders/scheme.vfolder-info. Writability will
1733 * depend on permissions of this file.
1735 info->filename = g_strconcat (SYSCONFDIR,
1736 "/gnome-vfs-2.0/vfolders/",
1737 scheme, ".vfolder-info",
1739 file_uri = gnome_vfs_uri_new (info->filename);
1741 exists = gnome_vfs_uri_exists (file_uri);
1742 gnome_vfs_uri_unref (file_uri);
1746 * 2nd: Try user-private ~/.gnome2/vfolders/scheme.vfolder-info
1748 g_free (info->filename);
1749 info->filename = g_strconcat (g_get_home_dir (),
1750 "/" DOT_GNOME "/vfolders/",
1751 scheme, ".vfolder-info",
1756 * Special case for applications-all-users where we want to add any
1757 * paths specified in $GNOME2_PATH, for people installing in strange
1760 if (strcmp (scheme, "applications-all-users")) {
1767 path = g_getenv ("GNOME2_PATH");
1769 ppath = g_strsplit (path, ":", -1);
1771 for (i = 0; ppath[i] != NULL; i++) {
1772 dir = g_build_filename (ppath[i],
1773 "/share/applications/",
1775 id = itemdir_new (info,
1788 g_str_case_equal (gconstpointer v1,
1791 const gchar *string1 = v1;
1792 const gchar *string2 = v2;
1794 return g_ascii_strcasecmp (string1, string2) == 0;
1797 /* 31 bit hash function */
1799 g_str_case_hash (gconstpointer key)
1801 const char *p = key;
1802 guint h = g_ascii_toupper (*p);
1805 for (p += 1; *p != '\0'; p++)
1806 h = (h << 5) - h + g_ascii_toupper (*p);
1812 vfolder_info_init (VFolderInfo *info)
1814 gchar *all_user_scheme;
1816 info->loading = TRUE;
1817 info->entries_ht = g_hash_table_new (g_str_case_hash, g_str_case_equal);
1818 info->root = folder_new (info, "Root", TRUE);
1821 * Set the extend uri for the root folder to the -all-users version of
1822 * the scheme, in case the user doesn't have a private .vfolder-info
1825 all_user_scheme = g_strconcat (info->scheme, "-all-users:///", NULL);
1826 folder_set_extend_uri (info->root, all_user_scheme);
1827 g_free (all_user_scheme);
1830 * Set the default writedir, in case there is no .vfolder-info for this
1831 * scheme yet. Otherwise this will be overwritten when we read our
1834 info->write_dir = g_strconcat (g_get_home_dir (),
1835 "/" DOT_GNOME "/vfolders/",
1839 /* Figure out which .vfolder-info to read */
1840 vfolder_info_find_filenames (info);
1842 if (g_getenv ("GNOME_VFS_VFOLDER_INFODIR")) {
1843 gchar *filename = g_strconcat (info->scheme,
1847 g_free (info->filename);
1850 g_getenv ("GNOME_VFS_VFOLDER_INFODIR"),
1856 if (g_getenv ("GNOME_VFS_VFOLDER_WRITEDIR")) {
1857 g_free (info->write_dir);
1860 g_getenv ("GNOME_VFS_VFOLDER_WRITEDIR"),
1865 info->filename_monitor =
1866 vfolder_monitor_file_new (info->filename,
1867 filename_monitor_cb,
1870 info->modification_time = time (NULL);
1871 info->loading = FALSE;
1872 info->dirty = FALSE;
1874 /* Read from the user's .vfolder-info if it exists */
1875 return vfolder_info_read_info (info, NULL, NULL);
1879 vfolder_info_destroy (VFolderInfo *info)
1884 vfolder_info_reset (info);
1886 if (info->filename_reload_tag)
1887 g_source_remove (info->filename_reload_tag);
1889 g_static_rw_lock_free (&info->rw_lock);
1891 g_free (info->scheme);
1893 while (info->requested_monitors) {
1894 GnomeVFSMethodHandle *monitor = info->requested_monitors->data;
1895 vfolder_info_cancel_monitor (monitor);
1902 * Call to recursively list folder contents, causing them to allocate entries,
1903 * so that we get OnlyUnallocated folder counts correctly.
1906 load_folders (Folder *folder)
1910 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1911 Folder *folder = iter->data;
1912 load_folders (folder);
1916 static GHashTable *infos = NULL;
1917 G_LOCK_DEFINE_STATIC (vfolder_lock);
1920 vfolder_info_locate (const gchar *scheme)
1922 VFolderInfo *info = NULL;
1924 G_LOCK (vfolder_lock);
1928 g_hash_table_new_full (
1932 (GDestroyNotify) vfolder_info_destroy);
1935 info = g_hash_table_lookup (infos, scheme);
1937 G_UNLOCK (vfolder_lock);
1941 info = vfolder_info_new (scheme);
1942 g_hash_table_insert (infos, info->scheme, info);
1944 VFOLDER_INFO_WRITE_LOCK (info);
1945 G_UNLOCK (vfolder_lock);
1947 if (!vfolder_info_init (info)) {
1948 D (g_print ("DESTROYING INFO FOR SCHEME: %s\n",
1951 G_LOCK (vfolder_lock);
1952 g_hash_table_remove (infos, info);
1953 G_UNLOCK (vfolder_lock);
1958 if (info->has_unallocated_folder) {
1959 info->loading = TRUE;
1960 load_folders (info->root);
1961 info->loading = FALSE;
1964 VFOLDER_INFO_WRITE_UNLOCK (info);
1970 vfolder_info_set_dirty (VFolderInfo *info)
1979 get_folder_for_path_list_n (Folder *parent,
1985 gchar *subname, *subsubname;
1987 if (!parent || folder_is_hidden (parent))
1990 subname = paths [path_index];
1994 subsubname = paths [path_index + 1];
1995 if (!subsubname && skip_last)
1998 if (*subname == '\0')
2001 child = folder_get_subfolder (parent, subname);
2003 return get_folder_for_path_list_n (child,
2010 get_folder_for_path (Folder *root, const gchar *path, gboolean skip_last)
2015 paths = g_strsplit (path, "/", -1);
2019 folder = get_folder_for_path_list_n (root, paths, 0, skip_last);
2027 vfolder_info_get_parent (VFolderInfo *info, const gchar *path)
2029 return get_folder_for_path (info->root, path, TRUE);
2033 vfolder_info_get_folder (VFolderInfo *info, const gchar *path)
2035 return get_folder_for_path (info->root, path, FALSE);
2039 vfolder_info_get_entry (VFolderInfo *info, const gchar *path)
2044 parent = vfolder_info_get_parent (info, path);
2048 subname = strrchr (path, '/');
2054 return folder_get_entry (parent, subname);
2058 vfolder_info_list_all_entries (VFolderInfo *info)
2060 return info->entries;
2064 vfolder_info_lookup_entry (VFolderInfo *info, const gchar *name)
2066 return g_hash_table_lookup (info->entries_ht, name);
2070 vfolder_info_add_entry (VFolderInfo *info, Entry *entry)
2072 info->entries = g_slist_prepend (info->entries, entry);
2073 g_hash_table_insert (info->entries_ht,
2074 (gchar *) entry_get_displayname (entry),
2079 vfolder_info_remove_entry (VFolderInfo *info, Entry *entry)
2081 info->entries = g_slist_remove (info->entries, entry);
2082 g_hash_table_remove (info->entries_ht,
2083 entry_get_displayname (entry));
2086 #ifdef VFOLDER_DEBUG
2087 #define DEBUG_CHANGE_EMIT(_change_uri, _handle_uri) \
2088 g_print ("EMITTING CHANGE: %s for %s, %s%s%s\n", \
2091 event_type==GNOME_VFS_MONITOR_EVENT_CREATED?"CREATED":"", \
2092 event_type==GNOME_VFS_MONITOR_EVENT_DELETED?"DELETED":"", \
2093 event_type==GNOME_VFS_MONITOR_EVENT_CHANGED?"CHANGED":"")
2095 #define DEBUG_CHANGE_EMIT(_change_uri, _handle_uri)
2099 vfolder_info_emit_change (VFolderInfo *info,
2101 GnomeVFSMonitorEventType event_type)
2105 gchar *escpath, *uristr;
2110 escpath = gnome_vfs_escape_path_string (path);
2111 uristr = g_strconcat (info->scheme, "://", escpath, NULL);
2112 uri = gnome_vfs_uri_new (uristr);
2114 for (iter = info->requested_monitors; iter; iter = iter->next) {
2115 MonitorHandle *handle = iter->data;
2117 if (gnome_vfs_uri_equal (uri, handle->uri) ||
2118 (handle->type == GNOME_VFS_MONITOR_DIRECTORY &&
2119 gnome_vfs_uri_is_parent (handle->uri,
2122 DEBUG_CHANGE_EMIT (uristr, handle->uri->text);
2124 gnome_vfs_monitor_callback (
2125 (GnomeVFSMethodHandle *) handle,
2131 gnome_vfs_uri_unref (uri);
2137 vfolder_info_add_monitor (VFolderInfo *info,
2138 GnomeVFSMonitorType type,
2140 GnomeVFSMethodHandle **handle)
2142 MonitorHandle *monitor = g_new0 (MonitorHandle, 1);
2143 monitor->info = info;
2144 monitor->type = type;
2147 gnome_vfs_uri_ref (uri);
2149 info->requested_monitors = g_slist_prepend (info->requested_monitors,
2152 D (g_print ("EXTERNALLY WATCHING: %s\n",
2153 gnome_vfs_uri_to_string (uri, 0)));
2155 *handle = (GnomeVFSMethodHandle *) monitor;
2159 vfolder_info_cancel_monitor (GnomeVFSMethodHandle *handle)
2161 MonitorHandle *monitor = (MonitorHandle *) handle;
2163 monitor->info->requested_monitors =
2164 g_slist_remove (monitor->info->requested_monitors, monitor);
2166 gnome_vfs_uri_unref (monitor->uri);
2171 vfolder_info_destroy_all (void)
2173 G_LOCK (vfolder_lock);
2176 g_hash_table_destroy (infos);
2180 G_UNLOCK (vfolder_lock);
2184 vfolder_info_dump_entries (VFolderInfo *info, int offset)
2186 g_slist_foreach (info->entries,
2188 GINT_TO_POINTER (offset));