~elementary-apps/pantheon-files/trunk

« back to all changes in this revision

Viewing changes to libcore/gof-directory-async.vala

  • Committer: Jeremy Wootten
  • Date: 2017-05-08 10:39:31 UTC
  • mfrom: (2557 pantheon-files)
  • mto: This revision was merged to the branch mainline in revision 2558.
  • Revision ID: jeremy@elementaryos.org-20170508103931-jkxai92h7pf2yw2r
Merge trunk to r2557

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
 
5
5
    This program is free software: you can redistribute it and/or modify
6
6
    it under the terms of the GNU General Public License as published by
7
 
    the Free Software Foundation, either version 3 of the License, or
 
7
    the Free Software Foundation, Inc.,, either version 3 of the License, or
8
8
    (at your option) any later version.
9
9
 
10
10
    This program is distributed in the hope that it will be useful,
27
27
 
28
28
    private uint load_timeout_id = 0;
29
29
    private uint mount_timeout_id = 0;
30
 
    private const int ENUMERATE_TIMEOUT_SEC = 15;
31
 
    private const int QUERY_INFO_TIMEOUT_SEC = 5;
32
 
    private const int MOUNT_TIMEOUT_SEC = 10;
 
30
    private const int CONNECT_SOCKET_TIMEOUT_SEC = 30;
 
31
    private const int ENUMERATE_TIMEOUT_SEC = 30;
 
32
    private const int QUERY_INFO_TIMEOUT_SEC = 20;
 
33
    private const int MOUNT_TIMEOUT_SEC = 60;
33
34
 
34
35
    public GLib.File location;
35
36
    public GLib.File? selected_file {get; private set;}
46
47
    public enum State {
47
48
        NOT_LOADED,
48
49
        LOADING,
49
 
        LOADED
 
50
        LOADED,
 
51
        TIMED_OUT
50
52
    }
51
53
    public State state {get; private set;}
52
54
 
102
104
        get { return cancellable.is_cancelled (); }
103
105
    }
104
106
 
 
107
    public string last_error_message {get; private set; default = "";}
 
108
 
105
109
    private Async (GLib.File _file) {
106
110
        /* Ensure uri is correctly escaped */
107
111
        location = GLib.File.new_for_uri (PF.FileUtils.escape_uri (_file.get_uri ()));
115
119
        scheme = location.get_uri_scheme ();
116
120
        is_trash = (scheme == "trash");
117
121
        is_recent = (scheme == "recent");
118
 
        is_no_info = ("cdda mtp".contains (scheme));
 
122
        is_no_info = ("cdda mtp ssh sftp afp dav davs".contains (scheme)); //Try lifting requirement for info on remote connections 
119
123
        is_local = is_trash || is_recent || (scheme == "file");
120
124
        is_network = !is_local && ("ftp sftp afp dav davs".contains (scheme));
121
125
        can_open_files = !("mtp".contains (scheme));
122
126
        can_stream_files = !("ftp sftp mtp dav davs".contains (scheme));
123
127
 
124
 
        dir_cache_lock.@lock (); /* will always have been created via call to public static functions from_file () or from_gfile () */
125
 
        directory_cache.insert (location.dup (), this);
126
 
        dir_cache_lock.unlock ();
 
128
        file_hash = new HashTable<GLib.File, GOF.File> (GLib.File.hash, GLib.File.equal);
127
129
 
128
130
        this.add_toggle_ref ((ToggleNotify) toggle_ref_notify);
129
131
        this.unref ();
130
 
 
131
 
        file_hash = new HashTable<GLib.File, GOF.File> (GLib.File.hash, GLib.File.equal);
132
132
    }
133
133
 
134
134
    ~Async () {
146
146
     **/
147
147
    public void init (GOFFileLoadedFunc? file_loaded_func = null) {
148
148
        if (state == State.LOADING) {
 
149
            debug ("Directory Init re-entered - already loading");
149
150
            return; /* Do not re-enter */
150
151
        }
151
152
        var previous_state = state;
168
169
     * the network is made
169
170
     */
170
171
    private async void prepare_directory (GOFFileLoadedFunc? file_loaded_func) {
 
172
        debug ("Preparing directory for loading");
 
173
        /* Force info to be refreshed - the GOF.File may have been created already by another part of the program
 
174
         * that did not ensure the correct info Aync purposes, and retrieved from cache (bug 1511307).
 
175
         */
 
176
        file.info = null;
171
177
        bool success = yield get_file_info ();
 
178
 
172
179
        if (success) {
173
180
            if (!is_no_info && !file.is_folder () && !file.is_root_network_folder ()) {
174
 
                warning ("Trying to load a non-folder - finding parent");
 
181
                debug ("Trying to load a non-folder - finding parent");
175
182
                var parent = file.is_connected ? location.get_parent () : null;
176
183
                if (parent != null) {
177
184
                    file = GOF.File.get (parent);
187
194
            warning ("Failed to get file info for file %s", file.uri);
188
195
        }
189
196
 
 
197
        if (success) {
 
198
            file.update ();
 
199
        }
 
200
        debug ("success %s; enclosing mount %s", success.to_string (), file.mount != null ? file.mount.get_name () : "null");
190
201
        yield make_ready (is_no_info || success, file_loaded_func); /* Only place that should call this function */
191
202
    }
192
203
 
193
204
    /*** Returns false if should be able to get info but were unable to ***/
194
205
    private async bool get_file_info () {
195
 
        /* Force info to be refreshed - the GOF.File may have been created already by another part of the program
196
 
         * that did not ensure the correct info Aync purposes, and retrieved from cache (bug 1511307).
197
 
         */
198
 
        file.info = null;
 
206
        debug ("get_file_info");
199
207
 
200
208
        if (is_network && !yield check_network ()) {
 
209
            warning ("No network found");
201
210
            file.is_connected = false;
202
211
            return false;
203
212
        }
204
213
 
205
 
        if (is_no_info) {
206
 
            /* Not a failure when not expected to get file info */
207
 
            return true;
208
 
        }
209
 
 
210
 
        if (is_local) {
211
 
            return file.ensure_query_info ();
212
 
        }
 
214
        /* is_network flag fails to detect remote folders mapped to a local uri through fstab, so treat
 
215
         * all folders as potentially remote (and disconnected) */
213
216
 
214
217
        if (!yield try_query_info ()) { /* may already be mounted */
 
218
            debug ("try query info failed - trying to mount");
215
219
            if (yield mount_mountable ()) {
216
220
            /* Previously mounted Samba servers still appear mounted even if disconnected
217
221
             * e.g. by unplugging the network cable.  So the following function can block for
218
222
             * a long time; we therefore use a timeout */
219
223
                debug ("successful mount %s", file.uri);
220
 
                return yield try_query_info ();
 
224
                file.is_mounted = true;
 
225
                return (yield try_query_info ()) || is_no_info;
221
226
            } else {
 
227
                warning ("failed mount %s", file.uri);
222
228
                return false;
223
229
            }
224
230
        } else {
227
233
    }
228
234
 
229
235
    private async bool try_query_info () {
 
236
        debug ("try_query_info");
230
237
        cancellable = new Cancellable ();
231
238
        bool querying = true;
232
239
        assert (load_timeout_id == 0);
234
241
            if (querying) {
235
242
                warning ("Cancelled after timeout in query info async %s", file.uri);
236
243
                cancellable.cancel ();
 
244
                last_error_message = "Timed out while querying file info";
237
245
            }
238
246
            load_timeout_id = 0;
239
247
            return false;
249
257
        }
250
258
 
251
259
        if (success) {
252
 
            debug ("got file info");
 
260
            debug ("got file info - updating");
253
261
            file.update ();
 
262
            debug ("success %s; enclosing mount %s", success.to_string (), file.mount != null ? file.mount.get_name () : "null");
254
263
            return true;
255
264
        } else {
256
265
            warning ("Failed to get file info for %s", file.uri);
259
268
    }
260
269
 
261
270
    private async bool mount_mountable () {
 
271
        debug ("mount_mountable");
 
272
        bool res = false;
 
273
        var mount_op = new Gtk.MountOperation (null);
 
274
        cancellable = new Cancellable ();
 
275
 
262
276
        try {
263
 
            var mount_op = new Gtk.MountOperation (null);
264
 
            cancellable = new Cancellable ();
265
277
            bool mounting = true;
266
278
            bool asking_password = false;
267
279
            assert (mount_timeout_id == 0);
268
280
 
269
281
            mount_timeout_id = Timeout.add_seconds (MOUNT_TIMEOUT_SEC, () => {
270
282
                if (mounting && !asking_password) {
 
283
                    mount_timeout_id = 0;
271
284
                    warning ("Cancelled after timeout in mount mountable %s", file.uri);
 
285
                    last_error_message = ("Timed out when trying to mount %s").printf (file.uri);
 
286
                    state = State.TIMED_OUT;
272
287
                    cancellable.cancel ();
273
 
                    mount_timeout_id = 0;
 
288
 
274
289
                    return false;
275
290
                } else {
276
291
                    return true;
278
293
            });
279
294
 
280
295
            mount_op.ask_password.connect (() => {
 
296
                debug ("Asking for password");
281
297
                asking_password = true;
282
298
            });
283
299
 
284
300
            mount_op.reply.connect (() => {
 
301
                debug ("Password dialog finished");
285
302
                asking_password = false;
286
303
            });
287
304
 
288
 
            yield location.mount_enclosing_volume (0, mount_op, cancellable);
289
 
            var mount = location.find_enclosing_mount ();
290
 
 
291
 
            debug ("Found enclosing mount %s", mount != null ? mount.get_name () : "null");
292
 
            return mount != null;
 
305
            debug ("mounting ....");
 
306
            res =yield location.mount_enclosing_volume (GLib.MountMountFlags.NONE, mount_op, cancellable);
293
307
        } catch (Error e) {
 
308
            last_error_message = e.message;
294
309
            if (e is IOError.ALREADY_MOUNTED) {
295
310
                debug ("Already mounted %s", file.uri);
296
311
                file.is_connected = true;
 
312
                res = true;
297
313
            } else if (e is IOError.NOT_FOUND) {
298
314
                debug ("Enclosing mount not found %s (may be remote share)", file.uri);
299
 
                file.is_mounted = false;
300
 
                return true;
 
315
                /* Do not fail loading at this point - may still load */
 
316
                try {
 
317
                    yield location.mount_mountable (GLib.MountMountFlags.NONE, mount_op, cancellable);
 
318
                    res = true;
 
319
                } catch (GLib.Error e2) {
 
320
                    last_error_message = e2.message;
 
321
                    warning ("Unable to mount mountable");
 
322
                    res = false;
 
323
                }
 
324
 
301
325
            } else {
302
326
                file.is_connected = false;
303
327
                file.is_mounted = false;
 
328
                debug ("Setting mount null 1");
 
329
                file.mount = null;
304
330
                warning ("Mount_mountable failed: %s", e.message);
305
331
                if (e is IOError.PERMISSION_DENIED || e is IOError.FAILED_HANDLED) {
306
332
                    permission_denied = true;
307
333
                }
308
334
            }
309
 
            return false;
310
335
        } finally {
311
336
            cancel_timeout (ref mount_timeout_id);
312
337
        }
 
338
 
 
339
        debug ("success %s; enclosing mount %s", res.to_string (), file.mount != null ? file.mount.get_name () : "null");
 
340
        return res;
313
341
    }
314
342
 
315
343
    public async bool check_network () {
 
344
        debug ("check network");
316
345
        var net_mon = GLib.NetworkMonitor.get_default ();
317
346
        network_available = net_mon.get_network_available ();
318
347
 
319
348
        bool success = false;
320
349
 
321
350
        if (network_available) {
322
 
            SocketConnectable? connectable = null;
323
 
            try {
324
 
                connectable = NetworkAddress.parse_uri (file.uri, 21);
325
 
                if (((NetworkAddress)(connectable)).get_hostname () != "" && scheme != "smb") {
326
 
                    success = net_mon.can_reach (connectable, cancellable);
327
 
                    /* Try to connect for real.  This should time out after about 15 seconds if
328
 
                     * the host is not reachable */
329
 
                    var scl = new SocketClient ();
330
 
                    var sc = yield scl.connect_async (connectable, cancellable);
331
 
                    success = (sc != null && sc.is_connected ());
332
 
                    debug ("Attempt to connect to %s %s", file.uri, success ? "succeeded" : "failed");
 
351
            if (!file.is_mounted) {
 
352
                debug ("Network is available");
 
353
                if (scheme != "smb") {
 
354
                    try {
 
355
                        /* Try to connect for real.  */
 
356
                        var scl = new SocketClient ();
 
357
                        scl.set_timeout (CONNECT_SOCKET_TIMEOUT_SEC);
 
358
                        scl.set_tls (PF.FileUtils.get_is_tls_for_protocol (scheme));
 
359
                        debug ("Trying to connect to connectable");
 
360
                        var sc = yield scl.connect_to_uri_async (file.uri, PF.FileUtils.get_default_port_for_protocol (scheme), cancellable);
 
361
                        success = (sc != null && sc.is_connected ());
 
362
                        debug ("Socketclient is %s", sc == null ? "null" : (sc.is_connected () ? "connected" : "not connected"));
 
363
                    } catch (GLib.Error e) {
 
364
                        last_error_message = e.message;
 
365
                        warning ("Error: could not connect to connectable %s - %s", file.uri, e.message);
 
366
                        return false;
 
367
                    }
333
368
                } else {
334
369
                    success = true;
335
370
                }
336
 
            }
337
 
            catch (GLib.Error e) {
338
 
                warning ("Error connecting to connectable %s - %s", file.uri, e.message);
339
 
                success = false;
340
 
 
 
371
            } else {
 
372
                debug ("File is already mounted - not reconnecting");
 
373
                success = true;
341
374
            }
342
375
        } else {
343
376
            warning ("No network available");
344
377
        }
 
378
 
 
379
 
 
380
        debug ("Attempt to connect to %s %s", file.uri, success ? "succeeded" : "failed");
345
381
        return success;
346
382
    }
347
383
     
348
384
 
349
385
    private async void make_ready (bool ready, GOFFileLoadedFunc? file_loaded_func = null) {
 
386
        debug ("make ready");
350
387
        can_load = ready;
351
388
        if (!can_load) {
352
389
            warning ("%s cannot load.  Connected %s, Mounted %s, Exists %s", file.uri,
353
390
                                                                             file.is_connected.to_string (),
354
391
                                                                             file.is_mounted.to_string (),
355
392
                                                                             file.exists.to_string ());
356
 
            state = State.NOT_LOADED; /* ensure state is correct */
357
 
            done_loading ();
 
393
            after_loading (file_loaded_func);
358
394
            return;
359
395
        }
360
396
 
361
397
        if (!is_ready) {
 
398
            /* Do not cache directory until it prepared and loadable to avoid an incorrect key being used in some
 
399
             * in some cases.
 
400
             */ 
 
401
            dir_cache_lock.@lock (); /* will always have been created via call to public static functions from_file () or from_gfile () */
 
402
            directory_cache.insert (location.dup (), this);
 
403
            dir_cache_lock.unlock ();
 
404
 
362
405
            is_ready = true;
 
406
            if (file.mount != null) {
 
407
                debug ("Directory has mount point");
 
408
                unowned GLib.List? trash_dirs = null;
 
409
                trash_dirs = Marlin.FileOperations.get_trash_dirs_for_mount (file.mount);
 
410
                has_trash_dirs = (trash_dirs != null);
 
411
            } else {
 
412
                has_trash_dirs = is_local;
 
413
            }
 
414
 
363
415
            yield list_directory_async (file_loaded_func);
364
416
 
365
417
            if (can_load) {
370
422
                        monitor.rate_limit = 100;
371
423
                        monitor.changed.connect (directory_changed);
372
424
                    } catch (IOError e) {
 
425
                        last_error_message = e.message;
373
426
                        if (!(e is IOError.NOT_MOUNTED)) {
374
427
                            /* Will fail for remote filesystems - not an error */
375
428
                            debug ("directory monitor failed: %s %s", e.message, file.uri);
378
431
                }
379
432
 
380
433
                set_confirm_trash ();
381
 
                file.mount = GOF.File.get_mount_at (location);
382
 
                if (file.mount != null) {
383
 
                    file.is_mounted = true;
384
 
                    unowned GLib.List? trash_dirs = null;
385
 
                    trash_dirs = Marlin.FileOperations.get_trash_dirs_for_mount (file.mount);
386
 
                    has_trash_dirs = (trash_dirs != null);
387
 
                } else {
388
 
                    has_trash_dirs = is_local;
389
 
                }
390
434
 
391
435
                if (is_trash) {
392
436
                    connect_volume_monitor_signals ();
449
493
 
450
494
 
451
495
    public void reload () {
 
496
        debug ("Reload - state is %s", state.to_string ());
 
497
        if (state == State.TIMED_OUT && file.is_mounted) {
 
498
            debug ("Unmounting because of timeout");
 
499
            cancellable.cancel ();
 
500
            cancellable = new Cancellable ();
 
501
            file.location.unmount_mountable (GLib.MountUnmountFlags.FORCE, cancellable);
 
502
            file.mount = null;
 
503
            file.is_mounted = false;
 
504
        }
 
505
 
452
506
        clear_directory_info ();
 
507
        state = State.NOT_LOADED;
453
508
        init ();
454
509
    }
455
510
 
463
518
        monitor = null;
464
519
        sorted_dirs = null;
465
520
        files_count = 0;
466
 
        state = State.NOT_LOADED;
467
521
        is_ready = false;
468
522
        can_load = false;
469
523
    }
470
524
 
471
525
    private void list_cached_files (GOFFileLoadedFunc? file_loaded_func = null) {
 
526
        debug ("list cached files");
472
527
        if (state != State.LOADED) {
473
528
            warning ("list cached files called in %s state - not expected to happen", state.to_string ());
474
529
            return;
475
530
        }
 
531
 
 
532
        debug ("Listing cached files");  /* Required for ctest */
 
533
 
476
534
        state = State.LOADING;
477
 
        bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
 
535
        bool show_hidden = is_trash || Preferences.get_default ().show_hidden_files;
478
536
        foreach (GOF.File gof in file_hash.get_values ()) {
479
537
            if (gof != null) {
480
538
                after_load_file (gof, show_hidden, file_loaded_func);
485
543
    }
486
544
 
487
545
    private async void list_directory_async (GOFFileLoadedFunc? file_loaded_func) {
 
546
        debug ("list directory async");
488
547
        /* Should only be called after creation and if reloaded */
489
548
        if (!is_ready || file_hash.size () > 0) {
490
549
            critical ("(Re)load directory called when not cleared");
511
570
        can_load = true;
512
571
        files_count = 0;
513
572
        state = State.LOADING;
514
 
        bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
 
573
        bool show_hidden = is_trash || Preferences.get_default ().show_hidden_files;
 
574
        bool server_responding = false;
 
575
 
 
576
        debug ("(Re)loading folder children"); /* Required for ctest */
515
577
 
516
578
        try {
517
579
            /* This may hang for a long time if the connection was closed but is still mounted so we
518
580
             * impose a time limit */
519
581
            load_timeout_id = Timeout.add_seconds (ENUMERATE_TIMEOUT_SEC, () => {
520
 
                cancellable.cancel ();
521
 
                load_timeout_id = 0;
522
 
                return false;
 
582
                if (server_responding) {
 
583
                    return true;
 
584
                } else {
 
585
                    debug ("Load timeout expired");
 
586
                    state = State.TIMED_OUT;
 
587
                    last_error_message = _("Server did not respond within time limit");
 
588
                    load_timeout_id = 0;
 
589
                    cancellable.cancel ();
 
590
 
 
591
                    return false;
 
592
                }
523
593
            });
524
594
 
525
595
            var e = yield this.location.enumerate_children_async (gio_attrs, 0, Priority.HIGH, cancellable);
526
 
            cancel_timeout (ref load_timeout_id);
 
596
            debug ("Obtained file enumerator for location %s", location.get_uri ());
527
597
 
528
598
            GOF.File? gof;
529
599
            GLib.File loc;
530
600
            while (!cancellable.is_cancelled ()) {
531
 
                var files = yield e.next_files_async (200, 0, cancellable);
532
 
                if (files == null) {
533
 
                    break;
534
 
                } else {
535
 
                    foreach (var file_info in files) {
536
 
                        loc = location.get_child (file_info.get_name ());
537
 
                        assert (loc != null);
538
 
                        gof = GOF.File.cache_lookup (loc);
539
 
 
540
 
                        if (gof == null) {
541
 
                            gof = new GOF.File (loc, location); /*does not add to GOF file cache */
 
601
                try {
 
602
                    server_responding = false;
 
603
                    var files = yield e.next_files_async (200, GLib.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
 
604
                    server_responding = true;
 
605
 
 
606
                    if (files == null) {
 
607
                        break;
 
608
                    } else {
 
609
                        foreach (var file_info in files) {
 
610
                            loc = location.get_child (file_info.get_name ());
 
611
                            assert (loc != null);
 
612
                            gof = GOF.File.cache_lookup (loc);
 
613
 
 
614
                            if (gof == null) {
 
615
                                gof = new GOF.File (loc, location); /*does not add to GOF file cache */
 
616
                            }
 
617
 
 
618
                            gof.info = file_info;
 
619
                            gof.update ();
 
620
 
 
621
                            file_hash.insert (gof.location, gof);
 
622
                            after_load_file (gof, show_hidden, file_loaded_func);
 
623
                            files_count++;
542
624
                        }
543
 
 
544
 
                        gof.info = file_info;
545
 
                        gof.update ();
546
 
 
547
 
                        file_hash.insert (gof.location, gof);
548
 
                        after_load_file (gof, show_hidden, file_loaded_func);
549
 
                        files_count++;
550
625
                    }
 
626
                } catch (Error e) {
 
627
                    last_error_message = e.message;
 
628
                    warning ("Error reported by next_files_async - %s", e.message);
551
629
                }
552
630
            }
553
 
            state = State.LOADED;
 
631
            /* Load as many files as we can get info for */
 
632
            if (!(cancellable.is_cancelled ())) {
 
633
                state = State.LOADED;
 
634
            }
554
635
        } catch (Error err) {
555
 
            warning ("Listing directory error: %s %s", err.message, file.uri);
 
636
            warning ("Listing directory error: %s, %s %s", last_error_message, err.message, file.uri);
556
637
            can_load = false;
557
638
            if (err is IOError.NOT_FOUND || err is IOError.NOT_DIRECTORY) {
558
639
                file.exists = false;
559
 
            } else if (err is IOError.PERMISSION_DENIED)
 
640
            } else if (err is IOError.PERMISSION_DENIED) {
560
641
                permission_denied = true;
561
 
            else if (err is IOError.NOT_MOUNTED)
 
642
            } else if (err is IOError.NOT_MOUNTED) {
 
643
                file.mount = null;
562
644
                file.is_mounted = false;
 
645
            }
 
646
        } finally {
 
647
            cancel_timeout (ref load_timeout_id);
 
648
            after_loading (file_loaded_func);
563
649
        }
564
 
 
565
 
        after_loading (file_loaded_func);
566
650
    }
567
651
 
568
652
    private void after_load_file (GOF.File gof, bool show_hidden, GOFFileLoadedFunc? file_loaded_func) {
580
664
    private void after_loading (GOFFileLoadedFunc? file_loaded_func) {
581
665
        /* If loading failed reset */
582
666
        debug ("after loading state is %s", state.to_string ());
583
 
        if (state == State.LOADING) {
584
 
            state = State.NOT_LOADED; /* else clear directory info will fail */
 
667
        if (state == State.LOADING || state == State.TIMED_OUT) {
 
668
            state = State.TIMED_OUT; /* else clear directory info will fail */
 
669
            can_load = false;
 
670
        }
 
671
 
 
672
        if (file_loaded_func == null) {
 
673
            done_loading ();
 
674
        }
 
675
 
 
676
        if (state != State.LOADED) {
585
677
            clear_directory_info ();
586
 
            can_load = false;
587
 
        }
588
 
        if (file_loaded_func == null) {
589
 
            done_loading ();
590
678
        }
591
679
    }
592
680
 
623
711
    public void update_files () {
624
712
        foreach (GOF.File gof in file_hash.get_values ()) {
625
713
            if (gof != null && gof.info != null
626
 
                && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files))
 
714
                && (!gof.is_hidden || Preferences.get_default ().show_hidden_files))
627
715
 
628
716
                gof.update ();
629
717
        }
632
720
    public void update_desktop_files () {
633
721
        foreach (GOF.File gof in file_hash.get_values ()) {
634
722
            if (gof != null && gof.info != null
635
 
                && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files)
 
723
                && (!gof.is_hidden || Preferences.get_default ().show_hidden_files)
636
724
                && gof.is_desktop)
637
725
 
638
726
                gof.update_desktop_file ();
687
775
                f (gof);
688
776
            }
689
777
        } catch (Error err) {
 
778
            last_error_message = err.message;
690
779
            warning ("query info failed, %s %s", err.message, gof.uri);
691
780
            if (err is IOError.NOT_FOUND) {
692
781
                gof.exists = false;
703
792
 
704
793
        gof.update ();
705
794
 
706
 
        if (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files) {
 
795
        if (!gof.is_hidden || Preferences.get_default ().show_hidden_files) {
707
796
            file_changed (gof);
708
797
            gof.changed ();
709
798
        }
721
810
 
722
811
        gof.update ();
723
812
 
724
 
        if ((!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files)) {
 
813
        if ((!gof.is_hidden || Preferences.get_default ().show_hidden_files)) {
725
814
            file_added (gof);
726
815
        }
727
816
 
748
837
    }
749
838
 
750
839
    private void notify_file_removed (GOF.File gof) {
751
 
        if (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files) {
 
840
        if (!gof.is_hidden || Preferences.get_default ().show_hidden_files) {
752
841
            file_deleted (gof);
753
842
        }
754
843
 
955
1044
        if (file == null) {
956
1045
            critical ("Null file received in Async cache_lookup");
957
1046
        }
 
1047
 
958
1048
        dir_cache_lock.@lock ();
959
1049
        cached_dir = directory_cache.lookup (file);
 
1050
        dir_cache_lock.unlock ();
960
1051
 
961
1052
        if (cached_dir != null) {
962
1053
            if (cached_dir is Async && cached_dir.file != null) {
963
1054
                debug ("found cached dir %s", cached_dir.file.uri);
964
 
                if (cached_dir.file.info == null && cached_dir.can_load)
 
1055
                if (cached_dir.file.info == null && cached_dir.can_load) {
 
1056
                    debug ("updating cached file info");
965
1057
                    cached_dir.file.query_update ();  /* This is synchronous and causes blocking */
 
1058
                }
966
1059
            } else {
967
1060
                warning ("Invalid directory found in cache");
968
1061
                cached_dir = null;
 
1062
                dir_cache_lock.@lock ();
969
1063
                directory_cache.remove (file);
 
1064
                dir_cache_lock.unlock ();
970
1065
            }
971
1066
        } else {
972
1067
            debug ("Dir %s not in cache", file.get_uri ());
973
1068
        }
974
 
        dir_cache_lock.unlock ();
975
1069
 
976
1070
        return cached_dir;
977
1071
    }
1024
1118
        return this.state == State.LOADED;
1025
1119
    }
1026
1120
 
 
1121
    public bool has_timed_out () {
 
1122
        return this.state == State.TIMED_OUT;
 
1123
    }
 
1124
 
1027
1125
    public bool is_empty () {
1028
1126
        return (state == State.LOADED && file_hash.size () == 0); /* only return true when loaded to avoid temporary appearance of empty message while loading */
1029
1127
    }
1045
1143
        sorted_dirs.sort (GOF.File.compare_by_display_name);
1046
1144
        return sorted_dirs;
1047
1145
    }
 
1146
 
1048
1147
    private void cancel_timeouts () {
1049
1148
        cancel_timeout (ref idle_consume_changes_id);
1050
1149
        cancel_timeout (ref load_timeout_id);