Don't crash when closing a window.
[rox-filer.git] / ROX-Filer / src / dir.c
blobc744cf87d2386a9010e15df43468c138a5246932
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place, Suite 330, Boston, MA 02111-1307 USA
19 /* dir.c - directory scanning and caching */
21 /* How it works:
23 * A Directory contains a list DirItems, each having a name and some details
24 * (size, image, owner, etc).
26 * There is a list of file names that need to be rechecked. While this
27 * list is non-empty, items are taken from the list in an idle callback
28 * and checked. Missing items are removed from the Directory, new items are
29 * added and existing items are updated if they've changed.
31 * When a whole directory is to be rescanned:
33 * - A list of all filenames in the directory is fetched, without any
34 * of the extra details.
35 * - This list is compared to the current DirItems, removing any that are now
36 * missing.
37 * - Each window onto the directory is asked which items it will actually
38 * display, and the union of these sets is the new recheck list.
40 * This system is designed to get the number of items and their names quickly,
41 * so that the auto-sizer can make a good guess. It also prevents checking
42 * hidden files if they're not going to be displayed.
44 * To get the Directory object, use dir_cache, which will automatically
45 * trigger a rescan if needed.
47 * To get notified when the Directory changes, use the dir_attach() and
48 * dir_detach() functions.
51 #include "config.h"
53 #include <gtk/gtk.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <string.h>
58 #include "global.h"
60 #include "dir.h"
61 #include "diritem.h"
62 #include "support.h"
63 #include "gui_support.h"
64 #include "dir.h"
65 #include "fscache.h"
66 #include "mount.h"
67 #include "pixmaps.h"
68 #include "type.h"
69 #include "usericons.h"
70 #include "main.h"
72 #ifdef USE_DNOTIFY
73 /* Newer Linux kernels can tell us when the directories we are watching
74 * change, using the dnotify system.
76 static GHashTable *dnotify_fd_to_dir = NULL;
77 gboolean dnotify_wakeup_flag = FALSE;
78 static int dnotify_last_fd = -1;
79 #endif
81 /* For debugging. Can't detach when this is non-zero. */
82 static int in_callback = 0;
84 GHashTable *dir_cache_new = NULL; /* URI -> Directory */
85 GFSCache *dir_cache = NULL;
87 /* Static prototypes */
88 static void update(Directory *dir, gchar *pathname, gpointer data);
89 static void set_idle_callback(Directory *dir);
90 static DirItem *insert_item(Directory *dir, const guchar *leafname);
91 static void remove_missing(Directory *dir, GPtrArray *keep);
92 static void dir_recheck(Directory *dir,
93 const guchar *path, const guchar *leafname);
94 static GPtrArray *hash_to_array(GHashTable *hash);
95 static void dir_force_update_item(Directory *dir, const gchar *leaf);
96 static Directory *dir_new(const char *uri);
97 static void dir_rescan(Directory *dir);
98 #ifdef USE_DNOTIFY
99 static void dir_rescan_soon(Directory *dir);
100 static void dnotify_handler(int sig, siginfo_t *si, void *data);
101 #endif
103 /****************************************************************
104 * EXTERNAL INTERFACE *
105 ****************************************************************/
107 void dir_init(void)
109 dir_cache_new = g_hash_table_new(g_str_hash, g_str_equal);
110 dir_cache = g_fscache_new((GFSLoadFunc) dir_new,
111 (GFSUpdateFunc) update, NULL);
113 /* Check for dnotify support in the kernel */
114 #ifdef USE_DNOTIFY
116 struct sigaction act;
118 act.sa_sigaction = dnotify_handler;
119 sigemptyset(&act.sa_mask);
120 act.sa_flags = SA_SIGINFO;
121 sigaction(SIGRTMIN, &act, NULL);
123 /* Sometimes we get this instead of SIGRTMIN.
124 * Don't know why :-( but don't crash...
126 act.sa_handler = SIG_IGN;
127 sigemptyset(&act.sa_mask);
128 act.sa_flags = 0;
129 sigaction(SIGIO, &act, NULL);
131 dnotify_fd_to_dir = g_hash_table_new(NULL, NULL);
133 #endif
136 /* Get a new or existing Directory object.
137 * Call g_object_unref() when done.
138 * Will return null if directory doesn't exist : TODO
140 Directory *dir_lookup(const char *uri)
142 Directory *dir;
144 dir = g_hash_table_lookup(dir_cache_new, uri);
146 if (dir == NULL)
148 dir = dir_new(uri);
149 g_hash_table_insert(dir_cache_new, dir->pathname, dir);
150 g_object_ref(dir); /* One ref for the cache */
153 g_object_ref(dir); /* One ref for the caller */
155 return dir;
158 /* Periodically calls callback to notify about changes to the contents
159 * of the directory.
160 * Before this function returns, it calls the callback once to add all
161 * the items currently in the directory (unless the dir is empty).
162 * It then calls callback(DIR_QUEUE_INTERESTING) to find out which items the
163 * caller cares about.
164 * If we are not scanning, it also calls callback(DIR_END_SCAN).
166 void dir_attach(Directory *dir, DirCallback callback, gpointer data)
168 DirUser *user;
169 GPtrArray *items;
171 g_return_if_fail(dir != NULL);
172 g_return_if_fail(callback != NULL);
174 user = g_new(DirUser, 1);
175 user->callback = callback;
176 user->data = data;
178 #ifdef USE_DNOTIFY
179 if (!dir->users)
181 int fd;
183 if (dir->dnotify_fd != -1)
184 g_warning("dir_attach: dnotify error\n");
186 fd = open(dir->pathname, O_RDONLY);
187 g_return_if_fail(g_hash_table_lookup(dnotify_fd_to_dir,
188 GINT_TO_POINTER(fd)) == NULL);
189 if (fd != -1)
191 dir->dnotify_fd = fd;
192 g_hash_table_insert(dnotify_fd_to_dir,
193 GINT_TO_POINTER(fd), dir);
194 fcntl(fd, F_SETSIG, SIGRTMIN);
195 fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
196 DN_ATTRIB | DN_MULTISHOT);
199 #endif
201 dir->users = g_list_prepend(dir->users, user);
203 g_object_ref(dir);
205 items = hash_to_array(dir->known_items);
206 if (items->len)
207 callback(dir, DIR_ADD, items, data);
208 g_ptr_array_free(items, TRUE);
210 if (dir->needs_update && !dir->scanning)
211 dir_rescan(dir);
212 else
213 callback(dir, DIR_QUEUE_INTERESTING, NULL, data);
215 /* May start scanning if noone was watching before */
216 set_idle_callback(dir);
218 if (!dir->scanning)
219 callback(dir, DIR_END_SCAN, NULL, data);
222 /* Undo the effect of dir_attach */
223 void dir_detach(Directory *dir, DirCallback callback, gpointer data)
225 DirUser *user;
226 GList *list;
228 g_return_if_fail(dir != NULL);
229 g_return_if_fail(callback != NULL);
230 g_return_if_fail(in_callback == 0);
232 for (list = dir->users; list; list = list->next)
234 user = (DirUser *) list->data;
235 if (user->callback == callback && user->data == data)
237 g_free(user);
238 dir->users = g_list_remove(dir->users, user);
239 g_object_unref(dir);
241 /* May stop scanning if noone's watching */
242 set_idle_callback(dir);
244 #ifdef USE_DNOTIFY
245 if (!dir->users && dir->dnotify_fd != -1)
247 close(dir->dnotify_fd);
248 g_hash_table_remove(dnotify_fd_to_dir,
249 GINT_TO_POINTER(dir->dnotify_fd));
250 dir->dnotify_fd = -1;
252 #endif
253 return;
257 g_warning("dir_detach: Callback/data pair not attached!\n");
260 void dir_update(Directory *dir, gchar *pathname)
262 update(dir, pathname, NULL);
265 /* Rescan this directory */
266 void refresh_dirs(const char *path)
268 g_fscache_update(dir_cache, path);
271 /* When something has happened to a particular object, call this
272 * and all appropriate changes will be made.
274 void dir_check_this(const guchar *path)
276 guchar *real_path;
277 guchar *dir_path;
278 Directory *dir;
280 dir_path = g_path_get_dirname(path);
281 real_path = pathdup(dir_path);
282 g_free(dir_path);
284 dir = g_fscache_lookup_full(dir_cache, real_path,
285 FSCACHE_LOOKUP_PEEK, NULL);
286 if (dir)
288 dir_recheck(dir, real_path, g_basename(path));
289 g_object_unref(dir);
292 g_free(real_path);
295 #ifdef USE_DNOTIFY
296 static void drop_dnotify(gpointer key, gpointer value, gpointer data)
298 close(GPOINTER_TO_INT(key));
300 #endif
302 /* Used when we fork an action child, otherwise we can't delete or unmount
303 * any directory which we're watching!
305 void dir_drop_all_dnotifies(void)
307 #ifdef USE_DNOTIFY
308 g_hash_table_foreach(dnotify_fd_to_dir, drop_dnotify, NULL);
309 #endif
312 /* Tell watchers that this item has changed, but don't rescan.
313 * (used when thumbnail has been created for an item)
315 void dir_force_update_path(const gchar *path)
317 gchar *dir_path;
318 Directory *dir;
320 g_return_if_fail(path[0] == '/');
322 dir_path = g_path_get_dirname(path);
324 dir = g_fscache_lookup_full(dir_cache, dir_path, FSCACHE_LOOKUP_PEEK,
325 NULL);
326 if (dir)
328 dir_force_update_item(dir, g_basename(path));
329 g_object_unref(dir);
332 g_free(dir_path);
335 /* Ensure that 'leafname' is up-to-date. Returns the new/updated
336 * DirItem, or NULL if the file no longer exists.
338 DirItem *dir_update_item(Directory *dir, const gchar *leafname)
340 DirItem *item;
342 time(&diritem_recent_time);
343 item = insert_item(dir, leafname);
344 dir_merge_new(dir);
346 return item;
349 /* Add item to the recheck_list if it's marked as needing it.
350 * Item must have ITEM_FLAG_NEED_RESCAN_QUEUE.
351 * Items on the list will get checked later in an idle callback.
353 void dir_queue_recheck(Directory *dir, DirItem *item)
355 g_return_if_fail(dir != NULL);
356 g_return_if_fail(item != NULL);
357 g_return_if_fail(item->flags & ITEM_FLAG_NEED_RESCAN_QUEUE);
359 dir->recheck_list = g_list_prepend(dir->recheck_list,
360 g_strdup(item->leafname));
361 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
364 static void free_recheck_list(Directory *dir)
366 destroy_glist(&dir->recheck_list);
369 /* If scanning state has changed then notify all filer windows */
370 static void dir_set_scanning(Directory *dir, gboolean scanning)
372 GList *next;
374 if (scanning == dir->scanning)
375 return;
377 in_callback++;
379 dir->scanning = scanning;
381 for (next = dir->users; next; next = next->next)
383 DirUser *user = (DirUser *) next->data;
385 user->callback(dir,
386 scanning ? DIR_START_SCAN : DIR_END_SCAN,
387 NULL, user->data);
390 #if 0
391 /* Useful for profiling */
392 if (!scanning)
394 g_print("Done\n");
395 exit(0);
397 #endif
399 in_callback--;
402 /* Notify everyone that the error status of the directory has changed */
403 static void dir_error_changed(Directory *dir)
405 GList *next;
407 in_callback++;
409 for (next = dir->users; next; next = next->next)
411 DirUser *user = (DirUser *) next->data;
413 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
416 in_callback--;
419 /* This is called in the background when there are items on the
420 * dir->recheck_list to process.
422 static gboolean recheck_callback(gpointer data)
424 Directory *dir = (Directory *) data;
425 GList *next;
426 guchar *leaf;
428 g_return_val_if_fail(dir != NULL, FALSE);
429 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
431 /* Remove the first name from the list */
432 next = dir->recheck_list;
433 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
434 leaf = (guchar *) next->data;
435 g_list_free_1(next);
437 /* usleep(800); */
439 insert_item(dir, leaf);
441 g_free(leaf);
443 if (dir->recheck_list)
444 return TRUE; /* Call again */
446 /* The recheck_list list empty. Stop scanning, unless
447 * needs_update, in which case we start scanning again.
450 dir_merge_new(dir);
452 dir->have_scanned = TRUE;
453 dir_set_scanning(dir, FALSE);
454 g_source_remove(dir->idle_callback);
455 dir->idle_callback = 0;
457 if (dir->needs_update)
458 dir_rescan(dir);
460 return FALSE;
463 /* Add all the new items to the items array.
464 * Notify everyone who is watching us.
466 void dir_merge_new(Directory *dir)
468 GPtrArray *new = dir->new_items;
469 GPtrArray *up = dir->up_items;
470 GPtrArray *gone = dir->gone_items;
471 GList *list;
472 guint i;
474 in_callback++;
476 for (list = dir->users; list; list = list->next)
478 DirUser *user = (DirUser *) list->data;
480 if (new->len)
481 user->callback(dir, DIR_ADD, new, user->data);
482 if (up->len)
483 user->callback(dir, DIR_UPDATE, up, user->data);
484 if (gone->len)
485 user->callback(dir, DIR_REMOVE, gone, user->data);
488 in_callback--;
490 for (i = 0; i < new->len; i++)
492 DirItem *item = (DirItem *) new->pdata[i];
494 g_hash_table_insert(dir->known_items, item->leafname, item);
497 for (i = 0; i < gone->len; i++)
499 DirItem *item = (DirItem *) gone->pdata[i];
501 diritem_free(item);
504 g_ptr_array_set_size(gone, 0);
505 g_ptr_array_set_size(new, 0);
506 g_ptr_array_set_size(up, 0);
509 #ifdef USE_DNOTIFY
510 /* Called from the mainloop shortly after dnotify_handler */
511 void dnotify_wakeup(void)
513 Directory *dir;
515 dnotify_wakeup_flag = FALSE;
517 dir = g_hash_table_lookup(dnotify_fd_to_dir,
518 GINT_TO_POINTER(dnotify_last_fd));
520 if (dir)
521 dir_rescan_soon(dir);
523 #endif
525 /****************************************************************
526 * INTERNAL FUNCTIONS *
527 ****************************************************************/
529 #ifdef USE_DNOTIFY
530 static gint rescan_soon_timeout(gpointer data)
532 Directory *dir = (Directory *) data;
534 dir->rescan_timeout = -1;
535 if (dir->scanning)
536 dir->needs_update = TRUE;
537 else
538 dir_rescan(dir);
539 return FALSE;
542 /* Wait a fraction of a second and then rescan. If already waiting,
543 * this function does nothing.
545 static void dir_rescan_soon(Directory *dir)
547 if (dir->rescan_timeout != -1)
548 return;
549 dir->rescan_timeout = g_timeout_add(500, rescan_soon_timeout, dir);
551 #endif
553 static void free_items_array(GPtrArray *array)
555 guint i;
557 for (i = 0; i < array->len; i++)
559 DirItem *item = (DirItem *) array->pdata[i];
561 diritem_free(item);
564 g_ptr_array_free(array, TRUE);
567 /* Tell everyone watching that these items have gone */
568 static void notify_deleted(Directory *dir, GPtrArray *deleted)
570 GList *next;
572 if (!deleted->len)
573 return;
575 in_callback++;
577 for (next = dir->users; next; next = next->next)
579 DirUser *user = (DirUser *) next->data;
581 user->callback(dir, DIR_REMOVE, deleted, user->data);
584 in_callback--;
587 static void mark_unused(gpointer key, gpointer value, gpointer data)
589 DirItem *item = (DirItem *) value;
591 item->may_delete = TRUE;
594 static void keep_deleted(gpointer key, gpointer value, gpointer data)
596 DirItem *item = (DirItem *) value;
597 GPtrArray *deleted = (GPtrArray *) data;
599 if (item->may_delete)
600 g_ptr_array_add(deleted, item);
603 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
605 DirItem *item = (DirItem *) value;
607 return item->may_delete;
610 /* Remove all the old items that have gone.
611 * Notify everyone who is watching us of the removed items.
613 static void remove_missing(Directory *dir, GPtrArray *keep)
615 GPtrArray *deleted;
616 guint i;
618 deleted = g_ptr_array_new();
620 /* Mark all current items as may_delete */
621 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
623 /* Unmark all items also in 'keep' */
624 for (i = 0; i < keep->len; i++)
626 guchar *leaf = (guchar *) keep->pdata[i];
627 DirItem *item;
629 item = g_hash_table_lookup(dir->known_items, leaf);
631 if (item)
632 item->may_delete = FALSE;
635 /* Add each item still marked to 'deleted' */
636 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
638 /* Remove all items still marked */
639 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
641 notify_deleted(dir, deleted);
643 free_items_array(deleted);
646 static gint notify_timeout(gpointer data)
648 Directory *dir = (Directory *) data;
650 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
652 dir_merge_new(dir);
654 dir->notify_active = FALSE;
655 g_object_unref(dir);
657 return FALSE;
660 /* Call dir_merge_new() after a while. */
661 static void delayed_notify(Directory *dir)
663 if (dir->notify_active)
664 return;
665 g_object_ref(dir);
666 g_timeout_add(1500, notify_timeout, dir);
667 dir->notify_active = TRUE;
670 /* Stat this item and add, update or remove it.
671 * Returns the new/updated item, if any.
672 * (leafname may be from the current DirItem item)
673 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
675 static DirItem *insert_item(Directory *dir, const guchar *leafname)
677 GFile *full_path;
678 DirItem *item;
679 DirItem old;
680 gboolean do_compare = FALSE; /* (old is filled in) */
682 if (leafname[0] == '.' && (leafname[1] == '\n' ||
683 (leafname[1] == '.' && leafname[2] == '\n')))
684 return NULL; /* Ignore '.' and '..' */
686 full_path = g_file_get_child(g_file_new_for_uri(dir->pathname), leafname); // XXX
687 item = g_hash_table_lookup(dir->known_items, leafname);
689 if (item)
691 if (item->base_type != TYPE_UNKNOWN)
693 /* Preserve the old details so we can compare */
694 old = *item;
695 if (old._image)
696 g_object_ref(old._image);
697 do_compare = TRUE;
699 diritem_restat_gfile(full_path, item);
700 g_object_unref(full_path);
702 else
704 /* Item isn't already here. This won't normally happen,
705 * because blank items are added when scanning, before
706 * we get here.
708 item = diritem_new(leafname);
709 diritem_restat_gfile(full_path, item);
710 g_object_unref(full_path);
711 if (item->base_type == TYPE_ERROR &&
712 item->lstat_errno == ENOENT)
714 diritem_free(item);
715 return NULL;
717 g_ptr_array_add(dir->new_items, item);
721 /* No need to queue the item for scanning. If we got here because
722 * the item was queued, this flag will normally already be clear.
724 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
726 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
728 /* Item has been deleted */
729 g_hash_table_remove(dir->known_items, item->leafname);
730 g_ptr_array_add(dir->gone_items, item);
731 if (do_compare && old._image)
732 g_object_unref(old._image);
733 delayed_notify(dir);
734 return NULL;
737 if (do_compare)
739 /* It's a bit inefficient that we force the image to be
740 * loaded here, if we had an old image.
742 if (item->lstat_errno == old.lstat_errno
743 && item->base_type == old.base_type
744 && item->flags == old.flags
745 && item->size == old.size
746 && item->mode == old.mode
747 && item->atime == old.atime
748 && item->ctime == old.ctime
749 && item->mtime == old.mtime
750 && item->uid == old.uid
751 && item->gid == old.gid
752 && item->mime_type == old.mime_type
753 && (old._image == NULL || di_image(item) == old._image))
755 if (old._image)
756 g_object_unref(old._image);
757 return item;
759 if (old._image)
760 g_object_unref(old._image);
763 g_ptr_array_add(dir->up_items, item);
764 delayed_notify(dir);
766 return item;
769 static void update(Directory *dir, gchar *pathname, gpointer data)
771 g_free(dir->pathname);
772 dir->pathname = pathdup(pathname);
774 if (dir->scanning)
775 dir->needs_update = TRUE;
776 else
777 dir_rescan(dir);
780 /* If there is work to do, set the idle callback.
781 * Otherwise, stop scanning and unset the idle callback.
783 static void set_idle_callback(Directory *dir)
785 if (dir->recheck_list && dir->users)
787 /* Work to do, and someone's watching */
788 dir_set_scanning(dir, TRUE);
789 if (dir->idle_callback)
790 return;
791 time(&diritem_recent_time);
792 dir->idle_callback = g_idle_add(recheck_callback, dir);
793 /* Do the first call now (will remove the callback itself) */
794 recheck_callback(dir);
796 else
798 dir_set_scanning(dir, FALSE);
799 if (dir->idle_callback)
801 g_source_remove(dir->idle_callback);
802 dir->idle_callback = 0;
807 /* See dir_force_update_path() */
808 static void dir_force_update_item(Directory *dir, const gchar *leaf)
810 GList *list;
811 GPtrArray *items;
812 DirItem *item;
814 items = g_ptr_array_new();
816 item = g_hash_table_lookup(dir->known_items, leaf);
817 if (!item)
818 goto out;
820 g_ptr_array_add(items, item);
822 in_callback++;
824 for (list = dir->users; list; list = list->next)
826 DirUser *user = (DirUser *) list->data;
828 user->callback(dir, DIR_UPDATE, items, user->data);
831 in_callback--;
833 out:
834 g_ptr_array_free(items, TRUE);
837 static void dir_recheck(Directory *dir,
838 const guchar *path, const guchar *leafname)
840 guchar *old = dir->pathname;
842 dir->pathname = g_strdup(path);
843 g_free(old);
845 time(&diritem_recent_time);
846 insert_item(dir, leafname);
849 static void to_array(gpointer key, gpointer value, gpointer data)
851 GPtrArray *array = (GPtrArray *) data;
853 g_ptr_array_add(array, value);
856 /* Convert a hash table to an unsorted GPtrArray.
857 * g_ptr_array_free() the result.
859 static GPtrArray *hash_to_array(GHashTable *hash)
861 GPtrArray *array;
863 array = g_ptr_array_new();
865 g_hash_table_foreach(hash, to_array, array);
867 return array;
870 static gpointer parent_class;
872 /* Note: dir_cache is never purged, so this shouldn't get called */
873 static void dir_finialize(GObject *object)
875 GPtrArray *items;
876 Directory *dir = (Directory *) object;
878 g_return_if_fail(dir->users == NULL);
880 g_print("[ dir finalize ]\n");
882 free_recheck_list(dir);
883 set_idle_callback(dir);
884 if (dir->rescan_timeout != -1)
885 g_source_remove(dir->rescan_timeout);
887 dir_merge_new(dir); /* Ensures new, up and gone are empty */
889 g_ptr_array_free(dir->up_items, TRUE);
890 g_ptr_array_free(dir->new_items, TRUE);
891 g_ptr_array_free(dir->gone_items, TRUE);
893 items = hash_to_array(dir->known_items);
894 free_items_array(items);
895 g_hash_table_destroy(dir->known_items);
897 g_free(dir->error);
898 g_free(dir->pathname);
900 G_OBJECT_CLASS(parent_class)->finalize(object);
903 static void directory_class_init(gpointer gclass, gpointer data)
905 GObjectClass *object = (GObjectClass *) gclass;
907 parent_class = g_type_class_peek_parent(gclass);
909 object->finalize = dir_finialize;
912 static void directory_init(GTypeInstance *object, gpointer gclass)
914 Directory *dir = (Directory *) object;
916 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
917 dir->recheck_list = NULL;
918 dir->idle_callback = 0;
919 dir->scanning = FALSE;
920 dir->have_scanned = FALSE;
922 dir->users = NULL;
923 dir->needs_update = TRUE;
924 dir->notify_active = FALSE;
925 dir->pathname = NULL;
926 dir->error = NULL;
927 dir->rescan_timeout = -1;
928 #ifdef USE_DNOTIFY
929 dir->dnotify_fd = -1;
930 #endif
932 dir->new_items = g_ptr_array_new();
933 dir->up_items = g_ptr_array_new();
934 dir->gone_items = g_ptr_array_new();
937 static GType dir_get_type(void)
939 static GType type = 0;
941 if (!type)
943 static const GTypeInfo info =
945 sizeof (DirectoryClass),
946 NULL, /* base_init */
947 NULL, /* base_finalise */
948 directory_class_init,
949 NULL, /* class_finalise */
950 NULL, /* class_data */
951 sizeof(Directory),
952 0, /* n_preallocs */
953 directory_init
956 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
957 &info, 0);
960 return type;
963 static Directory *dir_new(const char *uri)
965 Directory *dir;
967 dir = g_object_new(dir_get_type(), NULL);
969 dir->pathname = g_strdup(uri); // TODO
971 return dir;
974 static void dir_rescan_internal(Directory *dir, GFile *pathname, GError **error)
976 GFileInfo *info;
977 GFileEnumerator *iter;
978 GPtrArray *names;
979 guint i;
980 GList *next;
982 info = g_file_query_info(pathname, "unix::*", G_FILE_QUERY_INFO_NONE, NULL, error);
983 if (!info)
984 return;
985 dir->stat_info.st_ino = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_UNIX_INODE);
986 dir->stat_info.st_dev = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_DEVICE);
987 g_object_unref(info);
989 iter = g_file_enumerate_children(pathname, "standard::*,unix::mode,thumbnail::path", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error);
990 if (!iter)
991 return
993 dir_set_scanning(dir, TRUE);
994 dir_merge_new(dir);
995 gdk_flush();
997 names = g_ptr_array_new();
999 /* Make a list of all the names in the directory */
1000 while ((info = g_file_enumerator_next_file(iter, NULL, error)))
1002 const char *name;
1004 if (*error)
1006 g_warning("Error scanning directory: %s", (*error)->message);
1007 g_error_free(*error);
1008 *error = NULL;
1009 continue;
1012 name = g_file_info_get_attribute_byte_string(info, G_FILE_ATTRIBUTE_STANDARD_NAME);
1013 g_return_if_fail(name != NULL);
1015 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
1017 /* Skip '.' and '..' */
1019 else
1021 g_ptr_array_add(names, g_strdup(name));
1024 g_object_unref(info);
1026 g_object_unref(iter);
1028 /* Compare the list with the current DirItems, removing
1029 * any that are missing.
1031 remove_missing(dir, names);
1033 free_recheck_list(dir);
1035 /* For each name found, mark it as needing to be put on the rescan
1036 * list at some point in the future.
1037 * If the item is new, put a blank place-holder item in the directory.
1039 for (i = 0; i < names->len; i++)
1041 DirItem *old;
1042 guchar *name = names->pdata[i];
1044 old = g_hash_table_lookup(dir->known_items, name);
1045 if (old)
1047 /* This flag is cleared when the item is added
1048 * to the rescan list.
1050 old->flags |= ITEM_FLAG_NEED_RESCAN_QUEUE;
1052 else
1054 DirItem *new;
1056 new = diritem_new(name);
1057 g_ptr_array_add(dir->new_items, new);
1062 dir_merge_new(dir);
1064 /* Ask everyone which items they need to display, and add them to
1065 * the recheck list. Typically, this means we don't waste time
1066 * scanning hidden items.
1068 in_callback++;
1069 for (next = dir->users; next; next = next->next)
1071 DirUser *user = (DirUser *) next->data;
1072 user->callback(dir,
1073 DIR_QUEUE_INTERESTING,
1074 NULL, user->data);
1076 in_callback--;
1078 g_ptr_array_free(names, TRUE);
1080 set_idle_callback(dir);
1081 dir_merge_new(dir);
1084 /* Get the names of all files in the directory.
1085 * Remove any DirItems that are no longer listed.
1086 * Replace the recheck_list with the items found.
1088 static void dir_rescan(Directory *dir)
1090 GError *error = NULL;
1091 GFile *gfile;
1093 g_return_if_fail(dir != NULL);
1095 dir->needs_update = FALSE;
1097 read_globicons();
1098 mount_update(FALSE);
1099 if (dir->error)
1101 null_g_free(&dir->error);
1102 dir_error_changed(dir);
1105 gfile = g_file_new_for_uri(dir->pathname);
1107 dir_rescan_internal(dir, gfile, &error);
1109 g_object_unref(gfile);
1111 if (error)
1113 dir->error = g_strdup(error->message);
1114 dir_error_changed(dir); /* Report on attach */
1115 g_error_free(error);
1119 #ifdef USE_DNOTIFY
1120 /* Signal handler - don't do anything dangerous here */
1121 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1123 /* Note: there is currently only one place to store the fd,
1124 * so we'll miss updates to several directories if they happen
1125 * close together.
1127 dnotify_last_fd = si->si_fd;
1128 dnotify_wakeup_flag = TRUE;
1129 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1131 #endif