2008-05-05 Paolo Borelli <pborelli@katamail.com>
[nautilus.git] / libnautilus-private / nautilus-directory-async.c
blob20ef5970afb8087d75120ab205d1e8d28e7a5c90
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
3 nautilus-directory-async.c: Nautilus directory model state machine.
5 Copyright (C) 1999, 2000, 2001 Eazel, Inc.
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the
19 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
22 Author: Darin Adler <darin@bentspoon.com>
25 #include <config.h>
27 #include "nautilus-directory-metafile.h"
28 #include "nautilus-directory-notify.h"
29 #include "nautilus-directory-private.h"
30 #include "nautilus-file-attributes.h"
31 #include "nautilus-file-private.h"
32 #include "nautilus-file-utilities.h"
33 #include "nautilus-signaller.h"
34 #include "nautilus-global-preferences.h"
35 #include "nautilus-link.h"
36 #include "nautilus-marshal.h"
37 #include "nautilus-metafile.h"
38 #include <eel/eel-glib-extensions.h>
39 #include <eel/eel-string.h>
40 #include <gtk/gtkmain.h>
41 #include <libxml/parser.h>
42 #include <stdio.h>
43 #include <stdlib.h>
45 /* turn this on to see messages about each load_directory call: */
46 #if 0
47 #define DEBUG_LOAD_DIRECTORY
48 #endif
50 /* turn this on to check if async. job calls are balanced */
51 #if 0
52 #define DEBUG_ASYNC_JOBS
53 #endif
55 /* turn this on to log things starting and stopping */
56 #if 0
57 #define DEBUG_START_STOP
58 #endif
60 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
62 /* Keep async. jobs down to this number for all directories. */
63 #define MAX_ASYNC_JOBS 10
65 struct TopLeftTextReadState {
66 NautilusDirectory *directory;
67 NautilusFile *file;
68 gboolean large;
69 GCancellable *cancellable;
72 struct LinkInfoReadState {
73 NautilusDirectory *directory;
74 GCancellable *cancellable;
75 NautilusFile *file;
78 struct ThumbnailState {
79 NautilusDirectory *directory;
80 GCancellable *cancellable;
81 NautilusFile *file;
82 gboolean trying_original;
83 gboolean tried_original;
86 struct MountState {
87 NautilusDirectory *directory;
88 GCancellable *cancellable;
89 NautilusFile *file;
92 struct FilesystemInfoState {
93 NautilusDirectory *directory;
94 GCancellable *cancellable;
95 NautilusFile *file;
98 struct DirectoryLoadState {
99 NautilusDirectory *directory;
100 GCancellable *cancellable;
101 GFileEnumerator *enumerator;
102 GHashTable *load_mime_list_hash;
103 NautilusFile *load_directory_file;
104 int load_file_count;
107 struct MimeListState {
108 NautilusDirectory *directory;
109 NautilusFile *mime_list_file;
110 GCancellable *cancellable;
111 GFileEnumerator *enumerator;
112 GHashTable *mime_list_hash;
115 struct GetInfoState {
116 NautilusDirectory *directory;
117 GCancellable *cancellable;
120 struct NewFilesState {
121 NautilusDirectory *directory;
122 GCancellable *cancellable;
123 int count;
126 struct DirectoryCountState {
127 NautilusDirectory *directory;
128 NautilusFile *count_file;
129 GCancellable *cancellable;
130 GFileEnumerator *enumerator;
131 int file_count;
134 struct DeepCountState {
135 NautilusDirectory *directory;
136 GCancellable *cancellable;
137 GFileEnumerator *enumerator;
138 GFile *deep_count_location;
139 GList *deep_count_subdirectories;
144 typedef struct {
145 NautilusFile *file; /* Which file, NULL means all. */
146 union {
147 NautilusDirectoryCallback directory;
148 NautilusFileCallback file;
149 } callback;
150 gpointer callback_data;
151 Request request;
152 gboolean active; /* Set to FALSE when the callback is triggered and
153 * scheduled to be called at idle, its still kept
154 * in the list so we can kill it when the file
155 * goes away.
157 } ReadyCallback;
159 typedef struct {
160 NautilusFile *file; /* Which file, NULL means all. */
161 gboolean monitor_hidden_files; /* defines whether "all" includes hidden files */
162 gboolean monitor_backup_files; /* defines whether "all" includes backup files */
163 gconstpointer client;
164 Request request;
165 } Monitor;
167 typedef struct {
168 NautilusDirectory *directory;
169 NautilusInfoProvider *provider;
170 NautilusOperationHandle *handle;
171 NautilusOperationResult result;
172 } InfoProviderResponse;
174 typedef gboolean (* RequestCheck) (const Request *);
175 typedef gboolean (* FileCheck) (NautilusFile *);
177 /* Current number of async. jobs. */
178 static int async_job_count;
179 static GHashTable *waiting_directories;
180 #ifdef DEBUG_ASYNC_JOBS
181 static GHashTable *async_jobs;
182 #endif
184 /* Hide kde trashcan directory */
185 static char *kde_trash_dir_name = NULL;
187 /* Forward declarations for functions that need them. */
188 static void deep_count_load (DeepCountState *state,
189 GFile *location);
190 static gboolean request_is_satisfied (NautilusDirectory *directory,
191 NautilusFile *file,
192 Request *request);
193 static void cancel_loading_attributes (NautilusDirectory *directory,
194 NautilusFileAttributes file_attributes);
195 static void add_all_files_to_work_queue (NautilusDirectory *directory);
196 static void link_info_done (NautilusDirectory *directory,
197 NautilusFile *file,
198 const char *uri,
199 const char *name,
200 const char *icon,
201 gboolean is_launcher);
202 static void move_file_to_low_priority_queue (NautilusDirectory *directory,
203 NautilusFile *file);
204 static void move_file_to_extension_queue (NautilusDirectory *directory,
205 NautilusFile *file);
206 static void nautilus_directory_invalidate_file_attributes (NautilusDirectory *directory,
207 NautilusFileAttributes file_attributes);
209 void
210 nautilus_set_kde_trash_name (const char *trash_dir)
212 g_free (kde_trash_dir_name);
213 kde_trash_dir_name = g_strdup (trash_dir);
216 /* Some helpers for case-insensitive strings.
217 * Move to nautilus-glib-extensions?
220 static gboolean
221 istr_equal (gconstpointer v, gconstpointer v2)
223 return g_ascii_strcasecmp (v, v2) == 0;
226 static guint
227 istr_hash (gconstpointer key)
229 const char *p;
230 guint h;
232 h = 0;
233 for (p = key; *p != '\0'; p++) {
234 h = (h << 5) - h + g_ascii_tolower (*p);
237 return h;
240 static GHashTable *
241 istr_set_new (void)
243 return g_hash_table_new_full (istr_hash, istr_equal, g_free, NULL);
246 static void
247 istr_set_insert (GHashTable *table, const char *istr)
249 char *key;
251 key = g_strdup (istr);
252 g_hash_table_replace (table, key, key);
255 static void
256 add_istr_to_list (gpointer key, gpointer value, gpointer callback_data)
258 GList **list;
260 list = callback_data;
261 *list = g_list_prepend (*list, g_strdup (key));
264 static GList *
265 istr_set_get_as_list (GHashTable *table)
267 GList *list;
269 list = NULL;
270 g_hash_table_foreach (table, add_istr_to_list, &list);
271 return list;
274 static void
275 istr_set_destroy (GHashTable *table)
277 g_hash_table_destroy (table);
280 /* Start a job. This is really just a way of limiting the number of
281 * async. requests that we issue at any given time. Without this, the
282 * number of requests is unbounded.
284 static gboolean
285 async_job_start (NautilusDirectory *directory,
286 const char *job)
288 #ifdef DEBUG_ASYNC_JOBS
289 char *key;
290 #endif
292 #ifdef DEBUG_START_STOP
293 g_message ("starting %s in %p", job, directory->details->location);
294 #endif
296 g_assert (async_job_count >= 0);
297 g_assert (async_job_count <= MAX_ASYNC_JOBS);
299 if (async_job_count >= MAX_ASYNC_JOBS) {
300 if (waiting_directories == NULL) {
301 waiting_directories = eel_g_hash_table_new_free_at_exit
302 (NULL, NULL,
303 "nautilus-directory-async.c: waiting_directories");
306 g_hash_table_insert (waiting_directories,
307 directory,
308 directory);
310 return FALSE;
313 #ifdef DEBUG_ASYNC_JOBS
315 char *uri;
316 if (async_jobs == NULL) {
317 async_jobs = eel_g_hash_table_new_free_at_exit
318 (g_str_hash, g_str_equal,
319 "nautilus-directory-async.c: async_jobs");
321 uri = nautilus_directory_get_uri (directory);
322 key = g_strconcat (uri, ": ", job, NULL);
323 if (g_hash_table_lookup (async_jobs, key) != NULL) {
324 g_warning ("same job twice: %s in %s",
325 job, uri);
327 g_free (uri);
328 g_hash_table_insert (async_jobs, key, directory);
330 #endif
332 async_job_count += 1;
333 return TRUE;
336 /* End a job. */
337 static void
338 async_job_end (NautilusDirectory *directory,
339 const char *job)
341 #ifdef DEBUG_ASYNC_JOBS
342 char *key;
343 gpointer table_key, value;
344 #endif
346 #ifdef DEBUG_START_STOP
347 g_message ("stopping %s in %p", job, directory->details->location);
348 #endif
350 g_assert (async_job_count > 0);
352 #ifdef DEBUG_ASYNC_JOBS
354 char *uri;
355 uri = nautilus_directory_get_uri (directory);
356 g_assert (async_jobs != NULL);
357 key = g_strconcat (uri, ": ", job, NULL);
358 if (!g_hash_table_lookup_extended (async_jobs, key, &table_key, &value)) {
359 g_warning ("ending job we didn't start: %s in %s",
360 job, uri);
361 } else {
362 g_hash_table_remove (async_jobs, key);
363 g_free (table_key);
365 g_free (uri);
366 g_free (key);
368 #endif
370 async_job_count -= 1;
373 /* Helper to get one value from a hash table. */
374 static void
375 get_one_value_callback (gpointer key, gpointer value, gpointer callback_data)
377 gpointer *returned_value;
379 returned_value = callback_data;
380 *returned_value = value;
383 /* return a single value from a hash table. */
384 static gpointer
385 get_one_value (GHashTable *table)
387 gpointer value;
389 value = NULL;
390 if (table != NULL) {
391 g_hash_table_foreach (table, get_one_value_callback, &value);
393 return value;
396 /* Wake up directories that are "blocked" as long as there are job
397 * slots available.
399 static void
400 async_job_wake_up (void)
402 static gboolean already_waking_up = FALSE;
403 gpointer value;
405 g_assert (async_job_count >= 0);
406 g_assert (async_job_count <= MAX_ASYNC_JOBS);
408 if (already_waking_up) {
409 return;
412 already_waking_up = TRUE;
413 while (async_job_count < MAX_ASYNC_JOBS) {
414 value = get_one_value (waiting_directories);
415 if (value == NULL) {
416 break;
418 g_hash_table_remove (waiting_directories, value);
419 nautilus_directory_async_state_changed
420 (NAUTILUS_DIRECTORY (value));
422 already_waking_up = FALSE;
425 static void
426 directory_count_cancel (NautilusDirectory *directory)
428 if (directory->details->count_in_progress != NULL) {
429 g_cancellable_cancel (directory->details->count_in_progress->cancellable);
433 static void
434 deep_count_cancel (NautilusDirectory *directory)
436 if (directory->details->deep_count_in_progress != NULL) {
437 g_assert (NAUTILUS_IS_FILE (directory->details->deep_count_file));
439 g_cancellable_cancel (directory->details->deep_count_in_progress->cancellable);
441 directory->details->deep_count_file->details->deep_counts_status = NAUTILUS_REQUEST_NOT_STARTED;
443 directory->details->deep_count_in_progress->directory = NULL;
444 directory->details->deep_count_in_progress = NULL;
445 directory->details->deep_count_file = NULL;
447 async_job_end (directory, "deep count");
451 static void
452 mime_list_cancel (NautilusDirectory *directory)
454 if (directory->details->mime_list_in_progress != NULL) {
455 g_cancellable_cancel (directory->details->mime_list_in_progress->cancellable);
459 static void
460 top_left_cancel (NautilusDirectory *directory)
462 if (directory->details->top_left_read_state != NULL) {
463 g_cancellable_cancel (directory->details->top_left_read_state->cancellable);
464 directory->details->top_left_read_state->directory = NULL;
465 directory->details->top_left_read_state = NULL;
467 async_job_end (directory, "top left");
471 static void
472 link_info_cancel (NautilusDirectory *directory)
474 if (directory->details->link_info_read_state != NULL) {
475 g_cancellable_cancel (directory->details->link_info_read_state->cancellable);
476 directory->details->link_info_read_state->directory = NULL;
477 directory->details->link_info_read_state = NULL;
478 async_job_end (directory, "link info");
482 static void
483 thumbnail_cancel (NautilusDirectory *directory)
485 if (directory->details->thumbnail_state != NULL) {
486 g_cancellable_cancel (directory->details->thumbnail_state->cancellable);
487 directory->details->thumbnail_state->directory = NULL;
488 directory->details->thumbnail_state = NULL;
489 async_job_end (directory, "thumbnail");
493 static void
494 mount_cancel (NautilusDirectory *directory)
496 if (directory->details->mount_state != NULL) {
497 g_cancellable_cancel (directory->details->mount_state->cancellable);
498 directory->details->mount_state->directory = NULL;
499 directory->details->mount_state = NULL;
500 async_job_end (directory, "mount");
504 static void
505 file_info_cancel (NautilusDirectory *directory)
507 if (directory->details->get_info_in_progress != NULL) {
508 g_cancellable_cancel (directory->details->get_info_in_progress->cancellable);
509 directory->details->get_info_in_progress->directory = NULL;
510 directory->details->get_info_in_progress = NULL;
511 directory->details->get_info_file = NULL;
513 async_job_end (directory, "file info");
517 static void
518 new_files_cancel (NautilusDirectory *directory)
520 GList *l;
521 NewFilesState *state;
523 if (directory->details->new_files_in_progress != NULL) {
524 for (l = directory->details->new_files_in_progress; l != NULL; l = l->next) {
525 state = l->data;
526 g_cancellable_cancel (state->cancellable);
527 state->directory = NULL;
529 g_list_free (directory->details->new_files_in_progress);
530 directory->details->new_files_in_progress = NULL;
534 static int
535 monitor_key_compare (gconstpointer a,
536 gconstpointer data)
538 const Monitor *monitor;
539 const Monitor *compare_monitor;
541 monitor = a;
542 compare_monitor = data;
544 if (monitor->client < compare_monitor->client) {
545 return -1;
547 if (monitor->client > compare_monitor->client) {
548 return +1;
551 if (monitor->file < compare_monitor->file) {
552 return -1;
554 if (monitor->file > compare_monitor->file) {
555 return +1;
558 return 0;
561 static GList *
562 find_monitor (NautilusDirectory *directory,
563 NautilusFile *file,
564 gconstpointer client)
566 Monitor monitor;
568 monitor.client = client;
569 monitor.file = file;
571 return g_list_find_custom (directory->details->monitor_list,
572 &monitor,
573 monitor_key_compare);
576 static void
577 remove_monitor_link (NautilusDirectory *directory,
578 GList *link)
580 if (link != NULL) {
581 directory->details->monitor_list =
582 g_list_remove_link (directory->details->monitor_list, link);
583 g_free (link->data);
584 g_list_free_1 (link);
588 static void
589 remove_monitor (NautilusDirectory *directory,
590 NautilusFile *file,
591 gconstpointer client)
593 remove_monitor_link (directory, find_monitor (directory, file, client));
596 void
597 nautilus_directory_set_up_request (Request *request,
598 NautilusFileAttributes file_attributes)
600 memset (request, 0, sizeof (*request));
602 request->directory_count =
603 (file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT) != 0;
604 request->deep_count =
605 (file_attributes & NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS) != 0;
606 request->mime_list =
607 (file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES) != 0;
608 request->file_info = (file_attributes & NAUTILUS_FILE_ATTRIBUTE_INFO) != 0;
610 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_LINK_INFO) {
611 request->file_info = TRUE;
612 request->link_info = TRUE;
615 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_TOP_LEFT_TEXT) {
616 request->top_left_text = TRUE;
617 request->file_info = TRUE;
620 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT) {
621 request->large_top_left_text = TRUE;
622 request->file_info = TRUE;
625 request->metafile |= (file_attributes & NAUTILUS_FILE_ATTRIBUTE_METADATA) != 0;
626 request->extension_info = (file_attributes & NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO) != 0;
628 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL) {
629 request->thumbnail = TRUE;
630 request->file_info = TRUE;
633 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_MOUNT) {
634 request->mount = TRUE;
635 request->file_info = TRUE;
638 if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO) {
639 request->filesystem_info = TRUE;
643 static void
644 mime_db_changed_callback (GObject *ignore, NautilusDirectory *dir)
646 NautilusFileAttributes attrs;
648 g_assert (dir != NULL);
649 g_assert (dir->details != NULL);
651 attrs = NAUTILUS_FILE_ATTRIBUTE_INFO |
652 NAUTILUS_FILE_ATTRIBUTE_LINK_INFO |
653 NAUTILUS_FILE_ATTRIBUTE_METADATA |
654 NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
656 nautilus_directory_force_reload_internal (dir, attrs);
659 void
660 nautilus_directory_monitor_add_internal (NautilusDirectory *directory,
661 NautilusFile *file,
662 gconstpointer client,
663 gboolean monitor_hidden_files,
664 gboolean monitor_backup_files,
665 NautilusFileAttributes file_attributes,
666 NautilusDirectoryCallback callback,
667 gpointer callback_data)
669 Monitor *monitor;
670 GList *file_list;
672 g_assert (NAUTILUS_IS_DIRECTORY (directory));
674 /* Replace any current monitor for this client/file pair. */
675 remove_monitor (directory, file, client);
677 /* Add the new monitor. */
678 monitor = g_new (Monitor, 1);
679 monitor->file = file;
680 monitor->monitor_hidden_files = monitor_hidden_files;
681 monitor->monitor_backup_files = monitor_backup_files;
682 monitor->client = client;
683 nautilus_directory_set_up_request (&monitor->request, file_attributes);
685 monitor->request.file_list = file == NULL;
686 directory->details->monitor_list =
687 g_list_prepend (directory->details->monitor_list, monitor);
689 if (callback != NULL) {
690 file_list = nautilus_directory_get_file_list (directory);
691 (* callback) (directory, file_list, callback_data);
692 nautilus_file_list_free (file_list);
695 /* Start the "real" monitoring (FAM or whatever). */
696 /* We always monitor the whole directory since in practice
697 * nautilus almost always shows the whole directory anyway, and
698 * it allows us to avoid one file monitor per file in a directory.
700 if (directory->details->monitor == NULL) {
701 directory->details->monitor = nautilus_monitor_directory (directory->details->location);
704 /* We could just call update_metadata_monitors here, but we can be smarter
705 * since we know what monitor was just added.
707 if (monitor->request.metafile && !directory->details->metafile_monitored) {
708 nautilus_directory_register_metadata_monitor (directory);
711 if (monitor->request.file_info && directory->details->mime_db_monitor == 0) {
712 directory->details->mime_db_monitor =
713 g_signal_connect_object (nautilus_signaller_get_current (),
714 "mime_data_changed",
715 G_CALLBACK (mime_db_changed_callback), directory, 0);
718 /* Put the monitor file or all the files on the work queue. */
719 if (file != NULL) {
720 nautilus_directory_add_file_to_work_queue (directory, file);
721 } else {
722 add_all_files_to_work_queue (directory);
725 /* Kick off I/O. */
726 nautilus_directory_async_state_changed (directory);
729 static void
730 set_file_unconfirmed (NautilusFile *file, gboolean unconfirmed)
732 NautilusDirectory *directory;
734 g_assert (NAUTILUS_IS_FILE (file));
735 g_assert (unconfirmed == FALSE || unconfirmed == TRUE);
737 if (file->details->unconfirmed == unconfirmed) {
738 return;
740 file->details->unconfirmed = unconfirmed;
742 directory = file->details->directory;
743 if (unconfirmed) {
744 directory->details->confirmed_file_count--;
745 } else {
746 directory->details->confirmed_file_count++;
750 static gboolean show_hidden_files = TRUE;
751 static gboolean show_backup_files = TRUE;
753 static void
754 show_hidden_files_changed_callback (gpointer callback_data)
756 show_hidden_files = eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES);
759 static void
760 show_backup_files_changed_callback (gpointer callback_data)
762 show_backup_files = eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES);
765 static gboolean
766 should_skip_file (NautilusDirectory *directory, GFileInfo *info)
768 static gboolean show_hidden_files_changed_callback_installed = FALSE;
769 static gboolean show_backup_files_changed_callback_installed = FALSE;
771 /* Add the callback once for the life of our process */
772 if (!show_hidden_files_changed_callback_installed) {
773 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
774 show_hidden_files_changed_callback,
775 NULL);
776 show_hidden_files_changed_callback_installed = TRUE;
778 /* Peek for the first time */
779 show_hidden_files_changed_callback (NULL);
782 /* Add the callback once for the life of our process */
783 if (!show_backup_files_changed_callback_installed) {
784 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES,
785 show_backup_files_changed_callback,
786 NULL);
787 show_backup_files_changed_callback_installed = TRUE;
789 /* Peek for the first time */
790 show_backup_files_changed_callback (NULL);
793 if (!show_hidden_files &&
794 (g_file_info_get_is_hidden (info) ||
795 (directory != NULL && directory->details->hidden_file_hash != NULL &&
796 g_hash_table_lookup (directory->details->hidden_file_hash,
797 g_file_info_get_name (info)) != NULL))) {
798 return TRUE;
801 if (!show_backup_files && g_file_info_get_is_backup (info)) {
802 return TRUE;
805 return FALSE;
808 static gboolean
809 dequeue_pending_idle_callback (gpointer callback_data)
811 NautilusDirectory *directory;
812 GList *pending_file_info;
813 GList *node, *next;
814 NautilusFile *file;
815 GList *changed_files, *added_files;
816 GFileInfo *file_info;
817 const char *mimetype, *name;
818 DirectoryLoadState *dir_load_state;
820 directory = NAUTILUS_DIRECTORY (callback_data);
822 nautilus_directory_ref (directory);
824 directory->details->dequeue_pending_idle_id = 0;
826 /* Handle the files in the order we saw them. */
827 pending_file_info = g_list_reverse (directory->details->pending_file_info);
828 directory->details->pending_file_info = NULL;
830 /* If we are no longer monitoring, then throw away these. */
831 if (!nautilus_directory_is_file_list_monitored (directory)) {
832 nautilus_directory_async_state_changed (directory);
833 goto drain;
836 added_files = NULL;
837 changed_files = NULL;
839 dir_load_state = directory->details->directory_load_in_progress;
841 /* Build a list of NautilusFile objects. */
842 for (node = pending_file_info; node != NULL; node = node->next) {
843 file_info = node->data;
845 name = g_file_info_get_name (file_info);
847 /* Update the file count. */
848 /* FIXME bugzilla.gnome.org 45063: This could count a
849 * file twice if we get it from both load_directory
850 * and from new_files_callback. Not too hard to fix by
851 * moving this into the actual callback instead of
852 * waiting for the idle function.
854 if (dir_load_state &&
855 !should_skip_file (directory, file_info)) {
856 dir_load_state->load_file_count += 1;
858 /* Add the MIME type to the set. */
859 mimetype = g_file_info_get_content_type (file_info);
860 if (mimetype != NULL) {
861 istr_set_insert (dir_load_state->load_mime_list_hash,
862 mimetype);
866 /* check if the file already exists */
867 file = nautilus_directory_find_file_by_name (directory, name);
868 if (file != NULL) {
869 /* file already exists in dir, check if we still need to
870 * emit file_added or if it changed */
871 set_file_unconfirmed (file, FALSE);
872 if (!file->details->is_added) {
873 /* We consider this newly added even if its in the list.
874 * This can happen if someone called nautilus_file_get_by_uri()
875 * on a file in the folder before the add signal was
876 * emitted */
877 nautilus_file_ref (file);
878 file->details->is_added = TRUE;
879 added_files = g_list_prepend (added_files, file);
880 } else if (nautilus_file_update_info (file, file_info)) {
881 /* File changed, notify about the change. */
882 nautilus_file_ref (file);
883 changed_files = g_list_prepend (changed_files, file);
885 } else {
886 /* new file, create a nautilus file object and add it to the list */
887 file = nautilus_file_new_from_info (directory, file_info);
888 nautilus_directory_add_file (directory, file);
889 file->details->is_added = TRUE;
890 added_files = g_list_prepend (added_files, file);
894 /* If we are done loading, then we assume that any unconfirmed
895 * files are gone.
897 if (directory->details->directory_loaded) {
898 for (node = directory->details->file_list;
899 node != NULL; node = next) {
900 file = NAUTILUS_FILE (node->data);
901 next = node->next;
903 if (file->details->unconfirmed) {
904 nautilus_file_ref (file);
905 changed_files = g_list_prepend (changed_files, file);
907 nautilus_file_mark_gone (file);
912 /* Send the changed and added signals. */
913 nautilus_directory_emit_change_signals (directory, changed_files);
914 nautilus_file_list_free (changed_files);
915 nautilus_directory_emit_files_added (directory, added_files);
916 nautilus_file_list_free (added_files);
918 if (directory->details->directory_loaded &&
919 !directory->details->directory_loaded_sent_notification) {
920 /* Send the done_loading signal. */
921 nautilus_directory_emit_done_loading (directory);
923 if (dir_load_state) {
924 file = dir_load_state->load_directory_file;
926 file->details->directory_count = dir_load_state->load_file_count;
927 file->details->directory_count_is_up_to_date = TRUE;
928 file->details->got_directory_count = TRUE;
930 file->details->got_mime_list = TRUE;
931 file->details->mime_list_is_up_to_date = TRUE;
932 eel_g_list_free_deep (file->details->mime_list);
933 file->details->mime_list = istr_set_get_as_list
934 (dir_load_state->load_mime_list_hash);
936 nautilus_file_changed (file);
939 nautilus_directory_async_state_changed (directory);
941 directory->details->directory_loaded_sent_notification = TRUE;
944 drain:
945 eel_g_object_list_free (pending_file_info);
947 /* Get the state machine running again. */
948 nautilus_directory_async_state_changed (directory);
950 nautilus_directory_unref (directory);
951 return FALSE;
954 void
955 nautilus_directory_schedule_dequeue_pending (NautilusDirectory *directory)
957 if (directory->details->dequeue_pending_idle_id == 0) {
958 directory->details->dequeue_pending_idle_id
959 = g_idle_add (dequeue_pending_idle_callback, directory);
963 static void
964 directory_load_one (NautilusDirectory *directory,
965 GFileInfo *info)
967 if (info == NULL) {
968 return;
971 if (g_file_info_get_name (info) == NULL) {
972 char *uri;
974 uri = nautilus_directory_get_uri (directory);
975 g_warning ("Got GFileInfo with NULL name in %s, ignoring. This shouldn't happen unless the gvfs backend is broken.\n", uri);
976 g_free (uri);
978 return;
981 /* Arrange for the "loading" part of the work. */
982 g_object_ref (info);
983 directory->details->pending_file_info
984 = g_list_prepend (directory->details->pending_file_info, info);
985 nautilus_directory_schedule_dequeue_pending (directory);
988 static void
989 directory_load_cancel (NautilusDirectory *directory)
991 NautilusFile *file;
992 DirectoryLoadState *state;
994 state = directory->details->directory_load_in_progress;
995 if (state != NULL) {
996 file = state->load_directory_file;
997 file->details->loading_directory = FALSE;
998 if (file->details->directory != directory) {
999 nautilus_directory_async_state_changed (file->details->directory);
1002 g_cancellable_cancel (state->cancellable);
1003 state->directory = NULL;
1004 directory->details->directory_load_in_progress = NULL;
1005 async_job_end (directory, "file list");
1009 static gboolean
1010 remove_callback (gpointer key, gpointer value, gpointer user_data)
1012 return TRUE;
1015 static void
1016 file_list_cancel (NautilusDirectory *directory)
1018 directory_load_cancel (directory);
1020 if (directory->details->dequeue_pending_idle_id != 0) {
1021 g_source_remove (directory->details->dequeue_pending_idle_id);
1022 directory->details->dequeue_pending_idle_id = 0;
1025 if (directory->details->pending_file_info != NULL) {
1026 eel_g_object_list_free (directory->details->pending_file_info);
1027 directory->details->pending_file_info = NULL;
1030 if (directory->details->hidden_file_hash) {
1031 g_hash_table_foreach_remove (directory->details->hidden_file_hash, remove_callback, NULL);
1035 static void
1036 directory_load_done (NautilusDirectory *directory,
1037 GError *error)
1039 GList *node;
1041 directory->details->directory_loaded = TRUE;
1042 directory->details->directory_loaded_sent_notification = FALSE;
1044 if (error != NULL) {
1045 /* The load did not complete successfully. This means
1046 * we don't know the status of the files in this directory.
1047 * We clear the unconfirmed bit on each file here so that
1048 * they won't be marked "gone" later -- we don't know enough
1049 * about them to know whether they are really gone.
1051 for (node = directory->details->file_list;
1052 node != NULL; node = node->next) {
1053 set_file_unconfirmed (NAUTILUS_FILE (node->data), FALSE);
1056 nautilus_directory_emit_load_error (directory, error);
1059 /* Call the idle function right away. */
1060 if (directory->details->dequeue_pending_idle_id != 0) {
1061 g_source_remove (directory->details->dequeue_pending_idle_id);
1063 dequeue_pending_idle_callback (directory);
1065 directory_load_cancel (directory);
1068 /* This checks if there's a request for the metafile contents. */
1069 static gboolean
1070 is_anyone_waiting_for_metafile (NautilusDirectory *directory)
1072 GList *node;
1073 ReadyCallback *callback;
1074 Monitor *monitor;
1076 for (node = directory->details->call_when_ready_list; node != NULL; node = node->next) {
1077 callback = node->data;
1078 if (callback->request.metafile) {
1079 return TRUE;
1083 for (node = directory->details->monitor_list; node != NULL; node = node->next) {
1084 monitor = node->data;
1085 if (monitor->request.metafile) {
1086 return TRUE;
1090 return FALSE;
1093 static void
1094 update_metadata_monitors (NautilusDirectory *directory)
1096 gboolean is_metadata_monitored;
1098 is_metadata_monitored = is_anyone_waiting_for_metafile (directory);
1100 if (!directory->details->metafile_monitored) {
1101 if (is_metadata_monitored) {
1102 nautilus_directory_register_metadata_monitor (directory);
1104 } else {
1105 if (!is_metadata_monitored) {
1106 nautilus_directory_unregister_metadata_monitor (directory);
1111 void
1112 nautilus_directory_monitor_remove_internal (NautilusDirectory *directory,
1113 NautilusFile *file,
1114 gconstpointer client)
1116 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1117 g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1118 g_assert (client != NULL);
1120 remove_monitor (directory, file, client);
1122 if (directory->details->monitor != NULL
1123 && directory->details->monitor_list == NULL) {
1124 nautilus_monitor_cancel (directory->details->monitor);
1125 directory->details->monitor = NULL;
1128 update_metadata_monitors (directory);
1130 /* XXX - do we need to remove anything from the work queue? */
1132 nautilus_directory_async_state_changed (directory);
1135 FileMonitors *
1136 nautilus_directory_remove_file_monitors (NautilusDirectory *directory,
1137 NautilusFile *file)
1139 GList *result, **list, *node, *next;
1140 Monitor *monitor;
1142 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1143 g_assert (NAUTILUS_IS_FILE (file));
1144 g_assert (file->details->directory == directory);
1146 result = NULL;
1148 list = &directory->details->monitor_list;
1149 for (node = directory->details->monitor_list; node != NULL; node = next) {
1150 next = node->next;
1151 monitor = node->data;
1153 if (monitor->file == file) {
1154 *list = g_list_remove_link (*list, node);
1155 result = g_list_concat (node, result);
1159 update_metadata_monitors (directory);
1161 /* XXX - do we need to remove anything from the work queue? */
1163 nautilus_directory_async_state_changed (directory);
1165 return (FileMonitors *) result;
1168 void
1169 nautilus_directory_add_file_monitors (NautilusDirectory *directory,
1170 NautilusFile *file,
1171 FileMonitors *monitors)
1173 GList **list;
1175 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1176 g_assert (NAUTILUS_IS_FILE (file));
1177 g_assert (file->details->directory == directory);
1179 if (monitors == NULL) {
1180 return;
1183 list = &directory->details->monitor_list;
1184 *list = g_list_concat (*list, (GList *) monitors);
1186 nautilus_directory_add_file_to_work_queue (directory, file);
1188 update_metadata_monitors (directory);
1189 nautilus_directory_async_state_changed (directory);
1192 static int
1193 ready_callback_key_compare (gconstpointer a, gconstpointer b)
1195 const ReadyCallback *callback_a, *callback_b;
1197 callback_a = a;
1198 callback_b = b;
1200 if (callback_a->file < callback_b->file) {
1201 return -1;
1203 if (callback_a->file > callback_b->file) {
1204 return 1;
1206 if (callback_a->file == NULL) {
1207 /* ANSI C doesn't allow ordered compares of function pointers, so we cast them to
1208 * normal pointers to make some overly pedantic compilers (*cough* HP-UX *cough*)
1209 * compile this. Of course, on any compiler where ordered function pointers actually
1210 * break this probably won't work, but at least it will compile on platforms where it
1211 * works, but stupid compilers won't let you use it.
1213 if ((void *)callback_a->callback.directory < (void *)callback_b->callback.directory) {
1214 return -1;
1216 if ((void *)callback_a->callback.directory > (void *)callback_b->callback.directory) {
1217 return 1;
1219 } else {
1220 if ((void *)callback_a->callback.file < (void *)callback_b->callback.file) {
1221 return -1;
1223 if ((void *)callback_a->callback.file > (void *)callback_b->callback.file) {
1224 return 1;
1227 if (callback_a->callback_data < callback_b->callback_data) {
1228 return -1;
1230 if (callback_a->callback_data > callback_b->callback_data) {
1231 return 1;
1233 return 0;
1236 static int
1237 ready_callback_key_compare_only_active (gconstpointer a, gconstpointer b)
1239 const ReadyCallback *callback_a;
1241 callback_a = a;
1243 /* Non active callbacks never match */
1244 if (!callback_a->active) {
1245 return -1;
1248 return ready_callback_key_compare (a, b);
1251 static void
1252 ready_callback_call (NautilusDirectory *directory,
1253 const ReadyCallback *callback)
1255 GList *file_list;
1257 /* Call the callback. */
1258 if (callback->file != NULL) {
1259 if (callback->callback.file) {
1260 (* callback->callback.file) (callback->file,
1261 callback->callback_data);
1263 } else if (callback->callback.directory != NULL) {
1264 if (directory == NULL || !callback->request.file_list) {
1265 file_list = NULL;
1266 } else {
1267 file_list = nautilus_directory_get_file_list (directory);
1270 /* Pass back the file list if the user was waiting for it. */
1271 (* callback->callback.directory) (directory,
1272 file_list,
1273 callback->callback_data);
1275 nautilus_file_list_free (file_list);
1279 void
1280 nautilus_directory_call_when_ready_internal (NautilusDirectory *directory,
1281 NautilusFile *file,
1282 NautilusFileAttributes file_attributes,
1283 gboolean wait_for_file_list,
1284 NautilusDirectoryCallback directory_callback,
1285 NautilusFileCallback file_callback,
1286 gpointer callback_data)
1288 ReadyCallback callback;
1290 g_assert (directory == NULL || NAUTILUS_IS_DIRECTORY (directory));
1291 g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1292 g_assert (file != NULL || directory_callback != NULL);
1294 /* Construct a callback object. */
1295 callback.active = TRUE;
1296 callback.file = file;
1297 if (file == NULL) {
1298 callback.callback.directory = directory_callback;
1299 } else {
1300 callback.callback.file = file_callback;
1302 callback.callback_data = callback_data;
1303 nautilus_directory_set_up_request (&callback.request, file_attributes);
1304 callback.request.file_list = wait_for_file_list;
1306 /* Handle the NULL case. */
1307 if (directory == NULL) {
1308 ready_callback_call (NULL, &callback);
1309 return;
1312 /* Check if the callback is already there. */
1313 if (g_list_find_custom (directory->details->call_when_ready_list,
1314 &callback,
1315 ready_callback_key_compare_only_active) != NULL) {
1316 if (file_callback != NULL && directory_callback != NULL) {
1317 g_warning ("tried to add a new callback while an old one was pending");
1319 /* NULL callback means, just read it. Conflicts are ok. */
1320 return;
1323 /* Add the new callback to the list. */
1324 directory->details->call_when_ready_list = g_list_prepend
1325 (directory->details->call_when_ready_list,
1326 g_memdup (&callback, sizeof (callback)));
1328 /* When we change the ready list we need to sync up metadata monitors.
1329 * We could just call update_metadata_monitors here, but we can be smarter
1330 * since we know what was just added.
1332 if (callback.request.metafile && !directory->details->metafile_monitored) {
1333 nautilus_directory_register_metadata_monitor (directory);
1336 /* Put the callback file or all the files on the work queue. */
1337 if (file != NULL) {
1338 nautilus_directory_add_file_to_work_queue (directory, file);
1339 } else {
1340 add_all_files_to_work_queue (directory);
1343 nautilus_directory_async_state_changed (directory);
1346 gboolean
1347 nautilus_directory_check_if_ready_internal (NautilusDirectory *directory,
1348 NautilusFile *file,
1349 NautilusFileAttributes file_attributes)
1351 Request request;
1353 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1355 nautilus_directory_set_up_request (&request, file_attributes);
1356 return request_is_satisfied (directory, file, &request);
1359 static void
1360 remove_callback_link_keep_data (NautilusDirectory *directory,
1361 GList *link)
1363 directory->details->call_when_ready_list = g_list_remove_link
1364 (directory->details->call_when_ready_list, link);
1365 g_list_free_1 (link);
1368 static void
1369 remove_callback_link (NautilusDirectory *directory,
1370 GList *link)
1372 g_free (link->data);
1373 remove_callback_link_keep_data (directory, link);
1376 void
1377 nautilus_directory_cancel_callback_internal (NautilusDirectory *directory,
1378 NautilusFile *file,
1379 NautilusDirectoryCallback directory_callback,
1380 NautilusFileCallback file_callback,
1381 gpointer callback_data)
1383 ReadyCallback callback;
1384 GList *node;
1386 if (directory == NULL) {
1387 return;
1390 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1391 g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1392 g_assert (file != NULL || directory_callback != NULL);
1393 g_assert (file == NULL || file_callback != NULL);
1395 /* Construct a callback object. */
1396 callback.file = file;
1397 if (file == NULL) {
1398 callback.callback.directory = directory_callback;
1399 } else {
1400 callback.callback.file = file_callback;
1402 callback.callback_data = callback_data;
1404 /* Remove all queued callback from the list (including non-active). */
1405 do {
1406 node = g_list_find_custom (directory->details->call_when_ready_list,
1407 &callback,
1408 ready_callback_key_compare);
1409 if (node != NULL) {
1410 remove_callback_link (directory, node);
1411 /* When we change the ready list we need to sync up metadata monitors. */
1412 update_metadata_monitors (directory);
1414 nautilus_directory_async_state_changed (directory);
1416 } while (node != NULL);
1419 static void
1420 new_files_state_unref (NewFilesState *state)
1422 state->count--;
1424 if (state->count == 0) {
1425 if (state->directory) {
1426 state->directory->details->new_files_in_progress =
1427 g_list_remove (state->directory->details->new_files_in_progress,
1428 state);
1431 g_object_unref (state->cancellable);
1432 g_free (state);
1436 static void
1437 new_files_callback (GObject *source_object,
1438 GAsyncResult *res,
1439 gpointer user_data)
1441 NautilusDirectory *directory;
1442 GFileInfo *info;
1443 NewFilesState *state;
1445 state = user_data;
1447 if (state->directory == NULL) {
1448 /* Operation was cancelled. Bail out */
1449 new_files_state_unref (state);
1450 return;
1453 directory = nautilus_directory_ref (state->directory);
1455 /* Queue up the new file. */
1456 info = g_file_query_info_finish (G_FILE (source_object), res, NULL);
1457 if (info != NULL) {
1458 directory_load_one (directory, info);
1459 g_object_unref (info);
1462 new_files_state_unref (state);
1464 nautilus_directory_unref (directory);
1467 void
1468 nautilus_directory_get_info_for_new_files (NautilusDirectory *directory,
1469 GList *location_list)
1471 NewFilesState *state;
1472 GFile *location;
1473 GList *l;
1475 if (location_list == NULL) {
1476 return;
1479 state = g_new (NewFilesState, 1);
1480 state->directory = directory;
1481 state->cancellable = g_cancellable_new ();
1482 state->count = 0;
1484 for (l = location_list; l != NULL; l = l->next) {
1485 location = l->data;
1487 state->count++;
1489 g_file_query_info_async (location,
1490 NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
1492 G_PRIORITY_DEFAULT,
1493 state->cancellable,
1494 new_files_callback, state);
1497 directory->details->new_files_in_progress
1498 = g_list_prepend (directory->details->new_files_in_progress,
1499 state);
1502 void
1503 nautilus_async_destroying_file (NautilusFile *file)
1505 NautilusDirectory *directory;
1506 gboolean changed;
1507 GList *node, *next;
1508 ReadyCallback *callback;
1509 Monitor *monitor;
1511 directory = file->details->directory;
1512 changed = FALSE;
1514 /* Check for callbacks. */
1515 for (node = directory->details->call_when_ready_list; node != NULL; node = next) {
1516 next = node->next;
1517 callback = node->data;
1519 if (callback->file == file) {
1520 /* Client should have cancelled callback. */
1521 if (callback->active) {
1522 g_warning ("destroyed file has call_when_ready pending");
1524 remove_callback_link (directory, node);
1525 changed = TRUE;
1529 /* Check for monitors. */
1530 for (node = directory->details->monitor_list; node != NULL; node = next) {
1531 next = node->next;
1532 monitor = node->data;
1534 if (monitor->file == file) {
1535 /* Client should have removed monitor earlier. */
1536 g_warning ("destroyed file still being monitored");
1537 remove_monitor_link (directory, node);
1538 changed = TRUE;
1542 /* When we change the monitor or ready list we need to sync up metadata monitors */
1543 if (changed) {
1544 update_metadata_monitors (directory);
1547 /* Check if it's a file that's currently being worked on.
1548 * If so, make that NULL so it gets canceled right away.
1550 if (directory->details->count_in_progress != NULL &&
1551 directory->details->count_in_progress->count_file == file) {
1552 directory->details->count_in_progress->count_file = NULL;
1553 changed = TRUE;
1555 if (directory->details->deep_count_file == file) {
1556 directory->details->deep_count_file = NULL;
1557 changed = TRUE;
1559 if (directory->details->mime_list_in_progress != NULL &&
1560 directory->details->mime_list_in_progress->mime_list_file == file) {
1561 directory->details->mime_list_in_progress->mime_list_file = NULL;
1562 changed = TRUE;
1564 if (directory->details->get_info_file == file) {
1565 directory->details->get_info_file = NULL;
1566 changed = TRUE;
1568 if (directory->details->top_left_read_state != NULL
1569 && directory->details->top_left_read_state->file == file) {
1570 directory->details->top_left_read_state->file = NULL;
1571 changed = TRUE;
1573 if (directory->details->link_info_read_state != NULL &&
1574 directory->details->link_info_read_state->file == file) {
1575 directory->details->link_info_read_state->file = NULL;
1576 changed = TRUE;
1578 if (directory->details->extension_info_file == file) {
1579 directory->details->extension_info_file = NULL;
1580 changed = TRUE;
1583 if (directory->details->thumbnail_state != NULL &&
1584 directory->details->thumbnail_state->file == file) {
1585 directory->details->thumbnail_state->file = NULL;
1586 changed = TRUE;
1589 if (directory->details->mount_state != NULL &&
1590 directory->details->mount_state->file == file) {
1591 directory->details->mount_state->file = NULL;
1592 changed = TRUE;
1595 if (directory->details->filesystem_info_state != NULL &&
1596 directory->details->filesystem_info_state->file == file) {
1597 directory->details->filesystem_info_state->file = NULL;
1598 changed = TRUE;
1601 /* Let the directory take care of the rest. */
1602 if (changed) {
1603 nautilus_directory_async_state_changed (directory);
1607 static gboolean
1608 lacks_directory_count (NautilusFile *file)
1610 return !file->details->directory_count_is_up_to_date
1611 && nautilus_file_should_show_directory_item_count (file);
1614 static gboolean
1615 should_get_directory_count_now (NautilusFile *file)
1617 return lacks_directory_count (file)
1618 && !file->details->loading_directory;
1621 static gboolean
1622 wants_directory_count (const Request *request)
1624 return request->directory_count;
1627 static gboolean
1628 lacks_top_left (NautilusFile *file)
1630 return file->details->file_info_is_up_to_date &&
1631 !file->details->top_left_text_is_up_to_date
1632 && nautilus_file_should_get_top_left_text (file);
1635 static gboolean
1636 wants_top_left (const Request *request)
1638 return request->top_left_text;
1641 static gboolean
1642 lacks_large_top_left (NautilusFile *file)
1644 return file->details->file_info_is_up_to_date &&
1645 (!file->details->top_left_text_is_up_to_date ||
1646 file->details->got_large_top_left_text != file->details->got_top_left_text)
1647 && nautilus_file_should_get_top_left_text (file);
1650 static gboolean
1651 wants_large_top_left (const Request *request)
1653 return request->large_top_left_text;
1656 static gboolean
1657 lacks_info (NautilusFile *file)
1659 return !file->details->file_info_is_up_to_date
1660 && !file->details->is_gone;
1663 static gboolean
1664 lacks_filesystem_info (NautilusFile *file)
1666 return !file->details->filesystem_info_is_up_to_date;
1669 static gboolean
1670 wants_info (const Request *request)
1672 return request->file_info;
1675 static gboolean
1676 wants_filesystem_info (const Request *request)
1678 return request->filesystem_info;
1681 static gboolean
1682 lacks_deep_count (NautilusFile *file)
1684 return file->details->deep_counts_status != NAUTILUS_REQUEST_DONE;
1687 static gboolean
1688 wants_deep_count (const Request *request)
1690 return request->deep_count;
1693 static gboolean
1694 lacks_mime_list (NautilusFile *file)
1696 return !file->details->mime_list_is_up_to_date;
1699 static gboolean
1700 should_get_mime_list (NautilusFile *file)
1702 return lacks_mime_list (file)
1703 && !file->details->loading_directory;
1706 static gboolean
1707 wants_mime_list (const Request *request)
1709 return request->mime_list;
1711 static gboolean
1712 lacks_link_info (NautilusFile *file)
1714 if (file->details->file_info_is_up_to_date &&
1715 !file->details->link_info_is_up_to_date) {
1716 if (nautilus_file_is_nautilus_link (file)) {
1717 return TRUE;
1718 } else {
1719 link_info_done (file->details->directory, file, NULL, NULL, NULL, FALSE);
1720 return FALSE;
1722 } else {
1723 return FALSE;
1727 static gboolean
1728 wants_link_info (const Request *request)
1730 return request->link_info;
1733 static gboolean
1734 lacks_extension_info (NautilusFile *file)
1736 return file->details->pending_info_providers != NULL;
1739 static gboolean
1740 wants_extension_info (const Request *request)
1742 return request->extension_info;
1745 static gboolean
1746 lacks_thumbnail (NautilusFile *file)
1748 return nautilus_file_should_show_thumbnail (file) &&
1749 file->details->thumbnail_path != NULL &&
1750 !file->details->thumbnail_is_up_to_date;
1753 static gboolean
1754 wants_thumbnail (const Request *request)
1756 return request->thumbnail;
1759 static gboolean
1760 lacks_mount (NautilusFile *file)
1762 return (!file->details->mount_is_up_to_date &&
1764 /* Unix mountpoint, could be a GMount */
1765 file->details->is_mountpoint ||
1767 /* The toplevel directory of something */
1768 (file->details->type == G_FILE_TYPE_DIRECTORY &&
1769 nautilus_file_is_self_owned (file)) ||
1771 /* Mountable with a target_uri, could be a mountpoint */
1772 (file->details->type == G_FILE_TYPE_MOUNTABLE &&
1773 file->details->activation_location != NULL)
1779 static gboolean
1780 wants_mount (const Request *request)
1782 return request->mount;
1785 static gboolean
1786 has_problem (NautilusDirectory *directory, NautilusFile *file, FileCheck problem)
1788 GList *node;
1790 if (file != NULL) {
1791 return (* problem) (file);
1794 for (node = directory->details->file_list; node != NULL; node = node->next) {
1795 if ((* problem) (node->data)) {
1796 return TRUE;
1800 return FALSE;
1803 static gboolean
1804 request_is_satisfied (NautilusDirectory *directory,
1805 NautilusFile *file,
1806 Request *request)
1808 if (request->metafile && !nautilus_directory_is_metadata_read (directory)) {
1809 return FALSE;
1812 if (request->file_list && !(directory->details->directory_loaded &&
1813 directory->details->directory_loaded_sent_notification)) {
1814 return FALSE;
1817 if (request->directory_count) {
1818 if (has_problem (directory, file, lacks_directory_count)) {
1819 return FALSE;
1823 if (request->file_info) {
1824 if (has_problem (directory, file, lacks_info)) {
1825 return FALSE;
1829 if (request->filesystem_info) {
1830 if (has_problem (directory, file, lacks_filesystem_info)) {
1831 return FALSE;
1835 if (request->top_left_text) {
1836 if (has_problem (directory, file, lacks_top_left)) {
1837 return FALSE;
1841 if (request->large_top_left_text) {
1842 if (has_problem (directory, file, lacks_large_top_left)) {
1843 return FALSE;
1847 if (request->deep_count) {
1848 if (has_problem (directory, file, lacks_deep_count)) {
1849 return FALSE;
1853 if (request->thumbnail) {
1854 if (has_problem (directory, file, lacks_thumbnail)) {
1855 return FALSE;
1859 if (request->mount) {
1860 if (has_problem (directory, file, lacks_mount)) {
1861 return FALSE;
1865 if (request->mime_list) {
1866 if (has_problem (directory, file, lacks_mime_list)) {
1867 return FALSE;
1871 if (request->link_info) {
1872 if (has_problem (directory, file, lacks_link_info)) {
1873 return FALSE;
1877 return TRUE;
1880 static gboolean
1881 call_ready_callbacks_at_idle (gpointer callback_data)
1883 NautilusDirectory *directory;
1884 GList *node, *next;
1885 ReadyCallback *callback;
1887 directory = NAUTILUS_DIRECTORY (callback_data);
1888 directory->details->call_ready_idle_id = 0;
1890 nautilus_directory_ref (directory);
1892 callback = NULL;
1893 while (1) {
1894 /* Check if any callbacks are non-active and call them if they are. */
1895 for (node = directory->details->call_when_ready_list;
1896 node != NULL; node = next) {
1897 next = node->next;
1898 callback = node->data;
1899 if (!callback->active) {
1900 /* Non-active, remove and call */
1901 break;
1904 if (node == NULL) {
1905 break;
1908 /* Callbacks are one-shots, so remove it now. */
1909 remove_callback_link_keep_data (directory, node);
1911 /* Call the callback. */
1912 ready_callback_call (directory, callback);
1913 g_free (callback);
1916 /* When we change the ready list we need to sync up metadata monitors. */
1917 update_metadata_monitors (directory);
1919 nautilus_directory_async_state_changed (directory);
1921 nautilus_directory_unref (directory);
1923 return FALSE;
1926 static void
1927 schedule_call_ready_callbacks (NautilusDirectory *directory)
1929 if (directory->details->call_ready_idle_id == 0) {
1930 directory->details->call_ready_idle_id
1931 = g_idle_add (call_ready_callbacks_at_idle, directory);
1935 /* Marks all callbacks that are ready as non-active and
1936 * calls them at idle time, unless they are removed
1937 * before then */
1938 static gboolean
1939 call_ready_callbacks (NautilusDirectory *directory)
1941 gboolean found_any;
1942 GList *node, *next;
1943 ReadyCallback *callback;
1945 found_any = FALSE;
1947 /* Check if any callbacks are satisifed and mark them for call them if they are. */
1948 for (node = directory->details->call_when_ready_list;
1949 node != NULL; node = next) {
1950 next = node->next;
1951 callback = node->data;
1952 if (callback->active &&
1953 request_is_satisfied (directory, callback->file, &callback->request)) {
1954 callback->active = FALSE;
1955 found_any = TRUE;
1959 if (found_any) {
1960 schedule_call_ready_callbacks (directory);
1963 return found_any;
1966 /* This checks if there's a request for monitoring the file list. */
1967 gboolean
1968 nautilus_directory_is_anyone_monitoring_file_list (NautilusDirectory *directory)
1970 GList *node;
1971 ReadyCallback *callback;
1972 Monitor *monitor;
1974 for (node = directory->details->call_when_ready_list;
1975 node != NULL; node = node->next) {
1976 callback = node->data;
1977 if (callback->request.file_list) {
1978 return TRUE;
1982 for (node = directory->details->monitor_list;
1983 node != NULL; node = node->next) {
1984 monitor = node->data;
1985 if (monitor->request.file_list) {
1986 return TRUE;
1990 return FALSE;
1993 /* This checks if the file list being monitored. */
1994 gboolean
1995 nautilus_directory_is_file_list_monitored (NautilusDirectory *directory)
1997 return directory->details->file_list_monitored;
2000 static void
2001 mark_all_files_unconfirmed (NautilusDirectory *directory)
2003 GList *node;
2004 NautilusFile *file;
2006 for (node = directory->details->file_list; node != NULL; node = node->next) {
2007 file = node->data;
2008 set_file_unconfirmed (file, TRUE);
2012 static void
2013 read_dot_hidden_file (NautilusDirectory *directory)
2015 gsize file_size;
2016 char *file_contents;
2017 GFile *child;
2018 GFileInfo *info;
2019 GFileType type;
2020 int i;
2023 /* FIXME: We only support .hidden on file: uri's for the moment.
2024 * Need to figure out if we should do this async or sync to extend
2025 * it to all types of uris.
2027 if (directory->details->location == NULL ||
2028 !g_file_is_native (directory->details->location)) {
2029 return;
2032 child = g_file_get_child (directory->details->location, ".hidden");
2034 type = G_FILE_TYPE_UNKNOWN;
2036 info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL);
2037 if (info != NULL) {
2038 type = g_file_info_get_file_type (info);
2039 g_object_unref (info);
2042 if (type != G_FILE_TYPE_REGULAR) {
2043 g_object_unref (child);
2044 return;
2047 if (!g_file_load_contents (child, NULL, &file_contents, &file_size, NULL, NULL)) {
2048 g_object_unref (child);
2049 return;
2052 g_object_unref (child);
2054 if (directory->details->hidden_file_hash == NULL) {
2055 directory->details->hidden_file_hash =
2056 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2059 /* Now parse the data */
2060 i = 0;
2061 while (i < file_size) {
2062 int start;
2064 start = i;
2065 while (i < file_size && file_contents[i] != '\n') {
2066 i++;
2069 if (i > start) {
2070 char *hidden_filename;
2072 hidden_filename = g_strndup (file_contents + start, i - start);
2073 g_hash_table_insert (directory->details->hidden_file_hash,
2074 hidden_filename, hidden_filename);
2077 i++;
2081 g_free (file_contents);
2084 static void
2085 directory_load_state_free (DirectoryLoadState *state)
2087 if (state->enumerator) {
2088 if (!g_file_enumerator_is_closed (state->enumerator)) {
2089 g_file_enumerator_close_async (state->enumerator,
2090 0, NULL, NULL, NULL);
2092 g_object_unref (state->enumerator);
2095 if (state->load_mime_list_hash != NULL) {
2096 istr_set_destroy (state->load_mime_list_hash);
2098 nautilus_file_unref (state->load_directory_file);
2099 g_object_unref (state->cancellable);
2100 g_free (state);
2103 static void
2104 more_files_callback (GObject *source_object,
2105 GAsyncResult *res,
2106 gpointer user_data)
2108 DirectoryLoadState *state;
2109 NautilusDirectory *directory;
2110 GError *error;
2111 GList *files, *l;
2112 GFileInfo *info;
2114 state = user_data;
2116 if (state->directory == NULL) {
2117 /* Operation was cancelled. Bail out */
2118 directory_load_state_free (state);
2119 return;
2122 directory = nautilus_directory_ref (state->directory);
2124 g_assert (directory->details->directory_load_in_progress != NULL);
2125 g_assert (directory->details->directory_load_in_progress == state);
2127 error = NULL;
2128 files = g_file_enumerator_next_files_finish (state->enumerator,
2129 res, &error);
2131 for (l = files; l != NULL; l = l->next) {
2132 info = l->data;
2133 directory_load_one (directory, info);
2134 g_object_unref (info);
2137 if (nautilus_directory_file_list_length_reached (directory) ||
2138 files == NULL) {
2139 directory_load_done (directory, error);
2140 directory_load_state_free (state);
2141 } else {
2142 g_file_enumerator_next_files_async (state->enumerator,
2143 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2144 G_PRIORITY_DEFAULT,
2145 state->cancellable,
2146 more_files_callback,
2147 state);
2150 nautilus_directory_unref (directory);
2152 if (error) {
2153 g_error_free (error);
2156 g_list_free (files);
2159 static void
2160 enumerate_children_callback (GObject *source_object,
2161 GAsyncResult *res,
2162 gpointer user_data)
2164 DirectoryLoadState *state;
2165 GFileEnumerator *enumerator;
2166 GError *error;
2168 state = user_data;
2170 if (state->directory == NULL) {
2171 /* Operation was cancelled. Bail out */
2172 directory_load_state_free (state);
2173 return;
2176 error = NULL;
2177 enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
2178 res, &error);
2180 if (enumerator == NULL) {
2181 directory_load_done (state->directory, error);
2182 g_error_free (error);
2183 directory_load_state_free (state);
2184 return;
2185 } else {
2186 state->enumerator = enumerator;
2187 g_file_enumerator_next_files_async (state->enumerator,
2188 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2189 G_PRIORITY_DEFAULT,
2190 state->cancellable,
2191 more_files_callback,
2192 state);
2197 /* Start monitoring the file list if it isn't already. */
2198 static void
2199 start_monitoring_file_list (NautilusDirectory *directory)
2201 DirectoryLoadState *state;
2203 if (!directory->details->file_list_monitored) {
2204 g_assert (!directory->details->directory_load_in_progress);
2205 directory->details->file_list_monitored = TRUE;
2206 nautilus_file_list_ref (directory->details->file_list);
2209 if (directory->details->directory_loaded ||
2210 directory->details->directory_load_in_progress != NULL) {
2211 return;
2214 if (!async_job_start (directory, "file list")) {
2215 return;
2218 mark_all_files_unconfirmed (directory);
2220 state = g_new0 (DirectoryLoadState, 1);
2221 state->directory = directory;
2222 state->cancellable = g_cancellable_new ();
2223 state->load_mime_list_hash = istr_set_new ();
2224 state->load_file_count = 0;
2226 g_assert (directory->details->location != NULL);
2227 state->load_directory_file =
2228 nautilus_directory_get_corresponding_file (directory);
2229 state->load_directory_file->details->loading_directory = TRUE;
2231 read_dot_hidden_file (directory);
2233 /* Hack to work around kde trash dir */
2234 if (kde_trash_dir_name != NULL && nautilus_directory_is_desktop_directory (directory)) {
2235 char *fn;
2237 if (directory->details->hidden_file_hash == NULL) {
2238 directory->details->hidden_file_hash =
2239 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2242 fn = g_strdup (kde_trash_dir_name);
2243 g_hash_table_insert (directory->details->hidden_file_hash,
2244 fn, fn);
2248 #ifdef DEBUG_LOAD_DIRECTORY
2249 g_message ("load_directory called to monitor file list of %p", directory->details->location);
2250 #endif
2252 directory->details->directory_load_in_progress = state;
2254 g_file_enumerate_children_async (directory->details->location,
2255 NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
2256 0, /* flags */
2257 G_PRIORITY_DEFAULT, /* prio */
2258 state->cancellable,
2259 enumerate_children_callback,
2260 state);
2263 /* Stop monitoring the file list if it is being monitored. */
2264 void
2265 nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory)
2267 if (!directory->details->file_list_monitored) {
2268 g_assert (directory->details->directory_load_in_progress == NULL);
2269 return;
2272 directory->details->file_list_monitored = FALSE;
2273 file_list_cancel (directory);
2274 nautilus_file_list_unref (directory->details->file_list);
2275 directory->details->directory_loaded = FALSE;
2278 static void
2279 file_list_start_or_stop (NautilusDirectory *directory)
2281 if (nautilus_directory_is_anyone_monitoring_file_list (directory)) {
2282 start_monitoring_file_list (directory);
2283 } else {
2284 nautilus_directory_stop_monitoring_file_list (directory);
2288 void
2289 nautilus_file_invalidate_count_and_mime_list (NautilusFile *file)
2291 NautilusFileAttributes attributes;
2293 attributes = NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
2294 NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
2296 nautilus_file_invalidate_attributes (file, attributes);
2300 /* Reset count and mime list. Invalidating deep counts is handled by
2301 * itself elsewhere because it's a relatively heavyweight and
2302 * special-purpose operation (see bug 5863). Also, the shallow count
2303 * needs to be refreshed when filtering changes, but the deep count
2304 * deliberately does not take filtering into account.
2306 void
2307 nautilus_directory_invalidate_count_and_mime_list (NautilusDirectory *directory)
2309 NautilusFile *file;
2311 file = nautilus_directory_get_existing_corresponding_file (directory);
2312 if (file != NULL) {
2313 nautilus_file_invalidate_count_and_mime_list (file);
2316 nautilus_file_unref (file);
2319 static void
2320 nautilus_directory_invalidate_file_attributes (NautilusDirectory *directory,
2321 NautilusFileAttributes file_attributes)
2323 GList *node;
2325 cancel_loading_attributes (directory, file_attributes);
2327 for (node = directory->details->file_list; node != NULL; node = node->next) {
2328 nautilus_file_invalidate_attributes_internal (NAUTILUS_FILE (node->data),
2329 file_attributes);
2332 if (directory->details->as_file != NULL) {
2333 nautilus_file_invalidate_attributes_internal (directory->details->as_file,
2334 file_attributes);
2338 void
2339 nautilus_directory_force_reload_internal (NautilusDirectory *directory,
2340 NautilusFileAttributes file_attributes)
2342 /* invalidate attributes that are getting reloaded for all files */
2343 nautilus_directory_invalidate_file_attributes (directory, file_attributes);
2345 /* Start a new directory load. */
2346 file_list_cancel (directory);
2347 directory->details->directory_loaded = FALSE;
2349 /* Start a new directory count. */
2350 nautilus_directory_invalidate_count_and_mime_list (directory);
2352 add_all_files_to_work_queue (directory);
2353 nautilus_directory_async_state_changed (directory);
2356 static gboolean
2357 monitor_includes_file (const Monitor *monitor,
2358 NautilusFile *file)
2360 if (monitor->file == file) {
2361 return TRUE;
2363 if (monitor->file != NULL) {
2364 return FALSE;
2366 if (file == file->details->directory->details->as_file) {
2367 return FALSE;
2369 return nautilus_file_should_show (file,
2370 monitor->monitor_hidden_files,
2371 monitor->monitor_backup_files);
2374 static gboolean
2375 is_needy (NautilusFile *file,
2376 FileCheck check_missing,
2377 RequestCheck check_wanted)
2379 NautilusDirectory *directory;
2380 GList *node;
2381 ReadyCallback *callback;
2382 Monitor *monitor;
2384 if (!(* check_missing) (file)) {
2385 return FALSE;
2388 directory = file->details->directory;
2389 for (node = directory->details->call_when_ready_list;
2390 node != NULL; node = node->next) {
2391 callback = node->data;
2392 if (callback->active &&
2393 (* check_wanted) (&callback->request)) {
2394 if (callback->file == file) {
2395 return TRUE;
2397 if (callback->file == NULL
2398 && file != directory->details->as_file) {
2399 return TRUE;
2403 for (node = directory->details->monitor_list;
2404 node != NULL; node = node->next) {
2405 monitor = node->data;
2406 if ((* check_wanted) (&monitor->request)) {
2407 if (monitor_includes_file (monitor, file)) {
2408 return TRUE;
2412 return FALSE;
2415 static void
2416 directory_count_stop (NautilusDirectory *directory)
2418 NautilusFile *file;
2420 if (directory->details->count_in_progress != NULL) {
2421 file = directory->details->count_in_progress->count_file;
2422 if (file != NULL) {
2423 g_assert (NAUTILUS_IS_FILE (file));
2424 g_assert (file->details->directory == directory);
2425 if (is_needy (file,
2426 should_get_directory_count_now,
2427 wants_directory_count)) {
2428 return;
2432 /* The count is not wanted, so stop it. */
2433 directory_count_cancel (directory);
2437 static guint
2438 count_non_skipped_files (GList *list)
2440 guint count;
2441 GList *node;
2442 GFileInfo *info;
2444 count = 0;
2445 for (node = list; node != NULL; node = node->next) {
2446 info = node->data;
2447 if (!should_skip_file (NULL, info)) {
2448 count += 1;
2451 return count;
2454 static void
2455 count_children_done (NautilusDirectory *directory,
2456 NautilusFile *count_file,
2457 gboolean succeeded,
2458 int count)
2460 g_assert (NAUTILUS_IS_FILE (count_file));
2462 count_file->details->directory_count_is_up_to_date = TRUE;
2464 /* Record either a failure or success. */
2465 if (!succeeded) {
2466 count_file->details->directory_count_failed = TRUE;
2467 count_file->details->got_directory_count = FALSE;
2468 count_file->details->directory_count = 0;
2469 } else {
2470 count_file->details->directory_count_failed = FALSE;
2471 count_file->details->got_directory_count = TRUE;
2472 count_file->details->directory_count = count;
2474 directory->details->count_in_progress = NULL;
2476 /* Send file-changed even if count failed, so interested parties can
2477 * distinguish between unknowable and not-yet-known cases.
2479 nautilus_file_changed (count_file);
2481 /* Start up the next one. */
2482 async_job_end (directory, "directory count");
2483 nautilus_directory_async_state_changed (directory);
2486 static void
2487 directory_count_state_free (DirectoryCountState *state)
2489 if (state->enumerator) {
2490 if (!g_file_enumerator_is_closed (state->enumerator)) {
2491 g_file_enumerator_close_async (state->enumerator,
2492 0, NULL, NULL, NULL);
2494 g_object_unref (state->enumerator);
2496 g_object_unref (state->cancellable);
2497 nautilus_directory_unref (state->directory);
2498 g_free (state);
2501 static void
2502 count_more_files_callback (GObject *source_object,
2503 GAsyncResult *res,
2504 gpointer user_data)
2506 DirectoryCountState *state;
2507 NautilusDirectory *directory;
2508 GError *error;
2509 GList *files;
2511 state = user_data;
2512 directory = state->directory;
2514 if (g_cancellable_is_cancelled (state->cancellable)) {
2515 /* Operation was cancelled. Bail out */
2516 directory->details->count_in_progress = NULL;
2518 async_job_end (directory, "directory count");
2519 nautilus_directory_async_state_changed (directory);
2521 directory_count_state_free (state);
2523 return;
2526 g_assert (directory->details->count_in_progress != NULL);
2527 g_assert (directory->details->count_in_progress == state);
2529 error = NULL;
2530 files = g_file_enumerator_next_files_finish (state->enumerator,
2531 res, &error);
2533 state->file_count += count_non_skipped_files (files);
2535 if (files == NULL) {
2536 count_children_done (directory, state->count_file,
2537 TRUE, state->file_count);
2538 directory_count_state_free (state);
2539 } else {
2540 g_file_enumerator_next_files_async (state->enumerator,
2541 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2542 G_PRIORITY_DEFAULT,
2543 state->cancellable,
2544 count_more_files_callback,
2545 state);
2548 eel_g_object_list_free (files);
2550 if (error) {
2551 g_error_free (error);
2555 static void
2556 count_children_callback (GObject *source_object,
2557 GAsyncResult *res,
2558 gpointer user_data)
2560 DirectoryCountState *state;
2561 GFileEnumerator *enumerator;
2562 NautilusDirectory *directory;
2563 GError *error;
2565 state = user_data;
2567 if (g_cancellable_is_cancelled (state->cancellable)) {
2568 /* Operation was cancelled. Bail out */
2569 directory = state->directory;
2570 directory->details->count_in_progress = NULL;
2572 async_job_end (directory, "directory count");
2573 nautilus_directory_async_state_changed (directory);
2575 directory_count_state_free (state);
2577 return;
2580 error = NULL;
2581 enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
2582 res, &error);
2584 if (enumerator == NULL) {
2585 count_children_done (state->directory,
2586 state->count_file,
2587 FALSE, 0);
2588 g_error_free (error);
2589 directory_count_state_free (state);
2590 return;
2591 } else {
2592 state->enumerator = enumerator;
2593 g_file_enumerator_next_files_async (state->enumerator,
2594 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2595 G_PRIORITY_DEFAULT,
2596 state->cancellable,
2597 count_more_files_callback,
2598 state);
2602 static void
2603 directory_count_start (NautilusDirectory *directory,
2604 NautilusFile *file,
2605 gboolean *doing_io)
2607 DirectoryCountState *state;
2608 GFile *location;
2610 if (directory->details->count_in_progress != NULL) {
2611 *doing_io = TRUE;
2612 return;
2615 if (!is_needy (file,
2616 should_get_directory_count_now,
2617 wants_directory_count)) {
2618 return;
2620 *doing_io = TRUE;
2622 if (!nautilus_file_is_directory (file)) {
2623 file->details->directory_count_is_up_to_date = TRUE;
2624 file->details->directory_count_failed = FALSE;
2625 file->details->got_directory_count = FALSE;
2627 nautilus_directory_async_state_changed (directory);
2628 return;
2631 if (!async_job_start (directory, "directory count")) {
2632 return;
2635 /* Start counting. */
2636 state = g_new0 (DirectoryCountState, 1);
2637 state->count_file = file;
2638 state->directory = nautilus_directory_ref (directory);
2639 state->cancellable = g_cancellable_new ();
2641 directory->details->count_in_progress = state;
2643 location = nautilus_file_get_location (file);
2644 #ifdef DEBUG_LOAD_DIRECTORY
2646 char *uri;
2647 uri = g_file_get_uri (location);
2648 g_message ("load_directory called to get shallow file count for %s", uri);
2649 g_free (uri);
2651 #endif
2653 g_file_enumerate_children_async (location,
2654 G_FILE_ATTRIBUTE_STANDARD_NAME ","
2655 G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
2656 G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
2657 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
2658 G_PRIORITY_DEFAULT, /* prio */
2659 state->cancellable,
2660 count_children_callback,
2661 state);
2662 g_object_unref (location);
2665 static void
2666 deep_count_one (DeepCountState *state,
2667 GFileInfo *info)
2669 NautilusFile *file;
2670 GFile *subdir;
2672 if (should_skip_file (NULL, info)) {
2673 return;
2676 file = state->directory->details->deep_count_file;
2678 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2679 /* Count the directory. */
2680 file->details->deep_directory_count += 1;
2682 /* Record the fact that we have to descend into this directory. */
2684 subdir = g_file_get_child (state->deep_count_location, g_file_info_get_name (info));
2685 state->deep_count_subdirectories = g_list_prepend
2686 (state->deep_count_subdirectories, subdir);
2687 } else {
2688 /* Even non-regular files count as files. */
2689 file->details->deep_file_count += 1;
2692 /* Count the size. */
2693 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
2694 file->details->deep_size += g_file_info_get_size (info);
2698 static void
2699 deep_count_state_free (DeepCountState *state)
2701 if (state->enumerator) {
2702 if (!g_file_enumerator_is_closed (state->enumerator)) {
2703 g_file_enumerator_close_async (state->enumerator,
2704 0, NULL, NULL, NULL);
2706 g_object_unref (state->enumerator);
2708 g_object_unref (state->cancellable);
2709 if (state->deep_count_location) {
2710 g_object_unref (state->deep_count_location);
2712 eel_g_object_list_free (state->deep_count_subdirectories);
2713 g_free (state);
2716 static void
2717 deep_count_next_dir (DeepCountState *state)
2719 GFile *location;
2720 NautilusFile *file;
2721 NautilusDirectory *directory;
2722 gboolean done;
2724 directory = state->directory;
2726 g_object_unref (state->deep_count_location);
2727 state->deep_count_location = NULL;
2729 done = FALSE;
2730 file = directory->details->deep_count_file;
2732 if (state->deep_count_subdirectories != NULL) {
2733 /* Work on a new directory. */
2734 location = state->deep_count_subdirectories->data;
2735 state->deep_count_subdirectories = g_list_remove
2736 (state->deep_count_subdirectories, location);
2737 deep_count_load (state, location);
2738 g_object_unref (location);
2739 } else {
2740 file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
2741 directory->details->deep_count_file = NULL;
2742 directory->details->deep_count_in_progress = NULL;
2743 deep_count_state_free (state);
2744 done = TRUE;
2747 nautilus_file_updated_deep_count_in_progress (file);
2749 if (done) {
2750 nautilus_file_changed (file);
2751 async_job_end (directory, "deep count");
2752 nautilus_directory_async_state_changed (directory);
2756 static void
2757 deep_count_more_files_callback (GObject *source_object,
2758 GAsyncResult *res,
2759 gpointer user_data)
2761 DeepCountState *state;
2762 NautilusDirectory *directory;
2763 GList *files, *l;
2764 GFileInfo *info;
2766 state = user_data;
2768 if (state->directory == NULL) {
2769 /* Operation was cancelled. Bail out */
2770 deep_count_state_free (state);
2771 return;
2774 directory = nautilus_directory_ref (state->directory);
2776 g_assert (directory->details->deep_count_in_progress != NULL);
2777 g_assert (directory->details->deep_count_in_progress == state);
2779 files = g_file_enumerator_next_files_finish (state->enumerator,
2780 res, NULL);
2782 for (l = files; l != NULL; l = l->next) {
2783 info = l->data;
2784 deep_count_one (state, info);
2785 g_object_unref (info);
2788 if (files == NULL) {
2789 g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
2790 g_object_unref (state->enumerator);
2791 state->enumerator = NULL;
2793 deep_count_next_dir (state);
2794 } else {
2795 g_file_enumerator_next_files_async (state->enumerator,
2796 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2797 G_PRIORITY_LOW,
2798 state->cancellable,
2799 deep_count_more_files_callback,
2800 state);
2803 g_list_free (files);
2805 nautilus_directory_unref (directory);
2808 static void
2809 deep_count_callback (GObject *source_object,
2810 GAsyncResult *res,
2811 gpointer user_data)
2813 DeepCountState *state;
2814 GFileEnumerator *enumerator;
2815 NautilusFile *file;
2817 state = user_data;
2819 if (state->directory == NULL) {
2820 /* Operation was cancelled. Bail out */
2821 deep_count_state_free (state);
2822 return;
2825 file = state->directory->details->deep_count_file;
2827 enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
2829 if (enumerator == NULL) {
2830 file->details->deep_unreadable_count += 1;
2832 deep_count_next_dir (state);
2833 } else {
2834 state->enumerator = enumerator;
2835 g_file_enumerator_next_files_async (state->enumerator,
2836 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2837 G_PRIORITY_LOW,
2838 state->cancellable,
2839 deep_count_more_files_callback,
2840 state);
2845 static void
2846 deep_count_load (DeepCountState *state, GFile *location)
2848 NautilusDirectory *directory;
2850 directory = state->directory;
2851 state->deep_count_location = g_object_ref (location);
2853 #ifdef DEBUG_LOAD_DIRECTORY
2854 g_message ("load_directory called to get deep file count for %p", location);
2855 #endif
2856 g_file_enumerate_children_async (state->deep_count_location,
2857 G_FILE_ATTRIBUTE_STANDARD_NAME ","
2858 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2859 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
2860 G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
2861 G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
2862 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
2863 G_PRIORITY_LOW, /* prio */
2864 state->cancellable,
2865 deep_count_callback,
2866 state);
2869 static void
2870 deep_count_stop (NautilusDirectory *directory)
2872 NautilusFile *file;
2874 if (directory->details->deep_count_in_progress != NULL) {
2875 file = directory->details->deep_count_file;
2876 if (file != NULL) {
2877 g_assert (NAUTILUS_IS_FILE (file));
2878 g_assert (file->details->directory == directory);
2879 if (is_needy (file,
2880 lacks_deep_count,
2881 wants_deep_count)) {
2882 return;
2886 /* The count is not wanted, so stop it. */
2887 deep_count_cancel (directory);
2891 static void
2892 deep_count_start (NautilusDirectory *directory,
2893 NautilusFile *file,
2894 gboolean *doing_io)
2896 GFile *location;
2897 DeepCountState *state;
2899 if (directory->details->deep_count_in_progress != NULL) {
2900 *doing_io = TRUE;
2901 return;
2904 if (!is_needy (file,
2905 lacks_deep_count,
2906 wants_deep_count)) {
2907 return;
2909 *doing_io = TRUE;
2911 if (!nautilus_file_is_directory (file)) {
2912 file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
2914 nautilus_directory_async_state_changed (directory);
2915 return;
2918 if (!async_job_start (directory, "deep count")) {
2919 return;
2922 /* Start counting. */
2923 file->details->deep_counts_status = NAUTILUS_REQUEST_IN_PROGRESS;
2924 file->details->deep_directory_count = 0;
2925 file->details->deep_file_count = 0;
2926 file->details->deep_unreadable_count = 0;
2927 file->details->deep_size = 0;
2928 directory->details->deep_count_file = file;
2930 state = g_new0 (DeepCountState, 1);
2931 state->directory = directory;
2932 state->cancellable = g_cancellable_new ();
2934 directory->details->deep_count_in_progress = state;
2936 location = nautilus_file_get_location (file);
2937 deep_count_load (state, location);
2938 g_object_unref (location);
2941 static void
2942 mime_list_stop (NautilusDirectory *directory)
2944 NautilusFile *file;
2946 if (directory->details->mime_list_in_progress != NULL) {
2947 file = directory->details->mime_list_in_progress->mime_list_file;
2948 if (file != NULL) {
2949 g_assert (NAUTILUS_IS_FILE (file));
2950 g_assert (file->details->directory == directory);
2951 if (is_needy (file,
2952 should_get_mime_list,
2953 wants_mime_list)) {
2954 return;
2958 /* The count is not wanted, so stop it. */
2959 mime_list_cancel (directory);
2963 static void
2964 mime_list_state_free (MimeListState *state)
2966 if (state->enumerator) {
2967 if (!g_file_enumerator_is_closed (state->enumerator)) {
2968 g_file_enumerator_close_async (state->enumerator,
2969 0, NULL, NULL, NULL);
2971 g_object_unref (state->enumerator);
2973 g_object_unref (state->cancellable);
2974 istr_set_destroy (state->mime_list_hash);
2975 nautilus_directory_unref (state->directory);
2976 g_free (state);
2980 static void
2981 mime_list_done (MimeListState *state, gboolean success)
2983 NautilusFile *file;
2984 NautilusDirectory *directory;
2986 directory = state->directory;
2987 g_assert (directory != NULL);
2989 file = state->mime_list_file;
2991 file->details->mime_list_is_up_to_date = TRUE;
2992 eel_g_list_free_deep (file->details->mime_list);
2993 if (success) {
2994 file->details->mime_list_failed = TRUE;
2995 file->details->mime_list = NULL;
2996 } else {
2997 file->details->got_mime_list = TRUE;
2998 file->details->mime_list = istr_set_get_as_list (state->mime_list_hash);
3000 directory->details->mime_list_in_progress = NULL;
3002 /* Send file-changed even if getting the item type list
3003 * failed, so interested parties can distinguish between
3004 * unknowable and not-yet-known cases.
3006 nautilus_file_changed (file);
3008 /* Start up the next one. */
3009 async_job_end (directory, "MIME list");
3010 nautilus_directory_async_state_changed (directory);
3013 static void
3014 mime_list_one (MimeListState *state,
3015 GFileInfo *info)
3017 const char *mime_type;
3019 if (should_skip_file (NULL, info)) {
3020 g_object_unref (info);
3021 return;
3024 mime_type = g_file_info_get_content_type (info);
3025 if (mime_type != NULL) {
3026 istr_set_insert (state->mime_list_hash, mime_type);
3030 static void
3031 mime_list_callback (GObject *source_object,
3032 GAsyncResult *res,
3033 gpointer user_data)
3035 MimeListState *state;
3036 NautilusDirectory *directory;
3037 GError *error;
3038 GList *files, *l;
3039 GFileInfo *info;
3041 state = user_data;
3042 directory = state->directory;
3044 if (g_cancellable_is_cancelled (state->cancellable)) {
3045 /* Operation was cancelled. Bail out */
3046 directory->details->mime_list_in_progress = NULL;
3048 async_job_end (directory, "MIME list");
3049 nautilus_directory_async_state_changed (directory);
3051 mime_list_state_free (state);
3053 return;
3056 g_assert (directory->details->mime_list_in_progress != NULL);
3057 g_assert (directory->details->mime_list_in_progress == state);
3059 error = NULL;
3060 files = g_file_enumerator_next_files_finish (state->enumerator,
3061 res, &error);
3063 for (l = files; l != NULL; l = l->next) {
3064 info = l->data;
3065 mime_list_one (state, info);
3066 g_object_unref (info);
3069 if (files == NULL) {
3070 mime_list_done (state, error != NULL);
3071 mime_list_state_free (state);
3072 } else {
3073 g_file_enumerator_next_files_async (state->enumerator,
3074 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3075 G_PRIORITY_DEFAULT,
3076 state->cancellable,
3077 mime_list_callback,
3078 state);
3081 g_list_free (files);
3083 if (error) {
3084 g_error_free (error);
3088 static void
3089 list_mime_enum_callback (GObject *source_object,
3090 GAsyncResult *res,
3091 gpointer user_data)
3093 MimeListState *state;
3094 GFileEnumerator *enumerator;
3095 NautilusDirectory *directory;
3096 GError *error;
3098 state = user_data;
3100 if (g_cancellable_is_cancelled (state->cancellable)) {
3101 /* Operation was cancelled. Bail out */
3102 directory = state->directory;
3103 directory->details->mime_list_in_progress = NULL;
3105 async_job_end (directory, "MIME list");
3106 nautilus_directory_async_state_changed (directory);
3108 mime_list_state_free (state);
3110 return;
3113 error = NULL;
3114 enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
3115 res, &error);
3117 if (enumerator == NULL) {
3118 mime_list_done (state, FALSE);
3119 g_error_free (error);
3120 mime_list_state_free (state);
3121 return;
3122 } else {
3123 state->enumerator = enumerator;
3124 g_file_enumerator_next_files_async (state->enumerator,
3125 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3126 G_PRIORITY_DEFAULT,
3127 state->cancellable,
3128 mime_list_callback,
3129 state);
3133 static void
3134 mime_list_start (NautilusDirectory *directory,
3135 NautilusFile *file,
3136 gboolean *doing_io)
3138 MimeListState *state;
3139 GFile *location;
3141 mime_list_stop (directory);
3143 if (directory->details->mime_list_in_progress != NULL) {
3144 *doing_io = TRUE;
3145 return;
3148 /* Figure out which file to get a mime list for. */
3149 if (!is_needy (file,
3150 should_get_mime_list,
3151 wants_mime_list)) {
3152 return;
3154 *doing_io = TRUE;
3156 if (!nautilus_file_is_directory (file)) {
3157 g_list_free (file->details->mime_list);
3158 file->details->mime_list_failed = FALSE;
3159 file->details->got_mime_list = FALSE;
3160 file->details->mime_list_is_up_to_date = TRUE;
3162 nautilus_directory_async_state_changed (directory);
3163 return;
3166 if (!async_job_start (directory, "MIME list")) {
3167 return;
3171 state = g_new0 (MimeListState, 1);
3172 state->mime_list_file = file;
3173 state->directory = nautilus_directory_ref (directory);
3174 state->cancellable = g_cancellable_new ();
3175 state->mime_list_hash = istr_set_new ();
3177 directory->details->mime_list_in_progress = state;
3179 location = nautilus_file_get_location (file);
3180 #ifdef DEBUG_LOAD_DIRECTORY
3182 char *uri;
3183 uri = g_file_get_uri (location);
3184 g_message ("load_directory called to get MIME list of %s", uri);
3185 g_free (uri);
3187 #endif
3189 g_file_enumerate_children_async (location,
3190 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3191 0, /* flags */
3192 G_PRIORITY_LOW, /* prio */
3193 state->cancellable,
3194 list_mime_enum_callback,
3195 state);
3196 g_object_unref (location);
3199 static void
3200 top_left_stop (NautilusDirectory *directory)
3202 NautilusFile *file;
3204 if (directory->details->top_left_read_state != NULL) {
3205 file = directory->details->top_left_read_state->file;
3206 if (file != NULL) {
3207 g_assert (NAUTILUS_IS_FILE (file));
3208 g_assert (file->details->directory == directory);
3209 if (is_needy (file,
3210 lacks_top_left,
3211 wants_top_left) ||
3212 is_needy (file,
3213 lacks_large_top_left,
3214 wants_large_top_left)) {
3215 return;
3219 /* The top left is not wanted, so stop it. */
3220 top_left_cancel (directory);
3224 static void
3225 top_left_read_state_free (TopLeftTextReadState *state)
3227 g_object_unref (state->cancellable);
3228 g_free (state);
3231 static void
3232 top_left_read_callback (GObject *source_object,
3233 GAsyncResult *res,
3234 gpointer callback_data)
3236 TopLeftTextReadState *state;
3237 NautilusDirectory *directory;
3238 NautilusFileDetails *file_details;
3239 gsize file_size;
3240 char *file_contents;
3242 state = callback_data;
3244 if (state->directory == NULL) {
3245 /* Operation was cancelled. Bail out */
3246 top_left_read_state_free (state);
3247 return;
3250 directory = nautilus_directory_ref (state->directory);
3252 file_details = state->file->details;
3254 file_details->top_left_text_is_up_to_date = TRUE;
3255 g_free (file_details->top_left_text);
3257 if (g_file_load_partial_contents_finish (G_FILE (source_object),
3258 res,
3259 &file_contents, &file_size,
3260 NULL, NULL)) {
3261 file_details->top_left_text = nautilus_extract_top_left_text (file_contents, state->large, file_size);
3262 file_details->got_top_left_text = TRUE;
3263 file_details->got_large_top_left_text = state->large;
3264 g_free (file_contents);
3265 } else {
3266 file_details->top_left_text = NULL;
3267 file_details->got_top_left_text = FALSE;
3268 file_details->got_large_top_left_text = FALSE;
3271 nautilus_file_changed (state->file);
3273 directory->details->top_left_read_state = NULL;
3274 async_job_end (directory, "top left");
3276 top_left_read_state_free (state);
3278 nautilus_directory_async_state_changed (directory);
3280 nautilus_directory_unref (directory);
3283 static int
3284 count_lines (const char *text, int length)
3286 int count, i;
3288 count = 0;
3289 for (i = 0; i < length; i++) {
3290 count += *text++ == '\n';
3292 return count;
3295 static gboolean
3296 top_left_read_more_callback (const char *file_contents,
3297 goffset bytes_read,
3298 gpointer callback_data)
3300 TopLeftTextReadState *state;
3302 state = callback_data;
3304 /* Stop reading when we have enough. */
3305 if (state->large) {
3306 return bytes_read < NAUTILUS_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_BYTES &&
3307 count_lines (file_contents, bytes_read) <= NAUTILUS_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_LINES;
3308 } else {
3309 return bytes_read < NAUTILUS_FILE_TOP_LEFT_TEXT_MAXIMUM_BYTES &&
3310 count_lines (file_contents, bytes_read) <= NAUTILUS_FILE_TOP_LEFT_TEXT_MAXIMUM_LINES;
3314 static void
3315 top_left_start (NautilusDirectory *directory,
3316 NautilusFile *file,
3317 gboolean *doing_io)
3319 GFile *location;
3320 gboolean needs_large;
3321 TopLeftTextReadState *state;
3323 if (directory->details->top_left_read_state != NULL) {
3324 *doing_io = TRUE;
3325 return;
3328 needs_large = FALSE;
3330 if (is_needy (file,
3331 lacks_large_top_left,
3332 wants_large_top_left)) {
3333 needs_large = TRUE;
3336 /* Figure out which file to read the top left for. */
3337 if (!(needs_large ||
3338 is_needy (file,
3339 lacks_top_left,
3340 wants_top_left))) {
3341 return;
3343 *doing_io = TRUE;
3345 if (!nautilus_file_contains_text (file)) {
3346 g_free (file->details->top_left_text);
3347 file->details->top_left_text = NULL;
3348 file->details->got_top_left_text = FALSE;
3349 file->details->got_large_top_left_text = FALSE;
3350 file->details->top_left_text_is_up_to_date = TRUE;
3352 nautilus_directory_async_state_changed (directory);
3353 return;
3356 if (!async_job_start (directory, "top left")) {
3357 return;
3360 /* Start reading. */
3361 state = g_new0 (TopLeftTextReadState, 1);
3362 state->directory = directory;
3363 state->cancellable = g_cancellable_new ();
3364 state->large = needs_large;
3365 state->file = file;
3367 directory->details->top_left_read_state = state;
3369 location = nautilus_file_get_location (file);
3370 g_file_load_partial_contents_async (location,
3371 state->cancellable,
3372 top_left_read_more_callback,
3373 top_left_read_callback,
3374 state);
3375 g_object_unref (location);
3378 static void
3379 get_info_state_free (GetInfoState *state)
3381 g_object_unref (state->cancellable);
3382 g_free (state);
3385 static void
3386 query_info_callback (GObject *source_object,
3387 GAsyncResult *res,
3388 gpointer user_data)
3390 NautilusDirectory *directory;
3391 NautilusFile *get_info_file;
3392 GFileInfo *info;
3393 GetInfoState *state;
3394 GError *error;
3396 state = user_data;
3398 if (state->directory == NULL) {
3399 /* Operation was cancelled. Bail out */
3400 get_info_state_free (state);
3401 return;
3404 directory = nautilus_directory_ref (state->directory);
3406 get_info_file = directory->details->get_info_file;
3407 g_assert (NAUTILUS_IS_FILE (get_info_file));
3409 directory->details->get_info_file = NULL;
3410 directory->details->get_info_in_progress = NULL;
3412 /* ref here because we might be removing the last ref when we
3413 * mark the file gone below, but we need to keep a ref at
3414 * least long enough to send the change notification.
3416 nautilus_file_ref (get_info_file);
3418 error = NULL;
3419 info = g_file_query_info_finish (G_FILE (source_object), res, &error);
3421 if (info == NULL) {
3422 if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) {
3423 /* mark file as gone */
3424 nautilus_file_mark_gone (get_info_file);
3426 get_info_file->details->file_info_is_up_to_date = TRUE;
3427 nautilus_file_clear_info (get_info_file);
3428 get_info_file->details->get_info_failed = TRUE;
3429 get_info_file->details->get_info_error = error;
3430 } else {
3431 nautilus_file_update_info (get_info_file, info);
3432 g_object_unref (info);
3435 nautilus_file_changed (get_info_file);
3436 nautilus_file_unref (get_info_file);
3438 async_job_end (directory, "file info");
3439 nautilus_directory_async_state_changed (directory);
3441 nautilus_directory_unref (directory);
3443 get_info_state_free (state);
3446 static void
3447 file_info_stop (NautilusDirectory *directory)
3449 NautilusFile *file;
3451 if (directory->details->get_info_in_progress != NULL) {
3452 file = directory->details->get_info_file;
3453 if (file != NULL) {
3454 g_assert (NAUTILUS_IS_FILE (file));
3455 g_assert (file->details->directory == directory);
3456 if (is_needy (file, lacks_info, wants_info)) {
3457 return;
3461 /* The info is not wanted, so stop it. */
3462 file_info_cancel (directory);
3466 static void
3467 file_info_start (NautilusDirectory *directory,
3468 NautilusFile *file,
3469 gboolean *doing_io)
3471 GFile *location;
3472 GetInfoState *state;
3474 file_info_stop (directory);
3476 if (directory->details->get_info_in_progress != NULL) {
3477 *doing_io = TRUE;
3478 return;
3481 if (!is_needy (file, lacks_info, wants_info)) {
3482 return;
3484 *doing_io = TRUE;
3486 if (!async_job_start (directory, "file info")) {
3487 return;
3490 directory->details->get_info_file = file;
3491 file->details->get_info_failed = FALSE;
3492 if (file->details->get_info_error) {
3493 g_error_free (file->details->get_info_error);
3494 file->details->get_info_error = NULL;
3497 state = g_new (GetInfoState, 1);
3498 state->directory = directory;
3499 state->cancellable = g_cancellable_new ();
3501 directory->details->get_info_in_progress = state;
3503 location = nautilus_file_get_location (file);
3504 g_file_query_info_async (location,
3505 NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
3507 G_PRIORITY_DEFAULT,
3508 state->cancellable, query_info_callback, state);
3509 g_object_unref (location);
3512 static void
3513 link_info_done (NautilusDirectory *directory,
3514 NautilusFile *file,
3515 const char *uri,
3516 const char *name,
3517 const char *icon,
3518 gboolean is_launcher)
3520 file->details->link_info_is_up_to_date = TRUE;
3522 nautilus_file_set_display_name (file, name, name, TRUE);
3524 file->details->got_link_info = TRUE;
3525 g_free (file->details->custom_icon);
3526 if (uri) {
3527 if (file->details->activation_location) {
3528 g_object_unref (file->details->activation_location);
3529 file->details->activation_location = NULL;
3531 file->details->got_custom_activation_location = TRUE;
3532 file->details->activation_location = g_file_new_for_uri (uri);
3534 file->details->custom_icon = g_strdup (icon);
3535 file->details->is_launcher = is_launcher;
3537 nautilus_directory_async_state_changed (directory);
3540 static gboolean
3541 should_read_link_info_sync (NautilusFile *file)
3543 #ifdef READ_LOCAL_LINKS_SYNC
3544 return (nautilus_file_is_local (file) && !nautilus_file_is_directory (file));
3545 #else
3546 return FALSE;
3547 #endif
3550 static void
3551 link_info_stop (NautilusDirectory *directory)
3553 NautilusFile *file;
3555 if (directory->details->link_info_read_state != NULL) {
3556 file = directory->details->link_info_read_state->file;
3558 if (file != NULL) {
3559 g_assert (NAUTILUS_IS_FILE (file));
3560 g_assert (file->details->directory == directory);
3561 if (is_needy (file,
3562 lacks_link_info,
3563 wants_link_info)) {
3564 return;
3568 /* The link info is not wanted, so stop it. */
3569 link_info_cancel (directory);
3573 static void
3574 link_info_got_data (NautilusDirectory *directory,
3575 NautilusFile *file,
3576 gboolean result,
3577 goffset bytes_read,
3578 char *file_contents)
3580 char *uri, *name, *icon;
3581 gboolean is_launcher;
3583 nautilus_directory_ref (directory);
3585 uri = NULL;
3586 name = NULL;
3587 icon = NULL;
3588 is_launcher = FALSE;
3590 /* Handle the case where we read the Nautilus link. */
3591 if (result) {
3592 nautilus_link_get_link_info_given_file_contents (file_contents, bytes_read,
3593 &uri, &name, &icon, &is_launcher);
3594 } else {
3595 /* FIXME bugzilla.gnome.org 42433: We should report this error to the user. */
3598 nautilus_file_ref (file);
3599 link_info_done (directory, file, uri, name, icon, is_launcher);
3600 nautilus_file_changed (file);
3601 nautilus_file_unref (file);
3603 g_free (uri);
3604 g_free (name);
3605 g_free (icon);
3607 nautilus_directory_unref (directory);
3610 static void
3611 link_info_read_state_free (LinkInfoReadState *state)
3613 g_object_unref (state->cancellable);
3614 g_free (state);
3617 static void
3618 link_info_nautilus_link_read_callback (GObject *source_object,
3619 GAsyncResult *res,
3620 gpointer user_data)
3622 LinkInfoReadState *state;
3623 gsize file_size;
3624 char *file_contents;
3625 gboolean result;
3626 NautilusDirectory *directory;
3628 state = user_data;
3630 if (state->directory == NULL) {
3631 /* Operation was cancelled. Bail out */
3632 link_info_read_state_free (state);
3633 return;
3636 directory = nautilus_directory_ref (state->directory);
3638 result = g_file_load_contents_finish (G_FILE (source_object),
3639 res,
3640 &file_contents, &file_size,
3641 NULL, NULL);
3643 state->directory->details->link_info_read_state = NULL;
3644 async_job_end (state->directory, "link info");
3646 link_info_got_data (state->directory, state->file, result, file_size, file_contents);
3648 if (result) {
3649 g_free (file_contents);
3652 link_info_read_state_free (state);
3654 nautilus_directory_unref (directory);
3657 static void
3658 link_info_start (NautilusDirectory *directory,
3659 NautilusFile *file,
3660 gboolean *doing_io)
3662 GFile *location;
3663 gboolean nautilus_style_link;
3664 gsize file_size;
3665 char *file_contents;
3666 gboolean result;
3667 LinkInfoReadState *state;
3669 if (directory->details->link_info_read_state != NULL) {
3670 *doing_io = TRUE;
3671 return;
3674 if (!is_needy (file,
3675 lacks_link_info,
3676 wants_link_info)) {
3677 return;
3679 *doing_io = TRUE;
3681 /* Figure out if it is a link. */
3682 nautilus_style_link = nautilus_file_is_nautilus_link (file);
3683 location = nautilus_file_get_location (file);
3685 /* If it's not a link we are done. If it is, we need to read it. */
3686 if (!nautilus_style_link) {
3687 link_info_done (directory, file, NULL, NULL, NULL, FALSE);
3688 } else if (should_read_link_info_sync (file)) {
3689 result = g_file_load_contents (location, NULL, &file_contents, &file_size, NULL, NULL);
3690 link_info_got_data (directory, file, result, file_size, file_contents);
3691 g_free (file_contents);
3692 } else {
3693 if (!async_job_start (directory, "link info")) {
3694 g_object_unref (location);
3695 return;
3698 state = g_new0 (LinkInfoReadState, 1);
3699 state->directory = directory;
3700 state->file = file;
3701 state->cancellable = g_cancellable_new ();
3703 directory->details->link_info_read_state = state;
3705 g_file_load_contents_async (location,
3706 state->cancellable,
3707 link_info_nautilus_link_read_callback,
3708 state);
3710 g_object_unref (location);
3713 static void
3714 thumbnail_done (NautilusDirectory *directory,
3715 NautilusFile *file,
3716 GdkPixbuf *pixbuf,
3717 gboolean tried_original)
3719 const char *thumb_mtime_str;
3720 time_t thumb_mtime = 0;
3722 file->details->thumbnail_is_up_to_date = TRUE;
3723 file->details->thumbnail_tried_original = tried_original;
3724 if (file->details->thumbnail) {
3725 g_object_unref (file->details->thumbnail);
3726 file->details->thumbnail = NULL;
3728 file->details->thumbnail_size = 0;
3729 if (pixbuf) {
3730 thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
3731 if (thumb_mtime_str) {
3732 thumb_mtime = atol (thumb_mtime_str);
3735 if (thumb_mtime == 0 ||
3736 thumb_mtime == file->details->mtime) {
3737 file->details->thumbnail = g_object_ref (pixbuf);
3738 file->details->thumbnail_mtime = thumb_mtime;
3739 } else {
3740 g_free (file->details->thumbnail_path);
3741 file->details->thumbnail_path = NULL;
3745 nautilus_directory_async_state_changed (directory);
3748 static void
3749 thumbnail_stop (NautilusDirectory *directory)
3751 NautilusFile *file;
3753 if (directory->details->thumbnail_state != NULL) {
3754 file = directory->details->thumbnail_state->file;
3756 if (file != NULL) {
3757 g_assert (NAUTILUS_IS_FILE (file));
3758 g_assert (file->details->directory == directory);
3759 if (is_needy (file,
3760 lacks_thumbnail,
3761 wants_thumbnail)) {
3762 return;
3766 /* The link info is not wanted, so stop it. */
3767 thumbnail_cancel (directory);
3771 static void
3772 thumbnail_got_pixbuf (NautilusDirectory *directory,
3773 NautilusFile *file,
3774 GdkPixbuf *pixbuf,
3775 gboolean tried_original)
3777 nautilus_directory_ref (directory);
3779 nautilus_file_ref (file);
3780 thumbnail_done (directory, file, pixbuf, tried_original);
3781 nautilus_file_changed (file);
3782 nautilus_file_unref (file);
3784 if (pixbuf) {
3785 g_object_unref (pixbuf);
3788 nautilus_directory_unref (directory);
3791 static void
3792 thumbnail_state_free (ThumbnailState *state)
3794 g_object_unref (state->cancellable);
3795 g_free (state);
3798 static GdkPixbuf *
3799 get_pixbuf_for_content (goffset file_len,
3800 char *file_contents)
3802 gboolean res;
3803 GdkPixbuf *pixbuf, *pixbuf2;
3804 GdkPixbufLoader *loader;
3805 gsize chunk_len;
3806 pixbuf = NULL;
3808 loader = gdk_pixbuf_loader_new ();
3810 /* For some reason we have to write in chunks, or gdk-pixbuf fails */
3811 res = TRUE;
3812 while (res && file_len > 0) {
3813 chunk_len = MIN (32*1024, file_len);
3814 res = gdk_pixbuf_loader_write (loader, file_contents, chunk_len, NULL);
3815 file_contents += chunk_len;
3816 file_len -= chunk_len;
3818 if (res) {
3819 res = gdk_pixbuf_loader_close (loader, NULL);
3821 if (res) {
3822 pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
3824 g_object_unref (G_OBJECT (loader));
3826 if (pixbuf) {
3827 pixbuf2 = gdk_pixbuf_apply_embedded_orientation (pixbuf);
3828 g_object_unref (pixbuf);
3829 pixbuf = pixbuf2;
3831 return pixbuf;
3835 static void
3836 thumbnail_read_callback (GObject *source_object,
3837 GAsyncResult *res,
3838 gpointer user_data)
3840 ThumbnailState *state;
3841 gsize file_size;
3842 char *file_contents;
3843 gboolean result;
3844 NautilusDirectory *directory;
3845 GdkPixbuf *pixbuf;
3846 GFile *location;
3848 state = user_data;
3850 if (state->directory == NULL) {
3851 /* Operation was cancelled. Bail out */
3852 thumbnail_state_free (state);
3853 return;
3856 directory = nautilus_directory_ref (state->directory);
3858 result = g_file_load_contents_finish (G_FILE (source_object),
3859 res,
3860 &file_contents, &file_size,
3861 NULL, NULL);
3863 pixbuf = NULL;
3864 if (result) {
3865 pixbuf = get_pixbuf_for_content (file_size, file_contents);
3866 g_free (file_contents);
3869 if (pixbuf == NULL && state->trying_original) {
3870 state->trying_original = FALSE;
3872 location = g_file_new_for_path (state->file->details->thumbnail_path);
3873 g_file_load_contents_async (location,
3874 state->cancellable,
3875 thumbnail_read_callback,
3876 state);
3877 g_object_unref (location);
3878 } else {
3879 state->directory->details->thumbnail_state = NULL;
3880 async_job_end (state->directory, "thumbnail");
3882 thumbnail_got_pixbuf (state->directory, state->file, pixbuf, state->tried_original);
3884 thumbnail_state_free (state);
3887 nautilus_directory_unref (directory);
3890 static void
3891 thumbnail_start (NautilusDirectory *directory,
3892 NautilusFile *file,
3893 gboolean *doing_io)
3895 GFile *location;
3896 ThumbnailState *state;
3898 if (directory->details->thumbnail_state != NULL) {
3899 *doing_io = TRUE;
3900 return;
3903 if (!is_needy (file,
3904 lacks_thumbnail,
3905 wants_thumbnail)) {
3906 return;
3908 *doing_io = TRUE;
3910 if (!async_job_start (directory, "thumbnail")) {
3911 return;
3914 state = g_new0 (ThumbnailState, 1);
3915 state->directory = directory;
3916 state->file = file;
3917 state->cancellable = g_cancellable_new ();
3919 if (file->details->thumbnail_size > 128) {
3920 state->tried_original = TRUE;
3921 state->trying_original = TRUE;
3922 location = nautilus_file_get_location (file);
3923 } else {
3924 location = g_file_new_for_path (file->details->thumbnail_path);
3927 directory->details->thumbnail_state = state;
3929 g_file_load_contents_async (location,
3930 state->cancellable,
3931 thumbnail_read_callback,
3932 state);
3933 g_object_unref (location);
3936 static void
3937 mount_stop (NautilusDirectory *directory)
3939 NautilusFile *file;
3941 if (directory->details->mount_state != NULL) {
3942 file = directory->details->mount_state->file;
3944 if (file != NULL) {
3945 g_assert (NAUTILUS_IS_FILE (file));
3946 g_assert (file->details->directory == directory);
3947 if (is_needy (file,
3948 lacks_mount,
3949 wants_mount)) {
3950 return;
3954 /* The link info is not wanted, so stop it. */
3955 mount_cancel (directory);
3959 static void
3960 mount_state_free (MountState *state)
3962 g_object_unref (state->cancellable);
3963 g_free (state);
3966 static void
3967 got_mount (MountState *state, GMount *mount)
3969 NautilusDirectory *directory;
3970 NautilusFile *file;
3972 directory = nautilus_directory_ref (state->directory);
3974 state->directory->details->mount_state = NULL;
3975 async_job_end (state->directory, "mount");
3977 file = nautilus_file_ref (state->file);
3979 if (file->details->mount) {
3980 g_object_unref (file->details->mount);
3981 file->details->mount = NULL;
3984 file->details->mount_is_up_to_date = TRUE;
3985 if (mount) {
3986 file->details->mount = g_object_ref (mount);
3989 nautilus_directory_async_state_changed (directory);
3990 nautilus_file_changed (file);
3992 nautilus_file_unref (file);
3994 nautilus_directory_unref (directory);
3996 mount_state_free (state);
4000 static void
4001 find_enclosing_mount_callback (GObject *source_object,
4002 GAsyncResult *res,
4003 gpointer user_data)
4005 GMount *mount;
4006 MountState *state;
4007 GFile *location, *root;
4009 state = user_data;
4010 if (state->directory == NULL) {
4011 /* Operation was cancelled. Bail out */
4012 mount_state_free (state);
4013 return;
4016 mount = g_file_find_enclosing_mount_finish (G_FILE (source_object),
4017 res, NULL);
4019 if (mount) {
4020 root = g_mount_get_root (mount);
4021 location = nautilus_file_get_location (state->file);
4022 if (!g_file_equal (location, root)) {
4023 g_object_unref (mount);
4024 mount = NULL;
4026 g_object_unref (root);
4027 g_object_unref (location);
4030 got_mount (state, mount);
4032 if (mount) {
4033 g_object_unref (mount);
4037 static GMount *
4038 get_mount_at (GFile *target)
4040 GVolumeMonitor *monitor;
4041 GFile *root;
4042 GList *mounts, *l;
4043 GMount *found;
4045 monitor = g_volume_monitor_get ();
4046 mounts = g_volume_monitor_get_mounts (monitor);
4048 found = NULL;
4049 for (l = mounts; l != NULL; l = l->next) {
4050 root = g_mount_get_root (l->data);
4052 if (g_file_equal (target, root)) {
4053 found = g_object_ref (l->data);
4054 break;
4057 g_object_unref (root);
4060 eel_g_object_list_free (mounts);
4062 g_object_unref (monitor);
4064 return found;
4067 static void
4068 mount_start (NautilusDirectory *directory,
4069 NautilusFile *file,
4070 gboolean *doing_io)
4072 GFile *location;
4073 MountState *state;
4075 if (directory->details->mount_state != NULL) {
4076 *doing_io = TRUE;
4077 return;
4080 if (!is_needy (file,
4081 lacks_mount,
4082 wants_mount)) {
4083 return;
4085 *doing_io = TRUE;
4087 if (!async_job_start (directory, "mount")) {
4088 return;
4091 state = g_new0 (MountState, 1);
4092 state->directory = directory;
4093 state->file = file;
4094 state->cancellable = g_cancellable_new ();
4096 location = nautilus_file_get_location (file);
4098 directory->details->mount_state = state;
4100 if (file->details->type == G_FILE_TYPE_MOUNTABLE) {
4101 GFile *target;
4102 GMount *mount;
4104 mount = NULL;
4105 target = nautilus_file_get_activation_location (file);
4106 if (target != NULL) {
4107 mount = get_mount_at (target);
4108 g_object_unref (target);
4111 got_mount (state, mount);
4113 if (mount) {
4114 g_object_unref (mount);
4116 } else {
4117 g_file_find_enclosing_mount_async (location,
4118 G_PRIORITY_DEFAULT,
4119 state->cancellable,
4120 find_enclosing_mount_callback,
4121 state);
4123 g_object_unref (location);
4126 static void
4127 filesystem_info_cancel (NautilusDirectory *directory)
4129 if (directory->details->filesystem_info_state != NULL) {
4130 g_cancellable_cancel (directory->details->filesystem_info_state->cancellable);
4131 directory->details->filesystem_info_state->directory = NULL;
4132 directory->details->filesystem_info_state = NULL;
4133 async_job_end (directory, "filesystem info");
4137 static void
4138 filesystem_info_stop (NautilusDirectory *directory)
4140 NautilusFile *file;
4142 if (directory->details->filesystem_info_state != NULL) {
4143 file = directory->details->filesystem_info_state->file;
4145 if (file != NULL) {
4146 g_assert (NAUTILUS_IS_FILE (file));
4147 g_assert (file->details->directory == directory);
4148 if (is_needy (file,
4149 lacks_filesystem_info,
4150 wants_filesystem_info)) {
4151 return;
4155 /* The filesystem info is not wanted, so stop it. */
4156 filesystem_info_cancel (directory);
4160 static void
4161 filesystem_info_state_free (FilesystemInfoState *state)
4163 g_object_unref (state->cancellable);
4164 g_free (state);
4167 static void
4168 got_filesystem_info (FilesystemInfoState *state, GFileInfo *info)
4170 NautilusDirectory *directory;
4171 NautilusFile *file;
4173 /* careful here, info may be NULL */
4175 directory = nautilus_directory_ref (state->directory);
4177 state->directory->details->filesystem_info_state = NULL;
4178 async_job_end (state->directory, "filesystem info");
4180 file = nautilus_file_ref (state->file);
4182 file->details->filesystem_info_is_up_to_date = TRUE;
4183 if (info != NULL) {
4184 file->details->filesystem_use_preview =
4185 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
4186 file->details->filesystem_readonly =
4187 g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
4190 nautilus_directory_async_state_changed (directory);
4191 nautilus_file_changed (file);
4193 nautilus_file_unref (file);
4195 nautilus_directory_unref (directory);
4197 filesystem_info_state_free (state);
4200 static void
4201 query_filesystem_info_callback (GObject *source_object,
4202 GAsyncResult *res,
4203 gpointer user_data)
4205 GFileInfo *info;
4206 FilesystemInfoState *state;
4208 state = user_data;
4209 if (state->directory == NULL) {
4210 /* Operation was cancelled. Bail out */
4211 filesystem_info_state_free (state);
4212 return;
4215 info = g_file_query_filesystem_info_finish (G_FILE (source_object), res, NULL);
4217 got_filesystem_info (state, info);
4219 if (info != NULL) {
4220 g_object_unref (info);
4224 static void
4225 filesystem_info_start (NautilusDirectory *directory,
4226 NautilusFile *file,
4227 gboolean *doing_io)
4229 GFile *location;
4230 FilesystemInfoState *state;
4232 if (directory->details->filesystem_info_state != NULL) {
4233 *doing_io = TRUE;
4234 return;
4237 if (!is_needy (file,
4238 lacks_filesystem_info,
4239 wants_filesystem_info)) {
4240 return;
4242 *doing_io = TRUE;
4244 if (!async_job_start (directory, "filesystem info")) {
4245 return;
4248 state = g_new0 (FilesystemInfoState, 1);
4249 state->directory = directory;
4250 state->file = file;
4251 state->cancellable = g_cancellable_new ();
4253 location = nautilus_file_get_location (file);
4255 directory->details->filesystem_info_state = state;
4257 g_file_query_filesystem_info_async (location,
4258 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY ","
4259 G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
4260 G_PRIORITY_DEFAULT,
4261 state->cancellable,
4262 query_filesystem_info_callback,
4263 state);
4264 g_object_unref (location);
4267 static void
4268 extension_info_cancel (NautilusDirectory *directory)
4270 if (directory->details->extension_info_in_progress != NULL) {
4271 if (directory->details->extension_info_idle) {
4272 g_source_remove (directory->details->extension_info_idle);
4273 } else {
4274 nautilus_info_provider_cancel_update
4275 (directory->details->extension_info_provider,
4276 directory->details->extension_info_in_progress);
4279 directory->details->extension_info_in_progress = NULL;
4280 directory->details->extension_info_file = NULL;
4281 directory->details->extension_info_provider = NULL;
4282 directory->details->extension_info_idle = 0;
4284 async_job_end (directory, "extension info");
4288 static void
4289 extension_info_stop (NautilusDirectory *directory)
4291 if (directory->details->extension_info_in_progress != NULL) {
4292 NautilusFile *file;
4294 file = directory->details->extension_info_file;
4295 if (file != NULL) {
4296 g_assert (NAUTILUS_IS_FILE (file));
4297 g_assert (file->details->directory == directory);
4298 if (is_needy (file, lacks_extension_info, wants_extension_info)) {
4299 return;
4303 /* The info is not wanted, so stop it. */
4304 extension_info_cancel (directory);
4308 static void
4309 finish_info_provider (NautilusDirectory *directory,
4310 NautilusFile *file,
4311 NautilusInfoProvider *provider)
4313 file->details->pending_info_providers =
4314 g_list_remove (file->details->pending_info_providers,
4315 provider);
4316 g_object_unref (provider);
4318 nautilus_directory_async_state_changed (directory);
4320 if (file->details->pending_info_providers == NULL) {
4321 nautilus_file_info_providers_done (file);
4326 static gboolean
4327 info_provider_idle_callback (gpointer user_data)
4329 InfoProviderResponse *response;
4330 NautilusDirectory *directory;
4332 response = user_data;
4333 directory = response->directory;
4335 if (response->handle != directory->details->extension_info_in_progress
4336 || response->provider != directory->details->extension_info_provider) {
4337 g_warning ("Unexpected plugin response. This probably indicates a bug in a Nautilus extension: handle=%p", response->handle);
4338 } else {
4339 NautilusFile *file;
4340 async_job_end (directory, "extension info");
4342 file = directory->details->extension_info_file;
4344 directory->details->extension_info_file = NULL;
4345 directory->details->extension_info_provider = NULL;
4346 directory->details->extension_info_in_progress = NULL;
4347 directory->details->extension_info_idle = 0;
4349 finish_info_provider (directory, file, response->provider);
4352 return FALSE;
4355 static void
4356 info_provider_callback (NautilusInfoProvider *provider,
4357 NautilusOperationHandle *handle,
4358 NautilusOperationResult result,
4359 gpointer user_data)
4361 InfoProviderResponse *response;
4363 response = g_new0 (InfoProviderResponse, 1);
4364 response->provider = provider;
4365 response->handle = handle;
4366 response->result = result;
4367 response->directory = NAUTILUS_DIRECTORY (user_data);
4369 response->directory->details->extension_info_idle =
4370 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
4371 info_provider_idle_callback, response,
4372 g_free);
4375 static void
4376 extension_info_start (NautilusDirectory *directory,
4377 NautilusFile *file,
4378 gboolean *doing_io)
4380 NautilusInfoProvider *provider;
4381 NautilusOperationResult result;
4382 NautilusOperationHandle *handle;
4383 GClosure *update_complete;
4385 if (directory->details->extension_info_in_progress != NULL) {
4386 *doing_io = TRUE;
4387 return;
4390 if (!is_needy (file, lacks_extension_info, wants_extension_info)) {
4391 return;
4393 *doing_io = TRUE;
4395 if (!async_job_start (directory, "extension info")) {
4396 return;
4399 provider = file->details->pending_info_providers->data;
4401 update_complete = g_cclosure_new (G_CALLBACK (info_provider_callback),
4402 directory,
4403 NULL);
4404 g_closure_set_marshal (update_complete,
4405 nautilus_marshal_VOID__POINTER_ENUM);
4407 result = nautilus_info_provider_update_file_info
4408 (provider,
4409 NAUTILUS_FILE_INFO (file),
4410 update_complete,
4411 &handle);
4413 g_closure_unref (update_complete);
4415 if (result == NAUTILUS_OPERATION_COMPLETE ||
4416 result == NAUTILUS_OPERATION_FAILED) {
4417 finish_info_provider (directory, file, provider);
4418 async_job_end (directory, "extension info");
4419 } else {
4420 directory->details->extension_info_in_progress = handle;
4421 directory->details->extension_info_provider = provider;
4422 directory->details->extension_info_file = file;
4426 static void
4427 start_or_stop_io (NautilusDirectory *directory)
4429 NautilusFile *file;
4430 gboolean doing_io;
4432 /* Start or stop reading files. */
4433 file_list_start_or_stop (directory);
4435 /* Stop any no longer wanted attribute fetches. */
4436 file_info_stop (directory);
4437 directory_count_stop (directory);
4438 deep_count_stop (directory);
4439 mime_list_stop (directory);
4440 top_left_stop (directory);
4441 link_info_stop (directory);
4442 extension_info_stop (directory);
4443 mount_stop (directory);
4444 thumbnail_stop (directory);
4445 filesystem_info_stop (directory);
4447 doing_io = FALSE;
4448 /* Take files that are all done off the queue. */
4449 while (!nautilus_file_queue_is_empty (directory->details->high_priority_queue)) {
4450 file = nautilus_file_queue_head (directory->details->high_priority_queue);
4452 /* Start getting attributes if possible */
4453 file_info_start (directory, file, &doing_io);
4454 link_info_start (directory, file, &doing_io);
4456 if (doing_io) {
4457 return;
4460 move_file_to_low_priority_queue (directory, file);
4463 /* High priority queue must be empty */
4464 while (!nautilus_file_queue_is_empty (directory->details->low_priority_queue)) {
4465 file = nautilus_file_queue_head (directory->details->low_priority_queue);
4467 /* Start getting attributes if possible */
4468 mount_start (directory, file, &doing_io);
4469 directory_count_start (directory, file, &doing_io);
4470 deep_count_start (directory, file, &doing_io);
4471 mime_list_start (directory, file, &doing_io);
4472 top_left_start (directory, file, &doing_io);
4473 thumbnail_start (directory, file, &doing_io);
4474 filesystem_info_start (directory, file, &doing_io);
4476 if (doing_io) {
4477 return;
4480 move_file_to_extension_queue (directory, file);
4483 /* Low priority queue must be empty */
4484 while (!nautilus_file_queue_is_empty (directory->details->extension_queue)) {
4485 file = nautilus_file_queue_head (directory->details->extension_queue);
4487 /* Start getting attributes if possible */
4488 extension_info_start (directory, file, &doing_io);
4489 if (doing_io) {
4490 return;
4493 nautilus_directory_remove_file_from_work_queue (directory, file);
4497 /* Call this when the monitor or call when ready list changes,
4498 * or when some I/O is completed.
4500 void
4501 nautilus_directory_async_state_changed (NautilusDirectory *directory)
4503 /* Check if any callbacks are satisfied and call them if they
4504 * are. Do this last so that any changes done in start or stop
4505 * I/O functions immediately (not in callbacks) are taken into
4506 * consideration. If any callbacks are called, consider the
4507 * I/O state again so that we can release or cancel I/O that
4508 * is not longer needed once the callbacks are satisfied.
4511 if (directory->details->in_async_service_loop) {
4512 directory->details->state_changed = TRUE;
4513 return;
4515 directory->details->in_async_service_loop = TRUE;
4516 nautilus_directory_ref (directory);
4517 do {
4518 directory->details->state_changed = FALSE;
4519 start_or_stop_io (directory);
4520 if (call_ready_callbacks (directory)) {
4521 directory->details->state_changed = TRUE;
4523 } while (directory->details->state_changed);
4524 directory->details->in_async_service_loop = FALSE;
4525 nautilus_directory_unref (directory);
4527 /* Check if any directories should wake up. */
4528 async_job_wake_up ();
4531 void
4532 nautilus_directory_cancel (NautilusDirectory *directory)
4534 /* Arbitrary order (kept alphabetical). */
4535 deep_count_cancel (directory);
4536 directory_count_cancel (directory);
4537 file_info_cancel (directory);
4538 file_list_cancel (directory);
4539 link_info_cancel (directory);
4540 mime_list_cancel (directory);
4541 new_files_cancel (directory);
4542 top_left_cancel (directory);
4543 extension_info_cancel (directory);
4544 thumbnail_cancel (directory);
4545 mount_cancel (directory);
4546 filesystem_info_cancel (directory);
4548 /* We aren't waiting for anything any more. */
4549 if (waiting_directories != NULL) {
4550 g_hash_table_remove (waiting_directories, directory);
4553 /* Check if any directories should wake up. */
4554 async_job_wake_up ();
4557 static void
4558 cancel_directory_count_for_file (NautilusDirectory *directory,
4559 NautilusFile *file)
4561 if (directory->details->count_in_progress != NULL &&
4562 directory->details->count_in_progress->count_file == file) {
4563 directory_count_cancel (directory);
4567 static void
4568 cancel_deep_counts_for_file (NautilusDirectory *directory,
4569 NautilusFile *file)
4571 if (directory->details->deep_count_file == file) {
4572 deep_count_cancel (directory);
4576 static void
4577 cancel_mime_list_for_file (NautilusDirectory *directory,
4578 NautilusFile *file)
4580 if (directory->details->mime_list_in_progress != NULL &&
4581 directory->details->mime_list_in_progress->mime_list_file == file) {
4582 mime_list_cancel (directory);
4586 static void
4587 cancel_top_left_text_for_file (NautilusDirectory *directory,
4588 NautilusFile *file)
4590 if (directory->details->top_left_read_state != NULL &&
4591 directory->details->top_left_read_state->file == file) {
4592 top_left_cancel (directory);
4596 static void
4597 cancel_file_info_for_file (NautilusDirectory *directory,
4598 NautilusFile *file)
4600 if (directory->details->get_info_file == file) {
4601 file_info_cancel (directory);
4605 static void
4606 cancel_thumbnail_for_file (NautilusDirectory *directory,
4607 NautilusFile *file)
4609 if (directory->details->thumbnail_state != NULL &&
4610 directory->details->thumbnail_state->file == file) {
4611 thumbnail_cancel (directory);
4615 static void
4616 cancel_mount_for_file (NautilusDirectory *directory,
4617 NautilusFile *file)
4619 if (directory->details->mount_state != NULL &&
4620 directory->details->mount_state->file == file) {
4621 mount_cancel (directory);
4625 static void
4626 cancel_filesystem_info_for_file (NautilusDirectory *directory,
4627 NautilusFile *file)
4629 if (directory->details->filesystem_info_state != NULL &&
4630 directory->details->filesystem_info_state->file == file) {
4631 filesystem_info_cancel (directory);
4635 static void
4636 cancel_link_info_for_file (NautilusDirectory *directory,
4637 NautilusFile *file)
4639 if (directory->details->link_info_read_state != NULL &&
4640 directory->details->link_info_read_state->file == file) {
4641 link_info_cancel (directory);
4646 static void
4647 cancel_loading_attributes (NautilusDirectory *directory,
4648 NautilusFileAttributes file_attributes)
4650 Request request;
4652 nautilus_directory_set_up_request (&request,
4653 file_attributes);
4655 if (request.directory_count) {
4656 directory_count_cancel (directory);
4658 if (request.deep_count) {
4659 deep_count_cancel (directory);
4661 if (request.mime_list) {
4662 mime_list_cancel (directory);
4664 if (request.top_left_text) {
4665 top_left_cancel (directory);
4667 if (request.file_info) {
4668 file_info_cancel (directory);
4670 if (request.filesystem_info) {
4671 filesystem_info_cancel (directory);
4673 if (request.link_info) {
4674 link_info_cancel (directory);
4677 if (request.extension_info) {
4678 extension_info_cancel (directory);
4681 if (request.thumbnail) {
4682 thumbnail_cancel (directory);
4685 if (request.mount) {
4686 mount_cancel (directory);
4689 /* FIXME bugzilla.gnome.org 45064: implement cancelling metadata when we
4690 implement invalidating metadata */
4692 nautilus_directory_async_state_changed (directory);
4695 void
4696 nautilus_directory_cancel_loading_file_attributes (NautilusDirectory *directory,
4697 NautilusFile *file,
4698 NautilusFileAttributes file_attributes)
4700 Request request;
4702 nautilus_directory_remove_file_from_work_queue (directory, file);
4704 nautilus_directory_set_up_request (&request,
4705 file_attributes);
4707 if (request.directory_count) {
4708 cancel_directory_count_for_file (directory, file);
4710 if (request.deep_count) {
4711 cancel_deep_counts_for_file (directory, file);
4713 if (request.mime_list) {
4714 cancel_mime_list_for_file (directory, file);
4716 if (request.top_left_text) {
4717 cancel_top_left_text_for_file (directory, file);
4719 if (request.file_info) {
4720 cancel_file_info_for_file (directory, file);
4722 if (request.filesystem_info) {
4723 cancel_filesystem_info_for_file (directory, file);
4725 if (request.link_info) {
4726 cancel_link_info_for_file (directory, file);
4728 if (request.thumbnail) {
4729 cancel_thumbnail_for_file (directory, file);
4731 if (request.mount) {
4732 cancel_mount_for_file (directory, file);
4735 /* FIXME bugzilla.gnome.org 45064: implement cancelling metadata when we
4736 implement invalidating metadata */
4738 nautilus_directory_async_state_changed (directory);
4741 void
4742 nautilus_directory_add_file_to_work_queue (NautilusDirectory *directory,
4743 NautilusFile *file)
4745 g_return_if_fail (file->details->directory == directory);
4747 nautilus_file_queue_enqueue (directory->details->high_priority_queue,
4748 file);
4752 static void
4753 add_all_files_to_work_queue (NautilusDirectory *directory)
4755 GList *node;
4756 NautilusFile *file;
4758 for (node = directory->details->file_list; node != NULL; node = node->next) {
4759 file = NAUTILUS_FILE (node->data);
4761 nautilus_directory_add_file_to_work_queue (directory, file);
4765 void
4766 nautilus_directory_remove_file_from_work_queue (NautilusDirectory *directory,
4767 NautilusFile *file)
4769 nautilus_file_queue_remove (directory->details->high_priority_queue,
4770 file);
4771 nautilus_file_queue_remove (directory->details->low_priority_queue,
4772 file);
4773 nautilus_file_queue_remove (directory->details->extension_queue,
4774 file);
4778 static void
4779 move_file_to_low_priority_queue (NautilusDirectory *directory,
4780 NautilusFile *file)
4782 /* Must add before removing to avoid ref underflow */
4783 nautilus_file_queue_enqueue (directory->details->low_priority_queue,
4784 file);
4785 nautilus_file_queue_remove (directory->details->high_priority_queue,
4786 file);
4789 static void
4790 move_file_to_extension_queue (NautilusDirectory *directory,
4791 NautilusFile *file)
4793 /* Must add before removing to avoid ref underflow */
4794 nautilus_file_queue_enqueue (directory->details->extension_queue,
4795 file);
4796 nautilus_file_queue_remove (directory->details->low_priority_queue,
4797 file);