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;
64 entry->not_shown = FALSE;
67 * Lame-O special case .directory handling, as we don't want them
68 * showing up for all-applications:///.
70 if (strcmp (displayname, ".directory") != 0)
71 vfolder_info_add_entry (info, entry);
77 entry_ref (Entry *entry)
83 entry_unref (Entry *entry)
87 if (entry->refcnt == 0) {
88 D (g_print ("-- KILLING ENTRY: (%p) %s---\n",
92 vfolder_info_remove_entry (entry->info, entry);
94 g_free (entry->filename);
95 g_free (entry->displayname);
96 g_slist_free (entry->keywords);
97 g_slist_free (entry->implicit_keywords);
103 entry_alloc (Entry *entry)
109 entry_dealloc (Entry *entry)
115 entry_is_allocated (Entry *entry)
117 return entry->allocs > 0;
121 entry_make_user_private (Entry *entry, Folder *folder)
123 GnomeVFSURI *src_uri, *dest_uri;
124 GnomeVFSResult result;
125 gchar *uniqname, *filename;
127 if (entry->user_private)
130 /* Don't write privately if folder is link */
134 /* Need a writedir, otherwise just modify the original */
135 if (!entry->info->write_dir)
138 /* Need a filename to progress further */
139 if (!entry_get_filename (entry))
142 /* Make sure the destination directory exists */
143 result = vfolder_make_directory_and_parents (entry->info->write_dir,
146 if (result != GNOME_VFS_OK)
150 * Add a timestamp to the filename since we don't want conflicts between
151 * files in different logical folders with the same filename.
153 uniqname = vfolder_timestamp_file_name (entry_get_displayname (entry));
154 filename = vfolder_build_uri (entry->info->write_dir, uniqname, NULL);
157 src_uri = entry_get_real_uri (entry);
158 dest_uri = gnome_vfs_uri_new (filename);
160 result = gnome_vfs_xfer_uri (src_uri,
162 GNOME_VFS_XFER_USE_UNIQUE_NAMES,
163 GNOME_VFS_XFER_ERROR_MODE_ABORT,
164 GNOME_VFS_XFER_OVERWRITE_MODE_ABORT,
168 gnome_vfs_uri_unref (src_uri);
169 gnome_vfs_uri_unref (dest_uri);
171 if (result == GNOME_VFS_OK) {
172 if (!strcmp (entry_get_displayname (entry), ".directory")) {
173 folder_set_desktop_file (folder, filename);
175 /* Exclude current displayname. */
176 folder_add_exclude (folder,
177 entry_get_displayname (entry));
178 /* Remove include for current filename. */
179 folder_remove_include (folder,
180 entry_get_filename (entry));
181 /* Add include for new private filename. */
182 folder_add_include (folder, filename);
185 entry_set_filename (entry, filename);
186 entry_set_weight (entry, 1000);
187 entry->user_private = TRUE;
192 return result == GNOME_VFS_OK;
196 entry_is_user_private (Entry *entry)
198 return entry->user_private;
202 entry_reload_if_needed (Entry *entry)
204 gboolean changed = FALSE;
205 gchar *keywords, *deprecates, *onlyshowin;
211 entry_quick_read_keys (entry,
220 g_printerr ("Read cats=%s onlyshowin=%s from entry %s\n",
221 keywords ? keywords : "none",
222 onlyshowin ? onlyshowin : "none",
227 * Clear keywords from file, leaving only ones added from
230 g_slist_free (entry->keywords);
231 entry->keywords = g_slist_copy (entry->implicit_keywords);
234 char **parsed = g_strsplit (keywords, ";", -1);
235 GSList *keylist = entry->keywords;
237 for (i = 0; parsed[i] != NULL; i++) {
239 const char *word = parsed[i];
241 /* ignore empties (including end of list) */
245 quark = g_quark_from_string (word);
246 if (g_slist_find (keylist, GINT_TO_POINTER (quark)))
249 D (g_print ("ADDING KEYWORD: %s, %s\n",
250 entry_get_displayname (entry),
254 g_slist_prepend (entry->keywords,
255 GINT_TO_POINTER (quark));
261 /* FIXME: Support this */
263 char **parsed = g_strsplit (keywords, ";", -1);
266 for (i = 0; parsed[i] != NULL; i++) {
267 dep = vfolder_info_lookup_entry (entry->info,
270 vfolder_info_remove_entry (entry->info, dep);
271 #if 0 /* vfolder_monitor_emit is not defined */
272 vfolder_monitor_emit (
273 entry_get_filename (dep),
274 GNOME_VFS_MONITOR_EVENT_DELETED);
283 char **parsed = g_strsplit (onlyshowin, ";", -1);
285 /* If OnlyShowIn exists, then the default is
286 * that we don't show the entry, unless the
287 * value includes "GNOME"
289 entry->not_shown = TRUE;
291 for (i = 0; parsed[i] != NULL; i++) {
292 if (strcmp (parsed[i], "GNOME") == 0)
293 entry->not_shown = FALSE;
296 /* FIXME do we need to emit some kind of notification here? */
305 entry->dirty = FALSE;
309 entry_get_weight (Entry *entry)
311 return entry->weight;
315 entry_set_weight (Entry *entry, gushort weight)
317 entry->weight = weight;
321 entry_set_dirty (Entry *entry)
327 entry_set_filename (Entry *entry, const gchar *name)
329 g_free (entry->filename);
330 entry->filename = g_strdup (name);
333 gnome_vfs_uri_unref (entry->uri);
337 entry_set_dirty (entry);
341 entry_get_filename (Entry *entry)
343 return entry->filename;
347 entry_set_displayname (Entry *entry, const gchar *name)
349 g_free (entry->displayname);
350 entry->displayname = g_strdup (name);
354 entry_get_displayname (Entry *entry)
356 return entry->displayname;
360 entry_get_real_uri (Entry *entry)
362 if (!entry->filename)
366 entry->uri = gnome_vfs_uri_new (entry->filename);
368 gnome_vfs_uri_ref (entry->uri);
373 entry_get_keywords (Entry *entry)
375 entry_reload_if_needed (entry);
376 return entry->keywords;
380 entry_add_implicit_keyword (Entry *entry, GQuark keyword)
382 entry->keywords = g_slist_prepend (entry->keywords,
383 GINT_TO_POINTER (keyword));
384 entry->implicit_keywords = g_slist_prepend (entry->implicit_keywords,
385 GINT_TO_POINTER (keyword));
389 entry_key_val_from_string (gchar *src, const gchar *key, gchar **result)
392 gint keylen = strlen (key), end;
396 start = strstr (src, key);
398 (start == src || (*(start-1) == '\r') || (*(start-1) == '\n')) &&
399 ((*(start+keylen) == ' ') || (*(start+keylen) == '='))) {
401 start += strspn (start, "= ");
402 end = strcspn (start, "\r\n");
404 *result = g_strndup (start, end);
409 entry_quick_read_keys (Entry *entry,
417 GnomeVFSHandle *handle;
418 GnomeVFSFileSize readlen;
428 if (gnome_vfs_open (&handle,
429 entry_get_filename (entry),
430 GNOME_VFS_OPEN_READ) != GNOME_VFS_OK)
433 fullbuf = g_string_new (NULL);
434 while (gnome_vfs_read (handle,
437 &readlen) == GNOME_VFS_OK) {
438 g_string_append_len (fullbuf, buf, readlen);
441 gnome_vfs_close (handle);
444 g_string_free (fullbuf, TRUE);
448 entry_key_val_from_string (fullbuf->str, key1, result1);
451 entry_key_val_from_string (fullbuf->str, key2, result2);
454 entry_key_val_from_string (fullbuf->str, key3, result3);
456 g_string_free (fullbuf, TRUE);
460 entry_dump (Entry *entry, int indent)
462 gchar *space = g_strnfill (indent, ' ');
463 GSList *keywords = entry->keywords, *iter;
465 D (g_print ("%s%s\n%s Filename: %s\n%s Keywords: ",
467 entry_get_displayname (entry),
469 entry_get_filename (entry),
472 for (iter = keywords; iter; iter = iter->next) {
473 G_GNUC_UNUSED GQuark quark = GPOINTER_TO_INT (iter->data);
474 D (g_print (g_quark_to_string (quark)));
485 * Folder Implementation
488 folder_new (VFolderInfo *info, const gchar *name, gboolean user_private)
490 Folder *folder = g_new0 (Folder, 1);
492 folder->name = g_strdup (name);
493 folder->user_private = user_private;
497 folder->dirty = TRUE;
503 folder_ref (Folder *folder)
509 unalloc_exclude (gpointer key, gpointer val, gpointer user_data)
511 gchar *filename = key;
512 VFolderInfo *info = user_data;
515 /* Skip excludes which probably from the parent URI */
516 if (strchr (filename, '/'))
519 entry = vfolder_info_lookup_entry (info, filename);
521 entry_dealloc (entry);
525 folder_reset_entries (Folder *folder)
528 g_slist_foreach (folder->entries, (GFunc) entry_dealloc, NULL);
529 g_slist_foreach (folder->entries, (GFunc) entry_unref, NULL);
530 g_slist_free (folder->entries);
531 folder->entries = NULL;
533 if (folder->entries_ht) {
534 g_hash_table_destroy (folder->entries_ht);
535 folder->entries_ht = NULL;
540 folder_unref (Folder *folder)
544 if (folder->refcnt == 0) {
545 D (g_print ("DESTORYING FOLDER: %p, %s\n",
549 g_free (folder->name);
550 g_free (folder->extend_uri);
551 g_free (folder->desktop_file);
553 if (folder->extend_monitor)
554 vfolder_monitor_cancel (folder->extend_monitor);
556 query_free (folder->query);
558 if (folder->excludes) {
559 g_hash_table_foreach (folder->excludes,
560 (GHFunc) unalloc_exclude,
562 g_hash_table_destroy (folder->excludes);
565 g_slist_foreach (folder->includes, (GFunc) g_free, NULL);
566 g_slist_free (folder->includes);
569 g_slist_foreach (folder->subfolders,
570 (GFunc) folder_unref,
572 g_slist_free (folder->subfolders);
574 if (folder->subfolders_ht)
575 g_hash_table_destroy (folder->subfolders_ht);
577 folder_reset_entries (folder);
583 static gboolean read_one_extended_entry (Folder *folder,
584 const gchar *file_uri,
585 GnomeVFSFileInfo *file_info);
588 folder_extend_monitor_cb (GnomeVFSMonitorHandle *handle,
589 const gchar *monitor_uri,
590 const gchar *info_uri,
591 GnomeVFSMonitorEventType event_type,
594 Folder *folder = user_data;
596 GnomeVFSFileInfo *file_info;
597 GnomeVFSResult result;
598 GnomeVFSURI *uri, *entry_uri;
601 /* Operating on the whole directory, ignore */
602 if (!strcmp (monitor_uri, info_uri))
605 D (g_print ("*** Exdended folder %s ('%s') monitor %s%s%s called! ***\n",
608 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
609 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
610 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
612 uri = gnome_vfs_uri_new (info_uri);
613 filename = gnome_vfs_uri_extract_short_name (uri);
615 VFOLDER_INFO_WRITE_LOCK (folder->info);
617 switch (event_type) {
618 case GNOME_VFS_MONITOR_EVENT_CHANGED:
620 * We only care about entries here, as the extend_monitor_cb on
621 * the subfolders themselves should take care of emitting
624 child.entry = folder_get_entry (folder, filename);
626 entry_uri = entry_get_real_uri (child.entry);
628 if (gnome_vfs_uri_equal (entry_uri, uri)) {
629 entry_set_dirty (child.entry);
630 folder_emit_changed (
632 entry_get_displayname (child.entry),
633 GNOME_VFS_MONITOR_EVENT_CHANGED);
636 gnome_vfs_uri_unref (entry_uri);
639 case GNOME_VFS_MONITOR_EVENT_DELETED:
640 folder_get_child (folder, filename, &child);
643 * FIXME: should look for replacement in info's entry
644 * pool here, before sending event
647 if (child.type == DESKTOP_FILE) {
648 entry_uri = entry_get_real_uri (child.entry);
650 if (gnome_vfs_uri_equal (uri, entry_uri)) {
651 folder_remove_entry (folder, child.entry);
652 folder_emit_changed (
655 GNOME_VFS_MONITOR_EVENT_DELETED);
658 gnome_vfs_uri_unref (entry_uri);
660 else if (child.type == FOLDER) {
661 if (folder_is_user_private (child.folder)) {
662 folder_set_dirty (child.folder);
664 folder_remove_subfolder (folder, child.folder);
665 folder_emit_changed (
668 GNOME_VFS_MONITOR_EVENT_DELETED);
672 case GNOME_VFS_MONITOR_EVENT_CREATED:
673 file_info = gnome_vfs_file_info_new ();
675 gnome_vfs_get_file_info_uri (
678 GNOME_VFS_FILE_INFO_DEFAULT);
680 if (result == GNOME_VFS_OK &&
681 read_one_extended_entry (folder, info_uri, file_info))
682 folder_emit_changed (folder,
684 GNOME_VFS_MONITOR_EVENT_CREATED);
686 gnome_vfs_file_info_unref (file_info);
692 folder->info->modification_time = time (NULL);
694 VFOLDER_INFO_WRITE_UNLOCK (folder->info);
696 gnome_vfs_uri_unref (uri);
701 folder_make_user_private (Folder *folder)
703 if (folder->user_private)
706 if (folder->parent) {
707 if (folder->parent->read_only ||
708 !folder_make_user_private (folder->parent))
711 if (!folder->parent->has_user_private_subfolders) {
714 for (iter = folder->parent; iter; iter = iter->parent)
715 iter->has_user_private_subfolders = TRUE;
719 folder->user_private = TRUE;
721 vfolder_info_set_dirty (folder->info);
727 folder_is_user_private (Folder *folder)
729 return folder->user_private;
733 create_dot_directory_entry (Folder *folder)
735 Entry *entry = NULL, *existing;
736 const gchar *dot_directory = folder_get_desktop_file (folder);
738 /* Only replace if existing isn't user-private */
739 existing = folder_get_entry (folder, ".directory");
740 if (existing && entry_get_weight (existing) == 1000)
743 if (strchr (dot_directory, '/')) {
744 /* Assume full path or URI */
745 entry = entry_new (folder->info,
748 TRUE /*user_private*/,
751 gchar *dirpath = NULL;
754 if (folder->info->desktop_dir)
755 dirpath = folder->info->desktop_dir;
756 else if (folder->info->write_dir)
757 dirpath = folder->info->write_dir;
762 full_path = vfolder_build_uri (dirpath,
765 entry = entry_new (folder->info,
768 TRUE /*user_private*/,
775 folder_add_entry (folder, entry);
779 return entry != NULL;
783 read_one_include (Folder *folder, const gchar *file_uri)
785 Entry *entry = NULL, *existing;
787 gchar *basename, *basename_ts;
789 if (!strchr (file_uri, '/')) {
790 entry = vfolder_info_lookup_entry (folder->info, file_uri);
791 if (entry && entry != folder_get_entry (folder, file_uri)) {
792 folder_add_entry (folder, entry);
798 uri = gnome_vfs_uri_new (file_uri);
799 if (!uri || !gnome_vfs_uri_exists (uri))
802 basename = gnome_vfs_uri_extract_short_name (uri);
804 /* If including something from the WriteDir, untimestamp it. */
805 if (folder->info->write_dir &&
806 strstr (file_uri, folder->info->write_dir)) {
807 basename_ts = basename;
808 basename = vfolder_untimestamp_file_name (basename_ts);
809 g_free (basename_ts);
812 /* Only replace if existing is not user-private */
813 existing = folder_get_entry (folder, basename);
814 if (existing && entry_get_weight (existing) == 1000) {
815 gnome_vfs_uri_unref (uri);
820 entry = entry_new (folder->info,
825 folder_add_entry (folder, entry);
828 gnome_vfs_uri_unref (uri);
836 read_includes (Folder *folder)
839 gboolean changed = FALSE;
841 for (iter = folder->includes; iter; iter = iter->next) {
842 gchar *include = iter->data;
844 changed |= read_one_include (folder, include);
851 is_excluded (Folder *folder, const gchar *filename, const gchar *displayname)
853 if (!folder->excludes)
856 if (displayname && g_hash_table_lookup (folder->excludes, displayname))
859 if (filename && g_hash_table_lookup (folder->excludes, filename))
866 read_one_extended_entry (Folder *folder,
867 const gchar *file_uri,
868 GnomeVFSFileInfo *file_info)
870 Query *query = folder_get_query (folder);
873 g_printerr ("reading one extended entry %s\n", file_uri ? file_uri : "null");
876 if (is_excluded (folder, file_uri, file_info->name))
879 if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
882 if (folder_get_subfolder (folder, file_info->name))
885 sub = folder_new (folder->info, file_info->name, FALSE);
887 folder_set_extend_uri (sub, file_uri);
888 sub->is_link = folder->is_link;
890 folder_add_subfolder (folder, sub);
895 Entry *entry, *existing;
896 gboolean retval = FALSE;
898 /* Only replace if entry is more important than existing */
899 existing = folder_get_entry (folder, file_info->name);
900 if (existing && entry_get_weight (existing) >= 900)
903 entry = entry_new (folder->info,
906 FALSE /*user_private*/,
909 /* Include unless specifically excluded by query */
910 if (!query || query_try_match (query, folder, entry)) {
911 D (g_print ("ADDING EXTENDED ENTRY: "
913 folder_get_name (folder),
914 entry_get_displayname (entry),
915 g_slist_length ((GSList*)
916 folder_list_entries (folder))));
918 folder_add_entry (folder, entry);
928 read_extended_entries (Folder *folder)
930 GnomeVFSResult result;
931 GnomeVFSDirectoryHandle *handle;
932 GnomeVFSFileInfo *file_info;
933 const gchar *extend_uri;
934 gboolean changed = FALSE;
936 extend_uri = folder_get_extend_uri (folder);
938 result = gnome_vfs_directory_open (&handle,
940 GNOME_VFS_FILE_INFO_DEFAULT);
943 g_printerr ("reading extended entries from %s result = %s\n"<
944 extend_uri ? extend_uri : "null",
945 result == GNOME_VFS_OK ? "ok" : "failed");
948 if (result != GNOME_VFS_OK)
951 file_info = gnome_vfs_file_info_new ();
956 result = gnome_vfs_directory_read_next (handle, file_info);
957 if (result != GNOME_VFS_OK)
960 if (!strcmp (file_info->name, ".") ||
961 !strcmp (file_info->name, ".."))
964 file_uri = vfolder_build_uri (extend_uri,
968 changed |= read_one_extended_entry (folder,
975 gnome_vfs_file_info_unref (file_info);
976 gnome_vfs_directory_close (handle);
982 read_one_info_entry_pool (Folder *folder, Entry *entry)
984 Query *query = folder_get_query (folder);
987 if (is_excluded (folder,
988 entry_get_filename (entry),
989 entry_get_displayname (entry))) {
991 * Being excluded counts as a ref because we don't want
992 * them showing up in the Others menu.
998 /* Only replace if entry is more important than existing */
999 existing = folder_get_entry (folder, entry_get_displayname (entry));
1000 if (existing && entry_get_weight (existing) >= entry_get_weight (entry))
1003 /* Only include if matches a mandatory query. */
1004 if (query && query_try_match (query, folder, entry)) {
1005 D (g_print ("ADDING POOL ENTRY: %s, %s, #%d!!!!\n",
1006 folder_get_name (folder),
1007 entry_get_displayname (entry),
1009 (GSList*) folder_list_entries (folder))));
1011 folder_add_entry (folder, entry);
1019 read_info_entry_pool (Folder *folder)
1021 const GSList *all_entries, *iter;
1023 gboolean changed = FALSE;
1025 if (folder->only_unallocated)
1028 query = folder_get_query (folder);
1029 all_entries = vfolder_info_list_all_entries (folder->info);
1031 for (iter = all_entries; iter; iter = iter->next) {
1032 Entry *entry = iter->data;
1034 changed |= read_one_info_entry_pool (folder, entry);
1041 folder_emit_changed (Folder *folder,
1043 GnomeVFSMonitorEventType event_type)
1048 buf = g_string_new (NULL);
1051 g_string_prepend (buf, child);
1052 g_string_prepend_c (buf, '/');
1056 iter != NULL && iter != folder->info->root;
1057 iter = iter->parent) {
1058 g_string_prepend (buf, folder_get_name (iter));
1059 g_string_prepend_c (buf, '/');
1062 vfolder_info_emit_change (folder->info,
1063 buf->len ? buf->str : "/",
1066 g_string_free (buf, TRUE);
1070 remove_extended_subfolders (Folder *folder)
1072 GSList *iter, *copy;
1075 copy = g_slist_copy ((GSList *) folder_list_subfolders (folder));
1076 for (iter = copy; iter; iter = iter->next) {
1078 if (!folder_is_user_private (sub))
1079 folder_remove_subfolder (folder, sub);
1081 g_slist_free (copy);
1085 folder_reload_if_needed (Folder *folder)
1087 gboolean changed = FALSE;
1090 g_printerr ("folder maybe reload dirty = %d loading = %d\n",
1091 folder->dirty, folder->loading);
1094 if (!folder->dirty || folder->loading)
1097 D (g_print ("----- RELOADING FOLDER: %s -----\n",
1100 folder->loading = TRUE;
1101 folder->info->loading = TRUE;
1103 folder_reset_entries (folder);
1104 remove_extended_subfolders (folder);
1106 if (folder_get_desktop_file (folder))
1107 changed |= create_dot_directory_entry (folder);
1109 if (folder->includes)
1110 changed |= read_includes (folder);
1112 if (folder_get_extend_uri (folder)) {
1113 changed |= read_extended_entries (folder);
1115 /* Start monitoring here, to cut down on unneeded events */
1116 if (!folder->extend_monitor)
1117 folder->extend_monitor =
1118 vfolder_monitor_dir_new (
1119 folder_get_extend_uri (folder),
1120 folder_extend_monitor_cb,
1124 g_printerr ("folder %s has no extend uri, not reading\n",
1129 if (folder_get_query (folder))
1130 changed |= read_info_entry_pool (folder);
1133 folder_emit_changed (folder,
1135 GNOME_VFS_MONITOR_EVENT_CHANGED);
1137 folder->info->loading = FALSE;
1138 folder->loading = FALSE;
1139 folder->dirty = FALSE;
1143 folder_set_dirty (Folder *folder)
1145 folder->dirty = TRUE;
1149 folder_set_name (Folder *folder, const gchar *name)
1151 g_free (folder->name);
1152 folder->name = g_strdup (name);
1154 vfolder_info_set_dirty (folder->info);
1158 folder_get_name (Folder *folder)
1160 return folder->name;
1164 folder_set_query (Folder *folder, Query *query)
1167 query_free (folder->query);
1169 folder->query = query;
1171 folder_set_dirty (folder);
1172 vfolder_info_set_dirty (folder->info);
1176 folder_get_query (Folder *folder)
1178 return folder->query;
1182 folder_set_extend_uri (Folder *folder, const gchar *uri)
1185 g_printerr ("setting extend URI of %s to %s\n",
1187 uri ? uri : "null");
1190 g_free (folder->extend_uri);
1191 folder->extend_uri = g_strdup (uri);
1193 if (folder->extend_monitor) {
1194 vfolder_monitor_cancel (folder->extend_monitor);
1195 folder->extend_monitor = NULL;
1198 folder_set_dirty (folder);
1199 vfolder_info_set_dirty (folder->info);
1203 folder_get_extend_uri (Folder *folder)
1205 return folder->extend_uri;
1209 folder_set_desktop_file (Folder *folder, const gchar *filename)
1211 g_free (folder->desktop_file);
1212 folder->desktop_file = g_strdup (filename);
1214 vfolder_info_set_dirty (folder->info);
1218 folder_get_desktop_file (Folder *folder)
1220 return folder->desktop_file;
1224 folder_get_child (Folder *folder, const gchar *name, FolderChild *child)
1229 memset (child, 0, sizeof (FolderChild));
1232 subdir = folder_get_subfolder (folder, name);
1234 /* No name, just return the parent folder */
1238 child->type = FOLDER;
1239 child->folder = subdir;
1243 file = folder_get_entry (folder, name);
1245 child->type = DESKTOP_FILE;
1246 child->entry = file;
1254 child_list_foreach_prepend (gpointer key,
1259 GSList **list = user_data;
1261 *list = g_slist_prepend (*list, g_strdup (name));
1265 child_list_prepend_sorted (gchar *sortorder,
1266 GHashTable *name_hash)
1275 split_ord = g_strsplit (sortorder, ":", -1);
1276 if (split_ord && split_ord [0]) {
1277 for (i = 0; split_ord [i]; i++) {
1278 gchar *name = split_ord [i];
1280 if (g_hash_table_lookup (name_hash, name)) {
1281 g_hash_table_remove (name_hash, name);
1282 ret = g_slist_prepend (ret, g_strdup (name));
1291 folder_list_children (Folder *folder)
1293 Entry *dot_directory;
1294 GHashTable *name_hash;
1296 GSList *list = NULL;
1298 /* FIXME: handle duplicate names here, by not using a hashtable */
1300 name_hash = g_hash_table_new (g_str_hash, g_str_equal);
1302 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1303 Folder *child = iter->data;
1304 g_hash_table_insert (name_hash,
1305 (gchar *) folder_get_name (child),
1309 for (iter = folder_list_entries (folder); iter; iter = iter->next) {
1310 Entry *entry = iter->data;
1311 g_hash_table_insert (name_hash,
1312 (gchar *) entry_get_displayname (entry),
1316 if (folder->only_unallocated) {
1317 Query *query = folder_get_query (folder);
1319 iter = vfolder_info_list_all_entries (folder->info);
1320 for (; iter; iter = iter->next) {
1321 Entry *entry = iter->data;
1323 if (entry_is_allocated (entry))
1326 if (query && !query_try_match (query, folder, entry))
1329 if (entry->not_shown)
1332 g_hash_table_insert (
1334 (gchar *) entry_get_displayname (entry),
1339 dot_directory = folder_get_entry (folder, ".directory");
1340 if (dot_directory) {
1342 entry_quick_read_keys (dot_directory,
1350 list = child_list_prepend_sorted (sortorder,
1356 g_hash_table_foreach (name_hash,
1357 (GHFunc) child_list_foreach_prepend,
1359 g_hash_table_destroy (name_hash);
1361 list = g_slist_reverse (list);
1367 folder_get_entry (Folder *folder, const gchar *filename)
1369 Entry *retval = NULL;
1371 folder_reload_if_needed (folder);
1373 if (folder->entries_ht)
1374 retval = g_hash_table_lookup (folder->entries_ht, filename);
1376 if (!retval && folder->only_unallocated)
1377 retval = vfolder_info_lookup_entry (folder->info, filename);
1383 folder_list_entries (Folder *folder)
1385 folder_reload_if_needed (folder);
1387 return folder->entries;
1391 * This doesn't set the folder dirty.
1392 * Use the include/exclude functions for that.
1395 folder_remove_entry (Folder *folder, Entry *entry)
1400 if (!folder->entries_ht)
1403 name = entry_get_displayname (entry);
1404 existing = g_hash_table_lookup (folder->entries_ht, name);
1406 g_hash_table_remove (folder->entries_ht, name);
1407 folder->entries = g_slist_remove (folder->entries, existing);
1409 entry_dealloc (existing);
1410 entry_unref (existing);
1415 * This doesn't set the folder dirty.
1416 * Use the include/exclude functions for that.
1419 folder_add_entry (Folder *folder, Entry *entry)
1421 entry_alloc (entry);
1424 folder_remove_entry (folder, entry);
1426 if (!folder->entries_ht)
1427 folder->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1429 g_hash_table_insert (folder->entries_ht,
1430 (gchar *) entry_get_displayname (entry),
1432 folder->entries = g_slist_append (folder->entries, entry);
1436 folder_add_include (Folder *folder, const gchar *include)
1438 folder_remove_exclude (folder, include);
1440 folder->includes = g_slist_prepend (folder->includes,
1441 g_strdup (include));
1443 vfolder_info_set_dirty (folder->info);
1447 folder_remove_include (Folder *folder, const gchar *file)
1451 if (!folder->includes)
1454 li = g_slist_find_custom (folder->includes,
1456 (GCompareFunc) strcmp);
1458 folder->includes = g_slist_delete_link (folder->includes, li);
1459 vfolder_info_set_dirty (folder->info);
1464 folder_add_exclude (Folder *parent, const gchar *exclude)
1468 folder_remove_include (parent, exclude);
1470 if (!parent->excludes)
1472 g_hash_table_new_full (g_str_hash,
1474 (GDestroyNotify) g_free,
1477 s = g_strdup (exclude);
1478 g_hash_table_replace (parent->excludes, s, s);
1480 vfolder_info_set_dirty (parent->info);
1484 folder_remove_exclude (Folder *folder, const gchar *file)
1486 if (!folder->excludes)
1489 g_hash_table_remove (folder->excludes, file);
1491 vfolder_info_set_dirty (folder->info);
1495 folder_get_subfolder (Folder *folder, const gchar *name)
1497 folder_reload_if_needed (folder);
1499 if (!folder->subfolders_ht)
1502 return g_hash_table_lookup (folder->subfolders_ht, name);
1506 folder_list_subfolders (Folder *parent)
1508 folder_reload_if_needed (parent);
1510 return parent->subfolders;
1514 folder_remove_subfolder (Folder *parent, Folder *child)
1519 if (!parent->subfolders_ht)
1522 name = folder_get_name (child);
1523 existing = g_hash_table_lookup (parent->subfolders_ht, name);
1525 g_hash_table_remove (parent->subfolders_ht, name);
1526 parent->subfolders = g_slist_remove (parent->subfolders,
1528 existing->parent = NULL;
1529 folder_unref (existing);
1530 vfolder_info_set_dirty (parent->info);
1535 folder_add_subfolder (Folder *parent, Folder *child)
1537 if (child->user_private && !parent->has_user_private_subfolders) {
1539 for (iter = parent; iter != NULL; iter = iter->parent)
1540 iter->has_user_private_subfolders = TRUE;
1544 child->parent = parent;
1546 if (!parent->subfolders_ht)
1547 parent->subfolders_ht = g_hash_table_new (g_str_hash,
1550 folder_remove_subfolder (parent, child);
1552 g_hash_table_insert (parent->subfolders_ht,
1553 (gchar *) folder_get_name (child),
1555 parent->subfolders = g_slist_append (parent->subfolders, child);
1557 vfolder_info_set_dirty (parent->info);
1561 folder_dump_tree (Folder *folder, int indent)
1564 gchar *space = g_strnfill (indent, ' ');
1566 D (g_print ("%s(%p): %s\n",
1569 folder ? folder_get_name (folder) : NULL));
1573 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1574 Folder *child = iter->data;
1576 folder_dump_tree (child, indent + 2);
1580 /* This is a pretty lame hack */
1582 folder_is_hidden (Folder *folder)
1584 const GSList *iter, *ents;
1586 if (folder->dont_show_if_empty == FALSE)
1589 if (folder->only_unallocated) {
1590 Query *query = folder_get_query (folder);
1592 iter = vfolder_info_list_all_entries (folder->info);
1593 for (; iter; iter = iter->next) {
1594 Entry *entry = iter->data;
1596 if (entry_is_allocated (entry))
1599 if (entry->not_shown)
1602 if (query && !query_try_match (query, folder, entry))
1609 ents = folder_list_entries (folder);
1611 /* If there is only one entry, check it is not .directory */
1613 Entry *dot_directory = ents->data;
1616 name = entry_get_displayname (dot_directory);
1617 if (strcmp (".directory", name) != 0)
1623 for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1624 Folder *child = iter->data;
1626 if (!folder_is_hidden (child))
1636 * Query Implementation
1639 query_new (int type)
1643 query = g_new0 (Query, 1);
1650 query_free (Query *query)
1655 if (query->type == QUERY_OR || query->type == QUERY_AND) {
1656 g_slist_foreach (query->val.queries,
1659 g_slist_free (query->val.queries);
1661 else if (query->type == QUERY_FILENAME)
1662 g_free (query->val.filename);
1667 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
1670 query_try_match (Query *query,
1676 if (efile->not_shown)
1682 switch (query->type) {
1684 for (li = query->val.queries; li != NULL; li = li->next) {
1685 Query *subquery = li->data;
1687 if (query_try_match (subquery, folder, efile))
1688 return INVERT_IF_NEEDED (TRUE);
1690 return INVERT_IF_NEEDED (FALSE);
1692 for (li = query->val.queries; li != NULL; li = li->next) {
1693 Query *subquery = li->data;
1695 if (!query_try_match (subquery, folder, efile))
1696 return INVERT_IF_NEEDED (FALSE);
1698 return INVERT_IF_NEEDED (TRUE);
1701 const gchar *extend_uri;
1704 * Check that entry's path starts with that of the
1705 * folder's extend_uri, so that we know that it matches
1708 extend_uri = folder_get_extend_uri (folder);
1710 strncmp (entry_get_filename (efile),
1712 strlen (extend_uri)) == 0)
1713 return INVERT_IF_NEEDED (TRUE);
1715 return INVERT_IF_NEEDED (FALSE);
1719 const GSList *keywords;
1722 keywords = entry_get_keywords (efile);
1723 for (; keywords; keywords = keywords->next) {
1724 keyword = GPOINTER_TO_INT (keywords->data);
1725 if (keyword == query->val.keyword)
1726 return INVERT_IF_NEEDED (TRUE);
1729 return INVERT_IF_NEEDED (FALSE);
1730 case QUERY_FILENAME:
1731 if (strchr (query->val.filename, '/') &&
1732 !strcmp (query->val.filename, entry_get_filename (efile)))
1733 return INVERT_IF_NEEDED (TRUE);
1734 else if (!strcmp (query->val.filename,
1735 entry_get_displayname (efile)))
1736 return INVERT_IF_NEEDED (TRUE);
1738 return INVERT_IF_NEEDED (FALSE);
1741 g_assert_not_reached ();