Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve file move and copy operations #75330

Merged
merged 1 commit into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions editor/editor_dir_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@

#include "editor_dir_dialog.h"

#include "core/io/dir_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "editor/editor_file_system.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "scene/gui/check_box.h"
#include "scene/gui/tree.h"
#include "servers/display_server.h"

void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path) {
Expand All @@ -56,8 +59,6 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p
p_item->set_text(0, p_dir->get_name());
}

//this should be handled by EditorFileSystem already
//bool show_hidden = EDITOR_GET("filesystem/file_dialog/show_hidden_files");
updating = false;
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
TreeItem *ti = tree->create_item(p_item);
Expand All @@ -78,6 +79,10 @@ void EditorDirDialog::reload(const String &p_path) {
must_reload = false;
}

bool EditorDirDialog::is_copy_pressed() const {
return copy->is_pressed();
}

void EditorDirDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
Expand Down Expand Up @@ -107,6 +112,14 @@ void EditorDirDialog::_notification(int p_what) {
}
}

void EditorDirDialog::_copy_toggled(bool p_pressed) {
if (p_pressed) {
set_ok_button_text(TTR("Copy"));
} else {
set_ok_button_text(TTR("Move"));
}
}

void EditorDirDialog::_item_collapsed(Object *p_item) {
TreeItem *item = Object::cast_to<TreeItem>(p_item);

Expand Down Expand Up @@ -172,8 +185,7 @@ void EditorDirDialog::_make_dir_confirm() {
mkdirerr->popup_centered(Size2(250, 80) * EDSCALE);
} else {
opened_paths.insert(dir);
//reload(dir.path_join(makedirname->get_text()));
EditorFileSystem::get_singleton()->scan_changes(); //we created a dir, so rescan changes
EditorFileSystem::get_singleton()->scan_changes(); // We created a dir, so rescan changes.
}
makedirname->set_text(""); // reset label
}
Expand All @@ -186,11 +198,19 @@ EditorDirDialog::EditorDirDialog() {
set_title(TTR("Choose a Directory"));
set_hide_on_ok(false);

tree = memnew(Tree);
add_child(tree);
VBoxContainer *vb = memnew(VBoxContainer);
add_child(vb);

tree = memnew(Tree);
vb->add_child(tree);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated));

copy = memnew(CheckBox);
vb->add_child(copy);
copy->set_text(TTR("Copy File(s)"));
copy->connect("toggled", callable_mp(this, &EditorDirDialog::_copy_toggled));

makedir = add_button(TTR("Create Folder"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "makedir");
makedir->connect("pressed", callable_mp(this, &EditorDirDialog::_make_dir));

Expand All @@ -200,7 +220,6 @@ EditorDirDialog::EditorDirDialog() {

VBoxContainer *makevb = memnew(VBoxContainer);
makedialog->add_child(makevb);
//makedialog->set_child_rect(makevb);

makedirname = memnew(LineEdit);
makevb->add_margin_child(TTR("Name:"), makedirname);
Expand All @@ -211,5 +230,5 @@ EditorDirDialog::EditorDirDialog() {
mkdirerr->set_text(TTR("Could not create folder."));
add_child(mkdirerr);

set_ok_button_text(TTR("Choose"));
set_ok_button_text(TTR("Move"));
}
12 changes: 9 additions & 3 deletions editor/editor_dir_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
#ifndef EDITOR_DIR_DIALOG_H
#define EDITOR_DIR_DIALOG_H

#include "core/io/dir_access.h"
#include "editor/editor_file_system.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"

class CheckBox;
class EditorFileSystemDirectory;
class Tree;
class TreeItem;

class EditorDirDialog : public ConfirmationDialog {
GDCLASS(EditorDirDialog, ConfirmationDialog);
Expand All @@ -48,7 +50,9 @@ class EditorDirDialog : public ConfirmationDialog {

Tree *tree = nullptr;
bool updating = false;
CheckBox *copy = nullptr;

void _copy_toggled(bool p_pressed);
void _item_collapsed(Object *p_item);
void _item_activated();
void _update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path = String());
Expand All @@ -66,6 +70,8 @@ class EditorDirDialog : public ConfirmationDialog {

public:
void reload(const String &p_path = "");
bool is_copy_pressed() const;

EditorDirDialog();
};

Expand Down
160 changes: 99 additions & 61 deletions editor/filesystem_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,29 @@ void FileSystemDock::_update_project_settings_after_move(const HashMap<String, S
ProjectSettings::get_singleton()->save();
}

String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const String &p_at_path) {
String new_path;
String new_path_base;

if (p_entry.is_file) {
new_path = p_at_path.path_join(p_entry.path.get_file());
new_path_base = new_path.get_basename() + " (%d)." + new_path.get_extension();
} else {
PackedStringArray path_split = p_entry.path.split("/");
new_path = p_at_path.path_join(path_split[path_split.size() - 2]);
new_path_base = new_path + " (%d)";
}

int exist_counter = 1;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
while (da->file_exists(new_path) || da->dir_exists(new_path)) {
exist_counter++;
new_path = vformat(new_path_base, exist_counter);
}

return new_path;
}

void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const {
Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites();
Vector<String> new_favorites;
Expand Down Expand Up @@ -1658,8 +1681,9 @@ void FileSystemDock::_duplicate_operation_confirm() {
_rescan();
}

void FileSystemDock::_move_with_overwrite() {
_move_operation_confirm(to_move_path, true);
void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) {
overwrite_dialog->hide();
_move_operation_confirm(to_move_path, to_move_or_copy, p_overwrite ? OVERWRITE_REPLACE : OVERWRITE_RENAME);
}

Vector<String> FileSystemDock::_check_existing() {
Expand All @@ -1681,58 +1705,93 @@ Vector<String> FileSystemDock::_check_existing() {
return conflicting_items;
}

void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_overwrite) {
if (!p_overwrite) {
void FileSystemDock::_move_dialog_confirm(const String &p_path) {
_move_operation_confirm(p_path, move_dialog->is_copy_pressed());
}

void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_copy, Overwrite p_overwrite) {
if (p_overwrite == OVERWRITE_UNDECIDED) {
to_move_path = p_to_path;
to_move_or_copy = p_copy;

Vector<String> conflicting_items = _check_existing();
if (!conflicting_items.is_empty()) {
// Ask to do something.
overwrite_dialog->set_text(vformat(
TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them?"),
p_copy ? TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them or rename the copied files?")
: TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them or rename the moved files?"),
to_move_path,
String("\n").join(conflicting_items)));
overwrite_dialog->popup_centered();
return;
}
}

// Check groups.
Vector<String> new_paths;
new_paths.resize(to_move.size());
for (int i = 0; i < to_move.size(); i++) {
if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) {
EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, p_to_path.path_join(to_move[i].path.get_file()));
if (p_overwrite == OVERWRITE_RENAME) {
new_paths.write[i] = _get_unique_name(to_move[i], p_to_path);
} else {
new_paths.write[i] = p_to_path.path_join(to_move[i].path.get_file());
}
}

HashMap<String, String> file_renames;
HashMap<String, String> folder_renames;
bool is_moved = false;
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
String new_path = p_to_path.path_join(old_path.get_file());
if (old_path != new_path) {
_try_move_item(to_move[i], new_path, file_renames, folder_renames);
is_moved = true;
if (p_copy) {
bool is_copied = false;
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
const String &new_path = new_paths[i];

if (old_path != new_path) {
_try_duplicate_item(to_move[i], new_paths[i]);
is_copied = true;
}
}
}

if (is_moved) {
int current_tab = EditorNode::get_singleton()->get_current_tab();
_save_scenes_after_move(file_renames); // Save scenes before updating.
_update_dependencies_after_move(file_renames);
_update_resource_paths_after_move(file_renames);
_update_project_settings_after_move(file_renames);
_update_favorites_list_after_move(file_renames, folder_renames);
if (is_copied) {
_rescan();
}
} else {
// Check groups.
for (int i = 0; i < to_move.size(); i++) {
if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) {
EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, new_paths[i]);
}
}

EditorNode::get_singleton()->set_current_tab(current_tab);
bool is_moved = false;
HashMap<String, String> file_renames;
HashMap<String, String> folder_renames;

print_verbose("FileSystem: calling rescan.");
_rescan();
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
const String &new_path = new_paths[i];
if (old_path != new_path) {
_try_move_item(to_move[i], new_path, file_renames, folder_renames);
is_moved = true;
}
}

print_verbose("FileSystem: saving moved scenes.");
_save_scenes_after_move(file_renames);
if (is_moved) {
int current_tab = EditorNode::get_singleton()->get_current_tab();
_save_scenes_after_move(file_renames); // Save scenes before updating.
_update_dependencies_after_move(file_renames);
_update_resource_paths_after_move(file_renames);
_update_project_settings_after_move(file_renames);
_update_favorites_list_after_move(file_renames, folder_renames);

path = p_to_path;
current_path->set_text(path);
EditorNode::get_singleton()->set_current_tab(current_tab);

print_verbose("FileSystem: calling rescan.");
_rescan();

print_verbose("FileSystem: saving moved scenes.");
_save_scenes_after_move(file_renames);

path = p_to_path;
current_path->set_text(path);
}
}
}

Expand Down Expand Up @@ -1950,7 +2009,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;

case FILE_MOVE: {
// Move the files to a given location.
// Move or copy the files to a given location.
to_move.clear();
Vector<String> collapsed_paths = _remove_self_included_paths(p_selected);
for (int i = collapsed_paths.size() - 1; i >= 0; i--) {
Expand All @@ -1960,7 +2019,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
}
}
if (to_move.size() > 0) {
move_dialog->popup_centered_ratio();
move_dialog->popup_centered_ratio(0.4);
}
} break;

Expand Down Expand Up @@ -2425,28 +2484,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
}
if (!to_move.is_empty()) {
if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
for (int i = 0; i < to_move.size(); i++) {
String new_path;
String new_path_base;

if (to_move[i].is_file) {
new_path = to_dir.path_join(to_move[i].path.get_file());
new_path_base = new_path.get_basename() + " (%d)." + new_path.get_extension();
} else {
PackedStringArray path_split = to_move[i].path.split("/");
new_path = to_dir.path_join(path_split[path_split.size() - 2]);
new_path_base = new_path + " (%d)";
}

int exist_counter = 1;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
while (da->file_exists(new_path) || da->dir_exists(new_path)) {
exist_counter++;
new_path = vformat(new_path_base, exist_counter);
}
_try_duplicate_item(to_move[i], new_path);
}
_rescan();
_move_operation_confirm(to_dir, true);
} else {
_move_operation_confirm(to_dir);
}
Expand Down Expand Up @@ -2637,7 +2675,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
}

if (p_paths.size() > 1 || p_paths[0] != "res://") {
p_popup->add_icon_item(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), TTR("Move To..."), FILE_MOVE);
p_popup->add_icon_item(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), TTR("Move/Duplicate To..."), FILE_MOVE);
p_popup->add_icon_shortcut(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/delete"), FILE_REMOVE);
}

Expand Down Expand Up @@ -3278,9 +3316,8 @@ FileSystemDock::FileSystemDock() {
add_child(remove_dialog);

move_dialog = memnew(EditorDirDialog);
move_dialog->set_ok_button_text(TTR("Move"));
add_child(move_dialog);
move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_operation_confirm).bind(false));
move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_dialog_confirm));

rename_dialog = memnew(ConfirmationDialog);
VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
Expand All @@ -3294,9 +3331,10 @@ FileSystemDock::FileSystemDock() {
rename_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_rename_operation_confirm));

overwrite_dialog = memnew(ConfirmationDialog);
overwrite_dialog->set_ok_button_text(TTR("Overwrite"));
add_child(overwrite_dialog);
overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_move_with_overwrite));
overwrite_dialog->set_ok_button_text(TTR("Overwrite"));
overwrite_dialog->add_button(TTR("Keep Both"), true)->connect("pressed", callable_mp(this, &FileSystemDock::_overwrite_dialog_action).bind(false));
overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_overwrite_dialog_action).bind(true));

duplicate_dialog = memnew(ConfirmationDialog);
VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer);
Expand Down
Loading