~elementary-apps/pantheon-files/trunk

« back to all changes in this revision

Viewing changes to src/View/AbstractDirectoryView.vala

merge various-fixes-part4-fix-network-drag-drop-copy-paste r2049

Show diffs side-by-side

added added

removed removed

Lines of Context:
170
170
 
171
171
        /* Rename support */
172
172
        protected Marlin.TextRenderer? name_renderer = null;
173
 
        unowned Marlin.AbstractEditableLabel? editable_widget = null;
174
173
        public string original_name = "";
175
174
        public string proposed_name = "";
176
175
 
378
377
                });
379
378
            }
380
379
        }
381
 
 
382
380
        public void select_glib_files (GLib.List<GLib.File> location_list, GLib.File? focus_location) {
383
381
            unselect_all ();
384
382
            GLib.List<GOF.File>? file_list = null;
441
439
            clipboard.changed.disconnect (on_clipboard_changed);
442
440
            view.enter_notify_event.disconnect (on_enter_notify_event);
443
441
            view.key_press_event.disconnect (on_view_key_press_event);
 
442
            slot.directory.block_monitor ();
444
443
        }
445
444
 
446
445
        protected void unfreeze_updates () {
451
450
            clipboard.changed.connect (on_clipboard_changed);
452
451
            view.enter_notify_event.connect (on_enter_notify_event);
453
452
            view.key_press_event.connect (on_view_key_press_event);
 
453
            slot.directory.unblock_monitor ();
454
454
        }
455
455
 
456
456
        public new void grab_focus () {
570
570
            set_cursor (path, false, true, false);
571
571
        }
572
572
 
 
573
        protected void select_and_scroll_to_gof_file (GOF.File file) {
 
574
            var iter = Gtk.TreeIter ();
 
575
            if (!model.get_first_iter_for_file (file, out iter))
 
576
                return; /* file not in model */
 
577
 
 
578
            var path = model.get_path (iter);
 
579
            set_cursor (path, false, true, true);
 
580
        }
 
581
 
573
582
        protected void add_gof_file_to_selection (GOF.File file) {
574
583
            var iter = Gtk.TreeIter ();
575
584
 
879
888
            Marlin.FileOperations.new_folder (null, null, slot.location, (Marlin.CreateCallback?) create_file_done, this);
880
889
        }
881
890
 
 
891
        private void after_new_file_added (GOF.File? file) {
 
892
            slot.directory.file_added.disconnect (after_new_file_added);
 
893
            if (file != null) {
 
894
                rename_file (file);
 
895
            }
 
896
        }
 
897
 
882
898
        protected void rename_file (GOF.File file_to_rename) {
883
 
            var iter = Gtk.TreeIter ();
884
 
            uint count = 0;
885
 
            /* Allow time for the file to appear in the tree model before renaming
886
 
             */
887
 
            GLib.Timeout.add (10, () => {
888
 
                if (model.get_first_iter_for_file (file_to_rename, out iter)) {
889
 
                    /* Assume writability on remote locations */
890
 
                    /**TODO** Reliably determine writability with various remote protocols.*/
891
 
                    if (is_writable || !slot.directory.is_local)
892
 
                        start_renaming_file (file_to_rename, false);
893
 
                    else
894
 
                        warning ("You do not have permission to rename this file");
895
 
                } else if (count < 100) {
896
 
                    /* Guard against possible infinite loop */
897
 
                    count++;
898
 
                    return true;
899
 
                }
900
 
 
901
 
                return false;
902
 
            });
 
899
            if (renaming) {
 
900
                warning ("already renaming %s", file_to_rename.basename);
 
901
                return;
 
902
            }
 
903
            /* Assume writability on remote locations */
 
904
            /**TODO** Reliably determine writability with various remote protocols.*/
 
905
            if (is_writable || !slot.directory.is_local) {
 
906
                start_renaming_file (file_to_rename);
 
907
            } else {
 
908
                warning ("You do not have permission to rename this file");
 
909
            }
903
910
        }
904
911
 
905
 
 
906
912
/** File operation callbacks */
907
913
        static void create_file_done (GLib.File? new_file, void* data) {
908
914
            var view = data as FM.AbstractDirectoryView;
916
922
                warning ("View invalid after creating file");
917
923
                return;
918
924
            }
919
 
 
920
 
            var file_to_rename = GOF.File.@get (new_file);
921
 
            view.rename_file (file_to_rename); /* will wait for file to appear in model */
 
925
            /* Start to rename the file once we get signal that it has been added to model */
 
926
            view.slot.directory.file_added.connect_after (view.after_new_file_added);
922
927
        }
923
928
 
924
929
        /** Must pass a pointer to an instance of FM.AbstractDirectoryView as 3rd parameter when
1247
1252
            }
1248
1253
        }
1249
1254
 
1250
 
        private void on_directory_file_added (GOF.Directory.Async dir, GOF.File file) {
1251
 
            add_file (file, dir);
 
1255
        private void on_directory_file_added (GOF.Directory.Async dir, GOF.File? file) {
 
1256
            if (file != null) {
 
1257
                add_file (file, dir);
 
1258
            }
1252
1259
        }
1253
1260
 
1254
1261
        private void on_directory_file_loaded (GOF.Directory.Async dir, GOF.File file) {
2395
2402
 
2396
2403
        protected void block_model () {
2397
2404
            model.row_deleted.disconnect (on_row_deleted);
2398
 
            updates_frozen = true;
2399
2405
        }
2400
2406
 
2401
2407
        protected void unblock_model () {
2402
2408
            model.row_deleted.connect (on_row_deleted);
2403
 
            updates_frozen = false;
2404
2409
        }
2405
2410
 
2406
2411
        private void load_thumbnails (GOF.Directory.Async dir, Marlin.ZoomLevel zoom) {
2778
2783
        }
2779
2784
 
2780
2785
    /** name renderer signals */
2781
 
        protected void on_name_editing_started (Gtk.CellEditable? editable, string path) {
2782
 
            if (renaming)
 
2786
        protected void on_name_editing_started (Gtk.CellEditable? editable, string path_string) {
 
2787
            if (renaming) {
 
2788
                warning ("on_name_edited re-entered");
2783
2789
                return;
2784
 
 
 
2790
            }
2785
2791
            renaming = true;
2786
 
            freeze_updates ();
2787
 
            editable_widget = editable as Marlin.AbstractEditableLabel;
2788
 
            original_name = editable_widget.get_text ().dup ();
 
2792
 
 
2793
            var editable_widget = editable as Gtk.Editable?;
 
2794
            if (editable_widget != null) {
 
2795
                original_name = editable_widget.get_chars (0, -1);
 
2796
                var path = new Gtk.TreePath.from_string (path_string);
 
2797
                Gtk.TreeIter? iter = null;
 
2798
                model.get_iter (out iter, path);
 
2799
                GOF.File? file = null;
 
2800
                model.@get (iter, FM.ListModel.ColumnID.FILE_COLUMN, out file);
 
2801
                int start_offset= 0, end_offset = -1;
 
2802
                /* Select whole name if the file is a folder, otherwise do not select the extension */
 
2803
                if (!file.is_folder ()) {
 
2804
                    Marlin.get_rename_region (original_name, out start_offset, out end_offset, false);
 
2805
                }
 
2806
                editable_widget.select_region (start_offset, end_offset);
 
2807
            } else {
 
2808
                warning ("Editable widget is null");
 
2809
                on_name_editing_canceled ();
 
2810
            }
2789
2811
        }
2790
2812
 
2791
2813
        protected void on_name_editing_canceled () {
2792
 
            if (!renaming)
2793
 
                return;
2794
 
 
2795
2814
            renaming = false;
2796
2815
            name_renderer.editable = false;
 
2816
            proposed_name = "";
2797
2817
            unfreeze_updates ();
2798
2818
            grab_focus ();
2799
2819
        }
2800
2820
 
2801
2821
        protected void on_name_edited (string path_string, string new_name) {
2802
 
            if (!renaming)
 
2822
            /* Must not re-enter */
 
2823
            if (!renaming || proposed_name == new_name) {
 
2824
                warning ("on_name_edited re-entered");
2803
2825
                return;
2804
 
 
 
2826
            }
 
2827
            proposed_name = "";
2805
2828
            if (new_name != "") {
2806
2829
                var path = new Gtk.TreePath.from_string (path_string);
2807
2830
                Gtk.TreeIter? iter = null;
2811
2834
                model.@get (iter, FM.ListModel.ColumnID.FILE_COLUMN, out file);
2812
2835
 
2813
2836
                /* Only rename if name actually changed */
2814
 
                original_name = file.info.get_name ();
 
2837
                /* Because GOF.File.rename does not work correctly for remote files we handle ourselves */
 
2838
 
2815
2839
                if (new_name != original_name) {
2816
2840
                    proposed_name = new_name;
2817
 
                    file.rename (new_name, (GOF.FileOperationCallback)rename_callback, (void*)this);
 
2841
                    set_file_display_name (file.location, new_name, after_rename);
 
2842
                } else {
 
2843
                    warning ("Name unchanged");
 
2844
                    on_name_editing_canceled ();
2818
2845
                }
 
2846
            } else {
 
2847
                warning ("No new name");
 
2848
                on_name_editing_canceled ();
2819
2849
            }
2820
 
 
 
2850
            /* do not cancel editing here - will be cancelled in rename callback */
 
2851
        }
 
2852
 
 
2853
        public void set_file_display_name (GLib.File old_location, string new_name, PF.FileUtils.RenameCallbackFunc? f) {
 
2854
            /* Wait for the file to be added to the model before trying to select and scroll to it */
 
2855
            slot.directory.file_added.connect_after (after_renamed_file_added);
 
2856
            PF.FileUtils.set_file_display_name (old_location, new_name, f);
 
2857
        }
 
2858
 
 
2859
        public void after_rename (GLib.File file, GLib.File? result_location, GLib.Error? e) {
2821
2860
            on_name_editing_canceled ();
2822
 
        }
2823
 
 
2824
 
 
2825
 
        public static void rename_callback (GOF.File file, GLib.File? result_location, GLib.Error error, void* data) {
2826
 
            FM.AbstractDirectoryView? view = null;
2827
 
            Marlin.View.PropertiesWindow? pw = null;
2828
 
 
2829
 
            var object = (GLib.Object)data;
2830
 
 
2831
 
            if (object is FM.AbstractDirectoryView)
2832
 
                view = (FM.AbstractDirectoryView)data;
2833
 
            else if (object is Marlin.View.PropertiesWindow) {
2834
 
                pw = (Marlin.View.PropertiesWindow)object;
2835
 
                view = pw.view;
2836
 
            }
2837
 
 
2838
 
            assert (view != null);
2839
 
 
2840
 
            if (error != null) {
2841
 
                Eel.show_error_dialog (_("Could not rename to '%s'").printf (view.proposed_name),
2842
 
                                       error.message,
2843
 
                                       view.window as Gtk.Window);
2844
 
            } else {
2845
 
                Marlin.UndoManager.instance ().add_rename_action (file.location,
2846
 
                                                                  view.original_name);
2847
 
 
2848
 
                view.select_gof_file (file);  /* Select and scroll to show renamed file */
2849
 
            }
2850
 
 
2851
 
            if (pw != null) {
2852
 
                if (error == null)
2853
 
                    pw.reset_entry_text (file.info.get_name ());
2854
 
                else
2855
 
                    pw.reset_entry_text ();  //resets entry to old name
2856
 
            }
2857
2861
         }
2858
2862
 
 
2863
        private void after_renamed_file_added (GOF.File? new_file) {
 
2864
            slot.directory.file_added.disconnect (after_renamed_file_added);
 
2865
            /* new_file will be null if rename failed */
 
2866
            if (new_file != null) {
 
2867
                select_and_scroll_to_gof_file (new_file);
 
2868
            }
 
2869
        }
 
2870
 
2859
2871
        public virtual bool on_view_draw (Cairo.Context cr) {
2860
2872
            /* If folder is empty, draw the empty message in the middle of the view
2861
2873
             * otherwise pass on event */
3095
3107
            view.style_updated ();
3096
3108
        }
3097
3109
 
3098
 
        private void start_renaming_file (GOF.File file, bool preselect_whole_name) {
3099
 
            /* Select whole name if we are in renaming mode already */
3100
 
            if (renaming)
 
3110
        private void start_renaming_file (GOF.File file) {
 
3111
            if (updates_frozen) {
 
3112
                warning ("Trying to rename when frozen");
3101
3113
                return;
3102
 
 
 
3114
            }
3103
3115
            Gtk.TreeIter? iter = null;
3104
3116
            if (!model.get_first_iter_for_file (file, out iter)) {
3105
3117
                critical ("Failed to find rename file in model");
3108
3120
 
3109
3121
            /* Freeze updates to the view to prevent losing rename focus when the tree view updates */
3110
3122
            freeze_updates ();
3111
 
 
3112
3123
            Gtk.TreePath path = model.get_path (iter);
3113
3124
 
 
3125
            uint count = 0;
 
3126
            bool ok_next_time = false;
 
3127
            Gtk.TreePath? start_path = null;
3114
3128
            /* Scroll to row to be renamed and then start renaming after a delay
3115
3129
             * so that file to be renamed is on screen.  This avoids the renaming being
3116
3130
             * cancelled */
3117
 
            set_cursor (path, false, true, false);
3118
 
            uint count = 0;
3119
 
            Gtk.TreePath? start_path = null;
3120
 
            bool ok_next_time   = false;
3121
 
 
3122
3131
            GLib.Timeout.add (50, () => {
3123
3132
                /* Wait until view stops scrolling before starting to rename (up to 1 second)
3124
3133
                 * Scrolling is deemed to have stopped when the starting visible path is stable
3137
3146
                    return true;
3138
3147
                }
3139
3148
 
3140
 
                /* set cursor_on_cell also triggers editing-started, where we save the editable widget */
 
3149
                /* set cursor_on_cell also triggers editing-started */
3141
3150
                name_renderer.editable = true;
3142
3151
                set_cursor_on_cell (path, name_renderer as Gtk.CellRenderer, true, false);
3143
 
 
3144
 
                if (editable_widget != null) {
3145
 
                    int start_offset= 0, end_offset = -1;
3146
 
                    if (!file.is_folder ())
3147
 
                        Marlin.get_rename_region (original_name, out start_offset, out end_offset, preselect_whole_name);
3148
 
 
3149
 
                    editable_widget.select_region (start_offset, end_offset);
3150
 
                } else {
3151
 
                    warning ("Editable widget is null");
3152
 
                    on_name_editing_canceled ();
3153
 
                }
3154
3152
                return false;
3155
3153
            });
3156
3154