summaryrefslogtreecommitdiff
path: root/src/nautilus-directory-async.c
diff options
context:
space:
mode:
authorCarlos Soriano <csoriano@gnome.org>2016-04-22 17:02:38 +0200
committerCarlos Soriano <csoriano@gnome.org>2016-04-25 16:31:42 +0200
commit7e24f1b2a2b3b7860ee29820d69f5a1511fee994 (patch)
treea2ee175c5f9cd05ab518e768a073ef4a31fe8376 /src/nautilus-directory-async.c
parent2774b8552dcc89ae744700af5832dbf76c138a9e (diff)
downloadnautilus-7e24f1b2a2b3b7860ee29820d69f5a1511fee994.tar.gz
general: merge libnautilus-private to srcwip/csoriano/private-to-src
And fix make distcheck. Although libnautilus-private seem self contained, it was actually depending on the files on src/ for dnd. Not only that, but files in libnautilus-private also were depending on dnd files, which you can guess it's wrong. Before the desktop split, this was working because the files were distributed, but now was a problem since we reestructured the code, and now nautilus being a library make distcheck stop working. First solution was try to fix this inter dependency of files, but at some point I realized that there was no real point on splitting some of those files, because for example, is perfectly fine for dnd to need to access the window functions, and it's perfectly fine for the widgets in the private library to need to access to all dnd functions. So seems to me the private library of nautilus is somehow an artificial split, which provides more problems than solutions. We needed libnautilus-private to have a private library that we could isolate from extensions, but I don't think it worth given the problems it provides, and also, this not so good logical split. Right now, since with the desktop split we created a libnautilus to be used by the desktop part of nautilus, extensions have access to all the API of nautilus. We will think in future how this can be handled if we want. So for now, merge the libnautilus-private into src, and let's rethink a better logic to split the code and the private parts of nautilus than what we had. Thanks a lot to Rafael Fonseca for helping in get this done. https://bugzilla.gnome.org/show_bug.cgi?id=765543
Diffstat (limited to 'src/nautilus-directory-async.c')
-rw-r--r--src/nautilus-directory-async.c4558
1 files changed, 4558 insertions, 0 deletions
diff --git a/src/nautilus-directory-async.c b/src/nautilus-directory-async.c
new file mode 100644
index 000000000..ea97da61f
--- /dev/null
+++ b/src/nautilus-directory-async.c
@@ -0,0 +1,4558 @@
+/*
+ nautilus-directory-async.c: Nautilus directory model state machine.
+
+ Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ Author: Darin Adler <darin@bentspoon.com>
+*/
+
+#include <config.h>
+
+#include "nautilus-directory-notify.h"
+#include "nautilus-directory-private.h"
+#include "nautilus-file-attributes.h"
+#include "nautilus-file-private.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-signaller.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-link.h"
+#include "nautilus-profile.h"
+#include <eel/eel-glib-extensions.h>
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* turn this on to see messages about each load_directory call: */
+#if 0
+#define DEBUG_LOAD_DIRECTORY
+#endif
+
+/* turn this on to check if async. job calls are balanced */
+#if 0
+#define DEBUG_ASYNC_JOBS
+#endif
+
+/* turn this on to log things starting and stopping */
+#if 0
+#define DEBUG_START_STOP
+#endif
+
+#define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
+
+/* Keep async. jobs down to this number for all directories. */
+#define MAX_ASYNC_JOBS 10
+
+struct TopLeftTextReadState {
+ NautilusDirectory *directory;
+ NautilusFile *file;
+ gboolean large;
+ GCancellable *cancellable;
+};
+
+struct LinkInfoReadState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ NautilusFile *file;
+};
+
+struct ThumbnailState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ NautilusFile *file;
+ gboolean trying_original;
+ gboolean tried_original;
+};
+
+struct MountState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ NautilusFile *file;
+};
+
+struct FilesystemInfoState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ NautilusFile *file;
+};
+
+struct DirectoryLoadState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ GHashTable *load_mime_list_hash;
+ NautilusFile *load_directory_file;
+ int load_file_count;
+};
+
+struct MimeListState {
+ NautilusDirectory *directory;
+ NautilusFile *mime_list_file;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ GHashTable *mime_list_hash;
+};
+
+struct GetInfoState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+};
+
+struct NewFilesState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ int count;
+};
+
+struct DirectoryCountState {
+ NautilusDirectory *directory;
+ NautilusFile *count_file;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ int file_count;
+};
+
+struct DeepCountState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ GFile *deep_count_location;
+ GList *deep_count_subdirectories;
+ GArray *seen_deep_count_inodes;
+ char *fs_id;
+};
+
+
+
+typedef struct {
+ NautilusFile *file; /* Which file, NULL means all. */
+ union {
+ NautilusDirectoryCallback directory;
+ NautilusFileCallback file;
+ } callback;
+ gpointer callback_data;
+ Request request;
+ gboolean active; /* Set to FALSE when the callback is triggered and
+ * scheduled to be called at idle, its still kept
+ * in the list so we can kill it when the file
+ * goes away.
+ */
+} ReadyCallback;
+
+typedef struct {
+ NautilusFile *file; /* Which file, NULL means all. */
+ gboolean monitor_hidden_files; /* defines whether "all" includes hidden files */
+ gconstpointer client;
+ Request request;
+} Monitor;
+
+typedef struct {
+ NautilusDirectory *directory;
+ NautilusInfoProvider *provider;
+ NautilusOperationHandle *handle;
+ NautilusOperationResult result;
+} InfoProviderResponse;
+
+typedef gboolean (* RequestCheck) (Request);
+typedef gboolean (* FileCheck) (NautilusFile *);
+
+/* Current number of async. jobs. */
+static int async_job_count;
+static GHashTable *waiting_directories;
+#ifdef DEBUG_ASYNC_JOBS
+static GHashTable *async_jobs;
+#endif
+
+/* Forward declarations for functions that need them. */
+static void deep_count_load (DeepCountState *state,
+ GFile *location);
+static gboolean request_is_satisfied (NautilusDirectory *directory,
+ NautilusFile *file,
+ Request request);
+static void cancel_loading_attributes (NautilusDirectory *directory,
+ NautilusFileAttributes file_attributes);
+static void add_all_files_to_work_queue (NautilusDirectory *directory);
+static void link_info_done (NautilusDirectory *directory,
+ NautilusFile *file,
+ const char *uri,
+ const char *name,
+ GIcon *icon,
+ gboolean is_launcher,
+ gboolean is_foreign);
+static void move_file_to_low_priority_queue (NautilusDirectory *directory,
+ NautilusFile *file);
+static void move_file_to_extension_queue (NautilusDirectory *directory,
+ NautilusFile *file);
+static void nautilus_directory_invalidate_file_attributes (NautilusDirectory *directory,
+ NautilusFileAttributes file_attributes);
+
+/* Some helpers for case-insensitive strings.
+ * Move to nautilus-glib-extensions?
+ */
+
+static gboolean
+istr_equal (gconstpointer v, gconstpointer v2)
+{
+ return g_ascii_strcasecmp (v, v2) == 0;
+}
+
+static guint
+istr_hash (gconstpointer key)
+{
+ const char *p;
+ guint h;
+
+ h = 0;
+ for (p = key; *p != '\0'; p++) {
+ h = (h << 5) - h + g_ascii_tolower (*p);
+ }
+
+ return h;
+}
+
+static GHashTable *
+istr_set_new (void)
+{
+ return g_hash_table_new_full (istr_hash, istr_equal, g_free, NULL);
+}
+
+static void
+istr_set_insert (GHashTable *table, const char *istr)
+{
+ char *key;
+
+ key = g_strdup (istr);
+ g_hash_table_replace (table, key, key);
+}
+
+static void
+add_istr_to_list (gpointer key, gpointer value, gpointer callback_data)
+{
+ GList **list;
+
+ list = callback_data;
+ *list = g_list_prepend (*list, g_strdup (key));
+}
+
+static GList *
+istr_set_get_as_list (GHashTable *table)
+{
+ GList *list;
+
+ list = NULL;
+ g_hash_table_foreach (table, add_istr_to_list, &list);
+ return list;
+}
+
+static void
+istr_set_destroy (GHashTable *table)
+{
+ g_hash_table_destroy (table);
+}
+
+static void
+request_counter_add_request (RequestCounter counter,
+ Request request)
+{
+ guint i;
+
+ for (i = 0; i < REQUEST_TYPE_LAST; i++) {
+ if (REQUEST_WANTS_TYPE (request, i)) {
+ counter[i]++;
+ }
+ }
+}
+
+static void
+request_counter_remove_request (RequestCounter counter,
+ Request request)
+{
+ guint i;
+
+ for (i = 0; i < REQUEST_TYPE_LAST; i++) {
+ if (REQUEST_WANTS_TYPE (request, i)) {
+ counter[i]--;
+ }
+ }
+}
+
+#if 0
+static void
+nautilus_directory_verify_request_counts (NautilusDirectory *directory)
+{
+ GList *l;
+ RequestCounter counters;
+ int i;
+ gboolean fail;
+
+ fail = FALSE;
+ for (i = 0; i < REQUEST_TYPE_LAST; i ++) {
+ counters[i] = 0;
+ }
+ for (l = directory->details->monitor_list; l != NULL; l = l->next) {
+ Monitor *monitor = l->data;
+ request_counter_add_request (counters, monitor->request);
+ }
+ for (i = 0; i < REQUEST_TYPE_LAST; i ++) {
+ if (counters[i] != directory->details->monitor_counters[i]) {
+ g_warning ("monitor counter for %i is wrong, expecting %d but found %d",
+ i, counters[i], directory->details->monitor_counters[i]);
+ fail = TRUE;
+ }
+ }
+ for (i = 0; i < REQUEST_TYPE_LAST; i ++) {
+ counters[i] = 0;
+ }
+ for (l = directory->details->call_when_ready_list; l != NULL; l = l->next) {
+ ReadyCallback *callback = l->data;
+ request_counter_add_request (counters, callback->request);
+ }
+ for (i = 0; i < REQUEST_TYPE_LAST; i ++) {
+ if (counters[i] != directory->details->call_when_ready_counters[i]) {
+ g_warning ("call when ready counter for %i is wrong, expecting %d but found %d",
+ i, counters[i], directory->details->call_when_ready_counters[i]);
+ fail = TRUE;
+ }
+ }
+ g_assert (!fail);
+}
+#endif
+
+/* Start a job. This is really just a way of limiting the number of
+ * async. requests that we issue at any given time. Without this, the
+ * number of requests is unbounded.
+ */
+static gboolean
+async_job_start (NautilusDirectory *directory,
+ const char *job)
+{
+#ifdef DEBUG_ASYNC_JOBS
+ char *key;
+#endif
+
+#ifdef DEBUG_START_STOP
+ g_message ("starting %s in %p", job, directory->details->location);
+#endif
+
+ g_assert (async_job_count >= 0);
+ g_assert (async_job_count <= MAX_ASYNC_JOBS);
+
+ if (async_job_count >= MAX_ASYNC_JOBS) {
+ if (waiting_directories == NULL) {
+ waiting_directories = g_hash_table_new (NULL, NULL);
+ }
+
+ g_hash_table_insert (waiting_directories,
+ directory,
+ directory);
+
+ return FALSE;
+ }
+
+#ifdef DEBUG_ASYNC_JOBS
+ {
+ char *uri;
+ if (async_jobs == NULL) {
+ async_jobs = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+ uri = nautilus_directory_get_uri (directory);
+ key = g_strconcat (uri, ": ", job, NULL);
+ if (g_hash_table_lookup (async_jobs, key) != NULL) {
+ g_warning ("same job twice: %s in %s",
+ job, uri);
+ }
+ g_free (uri);
+ g_hash_table_insert (async_jobs, key, directory);
+ }
+#endif
+
+ async_job_count += 1;
+ return TRUE;
+}
+
+/* End a job. */
+static void
+async_job_end (NautilusDirectory *directory,
+ const char *job)
+{
+#ifdef DEBUG_ASYNC_JOBS
+ char *key;
+ gpointer table_key, value;
+#endif
+
+#ifdef DEBUG_START_STOP
+ g_message ("stopping %s in %p", job, directory->details->location);
+#endif
+
+ g_assert (async_job_count > 0);
+
+#ifdef DEBUG_ASYNC_JOBS
+ {
+ char *uri;
+ uri = nautilus_directory_get_uri (directory);
+ g_assert (async_jobs != NULL);
+ key = g_strconcat (uri, ": ", job, NULL);
+ if (!g_hash_table_lookup_extended (async_jobs, key, &table_key, &value)) {
+ g_warning ("ending job we didn't start: %s in %s",
+ job, uri);
+ } else {
+ g_hash_table_remove (async_jobs, key);
+ g_free (table_key);
+ }
+ g_free (uri);
+ g_free (key);
+ }
+#endif
+
+ async_job_count -= 1;
+}
+
+/* Helper to get one value from a hash table. */
+static void
+get_one_value_callback (gpointer key, gpointer value, gpointer callback_data)
+{
+ gpointer *returned_value;
+
+ returned_value = callback_data;
+ *returned_value = value;
+}
+
+/* return a single value from a hash table. */
+static gpointer
+get_one_value (GHashTable *table)
+{
+ gpointer value;
+
+ value = NULL;
+ if (table != NULL) {
+ g_hash_table_foreach (table, get_one_value_callback, &value);
+ }
+ return value;
+}
+
+/* Wake up directories that are "blocked" as long as there are job
+ * slots available.
+ */
+static void
+async_job_wake_up (void)
+{
+ static gboolean already_waking_up = FALSE;
+ gpointer value;
+
+ g_assert (async_job_count >= 0);
+ g_assert (async_job_count <= MAX_ASYNC_JOBS);
+
+ if (already_waking_up) {
+ return;
+ }
+
+ already_waking_up = TRUE;
+ while (async_job_count < MAX_ASYNC_JOBS) {
+ value = get_one_value (waiting_directories);
+ if (value == NULL) {
+ break;
+ }
+ g_hash_table_remove (waiting_directories, value);
+ nautilus_directory_async_state_changed
+ (NAUTILUS_DIRECTORY (value));
+ }
+ already_waking_up = FALSE;
+}
+
+static void
+directory_count_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->count_in_progress != NULL) {
+ g_cancellable_cancel (directory->details->count_in_progress->cancellable);
+ directory->details->count_in_progress = NULL;
+ }
+}
+
+static void
+deep_count_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->deep_count_in_progress != NULL) {
+ g_assert (NAUTILUS_IS_FILE (directory->details->deep_count_file));
+
+ g_cancellable_cancel (directory->details->deep_count_in_progress->cancellable);
+
+ directory->details->deep_count_file->details->deep_counts_status = NAUTILUS_REQUEST_NOT_STARTED;
+
+ directory->details->deep_count_in_progress->directory = NULL;
+ directory->details->deep_count_in_progress = NULL;
+ directory->details->deep_count_file = NULL;
+
+ async_job_end (directory, "deep count");
+ }
+}
+
+static void
+mime_list_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->mime_list_in_progress != NULL) {
+ g_cancellable_cancel (directory->details->mime_list_in_progress->cancellable);
+ }
+}
+
+static void
+link_info_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->link_info_read_state != NULL) {
+ g_cancellable_cancel (directory->details->link_info_read_state->cancellable);
+ directory->details->link_info_read_state->directory = NULL;
+ directory->details->link_info_read_state = NULL;
+ async_job_end (directory, "link info");
+ }
+}
+
+static void
+thumbnail_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->thumbnail_state != NULL) {
+ g_cancellable_cancel (directory->details->thumbnail_state->cancellable);
+ directory->details->thumbnail_state->directory = NULL;
+ directory->details->thumbnail_state = NULL;
+ async_job_end (directory, "thumbnail");
+ }
+}
+
+static void
+mount_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->mount_state != NULL) {
+ g_cancellable_cancel (directory->details->mount_state->cancellable);
+ directory->details->mount_state->directory = NULL;
+ directory->details->mount_state = NULL;
+ async_job_end (directory, "mount");
+ }
+}
+
+static void
+file_info_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->get_info_in_progress != NULL) {
+ g_cancellable_cancel (directory->details->get_info_in_progress->cancellable);
+ directory->details->get_info_in_progress->directory = NULL;
+ directory->details->get_info_in_progress = NULL;
+ directory->details->get_info_file = NULL;
+
+ async_job_end (directory, "file info");
+ }
+}
+
+static void
+new_files_cancel (NautilusDirectory *directory)
+{
+ GList *l;
+ NewFilesState *state;
+
+ if (directory->details->new_files_in_progress != NULL) {
+ for (l = directory->details->new_files_in_progress; l != NULL; l = l->next) {
+ state = l->data;
+ g_cancellable_cancel (state->cancellable);
+ state->directory = NULL;
+ }
+ g_list_free (directory->details->new_files_in_progress);
+ directory->details->new_files_in_progress = NULL;
+ }
+}
+
+static int
+monitor_key_compare (gconstpointer a,
+ gconstpointer data)
+{
+ const Monitor *monitor;
+ const Monitor *compare_monitor;
+
+ monitor = a;
+ compare_monitor = data;
+
+ if (monitor->client < compare_monitor->client) {
+ return -1;
+ }
+ if (monitor->client > compare_monitor->client) {
+ return +1;
+ }
+
+ if (monitor->file < compare_monitor->file) {
+ return -1;
+ }
+ if (monitor->file > compare_monitor->file) {
+ return +1;
+ }
+
+ return 0;
+}
+
+static GList *
+find_monitor (NautilusDirectory *directory,
+ NautilusFile *file,
+ gconstpointer client)
+{
+ Monitor monitor;
+
+ monitor.client = client;
+ monitor.file = file;
+
+ return g_list_find_custom (directory->details->monitor_list,
+ &monitor,
+ monitor_key_compare);
+}
+
+static void
+remove_monitor_link (NautilusDirectory *directory,
+ GList *link)
+{
+ Monitor *monitor;
+
+ if (link != NULL) {
+ monitor = link->data;
+ request_counter_remove_request (directory->details->monitor_counters,
+ monitor->request);
+ directory->details->monitor_list =
+ g_list_remove_link (directory->details->monitor_list, link);
+ g_free (monitor);
+ g_list_free_1 (link);
+ }
+}
+
+static void
+remove_monitor (NautilusDirectory *directory,
+ NautilusFile *file,
+ gconstpointer client)
+{
+ remove_monitor_link (directory, find_monitor (directory, file, client));
+}
+
+Request
+nautilus_directory_set_up_request (NautilusFileAttributes file_attributes)
+{
+ Request request;
+
+ request = 0;
+
+ if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT) != 0) {
+ REQUEST_SET_TYPE (request, REQUEST_DIRECTORY_COUNT);
+ }
+
+ if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS) != 0) {
+ REQUEST_SET_TYPE (request, REQUEST_DEEP_COUNT);
+ }
+
+ if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES) != 0) {
+ REQUEST_SET_TYPE (request, REQUEST_MIME_LIST);
+ }
+ if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_INFO) != 0) {
+ REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
+ }
+
+ if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_LINK_INFO) {
+ REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
+ REQUEST_SET_TYPE (request, REQUEST_LINK_INFO);
+ }
+
+ if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO) != 0) {
+ REQUEST_SET_TYPE (request, REQUEST_EXTENSION_INFO);
+ }
+
+ if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL) {
+ REQUEST_SET_TYPE (request, REQUEST_THUMBNAIL);
+ REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
+ }
+
+ if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_MOUNT) {
+ REQUEST_SET_TYPE (request, REQUEST_MOUNT);
+ REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
+ }
+
+ if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO) {
+ REQUEST_SET_TYPE (request, REQUEST_FILESYSTEM_INFO);
+ }
+
+ return request;
+}
+
+static void
+mime_db_changed_callback (GObject *ignore, NautilusDirectory *dir)
+{
+ NautilusFileAttributes attrs;
+
+ g_assert (dir != NULL);
+ g_assert (dir->details != NULL);
+
+ attrs = NAUTILUS_FILE_ATTRIBUTE_INFO |
+ NAUTILUS_FILE_ATTRIBUTE_LINK_INFO |
+ NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
+
+ nautilus_directory_force_reload_internal (dir, attrs);
+}
+
+void
+nautilus_directory_monitor_add_internal (NautilusDirectory *directory,
+ NautilusFile *file,
+ gconstpointer client,
+ gboolean monitor_hidden_files,
+ NautilusFileAttributes file_attributes,
+ NautilusDirectoryCallback callback,
+ gpointer callback_data)
+{
+ Monitor *monitor;
+ GList *file_list;
+ char *file_uri = NULL;
+ char *dir_uri = NULL;
+
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+
+ if (file != NULL)
+ file_uri = nautilus_file_get_uri (file);
+ if (directory != NULL)
+ dir_uri = nautilus_directory_get_uri (directory);
+ nautilus_profile_start ("uri %s file-uri %s client %p", dir_uri, file_uri, client);
+ g_free (dir_uri);
+ g_free (file_uri);
+
+ /* Replace any current monitor for this client/file pair. */
+ remove_monitor (directory, file, client);
+
+ /* Add the new monitor. */
+ monitor = g_new (Monitor, 1);
+ monitor->file = file;
+ monitor->monitor_hidden_files = monitor_hidden_files;
+ monitor->client = client;
+ monitor->request = nautilus_directory_set_up_request (file_attributes);
+
+ if (file == NULL) {
+ REQUEST_SET_TYPE (monitor->request, REQUEST_FILE_LIST);
+ }
+ directory->details->monitor_list =
+ g_list_prepend (directory->details->monitor_list, monitor);
+ request_counter_add_request (directory->details->monitor_counters,
+ monitor->request);
+
+ if (callback != NULL) {
+ file_list = nautilus_directory_get_file_list (directory);
+ (* callback) (directory, file_list, callback_data);
+ nautilus_file_list_free (file_list);
+ }
+
+ /* Start the "real" monitoring (FAM or whatever). */
+ /* We always monitor the whole directory since in practice
+ * nautilus almost always shows the whole directory anyway, and
+ * it allows us to avoid one file monitor per file in a directory.
+ */
+ if (directory->details->monitor == NULL) {
+ directory->details->monitor = nautilus_monitor_directory (directory->details->location);
+ }
+
+
+ if (REQUEST_WANTS_TYPE (monitor->request, REQUEST_FILE_INFO) &&
+ directory->details->mime_db_monitor == 0) {
+ directory->details->mime_db_monitor =
+ g_signal_connect_object (nautilus_signaller_get_current (),
+ "mime-data-changed",
+ G_CALLBACK (mime_db_changed_callback), directory, 0);
+ }
+
+ /* Put the monitor file or all the files on the work queue. */
+ if (file != NULL) {
+ nautilus_directory_add_file_to_work_queue (directory, file);
+ } else {
+ add_all_files_to_work_queue (directory);
+ }
+
+ /* Kick off I/O. */
+ nautilus_directory_async_state_changed (directory);
+ nautilus_profile_end (NULL);
+}
+
+static void
+set_file_unconfirmed (NautilusFile *file, gboolean unconfirmed)
+{
+ NautilusDirectory *directory;
+
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (unconfirmed == FALSE || unconfirmed == TRUE);
+
+ if (file->details->unconfirmed == unconfirmed) {
+ return;
+ }
+ file->details->unconfirmed = unconfirmed;
+
+ directory = file->details->directory;
+ if (unconfirmed) {
+ directory->details->confirmed_file_count--;
+ } else {
+ directory->details->confirmed_file_count++;
+ }
+}
+
+static gboolean show_hidden_files = TRUE;
+
+static void
+show_hidden_files_changed_callback (gpointer callback_data)
+{
+ show_hidden_files = g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES);
+}
+
+static gboolean
+should_skip_file (NautilusDirectory *directory, GFileInfo *info)
+{
+ static gboolean show_hidden_files_changed_callback_installed = FALSE;
+
+ /* Add the callback once for the life of our process */
+ if (!show_hidden_files_changed_callback_installed) {
+ g_signal_connect_swapped (gtk_filechooser_preferences,
+ "changed::" NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
+ G_CALLBACK(show_hidden_files_changed_callback),
+ NULL);
+
+ show_hidden_files_changed_callback_installed = TRUE;
+
+ /* Peek for the first time */
+ show_hidden_files_changed_callback (NULL);
+ }
+
+ if (!show_hidden_files &&
+ (g_file_info_get_is_hidden (info) ||
+ g_file_info_get_is_backup (info))) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+dequeue_pending_idle_callback (gpointer callback_data)
+{
+ NautilusDirectory *directory;
+ GList *pending_file_info;
+ GList *node, *next;
+ NautilusFile *file;
+ GList *changed_files, *added_files;
+ GFileInfo *file_info;
+ const char *mimetype, *name;
+ DirectoryLoadState *dir_load_state;
+
+ directory = NAUTILUS_DIRECTORY (callback_data);
+
+ nautilus_directory_ref (directory);
+
+ nautilus_profile_start ("nitems %d", g_list_length (directory->details->pending_file_info));
+
+ directory->details->dequeue_pending_idle_id = 0;
+
+ /* Handle the files in the order we saw them. */
+ pending_file_info = g_list_reverse (directory->details->pending_file_info);
+ directory->details->pending_file_info = NULL;
+
+ /* If we are no longer monitoring, then throw away these. */
+ if (!nautilus_directory_is_file_list_monitored (directory)) {
+ nautilus_directory_async_state_changed (directory);
+ goto drain;
+ }
+
+ added_files = NULL;
+ changed_files = NULL;
+
+ dir_load_state = directory->details->directory_load_in_progress;
+
+ /* Build a list of NautilusFile objects. */
+ for (node = pending_file_info; node != NULL; node = node->next) {
+ file_info = node->data;
+
+ name = g_file_info_get_name (file_info);
+
+ /* Update the file count. */
+ /* FIXME bugzilla.gnome.org 45063: This could count a
+ * file twice if we get it from both load_directory
+ * and from new_files_callback. Not too hard to fix by
+ * moving this into the actual callback instead of
+ * waiting for the idle function.
+ */
+ if (dir_load_state &&
+ !should_skip_file (directory, file_info)) {
+ dir_load_state->load_file_count += 1;
+
+ /* Add the MIME type to the set. */
+ mimetype = g_file_info_get_content_type (file_info);
+ if (mimetype != NULL) {
+ istr_set_insert (dir_load_state->load_mime_list_hash,
+ mimetype);
+ }
+ }
+
+ /* check if the file already exists */
+ file = nautilus_directory_find_file_by_name (directory, name);
+ if (file != NULL) {
+ /* file already exists in dir, check if we still need to
+ * emit file_added or if it changed */
+ set_file_unconfirmed (file, FALSE);
+ if (!file->details->is_added) {
+ /* We consider this newly added even if its in the list.
+ * This can happen if someone called nautilus_file_get_by_uri()
+ * on a file in the folder before the add signal was
+ * emitted */
+ nautilus_file_ref (file);
+ file->details->is_added = TRUE;
+ added_files = g_list_prepend (added_files, file);
+ } else if (nautilus_file_update_info (file, file_info)) {
+ /* File changed, notify about the change. */
+ nautilus_file_ref (file);
+ changed_files = g_list_prepend (changed_files, file);
+ }
+ } else {
+ /* new file, create a nautilus file object and add it to the list */
+ file = nautilus_file_new_from_info (directory, file_info);
+ nautilus_directory_add_file (directory, file);
+ file->details->is_added = TRUE;
+ added_files = g_list_prepend (added_files, file);
+ }
+ }
+
+ /* If we are done loading, then we assume that any unconfirmed
+ * files are gone.
+ */
+ if (directory->details->directory_loaded) {
+ for (node = directory->details->file_list;
+ node != NULL; node = next) {
+ file = NAUTILUS_FILE (node->data);
+ next = node->next;
+
+ if (file->details->unconfirmed) {
+ nautilus_file_ref (file);
+ changed_files = g_list_prepend (changed_files, file);
+
+ nautilus_file_mark_gone (file);
+ }
+ }
+ }
+
+ /* Send the changed and added signals. */
+ nautilus_directory_emit_change_signals (directory, changed_files);
+ nautilus_file_list_free (changed_files);
+ nautilus_directory_emit_files_added (directory, added_files);
+ nautilus_file_list_free (added_files);
+
+ if (directory->details->directory_loaded &&
+ !directory->details->directory_loaded_sent_notification) {
+ /* Send the done_loading signal. */
+ nautilus_directory_emit_done_loading (directory);
+
+ if (dir_load_state) {
+ file = dir_load_state->load_directory_file;
+
+ file->details->directory_count = dir_load_state->load_file_count;
+ file->details->directory_count_is_up_to_date = TRUE;
+ file->details->got_directory_count = TRUE;
+
+ file->details->got_mime_list = TRUE;
+ file->details->mime_list_is_up_to_date = TRUE;
+ g_list_free_full (file->details->mime_list, g_free);
+ file->details->mime_list = istr_set_get_as_list
+ (dir_load_state->load_mime_list_hash);
+
+ nautilus_file_changed (file);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+
+ directory->details->directory_loaded_sent_notification = TRUE;
+ }
+
+ drain:
+ g_list_free_full (pending_file_info, g_object_unref);
+
+ /* Get the state machine running again. */
+ nautilus_directory_async_state_changed (directory);
+
+ nautilus_profile_end (NULL);
+
+ nautilus_directory_unref (directory);
+ return FALSE;
+}
+
+void
+nautilus_directory_schedule_dequeue_pending (NautilusDirectory *directory)
+{
+ if (directory->details->dequeue_pending_idle_id == 0) {
+ directory->details->dequeue_pending_idle_id
+ = g_idle_add (dequeue_pending_idle_callback, directory);
+ }
+}
+
+static void
+directory_load_one (NautilusDirectory *directory,
+ GFileInfo *info)
+{
+ if (info == NULL) {
+ return;
+ }
+
+ if (g_file_info_get_name (info) == NULL) {
+ char *uri;
+
+ uri = nautilus_directory_get_uri (directory);
+ g_warning ("Got GFileInfo with NULL name in %s, ignoring. This shouldn't happen unless the gvfs backend is broken.\n", uri);
+ g_free (uri);
+
+ return;
+ }
+
+ /* Arrange for the "loading" part of the work. */
+ g_object_ref (info);
+ directory->details->pending_file_info
+ = g_list_prepend (directory->details->pending_file_info, info);
+ nautilus_directory_schedule_dequeue_pending (directory);
+}
+
+static void
+directory_load_cancel (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+ DirectoryLoadState *state;
+
+ state = directory->details->directory_load_in_progress;
+ if (state != NULL) {
+ file = state->load_directory_file;
+ file->details->loading_directory = FALSE;
+ if (file->details->directory != directory) {
+ nautilus_directory_async_state_changed (file->details->directory);
+ }
+
+ g_cancellable_cancel (state->cancellable);
+ state->directory = NULL;
+ directory->details->directory_load_in_progress = NULL;
+ async_job_end (directory, "file list");
+ }
+}
+
+static void
+file_list_cancel (NautilusDirectory *directory)
+{
+ directory_load_cancel (directory);
+
+ if (directory->details->dequeue_pending_idle_id != 0) {
+ g_source_remove (directory->details->dequeue_pending_idle_id);
+ directory->details->dequeue_pending_idle_id = 0;
+ }
+
+ if (directory->details->pending_file_info != NULL) {
+ g_list_free_full (directory->details->pending_file_info, g_object_unref);
+ directory->details->pending_file_info = NULL;
+ }
+}
+
+static void
+directory_load_done (NautilusDirectory *directory,
+ GError *error)
+{
+ GList *node;
+
+ nautilus_profile_start (NULL);
+ g_object_ref (directory);
+
+ directory->details->directory_loaded = TRUE;
+ directory->details->directory_loaded_sent_notification = FALSE;
+
+ if (error != NULL) {
+ /* The load did not complete successfully. This means
+ * we don't know the status of the files in this directory.
+ * We clear the unconfirmed bit on each file here so that
+ * they won't be marked "gone" later -- we don't know enough
+ * about them to know whether they are really gone.
+ */
+ for (node = directory->details->file_list;
+ node != NULL; node = node->next) {
+ set_file_unconfirmed (NAUTILUS_FILE (node->data), FALSE);
+ }
+
+ nautilus_directory_emit_load_error (directory, error);
+ }
+
+ /* Call the idle function right away. */
+ if (directory->details->dequeue_pending_idle_id != 0) {
+ g_source_remove (directory->details->dequeue_pending_idle_id);
+ }
+ dequeue_pending_idle_callback (directory);
+
+ directory_load_cancel (directory);
+
+ g_object_unref (directory);
+ nautilus_profile_end (NULL);
+}
+
+void
+nautilus_directory_monitor_remove_internal (NautilusDirectory *directory,
+ NautilusFile *file,
+ gconstpointer client)
+{
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+ g_assert (file == NULL || NAUTILUS_IS_FILE (file));
+ g_assert (client != NULL);
+
+ remove_monitor (directory, file, client);
+
+ if (directory->details->monitor != NULL
+ && directory->details->monitor_list == NULL) {
+ nautilus_monitor_cancel (directory->details->monitor);
+ directory->details->monitor = NULL;
+ }
+
+ /* XXX - do we need to remove anything from the work queue? */
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+FileMonitors *
+nautilus_directory_remove_file_monitors (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ GList *result, **list, *node, *next;
+ Monitor *monitor;
+
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+
+ result = NULL;
+
+ list = &directory->details->monitor_list;
+ for (node = directory->details->monitor_list; node != NULL; node = next) {
+ next = node->next;
+ monitor = node->data;
+
+ if (monitor->file == file) {
+ *list = g_list_remove_link (*list, node);
+ result = g_list_concat (node, result);
+ request_counter_remove_request (directory->details->monitor_counters,
+ monitor->request);
+ }
+ }
+
+ /* XXX - do we need to remove anything from the work queue? */
+
+ nautilus_directory_async_state_changed (directory);
+
+ return (FileMonitors *) result;
+}
+
+void
+nautilus_directory_add_file_monitors (NautilusDirectory *directory,
+ NautilusFile *file,
+ FileMonitors *monitors)
+{
+ GList **list;
+ GList *l;
+ Monitor *monitor;
+
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+
+ if (monitors == NULL) {
+ return;
+ }
+
+ for (l = (GList *)monitors; l != NULL; l = l->next) {
+ monitor = l->data;
+ request_counter_add_request (directory->details->monitor_counters,
+ monitor->request);
+ }
+
+ list = &directory->details->monitor_list;
+ *list = g_list_concat (*list, (GList *) monitors);
+
+ nautilus_directory_add_file_to_work_queue (directory, file);
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+static int
+ready_callback_key_compare (gconstpointer a, gconstpointer b)
+{
+ const ReadyCallback *callback_a, *callback_b;
+
+ callback_a = a;
+ callback_b = b;
+
+ if (callback_a->file < callback_b->file) {
+ return -1;
+ }
+ if (callback_a->file > callback_b->file) {
+ return 1;
+ }
+ if (callback_a->file == NULL) {
+ /* ANSI C doesn't allow ordered compares of function pointers, so we cast them to
+ * normal pointers to make some overly pedantic compilers (*cough* HP-UX *cough*)
+ * compile this. Of course, on any compiler where ordered function pointers actually
+ * break this probably won't work, but at least it will compile on platforms where it
+ * works, but stupid compilers won't let you use it.
+ */
+ if ((void *)callback_a->callback.directory < (void *)callback_b->callback.directory) {
+ return -1;
+ }
+ if ((void *)callback_a->callback.directory > (void *)callback_b->callback.directory) {
+ return 1;
+ }
+ } else {
+ if ((void *)callback_a->callback.file < (void *)callback_b->callback.file) {
+ return -1;
+ }
+ if ((void *)callback_a->callback.file > (void *)callback_b->callback.file) {
+ return 1;
+ }
+ }
+ if (callback_a->callback_data < callback_b->callback_data) {
+ return -1;
+ }
+ if (callback_a->callback_data > callback_b->callback_data) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+ready_callback_key_compare_only_active (gconstpointer a, gconstpointer b)
+{
+ const ReadyCallback *callback_a;
+
+ callback_a = a;
+
+ /* Non active callbacks never match */
+ if (!callback_a->active) {
+ return -1;
+ }
+
+ return ready_callback_key_compare (a, b);
+}
+
+static void
+ready_callback_call (NautilusDirectory *directory,
+ const ReadyCallback *callback)
+{
+ GList *file_list;
+
+ /* Call the callback. */
+ if (callback->file != NULL) {
+ if (callback->callback.file) {
+ (* callback->callback.file) (callback->file,
+ callback->callback_data);
+ }
+ } else if (callback->callback.directory != NULL) {
+ if (directory == NULL ||
+ !REQUEST_WANTS_TYPE (callback->request, REQUEST_FILE_LIST)) {
+ file_list = NULL;
+ } else {
+ file_list = nautilus_directory_get_file_list (directory);
+ }
+
+ /* Pass back the file list if the user was waiting for it. */
+ (* callback->callback.directory) (directory,
+ file_list,
+ callback->callback_data);
+
+ nautilus_file_list_free (file_list);
+ }
+}
+
+void
+nautilus_directory_call_when_ready_internal (NautilusDirectory *directory,
+ NautilusFile *file,
+ NautilusFileAttributes file_attributes,
+ gboolean wait_for_file_list,
+ NautilusDirectoryCallback directory_callback,
+ NautilusFileCallback file_callback,
+ gpointer callback_data)
+{
+ ReadyCallback callback;
+
+ g_assert (directory == NULL || NAUTILUS_IS_DIRECTORY (directory));
+ g_assert (file == NULL || NAUTILUS_IS_FILE (file));
+ g_assert (file != NULL || directory_callback != NULL);
+
+ /* Construct a callback object. */
+ callback.active = TRUE;
+ callback.file = file;
+ if (file == NULL) {
+ callback.callback.directory = directory_callback;
+ } else {
+ callback.callback.file = file_callback;
+ }
+ callback.callback_data = callback_data;
+ callback.request = nautilus_directory_set_up_request (file_attributes);
+ if (wait_for_file_list) {
+ REQUEST_SET_TYPE (callback.request, REQUEST_FILE_LIST);
+ }
+
+ /* Handle the NULL case. */
+ if (directory == NULL) {
+ ready_callback_call (NULL, &callback);
+ return;
+ }
+
+ /* Check if the callback is already there. */
+ if (g_list_find_custom (directory->details->call_when_ready_list,
+ &callback,
+ ready_callback_key_compare_only_active) != NULL) {
+ if (file_callback != NULL && directory_callback != NULL) {
+ g_warning ("tried to add a new callback while an old one was pending");
+ }
+ /* NULL callback means, just read it. Conflicts are ok. */
+ return;
+ }
+
+ /* Add the new callback to the list. */
+ directory->details->call_when_ready_list = g_list_prepend
+ (directory->details->call_when_ready_list,
+ g_memdup (&callback, sizeof (callback)));
+ request_counter_add_request (directory->details->call_when_ready_counters,
+ callback.request);
+
+ /* Put the callback file or all the files on the work queue. */
+ if (file != NULL) {
+ nautilus_directory_add_file_to_work_queue (directory, file);
+ } else {
+ add_all_files_to_work_queue (directory);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+gboolean
+nautilus_directory_check_if_ready_internal (NautilusDirectory *directory,
+ NautilusFile *file,
+ NautilusFileAttributes file_attributes)
+{
+ Request request;
+
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+
+ request = nautilus_directory_set_up_request (file_attributes);
+ return request_is_satisfied (directory, file, request);
+}
+
+static void
+remove_callback_link_keep_data (NautilusDirectory *directory,
+ GList *link)
+{
+ ReadyCallback *callback;
+
+ callback = link->data;
+
+ directory->details->call_when_ready_list = g_list_remove_link
+ (directory->details->call_when_ready_list, link);
+
+ request_counter_remove_request (directory->details->call_when_ready_counters,
+ callback->request);
+ g_list_free_1 (link);
+}
+
+static void
+remove_callback_link (NautilusDirectory *directory,
+ GList *link)
+{
+ ReadyCallback *callback;
+
+ callback = link->data;
+ remove_callback_link_keep_data (directory, link);
+ g_free (callback);
+}
+
+void
+nautilus_directory_cancel_callback_internal (NautilusDirectory *directory,
+ NautilusFile *file,
+ NautilusDirectoryCallback directory_callback,
+ NautilusFileCallback file_callback,
+ gpointer callback_data)
+{
+ ReadyCallback callback;
+ GList *node;
+
+ if (directory == NULL) {
+ return;
+ }
+
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+ g_assert (file == NULL || NAUTILUS_IS_FILE (file));
+ g_assert (file != NULL || directory_callback != NULL);
+ g_assert (file == NULL || file_callback != NULL);
+
+ /* Construct a callback object. */
+ callback.file = file;
+ if (file == NULL) {
+ callback.callback.directory = directory_callback;
+ } else {
+ callback.callback.file = file_callback;
+ }
+ callback.callback_data = callback_data;
+
+ /* Remove all queued callback from the list (including non-active). */
+ do {
+ node = g_list_find_custom (directory->details->call_when_ready_list,
+ &callback,
+ ready_callback_key_compare);
+ if (node != NULL) {
+ remove_callback_link (directory, node);
+
+ nautilus_directory_async_state_changed (directory);
+ }
+ } while (node != NULL);
+}
+
+static void
+new_files_state_unref (NewFilesState *state)
+{
+ state->count--;
+
+ if (state->count == 0) {
+ if (state->directory) {
+ state->directory->details->new_files_in_progress =
+ g_list_remove (state->directory->details->new_files_in_progress,
+ state);
+ }
+
+ g_object_unref (state->cancellable);
+ g_free (state);
+ }
+}
+
+static void
+new_files_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusDirectory *directory;
+ GFileInfo *info;
+ NewFilesState *state;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ new_files_state_unref (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ /* Queue up the new file. */
+ info = g_file_query_info_finish (G_FILE (source_object), res, NULL);
+ if (info != NULL) {
+ directory_load_one (directory, info);
+ g_object_unref (info);
+ }
+
+ new_files_state_unref (state);
+
+ nautilus_directory_unref (directory);
+}
+
+void
+nautilus_directory_get_info_for_new_files (NautilusDirectory *directory,
+ GList *location_list)
+{
+ NewFilesState *state;
+ GFile *location;
+ GList *l;
+
+ if (location_list == NULL) {
+ return;
+ }
+
+ state = g_new (NewFilesState, 1);
+ state->directory = directory;
+ state->cancellable = g_cancellable_new ();
+ state->count = 0;
+
+ for (l = location_list; l != NULL; l = l->next) {
+ location = l->data;
+
+ state->count++;
+
+ g_file_query_info_async (location,
+ NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
+ 0,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ new_files_callback, state);
+ }
+
+ directory->details->new_files_in_progress
+ = g_list_prepend (directory->details->new_files_in_progress,
+ state);
+}
+
+void
+nautilus_async_destroying_file (NautilusFile *file)
+{
+ NautilusDirectory *directory;
+ gboolean changed;
+ GList *node, *next;
+ ReadyCallback *callback;
+ Monitor *monitor;
+
+ directory = file->details->directory;
+ changed = FALSE;
+
+ /* Check for callbacks. */
+ for (node = directory->details->call_when_ready_list; node != NULL; node = next) {
+ next = node->next;
+ callback = node->data;
+
+ if (callback->file == file) {
+ /* Client should have cancelled callback. */
+ if (callback->active) {
+ g_warning ("destroyed file has call_when_ready pending");
+ }
+ remove_callback_link (directory, node);
+ changed = TRUE;
+ }
+ }
+
+ /* Check for monitors. */
+ for (node = directory->details->monitor_list; node != NULL; node = next) {
+ next = node->next;
+ monitor = node->data;
+
+ if (monitor->file == file) {
+ /* Client should have removed monitor earlier. */
+ g_warning ("destroyed file still being monitored");
+ remove_monitor_link (directory, node);
+ changed = TRUE;
+ }
+ }
+
+ /* Check if it's a file that's currently being worked on.
+ * If so, make that NULL so it gets canceled right away.
+ */
+ if (directory->details->count_in_progress != NULL &&
+ directory->details->count_in_progress->count_file == file) {
+ directory->details->count_in_progress->count_file = NULL;
+ changed = TRUE;
+ }
+ if (directory->details->deep_count_file == file) {
+ directory->details->deep_count_file = NULL;
+ changed = TRUE;
+ }
+ if (directory->details->mime_list_in_progress != NULL &&
+ directory->details->mime_list_in_progress->mime_list_file == file) {
+ directory->details->mime_list_in_progress->mime_list_file = NULL;
+ changed = TRUE;
+ }
+ if (directory->details->get_info_file == file) {
+ directory->details->get_info_file = NULL;
+ changed = TRUE;
+ }
+ if (directory->details->link_info_read_state != NULL &&
+ directory->details->link_info_read_state->file == file) {
+ directory->details->link_info_read_state->file = NULL;
+ changed = TRUE;
+ }
+ if (directory->details->extension_info_file == file) {
+ directory->details->extension_info_file = NULL;
+ changed = TRUE;
+ }
+
+ if (directory->details->thumbnail_state != NULL &&
+ directory->details->thumbnail_state->file == file) {
+ directory->details->thumbnail_state->file = NULL;
+ changed = TRUE;
+ }
+
+ if (directory->details->mount_state != NULL &&
+ directory->details->mount_state->file == file) {
+ directory->details->mount_state->file = NULL;
+ changed = TRUE;
+ }
+
+ if (directory->details->filesystem_info_state != NULL &&
+ directory->details->filesystem_info_state->file == file) {
+ directory->details->filesystem_info_state->file = NULL;
+ changed = TRUE;
+ }
+
+ /* Let the directory take care of the rest. */
+ if (changed) {
+ nautilus_directory_async_state_changed (directory);
+ }
+}
+
+static gboolean
+lacks_directory_count (NautilusFile *file)
+{
+ return !file->details->directory_count_is_up_to_date
+ && nautilus_file_should_show_directory_item_count (file);
+}
+
+static gboolean
+should_get_directory_count_now (NautilusFile *file)
+{
+ return lacks_directory_count (file)
+ && !file->details->loading_directory;
+}
+
+static gboolean
+lacks_info (NautilusFile *file)
+{
+ return !file->details->file_info_is_up_to_date
+ && !file->details->is_gone;
+}
+
+static gboolean
+lacks_filesystem_info (NautilusFile *file)
+{
+ return !file->details->filesystem_info_is_up_to_date;
+}
+
+static gboolean
+lacks_deep_count (NautilusFile *file)
+{
+ return file->details->deep_counts_status != NAUTILUS_REQUEST_DONE;
+}
+
+static gboolean
+lacks_mime_list (NautilusFile *file)
+{
+ return !file->details->mime_list_is_up_to_date;
+}
+
+static gboolean
+should_get_mime_list (NautilusFile *file)
+{
+ return lacks_mime_list (file)
+ && !file->details->loading_directory;
+}
+
+static gboolean
+lacks_link_info (NautilusFile *file)
+{
+ if (file->details->file_info_is_up_to_date &&
+ !file->details->link_info_is_up_to_date) {
+ if (nautilus_file_is_nautilus_link (file)) {
+ return TRUE;
+ } else {
+ link_info_done (file->details->directory, file, NULL, NULL, NULL, FALSE, FALSE);
+ return FALSE;
+ }
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean
+lacks_extension_info (NautilusFile *file)
+{
+ return file->details->pending_info_providers != NULL;
+}
+
+static gboolean
+lacks_thumbnail (NautilusFile *file)
+{
+ return nautilus_file_should_show_thumbnail (file) &&
+ file->details->thumbnail_path != NULL &&
+ !file->details->thumbnail_is_up_to_date;
+}
+
+static gboolean
+lacks_mount (NautilusFile *file)
+{
+ return (!file->details->mount_is_up_to_date &&
+ (
+ /* Unix mountpoint, could be a GMount */
+ file->details->is_mountpoint ||
+
+ /* The toplevel directory of something */
+ (file->details->type == G_FILE_TYPE_DIRECTORY &&
+ nautilus_file_is_self_owned (file)) ||
+
+ /* Mountable, could be a mountpoint */
+ (file->details->type == G_FILE_TYPE_MOUNTABLE)
+
+ )
+ );
+}
+
+static gboolean
+has_problem (NautilusDirectory *directory, NautilusFile *file, FileCheck problem)
+{
+ GList *node;
+
+ if (file != NULL) {
+ return (* problem) (file);
+ }
+
+ for (node = directory->details->file_list; node != NULL; node = node->next) {
+ if ((* problem) (node->data)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+request_is_satisfied (NautilusDirectory *directory,
+ NautilusFile *file,
+ Request request)
+{
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_LIST) &&
+ !(directory->details->directory_loaded &&
+ directory->details->directory_loaded_sent_notification)) {
+ return FALSE;
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT)) {
+ if (has_problem (directory, file, lacks_directory_count)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO)) {
+ if (has_problem (directory, file, lacks_info)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO)) {
+ if (has_problem (directory, file, lacks_filesystem_info)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT)) {
+ if (has_problem (directory, file, lacks_deep_count)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL)) {
+ if (has_problem (directory, file, lacks_thumbnail)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT)) {
+ if (has_problem (directory, file, lacks_mount)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST)) {
+ if (has_problem (directory, file, lacks_mime_list)) {
+ return FALSE;
+ }
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO)) {
+ if (has_problem (directory, file, lacks_link_info)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+call_ready_callbacks_at_idle (gpointer callback_data)
+{
+ NautilusDirectory *directory;
+ GList *node, *next;
+ ReadyCallback *callback;
+
+ directory = NAUTILUS_DIRECTORY (callback_data);
+ directory->details->call_ready_idle_id = 0;
+
+ nautilus_directory_ref (directory);
+
+ callback = NULL;
+ while (1) {
+ /* Check if any callbacks are non-active and call them if they are. */
+ for (node = directory->details->call_when_ready_list;
+ node != NULL; node = next) {
+ next = node->next;
+ callback = node->data;
+ if (!callback->active) {
+ /* Non-active, remove and call */
+ break;
+ }
+ }
+ if (node == NULL) {
+ break;
+ }
+
+ /* Callbacks are one-shots, so remove it now. */
+ remove_callback_link_keep_data (directory, node);
+
+ /* Call the callback. */
+ ready_callback_call (directory, callback);
+ g_free (callback);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+
+ nautilus_directory_unref (directory);
+
+ return FALSE;
+}
+
+static void
+schedule_call_ready_callbacks (NautilusDirectory *directory)
+{
+ if (directory->details->call_ready_idle_id == 0) {
+ directory->details->call_ready_idle_id
+ = g_idle_add (call_ready_callbacks_at_idle, directory);
+ }
+}
+
+/* Marks all callbacks that are ready as non-active and
+ * calls them at idle time, unless they are removed
+ * before then */
+static gboolean
+call_ready_callbacks (NautilusDirectory *directory)
+{
+ gboolean found_any;
+ GList *node, *next;
+ ReadyCallback *callback;
+
+ found_any = FALSE;
+
+ /* Check if any callbacks are satisifed and mark them for call them if they are. */
+ for (node = directory->details->call_when_ready_list;
+ node != NULL; node = next) {
+ next = node->next;
+ callback = node->data;
+ if (callback->active &&
+ request_is_satisfied (directory, callback->file, callback->request)) {
+ callback->active = FALSE;
+ found_any = TRUE;
+ }
+ }
+
+ if (found_any) {
+ schedule_call_ready_callbacks (directory);
+ }
+
+ return found_any;
+}
+
+gboolean
+nautilus_directory_has_active_request_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ GList *node;
+ ReadyCallback *callback;
+ Monitor *monitor;
+
+ for (node = directory->details->call_when_ready_list;
+ node != NULL; node = node->next) {
+ callback = node->data;
+ if (callback->file == file ||
+ callback->file == NULL) {
+ return TRUE;
+ }
+ }
+
+ for (node = directory->details->monitor_list;
+ node != NULL; node = node->next) {
+ monitor = node->data;
+ if (monitor->file == file ||
+ monitor->file == NULL) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* This checks if there's a request for monitoring the file list. */
+gboolean
+nautilus_directory_is_anyone_monitoring_file_list (NautilusDirectory *directory)
+{
+ if (directory->details->call_when_ready_counters[REQUEST_FILE_LIST] > 0) {
+ return TRUE;
+ }
+
+ if (directory->details->monitor_counters[REQUEST_FILE_LIST] > 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* This checks if the file list being monitored. */
+gboolean
+nautilus_directory_is_file_list_monitored (NautilusDirectory *directory)
+{
+ return directory->details->file_list_monitored;
+}
+
+static void
+mark_all_files_unconfirmed (NautilusDirectory *directory)
+{
+ GList *node;
+ NautilusFile *file;
+
+ for (node = directory->details->file_list; node != NULL; node = node->next) {
+ file = node->data;
+ set_file_unconfirmed (file, TRUE);
+ }
+}
+
+static void
+directory_load_state_free (DirectoryLoadState *state)
+{
+ if (state->enumerator) {
+ if (!g_file_enumerator_is_closed (state->enumerator)) {
+ g_file_enumerator_close_async (state->enumerator,
+ 0, NULL, NULL, NULL);
+ }
+ g_object_unref (state->enumerator);
+ }
+
+ if (state->load_mime_list_hash != NULL) {
+ istr_set_destroy (state->load_mime_list_hash);
+ }
+ nautilus_file_unref (state->load_directory_file);
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+more_files_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DirectoryLoadState *state;
+ NautilusDirectory *directory;
+ GError *error;
+ GList *files, *l;
+ GFileInfo *info;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ directory_load_state_free (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ g_assert (directory->details->directory_load_in_progress != NULL);
+ g_assert (directory->details->directory_load_in_progress == state);
+
+ error = NULL;
+ files = g_file_enumerator_next_files_finish (state->enumerator,
+ res, &error);
+
+ for (l = files; l != NULL; l = l->next) {
+ info = l->data;
+ directory_load_one (directory, info);
+ g_object_unref (info);
+ }
+
+ if (files == NULL) {
+ directory_load_done (directory, error);
+ directory_load_state_free (state);
+ } else {
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ more_files_callback,
+ state);
+ }
+
+ nautilus_directory_unref (directory);
+
+ if (error) {
+ g_error_free (error);
+ }
+
+ g_list_free (files);
+}
+
+static void
+enumerate_children_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DirectoryLoadState *state;
+ GFileEnumerator *enumerator;
+ GError *error;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ directory_load_state_free (state);
+ return;
+ }
+
+ error = NULL;
+ enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
+ res, &error);
+
+ if (enumerator == NULL) {
+ directory_load_done (state->directory, error);
+ g_error_free (error);
+ directory_load_state_free (state);
+ return;
+ } else {
+ state->enumerator = enumerator;
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ more_files_callback,
+ state);
+ }
+}
+
+
+/* Start monitoring the file list if it isn't already. */
+static void
+start_monitoring_file_list (NautilusDirectory *directory)
+{
+ DirectoryLoadState *state;
+
+ if (!directory->details->file_list_monitored) {
+ g_assert (!directory->details->directory_load_in_progress);
+ directory->details->file_list_monitored = TRUE;
+ nautilus_file_list_ref (directory->details->file_list);
+ }
+
+ if (directory->details->directory_loaded ||
+ directory->details->directory_load_in_progress != NULL) {
+ return;
+ }
+
+ if (!async_job_start (directory, "file list")) {
+ return;
+ }
+
+ mark_all_files_unconfirmed (directory);
+
+ state = g_new0 (DirectoryLoadState, 1);
+ state->directory = directory;
+ state->cancellable = g_cancellable_new ();
+ state->load_mime_list_hash = istr_set_new ();
+ state->load_file_count = 0;
+
+ g_assert (directory->details->location != NULL);
+ state->load_directory_file =
+ nautilus_directory_get_corresponding_file (directory);
+ state->load_directory_file->details->loading_directory = TRUE;
+
+
+#ifdef DEBUG_LOAD_DIRECTORY
+ g_message ("load_directory called to monitor file list of %p", directory->details->location);
+#endif
+
+ directory->details->directory_load_in_progress = state;
+
+ g_file_enumerate_children_async (directory->details->location,
+ NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
+ 0, /* flags */
+ G_PRIORITY_DEFAULT, /* prio */
+ state->cancellable,
+ enumerate_children_callback,
+ state);
+}
+
+/* Stop monitoring the file list if it is being monitored. */
+void
+nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory)
+{
+ if (!directory->details->file_list_monitored) {
+ g_assert (directory->details->directory_load_in_progress == NULL);
+ return;
+ }
+
+ directory->details->file_list_monitored = FALSE;
+ file_list_cancel (directory);
+ nautilus_file_list_unref (directory->details->file_list);
+ directory->details->directory_loaded = FALSE;
+}
+
+static void
+file_list_start_or_stop (NautilusDirectory *directory)
+{
+ if (nautilus_directory_is_anyone_monitoring_file_list (directory)) {
+ start_monitoring_file_list (directory);
+ } else {
+ nautilus_directory_stop_monitoring_file_list (directory);
+ }
+}
+
+void
+nautilus_file_invalidate_count_and_mime_list (NautilusFile *file)
+{
+ NautilusFileAttributes attributes;
+
+ attributes = NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
+ NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
+
+ nautilus_file_invalidate_attributes (file, attributes);
+}
+
+
+/* Reset count and mime list. Invalidating deep counts is handled by
+ * itself elsewhere because it's a relatively heavyweight and
+ * special-purpose operation (see bug 5863). Also, the shallow count
+ * needs to be refreshed when filtering changes, but the deep count
+ * deliberately does not take filtering into account.
+ */
+void
+nautilus_directory_invalidate_count_and_mime_list (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ file = nautilus_directory_get_existing_corresponding_file (directory);
+ if (file != NULL) {
+ nautilus_file_invalidate_count_and_mime_list (file);
+ }
+
+ nautilus_file_unref (file);
+}
+
+static void
+nautilus_directory_invalidate_file_attributes (NautilusDirectory *directory,
+ NautilusFileAttributes file_attributes)
+{
+ GList *node;
+
+ cancel_loading_attributes (directory, file_attributes);
+
+ for (node = directory->details->file_list; node != NULL; node = node->next) {
+ nautilus_file_invalidate_attributes_internal (NAUTILUS_FILE (node->data),
+ file_attributes);
+ }
+
+ if (directory->details->as_file != NULL) {
+ nautilus_file_invalidate_attributes_internal (directory->details->as_file,
+ file_attributes);
+ }
+}
+
+void
+nautilus_directory_force_reload_internal (NautilusDirectory *directory,
+ NautilusFileAttributes file_attributes)
+{
+ nautilus_profile_start (NULL);
+
+ /* invalidate attributes that are getting reloaded for all files */
+ nautilus_directory_invalidate_file_attributes (directory, file_attributes);
+
+ /* Start a new directory load. */
+ file_list_cancel (directory);
+ directory->details->directory_loaded = FALSE;
+
+ /* Start a new directory count. */
+ nautilus_directory_invalidate_count_and_mime_list (directory);
+
+ add_all_files_to_work_queue (directory);
+ nautilus_directory_async_state_changed (directory);
+
+ nautilus_profile_end (NULL);
+}
+
+static gboolean
+monitor_includes_file (const Monitor *monitor,
+ NautilusFile *file)
+{
+ if (monitor->file == file) {
+ return TRUE;
+ }
+ if (monitor->file != NULL) {
+ return FALSE;
+ }
+ if (file == file->details->directory->details->as_file) {
+ return FALSE;
+ }
+ return nautilus_file_should_show (file,
+ monitor->monitor_hidden_files,
+ TRUE);
+}
+
+static gboolean
+is_needy (NautilusFile *file,
+ FileCheck check_missing,
+ RequestType request_type_wanted)
+{
+ NautilusDirectory *directory;
+ GList *node;
+ ReadyCallback *callback;
+ Monitor *monitor;
+
+ if (!(* check_missing) (file)) {
+ return FALSE;
+ }
+
+ directory = file->details->directory;
+ if (directory->details->call_when_ready_counters[request_type_wanted] > 0) {
+ for (node = directory->details->call_when_ready_list;
+ node != NULL; node = node->next) {
+ callback = node->data;
+ if (callback->active &&
+ REQUEST_WANTS_TYPE (callback->request, request_type_wanted)) {
+ if (callback->file == file) {
+ return TRUE;
+ }
+ if (callback->file == NULL
+ && file != directory->details->as_file) {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ if (directory->details->monitor_counters[request_type_wanted] > 0) {
+ for (node = directory->details->monitor_list;
+ node != NULL; node = node->next) {
+ monitor = node->data;
+ if (REQUEST_WANTS_TYPE (monitor->request, request_type_wanted)) {
+ if (monitor_includes_file (monitor, file)) {
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static void
+directory_count_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->count_in_progress != NULL) {
+ file = directory->details->count_in_progress->count_file;
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ should_get_directory_count_now,
+ REQUEST_DIRECTORY_COUNT)) {
+ return;
+ }
+ }
+
+ /* The count is not wanted, so stop it. */
+ directory_count_cancel (directory);
+ }
+}
+
+static guint
+count_non_skipped_files (GList *list)
+{
+ guint count;
+ GList *node;
+ GFileInfo *info;
+
+ count = 0;
+ for (node = list; node != NULL; node = node->next) {
+ info = node->data;
+ if (!should_skip_file (NULL, info)) {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+static void
+count_children_done (NautilusDirectory *directory,
+ NautilusFile *count_file,
+ gboolean succeeded,
+ int count)
+{
+ g_assert (NAUTILUS_IS_FILE (count_file));
+
+ count_file->details->directory_count_is_up_to_date = TRUE;
+
+ /* Record either a failure or success. */
+ if (!succeeded) {
+ count_file->details->directory_count_failed = TRUE;
+ count_file->details->got_directory_count = FALSE;
+ count_file->details->directory_count = 0;
+ } else {
+ count_file->details->directory_count_failed = FALSE;
+ count_file->details->got_directory_count = TRUE;
+ count_file->details->directory_count = count;
+ }
+ directory->details->count_in_progress = NULL;
+
+ /* Send file-changed even if count failed, so interested parties can
+ * distinguish between unknowable and not-yet-known cases.
+ */
+ nautilus_file_changed (count_file);
+
+ /* Start up the next one. */
+ async_job_end (directory, "directory count");
+ nautilus_directory_async_state_changed (directory);
+}
+
+static void
+directory_count_state_free (DirectoryCountState *state)
+{
+ if (state->enumerator) {
+ if (!g_file_enumerator_is_closed (state->enumerator)) {
+ g_file_enumerator_close_async (state->enumerator,
+ 0, NULL, NULL, NULL);
+ }
+ g_object_unref (state->enumerator);
+ }
+ g_object_unref (state->cancellable);
+ nautilus_directory_unref (state->directory);
+ g_free (state);
+}
+
+static void
+count_more_files_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DirectoryCountState *state;
+ NautilusDirectory *directory;
+ GError *error;
+ GList *files;
+
+ state = user_data;
+ directory = state->directory;
+
+ if (g_cancellable_is_cancelled (state->cancellable)) {
+ /* Operation was cancelled. Bail out */
+
+ async_job_end (directory, "directory count");
+ nautilus_directory_async_state_changed (directory);
+
+ directory_count_state_free (state);
+
+ return;
+ }
+
+ g_assert (directory->details->count_in_progress != NULL);
+ g_assert (directory->details->count_in_progress == state);
+
+ error = NULL;
+ files = g_file_enumerator_next_files_finish (state->enumerator,
+ res, &error);
+
+ state->file_count += count_non_skipped_files (files);
+
+ if (files == NULL) {
+ count_children_done (directory, state->count_file,
+ TRUE, state->file_count);
+ directory_count_state_free (state);
+ } else {
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ count_more_files_callback,
+ state);
+ }
+
+ g_list_free_full (files, g_object_unref);
+
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+static void
+count_children_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DirectoryCountState *state;
+ GFileEnumerator *enumerator;
+ NautilusDirectory *directory;
+ GError *error;
+
+ state = user_data;
+
+ if (g_cancellable_is_cancelled (state->cancellable)) {
+ /* Operation was cancelled. Bail out */
+ directory = state->directory;
+
+ async_job_end (directory, "directory count");
+ nautilus_directory_async_state_changed (directory);
+
+ directory_count_state_free (state);
+
+ return;
+ }
+
+ error = NULL;
+ enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
+ res, &error);
+
+ if (enumerator == NULL) {
+ count_children_done (state->directory,
+ state->count_file,
+ FALSE, 0);
+ g_error_free (error);
+ directory_count_state_free (state);
+ return;
+ } else {
+ state->enumerator = enumerator;
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ count_more_files_callback,
+ state);
+ }
+}
+
+static void
+directory_count_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ DirectoryCountState *state;
+ GFile *location;
+
+ if (directory->details->count_in_progress != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ should_get_directory_count_now,
+ REQUEST_DIRECTORY_COUNT)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!nautilus_file_is_directory (file)) {
+ file->details->directory_count_is_up_to_date = TRUE;
+ file->details->directory_count_failed = FALSE;
+ file->details->got_directory_count = FALSE;
+
+ nautilus_directory_async_state_changed (directory);
+ return;
+ }
+
+ if (!async_job_start (directory, "directory count")) {
+ return;
+ }
+
+ /* Start counting. */
+ state = g_new0 (DirectoryCountState, 1);
+ state->count_file = file;
+ state->directory = nautilus_directory_ref (directory);
+ state->cancellable = g_cancellable_new ();
+
+ directory->details->count_in_progress = state;
+
+ location = nautilus_file_get_location (file);
+#ifdef DEBUG_LOAD_DIRECTORY
+ {
+ char *uri;
+ uri = g_file_get_uri (location);
+ g_message ("load_directory called to get shallow file count for %s", uri);
+ g_free (uri);
+ }
+#endif
+
+ g_file_enumerate_children_async (location,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
+ G_PRIORITY_DEFAULT, /* prio */
+ state->cancellable,
+ count_children_callback,
+ state);
+ g_object_unref (location);
+}
+
+static inline gboolean
+seen_inode (DeepCountState *state,
+ GFileInfo *info)
+{
+ guint64 inode, inode2;
+ guint i;
+
+ inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
+
+ if (inode != 0) {
+ for (i = 0; i < state->seen_deep_count_inodes->len; i++) {
+ inode2 = g_array_index (state->seen_deep_count_inodes, guint64, i);
+ if (inode == inode2) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static inline void
+mark_inode_as_seen (DeepCountState *state,
+ GFileInfo *info)
+{
+ guint64 inode;
+
+ inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
+ if (inode != 0) {
+ g_array_append_val (state->seen_deep_count_inodes, inode);
+ }
+}
+
+static void
+deep_count_one (DeepCountState *state,
+ GFileInfo *info)
+{
+ NautilusFile *file;
+ GFile *subdir;
+ gboolean is_seen_inode;
+ const char *fs_id;
+
+ if (should_skip_file (NULL, info)) {
+ return;
+ }
+
+ is_seen_inode = seen_inode (state, info);
+ if (!is_seen_inode) {
+ mark_inode_as_seen (state, info);
+ }
+
+ file = state->directory->details->deep_count_file;
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ /* Count the directory. */
+ file->details->deep_directory_count += 1;
+
+ /* Record the fact that we have to descend into this directory. */
+ fs_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
+ if (g_strcmp0 (fs_id, state->fs_id) == 0) {
+ /* only if it is on the same filesystem */
+ subdir = g_file_get_child (state->deep_count_location, g_file_info_get_name (info));
+ state->deep_count_subdirectories = g_list_prepend
+ (state->deep_count_subdirectories, subdir);
+ }
+ } else {
+ /* Even non-regular files count as files. */
+ file->details->deep_file_count += 1;
+ }
+
+ /* Count the size. */
+ if (!is_seen_inode && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
+ file->details->deep_size += g_file_info_get_size (info);
+ }
+}
+
+static void
+deep_count_state_free (DeepCountState *state)
+{
+ if (state->enumerator) {
+ if (!g_file_enumerator_is_closed (state->enumerator)) {
+ g_file_enumerator_close_async (state->enumerator,
+ 0, NULL, NULL, NULL);
+ }
+ g_object_unref (state->enumerator);
+ }
+ g_object_unref (state->cancellable);
+ if (state->deep_count_location) {
+ g_object_unref (state->deep_count_location);
+ }
+ g_list_free_full (state->deep_count_subdirectories, g_object_unref);
+ g_array_free (state->seen_deep_count_inodes, TRUE);
+ g_free (state->fs_id);
+ g_free (state);
+}
+
+static void
+deep_count_next_dir (DeepCountState *state)
+{
+ GFile *location;
+ NautilusFile *file;
+ NautilusDirectory *directory;
+ gboolean done;
+
+ directory = state->directory;
+
+ g_object_unref (state->deep_count_location);
+ state->deep_count_location = NULL;
+
+ done = FALSE;
+ file = directory->details->deep_count_file;
+
+ if (state->deep_count_subdirectories != NULL) {
+ /* Work on a new directory. */
+ location = state->deep_count_subdirectories->data;
+ state->deep_count_subdirectories = g_list_remove
+ (state->deep_count_subdirectories, location);
+ deep_count_load (state, location);
+ g_object_unref (location);
+ } else {
+ file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
+ directory->details->deep_count_file = NULL;
+ directory->details->deep_count_in_progress = NULL;
+ deep_count_state_free (state);
+ done = TRUE;
+ }
+
+ nautilus_file_updated_deep_count_in_progress (file);
+
+ if (done) {
+ nautilus_file_changed (file);
+ async_job_end (directory, "deep count");
+ nautilus_directory_async_state_changed (directory);
+ }
+}
+
+static void
+deep_count_more_files_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeepCountState *state;
+ NautilusDirectory *directory;
+ GList *files, *l;
+ GFileInfo *info;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ deep_count_state_free (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ g_assert (directory->details->deep_count_in_progress != NULL);
+ g_assert (directory->details->deep_count_in_progress == state);
+
+ files = g_file_enumerator_next_files_finish (state->enumerator,
+ res, NULL);
+
+ for (l = files; l != NULL; l = l->next) {
+ info = l->data;
+ deep_count_one (state, info);
+ g_object_unref (info);
+ }
+
+ if (files == NULL) {
+ g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
+ g_object_unref (state->enumerator);
+ state->enumerator = NULL;
+
+ deep_count_next_dir (state);
+ } else {
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_LOW,
+ state->cancellable,
+ deep_count_more_files_callback,
+ state);
+ }
+
+ g_list_free (files);
+
+ nautilus_directory_unref (directory);
+}
+
+static void
+deep_count_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeepCountState *state;
+ GFileEnumerator *enumerator;
+ NautilusFile *file;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ deep_count_state_free (state);
+ return;
+ }
+
+ file = state->directory->details->deep_count_file;
+
+ enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
+
+ if (enumerator == NULL) {
+ file->details->deep_unreadable_count += 1;
+
+ deep_count_next_dir (state);
+ } else {
+ state->enumerator = enumerator;
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_LOW,
+ state->cancellable,
+ deep_count_more_files_callback,
+ state);
+ }
+}
+
+
+static void
+deep_count_load (DeepCountState *state, GFile *location)
+{
+ state->deep_count_location = g_object_ref (location);
+
+#ifdef DEBUG_LOAD_DIRECTORY
+ g_message ("load_directory called to get deep file count for %p", location);
+#endif
+ g_file_enumerate_children_async (state->deep_count_location,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP ","
+ G_FILE_ATTRIBUTE_ID_FILESYSTEM ","
+ G_FILE_ATTRIBUTE_UNIX_INODE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
+ G_PRIORITY_LOW, /* prio */
+ state->cancellable,
+ deep_count_callback,
+ state);
+}
+
+static void
+deep_count_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->deep_count_in_progress != NULL) {
+ file = directory->details->deep_count_file;
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_deep_count,
+ REQUEST_DEEP_COUNT)) {
+ return;
+ }
+ }
+
+ /* The count is not wanted, so stop it. */
+ deep_count_cancel (directory);
+ }
+}
+
+static void
+deep_count_got_info (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GFileInfo *info;
+ const char *id;
+ GFile *file = (GFile *)source_object;
+ DeepCountState *state = (DeepCountState *)user_data;
+
+ info = g_file_query_info_finish (file, res, NULL);
+ if (info != NULL) {
+ id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
+ state->fs_id = g_strdup (id);
+ g_object_unref (info);
+ }
+ deep_count_load (state, file);
+}
+
+static void
+deep_count_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ DeepCountState *state;
+
+ if (directory->details->deep_count_in_progress != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_deep_count,
+ REQUEST_DEEP_COUNT)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!nautilus_file_is_directory (file)) {
+ file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
+
+ nautilus_directory_async_state_changed (directory);
+ return;
+ }
+
+ if (!async_job_start (directory, "deep count")) {
+ return;
+ }
+
+ /* Start counting. */
+ file->details->deep_counts_status = NAUTILUS_REQUEST_IN_PROGRESS;
+ file->details->deep_directory_count = 0;
+ file->details->deep_file_count = 0;
+ file->details->deep_unreadable_count = 0;
+ file->details->deep_size = 0;
+ directory->details->deep_count_file = file;
+
+ state = g_new0 (DeepCountState, 1);
+ state->directory = directory;
+ state->cancellable = g_cancellable_new ();
+ state->seen_deep_count_inodes = g_array_new (FALSE, TRUE, sizeof (guint64));
+ state->fs_id = NULL;
+
+ directory->details->deep_count_in_progress = state;
+
+ location = nautilus_file_get_location (file);
+ g_file_query_info_async (location,
+ G_FILE_ATTRIBUTE_ID_FILESYSTEM,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ deep_count_got_info,
+ state);
+ g_object_unref (location);
+}
+
+static void
+mime_list_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->mime_list_in_progress != NULL) {
+ file = directory->details->mime_list_in_progress->mime_list_file;
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ should_get_mime_list,
+ REQUEST_MIME_LIST)) {
+ return;
+ }
+ }
+
+ /* The count is not wanted, so stop it. */
+ mime_list_cancel (directory);
+ }
+}
+
+static void
+mime_list_state_free (MimeListState *state)
+{
+ if (state->enumerator) {
+ if (!g_file_enumerator_is_closed (state->enumerator)) {
+ g_file_enumerator_close_async (state->enumerator,
+ 0, NULL, NULL, NULL);
+ }
+ g_object_unref (state->enumerator);
+ }
+ g_object_unref (state->cancellable);
+ istr_set_destroy (state->mime_list_hash);
+ nautilus_directory_unref (state->directory);
+ g_free (state);
+}
+
+
+static void
+mime_list_done (MimeListState *state, gboolean success)
+{
+ NautilusFile *file;
+ NautilusDirectory *directory;
+
+ directory = state->directory;
+ g_assert (directory != NULL);
+
+ file = state->mime_list_file;
+
+ file->details->mime_list_is_up_to_date = TRUE;
+ g_list_free_full (file->details->mime_list, g_free);
+ if (success) {
+ file->details->mime_list_failed = TRUE;
+ file->details->mime_list = NULL;
+ } else {
+ file->details->got_mime_list = TRUE;
+ file->details->mime_list = istr_set_get_as_list (state->mime_list_hash);
+ }
+ directory->details->mime_list_in_progress = NULL;
+
+ /* Send file-changed even if getting the item type list
+ * failed, so interested parties can distinguish between
+ * unknowable and not-yet-known cases.
+ */
+ nautilus_file_changed (file);
+
+ /* Start up the next one. */
+ async_job_end (directory, "MIME list");
+ nautilus_directory_async_state_changed (directory);
+}
+
+static void
+mime_list_one (MimeListState *state,
+ GFileInfo *info)
+{
+ const char *mime_type;
+
+ if (should_skip_file (NULL, info)) {
+ g_object_unref (info);
+ return;
+ }
+
+ mime_type = g_file_info_get_content_type (info);
+ if (mime_type != NULL) {
+ istr_set_insert (state->mime_list_hash, mime_type);
+ }
+}
+
+static void
+mime_list_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ MimeListState *state;
+ NautilusDirectory *directory;
+ GError *error;
+ GList *files, *l;
+ GFileInfo *info;
+
+ state = user_data;
+ directory = state->directory;
+
+ if (g_cancellable_is_cancelled (state->cancellable)) {
+ /* Operation was cancelled. Bail out */
+ directory->details->mime_list_in_progress = NULL;
+
+ async_job_end (directory, "MIME list");
+ nautilus_directory_async_state_changed (directory);
+
+ mime_list_state_free (state);
+
+ return;
+ }
+
+ g_assert (directory->details->mime_list_in_progress != NULL);
+ g_assert (directory->details->mime_list_in_progress == state);
+
+ error = NULL;
+ files = g_file_enumerator_next_files_finish (state->enumerator,
+ res, &error);
+
+ for (l = files; l != NULL; l = l->next) {
+ info = l->data;
+ mime_list_one (state, info);
+ g_object_unref (info);
+ }
+
+ if (files == NULL) {
+ mime_list_done (state, error != NULL);
+ mime_list_state_free (state);
+ } else {
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ mime_list_callback,
+ state);
+ }
+
+ g_list_free (files);
+
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+static void
+list_mime_enum_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ MimeListState *state;
+ GFileEnumerator *enumerator;
+ NautilusDirectory *directory;
+ GError *error;
+
+ state = user_data;
+
+ if (g_cancellable_is_cancelled (state->cancellable)) {
+ /* Operation was cancelled. Bail out */
+ directory = state->directory;
+ directory->details->mime_list_in_progress = NULL;
+
+ async_job_end (directory, "MIME list");
+ nautilus_directory_async_state_changed (directory);
+
+ mime_list_state_free (state);
+
+ return;
+ }
+
+ error = NULL;
+ enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
+ res, &error);
+
+ if (enumerator == NULL) {
+ mime_list_done (state, FALSE);
+ g_error_free (error);
+ mime_list_state_free (state);
+ return;
+ } else {
+ state->enumerator = enumerator;
+ g_file_enumerator_next_files_async (state->enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ mime_list_callback,
+ state);
+ }
+}
+
+static void
+mime_list_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ MimeListState *state;
+ GFile *location;
+
+ mime_list_stop (directory);
+
+ if (directory->details->mime_list_in_progress != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ /* Figure out which file to get a mime list for. */
+ if (!is_needy (file,
+ should_get_mime_list,
+ REQUEST_MIME_LIST)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!nautilus_file_is_directory (file)) {
+ g_list_free (file->details->mime_list);
+ file->details->mime_list_failed = FALSE;
+ file->details->got_mime_list = FALSE;
+ file->details->mime_list_is_up_to_date = TRUE;
+
+ nautilus_directory_async_state_changed (directory);
+ return;
+ }
+
+ if (!async_job_start (directory, "MIME list")) {
+ return;
+ }
+
+
+ state = g_new0 (MimeListState, 1);
+ state->mime_list_file = file;
+ state->directory = nautilus_directory_ref (directory);
+ state->cancellable = g_cancellable_new ();
+ state->mime_list_hash = istr_set_new ();
+
+ directory->details->mime_list_in_progress = state;
+
+ location = nautilus_file_get_location (file);
+#ifdef DEBUG_LOAD_DIRECTORY
+ {
+ char *uri;
+ uri = g_file_get_uri (location);
+ g_message ("load_directory called to get MIME list of %s", uri);
+ g_free (uri);
+ }
+#endif
+
+ g_file_enumerate_children_async (location,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, /* flags */
+ G_PRIORITY_LOW, /* prio */
+ state->cancellable,
+ list_mime_enum_callback,
+ state);
+ g_object_unref (location);
+}
+
+static void
+get_info_state_free (GetInfoState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+query_info_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusDirectory *directory;
+ NautilusFile *get_info_file;
+ GFileInfo *info;
+ GetInfoState *state;
+ GError *error;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ get_info_state_free (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ get_info_file = directory->details->get_info_file;
+ g_assert (NAUTILUS_IS_FILE (get_info_file));
+
+ directory->details->get_info_file = NULL;
+ directory->details->get_info_in_progress = NULL;
+
+ /* ref here because we might be removing the last ref when we
+ * mark the file gone below, but we need to keep a ref at
+ * least long enough to send the change notification.
+ */
+ nautilus_file_ref (get_info_file);
+
+ error = NULL;
+ info = g_file_query_info_finish (G_FILE (source_object), res, &error);
+
+ if (info == NULL) {
+ if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) {
+ /* mark file as gone */
+ nautilus_file_mark_gone (get_info_file);
+ }
+ get_info_file->details->file_info_is_up_to_date = TRUE;
+ nautilus_file_clear_info (get_info_file);
+ get_info_file->details->get_info_failed = TRUE;
+ get_info_file->details->get_info_error = error;
+ } else {
+ nautilus_file_update_info (get_info_file, info);
+ g_object_unref (info);
+ }
+
+ nautilus_file_changed (get_info_file);
+ nautilus_file_unref (get_info_file);
+
+ async_job_end (directory, "file info");
+ nautilus_directory_async_state_changed (directory);
+
+ nautilus_directory_unref (directory);
+
+ get_info_state_free (state);
+}
+
+static void
+file_info_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->get_info_in_progress != NULL) {
+ file = directory->details->get_info_file;
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file, lacks_info, REQUEST_FILE_INFO)) {
+ return;
+ }
+ }
+
+ /* The info is not wanted, so stop it. */
+ file_info_cancel (directory);
+ }
+}
+
+static void
+file_info_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ GetInfoState *state;
+
+ file_info_stop (directory);
+
+ if (directory->details->get_info_in_progress != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file, lacks_info, REQUEST_FILE_INFO)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "file info")) {
+ return;
+ }
+
+ directory->details->get_info_file = file;
+ file->details->get_info_failed = FALSE;
+ if (file->details->get_info_error) {
+ g_error_free (file->details->get_info_error);
+ file->details->get_info_error = NULL;
+ }
+
+ state = g_new (GetInfoState, 1);
+ state->directory = directory;
+ state->cancellable = g_cancellable_new ();
+
+ directory->details->get_info_in_progress = state;
+
+ location = nautilus_file_get_location (file);
+ g_file_query_info_async (location,
+ NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
+ 0,
+ G_PRIORITY_DEFAULT,
+ state->cancellable, query_info_callback, state);
+ g_object_unref (location);
+}
+
+static gboolean
+is_link_trusted (NautilusFile *file,
+ gboolean is_launcher)
+{
+ GFile *location;
+ gboolean res;
+
+ if (!is_launcher) {
+ return TRUE;
+ }
+
+ if (nautilus_file_can_execute (file)) {
+ return TRUE;
+ }
+
+ res = FALSE;
+
+ if (nautilus_file_is_local (file)) {
+ location = nautilus_file_get_location (file);
+ res = nautilus_is_in_system_dir (location);
+ g_object_unref (location);
+ }
+
+ return res;
+}
+
+static void
+link_info_done (NautilusDirectory *directory,
+ NautilusFile *file,
+ const char *uri,
+ const char *name,
+ GIcon *icon,
+ gboolean is_launcher,
+ gboolean is_foreign)
+{
+ gboolean is_trusted;
+
+ file->details->link_info_is_up_to_date = TRUE;
+
+ is_trusted = is_link_trusted (file, is_launcher);
+
+ if (is_trusted) {
+ nautilus_file_set_display_name (file, name, name, TRUE);
+ } else {
+ nautilus_file_set_display_name (file, NULL, NULL, TRUE);
+ }
+
+ file->details->got_link_info = TRUE;
+ g_clear_object (&file->details->custom_icon);
+
+ if (uri) {
+ g_free (file->details->activation_uri);
+ file->details->activation_uri = NULL;
+ file->details->got_custom_activation_uri = TRUE;
+ file->details->activation_uri = g_strdup (uri);
+ }
+ if (is_trusted && (icon != NULL)) {
+ file->details->custom_icon = g_object_ref (icon);
+ }
+ file->details->is_launcher = is_launcher;
+ file->details->is_foreign_link = is_foreign;
+ file->details->is_trusted_link = is_trusted;
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+static void
+link_info_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->link_info_read_state != NULL) {
+ file = directory->details->link_info_read_state->file;
+
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_link_info,
+ REQUEST_LINK_INFO)) {
+ return;
+ }
+ }
+
+ /* The link info is not wanted, so stop it. */
+ link_info_cancel (directory);
+ }
+}
+
+static void
+link_info_got_data (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean result,
+ goffset bytes_read,
+ char *file_contents)
+{
+ char *link_uri, *uri, *name;
+ GIcon *icon;
+ gboolean is_launcher;
+ gboolean is_foreign;
+
+ nautilus_directory_ref (directory);
+
+ uri = NULL;
+ name = NULL;
+ icon = NULL;
+ is_launcher = FALSE;
+ is_foreign = FALSE;
+
+ /* Handle the case where we read the Nautilus link. */
+ if (result) {
+ link_uri = nautilus_file_get_uri (file);
+ nautilus_link_get_link_info_given_file_contents (file_contents, bytes_read, link_uri,
+ &uri, &name, &icon, &is_launcher, &is_foreign);
+ g_free (link_uri);
+ } else {
+ /* FIXME bugzilla.gnome.org 42433: We should report this error to the user. */
+ }
+
+ nautilus_file_ref (file);
+ link_info_done (directory, file, uri, name, icon, is_launcher, is_foreign);
+ nautilus_file_changed (file);
+ nautilus_file_unref (file);
+
+ g_free (uri);
+ g_free (name);
+
+ if (icon != NULL) {
+ g_object_unref (icon);
+ }
+
+ nautilus_directory_unref (directory);
+}
+
+static void
+link_info_read_state_free (LinkInfoReadState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+link_info_nautilus_link_read_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ LinkInfoReadState *state;
+ gsize file_size;
+ char *file_contents;
+ gboolean result;
+ NautilusDirectory *directory;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ link_info_read_state_free (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ result = g_file_load_contents_finish (G_FILE (source_object),
+ res,
+ &file_contents, &file_size,
+ NULL, NULL);
+
+ state->directory->details->link_info_read_state = NULL;
+ async_job_end (state->directory, "link info");
+
+ link_info_got_data (state->directory, state->file, result, file_size, file_contents);
+
+ if (result) {
+ g_free (file_contents);
+ }
+
+ link_info_read_state_free (state);
+
+ nautilus_directory_unref (directory);
+}
+
+static void
+link_info_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ gboolean nautilus_style_link;
+ LinkInfoReadState *state;
+
+ if (directory->details->link_info_read_state != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_link_info,
+ REQUEST_LINK_INFO)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ /* Figure out if it is a link. */
+ nautilus_style_link = nautilus_file_is_nautilus_link (file);
+ location = nautilus_file_get_location (file);
+
+ /* If it's not a link we are done. If it is, we need to read it. */
+ if (!nautilus_style_link) {
+ link_info_done (directory, file, NULL, NULL, NULL, FALSE, FALSE);
+ } else {
+ if (!async_job_start (directory, "link info")) {
+ g_object_unref (location);
+ return;
+ }
+
+ state = g_new0 (LinkInfoReadState, 1);
+ state->directory = directory;
+ state->file = file;
+ state->cancellable = g_cancellable_new ();
+
+ directory->details->link_info_read_state = state;
+
+ g_file_load_contents_async (location,
+ state->cancellable,
+ link_info_nautilus_link_read_callback,
+ state);
+ }
+ g_object_unref (location);
+}
+
+static void
+thumbnail_done (NautilusDirectory *directory,
+ NautilusFile *file,
+ GdkPixbuf *pixbuf,
+ gboolean tried_original)
+{
+ const char *thumb_mtime_str;
+ time_t thumb_mtime = 0;
+
+ file->details->thumbnail_is_up_to_date = TRUE;
+ file->details->thumbnail_tried_original = tried_original;
+ if (file->details->thumbnail) {
+ g_object_unref (file->details->thumbnail);
+ file->details->thumbnail = NULL;
+ }
+ if (file->details->scaled_thumbnail) {
+ g_object_unref (file->details->scaled_thumbnail);
+ file->details->scaled_thumbnail = NULL;
+ }
+
+ if (pixbuf) {
+ if (tried_original) {
+ thumb_mtime = file->details->mtime;
+ } else {
+ thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
+ if (thumb_mtime_str) {
+ thumb_mtime = atol (thumb_mtime_str);
+ }
+ }
+
+ if (thumb_mtime == 0 ||
+ thumb_mtime == file->details->mtime) {
+ file->details->thumbnail = g_object_ref (pixbuf);
+ file->details->thumbnail_mtime = thumb_mtime;
+ } else {
+ g_free (file->details->thumbnail_path);
+ file->details->thumbnail_path = NULL;
+ }
+ }
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+static void
+thumbnail_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->thumbnail_state != NULL) {
+ file = directory->details->thumbnail_state->file;
+
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_thumbnail,
+ REQUEST_THUMBNAIL)) {
+ return;
+ }
+ }
+
+ /* The link info is not wanted, so stop it. */
+ thumbnail_cancel (directory);
+ }
+}
+
+static void
+thumbnail_got_pixbuf (NautilusDirectory *directory,
+ NautilusFile *file,
+ GdkPixbuf *pixbuf,
+ gboolean tried_original)
+{
+ nautilus_directory_ref (directory);
+
+ nautilus_file_ref (file);
+ thumbnail_done (directory, file, pixbuf, tried_original);
+ nautilus_file_changed (file);
+ nautilus_file_unref (file);
+
+ if (pixbuf) {
+ g_object_unref (pixbuf);
+ }
+
+ nautilus_directory_unref (directory);
+}
+
+static void
+thumbnail_state_free (ThumbnailState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+extern int cached_thumbnail_size;
+
+/* scale very large images down to the max. size we need */
+static void
+thumbnail_loader_size_prepared (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ gpointer user_data)
+{
+ int max_thumbnail_size;
+ double aspect_ratio;
+
+ aspect_ratio = ((double) width) / height;
+
+ /* cf. nautilus_file_get_icon() */
+ max_thumbnail_size = NAUTILUS_CANVAS_ICON_SIZE_LARGER * cached_thumbnail_size / NAUTILUS_CANVAS_ICON_SIZE_SMALL;
+ if (MAX (width, height) > max_thumbnail_size) {
+ if (width > height) {
+ width = max_thumbnail_size;
+ height = width / aspect_ratio;
+ } else {
+ height = max_thumbnail_size;
+ width = height * aspect_ratio;
+ }
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+ }
+}
+
+static GdkPixbuf *
+get_pixbuf_for_content (goffset file_len,
+ char *file_contents)
+{
+ gboolean res;
+ GdkPixbuf *pixbuf, *pixbuf2;
+ GdkPixbufLoader *loader;
+ gsize chunk_len;
+ pixbuf = NULL;
+
+ loader = gdk_pixbuf_loader_new ();
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (thumbnail_loader_size_prepared),
+ NULL);
+
+ /* For some reason we have to write in chunks, or gdk-pixbuf fails */
+ res = TRUE;
+ while (res && file_len > 0) {
+ chunk_len = file_len;
+ res = gdk_pixbuf_loader_write (loader, (guchar *) file_contents, chunk_len, NULL);
+ file_contents += chunk_len;
+ file_len -= chunk_len;
+ }
+ if (res) {
+ res = gdk_pixbuf_loader_close (loader, NULL);
+ }
+ if (res) {
+ pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
+ }
+ g_object_unref (G_OBJECT (loader));
+
+ if (pixbuf) {
+ pixbuf2 = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+ g_object_unref (pixbuf);
+ pixbuf = pixbuf2;
+ }
+ return pixbuf;
+}
+
+
+static void
+thumbnail_read_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ ThumbnailState *state;
+ gsize file_size;
+ char *file_contents;
+ gboolean result;
+ NautilusDirectory *directory;
+ GdkPixbuf *pixbuf;
+ GFile *location;
+
+ state = user_data;
+
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ thumbnail_state_free (state);
+ return;
+ }
+
+ directory = nautilus_directory_ref (state->directory);
+
+ result = g_file_load_contents_finish (G_FILE (source_object),
+ res,
+ &file_contents, &file_size,
+ NULL, NULL);
+
+ pixbuf = NULL;
+ if (result) {
+ pixbuf = get_pixbuf_for_content (file_size, file_contents);
+ g_free (file_contents);
+ }
+
+ if (pixbuf == NULL && state->trying_original) {
+ state->trying_original = FALSE;
+
+ location = g_file_new_for_path (state->file->details->thumbnail_path);
+ g_file_load_contents_async (location,
+ state->cancellable,
+ thumbnail_read_callback,
+ state);
+ g_object_unref (location);
+ } else {
+ state->directory->details->thumbnail_state = NULL;
+ async_job_end (state->directory, "thumbnail");
+
+ thumbnail_got_pixbuf (state->directory, state->file, pixbuf, state->tried_original);
+
+ thumbnail_state_free (state);
+ }
+
+ nautilus_directory_unref (directory);
+}
+
+static void
+thumbnail_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ ThumbnailState *state;
+
+ if (directory->details->thumbnail_state != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_thumbnail,
+ REQUEST_THUMBNAIL)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "thumbnail")) {
+ return;
+ }
+
+ state = g_new0 (ThumbnailState, 1);
+ state->directory = directory;
+ state->file = file;
+ state->cancellable = g_cancellable_new ();
+
+ if (file->details->thumbnail_wants_original) {
+ state->tried_original = TRUE;
+ state->trying_original = TRUE;
+ location = nautilus_file_get_location (file);
+ } else {
+ location = g_file_new_for_path (file->details->thumbnail_path);
+ }
+
+ directory->details->thumbnail_state = state;
+
+ g_file_load_contents_async (location,
+ state->cancellable,
+ thumbnail_read_callback,
+ state);
+ g_object_unref (location);
+}
+
+static void
+mount_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->mount_state != NULL) {
+ file = directory->details->mount_state->file;
+
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_mount,
+ REQUEST_MOUNT)) {
+ return;
+ }
+ }
+
+ /* The link info is not wanted, so stop it. */
+ mount_cancel (directory);
+ }
+}
+
+static void
+mount_state_free (MountState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+got_mount (MountState *state, GMount *mount)
+{
+ NautilusDirectory *directory;
+ NautilusFile *file;
+
+ directory = nautilus_directory_ref (state->directory);
+
+ state->directory->details->mount_state = NULL;
+ async_job_end (state->directory, "mount");
+
+ file = nautilus_file_ref (state->file);
+
+ file->details->mount_is_up_to_date = TRUE;
+ nautilus_file_set_mount (file, mount);
+
+ nautilus_directory_async_state_changed (directory);
+ nautilus_file_changed (file);
+
+ nautilus_file_unref (file);
+
+ nautilus_directory_unref (directory);
+
+ mount_state_free (state);
+
+}
+
+static void
+find_enclosing_mount_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GMount *mount;
+ MountState *state;
+ GFile *location, *root;
+
+ state = user_data;
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ mount_state_free (state);
+ return;
+ }
+
+ mount = g_file_find_enclosing_mount_finish (G_FILE (source_object),
+ res, NULL);
+
+ if (mount) {
+ root = g_mount_get_root (mount);
+ location = nautilus_file_get_location (state->file);
+ if (!g_file_equal (location, root)) {
+ g_object_unref (mount);
+ mount = NULL;
+ }
+ g_object_unref (root);
+ g_object_unref (location);
+ }
+
+ got_mount (state, mount);
+
+ if (mount) {
+ g_object_unref (mount);
+ }
+}
+
+static GMount *
+get_mount_at (GFile *target)
+{
+ GVolumeMonitor *monitor;
+ GFile *root;
+ GList *mounts, *l;
+ GMount *found;
+
+ monitor = g_volume_monitor_get ();
+ mounts = g_volume_monitor_get_mounts (monitor);
+
+ found = NULL;
+ for (l = mounts; l != NULL; l = l->next) {
+ GMount *mount = G_MOUNT (l->data);
+
+ if (g_mount_is_shadowed (mount))
+ continue;
+
+ root = g_mount_get_root (mount);
+
+ if (g_file_equal (target, root)) {
+ found = g_object_ref (mount);
+ break;
+ }
+
+ g_object_unref (root);
+ }
+
+ g_list_free_full (mounts, g_object_unref);
+
+ g_object_unref (monitor);
+
+ return found;
+}
+
+static void
+mount_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ MountState *state;
+
+ if (directory->details->mount_state != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_mount,
+ REQUEST_MOUNT)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "mount")) {
+ return;
+ }
+
+ state = g_new0 (MountState, 1);
+ state->directory = directory;
+ state->file = file;
+ state->cancellable = g_cancellable_new ();
+
+ location = nautilus_file_get_location (file);
+
+ directory->details->mount_state = state;
+
+ if (file->details->type == G_FILE_TYPE_MOUNTABLE) {
+ GFile *target;
+ GMount *mount;
+
+ mount = NULL;
+ target = nautilus_file_get_activation_location (file);
+ if (target != NULL) {
+ mount = get_mount_at (target);
+ g_object_unref (target);
+ }
+
+ got_mount (state, mount);
+
+ if (mount) {
+ g_object_unref (mount);
+ }
+ } else {
+ g_file_find_enclosing_mount_async (location,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ find_enclosing_mount_callback,
+ state);
+ }
+ g_object_unref (location);
+}
+
+static void
+filesystem_info_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->filesystem_info_state != NULL) {
+ g_cancellable_cancel (directory->details->filesystem_info_state->cancellable);
+ directory->details->filesystem_info_state->directory = NULL;
+ directory->details->filesystem_info_state = NULL;
+ async_job_end (directory, "filesystem info");
+ }
+}
+
+static void
+filesystem_info_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->filesystem_info_state != NULL) {
+ file = directory->details->filesystem_info_state->file;
+
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_filesystem_info,
+ REQUEST_FILESYSTEM_INFO)) {
+ return;
+ }
+ }
+
+ /* The filesystem info is not wanted, so stop it. */
+ filesystem_info_cancel (directory);
+ }
+}
+
+static void
+filesystem_info_state_free (FilesystemInfoState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+got_filesystem_info (FilesystemInfoState *state, GFileInfo *info)
+{
+ NautilusDirectory *directory;
+ NautilusFile *file;
+ const char *filesystem_type;
+
+ /* careful here, info may be NULL */
+
+ directory = nautilus_directory_ref (state->directory);
+
+ state->directory->details->filesystem_info_state = NULL;
+ async_job_end (state->directory, "filesystem info");
+
+ file = nautilus_file_ref (state->file);
+
+ file->details->filesystem_info_is_up_to_date = TRUE;
+ if (info != NULL) {
+ file->details->filesystem_use_preview =
+ g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
+ file->details->filesystem_readonly =
+ g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
+ filesystem_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
+ if (g_strcmp0 (eel_ref_str_peek (file->details->filesystem_type), filesystem_type) != 0) {
+ eel_ref_str_unref (file->details->filesystem_type);
+ file->details->filesystem_type = eel_ref_str_get_unique (filesystem_type);
+ }
+ }
+
+ nautilus_directory_async_state_changed (directory);
+ nautilus_file_changed (file);
+
+ nautilus_file_unref (file);
+
+ nautilus_directory_unref (directory);
+
+ filesystem_info_state_free (state);
+}
+
+static void
+query_filesystem_info_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GFileInfo *info;
+ FilesystemInfoState *state;
+
+ state = user_data;
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ filesystem_info_state_free (state);
+ return;
+ }
+
+ info = g_file_query_filesystem_info_finish (G_FILE (source_object), res, NULL);
+
+ got_filesystem_info (state, info);
+
+ if (info != NULL) {
+ g_object_unref (info);
+ }
+}
+
+static void
+filesystem_info_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ FilesystemInfoState *state;
+
+ if (directory->details->filesystem_info_state != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_filesystem_info,
+ REQUEST_FILESYSTEM_INFO)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "filesystem info")) {
+ return;
+ }
+
+ state = g_new0 (FilesystemInfoState, 1);
+ state->directory = directory;
+ state->file = file;
+ state->cancellable = g_cancellable_new ();
+
+ location = nautilus_file_get_location (file);
+
+ directory->details->filesystem_info_state = state;
+
+ g_file_query_filesystem_info_async (location,
+ G_FILE_ATTRIBUTE_FILESYSTEM_READONLY ","
+ G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW ","
+ G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ query_filesystem_info_callback,
+ state);
+ g_object_unref (location);
+}
+
+static void
+extension_info_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->extension_info_in_progress != NULL) {
+ if (directory->details->extension_info_idle) {
+ g_source_remove (directory->details->extension_info_idle);
+ } else {
+ nautilus_info_provider_cancel_update
+ (directory->details->extension_info_provider,
+ directory->details->extension_info_in_progress);
+ }
+
+ directory->details->extension_info_in_progress = NULL;
+ directory->details->extension_info_file = NULL;
+ directory->details->extension_info_provider = NULL;
+ directory->details->extension_info_idle = 0;
+
+ async_job_end (directory, "extension info");
+ }
+}
+
+static void
+extension_info_stop (NautilusDirectory *directory)
+{
+ if (directory->details->extension_info_in_progress != NULL) {
+ NautilusFile *file;
+
+ file = directory->details->extension_info_file;
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO)) {
+ return;
+ }
+ }
+
+ /* The info is not wanted, so stop it. */
+ extension_info_cancel (directory);
+ }
+}
+
+static void
+finish_info_provider (NautilusDirectory *directory,
+ NautilusFile *file,
+ NautilusInfoProvider *provider)
+{
+ file->details->pending_info_providers =
+ g_list_remove (file->details->pending_info_providers,
+ provider);
+ g_object_unref (provider);
+
+ nautilus_directory_async_state_changed (directory);
+
+ if (file->details->pending_info_providers == NULL) {
+ nautilus_file_info_providers_done (file);
+ }
+}
+
+
+static gboolean
+info_provider_idle_callback (gpointer user_data)
+{
+ InfoProviderResponse *response;
+ NautilusDirectory *directory;
+
+ response = user_data;
+ directory = response->directory;
+
+ if (response->handle != directory->details->extension_info_in_progress
+ || response->provider != directory->details->extension_info_provider) {
+ g_warning ("Unexpected plugin response. This probably indicates a bug in a Nautilus extension: handle=%p", response->handle);
+ } else {
+ NautilusFile *file;
+ async_job_end (directory, "extension info");
+
+ file = directory->details->extension_info_file;
+
+ directory->details->extension_info_file = NULL;
+ directory->details->extension_info_provider = NULL;
+ directory->details->extension_info_in_progress = NULL;
+ directory->details->extension_info_idle = 0;
+
+ finish_info_provider (directory, file, response->provider);
+ }
+
+ return FALSE;
+}
+
+static void
+info_provider_callback (NautilusInfoProvider *provider,
+ NautilusOperationHandle *handle,
+ NautilusOperationResult result,
+ gpointer user_data)
+{
+ InfoProviderResponse *response;
+
+ response = g_new0 (InfoProviderResponse, 1);
+ response->provider = provider;
+ response->handle = handle;
+ response->result = result;
+ response->directory = NAUTILUS_DIRECTORY (user_data);
+
+ response->directory->details->extension_info_idle =
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ info_provider_idle_callback, response,
+ g_free);
+}
+
+static void
+extension_info_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ NautilusInfoProvider *provider;
+ NautilusOperationResult result;
+ NautilusOperationHandle *handle;
+ GClosure *update_complete;
+
+ if (directory->details->extension_info_in_progress != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "extension info")) {
+ return;
+ }
+
+ provider = file->details->pending_info_providers->data;
+
+ update_complete = g_cclosure_new (G_CALLBACK (info_provider_callback),
+ directory,
+ NULL);
+ g_closure_set_marshal (update_complete,
+ g_cclosure_marshal_generic);
+
+ result = nautilus_info_provider_update_file_info
+ (provider,
+ NAUTILUS_FILE_INFO (file),
+ update_complete,
+ &handle);
+
+ g_closure_unref (update_complete);
+
+ if (result == NAUTILUS_OPERATION_COMPLETE ||
+ result == NAUTILUS_OPERATION_FAILED) {
+ finish_info_provider (directory, file, provider);
+ async_job_end (directory, "extension info");
+ } else {
+ directory->details->extension_info_in_progress = handle;
+ directory->details->extension_info_provider = provider;
+ directory->details->extension_info_file = file;
+ }
+}
+
+static void
+start_or_stop_io (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+ gboolean doing_io;
+
+ /* Start or stop reading files. */
+ file_list_start_or_stop (directory);
+
+ /* Stop any no longer wanted attribute fetches. */
+ file_info_stop (directory);
+ directory_count_stop (directory);
+ deep_count_stop (directory);
+ mime_list_stop (directory);
+ link_info_stop (directory);
+ extension_info_stop (directory);
+ mount_stop (directory);
+ thumbnail_stop (directory);
+ filesystem_info_stop (directory);
+
+ doing_io = FALSE;
+ /* Take files that are all done off the queue. */
+ while (!nautilus_file_queue_is_empty (directory->details->high_priority_queue)) {
+ file = nautilus_file_queue_head (directory->details->high_priority_queue);
+
+ /* Start getting attributes if possible */
+ file_info_start (directory, file, &doing_io);
+ link_info_start (directory, file, &doing_io);
+
+ if (doing_io) {
+ return;
+ }
+
+ move_file_to_low_priority_queue (directory, file);
+ }
+
+ /* High priority queue must be empty */
+ while (!nautilus_file_queue_is_empty (directory->details->low_priority_queue)) {
+ file = nautilus_file_queue_head (directory->details->low_priority_queue);
+
+ /* Start getting attributes if possible */
+ mount_start (directory, file, &doing_io);
+ directory_count_start (directory, file, &doing_io);
+ deep_count_start (directory, file, &doing_io);
+ mime_list_start (directory, file, &doing_io);
+ thumbnail_start (directory, file, &doing_io);
+ filesystem_info_start (directory, file, &doing_io);
+
+ if (doing_io) {
+ return;
+ }
+
+ move_file_to_extension_queue (directory, file);
+ }
+
+ /* Low priority queue must be empty */
+ while (!nautilus_file_queue_is_empty (directory->details->extension_queue)) {
+ file = nautilus_file_queue_head (directory->details->extension_queue);
+
+ /* Start getting attributes if possible */
+ extension_info_start (directory, file, &doing_io);
+ if (doing_io) {
+ return;
+ }
+
+ nautilus_directory_remove_file_from_work_queue (directory, file);
+ }
+}
+
+/* Call this when the monitor or call when ready list changes,
+ * or when some I/O is completed.
+ */
+void
+nautilus_directory_async_state_changed (NautilusDirectory *directory)
+{
+ /* Check if any callbacks are satisfied and call them if they
+ * are. Do this last so that any changes done in start or stop
+ * I/O functions immediately (not in callbacks) are taken into
+ * consideration. If any callbacks are called, consider the
+ * I/O state again so that we can release or cancel I/O that
+ * is not longer needed once the callbacks are satisfied.
+ */
+
+ if (directory->details->in_async_service_loop) {
+ directory->details->state_changed = TRUE;
+ return;
+ }
+ directory->details->in_async_service_loop = TRUE;
+ nautilus_directory_ref (directory);
+ do {
+ directory->details->state_changed = FALSE;
+ start_or_stop_io (directory);
+ if (call_ready_callbacks (directory)) {
+ directory->details->state_changed = TRUE;
+ }
+ } while (directory->details->state_changed);
+ directory->details->in_async_service_loop = FALSE;
+ nautilus_directory_unref (directory);
+
+ /* Check if any directories should wake up. */
+ async_job_wake_up ();
+}
+
+void
+nautilus_directory_cancel (NautilusDirectory *directory)
+{
+ /* Arbitrary order (kept alphabetical). */
+ deep_count_cancel (directory);
+ directory_count_cancel (directory);
+ file_info_cancel (directory);
+ file_list_cancel (directory);
+ link_info_cancel (directory);
+ mime_list_cancel (directory);
+ new_files_cancel (directory);
+ extension_info_cancel (directory);
+ thumbnail_cancel (directory);
+ mount_cancel (directory);
+ filesystem_info_cancel (directory);
+
+ /* We aren't waiting for anything any more. */
+ if (waiting_directories != NULL) {
+ g_hash_table_remove (waiting_directories, directory);
+ }
+
+ /* Check if any directories should wake up. */
+ async_job_wake_up ();
+}
+
+static void
+cancel_directory_count_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->count_in_progress != NULL &&
+ directory->details->count_in_progress->count_file == file) {
+ directory_count_cancel (directory);
+ }
+}
+
+static void
+cancel_deep_counts_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->deep_count_file == file) {
+ deep_count_cancel (directory);
+ }
+}
+
+static void
+cancel_mime_list_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->mime_list_in_progress != NULL &&
+ directory->details->mime_list_in_progress->mime_list_file == file) {
+ mime_list_cancel (directory);
+ }
+}
+
+static void
+cancel_file_info_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->get_info_file == file) {
+ file_info_cancel (directory);
+ }
+}
+
+static void
+cancel_thumbnail_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->thumbnail_state != NULL &&
+ directory->details->thumbnail_state->file == file) {
+ thumbnail_cancel (directory);
+ }
+}
+
+static void
+cancel_mount_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->mount_state != NULL &&
+ directory->details->mount_state->file == file) {
+ mount_cancel (directory);
+ }
+}
+
+static void
+cancel_filesystem_info_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->filesystem_info_state != NULL &&
+ directory->details->filesystem_info_state->file == file) {
+ filesystem_info_cancel (directory);
+ }
+}
+
+static void
+cancel_link_info_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->link_info_read_state != NULL &&
+ directory->details->link_info_read_state->file == file) {
+ link_info_cancel (directory);
+ }
+}
+
+
+static void
+cancel_loading_attributes (NautilusDirectory *directory,
+ NautilusFileAttributes file_attributes)
+{
+ Request request;
+
+ request = nautilus_directory_set_up_request (file_attributes);
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT)) {
+ directory_count_cancel (directory);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT)) {
+ deep_count_cancel (directory);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST)) {
+ mime_list_cancel (directory);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO)) {
+ file_info_cancel (directory);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO)) {
+ filesystem_info_cancel (directory);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO)) {
+ link_info_cancel (directory);
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_EXTENSION_INFO)) {
+ extension_info_cancel (directory);
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL)) {
+ thumbnail_cancel (directory);
+ }
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT)) {
+ mount_cancel (directory);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+void
+nautilus_directory_cancel_loading_file_attributes (NautilusDirectory *directory,
+ NautilusFile *file,
+ NautilusFileAttributes file_attributes)
+{
+ Request request;
+
+ nautilus_directory_remove_file_from_work_queue (directory, file);
+
+ request = nautilus_directory_set_up_request (file_attributes);
+
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT)) {
+ cancel_directory_count_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT)) {
+ cancel_deep_counts_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST)) {
+ cancel_mime_list_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO)) {
+ cancel_file_info_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO)) {
+ cancel_filesystem_info_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO)) {
+ cancel_link_info_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL)) {
+ cancel_thumbnail_for_file (directory, file);
+ }
+ if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT)) {
+ cancel_mount_for_file (directory, file);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+}
+
+void
+nautilus_directory_add_file_to_work_queue (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ g_return_if_fail (file->details->directory == directory);
+
+ nautilus_file_queue_enqueue (directory->details->high_priority_queue,
+ file);
+}
+
+
+static void
+add_all_files_to_work_queue (NautilusDirectory *directory)
+{
+ GList *node;
+ NautilusFile *file;
+
+ for (node = directory->details->file_list; node != NULL; node = node->next) {
+ file = NAUTILUS_FILE (node->data);
+
+ nautilus_directory_add_file_to_work_queue (directory, file);
+ }
+}
+
+void
+nautilus_directory_remove_file_from_work_queue (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ nautilus_file_queue_remove (directory->details->high_priority_queue,
+ file);
+ nautilus_file_queue_remove (directory->details->low_priority_queue,
+ file);
+ nautilus_file_queue_remove (directory->details->extension_queue,
+ file);
+}
+
+
+static void
+move_file_to_low_priority_queue (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ /* Must add before removing to avoid ref underflow */
+ nautilus_file_queue_enqueue (directory->details->low_priority_queue,
+ file);
+ nautilus_file_queue_remove (directory->details->high_priority_queue,
+ file);
+}
+
+static void
+move_file_to_extension_queue (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ /* Must add before removing to avoid ref underflow */
+ nautilus_file_queue_enqueue (directory->details->extension_queue,
+ file);
+ nautilus_file_queue_remove (directory->details->low_priority_queue,
+ file);
+}