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 /* this would confuse KDE, just use hardcoded value. */
199 xmlNewChild (folder_node /* parent */,
201 folder->is_link ? "ParentLink" : "Parent",
202 extend_uri /* content */);
206 if (folder->user_private) {
207 const gchar *desktop_file;
209 if (folder->read_only)
210 xmlNewChild (folder_node /* parent */,
212 "ReadOnly" /* name */,
214 if (folder->dont_show_if_empty)
215 xmlNewChild (folder_node /* parent */,
217 "DontShowIfEmpty" /* name */,
219 if (folder->only_unallocated)
220 xmlNewChild (folder_node /* parent */,
222 "OnlyUnallocated" /* name */,
225 if (folder->desktop_file != NULL) {
226 desktop_file = folder_get_desktop_file (folder);
228 xmlNewChild (folder_node /* parent */,
230 "Desktop" /* name */,
234 for (li = folder->includes; li != NULL; li = li->next) {
235 const char *include = li->data;
236 xmlNewChild (folder_node /* parent */,
238 "Include" /* name */,
239 include /* content */);
242 if (folder->excludes) {
243 g_hash_table_foreach (folder->excludes,
250 query_node = xmlNewChild (folder_node /* parent */,
254 add_xml_tree_from_query (query_node,
255 folder_get_query (folder));
259 for (li = folder_list_subfolders (folder); li != NULL; li = li->next) {
260 Folder *subfolder = li->data;
261 add_xml_tree_from_folder (folder_node, subfolder);
266 xml_tree_from_vfolder (VFolderInfo *info)
272 doc = xmlNewDoc ("1.0");
274 topnode = xmlNewDocNode (doc /* doc */,
276 "VFolderInfo" /* name */,
278 doc->xmlRootNode = topnode;
281 /* Never write the WriteDir field, it will
282 * break KDE menus. Just use the hardcoded one.
284 if (info->write_dir != NULL) {
285 xmlNewChild (topnode /* parent */,
287 "WriteDir" /* name */,
288 info->write_dir /* content */);
293 if (info->desktop_dir != NULL) {
294 xmlNewChild (topnode /* parent */,
296 "DesktopDir" /* name */,
297 info->desktop_dir /* content */);
300 for (li = info->item_dirs; li != NULL; li = li->next) {
301 ItemDir *item_dir = li->data;
303 switch (item_dir->type) {
305 xmlNewChild (topnode /* parent */,
307 "MergeDir" /* name */,
308 item_dir->uri /* content */);
311 xmlNewChild (topnode /* parent */,
313 "ItemDir" /* name */,
314 item_dir->uri /* content */);
319 if (info->root != NULL)
320 add_xml_tree_from_folder (topnode, info->root);
325 /* FIXME: what to do about errors */
327 vfolder_info_write_user (VFolderInfo *info)
330 GnomeVFSResult result;
334 if (info->loading || !info->dirty)
340 info->loading = TRUE;
342 /* FIXME: errors, anyone? */
343 result = vfolder_make_directory_and_parents (info->filename,
346 if (result != GNOME_VFS_OK) {
347 g_warning ("Unable to create parent directory for "
348 "vfolder-info file: %s",
353 doc = xml_tree_from_vfolder (info);
357 gettimeofday (&tv, NULL);
358 tmpfile = g_strdup_printf ("%s.tmp-%d",
360 (int) (tv.tv_sec ^ tv.tv_usec));
362 /* Write to temporary file */
363 xmlSaveFormatFile (tmpfile, doc, TRUE /* format */);
365 /* Avoid being notified of move, since we're performing it */
366 if (info->filename_monitor)
367 vfolder_monitor_freeze (info->filename_monitor);
369 /* Move temp file over to real filename */
370 result = gnome_vfs_move (tmpfile,
372 TRUE /*force_replace*/);
373 if (result != GNOME_VFS_OK) {
374 g_warning ("Error writing vfolder configuration "
377 gnome_vfs_result_to_string (result));
380 /* Start listening to changes again */
381 if (info->filename_monitor)
382 vfolder_monitor_thaw (info->filename_monitor);
387 info->modification_time = time (NULL);
389 info->loading = FALSE;
394 * XML VFolder description reading
397 single_query_read (xmlNode *qnode)
402 if (qnode->type != XML_ELEMENT_NODE || qnode->name == NULL)
407 if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
408 qnode->xmlChildrenNode != NULL) {
411 for (iter = qnode->xmlChildrenNode;
412 iter != NULL && query == NULL;
414 query = single_query_read (iter);
416 query->not = ! query->not;
420 else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
421 xmlChar *word = xmlNodeGetContent (qnode);
424 query = query_new (QUERY_KEYWORD);
425 query->val.keyword = g_quark_from_string (word);
430 else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
431 xmlChar *file = xmlNodeGetContent (qnode);
434 query = query_new (QUERY_FILENAME);
435 query->val.filename = g_strdup (file);
440 else if (g_ascii_strcasecmp (qnode->name, "ParentQuery") == 0) {
441 query = query_new (QUERY_PARENT);
443 else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
444 query = query_new (QUERY_AND);
446 else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
447 query = query_new (QUERY_OR);
450 /* We don't understand */
454 /* This must be OR or AND */
455 g_assert (query != NULL);
457 for (node = qnode->xmlChildrenNode; node; node = node->next) {
458 Query *new_query = single_query_read (node);
460 if (new_query != NULL)
462 g_slist_prepend (query->val.queries, new_query);
465 query->val.queries = g_slist_reverse (query->val.queries);
471 add_or_set_query (Query **query, Query *new_query)
473 if (*query == NULL) {
476 Query *old_query = *query;
477 *query = query_new (QUERY_OR);
478 (*query)->val.queries =
479 g_slist_append ((*query)->val.queries, old_query);
480 (*query)->val.queries =
481 g_slist_append ((*query)->val.queries, new_query);
486 query_read (xmlNode *qnode)
493 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
494 if (node->type != XML_ELEMENT_NODE ||
498 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
499 node->xmlChildrenNode != NULL) {
501 Query *new_query = NULL;
503 for (iter = node->xmlChildrenNode;
504 iter != NULL && new_query == NULL;
506 new_query = single_query_read (iter);
507 if (new_query != NULL) {
508 new_query->not = ! new_query->not;
509 add_or_set_query (&query, new_query);
512 Query *new_query = single_query_read (node);
513 if (new_query != NULL)
514 add_or_set_query (&query, new_query);
522 folder_read (VFolderInfo *info, gboolean user_private, xmlNode *fnode)
527 folder = folder_new (info, NULL, user_private);
529 for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
530 if (node->type != XML_ELEMENT_NODE ||
534 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
535 xmlChar *name = xmlNodeGetContent (node);
538 g_free (folder->name);
539 folder_set_name (folder, name);
543 else if (g_ascii_strcasecmp (node->name, "Parent") == 0) {
544 xmlChar *parent = xmlNodeGetContent (node);
549 esc_parent = vfolder_escape_home (parent);
550 if (*esc_parent != '\0') {
551 folder_set_extend_uri (folder, esc_parent);
552 folder->is_link = FALSE;
559 else if (g_ascii_strcasecmp (node->name, "ParentLink") == 0) {
560 xmlChar *parent = xmlNodeGetContent (node);
565 esc_parent = vfolder_escape_home (parent);
566 if (*esc_parent != '\0') {
567 folder_set_extend_uri (folder, esc_parent);
568 folder->is_link = TRUE;
575 else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
576 xmlChar *desktop = xmlNodeGetContent (node);
579 folder_set_desktop_file (folder, desktop);
583 else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
584 xmlChar *file = xmlNodeGetContent (node);
589 esc_file = vfolder_escape_home (file);
590 folder_add_include (folder, esc_file);
596 else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
597 xmlChar *file = xmlNodeGetContent (node);
602 esc_file = vfolder_escape_home (file);
603 folder_add_exclude (folder, esc_file);
609 else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
612 query = query_read (node);
614 folder_set_query (folder, query);
616 else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
617 Folder *new_folder = folder_read (info,
621 if (new_folder != NULL) {
622 folder_add_subfolder (folder, new_folder);
623 folder_unref (new_folder);
626 else if (g_ascii_strcasecmp (node->name,
627 "OnlyUnallocated") == 0) {
628 folder->only_unallocated = TRUE;
629 info->has_unallocated_folder = TRUE;
631 else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
632 folder->read_only = TRUE;
634 else if (g_ascii_strcasecmp (node->name,
635 "DontShowIfEmpty") == 0) {
636 folder->dont_show_if_empty = TRUE;
640 /* Name is required */
641 if (!folder_get_name (folder)) {
642 folder_unref (folder);
649 static void itemdir_monitor_cb (GnomeVFSMonitorHandle *handle,
650 const gchar *monitor_uri,
651 const gchar *info_uri,
652 GnomeVFSMonitorEventType event_type,
655 static void writedir_monitor_cb (GnomeVFSMonitorHandle *handle,
656 const gchar *monitor_uri,
657 const gchar *info_uri,
658 GnomeVFSMonitorEventType event_type,
661 static void desktopdir_monitor_cb (GnomeVFSMonitorHandle *handle,
662 const gchar *monitor_uri,
663 const gchar *info_uri,
664 GnomeVFSMonitorEventType event_type,
669 remove_double_slashes (const char *uri)
680 result = malloc (strlen (uri) + 1);
681 if (result == NULL) {
689 while (*src != '\0') {
690 /* Don't do anything if current char is a / and slash is TRUE*/
691 if ((*src == '/') && (slash != FALSE)) {
696 if ((*src == '/') && (slash == FALSE)) {
713 itemdir_new (VFolderInfo *info,
721 ret = g_new0 (ItemDir, 1);
723 ret->weight = weight;
724 tmp_uri = vfolder_escape_home (uri);
725 ret->uri = remove_double_slashes (tmp_uri);
729 info->item_dirs = g_slist_append (info->item_dirs, ret);
735 itemdir_free (ItemDir *itemdir)
739 for (iter = itemdir->monitors; iter; iter = iter->next) {
740 VFolderMonitor *monitor = iter->data;
741 vfolder_monitor_cancel (monitor);
744 g_slist_free (itemdir->monitors);
745 g_free (itemdir->uri);
750 set_hardcoded_extend_uri_recursive (Folder *parent,
751 const char *parent_uri)
756 /* we set the extend URI unconditionally,
757 * ignoring anything in the file
759 folder_set_extend_uri (parent, parent_uri);
761 subfolders = (GSList*) folder_list_subfolders (parent);
763 while (tmp != NULL) {
764 Folder *sub = tmp->data;
767 child_uri = g_strconcat (parent_uri, folder_get_name (sub),
770 set_hardcoded_extend_uri_recursive (sub, child_uri);
779 set_hardcoded_extend_uri (VFolderInfo *info)
781 gchar *all_user_scheme;
784 * Set the extend uri for the root folder to the -all-users version of
785 * the scheme, in case the user doesn't have a private .vfolder-info
788 if (!g_str_has_suffix (info->scheme,
790 all_user_scheme = g_strconcat (info->scheme, "-all-users:///", NULL);
791 set_hardcoded_extend_uri_recursive (info->root, all_user_scheme);
792 g_free (all_user_scheme);
797 read_vfolder_from_file (VFolderInfo *info,
798 const gchar *filename,
799 gboolean user_private,
800 GnomeVFSResult *result,
801 GnomeVFSContext *context)
805 GnomeVFSResult my_result;
811 /* Fail silently if filename does not exist */
812 if (access (filename, F_OK) != 0)
815 doc = xmlParseFile (filename);
817 || doc->xmlRootNode == NULL
818 || doc->xmlRootNode->name == NULL
819 || g_ascii_strcasecmp (doc->xmlRootNode->name,
820 "VFolderInfo") != 0) {
821 *result = GNOME_VFS_ERROR_WRONG_FORMAT;
826 if (context != NULL &&
827 gnome_vfs_context_check_cancellation (context)) {
829 *result = GNOME_VFS_ERROR_CANCELLED;
833 for (node = doc->xmlRootNode->xmlChildrenNode;
836 if (node->type != XML_ELEMENT_NODE ||
840 if (context != NULL &&
841 gnome_vfs_context_check_cancellation (context)) {
843 *result = GNOME_VFS_ERROR_CANCELLED;
847 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
848 xmlChar *dir = xmlNodeGetContent (node);
851 itemdir_new (info, dir, MERGE_DIR, weight--);
855 else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
856 xmlChar *dir = xmlNodeGetContent (node);
859 itemdir_new (info, dir, ITEM_DIR, weight--);
863 else if (g_ascii_strcasecmp (node->name, "WriteDir") == 0) {
864 xmlChar *dir = xmlNodeGetContent (node);
867 g_free (info->write_dir);
868 info->write_dir = vfolder_escape_home (dir);
872 else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
873 xmlChar *dir = xmlNodeGetContent (node);
876 g_free (info->desktop_dir);
877 info->desktop_dir = vfolder_escape_home (dir);
881 else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
882 Folder *folder = folder_read (info,
886 if (folder != NULL) {
887 if (info->root != NULL)
888 folder_unref (info->root);
891 set_hardcoded_extend_uri (info);
894 else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
895 info->read_only = TRUE;
906 * MergeDir/ItemDir entry pool reading
909 const gchar *dirname;
910 const gchar *keyword;
911 } mergedir_keywords[] = {
912 /*Parent Dir*/ /*Keyword to add*/
915 { "Development", "Development" },
916 { "Editors", "TextEditor" },
918 { "Graphics", "Graphics" },
919 { "Internet", "Network" },
920 { "Multimedia", "AudioVideo" },
921 { "Office", "Office" },
922 { "Settings", "Settings" },
923 { "System", "System" },
924 { "Utilities", "Utility" },
927 { "Addressbook", "Office" },
928 { "Audio", "AudioVideo" },
929 { "Calendar", "Office" },
930 { "Finance", "Office" },
933 { "WordProcessing", "Office" },
934 { "Toys", "Utility" },
938 get_mergedir_keyword (const gchar *dirname)
942 for (i = 0; i < G_N_ELEMENTS (mergedir_keywords); i++) {
943 if (g_ascii_strcasecmp (mergedir_keywords [i].dirname,
945 return g_quark_from_static_string (
946 mergedir_keywords [i].keyword);
954 create_itemdir_entry (ItemDir *id,
955 const gchar *rel_path,
956 GnomeVFSFileInfo *file_info)
958 Entry *new_entry = NULL;
961 if (!vfolder_check_extension (file_info->name, ".desktop"))
964 if (vfolder_info_lookup_entry (id->info, file_info->name)) {
965 D (g_print ("EXCLUDING DUPLICATE ENTRY: %s\n",
970 file_uri = vfolder_build_uri (id->uri, rel_path, NULL);
972 /* Ref belongs to the VFolderInfo */
973 new_entry = entry_new (id->info,
974 file_uri /*filename*/,
975 file_info->name /*displayname*/,
976 FALSE /*user_private*/,
977 id->weight /*weight*/);
985 add_keywords_from_relative_path (Entry *new_entry, const gchar *rel_path)
991 pelems = g_strsplit (rel_path, "/", -1);
995 for (i = 0; pelems [i]; i++) {
996 keyword = get_mergedir_keyword (pelems [i]);
998 entry_add_implicit_keyword (new_entry, keyword);
1001 g_strfreev (pelems);
1005 set_mergedir_entry_keywords (Entry *new_entry, const gchar *rel_path)
1007 static GQuark merged = 0, application = 0, core_quark = 0;
1010 merged = g_quark_from_static_string ("Merged");
1011 application = g_quark_from_static_string("Application");
1012 core_quark = g_quark_from_static_string ("Core");
1016 * Mergedirs have the 'Merged' and 'Appliction' keywords added.
1018 entry_add_implicit_keyword (new_entry, merged);
1019 entry_add_implicit_keyword (new_entry, application);
1021 if (!strcmp (rel_path, entry_get_displayname (new_entry)))
1022 entry_add_implicit_keyword (new_entry, core_quark);
1024 add_keywords_from_relative_path (new_entry, rel_path);
1028 create_mergedir_entry (ItemDir *id,
1029 const gchar *rel_path,
1030 GnomeVFSFileInfo *file_info)
1034 new_entry = create_itemdir_entry (id, rel_path, file_info);
1036 set_mergedir_entry_keywords (new_entry, rel_path);
1042 create_entry_or_add_dir_monitor (ItemDir *id,
1043 const gchar *rel_path,
1044 GnomeVFSFileInfo *file_info)
1046 VFolderMonitor *dir_monitor;
1050 if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1051 /* Add monitor for subdirectory of this MergeDir/ItemDir */
1052 file_uri = vfolder_build_uri (id->uri, rel_path, NULL);
1053 dir_monitor = vfolder_monitor_dir_new (file_uri,
1057 id->monitors = g_slist_prepend (id->monitors,
1064 ret = create_mergedir_entry (id, rel_path, file_info);
1067 ret = create_itemdir_entry (id, rel_path, file_info);
1076 create_entry_directory_visit_cb (const gchar *rel_path,
1077 GnomeVFSFileInfo *file_info,
1078 gboolean recursing_will_loop,
1082 ItemDir *id = user_data;
1084 create_entry_or_add_dir_monitor (id, rel_path, file_info);
1086 *recurse = !recursing_will_loop;
1091 vfolder_info_read_info (VFolderInfo *info,
1092 GnomeVFSResult *result,
1093 GnomeVFSContext *context)
1095 gboolean ret = FALSE;
1098 if (!info->filename)
1101 /* Don't let set_dirty write out the file */
1102 info->loading = TRUE;
1104 ret = read_vfolder_from_file (info,
1110 if (info->write_dir)
1111 info->write_dir_monitor =
1112 vfolder_monitor_dir_new (info->write_dir,
1113 writedir_monitor_cb,
1116 if (info->desktop_dir)
1117 info->desktop_dir_monitor =
1118 vfolder_monitor_dir_new (info->desktop_dir,
1119 desktopdir_monitor_cb,
1122 /* Load ItemDir/MergeDirs in order of appearance. */
1123 for (iter = info->item_dirs; iter; iter = iter->next) {
1124 ItemDir *id = iter->data;
1125 VFolderMonitor *dir_monitor;
1127 /* Add a monitor for the root directory */
1129 vfolder_monitor_dir_new (id->uri,
1133 id->monitors = g_slist_prepend (id->monitors,
1136 gnome_vfs_directory_visit (
1138 GNOME_VFS_FILE_INFO_DEFAULT,
1139 GNOME_VFS_DIRECTORY_VISIT_DEFAULT,
1140 create_entry_directory_visit_cb,
1145 /* Allow set_dirty to write config file again */
1146 info->loading = FALSE;
1152 vfolder_info_reset (VFolderInfo *info)
1156 info->loading = TRUE;
1158 if (info->filename_monitor) {
1159 vfolder_monitor_cancel (info->filename_monitor);
1160 info->filename_monitor = NULL;
1163 if (info->write_dir_monitor) {
1164 vfolder_monitor_cancel (info->write_dir_monitor);
1165 info->write_dir_monitor = NULL;
1168 for (iter = info->item_dirs; iter; iter = iter->next) {
1169 ItemDir *dir = iter->data;
1172 g_slist_free (info->item_dirs);
1173 info->item_dirs = NULL;
1175 g_free (info->filename);
1176 g_free (info->write_dir);
1177 g_free (info->desktop_dir);
1179 info->filename = NULL;
1180 info->desktop_dir = NULL;
1181 info->write_dir = NULL;
1183 folder_unref (info->root);
1186 g_slist_foreach (info->entries, (GFunc) entry_unref, NULL);
1187 g_slist_free (info->entries);
1188 info->entries = NULL;
1190 if (info->entries_ht) {
1191 g_hash_table_destroy (info->entries_ht);
1192 info->entries_ht = NULL;
1199 info->has_unallocated_folder = FALSE;
1204 * VFolder ItemDir/MergeDir/WriteDir/DesktopDir directory monitoring
1207 integrate_entry (Folder *folder, Entry *entry, gboolean do_add)
1212 gboolean matches = FALSE;
1214 for (subs = folder_list_subfolders (folder); subs; subs = subs->next) {
1215 Folder *asub = subs->data;
1216 integrate_entry (asub, entry, do_add);
1219 if (folder->only_unallocated)
1222 query = folder_get_query (folder);
1224 matches = query_try_match (query, folder, entry);
1226 existing = folder_get_entry (folder, entry_get_displayname (entry));
1229 * Do nothing if the existing entry has a higher weight than the
1230 * one we wish to add.
1232 if (entry_get_weight (existing) > entry_get_weight (entry))
1235 folder_remove_entry (folder, existing);
1237 if (do_add && matches) {
1238 folder_add_entry (folder, entry);
1240 folder_emit_changed (folder,
1241 entry_get_displayname (entry),
1242 GNOME_VFS_MONITOR_EVENT_CHANGED);
1244 folder_emit_changed (folder,
1245 entry_get_displayname (entry),
1246 GNOME_VFS_MONITOR_EVENT_DELETED);
1248 else if (do_add && matches) {
1249 folder_add_entry (folder, entry);
1251 folder_emit_changed (folder,
1252 entry_get_displayname (entry),
1253 GNOME_VFS_MONITOR_EVENT_CREATED);
1258 integrate_itemdir_entry_createupdate (ItemDir *id,
1259 GnomeVFSURI *full_uri,
1260 const gchar *full_uristr,
1261 const gchar *displayname,
1262 GnomeVFSMonitorEventType event_type)
1265 GnomeVFSURI *real_uri;
1266 const gchar *rel_path;
1268 rel_path = strstr (full_uristr, id->uri);
1269 g_assert (rel_path != NULL);
1270 rel_path += strlen (id->uri);
1272 /* Look for an existing entry with the same displayname */
1273 entry = vfolder_info_lookup_entry (id->info, displayname);
1275 real_uri = entry_get_real_uri (entry);
1277 if (gnome_vfs_uri_equal (full_uri, real_uri)) {
1279 entry_set_dirty (entry);
1281 else if (entry_get_weight (entry) < id->weight) {
1283 * Existing entry is less important than the new
1286 entry_set_filename (entry, full_uristr);
1287 entry_set_weight (entry, id->weight);
1289 if (id->type == MERGE_DIR) {
1290 /* Add keywords from relative path */
1291 set_mergedir_entry_keywords (entry, rel_path);
1295 gnome_vfs_uri_unref (real_uri);
1297 else if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED) {
1298 GnomeVFSFileInfo *file_info;
1299 GnomeVFSResult result;
1301 file_info = gnome_vfs_file_info_new ();
1304 gnome_vfs_get_file_info_uri (
1307 GNOME_VFS_FILE_INFO_DEFAULT);
1309 if (result == GNOME_VFS_OK)
1310 entry = create_entry_or_add_dir_monitor (id,
1314 gnome_vfs_file_info_unref (file_info);
1319 integrate_entry (id->info->root,
1322 entry_unref (entry);
1324 id->info->modification_time = time (NULL);
1329 find_replacement_for_delete (ItemDir *id, Entry *entry)
1331 GSList *iter, *miter;
1334 idx = g_slist_index (id->info->item_dirs, id);
1338 iter = g_slist_nth (id->info->item_dirs, idx + 1);
1340 for (; iter; iter = iter->next) {
1341 ItemDir *id_next = iter->data;
1343 for (miter = id_next->monitors; miter; miter = miter->next) {
1344 VFolderMonitor *monitor = miter->data;
1345 GnomeVFSURI *check_uri;
1346 gchar *uristr, *rel_path;
1352 entry_get_displayname (entry),
1355 check_uri = gnome_vfs_uri_new (uristr);
1356 exists = gnome_vfs_uri_exists (check_uri);
1357 gnome_vfs_uri_unref (check_uri);
1364 entry_set_filename (entry, uristr);
1365 entry_set_weight (entry, id_next->weight);
1367 if (id_next->type == MERGE_DIR) {
1368 rel_path = strstr (uristr, id_next->uri);
1369 rel_path += strlen (id_next->uri);
1371 /* Add keywords based on relative path */
1372 set_mergedir_entry_keywords (entry, rel_path);
1384 integrate_itemdir_entry_delete (ItemDir *id,
1385 GnomeVFSURI *full_uri,
1386 const gchar *displayname)
1389 GnomeVFSURI *real_uri;
1390 gboolean replaced, equal;
1392 entry = vfolder_info_lookup_entry (id->info, displayname);
1396 real_uri = entry_get_real_uri (entry);
1397 equal = gnome_vfs_uri_equal (full_uri, real_uri);
1398 gnome_vfs_uri_unref (real_uri);
1400 /* Only care if its the currently visible entry being deleted */
1404 replaced = find_replacement_for_delete (id, entry);
1407 integrate_entry (id->info->root, entry, replaced /* do_add */);
1408 entry_unref (entry);
1410 id->info->modification_time = time (NULL);
1414 itemdir_monitor_cb (GnomeVFSMonitorHandle *handle,
1415 const gchar *monitor_uri,
1416 const gchar *info_uri,
1417 GnomeVFSMonitorEventType event_type,
1420 ItemDir *id = user_data;
1424 D (g_print ("*** Itemdir '%s' monitor %s%s%s called! ***\n",
1426 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
1427 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
1428 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
1430 /* Operating on the whole directory, ignore */
1431 if (!strcmp (monitor_uri, info_uri) ||
1432 !vfolder_check_extension (info_uri, ".desktop"))
1435 uri = gnome_vfs_uri_new (info_uri);
1436 filename = gnome_vfs_uri_extract_short_name (uri);
1438 switch (event_type) {
1439 case GNOME_VFS_MONITOR_EVENT_CREATED:
1440 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1441 VFOLDER_INFO_WRITE_LOCK (id->info);
1442 integrate_itemdir_entry_createupdate (id,
1447 VFOLDER_INFO_WRITE_UNLOCK (id->info);
1449 case GNOME_VFS_MONITOR_EVENT_DELETED:
1450 VFOLDER_INFO_WRITE_LOCK (id->info);
1451 integrate_itemdir_entry_delete (id, uri, filename);
1452 VFOLDER_INFO_WRITE_UNLOCK (id->info);
1458 gnome_vfs_uri_unref (uri);
1463 integrate_writedir_entry_changed (Folder *folder,
1465 GnomeVFSURI *changed_uri)
1468 GnomeVFSURI *real_uri;
1471 entry = folder_get_entry (folder, displayname);
1473 real_uri = entry_get_real_uri (entry);
1475 if (gnome_vfs_uri_equal (real_uri, changed_uri)) {
1476 entry_set_dirty (entry);
1477 folder_emit_changed (folder,
1479 GNOME_VFS_MONITOR_EVENT_CHANGED);
1482 gnome_vfs_uri_unref (real_uri);
1485 for (subs = folder_list_subfolders (folder); subs; subs = subs->next) {
1486 Folder *asub = subs->data;
1487 integrate_writedir_entry_changed (asub,
1494 writedir_monitor_cb (GnomeVFSMonitorHandle *handle,
1495 const gchar *monitor_uri,
1496 const gchar *info_uri,
1497 GnomeVFSMonitorEventType event_type,
1500 VFolderInfo *info = user_data;
1502 gchar *filename, *filename_ts;
1504 /* Operating on the whole directory, ignore */
1505 if (!strcmp (monitor_uri, info_uri) ||
1506 (!vfolder_check_extension (info_uri, ".desktop") &&
1507 !vfolder_check_extension (info_uri, ".directory")))
1510 switch (event_type) {
1511 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1512 uri = gnome_vfs_uri_new (info_uri);
1513 filename_ts = gnome_vfs_uri_extract_short_name (uri);
1514 filename = vfolder_untimestamp_file_name (filename_ts);
1516 VFOLDER_INFO_WRITE_LOCK (info);
1517 integrate_writedir_entry_changed (info->root, filename, uri);
1518 VFOLDER_INFO_WRITE_UNLOCK (info);
1520 gnome_vfs_uri_unref (uri);
1521 g_free (filename_ts);
1524 case GNOME_VFS_MONITOR_EVENT_DELETED:
1525 case GNOME_VFS_MONITOR_EVENT_CREATED:
1532 desktopdir_monitor_cb (GnomeVFSMonitorHandle *handle,
1533 const gchar *monitor_uri,
1534 const gchar *info_uri,
1535 GnomeVFSMonitorEventType event_type,
1538 VFolderInfo *info = user_data;
1541 /* Operating on the whole directory, ignore */
1542 if (!strcmp (monitor_uri, info_uri) ||
1543 !vfolder_check_extension (info_uri, ".directory"))
1546 switch (event_type) {
1547 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1548 uri = gnome_vfs_uri_new (info_uri);
1550 VFOLDER_INFO_WRITE_LOCK (info);
1551 integrate_writedir_entry_changed (info->root,
1554 VFOLDER_INFO_WRITE_UNLOCK (info);
1556 gnome_vfs_uri_unref (uri);
1558 case GNOME_VFS_MONITOR_EVENT_DELETED:
1559 case GNOME_VFS_MONITOR_EVENT_CREATED:
1567 * .vfolder-info monitoring
1570 check_monitors_foreach (gpointer key, gpointer val, gpointer user_data)
1572 MonitorHandle *handle = key;
1573 GSList *children = val;
1574 GnomeVFSURI *uri, *curi;
1578 path = gnome_vfs_uri_get_path (handle->uri);
1580 if (handle->type == GNOME_VFS_MONITOR_DIRECTORY) {
1582 GSList *new_children, *iter, *found;
1584 folder = vfolder_info_get_folder (handle->info, path);
1586 gnome_vfs_monitor_callback (
1587 (GnomeVFSMethodHandle *) handle,
1589 GNOME_VFS_MONITOR_EVENT_DELETED);
1594 * FIXME: If someone has an <OnlyUnallocated> folder which also
1595 * has a <Query>, we won't receive change events for
1596 * children matching the query... I think this is corner
1597 * enough to ignore * though.
1599 if (folder->only_unallocated)
1602 new_children = folder_list_children (folder);
1604 for (iter = children; iter; iter = iter->next) {
1605 gchar *child_name = iter->data;
1607 /* Look for a child with the same name */
1608 found = g_slist_find_custom (new_children,
1610 (GCompareFunc) strcmp);
1612 g_free (found->data);
1614 g_slist_delete_link (new_children,
1618 gnome_vfs_uri_append_file_name (
1622 gnome_vfs_monitor_callback (
1623 (GnomeVFSMethodHandle *) handle,
1625 GNOME_VFS_MONITOR_EVENT_DELETED);
1627 gnome_vfs_uri_unref (curi);
1630 g_free (child_name);
1633 /* Whatever is left is new, send created events */
1634 for (iter = new_children; iter; iter = iter->next) {
1635 gchar *child_name = iter->data;
1637 curi = gnome_vfs_uri_append_file_name (handle->uri,
1640 gnome_vfs_monitor_callback (
1641 (GnomeVFSMethodHandle *) handle,
1643 GNOME_VFS_MONITOR_EVENT_CREATED);
1645 gnome_vfs_uri_unref (curi);
1646 g_free (child_name);
1649 g_slist_free (new_children);
1650 g_slist_free (children);
1655 found = vfolder_info_get_entry (handle->info, path) ||
1656 vfolder_info_get_folder (handle->info, path);
1658 gnome_vfs_monitor_callback (
1659 (GnomeVFSMethodHandle *) handle,
1662 GNOME_VFS_MONITOR_EVENT_CHANGED :
1663 GNOME_VFS_MONITOR_EVENT_DELETED);
1667 static gboolean vfolder_info_init (VFolderInfo *info);
1670 filename_monitor_handle (gpointer user_data)
1672 VFolderInfo *info = user_data;
1673 GHashTable *monitors;
1676 D (g_print ("*** PROCESSING .vfolder-info!!! ***\n"));
1678 monitors = g_hash_table_new (g_direct_hash, g_direct_equal);
1680 VFOLDER_INFO_WRITE_LOCK (info);
1682 /* Don't emit any events while we load */
1683 info->loading = TRUE;
1685 /* Compose a hash of all existing monitors and their children */
1686 for (iter = info->requested_monitors; iter; iter = iter->next) {
1687 MonitorHandle *mhandle = iter->data;
1688 GSList *monitored_paths = NULL;
1691 if (mhandle->type == GNOME_VFS_MONITOR_DIRECTORY) {
1693 vfolder_info_get_folder (
1695 gnome_vfs_uri_get_path (mhandle->uri));
1697 monitored_paths = folder_list_children (folder);
1700 g_hash_table_insert (monitors, mhandle, monitored_paths);
1703 vfolder_info_reset (info);
1704 vfolder_info_init (info);
1706 /* Start sending events again */
1707 info->loading = FALSE;
1709 /* Traverse monitor hash and diff with newly read folder structure */
1710 g_hash_table_foreach (monitors, check_monitors_foreach, info);
1712 VFOLDER_INFO_WRITE_UNLOCK (info);
1714 g_hash_table_destroy (monitors);
1716 info->filename_reload_tag = 0;
1721 filename_monitor_cb (GnomeVFSMonitorHandle *handle,
1722 const gchar *monitor_uri,
1723 const gchar *info_uri,
1724 GnomeVFSMonitorEventType event_type,
1727 VFolderInfo *info = user_data;
1729 D (g_print ("*** Filename '%s' monitor %s%s%s called! ***\n",
1731 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
1732 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
1733 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
1735 if (info->filename_reload_tag) {
1736 g_source_remove (info->filename_reload_tag);
1737 info->filename_reload_tag = 0;
1741 * Don't process the .vfolder-info for 2 seconds after a delete event or
1742 * .5 seconds after a create event. This allows files to be rewritten
1743 * before we start reading it and possibly copying the system default
1744 * file over top of it.
1746 switch (event_type) {
1747 case GNOME_VFS_MONITOR_EVENT_DELETED:
1748 info->filename_reload_tag =
1749 g_timeout_add (2000, filename_monitor_handle, info);
1751 case GNOME_VFS_MONITOR_EVENT_CREATED:
1752 info->filename_reload_tag =
1753 g_timeout_add (500, filename_monitor_handle, info);
1755 case GNOME_VFS_MONITOR_EVENT_CHANGED:
1757 filename_monitor_handle (info);
1764 * VFolderInfo Implementation
1766 static VFolderInfo *
1767 vfolder_info_new (const char *scheme)
1771 info = g_new0 (VFolderInfo, 1);
1772 info->scheme = g_strdup (scheme);
1774 g_static_rw_lock_init (&info->rw_lock);
1780 vfolder_info_find_filenames (VFolderInfo *info)
1782 gchar *scheme = info->scheme;
1783 GnomeVFSURI *file_uri;
1786 /* Here we're finding the primary XML file
1787 * that this URI will write changes to.
1788 * It's the .menu file for -all-users,
1789 * and the ~/.gnome2/vfolders/scheme.vfolder-info
1793 if (g_str_has_suffix (info->scheme,
1795 /* The all-users scheme uses the
1799 char *single_scheme;
1801 suffix = g_strrstr (info->scheme, "-all-users");
1802 g_assert (suffix != NULL);
1803 single_scheme = g_strndup (info->scheme,
1804 suffix - info->scheme);
1806 info->filename = g_strconcat (SYSCONFDIR,
1807 "/X11/desktop-menus/",
1812 g_free (single_scheme);
1814 file_uri = gnome_vfs_uri_new (info->filename);
1816 exists = gnome_vfs_uri_exists (file_uri);
1817 gnome_vfs_uri_unref (file_uri);
1820 g_printerr ("all-users filename %s exists = %d\n",
1821 info->filename, exists);
1829 * 2nd: Try user-private
1830 * ~/.gnome2/vfolders/scheme.vfolder-info
1832 g_free (info->filename);
1833 info->filename = g_strconcat (g_get_home_dir (),
1834 "/" DOT_GNOME "/vfolders/",
1835 scheme, ".vfolder-info",
1839 g_printerr ("using filename %s\n",
1846 g_str_case_equal (gconstpointer v1,
1849 const gchar *string1 = v1;
1850 const gchar *string2 = v2;
1852 return g_ascii_strcasecmp (string1, string2) == 0;
1855 /* 31 bit hash function */
1857 g_str_case_hash (gconstpointer key)
1859 const char *p = key;
1860 guint h = g_ascii_toupper (*p);
1863 for (p += 1; *p != '\0'; p++)
1864 h = (h << 5) - h + g_ascii_toupper (*p);
1870 vfolder_info_init (VFolderInfo *info)
1872 info->loading = TRUE;
1873 info->entries_ht = g_hash_table_new (g_str_case_hash, g_str_case_equal);
1874 info->root = folder_new (info, "Root", TRUE);
1876 set_hardcoded_extend_uri (info);
1879 * Set the default writedir, in case there is no .vfolder-info
1880 * for this scheme yet. Otherwise this will be overwritten
1881 * when we read our source.
1883 info->write_dir = g_strconcat (g_get_home_dir (),
1884 "/" DOT_GNOME "/vfolders/",
1888 /* Figure out which .vfolder-info to read */
1889 vfolder_info_find_filenames (info);
1891 info->filename_monitor =
1892 vfolder_monitor_file_new (info->filename,
1893 filename_monitor_cb,
1896 info->modification_time = time (NULL);
1897 info->loading = FALSE;
1898 info->dirty = FALSE;
1900 /* Read from the user's .vfolder-info if it exists */
1901 return vfolder_info_read_info (info, NULL, NULL);
1905 vfolder_info_destroy (VFolderInfo *info)
1910 vfolder_info_reset (info);
1912 if (info->filename_reload_tag)
1913 g_source_remove (info->filename_reload_tag);
1915 g_static_rw_lock_free (&info->rw_lock);
1917 g_free (info->scheme);
1919 while (info->requested_monitors) {
1920 GnomeVFSMethodHandle *monitor = info->requested_monitors->data;
1921 vfolder_info_cancel_monitor (monitor);
1928 * Call to recursively list folder contents, causing them to allocate entries,
1929 * so that we get OnlyUnallocated folder counts correctly.
1932 load_folders (Folder *folder)
1936 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1937 Folder *folder = iter->data;
1938 load_folders (folder);
1942 static GHashTable *infos = NULL;
1943 G_LOCK_DEFINE_STATIC (vfolder_lock);
1946 vfolder_info_locate (const gchar *scheme)
1948 VFolderInfo *info = NULL;
1950 G_LOCK (vfolder_lock);
1954 g_hash_table_new_full (
1958 (GDestroyNotify) vfolder_info_destroy);
1961 info = g_hash_table_lookup (infos, scheme);
1963 G_UNLOCK (vfolder_lock);
1967 info = vfolder_info_new (scheme);
1968 g_hash_table_insert (infos, info->scheme, info);
1970 VFOLDER_INFO_WRITE_LOCK (info);
1971 G_UNLOCK (vfolder_lock);
1973 if (!vfolder_info_init (info)) {
1974 D (g_print ("DESTROYING INFO FOR SCHEME: %s\n",
1977 G_LOCK (vfolder_lock);
1978 g_hash_table_remove (infos, info);
1979 G_UNLOCK (vfolder_lock);
1984 if (info->has_unallocated_folder) {
1985 info->loading = TRUE;
1986 load_folders (info->root);
1987 info->loading = FALSE;
1990 VFOLDER_INFO_WRITE_UNLOCK (info);
1996 vfolder_info_set_dirty (VFolderInfo *info)
2005 get_folder_for_path_list_n (Folder *parent,
2011 gchar *subname, *subsubname;
2013 if (!parent || folder_is_hidden (parent))
2016 subname = paths [path_index];
2020 subsubname = paths [path_index + 1];
2021 if (!subsubname && skip_last)
2024 if (*subname == '\0')
2027 child = folder_get_subfolder (parent, subname);
2029 return get_folder_for_path_list_n (child,
2036 get_folder_for_path (Folder *root, const gchar *path, gboolean skip_last)
2041 paths = g_strsplit (path, "/", -1);
2045 folder = get_folder_for_path_list_n (root, paths, 0, skip_last);
2053 vfolder_info_get_parent (VFolderInfo *info, const gchar *path)
2055 return get_folder_for_path (info->root, path, TRUE);
2059 vfolder_info_get_folder (VFolderInfo *info, const gchar *path)
2061 return get_folder_for_path (info->root, path, FALSE);
2065 vfolder_info_get_entry (VFolderInfo *info, const gchar *path)
2070 parent = vfolder_info_get_parent (info, path);
2074 subname = strrchr (path, '/');
2080 return folder_get_entry (parent, subname);
2084 vfolder_info_list_all_entries (VFolderInfo *info)
2086 return info->entries;
2090 vfolder_info_lookup_entry (VFolderInfo *info, const gchar *name)
2092 return g_hash_table_lookup (info->entries_ht, name);
2096 vfolder_info_add_entry (VFolderInfo *info, Entry *entry)
2098 info->entries = g_slist_prepend (info->entries, entry);
2099 g_hash_table_insert (info->entries_ht,
2100 (gchar *) entry_get_displayname (entry),
2105 vfolder_info_remove_entry (VFolderInfo *info, Entry *entry)
2107 info->entries = g_slist_remove (info->entries, entry);
2108 g_hash_table_remove (info->entries_ht,
2109 entry_get_displayname (entry));
2112 #ifdef VFOLDER_DEBUG
2113 #define DEBUG_CHANGE_EMIT(_change_uri, _handle_uri) \
2114 g_print ("EMITTING CHANGE: %s for %s, %s%s%s\n", \
2117 event_type==GNOME_VFS_MONITOR_EVENT_CREATED?"CREATED":"", \
2118 event_type==GNOME_VFS_MONITOR_EVENT_DELETED?"DELETED":"", \
2119 event_type==GNOME_VFS_MONITOR_EVENT_CHANGED?"CHANGED":"")
2121 #define DEBUG_CHANGE_EMIT(_change_uri, _handle_uri)
2125 vfolder_info_emit_change (VFolderInfo *info,
2127 GnomeVFSMonitorEventType event_type)
2131 gchar *escpath, *uristr;
2136 escpath = gnome_vfs_escape_path_string (path);
2137 uristr = g_strconcat (info->scheme, "://", escpath, NULL);
2138 uri = gnome_vfs_uri_new (uristr);
2140 for (iter = info->requested_monitors; iter; iter = iter->next) {
2141 MonitorHandle *handle = iter->data;
2143 if (gnome_vfs_uri_equal (uri, handle->uri) ||
2144 (handle->type == GNOME_VFS_MONITOR_DIRECTORY &&
2145 gnome_vfs_uri_is_parent (handle->uri,
2148 DEBUG_CHANGE_EMIT (uristr, handle->uri->text);
2150 gnome_vfs_monitor_callback (
2151 (GnomeVFSMethodHandle *) handle,
2157 gnome_vfs_uri_unref (uri);
2163 vfolder_info_add_monitor (VFolderInfo *info,
2164 GnomeVFSMonitorType type,
2166 GnomeVFSMethodHandle **handle)
2168 MonitorHandle *monitor = g_new0 (MonitorHandle, 1);
2169 monitor->info = info;
2170 monitor->type = type;
2173 gnome_vfs_uri_ref (uri);
2175 info->requested_monitors = g_slist_prepend (info->requested_monitors,
2178 D (g_print ("EXTERNALLY WATCHING: %s\n",
2179 gnome_vfs_uri_to_string (uri, 0)));
2181 *handle = (GnomeVFSMethodHandle *) monitor;
2185 vfolder_info_cancel_monitor (GnomeVFSMethodHandle *handle)
2187 MonitorHandle *monitor = (MonitorHandle *) handle;
2189 monitor->info->requested_monitors =
2190 g_slist_remove (monitor->info->requested_monitors, monitor);
2192 gnome_vfs_uri_unref (monitor->uri);
2197 vfolder_info_destroy_all (void)
2199 G_LOCK (vfolder_lock);
2202 g_hash_table_destroy (infos);
2206 G_UNLOCK (vfolder_lock);
2210 vfolder_info_dump_entries (VFolderInfo *info, int offset)
2212 g_slist_foreach (info->entries,
2214 GINT_TO_POINTER (offset));