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