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