Browse Source

Rework DependencyRemoveDialog for deleting folders

DependencyRemoveDialog now takes two lists (files and folders) to delete.
Sort the folders above files in DependencyRemoveDialog & use some more icons.
Stop files which will be deleted from also being listed as having broken dependencies.
Add right-click option for removing folder to filesystem folder tree.
MillionOstrich 8 years ago
parent
commit
40d1866b05
4 changed files with 149 additions and 70 deletions
  1. 102 52
      editor/dependency_editor.cpp
  2. 23 4
      editor/dependency_editor.h
  3. 23 14
      editor/filesystem_dock.cpp
  4. 1 0
      editor/filesystem_dock.h

+ 102 - 52
editor/dependency_editor.cpp

@@ -337,92 +337,142 @@ DependencyEditorOwners::DependencyEditorOwners() {
 
 ///////////////////////
 
-void DependencyRemoveDialog::_fill_owners(EditorFileSystemDirectory *efsd) {
+void DependencyRemoveDialog::_find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder) {
+	if (!efsd)
+		return;
+
+	for (int i = 0; i < efsd->get_subdir_count(); ++i) {
+		_find_files_in_removed_folder(efsd->get_subdir(i), p_folder);
+	}
+	for (int i = 0; i < efsd->get_file_count(); i++) {
+		String file = efsd->get_file_path(i);
+		ERR_FAIL_COND(all_remove_files.has(file)); //We are deleting a directory which is contained in a directory we are deleting...
+		all_remove_files[file] = p_folder; //Point the file to the ancestor directory we are deleting so we know what to parent it under in the tree.
+	}
+}
 
+void DependencyRemoveDialog::_find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed) {
 	if (!efsd)
 		return;
 
 	for (int i = 0; i < efsd->get_subdir_count(); i++) {
-		_fill_owners(efsd->get_subdir(i));
+		_find_all_removed_dependencies(efsd->get_subdir(i), p_removed);
 	}
 
 	for (int i = 0; i < efsd->get_file_count(); i++) {
+		const String path = efsd->get_file_path(i);
 
-		Vector<String> deps = efsd->get_file_deps(i);
-		//print_line(":::"+efsd->get_file_path(i));
-		Set<String> met;
-		for (int j = 0; j < deps.size(); j++) {
-			if (files.has(deps[j])) {
-				met.insert(deps[j]);
-			}
-		}
-		if (!met.size())
+		//It doesn't matter if a file we are about to delete will have some of its dependencies removed too
+		if (all_remove_files.has(path))
 			continue;
 
-		exist = true;
-
-		Ref<Texture> icon;
-		String type = efsd->get_file_type(i);
-		if (!has_icon(type, "EditorIcons")) {
-			icon = get_icon("Object", "EditorIcons");
-		} else {
-			icon = get_icon(type, "EditorIcons");
+		Vector<String> all_deps = efsd->get_file_deps(i);
+		for (int j = 0; j < all_deps.size(); ++j) {
+			if (all_remove_files.has(all_deps[j])) {
+				RemovedDependency dep;
+				dep.file = path;
+				dep.file_type = efsd->get_file_type(i);
+				dep.dependency = all_deps[j];
+				dep.dependency_folder = all_remove_files[all_deps[j]];
+				p_removed.push_back(dep);
+			}
 		}
+	}
+}
 
-		for (Set<String>::Element *E = met.front(); E; E = E->next()) {
+void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed) {
+	owners->clear();
+	owners->create_item(); // root
 
-			String which = E->get();
-			if (!files[which]) {
-				TreeItem *ti = owners->create_item(owners->get_root());
-				ti->set_text(0, which.get_file());
-				files[which] = ti;
+	Map<String, TreeItem *> tree_items;
+	for (int i = 0; i < p_removed.size(); i++) {
+		RemovedDependency rd = p_removed[i];
+
+		//Ensure that the dependency is already in the tree
+		if (!tree_items.has(rd.dependency)) {
+			if (rd.dependency_folder.length() > 0) {
+				//Ensure the ancestor folder is already in the tree
+				if (!tree_items.has(rd.dependency_folder)) {
+					TreeItem *folder_item = owners->create_item(owners->get_root());
+					folder_item->set_text(0, rd.dependency_folder);
+					folder_item->set_icon(0, get_icon("Folder", "EditorIcons"));
+					tree_items[rd.dependency_folder] = folder_item;
+				}
+				TreeItem *dependency_item = owners->create_item(tree_items[rd.dependency_folder]);
+				dependency_item->set_text(0, rd.dependency);
+				dependency_item->set_icon(0, get_icon("Warning", "EditorIcons"));
+				tree_items[rd.dependency] = dependency_item;
+			} else {
+				TreeItem *dependency_item = owners->create_item(owners->get_root());
+				dependency_item->set_text(0, rd.dependency);
+				dependency_item->set_icon(0, get_icon("Warning", "EditorIcons"));
+				tree_items[rd.dependency] = dependency_item;
 			}
-			TreeItem *ti = owners->create_item(files[which]);
-			ti->set_text(0, efsd->get_file_path(i));
-			ti->set_icon(0, icon);
 		}
+
+		//List this file under this dependency
+		Ref<Texture> icon = has_icon(rd.file_type, "EditorIcons") ? get_icon(rd.file_type, "EditorIcons") : get_icon("Object", "EditorIcons");
+		TreeItem *file_item = owners->create_item(tree_items[rd.dependency]);
+		file_item->set_text(0, rd.file);
+		file_item->set_icon(0, icon);
 	}
 }
 
-void DependencyRemoveDialog::show(const Vector<String> &to_erase) {
-
-	exist = false;
+void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<String> &p_files) {
+	all_remove_files.clear();
+	to_delete.clear();
 	owners->clear();
-	files.clear();
-	owners->create_item(); // root
-	for (int i = 0; i < to_erase.size(); i++) {
-		files[to_erase[i]] = NULL;
+
+	for (int i = 0; i < p_folders.size(); ++i) {
+		String folder = p_folders[i].ends_with("/") ? p_folders[i] : (p_folders[i] + "/");
+		_find_files_in_removed_folder(EditorFileSystem::get_singleton()->get_filesystem_path(folder), folder);
+		to_delete.push_back(folder);
+	}
+	for (int i = 0; i < p_files.size(); ++i) {
+		all_remove_files[p_files[i]] = String();
+		to_delete.push_back(p_files[i]);
 	}
 
-	_fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
+	Vector<RemovedDependency> removed_deps;
+	_find_all_removed_dependencies(EditorFileSystem::get_singleton()->get_filesystem(), removed_deps);
+	removed_deps.sort();
 
-	if (exist) {
-		owners->show();
-		text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"));
-		popup_centered_minsize(Size2(500, 220));
-	} else {
+	if (removed_deps.empty()) {
 		owners->hide();
 		text->set_text(TTR("Remove selected files from the project? (no undo)"));
 		popup_centered_minsize(Size2(400, 100));
+	} else {
+		_build_removed_dependency_tree(removed_deps);
+		owners->show();
+		text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"));
+		popup_centered_minsize(Size2(500, 350));
 	}
 }
 
 void DependencyRemoveDialog::ok_pressed() {
-
-	bool changed = false;
-
-	for (Map<String, TreeItem *>::Element *E = files.front(); E; E = E->next()) {
-
-		if (ResourceCache::has(E->key())) {
-			Resource *res = ResourceCache::get(E->key());
+	bool files_only = true;
+	for (int i = 0; i < to_delete.size(); ++i) {
+		if (to_delete[i].ends_with("/")) {
+			files_only = false;
+		} else if (ResourceCache::has(to_delete[i])) {
+			Resource *res = ResourceCache::get(to_delete[i]);
 			res->set_path(""); //clear reference to path
 		}
-		String fpath = OS::get_singleton()->get_resource_dir() + E->key().replace_first("res://", "/");
-		OS::get_singleton()->move_to_trash(fpath);
-		changed = true;
+
+		String path = OS::get_singleton()->get_resource_dir() + to_delete[i].replace_first("res://", "/");
+		print_line("Moving to trash: " + path);
+		Error err = OS::get_singleton()->move_to_trash(path);
+		if (err != OK) {
+			EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:\n") + to_delete[i] + "\n");
+		}
 	}
 
-	if (changed) {
+	if (files_only) {
+		//If we only deleted files we should only need to tell the file system about the files we touched.
+		for (int i = 0; i < to_delete.size(); ++i) {
+			EditorFileSystem::get_singleton()->update_file(to_delete[i]);
+		}
+	} else {
 		EditorFileSystem::get_singleton()->scan_changes();
 	}
 }

+ 23 - 4
editor/dependency_editor.h

@@ -84,14 +84,33 @@ class DependencyRemoveDialog : public ConfirmationDialog {
 
 	Label *text;
 	Tree *owners;
-	bool exist;
-	Map<String, TreeItem *> files;
-	void _fill_owners(EditorFileSystemDirectory *efsd);
+
+	Map<String, String> all_remove_files;
+	Vector<String> to_delete;
+
+	struct RemovedDependency {
+		String file;
+		String file_type;
+		String dependency;
+		String dependency_folder;
+
+		bool operator<(const RemovedDependency &p_other) const {
+			if (dependency_folder.empty() != p_other.dependency_folder.empty()) {
+				return p_other.dependency_folder.empty();
+			} else {
+				return dependency < p_other.dependency;
+			}
+		}
+	};
+
+	void _find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder);
+	void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed);
+	void _build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed);
 
 	void ok_pressed();
 
 public:
-	void show(const Vector<String> &to_erase);
+	void show(const Vector<String> &p_folders, const Vector<String> &p_files);
 	DependencyRemoveDialog();
 };
 

+ 23 - 14
editor/filesystem_dock.cpp

@@ -937,26 +937,25 @@ void FileSystemDock::_file_option(int p_option) {
 			rename_dialog_text->grab_focus();
 		} break;
 		case FILE_REMOVE: {
-
-			Vector<String> torem;
+			Vector<String> remove_files;
+			Vector<String> remove_folders;
 
 			for (int i = 0; i < files->get_item_count(); i++) {
-
 				String path = files->get_item_metadata(i);
-				if (!files->is_selected(i))
-					continue;
-				torem.push_back(path);
+				if (files->is_selected(i) && path != "res://") {
+					if (path.ends_with("/")) {
+						remove_folders.push_back(path);
+					} else {
+						remove_files.push_back(path);
+					}
+				}
 			}
 
-			if (torem.empty()) {
-				EditorNode::get_singleton()->show_warning(TTR("No files selected!"));
-				break;
+			if (remove_files.size() + remove_folders.size() > 0) {
+				remove_dialog->show(remove_folders, remove_files);
+				//1) find if used
+				//2) warn
 			}
-
-			remove_dialog->show(torem);
-			//1) find if used
-			//2) warn
-
 		} break;
 		case FILE_INFO: {
 
@@ -1044,6 +1043,15 @@ void FileSystemDock::_folder_option(int p_option) {
 				rename_dialog_text->grab_focus();
 			}
 		} break;
+		case FOLDER_REMOVE: {
+			Vector<String> remove_folders;
+			Vector<String> remove_files;
+			String path = item->get_metadata(tree->get_selected_column());
+			if (path != "res://") {
+				remove_folders.push_back(path);
+				remove_dialog->show(remove_folders, remove_files);
+			}
+		} break;
 		case FOLDER_COPY_PATH: {
 			String path = item->get_metadata(tree->get_selected_column());
 			OS::get_singleton()->set_clipboard(path);
@@ -1099,6 +1107,7 @@ void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) {
 		if (fpath != "res://") {
 			folder_options->add_item(TTR("Rename.."), FOLDER_RENAME);
 			folder_options->add_item(TTR("Move To.."), FOLDER_MOVE);
+			folder_options->add_item(TTR("Delete"), FOLDER_REMOVE);
 		}
 		folder_options->add_separator();
 		folder_options->add_item(TTR("Show In File Manager"), FOLDER_SHOW_IN_EXPLORER);

+ 1 - 0
editor/filesystem_dock.h

@@ -81,6 +81,7 @@ private:
 		FOLDER_COLLAPSE_ALL,
 		FOLDER_MOVE,
 		FOLDER_RENAME,
+		FOLDER_REMOVE,
 		FOLDER_SHOW_IN_EXPLORER,
 		FOLDER_COPY_PATH
 	};