1 /*
2  *  nautilus-directory-async.c: Nautilus directory model state machine.
3  *
4  *  Copyright (C) 1999, 2000, 2001 Eazel, Inc.
5  *
6  *  This program is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License as
8  *  published by the Free Software Foundation; either version 2 of the
9  *  License, or (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public
17  *  License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  *
19  *  Author: Darin Adler <darin@bentspoon.com>
20  */
21 
22 #include <libxml/parser.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #define DEBUG_FLAG NAUTILUS_DEBUG_ASYNC_JOBS
27 
28 #include "nautilus-debug.h"
29 #include "nautilus-directory-notify.h"
30 #include "nautilus-directory-private.h"
31 #include "nautilus-enums.h"
32 #include "nautilus-file-private.h"
33 #include "nautilus-file-queue.h"
34 #include "nautilus-global-preferences.h"
35 #include "nautilus-metadata.h"
36 #include "nautilus-profile.h"
37 #include "nautilus-signaller.h"
38 
39 /* turn this on to check if async. job calls are balanced */
40 #if 0
41 #define DEBUG_ASYNC_JOBS
42 #endif
43 
44 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
45 
46 /* Keep async. jobs down to this number for all directories. */
47 #define MAX_ASYNC_JOBS 10
48 
49 struct ThumbnailState
50 {
51     NautilusDirectory *directory;
52     GCancellable *cancellable;
53     NautilusFile *file;
54 };
55 
56 struct MountState
57 {
58     NautilusDirectory *directory;
59     GCancellable *cancellable;
60     NautilusFile *file;
61 };
62 
63 struct FilesystemInfoState
64 {
65     NautilusDirectory *directory;
66     GCancellable *cancellable;
67     NautilusFile *file;
68 };
69 
70 struct DirectoryLoadState
71 {
72     NautilusDirectory *directory;
73     GCancellable *cancellable;
74     GFileEnumerator *enumerator;
75     GHashTable *load_mime_list_hash;
76     NautilusFile *load_directory_file;
77     int load_file_count;
78 };
79 
80 struct MimeListState
81 {
82     NautilusDirectory *directory;
83     NautilusFile *mime_list_file;
84     GCancellable *cancellable;
85     GFileEnumerator *enumerator;
86     GHashTable *mime_list_hash;
87 };
88 
89 struct GetInfoState
90 {
91     NautilusDirectory *directory;
92     GCancellable *cancellable;
93 };
94 
95 struct NewFilesState
96 {
97     NautilusDirectory *directory;
98     GCancellable *cancellable;
99     int count;
100 };
101 
102 struct DirectoryCountState
103 {
104     NautilusDirectory *directory;
105     NautilusFile *count_file;
106     GCancellable *cancellable;
107     GFileEnumerator *enumerator;
108     int file_count;
109 };
110 
111 struct DeepCountState
112 {
113     NautilusDirectory *directory;
114     GCancellable *cancellable;
115     GFileEnumerator *enumerator;
116     GFile *deep_count_location;
117     GList *deep_count_subdirectories;
118     GArray *seen_deep_count_inodes;
119     char *fs_id;
120 };
121 
122 
123 
124 typedef struct
125 {
126     NautilusFile *file;     /* Which file, NULL means all. */
127     union
128     {
129         NautilusDirectoryCallback directory;
130         NautilusFileCallback file;
131     } callback;
132     gpointer callback_data;
133     Request request;
134     gboolean active;     /* Set to FALSE when the callback is triggered and
135                           * scheduled to be called at idle, its still kept
136                           * in the list so we can kill it when the file
137                           * goes away.
138                           */
139 } ReadyCallback;
140 
141 typedef struct
142 {
143     NautilusFile *file;     /* Which file, NULL means all. */
144     gboolean monitor_hidden_files;     /* defines whether "all" includes hidden files */
145     gconstpointer client;
146     Request request;
147 } Monitor;
148 
149 typedef struct
150 {
151     NautilusDirectory *directory;
152     NautilusInfoProvider *provider;
153     NautilusOperationHandle *handle;
154     NautilusOperationResult result;
155 } InfoProviderResponse;
156 
157 typedef gboolean (*RequestCheck) (Request);
158 typedef gboolean (*FileCheck) (NautilusFile *);
159 
160 /* Current number of async. jobs. */
161 static int async_job_count;
162 static GHashTable *waiting_directories;
163 #ifdef DEBUG_ASYNC_JOBS
164 static GHashTable *async_jobs;
165 #endif
166 
167 /* Forward declarations for functions that need them. */
168 static void     deep_count_load (DeepCountState *state,
169                                  GFile          *location);
170 static gboolean request_is_satisfied (NautilusDirectory *directory,
171                                       NautilusFile      *file,
172                                       Request            request);
173 static void     cancel_loading_attributes (NautilusDirectory     *directory,
174                                            NautilusFileAttributes file_attributes);
175 static void     add_all_files_to_work_queue (NautilusDirectory *directory);
176 static void     move_file_to_low_priority_queue (NautilusDirectory *directory,
177                                                  NautilusFile      *file);
178 static void     move_file_to_extension_queue (NautilusDirectory *directory,
179                                               NautilusFile      *file);
180 static void     nautilus_directory_invalidate_file_attributes (NautilusDirectory     *directory,
181                                                                NautilusFileAttributes file_attributes);
182 
183 /* Some helpers for case-insensitive strings.
184  * Move to nautilus-glib-extensions?
185  */
186 
187 static gboolean
istr_equal(gconstpointer v,gconstpointer v2)188 istr_equal (gconstpointer v,
189             gconstpointer v2)
190 {
191     return g_ascii_strcasecmp (v, v2) == 0;
192 }
193 
194 static guint
istr_hash(gconstpointer key)195 istr_hash (gconstpointer key)
196 {
197     const char *p;
198     guint h;
199 
200     h = 0;
201     for (p = key; *p != '\0'; p++)
202     {
203         h = (h << 5) - h + g_ascii_tolower (*p);
204     }
205 
206     return h;
207 }
208 
209 static GHashTable *
istr_set_new(void)210 istr_set_new (void)
211 {
212     return g_hash_table_new_full (istr_hash, istr_equal, g_free, NULL);
213 }
214 
215 static void
istr_set_insert(GHashTable * table,const char * istr)216 istr_set_insert (GHashTable *table,
217                  const char *istr)
218 {
219     char *key;
220 
221     key = g_strdup (istr);
222     g_hash_table_replace (table, key, key);
223 }
224 
225 static void
add_istr_to_list(gpointer key,gpointer value,gpointer callback_data)226 add_istr_to_list (gpointer key,
227                   gpointer value,
228                   gpointer callback_data)
229 {
230     GList **list;
231 
232     list = callback_data;
233     *list = g_list_prepend (*list, g_strdup (key));
234 }
235 
236 static GList *
istr_set_get_as_list(GHashTable * table)237 istr_set_get_as_list (GHashTable *table)
238 {
239     GList *list;
240 
241     list = NULL;
242     g_hash_table_foreach (table, add_istr_to_list, &list);
243     return list;
244 }
245 
246 static void
istr_set_destroy(GHashTable * table)247 istr_set_destroy (GHashTable *table)
248 {
249     g_hash_table_destroy (table);
250 }
251 
252 static void
request_counter_add_request(RequestCounter counter,Request request)253 request_counter_add_request (RequestCounter counter,
254                              Request        request)
255 {
256     guint i;
257 
258     for (i = 0; i < REQUEST_TYPE_LAST; i++)
259     {
260         if (REQUEST_WANTS_TYPE (request, i))
261         {
262             counter[i]++;
263         }
264     }
265 }
266 
267 static void
request_counter_remove_request(RequestCounter counter,Request request)268 request_counter_remove_request (RequestCounter counter,
269                                 Request        request)
270 {
271     guint i;
272 
273     for (i = 0; i < REQUEST_TYPE_LAST; i++)
274     {
275         if (REQUEST_WANTS_TYPE (request, i))
276         {
277             counter[i]--;
278         }
279     }
280 }
281 
282 #if 0
283 static void
284 nautilus_directory_verify_request_counts (NautilusDirectory *directory)
285 {
286     GList *l;
287     RequestCounter counters;
288     int i;
289     gboolean fail;
290     GHashTableIter monitor_iter;
291     gpointer value;
292 
293     fail = FALSE;
294     for (i = 0; i < REQUEST_TYPE_LAST; i++)
295     {
296         counters[i] = 0;
297     }
298     g_hash_table_iter_init (&monitor_iter, directory->details->monitor_table);
299     while (g_hash_table_iter_next (&monitor_iter, NULL, &value))
300     {
301         for (l = value; l; l = l->next)
302         {
303             Monitor *monitor = l->data;
304             request_counter_add_request (counters, monitor->request);
305         }
306     }
307     for (i = 0; i < REQUEST_TYPE_LAST; i++)
308     {
309         if (counters[i] != directory->details->monitor_counters[i])
310         {
311             g_warning ("monitor counter for %i is wrong, expecting %d but found %d",
312                        i, counters[i], directory->details->monitor_counters[i]);
313             fail = TRUE;
314         }
315     }
316     for (i = 0; i < REQUEST_TYPE_LAST; i++)
317     {
318         counters[i] = 0;
319     }
320     for (l = directory->details->call_when_ready_list; l != NULL; l = l->next)
321     {
322         ReadyCallback *callback = l->data;
323         request_counter_add_request (counters, callback->request);
324     }
325     for (i = 0; i < REQUEST_TYPE_LAST; i++)
326     {
327         if (counters[i] != directory->details->call_when_ready_counters[i])
328         {
329             g_warning ("call when ready counter for %i is wrong, expecting %d but found %d",
330                        i, counters[i], directory->details->call_when_ready_counters[i]);
331             fail = TRUE;
332         }
333     }
334     g_assert (!fail);
335 }
336 #endif
337 
338 /* Start a job. This is really just a way of limiting the number of
339  * async. requests that we issue at any given time. Without this, the
340  * number of requests is unbounded.
341  */
342 static gboolean
async_job_start(NautilusDirectory * directory,const char * job)343 async_job_start (NautilusDirectory *directory,
344                  const char        *job)
345 {
346 #ifdef DEBUG_ASYNC_JOBS
347     char *key;
348 #endif
349 
350     DEBUG ("starting %s in %p", job, directory->details->location);
351 
352     g_assert (async_job_count >= 0);
353     g_assert (async_job_count <= MAX_ASYNC_JOBS);
354 
355     if (async_job_count >= MAX_ASYNC_JOBS)
356     {
357         if (waiting_directories == NULL)
358         {
359             waiting_directories = g_hash_table_new (NULL, NULL);
360         }
361 
362         g_hash_table_insert (waiting_directories,
363                              directory,
364                              directory);
365 
366         return FALSE;
367     }
368 
369 #ifdef DEBUG_ASYNC_JOBS
370     {
371         char *uri;
372         if (async_jobs == NULL)
373         {
374             async_jobs = g_hash_table_new (g_str_hash, g_str_equal);
375         }
376         uri = nautilus_directory_get_uri (directory);
377         key = g_strconcat (uri, ": ", job, NULL);
378         if (g_hash_table_lookup (async_jobs, key) != NULL)
379         {
380             g_warning ("same job twice: %s in %s",
381                        job, uri);
382         }
383         g_free (uri);
384         g_hash_table_insert (async_jobs, key, directory);
385     }
386 #endif
387 
388     async_job_count += 1;
389     return TRUE;
390 }
391 
392 /* End a job. */
393 static void
async_job_end(NautilusDirectory * directory,const char * job)394 async_job_end (NautilusDirectory *directory,
395                const char        *job)
396 {
397 #ifdef DEBUG_ASYNC_JOBS
398     char *key;
399     gpointer table_key, value;
400 #endif
401 
402     DEBUG ("stopping %s in %p", job, directory->details->location);
403 
404     g_assert (async_job_count > 0);
405 
406 #ifdef DEBUG_ASYNC_JOBS
407     {
408         char *uri;
409         uri = nautilus_directory_get_uri (directory);
410         g_assert (async_jobs != NULL);
411         key = g_strconcat (uri, ": ", job, NULL);
412         if (!g_hash_table_lookup_extended (async_jobs, key, &table_key, &value))
413         {
414             g_warning ("ending job we didn't start: %s in %s",
415                        job, uri);
416         }
417         else
418         {
419             g_hash_table_remove (async_jobs, key);
420             g_free (table_key);
421         }
422         g_free (uri);
423         g_free (key);
424     }
425 #endif
426 
427     async_job_count -= 1;
428 }
429 
430 /* Helper to get one value from a hash table. */
431 static void
get_one_value_callback(gpointer key,gpointer value,gpointer callback_data)432 get_one_value_callback (gpointer key,
433                         gpointer value,
434                         gpointer callback_data)
435 {
436     gpointer *returned_value;
437 
438     returned_value = callback_data;
439     *returned_value = value;
440 }
441 
442 /* return a single value from a hash table. */
443 static gpointer
get_one_value(GHashTable * table)444 get_one_value (GHashTable *table)
445 {
446     gpointer value;
447 
448     value = NULL;
449     if (table != NULL)
450     {
451         g_hash_table_foreach (table, get_one_value_callback, &value);
452     }
453     return value;
454 }
455 
456 /* Wake up directories that are "blocked" as long as there are job
457  * slots available.
458  */
459 static void
async_job_wake_up(void)460 async_job_wake_up (void)
461 {
462     static gboolean already_waking_up = FALSE;
463     gpointer value;
464 
465     g_assert (async_job_count >= 0);
466     g_assert (async_job_count <= MAX_ASYNC_JOBS);
467 
468     if (already_waking_up)
469     {
470         return;
471     }
472 
473     already_waking_up = TRUE;
474     while (async_job_count < MAX_ASYNC_JOBS)
475     {
476         value = get_one_value (waiting_directories);
477         if (value == NULL)
478         {
479             break;
480         }
481         g_hash_table_remove (waiting_directories, value);
482         nautilus_directory_async_state_changed
483             (NAUTILUS_DIRECTORY (value));
484     }
485     already_waking_up = FALSE;
486 }
487 
488 static void
directory_count_cancel(NautilusDirectory * directory)489 directory_count_cancel (NautilusDirectory *directory)
490 {
491     if (directory->details->count_in_progress != NULL)
492     {
493         g_cancellable_cancel (directory->details->count_in_progress->cancellable);
494         directory->details->count_in_progress = NULL;
495     }
496 }
497 
498 static void
deep_count_cancel(NautilusDirectory * directory)499 deep_count_cancel (NautilusDirectory *directory)
500 {
501     if (directory->details->deep_count_in_progress != NULL)
502     {
503         g_assert (NAUTILUS_IS_FILE (directory->details->deep_count_file));
504 
505         g_cancellable_cancel (directory->details->deep_count_in_progress->cancellable);
506 
507         directory->details->deep_count_file->details->deep_counts_status = NAUTILUS_REQUEST_NOT_STARTED;
508 
509         directory->details->deep_count_in_progress->directory = NULL;
510         directory->details->deep_count_in_progress = NULL;
511         directory->details->deep_count_file = NULL;
512 
513         async_job_end (directory, "deep count");
514     }
515 }
516 
517 static void
mime_list_cancel(NautilusDirectory * directory)518 mime_list_cancel (NautilusDirectory *directory)
519 {
520     if (directory->details->mime_list_in_progress != NULL)
521     {
522         g_cancellable_cancel (directory->details->mime_list_in_progress->cancellable);
523     }
524 }
525 
526 static void
thumbnail_cancel(NautilusDirectory * directory)527 thumbnail_cancel (NautilusDirectory *directory)
528 {
529     if (directory->details->thumbnail_state != NULL)
530     {
531         g_cancellable_cancel (directory->details->thumbnail_state->cancellable);
532         directory->details->thumbnail_state->directory = NULL;
533         directory->details->thumbnail_state = NULL;
534         async_job_end (directory, "thumbnail");
535     }
536 }
537 
538 static void
mount_cancel(NautilusDirectory * directory)539 mount_cancel (NautilusDirectory *directory)
540 {
541     if (directory->details->mount_state != NULL)
542     {
543         g_cancellable_cancel (directory->details->mount_state->cancellable);
544         directory->details->mount_state->directory = NULL;
545         directory->details->mount_state = NULL;
546         async_job_end (directory, "mount");
547     }
548 }
549 
550 static void
file_info_cancel(NautilusDirectory * directory)551 file_info_cancel (NautilusDirectory *directory)
552 {
553     if (directory->details->get_info_in_progress != NULL)
554     {
555         g_cancellable_cancel (directory->details->get_info_in_progress->cancellable);
556         directory->details->get_info_in_progress->directory = NULL;
557         directory->details->get_info_in_progress = NULL;
558         directory->details->get_info_file = NULL;
559 
560         async_job_end (directory, "file info");
561     }
562 }
563 
564 static void
new_files_cancel(NautilusDirectory * directory)565 new_files_cancel (NautilusDirectory *directory)
566 {
567     GList *l;
568     NewFilesState *state;
569 
570     if (directory->details->new_files_in_progress != NULL)
571     {
572         for (l = directory->details->new_files_in_progress; l != NULL; l = l->next)
573         {
574             state = l->data;
575             g_cancellable_cancel (state->cancellable);
576             state->directory = NULL;
577         }
578         g_list_free (directory->details->new_files_in_progress);
579         directory->details->new_files_in_progress = NULL;
580     }
581 }
582 
583 static int
monitor_key_compare(gconstpointer a,gconstpointer data)584 monitor_key_compare (gconstpointer a,
585                      gconstpointer data)
586 {
587     const Monitor *monitor;
588     const Monitor *compare_monitor;
589 
590     monitor = a;
591     compare_monitor = data;
592 
593     if (monitor->client < compare_monitor->client)
594     {
595         return -1;
596     }
597     if (monitor->client > compare_monitor->client)
598     {
599         return +1;
600     }
601 
602     if (monitor->file < compare_monitor->file)
603     {
604         return -1;
605     }
606     if (monitor->file > compare_monitor->file)
607     {
608         return +1;
609     }
610 
611     return 0;
612 }
613 
614 static Monitor *
find_monitor(NautilusDirectory * directory,NautilusFile * file,gconstpointer client)615 find_monitor (NautilusDirectory *directory,
616               NautilusFile      *file,
617               gconstpointer      client)
618 {
619     GList *l;
620 
621     l = g_hash_table_lookup (directory->details->monitor_table, file);
622 
623     if (l)
624     {
625         Monitor key = {};
626         key.client = client;
627         key.file = file;
628 
629         l = g_list_find_custom (l, &key, monitor_key_compare);
630         return l ? l->data : NULL;
631     }
632 
633     return NULL;
634 }
635 
636 static gboolean
insert_new_monitor(NautilusDirectory * directory,Monitor * monitor)637 insert_new_monitor (NautilusDirectory *directory,
638                     Monitor           *monitor)
639 {
640     GList *list;
641 
642     if (find_monitor (directory, monitor->file, monitor->client) != NULL)
643     {
644         return FALSE;
645     }
646 
647     list = g_hash_table_lookup (directory->details->monitor_table, monitor->file);
648     if (list == NULL)
649     {
650         list = g_list_append (list, monitor);
651         g_hash_table_insert (directory->details->monitor_table,
652                              monitor->file,
653                              list);
654     }
655     else
656     {
657         list = g_list_append (list, monitor);
658     }
659 
660     request_counter_add_request (directory->details->monitor_counters,
661                                  monitor->request);
662     return TRUE;
663 }
664 
665 static Monitor *
remove_monitor_from_table(NautilusDirectory * directory,NautilusFile * file,gconstpointer client)666 remove_monitor_from_table (NautilusDirectory *directory,
667                            NautilusFile      *file,
668                            gconstpointer      client)
669 {
670     GList *list, *l, *new_list;
671     Monitor *monitor = NULL;
672 
673     list = g_hash_table_lookup (directory->details->monitor_table, file);
674     if (list)
675     {
676         Monitor key = {};
677         key.client = client;
678         key.file = file;
679 
680         l = g_list_find_custom (list, &key, monitor_key_compare);
681         monitor = l ? l->data : NULL;
682     }
683 
684     if (monitor != NULL)
685     {
686         new_list = g_list_delete_link (list, l);
687         if (new_list == NULL)
688         {
689             g_hash_table_remove (directory->details->monitor_table, file);
690         }
691         else
692         {
693             g_hash_table_replace (directory->details->monitor_table, file, new_list);
694         }
695     }
696 
697     return monitor;
698 }
699 
700 static void
remove_monitor(NautilusDirectory * directory,NautilusFile * file,gconstpointer client)701 remove_monitor (NautilusDirectory *directory,
702                 NautilusFile      *file,
703                 gconstpointer      client)
704 {
705     Monitor *monitor;
706 
707     monitor = remove_monitor_from_table (directory, file, client);
708 
709     if (monitor != NULL)
710     {
711         request_counter_remove_request (directory->details->monitor_counters,
712                                         monitor->request);
713         g_free (monitor);
714     }
715 }
716 
717 Request
nautilus_directory_set_up_request(NautilusFileAttributes file_attributes)718 nautilus_directory_set_up_request (NautilusFileAttributes file_attributes)
719 {
720     Request request;
721 
722     request = 0;
723 
724     if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT) != 0)
725     {
726         REQUEST_SET_TYPE (request, REQUEST_DIRECTORY_COUNT);
727     }
728 
729     if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS) != 0)
730     {
731         REQUEST_SET_TYPE (request, REQUEST_DEEP_COUNT);
732     }
733 
734     if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES) != 0)
735     {
736         REQUEST_SET_TYPE (request, REQUEST_MIME_LIST);
737     }
738     if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_INFO) != 0)
739     {
740         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
741     }
742 
743     if ((file_attributes & NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO) != 0)
744     {
745         REQUEST_SET_TYPE (request, REQUEST_EXTENSION_INFO);
746     }
747 
748     if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL)
749     {
750         REQUEST_SET_TYPE (request, REQUEST_THUMBNAIL);
751         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
752     }
753 
754     if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_MOUNT)
755     {
756         REQUEST_SET_TYPE (request, REQUEST_MOUNT);
757         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
758     }
759 
760     if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO)
761     {
762         REQUEST_SET_TYPE (request, REQUEST_FILESYSTEM_INFO);
763     }
764 
765     return request;
766 }
767 
768 static void
mime_db_changed_callback(GObject * ignore,NautilusDirectory * dir)769 mime_db_changed_callback (GObject           *ignore,
770                           NautilusDirectory *dir)
771 {
772     NautilusFileAttributes attrs;
773 
774     g_assert (dir != NULL);
775     g_assert (dir->details != NULL);
776 
777     attrs = NAUTILUS_FILE_ATTRIBUTE_INFO |
778             NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
779 
780     nautilus_directory_force_reload_internal (dir, attrs);
781 }
782 
783 void
nautilus_directory_monitor_add_internal(NautilusDirectory * directory,NautilusFile * file,gconstpointer client,gboolean monitor_hidden_files,NautilusFileAttributes file_attributes,NautilusDirectoryCallback callback,gpointer callback_data)784 nautilus_directory_monitor_add_internal (NautilusDirectory         *directory,
785                                          NautilusFile              *file,
786                                          gconstpointer              client,
787                                          gboolean                   monitor_hidden_files,
788                                          NautilusFileAttributes     file_attributes,
789                                          NautilusDirectoryCallback  callback,
790                                          gpointer                   callback_data)
791 {
792     Monitor *monitor;
793     GList *file_list;
794     char *file_uri = NULL;
795     char *dir_uri = NULL;
796 
797     g_assert (NAUTILUS_IS_DIRECTORY (directory));
798 
799     if (file != NULL)
800     {
801         file_uri = nautilus_file_get_uri (file);
802     }
803     if (directory != NULL)
804     {
805         dir_uri = nautilus_directory_get_uri (directory);
806     }
807     nautilus_profile_start ("uri %s file-uri %s client %p", dir_uri, file_uri, client);
808     g_free (dir_uri);
809     g_free (file_uri);
810 
811     /* Replace any current monitor for this client/file pair. */
812     remove_monitor (directory, file, client);
813 
814     /* Add the new monitor. */
815     monitor = g_new (Monitor, 1);
816     monitor->file = file;
817     monitor->monitor_hidden_files = monitor_hidden_files;
818     monitor->client = client;
819     monitor->request = nautilus_directory_set_up_request (file_attributes);
820 
821     if (file == NULL)
822     {
823         REQUEST_SET_TYPE (monitor->request, REQUEST_FILE_LIST);
824     }
825 
826     insert_new_monitor (directory, monitor);
827 
828     if (callback != NULL)
829     {
830         file_list = nautilus_directory_get_file_list (directory);
831         (*callback)(directory, file_list, callback_data);
832         nautilus_file_list_free (file_list);
833     }
834 
835     /* Start the "real" monitoring (FAM or whatever). */
836     /* We always monitor the whole directory since in practice
837      * nautilus almost always shows the whole directory anyway, and
838      * it allows us to avoid one file monitor per file in a directory.
839      */
840     if (directory->details->monitor == NULL)
841     {
842         directory->details->monitor = nautilus_monitor_directory (directory->details->location);
843     }
844 
845 
846     if (REQUEST_WANTS_TYPE (monitor->request, REQUEST_FILE_INFO) &&
847         directory->details->mime_db_monitor == 0)
848     {
849         directory->details->mime_db_monitor =
850             g_signal_connect_object (nautilus_signaller_get_current (),
851                                      "mime-data-changed",
852                                      G_CALLBACK (mime_db_changed_callback), directory, 0);
853     }
854 
855     /* Put the monitor file or all the files on the work queue. */
856     if (file != NULL)
857     {
858         nautilus_directory_add_file_to_work_queue (directory, file);
859     }
860     else
861     {
862         add_all_files_to_work_queue (directory);
863     }
864 
865     /* Kick off I/O. */
866     nautilus_directory_async_state_changed (directory);
867     nautilus_profile_end (NULL);
868 }
869 
870 static void
set_file_unconfirmed(NautilusFile * file,gboolean unconfirmed)871 set_file_unconfirmed (NautilusFile *file,
872                       gboolean      unconfirmed)
873 {
874     NautilusDirectory *directory;
875 
876     g_assert (NAUTILUS_IS_FILE (file));
877     g_assert (unconfirmed == FALSE || unconfirmed == TRUE);
878 
879     if (file->details->unconfirmed == unconfirmed)
880     {
881         return;
882     }
883     file->details->unconfirmed = unconfirmed;
884 
885     directory = file->details->directory;
886     if (unconfirmed)
887     {
888         directory->details->confirmed_file_count--;
889     }
890     else
891     {
892         directory->details->confirmed_file_count++;
893     }
894 }
895 
896 static gboolean show_hidden_files = TRUE;
897 
898 static void
show_hidden_files_changed_callback(gpointer callback_data)899 show_hidden_files_changed_callback (gpointer callback_data)
900 {
901     show_hidden_files = g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES);
902 }
903 
904 static gboolean
should_skip_file(NautilusDirectory * directory,GFileInfo * info)905 should_skip_file (NautilusDirectory *directory,
906                   GFileInfo         *info)
907 {
908     static gboolean show_hidden_files_changed_callback_installed = FALSE;
909 
910     /* Add the callback once for the life of our process */
911     if (!show_hidden_files_changed_callback_installed)
912     {
913         g_signal_connect_swapped (gtk_filechooser_preferences,
914                                   "changed::" NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
915                                   G_CALLBACK (show_hidden_files_changed_callback),
916                                   NULL);
917 
918         show_hidden_files_changed_callback_installed = TRUE;
919 
920         /* Peek for the first time */
921         show_hidden_files_changed_callback (NULL);
922     }
923 
924     if (!show_hidden_files &&
925         (g_file_info_get_is_hidden (info) ||
926          g_file_info_get_is_backup (info)))
927     {
928         return TRUE;
929     }
930 
931     return FALSE;
932 }
933 
934 static gboolean
dequeue_pending_idle_callback(gpointer callback_data)935 dequeue_pending_idle_callback (gpointer callback_data)
936 {
937     NautilusDirectory *directory;
938     GList *pending_file_info;
939     GList *node, *next;
940     NautilusFile *file;
941     GList *changed_files, *added_files;
942     GFileInfo *file_info;
943     const char *mimetype, *name;
944     DirectoryLoadState *dir_load_state;
945 
946     directory = NAUTILUS_DIRECTORY (callback_data);
947 
948     nautilus_directory_ref (directory);
949 
950     nautilus_profile_start ("nitems %d", g_list_length (directory->details->pending_file_info));
951 
952     directory->details->dequeue_pending_idle_id = 0;
953 
954     /* Handle the files in the order we saw them. */
955     pending_file_info = g_list_reverse (directory->details->pending_file_info);
956     directory->details->pending_file_info = NULL;
957 
958     /* If we are no longer monitoring, then throw away these. */
959     if (!nautilus_directory_is_file_list_monitored (directory))
960     {
961         nautilus_directory_async_state_changed (directory);
962         goto drain;
963     }
964 
965     added_files = NULL;
966     changed_files = NULL;
967 
968     dir_load_state = directory->details->directory_load_in_progress;
969 
970     /* Build a list of NautilusFile objects. */
971     for (node = pending_file_info; node != NULL; node = node->next)
972     {
973         file_info = node->data;
974 
975         name = g_file_info_get_name (file_info);
976 
977         /* Update the file count. */
978         /* FIXME bugzilla.gnome.org 45063: This could count a
979          * file twice if we get it from both load_directory
980          * and from new_files_callback. Not too hard to fix by
981          * moving this into the actual callback instead of
982          * waiting for the idle function.
983          */
984         if (dir_load_state &&
985             !should_skip_file (directory, file_info))
986         {
987             dir_load_state->load_file_count += 1;
988 
989             /* Add the MIME type to the set. */
990             mimetype = g_file_info_get_content_type (file_info);
991             if (mimetype != NULL)
992             {
993                 istr_set_insert (dir_load_state->load_mime_list_hash,
994                                  mimetype);
995             }
996         }
997 
998         /* check if the file already exists */
999         file = nautilus_directory_find_file_by_name (directory, name);
1000         if (file != NULL)
1001         {
1002             /* file already exists in dir, check if we still need to
1003              *  emit file_added or if it changed */
1004             set_file_unconfirmed (file, FALSE);
1005             if (!file->details->is_added)
1006             {
1007                 /* We consider this newly added even if its in the list.
1008                  * This can happen if someone called nautilus_file_get_by_uri()
1009                  * on a file in the folder before the add signal was
1010                  * emitted */
1011                 nautilus_file_ref (file);
1012                 file->details->is_added = TRUE;
1013                 added_files = g_list_prepend (added_files, file);
1014             }
1015             else if (nautilus_file_update_info (file, file_info))
1016             {
1017                 /* File changed, notify about the change. */
1018                 nautilus_file_ref (file);
1019                 changed_files = g_list_prepend (changed_files, file);
1020             }
1021         }
1022         else
1023         {
1024             /* new file, create a nautilus file object and add it to the list */
1025             file = nautilus_file_new_from_info (directory, file_info);
1026             nautilus_directory_add_file (directory, file);
1027             file->details->is_added = TRUE;
1028             added_files = g_list_prepend (added_files, file);
1029         }
1030     }
1031 
1032     /* If we are done loading, then we assume that any unconfirmed
1033      * files are gone.
1034      */
1035     if (directory->details->directory_loaded)
1036     {
1037         for (node = directory->details->file_list;
1038              node != NULL; node = next)
1039         {
1040             file = NAUTILUS_FILE (node->data);
1041             next = node->next;
1042 
1043             if (file->details->unconfirmed)
1044             {
1045                 nautilus_file_ref (file);
1046                 changed_files = g_list_prepend (changed_files, file);
1047 
1048                 nautilus_file_mark_gone (file);
1049             }
1050         }
1051     }
1052 
1053     /* Send the changed and added signals. */
1054     nautilus_directory_emit_change_signals (directory, changed_files);
1055     nautilus_file_list_free (changed_files);
1056     nautilus_directory_emit_files_added (directory, added_files);
1057     nautilus_file_list_free (added_files);
1058 
1059     if (directory->details->directory_loaded &&
1060         !directory->details->directory_loaded_sent_notification)
1061     {
1062         /* Send the done_loading signal. */
1063         nautilus_directory_emit_done_loading (directory);
1064 
1065         if (dir_load_state)
1066         {
1067             file = dir_load_state->load_directory_file;
1068 
1069             file->details->directory_count = dir_load_state->load_file_count;
1070             file->details->directory_count_is_up_to_date = TRUE;
1071             file->details->got_directory_count = TRUE;
1072 
1073             file->details->got_mime_list = TRUE;
1074             file->details->mime_list_is_up_to_date = TRUE;
1075             g_list_free_full (file->details->mime_list, g_free);
1076             file->details->mime_list = istr_set_get_as_list
1077                                            (dir_load_state->load_mime_list_hash);
1078 
1079             nautilus_file_changed (file);
1080         }
1081 
1082         nautilus_directory_async_state_changed (directory);
1083 
1084         directory->details->directory_loaded_sent_notification = TRUE;
1085     }
1086 
1087 drain:
1088     g_list_free_full (pending_file_info, g_object_unref);
1089 
1090     /* Get the state machine running again. */
1091     nautilus_directory_async_state_changed (directory);
1092 
1093     nautilus_profile_end (NULL);
1094 
1095     nautilus_directory_unref (directory);
1096     return FALSE;
1097 }
1098 
1099 void
nautilus_directory_schedule_dequeue_pending(NautilusDirectory * directory)1100 nautilus_directory_schedule_dequeue_pending (NautilusDirectory *directory)
1101 {
1102     if (directory->details->dequeue_pending_idle_id == 0)
1103     {
1104         directory->details->dequeue_pending_idle_id
1105             = g_idle_add (dequeue_pending_idle_callback, directory);
1106     }
1107 }
1108 
1109 static void
directory_load_one(NautilusDirectory * directory,GFileInfo * info)1110 directory_load_one (NautilusDirectory *directory,
1111                     GFileInfo         *info)
1112 {
1113     if (info == NULL)
1114     {
1115         return;
1116     }
1117 
1118     if (g_file_info_get_name (info) == NULL)
1119     {
1120         char *uri;
1121 
1122         uri = nautilus_directory_get_uri (directory);
1123         g_warning ("Got GFileInfo with NULL name in %s, ignoring. This shouldn't happen unless the gvfs backend is broken.\n", uri);
1124         g_free (uri);
1125 
1126         return;
1127     }
1128 
1129     /* Arrange for the "loading" part of the work. */
1130     g_object_ref (info);
1131     directory->details->pending_file_info
1132         = g_list_prepend (directory->details->pending_file_info, info);
1133     nautilus_directory_schedule_dequeue_pending (directory);
1134 }
1135 
1136 static void
directory_load_cancel(NautilusDirectory * directory)1137 directory_load_cancel (NautilusDirectory *directory)
1138 {
1139     NautilusFile *file;
1140     DirectoryLoadState *state;
1141 
1142     state = directory->details->directory_load_in_progress;
1143     if (state != NULL)
1144     {
1145         file = state->load_directory_file;
1146         file->details->loading_directory = FALSE;
1147         if (file->details->directory != directory)
1148         {
1149             nautilus_directory_async_state_changed (file->details->directory);
1150         }
1151 
1152         g_cancellable_cancel (state->cancellable);
1153         state->directory = NULL;
1154         directory->details->directory_load_in_progress = NULL;
1155         async_job_end (directory, "file list");
1156     }
1157 }
1158 
1159 static void
file_list_cancel(NautilusDirectory * directory)1160 file_list_cancel (NautilusDirectory *directory)
1161 {
1162     directory_load_cancel (directory);
1163 
1164     if (directory->details->dequeue_pending_idle_id != 0)
1165     {
1166         g_source_remove (directory->details->dequeue_pending_idle_id);
1167         directory->details->dequeue_pending_idle_id = 0;
1168     }
1169 
1170     if (directory->details->pending_file_info != NULL)
1171     {
1172         g_list_free_full (directory->details->pending_file_info, g_object_unref);
1173         directory->details->pending_file_info = NULL;
1174     }
1175 }
1176 
1177 static void
directory_load_done(NautilusDirectory * directory,GError * error)1178 directory_load_done (NautilusDirectory *directory,
1179                      GError            *error)
1180 {
1181     GList *node;
1182 
1183     nautilus_profile_start (NULL);
1184     g_object_ref (directory);
1185 
1186     directory->details->directory_loaded = TRUE;
1187     directory->details->directory_loaded_sent_notification = FALSE;
1188 
1189     if (error != NULL)
1190     {
1191         /* The load did not complete successfully. This means
1192          * we don't know the status of the files in this directory.
1193          * We clear the unconfirmed bit on each file here so that
1194          * they won't be marked "gone" later -- we don't know enough
1195          * about them to know whether they are really gone.
1196          */
1197         for (node = directory->details->file_list;
1198              node != NULL; node = node->next)
1199         {
1200             set_file_unconfirmed (NAUTILUS_FILE (node->data), FALSE);
1201         }
1202 
1203         nautilus_directory_emit_load_error (directory, error);
1204     }
1205 
1206     /* Call the idle function right away. */
1207     if (directory->details->dequeue_pending_idle_id != 0)
1208     {
1209         g_source_remove (directory->details->dequeue_pending_idle_id);
1210     }
1211     dequeue_pending_idle_callback (directory);
1212 
1213     directory_load_cancel (directory);
1214 
1215     g_object_unref (directory);
1216     nautilus_profile_end (NULL);
1217 }
1218 
1219 void
nautilus_directory_monitor_remove_internal(NautilusDirectory * directory,NautilusFile * file,gconstpointer client)1220 nautilus_directory_monitor_remove_internal (NautilusDirectory *directory,
1221                                             NautilusFile      *file,
1222                                             gconstpointer      client)
1223 {
1224     g_assert (NAUTILUS_IS_DIRECTORY (directory));
1225     g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1226     g_assert (client != NULL);
1227 
1228     remove_monitor (directory, file, client);
1229 
1230     if (directory->details->monitor != NULL
1231         && g_hash_table_size (directory->details->monitor_table) == 0)
1232     {
1233         nautilus_monitor_cancel (directory->details->monitor);
1234         directory->details->monitor = NULL;
1235     }
1236 
1237     /* XXX - do we need to remove anything from the work queue? */
1238 
1239     nautilus_directory_async_state_changed (directory);
1240 }
1241 
1242 FileMonitors *
nautilus_directory_remove_file_monitors(NautilusDirectory * directory,NautilusFile * file)1243 nautilus_directory_remove_file_monitors (NautilusDirectory *directory,
1244                                          NautilusFile      *file)
1245 {
1246     GList *result, *node;
1247     Monitor *monitor;
1248 
1249     g_assert (NAUTILUS_IS_DIRECTORY (directory));
1250     g_assert (NAUTILUS_IS_FILE (file));
1251     g_assert (file->details->directory == directory);
1252 
1253     result = g_hash_table_lookup (directory->details->monitor_table, file);
1254 
1255     if (result != NULL)
1256     {
1257         g_hash_table_remove (directory->details->monitor_table, file);
1258 
1259         for (node = result; node; node = node->next)
1260         {
1261             monitor = node->data;
1262             request_counter_remove_request (directory->details->monitor_counters,
1263                                             monitor->request);
1264         }
1265         result = g_list_reverse (result);
1266     }
1267 
1268     /* XXX - do we need to remove anything from the work queue? */
1269 
1270     nautilus_directory_async_state_changed (directory);
1271 
1272     return (FileMonitors *) result;
1273 }
1274 
1275 void
nautilus_directory_add_file_monitors(NautilusDirectory * directory,NautilusFile * file,FileMonitors * monitors)1276 nautilus_directory_add_file_monitors (NautilusDirectory *directory,
1277                                       NautilusFile      *file,
1278                                       FileMonitors      *monitors)
1279 {
1280     GList *l;
1281     Monitor *monitor;
1282 
1283     g_assert (NAUTILUS_IS_DIRECTORY (directory));
1284     g_assert (NAUTILUS_IS_FILE (file));
1285     g_assert (file->details->directory == directory);
1286 
1287     if (monitors == NULL)
1288     {
1289         return;
1290     }
1291 
1292     for (l = (GList *) monitors; l != NULL; l = l->next)
1293     {
1294         monitor = l->data;
1295 
1296         remove_monitor (directory, monitor->file, monitor->client);
1297         insert_new_monitor (directory, monitor);
1298     }
1299 
1300     g_list_free ((GList *) monitors);
1301 
1302     nautilus_directory_add_file_to_work_queue (directory, file);
1303 
1304     nautilus_directory_async_state_changed (directory);
1305 }
1306 
1307 static int
ready_callback_key_compare(gconstpointer a,gconstpointer b)1308 ready_callback_key_compare (gconstpointer a,
1309                             gconstpointer b)
1310 {
1311     const ReadyCallback *callback_a, *callback_b;
1312 
1313     callback_a = a;
1314     callback_b = b;
1315 
1316     if (callback_a->file < callback_b->file)
1317     {
1318         return -1;
1319     }
1320     if (callback_a->file > callback_b->file)
1321     {
1322         return 1;
1323     }
1324     if (callback_a->file == NULL)
1325     {
1326         /* ANSI C doesn't allow ordered compares of function pointers, so we cast them to
1327          * normal pointers to make some overly pedantic compilers (*cough* HP-UX *cough*)
1328          * compile this. Of course, on any compiler where ordered function pointers actually
1329          * break this probably won't work, but at least it will compile on platforms where it
1330          * works, but stupid compilers won't let you use it.
1331          */
1332         if ((void *) callback_a->callback.directory < (void *) callback_b->callback.directory)
1333         {
1334             return -1;
1335         }
1336         if ((void *) callback_a->callback.directory > (void *) callback_b->callback.directory)
1337         {
1338             return 1;
1339         }
1340     }
1341     else
1342     {
1343         if ((void *) callback_a->callback.file < (void *) callback_b->callback.file)
1344         {
1345             return -1;
1346         }
1347         if ((void *) callback_a->callback.file > (void *) callback_b->callback.file)
1348         {
1349             return 1;
1350         }
1351     }
1352     if (callback_a->callback_data < callback_b->callback_data)
1353     {
1354         return -1;
1355     }
1356     if (callback_a->callback_data > callback_b->callback_data)
1357     {
1358         return 1;
1359     }
1360     return 0;
1361 }
1362 
1363 static int
ready_callback_key_compare_only_active(gconstpointer a,gconstpointer b)1364 ready_callback_key_compare_only_active (gconstpointer a,
1365                                         gconstpointer b)
1366 {
1367     const ReadyCallback *callback_a;
1368 
1369     callback_a = a;
1370 
1371     /* Non active callbacks never match */
1372     if (!callback_a->active)
1373     {
1374         return -1;
1375     }
1376 
1377     return ready_callback_key_compare (a, b);
1378 }
1379 
1380 static void
ready_callback_call(NautilusDirectory * directory,const ReadyCallback * callback)1381 ready_callback_call (NautilusDirectory   *directory,
1382                      const ReadyCallback *callback)
1383 {
1384     GList *file_list;
1385 
1386     /* Call the callback. */
1387     if (callback->file != NULL)
1388     {
1389         if (callback->callback.file)
1390         {
1391             (*callback->callback.file)(callback->file,
1392                                        callback->callback_data);
1393         }
1394     }
1395     else if (callback->callback.directory != NULL)
1396     {
1397         if (directory == NULL ||
1398             !REQUEST_WANTS_TYPE (callback->request, REQUEST_FILE_LIST))
1399         {
1400             file_list = NULL;
1401         }
1402         else
1403         {
1404             file_list = nautilus_directory_get_file_list (directory);
1405         }
1406 
1407         /* Pass back the file list if the user was waiting for it. */
1408         (*callback->callback.directory)(directory,
1409                                         file_list,
1410                                         callback->callback_data);
1411 
1412         nautilus_file_list_free (file_list);
1413     }
1414 }
1415 
1416 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)1417 nautilus_directory_call_when_ready_internal (NautilusDirectory         *directory,
1418                                              NautilusFile              *file,
1419                                              NautilusFileAttributes     file_attributes,
1420                                              gboolean                   wait_for_file_list,
1421                                              NautilusDirectoryCallback  directory_callback,
1422                                              NautilusFileCallback       file_callback,
1423                                              gpointer                   callback_data)
1424 {
1425     ReadyCallback callback;
1426 
1427     g_assert (directory == NULL || NAUTILUS_IS_DIRECTORY (directory));
1428     g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1429     g_assert (file != NULL || directory_callback != NULL);
1430 
1431     /* Construct a callback object. */
1432     callback.active = TRUE;
1433     callback.file = file;
1434     if (file == NULL)
1435     {
1436         callback.callback.directory = directory_callback;
1437     }
1438     else
1439     {
1440         callback.callback.file = file_callback;
1441     }
1442     callback.callback_data = callback_data;
1443     callback.request = nautilus_directory_set_up_request (file_attributes);
1444     if (wait_for_file_list)
1445     {
1446         REQUEST_SET_TYPE (callback.request, REQUEST_FILE_LIST);
1447     }
1448 
1449     /* Handle the NULL case. */
1450     if (directory == NULL)
1451     {
1452         ready_callback_call (NULL, &callback);
1453         return;
1454     }
1455 
1456     /* Check if the callback is already there. */
1457     if (g_list_find_custom (directory->details->call_when_ready_list,
1458                             &callback,
1459                             ready_callback_key_compare_only_active) != NULL)
1460     {
1461         if (file_callback != NULL && directory_callback != NULL)
1462         {
1463             g_warning ("tried to add a new callback while an old one was pending");
1464         }
1465         /* NULL callback means, just read it. Conflicts are ok. */
1466         return;
1467     }
1468 
1469     /* Add the new callback to the list. */
1470     directory->details->call_when_ready_list = g_list_prepend
1471                                                    (directory->details->call_when_ready_list,
1472                                                    g_memdup (&callback, sizeof (callback)));
1473     request_counter_add_request (directory->details->call_when_ready_counters,
1474                                  callback.request);
1475 
1476     /* Put the callback file or all the files on the work queue. */
1477     if (file != NULL)
1478     {
1479         nautilus_directory_add_file_to_work_queue (directory, file);
1480     }
1481     else
1482     {
1483         add_all_files_to_work_queue (directory);
1484     }
1485 
1486     nautilus_directory_async_state_changed (directory);
1487 }
1488 
1489 gboolean
nautilus_directory_check_if_ready_internal(NautilusDirectory * directory,NautilusFile * file,NautilusFileAttributes file_attributes)1490 nautilus_directory_check_if_ready_internal (NautilusDirectory      *directory,
1491                                             NautilusFile           *file,
1492                                             NautilusFileAttributes  file_attributes)
1493 {
1494     Request request;
1495 
1496     g_assert (NAUTILUS_IS_DIRECTORY (directory));
1497 
1498     request = nautilus_directory_set_up_request (file_attributes);
1499     return request_is_satisfied (directory, file, request);
1500 }
1501 
1502 static void
remove_callback_link_keep_data(NautilusDirectory * directory,GList * link)1503 remove_callback_link_keep_data (NautilusDirectory *directory,
1504                                 GList             *link)
1505 {
1506     ReadyCallback *callback;
1507 
1508     callback = link->data;
1509 
1510     directory->details->call_when_ready_list = g_list_remove_link
1511                                                    (directory->details->call_when_ready_list, link);
1512 
1513     request_counter_remove_request (directory->details->call_when_ready_counters,
1514                                     callback->request);
1515     g_list_free_1 (link);
1516 }
1517 
1518 static void
remove_callback_link(NautilusDirectory * directory,GList * link)1519 remove_callback_link (NautilusDirectory *directory,
1520                       GList             *link)
1521 {
1522     ReadyCallback *callback;
1523 
1524     callback = link->data;
1525     remove_callback_link_keep_data (directory, link);
1526     g_free (callback);
1527 }
1528 
1529 void
nautilus_directory_cancel_callback_internal(NautilusDirectory * directory,NautilusFile * file,NautilusDirectoryCallback directory_callback,NautilusFileCallback file_callback,gpointer callback_data)1530 nautilus_directory_cancel_callback_internal (NautilusDirectory         *directory,
1531                                              NautilusFile              *file,
1532                                              NautilusDirectoryCallback  directory_callback,
1533                                              NautilusFileCallback       file_callback,
1534                                              gpointer                   callback_data)
1535 {
1536     ReadyCallback callback;
1537     GList *node;
1538 
1539     if (directory == NULL)
1540     {
1541         return;
1542     }
1543 
1544     g_assert (NAUTILUS_IS_DIRECTORY (directory));
1545     g_assert (file == NULL || NAUTILUS_IS_FILE (file));
1546     g_assert (file != NULL || directory_callback != NULL);
1547     g_assert (file == NULL || file_callback != NULL);
1548 
1549     /* Construct a callback object. */
1550     callback.file = file;
1551     if (file == NULL)
1552     {
1553         callback.callback.directory = directory_callback;
1554     }
1555     else
1556     {
1557         callback.callback.file = file_callback;
1558     }
1559     callback.callback_data = callback_data;
1560 
1561     /* Remove all queued callback from the list (including non-active). */
1562     do
1563     {
1564         node = g_list_find_custom (directory->details->call_when_ready_list,
1565                                    &callback,
1566                                    ready_callback_key_compare);
1567         if (node != NULL)
1568         {
1569             remove_callback_link (directory, node);
1570 
1571             nautilus_directory_async_state_changed (directory);
1572         }
1573     }
1574     while (node != NULL);
1575 }
1576 
1577 static void
new_files_state_unref(NewFilesState * state)1578 new_files_state_unref (NewFilesState *state)
1579 {
1580     state->count--;
1581 
1582     if (state->count == 0)
1583     {
1584         if (state->directory)
1585         {
1586             state->directory->details->new_files_in_progress =
1587                 g_list_remove (state->directory->details->new_files_in_progress,
1588                                state);
1589         }
1590 
1591         g_object_unref (state->cancellable);
1592         g_free (state);
1593     }
1594 }
1595 
1596 static void
new_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)1597 new_files_callback (GObject      *source_object,
1598                     GAsyncResult *res,
1599                     gpointer      user_data)
1600 {
1601     NautilusDirectory *directory;
1602     GFileInfo *info;
1603     NewFilesState *state;
1604 
1605     state = user_data;
1606 
1607     if (state->directory == NULL)
1608     {
1609         /* Operation was cancelled. Bail out */
1610         new_files_state_unref (state);
1611         return;
1612     }
1613 
1614     directory = nautilus_directory_ref (state->directory);
1615 
1616     /* Queue up the new file. */
1617     info = g_file_query_info_finish (G_FILE (source_object), res, NULL);
1618     if (info != NULL)
1619     {
1620         directory_load_one (directory, info);
1621         g_object_unref (info);
1622     }
1623 
1624     new_files_state_unref (state);
1625 
1626     nautilus_directory_unref (directory);
1627 }
1628 
1629 void
nautilus_directory_get_info_for_new_files(NautilusDirectory * directory,GList * location_list)1630 nautilus_directory_get_info_for_new_files (NautilusDirectory *directory,
1631                                            GList             *location_list)
1632 {
1633     NewFilesState *state;
1634     GFile *location;
1635     GList *l;
1636 
1637     if (location_list == NULL)
1638     {
1639         return;
1640     }
1641 
1642     state = g_new (NewFilesState, 1);
1643     state->directory = directory;
1644     state->cancellable = g_cancellable_new ();
1645     state->count = 0;
1646 
1647     for (l = location_list; l != NULL; l = l->next)
1648     {
1649         location = l->data;
1650 
1651         state->count++;
1652 
1653         g_file_query_info_async (location,
1654                                  NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
1655                                  0,
1656                                  G_PRIORITY_DEFAULT,
1657                                  state->cancellable,
1658                                  new_files_callback, state);
1659     }
1660 
1661     directory->details->new_files_in_progress
1662         = g_list_prepend (directory->details->new_files_in_progress,
1663                           state);
1664 }
1665 
1666 void
nautilus_async_destroying_file(NautilusFile * file)1667 nautilus_async_destroying_file (NautilusFile *file)
1668 {
1669     NautilusDirectory *directory;
1670     gboolean changed;
1671     GList *node, *next;
1672     ReadyCallback *callback;
1673     Monitor *monitor;
1674 
1675     directory = file->details->directory;
1676     changed = FALSE;
1677 
1678     /* Check for callbacks. */
1679     for (node = directory->details->call_when_ready_list; node != NULL; node = next)
1680     {
1681         next = node->next;
1682         callback = node->data;
1683 
1684         if (callback->file == file)
1685         {
1686             /* Client should have cancelled callback. */
1687             if (callback->active)
1688             {
1689                 g_warning ("destroyed file has call_when_ready pending");
1690             }
1691             remove_callback_link (directory, node);
1692             changed = TRUE;
1693         }
1694     }
1695 
1696     /* Check for monitors. */
1697     node = g_hash_table_lookup (directory->details->monitor_table, file);
1698     if (node != NULL)
1699     {
1700         /* Client should have removed monitor earlier. */
1701         g_warning ("destroyed file still being monitored");
1702         for (; node; node = next)
1703         {
1704             next = node->next;
1705             monitor = node->data;
1706 
1707             remove_monitor (directory, monitor->file, monitor->client);
1708         }
1709         changed = TRUE;
1710     }
1711 
1712     /* Check if it's a file that's currently being worked on.
1713      * If so, make that NULL so it gets canceled right away.
1714      */
1715     if (directory->details->count_in_progress != NULL &&
1716         directory->details->count_in_progress->count_file == file)
1717     {
1718         directory->details->count_in_progress->count_file = NULL;
1719         changed = TRUE;
1720     }
1721     if (directory->details->deep_count_file == file)
1722     {
1723         directory->details->deep_count_file = NULL;
1724         changed = TRUE;
1725     }
1726     if (directory->details->mime_list_in_progress != NULL &&
1727         directory->details->mime_list_in_progress->mime_list_file == file)
1728     {
1729         directory->details->mime_list_in_progress->mime_list_file = NULL;
1730         changed = TRUE;
1731     }
1732     if (directory->details->get_info_file == file)
1733     {
1734         directory->details->get_info_file = NULL;
1735         changed = TRUE;
1736     }
1737     if (directory->details->extension_info_file == file)
1738     {
1739         directory->details->extension_info_file = NULL;
1740         changed = TRUE;
1741     }
1742 
1743     if (directory->details->thumbnail_state != NULL &&
1744         directory->details->thumbnail_state->file == file)
1745     {
1746         directory->details->thumbnail_state->file = NULL;
1747         changed = TRUE;
1748     }
1749 
1750     if (directory->details->mount_state != NULL &&
1751         directory->details->mount_state->file == file)
1752     {
1753         directory->details->mount_state->file = NULL;
1754         changed = TRUE;
1755     }
1756 
1757     if (directory->details->filesystem_info_state != NULL &&
1758         directory->details->filesystem_info_state->file == file)
1759     {
1760         directory->details->filesystem_info_state->file = NULL;
1761         changed = TRUE;
1762     }
1763 
1764     /* Let the directory take care of the rest. */
1765     if (changed)
1766     {
1767         nautilus_directory_async_state_changed (directory);
1768     }
1769 }
1770 
1771 static gboolean
lacks_directory_count(NautilusFile * file)1772 lacks_directory_count (NautilusFile *file)
1773 {
1774     return !file->details->directory_count_is_up_to_date
1775            && nautilus_file_should_show_directory_item_count (file);
1776 }
1777 
1778 static gboolean
should_get_directory_count_now(NautilusFile * file)1779 should_get_directory_count_now (NautilusFile *file)
1780 {
1781     return lacks_directory_count (file)
1782            && !file->details->loading_directory;
1783 }
1784 
1785 static gboolean
lacks_info(NautilusFile * file)1786 lacks_info (NautilusFile *file)
1787 {
1788     return !file->details->file_info_is_up_to_date
1789            && !file->details->is_gone;
1790 }
1791 
1792 static gboolean
lacks_filesystem_info(NautilusFile * file)1793 lacks_filesystem_info (NautilusFile *file)
1794 {
1795     return !file->details->filesystem_info_is_up_to_date;
1796 }
1797 
1798 static gboolean
lacks_deep_count(NautilusFile * file)1799 lacks_deep_count (NautilusFile *file)
1800 {
1801     return file->details->deep_counts_status != NAUTILUS_REQUEST_DONE;
1802 }
1803 
1804 static gboolean
lacks_mime_list(NautilusFile * file)1805 lacks_mime_list (NautilusFile *file)
1806 {
1807     return !file->details->mime_list_is_up_to_date;
1808 }
1809 
1810 static gboolean
should_get_mime_list(NautilusFile * file)1811 should_get_mime_list (NautilusFile *file)
1812 {
1813     return lacks_mime_list (file)
1814            && !file->details->loading_directory;
1815 }
1816 
1817 static gboolean
lacks_extension_info(NautilusFile * file)1818 lacks_extension_info (NautilusFile *file)
1819 {
1820     return file->details->pending_info_providers != NULL;
1821 }
1822 
1823 static gboolean
lacks_thumbnail(NautilusFile * file)1824 lacks_thumbnail (NautilusFile *file)
1825 {
1826     return nautilus_file_should_show_thumbnail (file) &&
1827            file->details->thumbnail_path != NULL &&
1828            !file->details->thumbnail_is_up_to_date;
1829 }
1830 
1831 static gboolean
lacks_mount(NautilusFile * file)1832 lacks_mount (NautilusFile *file)
1833 {
1834     return (!file->details->mount_is_up_to_date &&
1835             (
1836                 /* Unix mountpoint, could be a GMount */
1837                 file->details->is_mountpoint ||
1838 
1839                 /* The toplevel directory of something */
1840                 (file->details->type == G_FILE_TYPE_DIRECTORY &&
1841                  nautilus_file_is_self_owned (file)) ||
1842 
1843                 /* Mountable, could be a mountpoint */
1844                 (file->details->type == G_FILE_TYPE_MOUNTABLE)
1845 
1846             )
1847             );
1848 }
1849 
1850 static gboolean
has_problem(NautilusDirectory * directory,NautilusFile * file,FileCheck problem)1851 has_problem (NautilusDirectory *directory,
1852              NautilusFile      *file,
1853              FileCheck          problem)
1854 {
1855     GList *node;
1856 
1857     if (file != NULL)
1858     {
1859         return (*problem)(file);
1860     }
1861 
1862     for (node = directory->details->file_list; node != NULL; node = node->next)
1863     {
1864         if ((*problem)(node->data))
1865         {
1866             return TRUE;
1867         }
1868     }
1869 
1870     return FALSE;
1871 }
1872 
1873 static gboolean
request_is_satisfied(NautilusDirectory * directory,NautilusFile * file,Request request)1874 request_is_satisfied (NautilusDirectory *directory,
1875                       NautilusFile      *file,
1876                       Request            request)
1877 {
1878     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_LIST) &&
1879         !(directory->details->directory_loaded &&
1880           directory->details->directory_loaded_sent_notification))
1881     {
1882         return FALSE;
1883     }
1884 
1885     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
1886     {
1887         if (has_problem (directory, file, lacks_directory_count))
1888         {
1889             return FALSE;
1890         }
1891     }
1892 
1893     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
1894     {
1895         if (has_problem (directory, file, lacks_info))
1896         {
1897             return FALSE;
1898         }
1899     }
1900 
1901     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
1902     {
1903         if (has_problem (directory, file, lacks_filesystem_info))
1904         {
1905             return FALSE;
1906         }
1907     }
1908 
1909     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
1910     {
1911         if (has_problem (directory, file, lacks_deep_count))
1912         {
1913             return FALSE;
1914         }
1915     }
1916 
1917     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
1918     {
1919         if (has_problem (directory, file, lacks_thumbnail))
1920         {
1921             return FALSE;
1922         }
1923     }
1924 
1925     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
1926     {
1927         if (has_problem (directory, file, lacks_mount))
1928         {
1929             return FALSE;
1930         }
1931     }
1932 
1933     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
1934     {
1935         if (has_problem (directory, file, lacks_mime_list))
1936         {
1937             return FALSE;
1938         }
1939     }
1940 
1941     return TRUE;
1942 }
1943 
1944 static gboolean
call_ready_callbacks_at_idle(gpointer callback_data)1945 call_ready_callbacks_at_idle (gpointer callback_data)
1946 {
1947     NautilusDirectory *directory;
1948     GList *node, *next;
1949     ReadyCallback *callback;
1950 
1951     directory = NAUTILUS_DIRECTORY (callback_data);
1952     directory->details->call_ready_idle_id = 0;
1953 
1954     nautilus_directory_ref (directory);
1955 
1956     callback = NULL;
1957     while (1)
1958     {
1959         /* Check if any callbacks are non-active and call them if they are. */
1960         for (node = directory->details->call_when_ready_list;
1961              node != NULL; node = next)
1962         {
1963             next = node->next;
1964             callback = node->data;
1965             if (!callback->active)
1966             {
1967                 /* Non-active, remove and call */
1968                 break;
1969             }
1970         }
1971         if (node == NULL)
1972         {
1973             break;
1974         }
1975 
1976         /* Callbacks are one-shots, so remove it now. */
1977         remove_callback_link_keep_data (directory, node);
1978 
1979         /* Call the callback. */
1980         ready_callback_call (directory, callback);
1981         g_free (callback);
1982     }
1983 
1984     nautilus_directory_async_state_changed (directory);
1985 
1986     nautilus_directory_unref (directory);
1987 
1988     return FALSE;
1989 }
1990 
1991 static void
schedule_call_ready_callbacks(NautilusDirectory * directory)1992 schedule_call_ready_callbacks (NautilusDirectory *directory)
1993 {
1994     if (directory->details->call_ready_idle_id == 0)
1995     {
1996         directory->details->call_ready_idle_id
1997             = g_idle_add (call_ready_callbacks_at_idle, directory);
1998     }
1999 }
2000 
2001 /* Marks all callbacks that are ready as non-active and
2002  * calls them at idle time, unless they are removed
2003  * before then */
2004 static gboolean
call_ready_callbacks(NautilusDirectory * directory)2005 call_ready_callbacks (NautilusDirectory *directory)
2006 {
2007     gboolean found_any;
2008     GList *node, *next;
2009     ReadyCallback *callback;
2010 
2011     found_any = FALSE;
2012 
2013     /* Check if any callbacks are satisifed and mark them for call them if they are. */
2014     for (node = directory->details->call_when_ready_list;
2015          node != NULL; node = next)
2016     {
2017         next = node->next;
2018         callback = node->data;
2019         if (callback->active &&
2020             request_is_satisfied (directory, callback->file, callback->request))
2021         {
2022             callback->active = FALSE;
2023             found_any = TRUE;
2024         }
2025     }
2026 
2027     if (found_any)
2028     {
2029         schedule_call_ready_callbacks (directory);
2030     }
2031 
2032     return found_any;
2033 }
2034 
2035 static GList *
lookup_monitors(GHashTable * monitor_table,NautilusFile * file)2036 lookup_monitors (GHashTable   *monitor_table,
2037                  NautilusFile *file)
2038 {
2039     /* To find monitors monitoring all files, use lookup_all_files_monitors. */
2040     g_return_val_if_fail (file, NULL);
2041 
2042     return g_hash_table_lookup (monitor_table, file);
2043 }
2044 
2045 static GList *
lookup_all_files_monitors(GHashTable * monitor_table)2046 lookup_all_files_monitors (GHashTable *monitor_table)
2047 {
2048     /* monitor->file == NULL means monitor all files. */
2049     return g_hash_table_lookup (monitor_table, NULL);
2050 }
2051 
2052 gboolean
nautilus_directory_has_active_request_for_file(NautilusDirectory * directory,NautilusFile * file)2053 nautilus_directory_has_active_request_for_file (NautilusDirectory *directory,
2054                                                 NautilusFile      *file)
2055 {
2056     GList *node;
2057     ReadyCallback *callback;
2058 
2059     for (node = directory->details->call_when_ready_list;
2060          node != NULL; node = node->next)
2061     {
2062         callback = node->data;
2063         if (callback->file == file ||
2064             callback->file == NULL)
2065         {
2066             return TRUE;
2067         }
2068     }
2069 
2070     if (lookup_monitors (directory->details->monitor_table, file) != NULL)
2071     {
2072         return TRUE;
2073     }
2074     if (lookup_all_files_monitors (directory->details->monitor_table) != NULL)
2075     {
2076         return TRUE;
2077     }
2078 
2079     return FALSE;
2080 }
2081 
2082 
2083 /* This checks if there's a request for monitoring the file list. */
2084 gboolean
nautilus_directory_is_anyone_monitoring_file_list(NautilusDirectory * directory)2085 nautilus_directory_is_anyone_monitoring_file_list (NautilusDirectory *directory)
2086 {
2087     if (directory->details->call_when_ready_counters[REQUEST_FILE_LIST] > 0)
2088     {
2089         return TRUE;
2090     }
2091 
2092     if (directory->details->monitor_counters[REQUEST_FILE_LIST] > 0)
2093     {
2094         return TRUE;
2095     }
2096 
2097     return FALSE;
2098 }
2099 
2100 /* This checks if the file list being monitored. */
2101 gboolean
nautilus_directory_is_file_list_monitored(NautilusDirectory * directory)2102 nautilus_directory_is_file_list_monitored (NautilusDirectory *directory)
2103 {
2104     return directory->details->file_list_monitored;
2105 }
2106 
2107 static void
mark_all_files_unconfirmed(NautilusDirectory * directory)2108 mark_all_files_unconfirmed (NautilusDirectory *directory)
2109 {
2110     GList *node;
2111     NautilusFile *file;
2112 
2113     for (node = directory->details->file_list; node != NULL; node = node->next)
2114     {
2115         file = node->data;
2116         set_file_unconfirmed (file, TRUE);
2117     }
2118 }
2119 
2120 static void
directory_load_state_free(DirectoryLoadState * state)2121 directory_load_state_free (DirectoryLoadState *state)
2122 {
2123     if (state->enumerator)
2124     {
2125         if (!g_file_enumerator_is_closed (state->enumerator))
2126         {
2127             g_file_enumerator_close_async (state->enumerator,
2128                                            0, NULL, NULL, NULL);
2129         }
2130         g_object_unref (state->enumerator);
2131     }
2132 
2133     if (state->load_mime_list_hash != NULL)
2134     {
2135         istr_set_destroy (state->load_mime_list_hash);
2136     }
2137     nautilus_file_unref (state->load_directory_file);
2138     g_object_unref (state->cancellable);
2139     g_free (state);
2140 }
2141 
2142 static void
more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2143 more_files_callback (GObject      *source_object,
2144                      GAsyncResult *res,
2145                      gpointer      user_data)
2146 {
2147     DirectoryLoadState *state;
2148     NautilusDirectory *directory;
2149     GError *error;
2150     GList *files, *l;
2151     GFileInfo *info;
2152 
2153     state = user_data;
2154 
2155     if (state->directory == NULL)
2156     {
2157         /* Operation was cancelled. Bail out */
2158         directory_load_state_free (state);
2159         return;
2160     }
2161 
2162     directory = nautilus_directory_ref (state->directory);
2163 
2164     g_assert (directory->details->directory_load_in_progress != NULL);
2165     g_assert (directory->details->directory_load_in_progress == state);
2166 
2167     error = NULL;
2168     files = g_file_enumerator_next_files_finish (state->enumerator,
2169                                                  res, &error);
2170 
2171     for (l = files; l != NULL; l = l->next)
2172     {
2173         info = l->data;
2174         directory_load_one (directory, info);
2175         g_object_unref (info);
2176     }
2177 
2178     if (files == NULL)
2179     {
2180         directory_load_done (directory, error);
2181         directory_load_state_free (state);
2182     }
2183     else
2184     {
2185         g_file_enumerator_next_files_async (state->enumerator,
2186                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2187                                             G_PRIORITY_DEFAULT,
2188                                             state->cancellable,
2189                                             more_files_callback,
2190                                             state);
2191     }
2192 
2193     nautilus_directory_unref (directory);
2194 
2195     if (error)
2196     {
2197         g_error_free (error);
2198     }
2199 
2200     g_list_free (files);
2201 }
2202 
2203 static void
enumerate_children_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2204 enumerate_children_callback (GObject      *source_object,
2205                              GAsyncResult *res,
2206                              gpointer      user_data)
2207 {
2208     DirectoryLoadState *state;
2209     GFileEnumerator *enumerator;
2210     GError *error;
2211 
2212     state = user_data;
2213 
2214     if (state->directory == NULL)
2215     {
2216         /* Operation was cancelled. Bail out */
2217         directory_load_state_free (state);
2218         return;
2219     }
2220 
2221     error = NULL;
2222     enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
2223                                                    res, &error);
2224 
2225     if (enumerator == NULL)
2226     {
2227         directory_load_done (state->directory, error);
2228         g_error_free (error);
2229         directory_load_state_free (state);
2230         return;
2231     }
2232     else
2233     {
2234         state->enumerator = enumerator;
2235         g_file_enumerator_next_files_async (state->enumerator,
2236                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2237                                             G_PRIORITY_DEFAULT,
2238                                             state->cancellable,
2239                                             more_files_callback,
2240                                             state);
2241     }
2242 }
2243 
2244 
2245 /* Start monitoring the file list if it isn't already. */
2246 static void
start_monitoring_file_list(NautilusDirectory * directory)2247 start_monitoring_file_list (NautilusDirectory *directory)
2248 {
2249     DirectoryLoadState *state;
2250 
2251     if (!directory->details->file_list_monitored)
2252     {
2253         g_assert (!directory->details->directory_load_in_progress);
2254         directory->details->file_list_monitored = TRUE;
2255         nautilus_file_list_ref (directory->details->file_list);
2256     }
2257 
2258     if (directory->details->directory_loaded ||
2259         directory->details->directory_load_in_progress != NULL)
2260     {
2261         return;
2262     }
2263 
2264     if (!async_job_start (directory, "file list"))
2265     {
2266         return;
2267     }
2268 
2269     mark_all_files_unconfirmed (directory);
2270 
2271     state = g_new0 (DirectoryLoadState, 1);
2272     state->directory = directory;
2273     state->cancellable = g_cancellable_new ();
2274     state->load_mime_list_hash = istr_set_new ();
2275     state->load_file_count = 0;
2276 
2277     g_assert (directory->details->location != NULL);
2278     state->load_directory_file =
2279         nautilus_directory_get_corresponding_file (directory);
2280     state->load_directory_file->details->loading_directory = TRUE;
2281 
2282 
2283     DEBUG ("load_directory called to monitor file list of %p", directory->details->location);
2284 
2285     directory->details->directory_load_in_progress = state;
2286 
2287     g_file_enumerate_children_async (directory->details->location,
2288                                      NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
2289                                      0,     /* flags */
2290                                      G_PRIORITY_DEFAULT,     /* prio */
2291                                      state->cancellable,
2292                                      enumerate_children_callback,
2293                                      state);
2294 }
2295 
2296 /* Stop monitoring the file list if it is being monitored. */
2297 void
nautilus_directory_stop_monitoring_file_list(NautilusDirectory * directory)2298 nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory)
2299 {
2300     if (!directory->details->file_list_monitored)
2301     {
2302         g_assert (directory->details->directory_load_in_progress == NULL);
2303         return;
2304     }
2305 
2306     directory->details->file_list_monitored = FALSE;
2307     file_list_cancel (directory);
2308     nautilus_file_list_unref (directory->details->file_list);
2309     directory->details->directory_loaded = FALSE;
2310 }
2311 
2312 static void
file_list_start_or_stop(NautilusDirectory * directory)2313 file_list_start_or_stop (NautilusDirectory *directory)
2314 {
2315     if (nautilus_directory_is_anyone_monitoring_file_list (directory))
2316     {
2317         start_monitoring_file_list (directory);
2318     }
2319     else
2320     {
2321         nautilus_directory_stop_monitoring_file_list (directory);
2322     }
2323 }
2324 
2325 void
nautilus_file_invalidate_count_and_mime_list(NautilusFile * file)2326 nautilus_file_invalidate_count_and_mime_list (NautilusFile *file)
2327 {
2328     NautilusFileAttributes attributes;
2329 
2330     attributes = NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
2331                  NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
2332 
2333     nautilus_file_invalidate_attributes (file, attributes);
2334 }
2335 
2336 
2337 /* Reset count and mime list. Invalidating deep counts is handled by
2338  * itself elsewhere because it's a relatively heavyweight and
2339  * special-purpose operation (see bug 5863). Also, the shallow count
2340  * needs to be refreshed when filtering changes, but the deep count
2341  * deliberately does not take filtering into account.
2342  */
2343 void
nautilus_directory_invalidate_count_and_mime_list(NautilusDirectory * directory)2344 nautilus_directory_invalidate_count_and_mime_list (NautilusDirectory *directory)
2345 {
2346     NautilusFile *file;
2347 
2348     file = nautilus_directory_get_existing_corresponding_file (directory);
2349     if (file != NULL)
2350     {
2351         nautilus_file_invalidate_count_and_mime_list (file);
2352     }
2353 
2354     nautilus_file_unref (file);
2355 }
2356 
2357 static void
nautilus_directory_invalidate_file_attributes(NautilusDirectory * directory,NautilusFileAttributes file_attributes)2358 nautilus_directory_invalidate_file_attributes (NautilusDirectory      *directory,
2359                                                NautilusFileAttributes  file_attributes)
2360 {
2361     GList *node;
2362 
2363     cancel_loading_attributes (directory, file_attributes);
2364 
2365     for (node = directory->details->file_list; node != NULL; node = node->next)
2366     {
2367         nautilus_file_invalidate_attributes_internal (NAUTILUS_FILE (node->data),
2368                                                       file_attributes);
2369     }
2370 
2371     if (directory->details->as_file != NULL)
2372     {
2373         nautilus_file_invalidate_attributes_internal (directory->details->as_file,
2374                                                       file_attributes);
2375     }
2376 }
2377 
2378 void
nautilus_directory_force_reload_internal(NautilusDirectory * directory,NautilusFileAttributes file_attributes)2379 nautilus_directory_force_reload_internal (NautilusDirectory      *directory,
2380                                           NautilusFileAttributes  file_attributes)
2381 {
2382     nautilus_profile_start (NULL);
2383 
2384     /* invalidate attributes that are getting reloaded for all files */
2385     nautilus_directory_invalidate_file_attributes (directory, file_attributes);
2386 
2387     /* Start a new directory load. */
2388     file_list_cancel (directory);
2389     directory->details->directory_loaded = FALSE;
2390 
2391     /* Start a new directory count. */
2392     nautilus_directory_invalidate_count_and_mime_list (directory);
2393 
2394     add_all_files_to_work_queue (directory);
2395     nautilus_directory_async_state_changed (directory);
2396 
2397     nautilus_profile_end (NULL);
2398 }
2399 
2400 static gboolean
monitor_includes_file(const Monitor * monitor,NautilusFile * file)2401 monitor_includes_file (const Monitor *monitor,
2402                        NautilusFile  *file)
2403 {
2404     if (monitor->file == file)
2405     {
2406         return TRUE;
2407     }
2408     /* monitor->file == NULL means monitor all files. */
2409     if (monitor->file != NULL)
2410     {
2411         return FALSE;
2412     }
2413     if (file == file->details->directory->details->as_file)
2414     {
2415         return FALSE;
2416     }
2417     return nautilus_file_should_show (file,
2418                                       monitor->monitor_hidden_files);
2419 }
2420 
2421 static gboolean
is_wanted_by_monitor(NautilusFile * file,GList * monitors,RequestType request_type_wanted)2422 is_wanted_by_monitor (NautilusFile *file,
2423                       GList        *monitors,
2424                       RequestType   request_type_wanted)
2425 {
2426     GList *node;
2427 
2428     for (node = monitors; node; node = node->next)
2429     {
2430         Monitor *monitor = node->data;
2431         if (REQUEST_WANTS_TYPE (monitor->request, request_type_wanted))
2432         {
2433             if (monitor_includes_file (monitor, file))
2434             {
2435                 return TRUE;
2436             }
2437         }
2438     }
2439 
2440     return FALSE;
2441 }
2442 
2443 static gboolean
is_needy(NautilusFile * file,FileCheck check_missing,RequestType request_type_wanted)2444 is_needy (NautilusFile *file,
2445           FileCheck     check_missing,
2446           RequestType   request_type_wanted)
2447 {
2448     NautilusDirectory *directory;
2449     GList *node;
2450     ReadyCallback *callback;
2451 
2452     if (!(*check_missing)(file))
2453     {
2454         return FALSE;
2455     }
2456 
2457     directory = file->details->directory;
2458     if (directory->details->call_when_ready_counters[request_type_wanted] > 0)
2459     {
2460         for (node = directory->details->call_when_ready_list;
2461              node != NULL; node = node->next)
2462         {
2463             callback = node->data;
2464             if (callback->active &&
2465                 REQUEST_WANTS_TYPE (callback->request, request_type_wanted))
2466             {
2467                 if (callback->file == file)
2468                 {
2469                     return TRUE;
2470                 }
2471                 if (callback->file == NULL
2472                     && file != directory->details->as_file)
2473                 {
2474                     return TRUE;
2475                 }
2476             }
2477         }
2478     }
2479 
2480     if (directory->details->monitor_counters[request_type_wanted] > 0)
2481     {
2482         GList *monitors;
2483 
2484         monitors = lookup_monitors (directory->details->monitor_table, file);
2485         if (is_wanted_by_monitor (file, monitors, request_type_wanted))
2486         {
2487             return TRUE;
2488         }
2489 
2490         monitors = lookup_all_files_monitors (directory->details->monitor_table);
2491         if (is_wanted_by_monitor (file, monitors, request_type_wanted))
2492         {
2493             return TRUE;
2494         }
2495     }
2496 
2497     return FALSE;
2498 }
2499 
2500 static void
directory_count_stop(NautilusDirectory * directory)2501 directory_count_stop (NautilusDirectory *directory)
2502 {
2503     NautilusFile *file;
2504 
2505     if (directory->details->count_in_progress != NULL)
2506     {
2507         file = directory->details->count_in_progress->count_file;
2508         if (file != NULL)
2509         {
2510             g_assert (NAUTILUS_IS_FILE (file));
2511             g_assert (file->details->directory == directory);
2512             if (is_needy (file,
2513                           should_get_directory_count_now,
2514                           REQUEST_DIRECTORY_COUNT))
2515             {
2516                 return;
2517             }
2518         }
2519 
2520         /* The count is not wanted, so stop it. */
2521         directory_count_cancel (directory);
2522     }
2523 }
2524 
2525 static guint
count_non_skipped_files(GList * list)2526 count_non_skipped_files (GList *list)
2527 {
2528     guint count;
2529     GList *node;
2530     GFileInfo *info;
2531 
2532     count = 0;
2533     for (node = list; node != NULL; node = node->next)
2534     {
2535         info = node->data;
2536         if (!should_skip_file (NULL, info))
2537         {
2538             count += 1;
2539         }
2540     }
2541     return count;
2542 }
2543 
2544 static void
count_children_done(NautilusDirectory * directory,NautilusFile * count_file,gboolean succeeded,int count)2545 count_children_done (NautilusDirectory *directory,
2546                      NautilusFile      *count_file,
2547                      gboolean           succeeded,
2548                      int                count)
2549 {
2550     g_assert (NAUTILUS_IS_FILE (count_file));
2551 
2552     count_file->details->directory_count_is_up_to_date = TRUE;
2553 
2554     /* Record either a failure or success. */
2555     if (!succeeded)
2556     {
2557         count_file->details->directory_count_failed = TRUE;
2558         count_file->details->got_directory_count = FALSE;
2559         count_file->details->directory_count = 0;
2560     }
2561     else
2562     {
2563         count_file->details->directory_count_failed = FALSE;
2564         count_file->details->got_directory_count = TRUE;
2565         count_file->details->directory_count = count;
2566     }
2567     directory->details->count_in_progress = NULL;
2568 
2569     /* Send file-changed even if count failed, so interested parties can
2570      * distinguish between unknowable and not-yet-known cases.
2571      */
2572     nautilus_file_changed (count_file);
2573 
2574     /* Start up the next one. */
2575     async_job_end (directory, "directory count");
2576     nautilus_directory_async_state_changed (directory);
2577 }
2578 
2579 static void
directory_count_state_free(DirectoryCountState * state)2580 directory_count_state_free (DirectoryCountState *state)
2581 {
2582     if (state->enumerator)
2583     {
2584         if (!g_file_enumerator_is_closed (state->enumerator))
2585         {
2586             g_file_enumerator_close_async (state->enumerator,
2587                                            0, NULL, NULL, NULL);
2588         }
2589         g_object_unref (state->enumerator);
2590     }
2591     g_object_unref (state->cancellable);
2592     nautilus_directory_unref (state->directory);
2593     g_free (state);
2594 }
2595 
2596 static void
count_more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2597 count_more_files_callback (GObject      *source_object,
2598                            GAsyncResult *res,
2599                            gpointer      user_data)
2600 {
2601     DirectoryCountState *state;
2602     NautilusDirectory *directory;
2603     GError *error;
2604     GList *files;
2605 
2606     state = user_data;
2607     directory = state->directory;
2608 
2609     if (g_cancellable_is_cancelled (state->cancellable))
2610     {
2611         /* Operation was cancelled. Bail out */
2612 
2613         async_job_end (directory, "directory count");
2614         nautilus_directory_async_state_changed (directory);
2615 
2616         directory_count_state_free (state);
2617 
2618         return;
2619     }
2620 
2621     g_assert (directory->details->count_in_progress != NULL);
2622     g_assert (directory->details->count_in_progress == state);
2623 
2624     error = NULL;
2625     files = g_file_enumerator_next_files_finish (state->enumerator,
2626                                                  res, &error);
2627 
2628     state->file_count += count_non_skipped_files (files);
2629 
2630     if (files == NULL)
2631     {
2632         count_children_done (directory, state->count_file,
2633                              TRUE, state->file_count);
2634         directory_count_state_free (state);
2635     }
2636     else
2637     {
2638         g_file_enumerator_next_files_async (state->enumerator,
2639                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2640                                             G_PRIORITY_DEFAULT,
2641                                             state->cancellable,
2642                                             count_more_files_callback,
2643                                             state);
2644     }
2645 
2646     g_list_free_full (files, g_object_unref);
2647 
2648     if (error)
2649     {
2650         g_error_free (error);
2651     }
2652 }
2653 
2654 static void
count_children_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2655 count_children_callback (GObject      *source_object,
2656                          GAsyncResult *res,
2657                          gpointer      user_data)
2658 {
2659     DirectoryCountState *state;
2660     GFileEnumerator *enumerator;
2661     NautilusDirectory *directory;
2662     GError *error;
2663 
2664     state = user_data;
2665 
2666     if (g_cancellable_is_cancelled (state->cancellable))
2667     {
2668         /* Operation was cancelled. Bail out */
2669         directory = state->directory;
2670 
2671         async_job_end (directory, "directory count");
2672         nautilus_directory_async_state_changed (directory);
2673 
2674         directory_count_state_free (state);
2675 
2676         return;
2677     }
2678 
2679     error = NULL;
2680     enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
2681                                                    res, &error);
2682 
2683     if (enumerator == NULL)
2684     {
2685         count_children_done (state->directory,
2686                              state->count_file,
2687                              FALSE, 0);
2688         g_error_free (error);
2689         directory_count_state_free (state);
2690         return;
2691     }
2692     else
2693     {
2694         state->enumerator = enumerator;
2695         g_file_enumerator_next_files_async (state->enumerator,
2696                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2697                                             G_PRIORITY_DEFAULT,
2698                                             state->cancellable,
2699                                             count_more_files_callback,
2700                                             state);
2701     }
2702 }
2703 
2704 static void
directory_count_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)2705 directory_count_start (NautilusDirectory *directory,
2706                        NautilusFile      *file,
2707                        gboolean          *doing_io)
2708 {
2709     DirectoryCountState *state;
2710     GFile *location;
2711 
2712     if (directory->details->count_in_progress != NULL)
2713     {
2714         *doing_io = TRUE;
2715         return;
2716     }
2717 
2718     if (!is_needy (file,
2719                    should_get_directory_count_now,
2720                    REQUEST_DIRECTORY_COUNT))
2721     {
2722         return;
2723     }
2724     *doing_io = TRUE;
2725 
2726     if (!nautilus_file_is_directory (file))
2727     {
2728         file->details->directory_count_is_up_to_date = TRUE;
2729         file->details->directory_count_failed = FALSE;
2730         file->details->got_directory_count = FALSE;
2731 
2732         nautilus_directory_async_state_changed (directory);
2733         return;
2734     }
2735 
2736     if (!async_job_start (directory, "directory count"))
2737     {
2738         return;
2739     }
2740 
2741     /* Start counting. */
2742     state = g_new0 (DirectoryCountState, 1);
2743     state->count_file = file;
2744     state->directory = nautilus_directory_ref (directory);
2745     state->cancellable = g_cancellable_new ();
2746 
2747     directory->details->count_in_progress = state;
2748 
2749     location = nautilus_file_get_location (file);
2750 
2751     {
2752         g_autofree char *uri = NULL;
2753         uri = g_file_get_uri (location);
2754         DEBUG ("load_directory called to get shallow file count for %s", uri);
2755     }
2756 
2757     g_file_enumerate_children_async (location,
2758                                      G_FILE_ATTRIBUTE_STANDARD_NAME ","
2759                                      G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
2760                                      G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
2761                                      G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,     /* flags */
2762                                      G_PRIORITY_DEFAULT,     /* prio */
2763                                      state->cancellable,
2764                                      count_children_callback,
2765                                      state);
2766     g_object_unref (location);
2767 }
2768 
2769 static inline gboolean
seen_inode(DeepCountState * state,GFileInfo * info)2770 seen_inode (DeepCountState *state,
2771             GFileInfo      *info)
2772 {
2773     guint64 inode, inode2;
2774     guint i;
2775 
2776     inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
2777 
2778     if (inode != 0)
2779     {
2780         for (i = 0; i < state->seen_deep_count_inodes->len; i++)
2781         {
2782             inode2 = g_array_index (state->seen_deep_count_inodes, guint64, i);
2783             if (inode == inode2)
2784             {
2785                 return TRUE;
2786             }
2787         }
2788     }
2789 
2790     return FALSE;
2791 }
2792 
2793 static inline void
mark_inode_as_seen(DeepCountState * state,GFileInfo * info)2794 mark_inode_as_seen (DeepCountState *state,
2795                     GFileInfo      *info)
2796 {
2797     guint64 inode;
2798 
2799     inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
2800     if (inode != 0)
2801     {
2802         g_array_append_val (state->seen_deep_count_inodes, inode);
2803     }
2804 }
2805 
2806 static void
deep_count_one(DeepCountState * state,GFileInfo * info)2807 deep_count_one (DeepCountState *state,
2808                 GFileInfo      *info)
2809 {
2810     NautilusFile *file;
2811     GFile *subdir;
2812     gboolean is_seen_inode;
2813     const char *fs_id;
2814 
2815     if (should_skip_file (NULL, info))
2816     {
2817         return;
2818     }
2819 
2820     is_seen_inode = seen_inode (state, info);
2821     if (!is_seen_inode)
2822     {
2823         mark_inode_as_seen (state, info);
2824     }
2825 
2826     file = state->directory->details->deep_count_file;
2827 
2828     if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
2829     {
2830         /* Count the directory. */
2831         file->details->deep_directory_count += 1;
2832 
2833         /* Record the fact that we have to descend into this directory. */
2834         fs_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
2835         if (g_strcmp0 (fs_id, state->fs_id) == 0)
2836         {
2837             /* only if it is on the same filesystem */
2838             subdir = g_file_get_child (state->deep_count_location, g_file_info_get_name (info));
2839             state->deep_count_subdirectories = g_list_prepend
2840                                                    (state->deep_count_subdirectories, subdir);
2841         }
2842     }
2843     else
2844     {
2845         /* Even non-regular files count as files. */
2846         file->details->deep_file_count += 1;
2847     }
2848 
2849     /* Count the size. */
2850     if (!is_seen_inode && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
2851     {
2852         file->details->deep_size += g_file_info_get_size (info);
2853     }
2854 }
2855 
2856 static void
deep_count_state_free(DeepCountState * state)2857 deep_count_state_free (DeepCountState *state)
2858 {
2859     if (state->enumerator)
2860     {
2861         if (!g_file_enumerator_is_closed (state->enumerator))
2862         {
2863             g_file_enumerator_close_async (state->enumerator,
2864                                            0, NULL, NULL, NULL);
2865         }
2866         g_object_unref (state->enumerator);
2867     }
2868     g_object_unref (state->cancellable);
2869     if (state->deep_count_location)
2870     {
2871         g_object_unref (state->deep_count_location);
2872     }
2873     g_list_free_full (state->deep_count_subdirectories, g_object_unref);
2874     g_array_free (state->seen_deep_count_inodes, TRUE);
2875     g_free (state->fs_id);
2876     g_free (state);
2877 }
2878 
2879 static void
deep_count_next_dir(DeepCountState * state)2880 deep_count_next_dir (DeepCountState *state)
2881 {
2882     GFile *location;
2883     NautilusFile *file;
2884     NautilusDirectory *directory;
2885     gboolean done;
2886 
2887     directory = state->directory;
2888 
2889     g_object_unref (state->deep_count_location);
2890     state->deep_count_location = NULL;
2891 
2892     done = FALSE;
2893     file = directory->details->deep_count_file;
2894 
2895     if (state->deep_count_subdirectories != NULL)
2896     {
2897         /* Work on a new directory. */
2898         location = state->deep_count_subdirectories->data;
2899         state->deep_count_subdirectories = g_list_remove
2900                                                (state->deep_count_subdirectories, location);
2901         deep_count_load (state, location);
2902         g_object_unref (location);
2903     }
2904     else
2905     {
2906         file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
2907         directory->details->deep_count_file = NULL;
2908         directory->details->deep_count_in_progress = NULL;
2909         deep_count_state_free (state);
2910         done = TRUE;
2911     }
2912 
2913     nautilus_file_updated_deep_count_in_progress (file);
2914 
2915     if (done)
2916     {
2917         nautilus_file_changed (file);
2918         async_job_end (directory, "deep count");
2919         nautilus_directory_async_state_changed (directory);
2920     }
2921 }
2922 
2923 static void
deep_count_more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2924 deep_count_more_files_callback (GObject      *source_object,
2925                                 GAsyncResult *res,
2926                                 gpointer      user_data)
2927 {
2928     DeepCountState *state;
2929     NautilusDirectory *directory;
2930     GList *files, *l;
2931     GFileInfo *info;
2932 
2933     state = user_data;
2934 
2935     if (state->directory == NULL)
2936     {
2937         /* Operation was cancelled. Bail out */
2938         deep_count_state_free (state);
2939         return;
2940     }
2941 
2942     directory = nautilus_directory_ref (state->directory);
2943 
2944     g_assert (directory->details->deep_count_in_progress != NULL);
2945     g_assert (directory->details->deep_count_in_progress == state);
2946 
2947     files = g_file_enumerator_next_files_finish (state->enumerator,
2948                                                  res, NULL);
2949 
2950     for (l = files; l != NULL; l = l->next)
2951     {
2952         info = l->data;
2953         deep_count_one (state, info);
2954         g_object_unref (info);
2955     }
2956 
2957     if (files == NULL)
2958     {
2959         g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
2960         g_object_unref (state->enumerator);
2961         state->enumerator = NULL;
2962 
2963         deep_count_next_dir (state);
2964     }
2965     else
2966     {
2967         g_file_enumerator_next_files_async (state->enumerator,
2968                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2969                                             G_PRIORITY_LOW,
2970                                             state->cancellable,
2971                                             deep_count_more_files_callback,
2972                                             state);
2973     }
2974 
2975     g_list_free (files);
2976 
2977     nautilus_directory_unref (directory);
2978 }
2979 
2980 static void
deep_count_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2981 deep_count_callback (GObject      *source_object,
2982                      GAsyncResult *res,
2983                      gpointer      user_data)
2984 {
2985     DeepCountState *state;
2986     GFileEnumerator *enumerator;
2987     NautilusFile *file;
2988 
2989     state = user_data;
2990 
2991     if (state->directory == NULL)
2992     {
2993         /* Operation was cancelled. Bail out */
2994         deep_count_state_free (state);
2995         return;
2996     }
2997 
2998     file = state->directory->details->deep_count_file;
2999 
3000     enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
3001 
3002     if (enumerator == NULL)
3003     {
3004         file->details->deep_unreadable_count += 1;
3005 
3006         deep_count_next_dir (state);
3007     }
3008     else
3009     {
3010         state->enumerator = enumerator;
3011         g_file_enumerator_next_files_async (state->enumerator,
3012                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3013                                             G_PRIORITY_LOW,
3014                                             state->cancellable,
3015                                             deep_count_more_files_callback,
3016                                             state);
3017     }
3018 }
3019 
3020 
3021 static void
deep_count_load(DeepCountState * state,GFile * location)3022 deep_count_load (DeepCountState *state,
3023                  GFile          *location)
3024 {
3025     state->deep_count_location = g_object_ref (location);
3026 
3027     DEBUG ("load_directory called to get deep file count for %p", location);
3028     g_file_enumerate_children_async (state->deep_count_location,
3029                                      G_FILE_ATTRIBUTE_STANDARD_NAME ","
3030                                      G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3031                                      G_FILE_ATTRIBUTE_STANDARD_SIZE ","
3032                                      G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
3033                                      G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP ","
3034                                      G_FILE_ATTRIBUTE_ID_FILESYSTEM ","
3035                                      G_FILE_ATTRIBUTE_UNIX_INODE,
3036                                      G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,     /* flags */
3037                                      G_PRIORITY_LOW,     /* prio */
3038                                      state->cancellable,
3039                                      deep_count_callback,
3040                                      state);
3041 }
3042 
3043 static void
deep_count_stop(NautilusDirectory * directory)3044 deep_count_stop (NautilusDirectory *directory)
3045 {
3046     NautilusFile *file;
3047 
3048     if (directory->details->deep_count_in_progress != NULL)
3049     {
3050         file = directory->details->deep_count_file;
3051         if (file != NULL)
3052         {
3053             g_assert (NAUTILUS_IS_FILE (file));
3054             g_assert (file->details->directory == directory);
3055             if (is_needy (file,
3056                           lacks_deep_count,
3057                           REQUEST_DEEP_COUNT))
3058             {
3059                 return;
3060             }
3061         }
3062 
3063         /* The count is not wanted, so stop it. */
3064         deep_count_cancel (directory);
3065     }
3066 }
3067 
3068 static void
deep_count_got_info(GObject * source_object,GAsyncResult * res,gpointer user_data)3069 deep_count_got_info (GObject      *source_object,
3070                      GAsyncResult *res,
3071                      gpointer      user_data)
3072 {
3073     GFileInfo *info;
3074     const char *id;
3075     GFile *file = (GFile *) source_object;
3076     DeepCountState *state = (DeepCountState *) user_data;
3077 
3078     info = g_file_query_info_finish (file, res, NULL);
3079     if (info != NULL)
3080     {
3081         id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
3082         state->fs_id = g_strdup (id);
3083         g_object_unref (info);
3084     }
3085     deep_count_load (state, file);
3086 }
3087 
3088 static void
deep_count_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)3089 deep_count_start (NautilusDirectory *directory,
3090                   NautilusFile      *file,
3091                   gboolean          *doing_io)
3092 {
3093     GFile *location;
3094     DeepCountState *state;
3095 
3096     if (directory->details->deep_count_in_progress != NULL)
3097     {
3098         *doing_io = TRUE;
3099         return;
3100     }
3101 
3102     if (!is_needy (file,
3103                    lacks_deep_count,
3104                    REQUEST_DEEP_COUNT))
3105     {
3106         return;
3107     }
3108     *doing_io = TRUE;
3109 
3110     if (!nautilus_file_is_directory (file))
3111     {
3112         file->details->deep_counts_status = NAUTILUS_REQUEST_DONE;
3113 
3114         nautilus_directory_async_state_changed (directory);
3115         return;
3116     }
3117 
3118     if (!async_job_start (directory, "deep count"))
3119     {
3120         return;
3121     }
3122 
3123     /* Start counting. */
3124     file->details->deep_counts_status = NAUTILUS_REQUEST_IN_PROGRESS;
3125     file->details->deep_directory_count = 0;
3126     file->details->deep_file_count = 0;
3127     file->details->deep_unreadable_count = 0;
3128     file->details->deep_size = 0;
3129     directory->details->deep_count_file = file;
3130 
3131     state = g_new0 (DeepCountState, 1);
3132     state->directory = directory;
3133     state->cancellable = g_cancellable_new ();
3134     state->seen_deep_count_inodes = g_array_new (FALSE, TRUE, sizeof (guint64));
3135     state->fs_id = NULL;
3136 
3137     directory->details->deep_count_in_progress = state;
3138 
3139     location = nautilus_file_get_location (file);
3140     g_file_query_info_async (location,
3141                              G_FILE_ATTRIBUTE_ID_FILESYSTEM,
3142                              G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3143                              G_PRIORITY_DEFAULT,
3144                              NULL,
3145                              deep_count_got_info,
3146                              state);
3147     g_object_unref (location);
3148 }
3149 
3150 static void
mime_list_stop(NautilusDirectory * directory)3151 mime_list_stop (NautilusDirectory *directory)
3152 {
3153     NautilusFile *file;
3154 
3155     if (directory->details->mime_list_in_progress != NULL)
3156     {
3157         file = directory->details->mime_list_in_progress->mime_list_file;
3158         if (file != NULL)
3159         {
3160             g_assert (NAUTILUS_IS_FILE (file));
3161             g_assert (file->details->directory == directory);
3162             if (is_needy (file,
3163                           should_get_mime_list,
3164                           REQUEST_MIME_LIST))
3165             {
3166                 return;
3167             }
3168         }
3169 
3170         /* The count is not wanted, so stop it. */
3171         mime_list_cancel (directory);
3172     }
3173 }
3174 
3175 static void
mime_list_state_free(MimeListState * state)3176 mime_list_state_free (MimeListState *state)
3177 {
3178     if (state->enumerator)
3179     {
3180         if (!g_file_enumerator_is_closed (state->enumerator))
3181         {
3182             g_file_enumerator_close_async (state->enumerator,
3183                                            0, NULL, NULL, NULL);
3184         }
3185         g_object_unref (state->enumerator);
3186     }
3187     g_object_unref (state->cancellable);
3188     istr_set_destroy (state->mime_list_hash);
3189     nautilus_directory_unref (state->directory);
3190     g_free (state);
3191 }
3192 
3193 
3194 static void
mime_list_done(MimeListState * state,gboolean success)3195 mime_list_done (MimeListState *state,
3196                 gboolean       success)
3197 {
3198     NautilusFile *file;
3199     NautilusDirectory *directory;
3200 
3201     directory = state->directory;
3202     g_assert (directory != NULL);
3203 
3204     file = state->mime_list_file;
3205 
3206     file->details->mime_list_is_up_to_date = TRUE;
3207     g_list_free_full (file->details->mime_list, g_free);
3208     if (success)
3209     {
3210         file->details->mime_list_failed = TRUE;
3211         file->details->mime_list = NULL;
3212     }
3213     else
3214     {
3215         file->details->got_mime_list = TRUE;
3216         file->details->mime_list = istr_set_get_as_list (state->mime_list_hash);
3217     }
3218     directory->details->mime_list_in_progress = NULL;
3219 
3220     /* Send file-changed even if getting the item type list
3221      * failed, so interested parties can distinguish between
3222      * unknowable and not-yet-known cases.
3223      */
3224     nautilus_file_changed (file);
3225 
3226     /* Start up the next one. */
3227     async_job_end (directory, "MIME list");
3228     nautilus_directory_async_state_changed (directory);
3229 }
3230 
3231 static void
mime_list_one(MimeListState * state,GFileInfo * info)3232 mime_list_one (MimeListState *state,
3233                GFileInfo     *info)
3234 {
3235     const char *mime_type;
3236 
3237     if (should_skip_file (NULL, info))
3238     {
3239         g_object_unref (info);
3240         return;
3241     }
3242 
3243     mime_type = g_file_info_get_content_type (info);
3244     if (mime_type != NULL)
3245     {
3246         istr_set_insert (state->mime_list_hash, mime_type);
3247     }
3248 }
3249 
3250 static void
mime_list_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3251 mime_list_callback (GObject      *source_object,
3252                     GAsyncResult *res,
3253                     gpointer      user_data)
3254 {
3255     MimeListState *state;
3256     NautilusDirectory *directory;
3257     GError *error;
3258     GList *files, *l;
3259     GFileInfo *info;
3260 
3261     state = user_data;
3262     directory = state->directory;
3263 
3264     if (g_cancellable_is_cancelled (state->cancellable))
3265     {
3266         /* Operation was cancelled. Bail out */
3267         directory->details->mime_list_in_progress = NULL;
3268 
3269         async_job_end (directory, "MIME list");
3270         nautilus_directory_async_state_changed (directory);
3271 
3272         mime_list_state_free (state);
3273 
3274         return;
3275     }
3276 
3277     g_assert (directory->details->mime_list_in_progress != NULL);
3278     g_assert (directory->details->mime_list_in_progress == state);
3279 
3280     error = NULL;
3281     files = g_file_enumerator_next_files_finish (state->enumerator,
3282                                                  res, &error);
3283 
3284     for (l = files; l != NULL; l = l->next)
3285     {
3286         info = l->data;
3287         mime_list_one (state, info);
3288         g_object_unref (info);
3289     }
3290 
3291     if (files == NULL)
3292     {
3293         mime_list_done (state, error != NULL);
3294         mime_list_state_free (state);
3295     }
3296     else
3297     {
3298         g_file_enumerator_next_files_async (state->enumerator,
3299                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3300                                             G_PRIORITY_DEFAULT,
3301                                             state->cancellable,
3302                                             mime_list_callback,
3303                                             state);
3304     }
3305 
3306     g_list_free (files);
3307 
3308     if (error)
3309     {
3310         g_error_free (error);
3311     }
3312 }
3313 
3314 static void
list_mime_enum_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3315 list_mime_enum_callback (GObject      *source_object,
3316                          GAsyncResult *res,
3317                          gpointer      user_data)
3318 {
3319     MimeListState *state;
3320     GFileEnumerator *enumerator;
3321     NautilusDirectory *directory;
3322     GError *error;
3323 
3324     state = user_data;
3325 
3326     if (g_cancellable_is_cancelled (state->cancellable))
3327     {
3328         /* Operation was cancelled. Bail out */
3329         directory = state->directory;
3330         directory->details->mime_list_in_progress = NULL;
3331 
3332         async_job_end (directory, "MIME list");
3333         nautilus_directory_async_state_changed (directory);
3334 
3335         mime_list_state_free (state);
3336 
3337         return;
3338     }
3339 
3340     error = NULL;
3341     enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
3342                                                    res, &error);
3343 
3344     if (enumerator == NULL)
3345     {
3346         mime_list_done (state, FALSE);
3347         g_error_free (error);
3348         mime_list_state_free (state);
3349         return;
3350     }
3351     else
3352     {
3353         state->enumerator = enumerator;
3354         g_file_enumerator_next_files_async (state->enumerator,
3355                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3356                                             G_PRIORITY_DEFAULT,
3357                                             state->cancellable,
3358                                             mime_list_callback,
3359                                             state);
3360     }
3361 }
3362 
3363 static void
mime_list_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)3364 mime_list_start (NautilusDirectory *directory,
3365                  NautilusFile      *file,
3366                  gboolean          *doing_io)
3367 {
3368     MimeListState *state;
3369     GFile *location;
3370 
3371     mime_list_stop (directory);
3372 
3373     if (directory->details->mime_list_in_progress != NULL)
3374     {
3375         *doing_io = TRUE;
3376         return;
3377     }
3378 
3379     /* Figure out which file to get a mime list for. */
3380     if (!is_needy (file,
3381                    should_get_mime_list,
3382                    REQUEST_MIME_LIST))
3383     {
3384         return;
3385     }
3386     *doing_io = TRUE;
3387 
3388     if (!nautilus_file_is_directory (file))
3389     {
3390         g_list_free (file->details->mime_list);
3391         file->details->mime_list_failed = FALSE;
3392         file->details->got_mime_list = FALSE;
3393         file->details->mime_list_is_up_to_date = TRUE;
3394 
3395         nautilus_directory_async_state_changed (directory);
3396         return;
3397     }
3398 
3399     if (!async_job_start (directory, "MIME list"))
3400     {
3401         return;
3402     }
3403 
3404 
3405     state = g_new0 (MimeListState, 1);
3406     state->mime_list_file = file;
3407     state->directory = nautilus_directory_ref (directory);
3408     state->cancellable = g_cancellable_new ();
3409     state->mime_list_hash = istr_set_new ();
3410 
3411     directory->details->mime_list_in_progress = state;
3412 
3413     location = nautilus_file_get_location (file);
3414 
3415     {
3416         g_autofree char *uri = NULL;
3417         uri = g_file_get_uri (location);
3418         DEBUG ("load_directory called to get MIME list of %s", uri);
3419     }
3420 
3421     g_file_enumerate_children_async (location,
3422                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3423                                      0,     /* flags */
3424                                      G_PRIORITY_LOW,     /* prio */
3425                                      state->cancellable,
3426                                      list_mime_enum_callback,
3427                                      state);
3428     g_object_unref (location);
3429 }
3430 
3431 static void
get_info_state_free(GetInfoState * state)3432 get_info_state_free (GetInfoState *state)
3433 {
3434     g_object_unref (state->cancellable);
3435     g_free (state);
3436 }
3437 
3438 static void
query_info_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3439 query_info_callback (GObject      *source_object,
3440                      GAsyncResult *res,
3441                      gpointer      user_data)
3442 {
3443     NautilusDirectory *directory;
3444     NautilusFile *get_info_file;
3445     GFileInfo *info;
3446     GetInfoState *state;
3447     GError *error;
3448 
3449     state = user_data;
3450 
3451     if (state->directory == NULL)
3452     {
3453         /* Operation was cancelled. Bail out */
3454         get_info_state_free (state);
3455         return;
3456     }
3457 
3458     directory = nautilus_directory_ref (state->directory);
3459 
3460     get_info_file = directory->details->get_info_file;
3461     g_assert (NAUTILUS_IS_FILE (get_info_file));
3462 
3463     directory->details->get_info_file = NULL;
3464     directory->details->get_info_in_progress = NULL;
3465 
3466     /* ref here because we might be removing the last ref when we
3467      * mark the file gone below, but we need to keep a ref at
3468      * least long enough to send the change notification.
3469      */
3470     nautilus_file_ref (get_info_file);
3471 
3472     error = NULL;
3473     info = g_file_query_info_finish (G_FILE (source_object), res, &error);
3474 
3475     if (info == NULL)
3476     {
3477         if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)
3478         {
3479             /* mark file as gone */
3480             nautilus_file_mark_gone (get_info_file);
3481         }
3482         get_info_file->details->file_info_is_up_to_date = TRUE;
3483         nautilus_file_clear_info (get_info_file);
3484         get_info_file->details->get_info_failed = TRUE;
3485         get_info_file->details->get_info_error = error;
3486     }
3487     else
3488     {
3489         nautilus_file_update_info (get_info_file, info);
3490         g_object_unref (info);
3491     }
3492 
3493     nautilus_file_changed (get_info_file);
3494     nautilus_file_unref (get_info_file);
3495 
3496     async_job_end (directory, "file info");
3497     nautilus_directory_async_state_changed (directory);
3498 
3499     nautilus_directory_unref (directory);
3500 
3501     get_info_state_free (state);
3502 }
3503 
3504 static void
file_info_stop(NautilusDirectory * directory)3505 file_info_stop (NautilusDirectory *directory)
3506 {
3507     NautilusFile *file;
3508 
3509     if (directory->details->get_info_in_progress != NULL)
3510     {
3511         file = directory->details->get_info_file;
3512         if (file != NULL)
3513         {
3514             g_assert (NAUTILUS_IS_FILE (file));
3515             g_assert (file->details->directory == directory);
3516             if (is_needy (file, lacks_info, REQUEST_FILE_INFO))
3517             {
3518                 return;
3519             }
3520         }
3521 
3522         /* The info is not wanted, so stop it. */
3523         file_info_cancel (directory);
3524     }
3525 }
3526 
3527 static void
file_info_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)3528 file_info_start (NautilusDirectory *directory,
3529                  NautilusFile      *file,
3530                  gboolean          *doing_io)
3531 {
3532     GFile *location;
3533     GetInfoState *state;
3534 
3535     file_info_stop (directory);
3536 
3537     if (directory->details->get_info_in_progress != NULL)
3538     {
3539         *doing_io = TRUE;
3540         return;
3541     }
3542 
3543     if (!is_needy (file, lacks_info, REQUEST_FILE_INFO))
3544     {
3545         return;
3546     }
3547     *doing_io = TRUE;
3548 
3549     if (!async_job_start (directory, "file info"))
3550     {
3551         return;
3552     }
3553 
3554     directory->details->get_info_file = file;
3555     file->details->get_info_failed = FALSE;
3556     if (file->details->get_info_error)
3557     {
3558         g_error_free (file->details->get_info_error);
3559         file->details->get_info_error = NULL;
3560     }
3561 
3562     state = g_new (GetInfoState, 1);
3563     state->directory = directory;
3564     state->cancellable = g_cancellable_new ();
3565 
3566     directory->details->get_info_in_progress = state;
3567 
3568     location = nautilus_file_get_location (file);
3569     g_file_query_info_async (location,
3570                              NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
3571                              0,
3572                              G_PRIORITY_DEFAULT,
3573                              state->cancellable, query_info_callback, state);
3574     g_object_unref (location);
3575 }
3576 
3577 static void
thumbnail_done(NautilusDirectory * directory,NautilusFile * file,GdkPixbuf * pixbuf)3578 thumbnail_done (NautilusDirectory *directory,
3579                 NautilusFile      *file,
3580                 GdkPixbuf         *pixbuf)
3581 {
3582     const char *thumb_mtime_str;
3583     time_t thumb_mtime = 0;
3584 
3585     file->details->thumbnail_is_up_to_date = TRUE;
3586     if (file->details->thumbnail)
3587     {
3588         g_object_unref (file->details->thumbnail);
3589         file->details->thumbnail = NULL;
3590     }
3591     if (file->details->scaled_thumbnail)
3592     {
3593         g_object_unref (file->details->scaled_thumbnail);
3594         file->details->scaled_thumbnail = NULL;
3595     }
3596 
3597     if (pixbuf)
3598     {
3599         thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
3600         if (thumb_mtime_str)
3601         {
3602             thumb_mtime = atol (thumb_mtime_str);
3603         }
3604 
3605         if (thumb_mtime == 0 ||
3606             thumb_mtime == file->details->mtime)
3607         {
3608             file->details->thumbnail = g_object_ref (pixbuf);
3609             file->details->thumbnail_mtime = thumb_mtime;
3610         }
3611         else
3612         {
3613             g_free (file->details->thumbnail_path);
3614             file->details->thumbnail_path = NULL;
3615         }
3616     }
3617 
3618     nautilus_directory_async_state_changed (directory);
3619 }
3620 
3621 static void
thumbnail_stop(NautilusDirectory * directory)3622 thumbnail_stop (NautilusDirectory *directory)
3623 {
3624     NautilusFile *file;
3625 
3626     if (directory->details->thumbnail_state != NULL)
3627     {
3628         file = directory->details->thumbnail_state->file;
3629 
3630         if (file != NULL)
3631         {
3632             g_assert (NAUTILUS_IS_FILE (file));
3633             g_assert (file->details->directory == directory);
3634             if (is_needy (file,
3635                           lacks_thumbnail,
3636                           REQUEST_THUMBNAIL))
3637             {
3638                 return;
3639             }
3640         }
3641 
3642         /* The link info is not wanted, so stop it. */
3643         thumbnail_cancel (directory);
3644     }
3645 }
3646 
3647 static void
thumbnail_got_pixbuf(NautilusDirectory * directory,NautilusFile * file,GdkPixbuf * pixbuf)3648 thumbnail_got_pixbuf (NautilusDirectory *directory,
3649                       NautilusFile      *file,
3650                       GdkPixbuf         *pixbuf)
3651 {
3652     nautilus_directory_ref (directory);
3653 
3654     nautilus_file_ref (file);
3655     thumbnail_done (directory, file, pixbuf);
3656     nautilus_file_changed (file);
3657     nautilus_file_unref (file);
3658 
3659     if (pixbuf)
3660     {
3661         g_object_unref (pixbuf);
3662     }
3663 
3664     nautilus_directory_unref (directory);
3665 }
3666 
3667 static void
thumbnail_state_free(ThumbnailState * state)3668 thumbnail_state_free (ThumbnailState *state)
3669 {
3670     g_object_unref (state->cancellable);
3671     g_free (state);
3672 }
3673 
3674 /* scale very large images down to the max. size we need */
3675 static void
thumbnail_loader_size_prepared(GdkPixbufLoader * loader,int width,int height,gpointer user_data)3676 thumbnail_loader_size_prepared (GdkPixbufLoader *loader,
3677                                 int              width,
3678                                 int              height,
3679                                 gpointer         user_data)
3680 {
3681     int max_thumbnail_size;
3682     double aspect_ratio;
3683 
3684     aspect_ratio = ((double) width) / height;
3685 
3686     /* cf. nautilus_file_get_icon() */
3687     max_thumbnail_size = NAUTILUS_CANVAS_ICON_SIZE_LARGEST * NAUTILUS_CANVAS_ICON_SIZE_STANDARD / NAUTILUS_CANVAS_ICON_SIZE_SMALL;
3688     if (MAX (width, height) > max_thumbnail_size)
3689     {
3690         if (width > height)
3691         {
3692             width = max_thumbnail_size;
3693             height = width / aspect_ratio;
3694         }
3695         else
3696         {
3697             height = max_thumbnail_size;
3698             width = height * aspect_ratio;
3699         }
3700 
3701         gdk_pixbuf_loader_set_size (loader, width, height);
3702     }
3703 }
3704 
3705 static GdkPixbuf *
get_pixbuf_for_content(goffset file_len,char * file_contents)3706 get_pixbuf_for_content (goffset  file_len,
3707                         char    *file_contents)
3708 {
3709     gboolean res;
3710     GdkPixbuf *pixbuf, *pixbuf2;
3711     GdkPixbufLoader *loader;
3712     gsize chunk_len;
3713     pixbuf = NULL;
3714 
3715     loader = gdk_pixbuf_loader_new ();
3716     g_signal_connect (loader, "size-prepared",
3717                       G_CALLBACK (thumbnail_loader_size_prepared),
3718                       NULL);
3719 
3720     /* For some reason we have to write in chunks, or gdk-pixbuf fails */
3721     res = TRUE;
3722     while (res && file_len > 0)
3723     {
3724         chunk_len = file_len;
3725         res = gdk_pixbuf_loader_write (loader, (guchar *) file_contents, chunk_len, NULL);
3726         file_contents += chunk_len;
3727         file_len -= chunk_len;
3728     }
3729     if (res)
3730     {
3731         res = gdk_pixbuf_loader_close (loader, NULL);
3732     }
3733     if (res)
3734     {
3735         pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
3736     }
3737     g_object_unref (G_OBJECT (loader));
3738 
3739     if (pixbuf)
3740     {
3741         pixbuf2 = gdk_pixbuf_apply_embedded_orientation (pixbuf);
3742         g_object_unref (pixbuf);
3743         pixbuf = pixbuf2;
3744     }
3745     return pixbuf;
3746 }
3747 
3748 
3749 static void
thumbnail_read_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3750 thumbnail_read_callback (GObject      *source_object,
3751                          GAsyncResult *res,
3752                          gpointer      user_data)
3753 {
3754     ThumbnailState *state;
3755     gsize file_size;
3756     char *file_contents;
3757     gboolean result;
3758     NautilusDirectory *directory;
3759     GdkPixbuf *pixbuf;
3760 
3761     state = user_data;
3762 
3763     if (state->directory == NULL)
3764     {
3765         /* Operation was cancelled. Bail out */
3766         thumbnail_state_free (state);
3767         return;
3768     }
3769 
3770     directory = nautilus_directory_ref (state->directory);
3771 
3772     result = g_file_load_contents_finish (G_FILE (source_object),
3773                                           res,
3774                                           &file_contents, &file_size,
3775                                           NULL, NULL);
3776 
3777     pixbuf = NULL;
3778     if (result)
3779     {
3780         pixbuf = get_pixbuf_for_content (file_size, file_contents);
3781         g_free (file_contents);
3782     }
3783 
3784     state->directory->details->thumbnail_state = NULL;
3785     async_job_end (state->directory, "thumbnail");
3786 
3787     thumbnail_got_pixbuf (state->directory, state->file, pixbuf);
3788 
3789     thumbnail_state_free (state);
3790 
3791     nautilus_directory_unref (directory);
3792 }
3793 
3794 static void
thumbnail_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)3795 thumbnail_start (NautilusDirectory *directory,
3796                  NautilusFile      *file,
3797                  gboolean          *doing_io)
3798 {
3799     GFile *location;
3800     ThumbnailState *state;
3801 
3802     if (directory->details->thumbnail_state != NULL)
3803     {
3804         *doing_io = TRUE;
3805         return;
3806     }
3807 
3808     if (!is_needy (file,
3809                    lacks_thumbnail,
3810                    REQUEST_THUMBNAIL))
3811     {
3812         return;
3813     }
3814     *doing_io = TRUE;
3815 
3816     if (!async_job_start (directory, "thumbnail"))
3817     {
3818         return;
3819     }
3820 
3821     state = g_new0 (ThumbnailState, 1);
3822     state->directory = directory;
3823     state->file = file;
3824     state->cancellable = g_cancellable_new ();
3825 
3826     location = g_file_new_for_path (file->details->thumbnail_path);
3827 
3828     directory->details->thumbnail_state = state;
3829 
3830     g_file_load_contents_async (location,
3831                                 state->cancellable,
3832                                 thumbnail_read_callback,
3833                                 state);
3834     g_object_unref (location);
3835 }
3836 
3837 static void
mount_stop(NautilusDirectory * directory)3838 mount_stop (NautilusDirectory *directory)
3839 {
3840     NautilusFile *file;
3841 
3842     if (directory->details->mount_state != NULL)
3843     {
3844         file = directory->details->mount_state->file;
3845 
3846         if (file != NULL)
3847         {
3848             g_assert (NAUTILUS_IS_FILE (file));
3849             g_assert (file->details->directory == directory);
3850             if (is_needy (file,
3851                           lacks_mount,
3852                           REQUEST_MOUNT))
3853             {
3854                 return;
3855             }
3856         }
3857 
3858         /* The link info is not wanted, so stop it. */
3859         mount_cancel (directory);
3860     }
3861 }
3862 
3863 static void
mount_state_free(MountState * state)3864 mount_state_free (MountState *state)
3865 {
3866     g_object_unref (state->cancellable);
3867     g_free (state);
3868 }
3869 
3870 static void
got_mount(MountState * state,GMount * mount)3871 got_mount (MountState *state,
3872            GMount     *mount)
3873 {
3874     NautilusDirectory *directory;
3875     NautilusFile *file;
3876 
3877     directory = nautilus_directory_ref (state->directory);
3878 
3879     state->directory->details->mount_state = NULL;
3880     async_job_end (state->directory, "mount");
3881 
3882     file = nautilus_file_ref (state->file);
3883 
3884     file->details->mount_is_up_to_date = TRUE;
3885     nautilus_file_set_mount (file, mount);
3886 
3887     nautilus_directory_async_state_changed (directory);
3888     nautilus_file_changed (file);
3889 
3890     nautilus_file_unref (file);
3891 
3892     nautilus_directory_unref (directory);
3893 
3894     mount_state_free (state);
3895 }
3896 
3897 static void
find_enclosing_mount_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3898 find_enclosing_mount_callback (GObject      *source_object,
3899                                GAsyncResult *res,
3900                                gpointer      user_data)
3901 {
3902     GMount *mount;
3903     MountState *state;
3904     GFile *location, *root;
3905 
3906     state = user_data;
3907     if (state->directory == NULL)
3908     {
3909         /* Operation was cancelled. Bail out */
3910         mount_state_free (state);
3911         return;
3912     }
3913 
3914     mount = g_file_find_enclosing_mount_finish (G_FILE (source_object),
3915                                                 res, NULL);
3916 
3917     if (mount)
3918     {
3919         root = g_mount_get_root (mount);
3920         location = nautilus_file_get_location (state->file);
3921         if (!g_file_equal (location, root))
3922         {
3923             g_object_unref (mount);
3924             mount = NULL;
3925         }
3926         g_object_unref (root);
3927         g_object_unref (location);
3928     }
3929 
3930     got_mount (state, mount);
3931 
3932     if (mount)
3933     {
3934         g_object_unref (mount);
3935     }
3936 }
3937 
3938 static GMount *
get_mount_at(GFile * target)3939 get_mount_at (GFile *target)
3940 {
3941     GVolumeMonitor *monitor;
3942     GFile *root;
3943     GList *mounts, *l;
3944     GMount *found;
3945 
3946     monitor = g_volume_monitor_get ();
3947     mounts = g_volume_monitor_get_mounts (monitor);
3948 
3949     found = NULL;
3950     for (l = mounts; l != NULL; l = l->next)
3951     {
3952         GMount *mount = G_MOUNT (l->data);
3953 
3954         if (g_mount_is_shadowed (mount))
3955         {
3956             continue;
3957         }
3958 
3959         root = g_mount_get_root (mount);
3960 
3961         if (g_file_equal (target, root))
3962         {
3963             found = g_object_ref (mount);
3964             break;
3965         }
3966 
3967         g_object_unref (root);
3968     }
3969 
3970     g_list_free_full (mounts, g_object_unref);
3971 
3972     g_object_unref (monitor);
3973 
3974     return found;
3975 }
3976 
3977 static void
mount_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)3978 mount_start (NautilusDirectory *directory,
3979              NautilusFile      *file,
3980              gboolean          *doing_io)
3981 {
3982     GFile *location;
3983     MountState *state;
3984 
3985     if (directory->details->mount_state != NULL)
3986     {
3987         *doing_io = TRUE;
3988         return;
3989     }
3990 
3991     if (!is_needy (file,
3992                    lacks_mount,
3993                    REQUEST_MOUNT))
3994     {
3995         return;
3996     }
3997     *doing_io = TRUE;
3998 
3999     if (!async_job_start (directory, "mount"))
4000     {
4001         return;
4002     }
4003 
4004     state = g_new0 (MountState, 1);
4005     state->directory = directory;
4006     state->file = file;
4007     state->cancellable = g_cancellable_new ();
4008 
4009     location = nautilus_file_get_location (file);
4010 
4011     directory->details->mount_state = state;
4012 
4013     if (file->details->type == G_FILE_TYPE_MOUNTABLE)
4014     {
4015         GFile *target;
4016         GMount *mount;
4017 
4018         mount = NULL;
4019         target = nautilus_file_get_activation_location (file);
4020         if (target != NULL)
4021         {
4022             mount = get_mount_at (target);
4023             g_object_unref (target);
4024         }
4025 
4026         got_mount (state, mount);
4027 
4028         if (mount)
4029         {
4030             g_object_unref (mount);
4031         }
4032     }
4033     else
4034     {
4035         g_file_find_enclosing_mount_async (location,
4036                                            G_PRIORITY_DEFAULT,
4037                                            state->cancellable,
4038                                            find_enclosing_mount_callback,
4039                                            state);
4040     }
4041     g_object_unref (location);
4042 }
4043 
4044 static void
filesystem_info_cancel(NautilusDirectory * directory)4045 filesystem_info_cancel (NautilusDirectory *directory)
4046 {
4047     if (directory->details->filesystem_info_state != NULL)
4048     {
4049         g_cancellable_cancel (directory->details->filesystem_info_state->cancellable);
4050         directory->details->filesystem_info_state->directory = NULL;
4051         directory->details->filesystem_info_state = NULL;
4052         async_job_end (directory, "filesystem info");
4053     }
4054 }
4055 
4056 static void
filesystem_info_stop(NautilusDirectory * directory)4057 filesystem_info_stop (NautilusDirectory *directory)
4058 {
4059     NautilusFile *file;
4060 
4061     if (directory->details->filesystem_info_state != NULL)
4062     {
4063         file = directory->details->filesystem_info_state->file;
4064 
4065         if (file != NULL)
4066         {
4067             g_assert (NAUTILUS_IS_FILE (file));
4068             g_assert (file->details->directory == directory);
4069             if (is_needy (file,
4070                           lacks_filesystem_info,
4071                           REQUEST_FILESYSTEM_INFO))
4072             {
4073                 return;
4074             }
4075         }
4076 
4077         /* The filesystem info is not wanted, so stop it. */
4078         filesystem_info_cancel (directory);
4079     }
4080 }
4081 
4082 static void
filesystem_info_state_free(FilesystemInfoState * state)4083 filesystem_info_state_free (FilesystemInfoState *state)
4084 {
4085     g_object_unref (state->cancellable);
4086     g_free (state);
4087 }
4088 
4089 static void
got_filesystem_info(FilesystemInfoState * state,GFileInfo * info)4090 got_filesystem_info (FilesystemInfoState *state,
4091                      GFileInfo           *info)
4092 {
4093     NautilusDirectory *directory;
4094     NautilusFile *file;
4095     const char *filesystem_type;
4096 
4097     /* careful here, info may be NULL */
4098 
4099     directory = nautilus_directory_ref (state->directory);
4100 
4101     state->directory->details->filesystem_info_state = NULL;
4102     async_job_end (state->directory, "filesystem info");
4103 
4104     file = nautilus_file_ref (state->file);
4105 
4106     file->details->filesystem_info_is_up_to_date = TRUE;
4107     if (info != NULL)
4108     {
4109         file->details->filesystem_use_preview =
4110             g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
4111         file->details->filesystem_readonly =
4112             g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
4113         filesystem_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
4114         file->details->filesystem_remote = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE);
4115         if (g_strcmp0 (file->details->filesystem_type, filesystem_type) != 0)
4116         {
4117             g_clear_pointer (&file->details->filesystem_type, g_ref_string_release);
4118             file->details->filesystem_type = g_ref_string_new_intern (filesystem_type);
4119         }
4120     }
4121 
4122     nautilus_directory_async_state_changed (directory);
4123     nautilus_file_changed (file);
4124 
4125     nautilus_file_unref (file);
4126 
4127     nautilus_directory_unref (directory);
4128 
4129     filesystem_info_state_free (state);
4130 }
4131 
4132 static void
query_filesystem_info_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)4133 query_filesystem_info_callback (GObject      *source_object,
4134                                 GAsyncResult *res,
4135                                 gpointer      user_data)
4136 {
4137     GFileInfo *info;
4138     FilesystemInfoState *state;
4139 
4140     state = user_data;
4141     if (state->directory == NULL)
4142     {
4143         /* Operation was cancelled. Bail out */
4144         filesystem_info_state_free (state);
4145         return;
4146     }
4147 
4148     info = g_file_query_filesystem_info_finish (G_FILE (source_object), res, NULL);
4149 
4150     got_filesystem_info (state, info);
4151 
4152     if (info != NULL)
4153     {
4154         g_object_unref (info);
4155     }
4156 }
4157 
4158 static void
filesystem_info_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)4159 filesystem_info_start (NautilusDirectory *directory,
4160                        NautilusFile      *file,
4161                        gboolean          *doing_io)
4162 {
4163     GFile *location;
4164     FilesystemInfoState *state;
4165 
4166     if (directory->details->filesystem_info_state != NULL)
4167     {
4168         *doing_io = TRUE;
4169         return;
4170     }
4171 
4172     if (!is_needy (file,
4173                    lacks_filesystem_info,
4174                    REQUEST_FILESYSTEM_INFO))
4175     {
4176         return;
4177     }
4178     *doing_io = TRUE;
4179 
4180     if (!async_job_start (directory, "filesystem info"))
4181     {
4182         return;
4183     }
4184 
4185     state = g_new0 (FilesystemInfoState, 1);
4186     state->directory = directory;
4187     state->file = file;
4188     state->cancellable = g_cancellable_new ();
4189 
4190     location = nautilus_file_get_location (file);
4191 
4192     directory->details->filesystem_info_state = state;
4193 
4194     g_file_query_filesystem_info_async (location,
4195                                         G_FILE_ATTRIBUTE_FILESYSTEM_READONLY ","
4196                                         G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW ","
4197                                         G_FILE_ATTRIBUTE_FILESYSTEM_TYPE ","
4198                                         G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
4199                                         G_PRIORITY_DEFAULT,
4200                                         state->cancellable,
4201                                         query_filesystem_info_callback,
4202                                         state);
4203     g_object_unref (location);
4204 }
4205 
4206 static void
extension_info_cancel(NautilusDirectory * directory)4207 extension_info_cancel (NautilusDirectory *directory)
4208 {
4209     if (directory->details->extension_info_in_progress != NULL)
4210     {
4211         if (directory->details->extension_info_idle)
4212         {
4213             g_source_remove (directory->details->extension_info_idle);
4214         }
4215         else
4216         {
4217             nautilus_info_provider_cancel_update
4218                 (directory->details->extension_info_provider,
4219                 directory->details->extension_info_in_progress);
4220         }
4221 
4222         directory->details->extension_info_in_progress = NULL;
4223         directory->details->extension_info_file = NULL;
4224         directory->details->extension_info_provider = NULL;
4225         directory->details->extension_info_idle = 0;
4226 
4227         async_job_end (directory, "extension info");
4228     }
4229 }
4230 
4231 static void
extension_info_stop(NautilusDirectory * directory)4232 extension_info_stop (NautilusDirectory *directory)
4233 {
4234     if (directory->details->extension_info_in_progress != NULL)
4235     {
4236         NautilusFile *file;
4237 
4238         file = directory->details->extension_info_file;
4239         if (file != NULL)
4240         {
4241             g_assert (NAUTILUS_IS_FILE (file));
4242             g_assert (file->details->directory == directory);
4243             if (is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO))
4244             {
4245                 return;
4246             }
4247         }
4248 
4249         /* The info is not wanted, so stop it. */
4250         extension_info_cancel (directory);
4251     }
4252 }
4253 
4254 static void
finish_info_provider(NautilusDirectory * directory,NautilusFile * file,NautilusInfoProvider * provider)4255 finish_info_provider (NautilusDirectory    *directory,
4256                       NautilusFile         *file,
4257                       NautilusInfoProvider *provider)
4258 {
4259     file->details->pending_info_providers =
4260         g_list_remove (file->details->pending_info_providers,
4261                        provider);
4262     g_object_unref (provider);
4263 
4264     nautilus_directory_async_state_changed (directory);
4265 
4266     if (file->details->pending_info_providers == NULL)
4267     {
4268         nautilus_file_info_providers_done (file);
4269     }
4270 }
4271 
4272 
4273 static gboolean
info_provider_idle_callback(gpointer user_data)4274 info_provider_idle_callback (gpointer user_data)
4275 {
4276     InfoProviderResponse *response;
4277     NautilusDirectory *directory;
4278 
4279     response = user_data;
4280     directory = response->directory;
4281 
4282     if (response->handle != directory->details->extension_info_in_progress
4283         || response->provider != directory->details->extension_info_provider)
4284     {
4285         g_warning ("Unexpected plugin response.  This probably indicates a bug in a Nautilus extension: handle=%p", response->handle);
4286     }
4287     else
4288     {
4289         NautilusFile *file;
4290         async_job_end (directory, "extension info");
4291 
4292         file = directory->details->extension_info_file;
4293 
4294         directory->details->extension_info_file = NULL;
4295         directory->details->extension_info_provider = NULL;
4296         directory->details->extension_info_in_progress = NULL;
4297         directory->details->extension_info_idle = 0;
4298 
4299         finish_info_provider (directory, file, response->provider);
4300     }
4301 
4302     return FALSE;
4303 }
4304 
4305 static void
info_provider_callback(NautilusInfoProvider * provider,NautilusOperationHandle * handle,NautilusOperationResult result,gpointer user_data)4306 info_provider_callback (NautilusInfoProvider    *provider,
4307                         NautilusOperationHandle *handle,
4308                         NautilusOperationResult  result,
4309                         gpointer                 user_data)
4310 {
4311     InfoProviderResponse *response;
4312 
4313     response = g_new0 (InfoProviderResponse, 1);
4314     response->provider = provider;
4315     response->handle = handle;
4316     response->result = result;
4317     response->directory = NAUTILUS_DIRECTORY (user_data);
4318 
4319     response->directory->details->extension_info_idle =
4320         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
4321                          info_provider_idle_callback, response,
4322                          g_free);
4323 }
4324 
4325 static void
extension_info_start(NautilusDirectory * directory,NautilusFile * file,gboolean * doing_io)4326 extension_info_start (NautilusDirectory *directory,
4327                       NautilusFile      *file,
4328                       gboolean          *doing_io)
4329 {
4330     NautilusInfoProvider *provider;
4331     NautilusOperationResult result;
4332     NautilusOperationHandle *handle;
4333     GClosure *update_complete;
4334 
4335     if (directory->details->extension_info_in_progress != NULL)
4336     {
4337         *doing_io = TRUE;
4338         return;
4339     }
4340 
4341     if (!is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO))
4342     {
4343         return;
4344     }
4345     *doing_io = TRUE;
4346 
4347     if (!async_job_start (directory, "extension info"))
4348     {
4349         return;
4350     }
4351 
4352     provider = file->details->pending_info_providers->data;
4353 
4354     update_complete = g_cclosure_new (G_CALLBACK (info_provider_callback),
4355                                       directory,
4356                                       NULL);
4357     g_closure_set_marshal (update_complete,
4358                            g_cclosure_marshal_generic);
4359 
4360     result = nautilus_info_provider_update_file_info
4361                  (provider,
4362                  NAUTILUS_FILE_INFO (file),
4363                  update_complete,
4364                  &handle);
4365 
4366     g_closure_unref (update_complete);
4367 
4368     if (result == NAUTILUS_OPERATION_COMPLETE ||
4369         result == NAUTILUS_OPERATION_FAILED)
4370     {
4371         finish_info_provider (directory, file, provider);
4372         async_job_end (directory, "extension info");
4373     }
4374     else
4375     {
4376         directory->details->extension_info_in_progress = handle;
4377         directory->details->extension_info_provider = provider;
4378         directory->details->extension_info_file = file;
4379     }
4380 }
4381 
4382 static void
start_or_stop_io(NautilusDirectory * directory)4383 start_or_stop_io (NautilusDirectory *directory)
4384 {
4385     NautilusFile *file;
4386     gboolean doing_io;
4387 
4388     /* Start or stop reading files. */
4389     file_list_start_or_stop (directory);
4390 
4391     /* Stop any no longer wanted attribute fetches. */
4392     file_info_stop (directory);
4393     directory_count_stop (directory);
4394     deep_count_stop (directory);
4395     mime_list_stop (directory);
4396     extension_info_stop (directory);
4397     mount_stop (directory);
4398     thumbnail_stop (directory);
4399     filesystem_info_stop (directory);
4400 
4401     doing_io = FALSE;
4402     /* Take files that are all done off the queue. */
4403     while (!nautilus_file_queue_is_empty (directory->details->high_priority_queue))
4404     {
4405         file = nautilus_file_queue_head (directory->details->high_priority_queue);
4406 
4407         /* Start getting attributes if possible */
4408         file_info_start (directory, file, &doing_io);
4409 
4410         if (doing_io)
4411         {
4412             return;
4413         }
4414 
4415         move_file_to_low_priority_queue (directory, file);
4416     }
4417 
4418     /* High priority queue must be empty */
4419     while (!nautilus_file_queue_is_empty (directory->details->low_priority_queue))
4420     {
4421         file = nautilus_file_queue_head (directory->details->low_priority_queue);
4422 
4423         /* Start getting attributes if possible */
4424         mount_start (directory, file, &doing_io);
4425         directory_count_start (directory, file, &doing_io);
4426         deep_count_start (directory, file, &doing_io);
4427         mime_list_start (directory, file, &doing_io);
4428         thumbnail_start (directory, file, &doing_io);
4429         filesystem_info_start (directory, file, &doing_io);
4430 
4431         if (doing_io)
4432         {
4433             return;
4434         }
4435 
4436         move_file_to_extension_queue (directory, file);
4437     }
4438 
4439     /* Low priority queue must be empty */
4440     while (!nautilus_file_queue_is_empty (directory->details->extension_queue))
4441     {
4442         file = nautilus_file_queue_head (directory->details->extension_queue);
4443 
4444         /* Start getting attributes if possible */
4445         extension_info_start (directory, file, &doing_io);
4446         if (doing_io)
4447         {
4448             return;
4449         }
4450 
4451         nautilus_directory_remove_file_from_work_queue (directory, file);
4452     }
4453 }
4454 
4455 /* Call this when the monitor or call when ready list changes,
4456  * or when some I/O is completed.
4457  */
4458 void
nautilus_directory_async_state_changed(NautilusDirectory * directory)4459 nautilus_directory_async_state_changed (NautilusDirectory *directory)
4460 {
4461     /* Check if any callbacks are satisfied and call them if they
4462      * are. Do this last so that any changes done in start or stop
4463      * I/O functions immediately (not in callbacks) are taken into
4464      * consideration. If any callbacks are called, consider the
4465      * I/O state again so that we can release or cancel I/O that
4466      * is not longer needed once the callbacks are satisfied.
4467      */
4468 
4469     if (directory->details->in_async_service_loop)
4470     {
4471         directory->details->state_changed = TRUE;
4472         return;
4473     }
4474     directory->details->in_async_service_loop = TRUE;
4475     nautilus_directory_ref (directory);
4476     do
4477     {
4478         directory->details->state_changed = FALSE;
4479         start_or_stop_io (directory);
4480         if (call_ready_callbacks (directory))
4481         {
4482             directory->details->state_changed = TRUE;
4483         }
4484     }
4485     while (directory->details->state_changed);
4486     directory->details->in_async_service_loop = FALSE;
4487     nautilus_directory_unref (directory);
4488 
4489     /* Check if any directories should wake up. */
4490     async_job_wake_up ();
4491 }
4492 
4493 void
nautilus_directory_cancel(NautilusDirectory * directory)4494 nautilus_directory_cancel (NautilusDirectory *directory)
4495 {
4496     /* Arbitrary order (kept alphabetical). */
4497     deep_count_cancel (directory);
4498     directory_count_cancel (directory);
4499     file_info_cancel (directory);
4500     file_list_cancel (directory);
4501     mime_list_cancel (directory);
4502     new_files_cancel (directory);
4503     extension_info_cancel (directory);
4504     thumbnail_cancel (directory);
4505     mount_cancel (directory);
4506     filesystem_info_cancel (directory);
4507 
4508     /* We aren't waiting for anything any more. */
4509     if (waiting_directories != NULL)
4510     {
4511         g_hash_table_remove (waiting_directories, directory);
4512     }
4513 
4514     /* Check if any directories should wake up. */
4515     async_job_wake_up ();
4516 }
4517 
4518 static void
cancel_directory_count_for_file(NautilusDirectory * directory,NautilusFile * file)4519 cancel_directory_count_for_file (NautilusDirectory *directory,
4520                                  NautilusFile      *file)
4521 {
4522     if (directory->details->count_in_progress != NULL &&
4523         directory->details->count_in_progress->count_file == file)
4524     {
4525         directory_count_cancel (directory);
4526     }
4527 }
4528 
4529 static void
cancel_deep_counts_for_file(NautilusDirectory * directory,NautilusFile * file)4530 cancel_deep_counts_for_file (NautilusDirectory *directory,
4531                              NautilusFile      *file)
4532 {
4533     if (directory->details->deep_count_file == file)
4534     {
4535         deep_count_cancel (directory);
4536     }
4537 }
4538 
4539 static void
cancel_mime_list_for_file(NautilusDirectory * directory,NautilusFile * file)4540 cancel_mime_list_for_file (NautilusDirectory *directory,
4541                            NautilusFile      *file)
4542 {
4543     if (directory->details->mime_list_in_progress != NULL &&
4544         directory->details->mime_list_in_progress->mime_list_file == file)
4545     {
4546         mime_list_cancel (directory);
4547     }
4548 }
4549 
4550 static void
cancel_file_info_for_file(NautilusDirectory * directory,NautilusFile * file)4551 cancel_file_info_for_file (NautilusDirectory *directory,
4552                            NautilusFile      *file)
4553 {
4554     if (directory->details->get_info_file == file)
4555     {
4556         file_info_cancel (directory);
4557     }
4558 }
4559 
4560 static void
cancel_thumbnail_for_file(NautilusDirectory * directory,NautilusFile * file)4561 cancel_thumbnail_for_file (NautilusDirectory *directory,
4562                            NautilusFile      *file)
4563 {
4564     if (directory->details->thumbnail_state != NULL &&
4565         directory->details->thumbnail_state->file == file)
4566     {
4567         thumbnail_cancel (directory);
4568     }
4569 }
4570 
4571 static void
cancel_mount_for_file(NautilusDirectory * directory,NautilusFile * file)4572 cancel_mount_for_file (NautilusDirectory *directory,
4573                        NautilusFile      *file)
4574 {
4575     if (directory->details->mount_state != NULL &&
4576         directory->details->mount_state->file == file)
4577     {
4578         mount_cancel (directory);
4579     }
4580 }
4581 
4582 static void
cancel_filesystem_info_for_file(NautilusDirectory * directory,NautilusFile * file)4583 cancel_filesystem_info_for_file (NautilusDirectory *directory,
4584                                  NautilusFile      *file)
4585 {
4586     if (directory->details->filesystem_info_state != NULL &&
4587         directory->details->filesystem_info_state->file == file)
4588     {
4589         filesystem_info_cancel (directory);
4590     }
4591 }
4592 
4593 static void
cancel_loading_attributes(NautilusDirectory * directory,NautilusFileAttributes file_attributes)4594 cancel_loading_attributes (NautilusDirectory      *directory,
4595                            NautilusFileAttributes  file_attributes)
4596 {
4597     Request request;
4598 
4599     request = nautilus_directory_set_up_request (file_attributes);
4600 
4601     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
4602     {
4603         directory_count_cancel (directory);
4604     }
4605     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
4606     {
4607         deep_count_cancel (directory);
4608     }
4609     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
4610     {
4611         mime_list_cancel (directory);
4612     }
4613     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
4614     {
4615         file_info_cancel (directory);
4616     }
4617     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
4618     {
4619         filesystem_info_cancel (directory);
4620     }
4621     if (REQUEST_WANTS_TYPE (request, REQUEST_EXTENSION_INFO))
4622     {
4623         extension_info_cancel (directory);
4624     }
4625 
4626     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
4627     {
4628         thumbnail_cancel (directory);
4629     }
4630 
4631     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
4632     {
4633         mount_cancel (directory);
4634     }
4635 
4636     nautilus_directory_async_state_changed (directory);
4637 }
4638 
4639 void
nautilus_directory_cancel_loading_file_attributes(NautilusDirectory * directory,NautilusFile * file,NautilusFileAttributes file_attributes)4640 nautilus_directory_cancel_loading_file_attributes (NautilusDirectory      *directory,
4641                                                    NautilusFile           *file,
4642                                                    NautilusFileAttributes  file_attributes)
4643 {
4644     Request request;
4645 
4646     nautilus_directory_remove_file_from_work_queue (directory, file);
4647 
4648     request = nautilus_directory_set_up_request (file_attributes);
4649 
4650     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
4651     {
4652         cancel_directory_count_for_file (directory, file);
4653     }
4654     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
4655     {
4656         cancel_deep_counts_for_file (directory, file);
4657     }
4658     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
4659     {
4660         cancel_mime_list_for_file (directory, file);
4661     }
4662     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
4663     {
4664         cancel_file_info_for_file (directory, file);
4665     }
4666     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
4667     {
4668         cancel_filesystem_info_for_file (directory, file);
4669     }
4670     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
4671     {
4672         cancel_thumbnail_for_file (directory, file);
4673     }
4674     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
4675     {
4676         cancel_mount_for_file (directory, file);
4677     }
4678 
4679     nautilus_directory_async_state_changed (directory);
4680 }
4681 
4682 void
nautilus_directory_add_file_to_work_queue(NautilusDirectory * directory,NautilusFile * file)4683 nautilus_directory_add_file_to_work_queue (NautilusDirectory *directory,
4684                                            NautilusFile      *file)
4685 {
4686     g_return_if_fail (file->details->directory == directory);
4687 
4688     nautilus_file_queue_enqueue (directory->details->high_priority_queue,
4689                                  file);
4690 }
4691 
4692 
4693 static void
add_all_files_to_work_queue(NautilusDirectory * directory)4694 add_all_files_to_work_queue (NautilusDirectory *directory)
4695 {
4696     GList *node;
4697     NautilusFile *file;
4698 
4699     for (node = directory->details->file_list; node != NULL; node = node->next)
4700     {
4701         file = NAUTILUS_FILE (node->data);
4702 
4703         nautilus_directory_add_file_to_work_queue (directory, file);
4704     }
4705 }
4706 
4707 void
nautilus_directory_remove_file_from_work_queue(NautilusDirectory * directory,NautilusFile * file)4708 nautilus_directory_remove_file_from_work_queue (NautilusDirectory *directory,
4709                                                 NautilusFile      *file)
4710 {
4711     nautilus_file_queue_remove (directory->details->high_priority_queue,
4712                                 file);
4713     nautilus_file_queue_remove (directory->details->low_priority_queue,
4714                                 file);
4715     nautilus_file_queue_remove (directory->details->extension_queue,
4716                                 file);
4717 }
4718 
4719 
4720 static void
move_file_to_low_priority_queue(NautilusDirectory * directory,NautilusFile * file)4721 move_file_to_low_priority_queue (NautilusDirectory *directory,
4722                                  NautilusFile      *file)
4723 {
4724     /* Must add before removing to avoid ref underflow */
4725     nautilus_file_queue_enqueue (directory->details->low_priority_queue,
4726                                  file);
4727     nautilus_file_queue_remove (directory->details->high_priority_queue,
4728                                 file);
4729 }
4730 
4731 static void
move_file_to_extension_queue(NautilusDirectory * directory,NautilusFile * file)4732 move_file_to_extension_queue (NautilusDirectory *directory,
4733                               NautilusFile      *file)
4734 {
4735     /* Must add before removing to avoid ref underflow */
4736     nautilus_file_queue_enqueue (directory->details->extension_queue,
4737                                  file);
4738     nautilus_file_queue_remove (directory->details->low_priority_queue,
4739                                 file);
4740 }
4741