1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 * vfolder-common.c - Implementation of abstract Folder, Entry, and Query
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>.
35 #include <libgnomevfs/gnome-vfs-directory.h>
36 #include <libgnomevfs/gnome-vfs-ops.h>
37 #include <libgnomevfs/gnome-vfs-xfer.h>
39 #include "vfolder-common.h"
43 * Entry Implementation
46 entry_new (VFolderInfo *info,
47 const gchar *filename,
48 const gchar *displayname,
49 gboolean user_private,
54 entry = g_new0 (Entry, 1);
58 entry->filename = g_strdup (filename);
59 entry->displayname = g_strdup (displayname);
60 entry->user_private = user_private;
61 entry->weight = weight;
66 * Lame-O special case .directory handling, as we don't want them
67 * showing up for all-applications:///.
69 if (strcmp (displayname, ".directory") != 0)
70 vfolder_info_add_entry (info, entry);
76 entry_ref (Entry *entry)
82 entry_unref (Entry *entry)
86 if (entry->refcnt == 0) {
87 D (g_print ("-- KILLING ENTRY: (%p) %s---\n",
91 vfolder_info_remove_entry (entry->info, entry);
93 g_free (entry->filename);
94 g_free (entry->displayname);
95 g_slist_free (entry->keywords);
96 g_slist_free (entry->implicit_keywords);
102 entry_alloc (Entry *entry)
108 entry_dealloc (Entry *entry)
114 entry_is_allocated (Entry *entry)
116 return entry->allocs > 0;
120 entry_make_user_private (Entry *entry, Folder *folder)
122 GnomeVFSURI *src_uri, *dest_uri;
123 GnomeVFSResult result;
124 gchar *uniqname, *filename;
126 if (entry->user_private)
129 /* Don't write privately if folder is link */
133 /* Need a writedir, otherwise just modify the original */
134 if (!entry->info->write_dir)
137 /* Need a filename to progress further */
138 if (!entry_get_filename (entry))
141 /* Make sure the destination directory exists */
142 result = vfolder_make_directory_and_parents (entry->info->write_dir,
145 if (result != GNOME_VFS_OK)
149 * Add a timestamp to the filename since we don't want conflicts between
150 * files in different logical folders with the same filename.
152 uniqname = vfolder_timestamp_file_name (entry_get_displayname (entry));
153 filename = vfolder_build_uri (entry->info->write_dir, uniqname, NULL);
156 src_uri = entry_get_real_uri (entry);
157 dest_uri = gnome_vfs_uri_new (filename);
159 result = gnome_vfs_xfer_uri (src_uri,
161 GNOME_VFS_XFER_USE_UNIQUE_NAMES,
162 GNOME_VFS_XFER_ERROR_MODE_ABORT,
163 GNOME_VFS_XFER_OVERWRITE_MODE_ABORT,
167 gnome_vfs_uri_unref (src_uri);
168 gnome_vfs_uri_unref (dest_uri);
170 if (result == GNOME_VFS_OK) {
171 if (!strcmp (entry_get_displayname (entry), ".directory")) {
172 folder_set_desktop_file (folder, filename);
174 /* Exclude current displayname. */
175 folder_add_exclude (folder,
176 entry_get_displayname (entry));
177 /* Remove include for current filename. */
178 folder_remove_include (folder,
179 entry_get_filename (entry));
180 /* Add include for new private filename. */
181 folder_add_include (folder, filename);
184 entry_set_filename (entry, filename);
185 entry_set_weight (entry, 1000);
186 entry->user_private = TRUE;
191 return result == GNOME_VFS_OK;
195 entry_is_user_private (Entry *entry)
197 return entry->user_private;
201 entry_reload_if_needed (Entry *entry)
203 gboolean changed = FALSE;
204 gchar *keywords, *deprecates;
210 entry_quick_read_keys (entry,
217 * Clear keywords from file, leaving only ones added from
220 g_slist_free (entry->keywords);
221 entry->keywords = g_slist_copy (entry->implicit_keywords);
224 char **parsed = g_strsplit (keywords, ";", -1);
225 GSList *keylist = entry->keywords;
227 for (i = 0; parsed[i] != NULL; i++) {
229 const char *word = parsed[i];
231 /* ignore empties (including end of list) */
235 quark = g_quark_from_string (word);
236 if (g_slist_find (keylist, GINT_TO_POINTER (quark)))
239 D (g_print ("ADDING KEYWORD: %s, %s\n",
240 entry_get_displayname (entry),
244 g_slist_prepend (entry->keywords,
245 GINT_TO_POINTER (quark));
251 /* FIXME: Support this */
253 char **parsed = g_strsplit (keywords, ";", -1);
256 for (i = 0; parsed[i] != NULL; i++) {
257 dep = vfolder_info_lookup_entry (entry->info,
260 vfolder_info_remove_entry (entry->info, dep);
261 #if 0 /* vfolder_monitor_emit is not defined */
262 vfolder_monitor_emit (
263 entry_get_filename (dep),
264 GNOME_VFS_MONITOR_EVENT_DELETED);
275 entry->dirty = FALSE;
279 entry_get_weight (Entry *entry)
281 return entry->weight;
285 entry_set_weight (Entry *entry, gushort weight)
287 entry->weight = weight;
291 entry_set_dirty (Entry *entry)
297 entry_set_filename (Entry *entry, const gchar *name)
299 g_free (entry->filename);
300 entry->filename = g_strdup (name);
303 gnome_vfs_uri_unref (entry->uri);
307 entry_set_dirty (entry);
311 entry_get_filename (Entry *entry)
313 return entry->filename;
317 entry_set_displayname (Entry *entry, const gchar *name)
319 g_free (entry->displayname);
320 entry->displayname = g_strdup (name);
324 entry_get_displayname (Entry *entry)
326 return entry->displayname;
330 entry_get_real_uri (Entry *entry)
332 if (!entry->filename)
336 entry->uri = gnome_vfs_uri_new (entry->filename);
338 gnome_vfs_uri_ref (entry->uri);
343 entry_get_keywords (Entry *entry)
345 entry_reload_if_needed (entry);
346 return entry->keywords;
350 entry_add_implicit_keyword (Entry *entry, GQuark keyword)
352 entry->keywords = g_slist_prepend (entry->keywords,
353 GINT_TO_POINTER (keyword));
354 entry->implicit_keywords = g_slist_prepend (entry->implicit_keywords,
355 GINT_TO_POINTER (keyword));
359 entry_key_val_from_string (gchar *src, const gchar *key, gchar **result)
362 gint keylen = strlen (key), end;
366 start = strstr (src, key);
368 (start == src || (*(start-1) == '\r') || (*(start-1) == '\n')) &&
369 ((*(start+keylen) == ' ') || (*(start+keylen) == '='))) {
371 start += strspn (start, "= ");
372 end = strcspn (start, "\r\n");
374 *result = g_strndup (start, end);
379 entry_quick_read_keys (Entry *entry,
385 GnomeVFSHandle *handle;
386 GnomeVFSFileSize readlen;
394 if (gnome_vfs_open (&handle,
395 entry_get_filename (entry),
396 GNOME_VFS_OPEN_READ) != GNOME_VFS_OK)
399 fullbuf = g_string_new (NULL);
400 while (gnome_vfs_read (handle,
403 &readlen) == GNOME_VFS_OK) {
404 g_string_append_len (fullbuf, buf, readlen);
407 gnome_vfs_close (handle);
410 g_string_free (fullbuf, TRUE);
414 entry_key_val_from_string (fullbuf->str, key1, result1);
417 entry_key_val_from_string (fullbuf->str, key2, result2);
419 g_string_free (fullbuf, TRUE);
423 entry_dump (Entry *entry, int indent)
425 gchar *space = g_strnfill (indent, ' ');
426 GSList *keywords = entry->keywords, *iter;
428 D (g_print ("%s%s\n%s Filename: %s\n%s Keywords: ",
430 entry_get_displayname (entry),
432 entry_get_filename (entry),
435 for (iter = keywords; iter; iter = iter->next) {
436 G_GNUC_UNUSED GQuark quark = GPOINTER_TO_INT (iter->data);
437 D (g_print (g_quark_to_string (quark)));
448 * Folder Implementation
451 folder_new (VFolderInfo *info, const gchar *name, gboolean user_private)
453 Folder *folder = g_new0 (Folder, 1);
455 folder->name = g_strdup (name);
456 folder->user_private = user_private;
460 folder->dirty = TRUE;
466 folder_ref (Folder *folder)
472 unalloc_exclude (gpointer key, gpointer val, gpointer user_data)
474 gchar *filename = key;
475 VFolderInfo *info = user_data;
478 /* Skip excludes which probably from the parent URI */
479 if (strchr (filename, '/'))
482 entry = vfolder_info_lookup_entry (info, filename);
484 entry_dealloc (entry);
488 folder_reset_entries (Folder *folder)
491 g_slist_foreach (folder->entries, (GFunc) entry_dealloc, NULL);
492 g_slist_foreach (folder->entries, (GFunc) entry_unref, NULL);
493 g_slist_free (folder->entries);
494 folder->entries = NULL;
496 if (folder->entries_ht) {
497 g_hash_table_destroy (folder->entries_ht);
498 folder->entries_ht = NULL;
503 folder_unref (Folder *folder)
507 if (folder->refcnt == 0) {
508 D (g_print ("DESTORYING FOLDER: %p, %s\n",
512 g_free (folder->name);
513 g_free (folder->extend_uri);
514 g_free (folder->desktop_file);
516 if (folder->extend_monitor)
517 vfolder_monitor_cancel (folder->extend_monitor);
519 query_free (folder->query);
521 if (folder->excludes) {
522 g_hash_table_foreach (folder->excludes,
523 (GHFunc) unalloc_exclude,
525 g_hash_table_destroy (folder->excludes);
528 g_slist_foreach (folder->includes, (GFunc) g_free, NULL);
529 g_slist_free (folder->includes);
532 g_slist_foreach (folder->subfolders,
533 (GFunc) folder_unref,
535 g_slist_free (folder->subfolders);
537 if (folder->subfolders_ht)
538 g_hash_table_destroy (folder->subfolders_ht);
540 folder_reset_entries (folder);
546 static gboolean read_one_extended_entry (Folder *folder,
547 const gchar *file_uri,
548 GnomeVFSFileInfo *file_info);
551 folder_extend_monitor_cb (GnomeVFSMonitorHandle *handle,
552 const gchar *monitor_uri,
553 const gchar *info_uri,
554 GnomeVFSMonitorEventType event_type,
557 Folder *folder = user_data;
559 GnomeVFSFileInfo *file_info;
560 GnomeVFSResult result;
561 GnomeVFSURI *uri, *entry_uri;
564 /* Operating on the whole directory, ignore */
565 if (!strcmp (monitor_uri, info_uri))
568 D (g_print ("*** Exdended folder %s ('%s') monitor %s%s%s called! ***\n",
571 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
572 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
573 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
575 uri = gnome_vfs_uri_new (info_uri);
576 filename = gnome_vfs_uri_extract_short_name (uri);
578 VFOLDER_INFO_WRITE_LOCK (folder->info);
580 switch (event_type) {
581 case GNOME_VFS_MONITOR_EVENT_CHANGED:
583 * We only care about entries here, as the extend_monitor_cb on
584 * the subfolders themselves should take care of emitting
587 child.entry = folder_get_entry (folder, filename);
589 entry_uri = entry_get_real_uri (child.entry);
591 if (gnome_vfs_uri_equal (entry_uri, uri)) {
592 entry_set_dirty (child.entry);
593 folder_emit_changed (
595 entry_get_displayname (child.entry),
596 GNOME_VFS_MONITOR_EVENT_CHANGED);
599 gnome_vfs_uri_unref (entry_uri);
602 case GNOME_VFS_MONITOR_EVENT_DELETED:
603 folder_get_child (folder, filename, &child);
606 * FIXME: should look for replacement in info's entry
607 * pool here, before sending event
610 if (child.type == DESKTOP_FILE) {
611 entry_uri = entry_get_real_uri (child.entry);
613 if (gnome_vfs_uri_equal (uri, entry_uri)) {
614 folder_remove_entry (folder, child.entry);
615 folder_emit_changed (
618 GNOME_VFS_MONITOR_EVENT_DELETED);
621 gnome_vfs_uri_unref (entry_uri);
623 else if (child.type == FOLDER) {
624 if (folder_is_user_private (child.folder)) {
625 folder_set_dirty (child.folder);
627 folder_remove_subfolder (folder, child.folder);
628 folder_emit_changed (
631 GNOME_VFS_MONITOR_EVENT_DELETED);
635 case GNOME_VFS_MONITOR_EVENT_CREATED:
636 file_info = gnome_vfs_file_info_new ();
638 gnome_vfs_get_file_info_uri (
641 GNOME_VFS_FILE_INFO_DEFAULT);
643 if (result == GNOME_VFS_OK &&
644 read_one_extended_entry (folder, info_uri, file_info))
645 folder_emit_changed (folder,
647 GNOME_VFS_MONITOR_EVENT_CREATED);
649 gnome_vfs_file_info_unref (file_info);
655 folder->info->modification_time = time (NULL);
657 VFOLDER_INFO_WRITE_UNLOCK (folder->info);
659 gnome_vfs_uri_unref (uri);
664 folder_make_user_private (Folder *folder)
666 if (folder->user_private)
669 if (folder->parent) {
670 if (folder->parent->read_only ||
671 !folder_make_user_private (folder->parent))
674 if (!folder->parent->has_user_private_subfolders) {
677 for (iter = folder->parent; iter; iter = iter->parent)
678 iter->has_user_private_subfolders = TRUE;
682 folder->user_private = TRUE;
684 vfolder_info_set_dirty (folder->info);
690 folder_is_user_private (Folder *folder)
692 return folder->user_private;
696 create_dot_directory_entry (Folder *folder)
698 Entry *entry = NULL, *existing;
699 const gchar *dot_directory = folder_get_desktop_file (folder);
701 /* Only replace if existing isn't user-private */
702 existing = folder_get_entry (folder, ".directory");
703 if (existing && entry_get_weight (existing) == 1000)
706 if (strchr (dot_directory, '/')) {
707 /* Assume full path or URI */
708 entry = entry_new (folder->info,
711 TRUE /*user_private*/,
714 gchar *dirpath = NULL;
717 if (folder->info->desktop_dir)
718 dirpath = folder->info->desktop_dir;
719 else if (folder->info->write_dir)
720 dirpath = folder->info->write_dir;
725 full_path = vfolder_build_uri (dirpath,
728 entry = entry_new (folder->info,
731 TRUE /*user_private*/,
738 folder_add_entry (folder, entry);
742 return entry != NULL;
746 read_one_include (Folder *folder, const gchar *file_uri)
748 Entry *entry = NULL, *existing;
750 gchar *basename, *basename_ts;
752 if (!strchr (file_uri, '/')) {
753 entry = vfolder_info_lookup_entry (folder->info, file_uri);
754 if (entry && entry != folder_get_entry (folder, file_uri)) {
755 folder_add_entry (folder, entry);
761 uri = gnome_vfs_uri_new (file_uri);
762 if (!uri || !gnome_vfs_uri_exists (uri))
765 basename = gnome_vfs_uri_extract_short_name (uri);
767 /* If including something from the WriteDir, untimestamp it. */
768 if (folder->info->write_dir &&
769 strstr (file_uri, folder->info->write_dir)) {
770 basename_ts = basename;
771 basename = vfolder_untimestamp_file_name (basename_ts);
772 g_free (basename_ts);
775 /* Only replace if existing is not user-private */
776 existing = folder_get_entry (folder, basename);
777 if (existing && entry_get_weight (existing) == 1000) {
778 gnome_vfs_uri_unref (uri);
783 entry = entry_new (folder->info,
788 folder_add_entry (folder, entry);
791 gnome_vfs_uri_unref (uri);
799 read_includes (Folder *folder)
802 gboolean changed = FALSE;
804 for (iter = folder->includes; iter; iter = iter->next) {
805 gchar *include = iter->data;
807 changed |= read_one_include (folder, include);
814 is_excluded (Folder *folder, const gchar *filename, const gchar *displayname)
816 if (!folder->excludes)
819 if (displayname && g_hash_table_lookup (folder->excludes, displayname))
822 if (filename && g_hash_table_lookup (folder->excludes, filename))
829 read_one_extended_entry (Folder *folder,
830 const gchar *file_uri,
831 GnomeVFSFileInfo *file_info)
833 Query *query = folder_get_query (folder);
835 if (is_excluded (folder, file_uri, file_info->name))
838 if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
841 if (folder_get_subfolder (folder, file_info->name))
844 sub = folder_new (folder->info, file_info->name, FALSE);
846 folder_set_extend_uri (sub, file_uri);
847 sub->is_link = folder->is_link;
849 folder_add_subfolder (folder, sub);
854 Entry *entry, *existing;
855 gboolean retval = FALSE;
857 /* Only replace if entry is more important than existing */
858 existing = folder_get_entry (folder, file_info->name);
859 if (existing && entry_get_weight (existing) >= 900)
862 entry = entry_new (folder->info,
865 FALSE /*user_private*/,
868 /* Include unless specifically excluded by query */
869 if (!query || query_try_match (query, folder, entry)) {
870 D (g_print ("ADDING EXTENDED ENTRY: "
872 folder_get_name (folder),
873 entry_get_displayname (entry),
874 g_slist_length ((GSList*)
875 folder_list_entries (folder))));
877 folder_add_entry (folder, entry);
887 read_extended_entries (Folder *folder)
889 GnomeVFSResult result;
890 GnomeVFSDirectoryHandle *handle;
891 GnomeVFSFileInfo *file_info;
892 const gchar *extend_uri;
893 gboolean changed = FALSE;
895 extend_uri = folder_get_extend_uri (folder);
897 result = gnome_vfs_directory_open (&handle,
899 GNOME_VFS_FILE_INFO_DEFAULT);
900 if (result != GNOME_VFS_OK)
903 file_info = gnome_vfs_file_info_new ();
908 result = gnome_vfs_directory_read_next (handle, file_info);
909 if (result != GNOME_VFS_OK)
912 if (!strcmp (file_info->name, ".") ||
913 !strcmp (file_info->name, ".."))
916 file_uri = vfolder_build_uri (extend_uri,
920 changed |= read_one_extended_entry (folder,
927 gnome_vfs_file_info_unref (file_info);
928 gnome_vfs_directory_close (handle);
934 read_one_info_entry_pool (Folder *folder, Entry *entry)
936 Query *query = folder_get_query (folder);
939 if (is_excluded (folder,
940 entry_get_filename (entry),
941 entry_get_displayname (entry))) {
943 * Being excluded counts as a ref because we don't want
944 * them showing up in the Others menu.
950 /* Only replace if entry is more important than existing */
951 existing = folder_get_entry (folder, entry_get_displayname (entry));
952 if (existing && entry_get_weight (existing) >= entry_get_weight (entry))
955 /* Only include if matches a mandatory query. */
956 if (query && query_try_match (query, folder, entry)) {
957 D (g_print ("ADDING POOL ENTRY: %s, %s, #%d!!!!\n",
958 folder_get_name (folder),
959 entry_get_displayname (entry),
961 (GSList*) folder_list_entries (folder))));
963 folder_add_entry (folder, entry);
971 read_info_entry_pool (Folder *folder)
973 const GSList *all_entries, *iter;
975 gboolean changed = FALSE;
977 if (folder->only_unallocated)
980 query = folder_get_query (folder);
981 all_entries = vfolder_info_list_all_entries (folder->info);
983 for (iter = all_entries; iter; iter = iter->next) {
984 Entry *entry = iter->data;
986 changed |= read_one_info_entry_pool (folder, entry);
993 folder_emit_changed (Folder *folder,
995 GnomeVFSMonitorEventType event_type)
1000 buf = g_string_new (NULL);
1003 g_string_prepend (buf, child);
1004 g_string_prepend_c (buf, '/');
1008 iter != NULL && iter != folder->info->root;
1009 iter = iter->parent) {
1010 g_string_prepend (buf, folder_get_name (iter));
1011 g_string_prepend_c (buf, '/');
1014 vfolder_info_emit_change (folder->info,
1015 buf->len ? buf->str : "/",
1018 g_string_free (buf, TRUE);
1022 remove_extended_subfolders (Folder *folder)
1024 GSList *iter, *copy;
1027 copy = g_slist_copy ((GSList *) folder_list_subfolders (folder));
1028 for (iter = copy; iter; iter = iter->next) {
1030 if (!folder_is_user_private (sub))
1031 folder_remove_subfolder (folder, sub);
1033 g_slist_free (copy);
1037 folder_reload_if_needed (Folder *folder)
1039 gboolean changed = FALSE;
1041 if (!folder->dirty || folder->loading)
1044 D (g_print ("----- RELOADING FOLDER: %s -----\n",
1047 folder->loading = TRUE;
1048 folder->info->loading = TRUE;
1050 folder_reset_entries (folder);
1051 remove_extended_subfolders (folder);
1053 if (folder_get_desktop_file (folder))
1054 changed |= create_dot_directory_entry (folder);
1056 if (folder->includes)
1057 changed |= read_includes (folder);
1059 if (folder_get_extend_uri (folder)) {
1060 changed |= read_extended_entries (folder);
1062 /* Start monitoring here, to cut down on unneeded events */
1063 if (!folder->extend_monitor)
1064 folder->extend_monitor =
1065 vfolder_monitor_dir_new (
1066 folder_get_extend_uri (folder),
1067 folder_extend_monitor_cb,
1071 if (folder_get_query (folder))
1072 changed |= read_info_entry_pool (folder);
1075 folder_emit_changed (folder,
1077 GNOME_VFS_MONITOR_EVENT_CHANGED);
1079 folder->info->loading = FALSE;
1080 folder->loading = FALSE;
1081 folder->dirty = FALSE;
1085 folder_set_dirty (Folder *folder)
1087 folder->dirty = TRUE;
1091 folder_set_name (Folder *folder, const gchar *name)
1093 g_free (folder->name);
1094 folder->name = g_strdup (name);
1096 vfolder_info_set_dirty (folder->info);
1100 folder_get_name (Folder *folder)
1102 return folder->name;
1106 folder_set_query (Folder *folder, Query *query)
1109 query_free (folder->query);
1111 folder->query = query;
1113 folder_set_dirty (folder);
1114 vfolder_info_set_dirty (folder->info);
1118 folder_get_query (Folder *folder)
1120 return folder->query;
1124 folder_set_extend_uri (Folder *folder, const gchar *uri)
1126 g_free (folder->extend_uri);
1127 folder->extend_uri = g_strdup (uri);
1129 if (folder->extend_monitor) {
1130 vfolder_monitor_cancel (folder->extend_monitor);
1131 folder->extend_monitor = NULL;
1134 folder_set_dirty (folder);
1135 vfolder_info_set_dirty (folder->info);
1139 folder_get_extend_uri (Folder *folder)
1141 return folder->extend_uri;
1145 folder_set_desktop_file (Folder *folder, const gchar *filename)
1147 g_free (folder->desktop_file);
1148 folder->desktop_file = g_strdup (filename);
1150 vfolder_info_set_dirty (folder->info);
1154 folder_get_desktop_file (Folder *folder)
1156 return folder->desktop_file;
1160 folder_get_child (Folder *folder, const gchar *name, FolderChild *child)
1165 memset (child, 0, sizeof (FolderChild));
1168 subdir = folder_get_subfolder (folder, name);
1170 /* No name, just return the parent folder */
1174 child->type = FOLDER;
1175 child->folder = subdir;
1179 file = folder_get_entry (folder, name);
1181 child->type = DESKTOP_FILE;
1182 child->entry = file;
1190 child_list_foreach_prepend (gpointer key,
1195 GSList **list = user_data;
1197 *list = g_slist_prepend (*list, g_strdup (name));
1201 child_list_prepend_sorted (gchar *sortorder,
1202 GHashTable *name_hash)
1211 split_ord = g_strsplit (sortorder, ":", -1);
1212 if (split_ord && split_ord [0]) {
1213 for (i = 0; split_ord [i]; i++) {
1214 gchar *name = split_ord [i];
1216 if (g_hash_table_lookup (name_hash, name)) {
1217 g_hash_table_remove (name_hash, name);
1218 ret = g_slist_prepend (ret, g_strdup (name));
1227 folder_list_children (Folder *folder)
1229 Entry *dot_directory;
1230 GHashTable *name_hash;
1232 GSList *list = NULL;
1234 /* FIXME: handle duplicate names here, by not using a hashtable */
1236 name_hash = g_hash_table_new (g_str_hash, g_str_equal);
1238 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1239 Folder *child = iter->data;
1240 g_hash_table_insert (name_hash,
1241 (gchar *) folder_get_name (child),
1245 for (iter = folder_list_entries (folder); iter; iter = iter->next) {
1246 Entry *entry = iter->data;
1247 g_hash_table_insert (name_hash,
1248 (gchar *) entry_get_displayname (entry),
1252 if (folder->only_unallocated) {
1253 Query *query = folder_get_query (folder);
1255 iter = vfolder_info_list_all_entries (folder->info);
1256 for (; iter; iter = iter->next) {
1257 Entry *entry = iter->data;
1259 if (entry_is_allocated (entry))
1262 if (query && !query_try_match (query, folder, entry))
1265 g_hash_table_insert (
1267 (gchar *) entry_get_displayname (entry),
1272 dot_directory = folder_get_entry (folder, ".directory");
1273 if (dot_directory) {
1275 entry_quick_read_keys (dot_directory,
1281 list = child_list_prepend_sorted (sortorder,
1287 g_hash_table_foreach (name_hash,
1288 (GHFunc) child_list_foreach_prepend,
1290 g_hash_table_destroy (name_hash);
1292 list = g_slist_reverse (list);
1298 folder_get_entry (Folder *folder, const gchar *filename)
1300 Entry *retval = NULL;
1302 folder_reload_if_needed (folder);
1304 if (folder->entries_ht)
1305 retval = g_hash_table_lookup (folder->entries_ht, filename);
1307 if (!retval && folder->only_unallocated)
1308 retval = vfolder_info_lookup_entry (folder->info, filename);
1314 folder_list_entries (Folder *folder)
1316 folder_reload_if_needed (folder);
1318 return folder->entries;
1322 * This doesn't set the folder dirty.
1323 * Use the include/exclude functions for that.
1326 folder_remove_entry (Folder *folder, Entry *entry)
1331 if (!folder->entries_ht)
1334 name = entry_get_displayname (entry);
1335 existing = g_hash_table_lookup (folder->entries_ht, name);
1337 g_hash_table_remove (folder->entries_ht, name);
1338 folder->entries = g_slist_remove (folder->entries, existing);
1340 entry_dealloc (existing);
1341 entry_unref (existing);
1346 * This doesn't set the folder dirty.
1347 * Use the include/exclude functions for that.
1350 folder_add_entry (Folder *folder, Entry *entry)
1352 entry_alloc (entry);
1355 folder_remove_entry (folder, entry);
1357 if (!folder->entries_ht)
1358 folder->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1360 g_hash_table_insert (folder->entries_ht,
1361 (gchar *) entry_get_displayname (entry),
1363 folder->entries = g_slist_append (folder->entries, entry);
1367 folder_add_include (Folder *folder, const gchar *include)
1369 folder_remove_exclude (folder, include);
1371 folder->includes = g_slist_prepend (folder->includes,
1372 g_strdup (include));
1374 vfolder_info_set_dirty (folder->info);
1378 folder_remove_include (Folder *folder, const gchar *file)
1382 if (!folder->includes)
1385 li = g_slist_find_custom (folder->includes,
1387 (GCompareFunc) strcmp);
1389 folder->includes = g_slist_delete_link (folder->includes, li);
1390 vfolder_info_set_dirty (folder->info);
1395 folder_add_exclude (Folder *parent, const gchar *exclude)
1399 folder_remove_include (parent, exclude);
1401 if (!parent->excludes)
1403 g_hash_table_new_full (g_str_hash,
1405 (GDestroyNotify) g_free,
1408 s = g_strdup (exclude);
1409 g_hash_table_replace (parent->excludes, s, s);
1411 vfolder_info_set_dirty (parent->info);
1415 folder_remove_exclude (Folder *folder, const gchar *file)
1417 if (!folder->excludes)
1420 g_hash_table_remove (folder->excludes, file);
1422 vfolder_info_set_dirty (folder->info);
1426 folder_get_subfolder (Folder *folder, const gchar *name)
1428 folder_reload_if_needed (folder);
1430 if (!folder->subfolders_ht)
1433 return g_hash_table_lookup (folder->subfolders_ht, name);
1437 folder_list_subfolders (Folder *parent)
1439 folder_reload_if_needed (parent);
1441 return parent->subfolders;
1445 folder_remove_subfolder (Folder *parent, Folder *child)
1450 if (!parent->subfolders_ht)
1453 name = folder_get_name (child);
1454 existing = g_hash_table_lookup (parent->subfolders_ht, name);
1456 g_hash_table_remove (parent->subfolders_ht, name);
1457 parent->subfolders = g_slist_remove (parent->subfolders,
1459 existing->parent = NULL;
1460 folder_unref (existing);
1461 vfolder_info_set_dirty (parent->info);
1466 folder_add_subfolder (Folder *parent, Folder *child)
1468 if (child->user_private && !parent->has_user_private_subfolders) {
1470 for (iter = parent; iter != NULL; iter = iter->parent)
1471 iter->has_user_private_subfolders = TRUE;
1475 child->parent = parent;
1477 if (!parent->subfolders_ht)
1478 parent->subfolders_ht = g_hash_table_new (g_str_hash,
1481 folder_remove_subfolder (parent, child);
1483 g_hash_table_insert (parent->subfolders_ht,
1484 (gchar *) folder_get_name (child),
1486 parent->subfolders = g_slist_append (parent->subfolders, child);
1488 vfolder_info_set_dirty (parent->info);
1492 folder_dump_tree (Folder *folder, int indent)
1495 gchar *space = g_strnfill (indent, ' ');
1497 D (g_print ("%s(%p): %s\n",
1500 folder ? folder_get_name (folder) : NULL));
1504 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1505 Folder *child = iter->data;
1507 folder_dump_tree (child, indent + 2);
1511 /* This is a pretty lame hack */
1513 folder_is_hidden (Folder *folder)
1515 const GSList *iter, *ents;
1517 if (folder->dont_show_if_empty == FALSE)
1520 if (folder->only_unallocated) {
1521 Query *query = folder_get_query (folder);
1523 iter = vfolder_info_list_all_entries (folder->info);
1524 for (; iter; iter = iter->next) {
1525 Entry *entry = iter->data;
1527 if (entry_is_allocated (entry))
1530 if (query && !query_try_match (query, folder, entry))
1537 ents = folder_list_entries (folder);
1539 /* If there is only one entry, check it is not .directory */
1541 Entry *dot_directory = ents->data;
1544 name = entry_get_displayname (dot_directory);
1545 if (strcmp (".directory", name) != 0)
1551 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1552 Folder *child = iter->data;
1554 if (!folder_is_hidden (child))
1564 * Query Implementation
1567 query_new (int type)
1571 query = g_new0 (Query, 1);
1578 query_free (Query *query)
1583 if (query->type == QUERY_OR || query->type == QUERY_AND) {
1584 g_slist_foreach (query->val.queries,
1587 g_slist_free (query->val.queries);
1589 else if (query->type == QUERY_FILENAME)
1590 g_free (query->val.filename);
1595 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
1598 query_try_match (Query *query,
1607 switch (query->type) {
1609 for (li = query->val.queries; li != NULL; li = li->next) {
1610 Query *subquery = li->data;
1612 if (query_try_match (subquery, folder, efile))
1613 return INVERT_IF_NEEDED (TRUE);
1615 return INVERT_IF_NEEDED (FALSE);
1617 for (li = query->val.queries; li != NULL; li = li->next) {
1618 Query *subquery = li->data;
1620 if (!query_try_match (subquery, folder, efile))
1621 return INVERT_IF_NEEDED (FALSE);
1623 return INVERT_IF_NEEDED (TRUE);
1626 const gchar *extend_uri;
1629 * Check that entry's path starts with that of the
1630 * folder's extend_uri, so that we know that it matches
1633 extend_uri = folder_get_extend_uri (folder);
1635 strncmp (entry_get_filename (efile),
1637 strlen (extend_uri)) == 0)
1638 return INVERT_IF_NEEDED (TRUE);
1640 return INVERT_IF_NEEDED (FALSE);
1644 const GSList *keywords;
1647 keywords = entry_get_keywords (efile);
1648 for (; keywords; keywords = keywords->next) {
1649 keyword = GPOINTER_TO_INT (keywords->data);
1650 if (keyword == query->val.keyword)
1651 return INVERT_IF_NEEDED (TRUE);
1654 return INVERT_IF_NEEDED (FALSE);
1655 case QUERY_FILENAME:
1656 if (strchr (query->val.filename, '/') &&
1657 !strcmp (query->val.filename, entry_get_filename (efile)))
1658 return INVERT_IF_NEEDED (TRUE);
1659 else if (!strcmp (query->val.filename,
1660 entry_get_displayname (efile)))
1661 return INVERT_IF_NEEDED (TRUE);
1663 return INVERT_IF_NEEDED (FALSE);
1666 g_assert_not_reached ();