~jeremywootten/pantheon-files/fix-backspace-in-columnv-view

« back to all changes in this revision

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

  • Committer: Jeremy Wootten
  • Date: 2016-01-29 17:21:40 UTC
  • mfrom: (2023.1.25 pantheon-files)
  • Revision ID: jeremy@elementaryos.org-20160129172140-hirnjv1uo5mxiz7b
merge trunk r2048

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
private Mutex dir_cache_lock;
22
22
 
23
23
public class GOF.Directory.Async : Object {
 
24
    public delegate void GOFFileLoadedFunc (GOF.File file);
 
25
 
24
26
    public GLib.File location;
25
27
    public GLib.File? selected_file = null;
26
28
    public GOF.File file;
75
77
    private string scheme;
76
78
    public bool is_local;
77
79
    public bool is_trash;
 
80
    public bool is_network;
78
81
    public bool is_recent;
79
82
    public bool has_mounts;
80
83
    public bool has_trash_dirs;
81
84
    public bool can_load;
82
 
    private bool _is_ready;
83
 
    public bool is_ready {
84
 
        get { return _is_ready;}
85
 
        private set {_is_ready = value;}
86
 
    }
 
85
    private bool is_cached = false;
87
86
 
88
87
    public bool is_cancelled {
89
88
        get { return cancellable.is_cancelled (); }
94
93
        file = GOF.File.get (location);
95
94
        cancellable = new Cancellable ();
96
95
        state = State.NOT_LOADED;
97
 
        is_ready = false;
98
 
        can_load = true;
 
96
        can_load = false;
99
97
 
100
98
        scheme = location.get_uri_scheme ();
101
99
        is_trash = (scheme == "trash");
102
100
        is_recent = (scheme == "recent");
103
101
        is_local = is_trash || is_recent || (scheme == "file");
104
 
 
105
 
        if (!prepare_directory ())
106
 
            return;
107
 
 
108
 
        if (is_trash)
109
 
            connect_volume_monitor_signals ();
110
 
 
111
 
        assert (directory_cache != null);
112
 
        directory_cache.insert (location, this);
113
 
 
114
 
        this.add_toggle_ref ((ToggleNotify) toggle_ref_notify);
115
 
        this.unref ();
116
 
 
117
 
        debug ("created dir %s ref_count %u", this.file.uri, this.ref_count);
118
 
        file_hash = new HashTable<GLib.File,GOF.File> (GLib.File.hash, GLib.File.equal);
119
 
        uri_contain_keypath_icons = "/icons" in file.uri || "/.icons" in file.uri;
 
102
        is_network = !is_local && ("ftp ftps afp dav davs".contains (scheme));
120
103
    }
121
104
 
122
105
    ~Async () {
125
108
            disconnect_volume_monitor_signals ();
126
109
    }
127
110
 
 
111
    public void init (GOFFileLoadedFunc? file_loaded_func = null) {
 
112
        if (state == State.LOADING) { /* Could happen reloading multiple windows */
 
113
            return;
 
114
        }
 
115
        state = State.LOADING;
 
116
        cancellable.cancel ();
 
117
        cancellable.reset ();
 
118
        if (file_hash != null && file_hash.size () > 0) { /* false on first visit or when reloading */
 
119
            list_cached_files (file_loaded_func); /* will call make ready when done */
 
120
        } else if (!prepare_directory (file_loaded_func)) { /* Returns true if has already called make_ready () or will do so in a callback */
 
121
            make_ready (false);
 
122
        }
 
123
        /* Otherwise the directory will be prepared and the done_loaded signal emitted when ready */
 
124
    }
 
125
 
128
126
    /* This is also called when reloading the directory so that another attempt to connect to
129
127
     * the network is made
130
128
     */
131
 
    private bool prepare_directory () {
132
 
        if (!get_file_info ()) {
133
 
            is_ready = true;
134
 
            /* local uris are deemed loadable even if they do not exist
135
 
             * If they do not exist an opportunity will be given to create them
136
 
             */
137
 
            can_load = is_local;
138
 
            return false;
139
 
        }
140
 
 
141
 
        if (!file.is_folder () && !file.is_root_network_folder () && !try_parent ()) {
142
 
            is_ready = true;
143
 
            can_load = false;
144
 
            return false;
 
129
    private bool prepare_directory (GOFFileLoadedFunc? file_loaded_func) {
 
130
        if (!get_file_info (file_loaded_func)) {
 
131
            return false;
 
132
        } else if (is_local && !file.is_folder ()) {
 
133
            if (!can_try_parent ()) {
 
134
                return false;
 
135
            } else {
 
136
                return get_file_info (file_loaded_func);
 
137
            }
145
138
        }
146
139
        return true;
147
140
    }
148
141
 
149
 
    private bool try_parent () {
 
142
    private bool can_try_parent () {
150
143
        if (file.is_connected) {
151
144
            GLib.File? parent = location.get_parent ();
152
145
            if (parent != null) {
153
146
                file = GOF.File.get (parent);
154
147
                selected_file = location.dup ();
155
148
                location = parent;
156
 
                if (get_file_info ())
157
 
                    return true;
 
149
                return true;
158
150
            }
159
151
        }
160
152
        return false;
161
153
    }
162
154
 
163
 
    private bool get_file_info () {
 
155
    private bool get_file_info (GOFFileLoadedFunc? file_loaded_func) {
164
156
        if (!is_local && !check_network ()) {
165
157
            return false;
166
158
        }
168
160
         * that did not ensure the correct info Aync purposes, and retrieved from cache (bug 1511307).
169
161
         */
170
162
        file.info = null; 
171
 
        if (!file.ensure_query_info()) {
172
 
            if (is_local || !file.is_connected)
 
163
        if (!file.ensure_query_info()) { /* should set file.exists and file.connected appropriately */
 
164
            if (is_local || !file.is_connected || !file.exists) {
173
165
                return false;
 
166
            }
174
167
        }
175
168
 
176
 
        can_load = true;
177
169
        if (!is_local) {
178
170
            mount_mountable.begin ((obj,res) => {
179
 
                can_load = false;
 
171
                bool success = false;
180
172
                try {
181
173
                    mount_mountable.end (res);
182
 
                    can_load = true;
 
174
                    success = true;
183
175
                } catch (Error e) {
184
 
                    debug ("mount_mountable failed: %s", e.message);
185
 
 
186
176
                    if (e is IOError.ALREADY_MOUNTED) {
187
 
                        can_load = true;
188
 
                    } else if (e is IOError.PERMISSION_DENIED ||
189
 
                               e is IOError.FAILED_HANDLED) {
 
177
                        success = true;
 
178
                    } else {
 
179
                        warning ("mount_mountable failed: %s", e.message);
 
180
                        if (e is IOError.PERMISSION_DENIED ||
 
181
                            e is IOError.FAILED_HANDLED) {
190
182
 
191
 
                        permission_denied = true;
 
183
                            permission_denied = true;
 
184
                        }
192
185
                    }
193
186
                }
194
 
                make_ready ();
 
187
                make_ready (success, file_loaded_func);
195
188
            });
196
 
        } else
197
 
            make_ready ();
198
 
 
 
189
        } else {
 
190
            make_ready (true, file_loaded_func);
 
191
        }
199
192
        return true;
200
193
    }
201
194
 
231
224
        var net_mon = GLib.NetworkMonitor.get_default ();
232
225
        var net_available = net_mon.get_network_available ();
233
226
 
234
 
        if (!net_available) {
 
227
        if (!net_available && is_network) {
235
228
            SocketConnectable? connectable = null;
236
229
            try {
237
230
                connectable = NetworkAddress.parse_uri (file.uri, 21);
240
233
 
241
234
            if (connectable != null) {
242
235
                try {
243
 
                    if (net_mon.can_reach (connectable))
244
 
                        return true;
245
 
                }
246
 
                catch (GLib.Error e) {}
 
236
                    net_mon.can_reach (connectable);
 
237
                }
 
238
                catch (GLib.Error e) {
 
239
                    warning ("Error connecting to connectable %s - %s", file.uri, e.message);
 
240
                   return false;
 
241
                }
247
242
            }
248
243
 
249
 
            return false;
 
244
 
250
245
        }
251
 
 
252
246
        return true;
253
247
    }
254
248
 
255
 
    private void make_ready () {
256
 
        is_ready = true;
257
 
        unowned GLib.List? trash_dirs = null;
258
 
        file.mount = GOF.File.get_mount_at (location);
259
 
 
260
 
        if (file.mount != null) {
261
 
            file.is_mounted = true;
262
 
            trash_dirs = Marlin.FileOperations.get_trash_dirs_for_mount (file.mount);
263
 
            has_trash_dirs = (trash_dirs != null);
264
 
        } else
265
 
            has_trash_dirs = is_local;
 
249
    private void make_ready (bool ready, GOFFileLoadedFunc? file_loaded_func = null) {
 
250
        can_load = ready;
 
251
        if (!can_load) {
 
252
            done_loading ();
 
253
            return;
 
254
        } else if (!is_cached) {
 
255
            assert (directory_cache != null);
 
256
            directory_cache.insert (location, this);
 
257
 
 
258
            this.add_toggle_ref ((ToggleNotify) toggle_ref_notify);
 
259
            this.unref ();
 
260
 
 
261
            debug ("created dir %s ref_count %u", this.file.uri, this.ref_count);
 
262
            file_hash = new HashTable<GLib.File,GOF.File> (GLib.File.hash, GLib.File.equal);
 
263
            uri_contain_keypath_icons = "/icons" in file.uri || "/.icons" in file.uri;
 
264
 
 
265
            try {
 
266
                monitor = location.monitor_directory (0);
 
267
                monitor.rate_limit = 100;
 
268
                monitor.changed.connect (directory_changed);
 
269
            } catch (IOError e) {
 
270
                if (!(e is IOError.NOT_MOUNTED)) {
 
271
                    /* Will fail for remote filesystems - not an error */
 
272
                    debug ("directory monitor failed: %s %s", e.message, file.uri);
 
273
                }
 
274
            }
 
275
 
 
276
            set_confirm_trash ();
 
277
            file.mount = GOF.File.get_mount_at (location);
 
278
            if (file.mount != null) {
 
279
                file.is_mounted = true;
 
280
                unowned GLib.List? trash_dirs = null;
 
281
                trash_dirs = Marlin.FileOperations.get_trash_dirs_for_mount (file.mount);
 
282
                has_trash_dirs = (trash_dirs != null);
 
283
            } else {
 
284
                has_trash_dirs = is_local;
 
285
            }
 
286
 
 
287
            if (is_trash) {
 
288
                connect_volume_monitor_signals ();
 
289
            }
 
290
 
 
291
            is_cached = true;
 
292
        }
 
293
        /* May be loading for the first time or reloading after clearing directory info */
 
294
        load (file_loaded_func);
266
295
    }
267
296
 
268
297
    private static void toggle_ref_notify (void* data, Object object, bool is_last) {
280
309
 
281
310
    public void cancel () {
282
311
        cancellable.cancel ();
 
312
        cancel_thumbnailing ();
 
313
    }
283
314
 
 
315
    public void cancel_thumbnailing () {
284
316
        /* remove any pending thumbnail generation */
285
317
        if (timeout_thumbsq != 0) {
286
318
            Source.remove (timeout_thumbsq);
288
320
        }
289
321
    }
290
322
 
 
323
    /** Called in preparation for a reload **/
291
324
    public void clear_directory_info () {
 
325
        if (state != State.LOADED) { /* Could get called multiple times if multiple windows reload the same directory */
 
326
            return;
 
327
        }
292
328
        cancel ();
293
329
 
294
330
        if (idle_consume_changes_id != 0) {
305
341
        state = State.NOT_LOADED;
306
342
    }
307
343
 
308
 
    public delegate void GOFFileLoadedFunc (GOF.File file);
309
 
 
310
344
    /** Views call the following function with null parameter - file_loaded and done_loading
311
345
      * signals are emitted and cause the view and view container to update.
312
346
      *
314
348
      * to perform filename completion.- Emitting a done_loaded signal in that case would cause
315
349
      * the premature ending of text entry.
316
350
     **/
317
 
    public void load (GOFFileLoadedFunc? file_loaded_func = null) {
318
 
        cancellable.reset ();
 
351
    private void load (GOFFileLoadedFunc? file_loaded_func = null) {
 
352
        /* Should only be called after creation and if reloaded */
 
353
        if (!is_cached || file_hash != null && file_hash.size () > 0) {
 
354
            critical ("(Re)load directory called when not cleared");
 
355
            return;
 
356
        }
 
357
        if (!can_load) {
 
358
            warning ("load called when cannot load - not expected to happen");
 
359
            after_loading (file_loaded_func);
 
360
            return;
 
361
        }
 
362
 
 
363
        if (state != State.LOADING) {
 
364
            warning ("load called in loaded or loading state - not expected to happen");
 
365
            return;
 
366
        }
 
367
 
319
368
        longest_file_name = "";
320
369
        permission_denied = false;
321
 
        if (state == State.LOADING)
 
370
 
 
371
        list_directory.begin (file_loaded_func);
 
372
    }
 
373
 
 
374
    private void list_cached_files (GOFFileLoadedFunc? file_loaded_func = null) {
 
375
        if (state == State.NOT_LOADED) {
 
376
            warning ("list cached files called in unloaded state - not expected to happen");
322
377
            return;
323
 
 
324
 
        if (state != State.LOADED) {
325
 
            set_confirm_trash ();
326
 
            list_directory.begin (file_loaded_func);
 
378
        }
 
379
        bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
 
380
        foreach (GOF.File gof in file_hash.get_values ()) {
 
381
            if (gof != null) {
 
382
                after_load_file (gof, show_hidden, file_loaded_func);
 
383
            }
 
384
        }
 
385
        after_loading (file_loaded_func);
 
386
    }
 
387
 
 
388
    private async void list_directory (GOFFileLoadedFunc? file_loaded_func) {
 
389
        try {
 
390
            bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
 
391
            var e = yield this.location.enumerate_children_async (gio_attrs, 0, 0, cancellable);
 
392
            while (state == State.LOADING) {
 
393
                var files = yield e.next_files_async (200, 0, cancellable);
 
394
                if (files == null) {
 
395
                    state = State.LOADED;
 
396
                } else {
 
397
                    foreach (var file_info in files) {
 
398
                        GLib.File loc = location.get_child (file_info.get_name ());
 
399
                        GOF.File? gof = GOF.File.cache_lookup (loc);
 
400
 
 
401
                        if (gof == null)
 
402
                            gof = new GOF.File (loc, location);
 
403
 
 
404
                        gof.info = file_info;
 
405
                        gof.update ();
 
406
 
 
407
                        file_hash.insert (gof.location, gof);
 
408
 
 
409
                        after_load_file (gof, show_hidden, file_loaded_func);
 
410
 
 
411
                        files_count++;
 
412
                    }
 
413
                }
 
414
            }
 
415
        } catch (Error err) {
 
416
            warning ("Listing directory error: %s %s", err.message, file.uri);
 
417
 
 
418
            if (err is IOError.NOT_FOUND || err is IOError.NOT_DIRECTORY) {
 
419
                file.exists = false;
 
420
            } else if (err is IOError.PERMISSION_DENIED)
 
421
                permission_denied = true;
 
422
            else if (err is IOError.NOT_MOUNTED)
 
423
                file.is_mounted = false;
 
424
        }
 
425
        after_loading (file_loaded_func);
 
426
    }
 
427
 
 
428
    private void after_load_file (GOF.File gof, bool show_hidden, GOFFileLoadedFunc? file_loaded_func) {
 
429
        if (!gof.is_hidden || show_hidden) {
 
430
            if (track_longest_name)
 
431
                update_longest_file_name (gof);
327
432
 
328
433
            if (file_loaded_func == null) {
329
 
                try {
330
 
                    monitor = location.monitor_directory (0);
331
 
                    monitor.rate_limit = 100;
332
 
                    monitor.changed.connect (directory_changed);
333
 
                } catch (IOError e) {
334
 
                    if (!(e is IOError.NOT_MOUNTED)) {
335
 
                        /* Will fail for remote filesystems - not an error */
336
 
                        debug ("directory monitor failed: %s %s", e.message, file.uri);
337
 
                    }
338
 
                }
339
 
            }
340
 
        } else {
341
 
            /* even if the directory is currently loading model_add_file manage duplicates */
342
 
            debug ("directory %s load cached files", file.uri);
343
 
 
344
 
            bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
345
 
 
346
 
            foreach (GOF.File gof in file_hash.get_values ()) {
347
 
                if (gof != null) {
348
 
                    if (gof.info != null && (!gof.is_hidden || show_hidden)) {
349
 
                        if (track_longest_name)
350
 
                            update_longest_file_name (gof);
351
 
 
352
 
                        if (file_loaded_func == null)
353
 
                            file_loaded (gof);
354
 
                        else
355
 
                            file_loaded_func (gof);
356
 
                    }
357
 
                }
358
 
            }
359
 
 
360
 
            if (file_loaded_func == null && !cancellable.is_cancelled ())
361
 
                done_loading ();
362
 
        }
 
434
                file_loaded (gof);
 
435
            } else
 
436
                file_loaded_func (gof);
 
437
        }
 
438
    }
 
439
 
 
440
    private void after_loading (GOFFileLoadedFunc? file_loaded_func) {
 
441
        if (file_loaded_func == null && !cancellable.is_cancelled ()) {
 
442
            done_loading ();
 
443
        }
 
444
        state = State.LOADED;
363
445
    }
364
446
 
365
447
    public void block_monitor () {
384
466
    }
385
467
 
386
468
    public void load_hiddens () {
387
 
        if (!can_load)
 
469
        if (!can_load) {
388
470
            return;
389
 
 
 
471
        }
390
472
        if (state != State.LOADED) {
391
473
            load ();
392
474
        } else {
393
 
            foreach (GOF.File gof in file_hash.get_values ()) {
394
 
                if (gof != null && gof.is_hidden) {
395
 
                    if (track_longest_name)
396
 
                        update_longest_file_name (gof);
397
 
 
398
 
                    file_loaded (gof);
399
 
                }
400
 
            }
 
475
            list_cached_files ();
401
476
        }
402
 
 
403
 
        done_loading ();
404
477
    }
 
478
 
405
479
    public void update_files () {
406
480
        foreach (GOF.File gof in file_hash.get_values ()) {
407
481
            if (gof != null && gof.info != null
427
501
        yield location.mount_enclosing_volume (0, mount_op, cancellable);
428
502
    }
429
503
 
430
 
    private async void list_directory (GOFFileLoadedFunc? file_loaded_func = null) {
431
 
        if (!can_load) {
432
 
            state = State.NOT_LOADED;
433
 
            return;
434
 
        }
435
 
 
436
 
        file.exists = true;
437
 
        files_count = 0;
438
 
        state = State.LOADING;
439
 
        try {
440
 
            var e = yield this.location.enumerate_children_async (gio_attrs, 0, 0, cancellable);
441
 
            while (state == State.LOADING) {
442
 
                var files = yield e.next_files_async (200, 0, cancellable);
443
 
                if (files == null)
444
 
                    break;
445
 
 
446
 
                bool show_hidden = is_trash || Preferences.get_default ().pref_show_hidden_files;
447
 
 
448
 
                foreach (var file_info in files) {
449
 
                    GLib.File loc = location.get_child (file_info.get_name ());
450
 
                    GOF.File? gof = GOF.File.cache_lookup (loc);
451
 
 
452
 
                    if (gof == null)
453
 
                        gof = new GOF.File (loc, location);
454
 
 
455
 
                    gof.info = file_info;
456
 
                    gof.update ();
457
 
 
458
 
                    file_hash.insert (gof.location, gof);
459
 
 
460
 
                    if (!gof.is_hidden || show_hidden) {
461
 
                        if (track_longest_name)
462
 
                            update_longest_file_name (gof);
463
 
 
464
 
                        if (file_loaded_func == null)
465
 
                            file_loaded (gof);
466
 
                        else
467
 
                            file_loaded_func (gof);
468
 
                    }
469
 
 
470
 
                    files_count++;
471
 
                }
472
 
            }
473
 
 
474
 
            if (state == State.LOADING) {
475
 
                file.exists = true;
476
 
                state = State.LOADED;
477
 
            } else {
478
 
                warning ("WARNING load() has been called again before LOADING finished");
479
 
                return;
480
 
            }
481
 
        } catch (Error err) {
482
 
            warning ("Listing directory error: %s %s", err.message, file.uri);
483
 
            state = State.NOT_LOADED;
484
 
 
485
 
            if (err is IOError.NOT_FOUND || err is IOError.NOT_DIRECTORY)
486
 
                file.exists = false;
487
 
            else if (err is IOError.PERMISSION_DENIED)
488
 
                permission_denied = true;
489
 
            else if (err is IOError.NOT_MOUNTED)
490
 
                file.is_mounted = false;
491
 
        }
492
 
 
493
 
        if (file_loaded_func == null && !cancellable.is_cancelled ())
494
 
            done_loading ();
495
 
    }
496
 
 
497
504
    public GOF.File? file_hash_lookup_location (GLib.File? location) {
498
505
        if (location != null && location is GLib.File) {
499
506
            GOF.File? result = file_hash.lookup (location);
679
686
 
680
687
                    if (tln) {
681
688
                        track_longest_name = true;
682
 
                        load ();
 
689
                        list_cached_files ();
683
690
                    }
684
691
                }
685
692
            }
740
747
        }
741
748
 
742
749
        foreach (var d in dirs) {
743
 
            if (d.track_longest_name)
744
 
                d.load ();
 
750
            if (d.track_longest_name) {
 
751
                d.list_cached_files ();
 
752
            }
745
753
        }
746
754
    }
747
755