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