94c34ff890
Done to ensure that no important identifiers are translatable, to fix compound strings using the new vformat() function, and some general English proofreading here and there.
694 lines
15 KiB
C++
694 lines
15 KiB
C++
#include "dependency_editor.h"
|
|
#include "os/file_access.h"
|
|
#include "scene/gui/margin_container.h"
|
|
#include "io/resource_loader.h"
|
|
#include "editor_node.h"
|
|
|
|
void DependencyEditor::_notification(int p_what){
|
|
|
|
|
|
}
|
|
|
|
void DependencyEditor::_searched(const String& p_path) {
|
|
|
|
Map<String,String> dep_rename;
|
|
dep_rename[replacing]=p_path;
|
|
|
|
|
|
ResourceLoader::rename_dependencies(editing,dep_rename);
|
|
|
|
_update_list();
|
|
_update_file();
|
|
}
|
|
|
|
void DependencyEditor::_load_pressed(Object* p_item,int p_cell,int p_button){
|
|
|
|
TreeItem *ti=p_item->cast_to<TreeItem>();
|
|
String fname = ti->get_text(0);
|
|
replacing = ti->get_text(1);
|
|
|
|
search->set_title(TTR("Search Replacement For:")+" "+replacing.get_file());
|
|
|
|
search->clear_filters();
|
|
List<String> ext;
|
|
ResourceLoader::get_recognized_extensions_for_type(ti->get_metadata(0),&ext);
|
|
for (List<String>::Element *E=ext.front();E;E=E->next()) {
|
|
search->add_filter("*"+E->get());
|
|
}
|
|
search->popup_centered_ratio();
|
|
|
|
}
|
|
|
|
void DependencyEditor::_fix_and_find(EditorFileSystemDirectory *efsd, Map<String,Map<String,String> >& candidates){
|
|
|
|
for(int i=0;i<efsd->get_subdir_count();i++) {
|
|
_fix_and_find(efsd->get_subdir(i),candidates);
|
|
}
|
|
|
|
for(int i=0;i<efsd->get_file_count();i++) {
|
|
|
|
String file = efsd->get_file(i);
|
|
if (!candidates.has(file))
|
|
continue;
|
|
|
|
String path = efsd->get_file_path(i);
|
|
Map<String,String> &ss = candidates[file];
|
|
|
|
|
|
for(Map<String,String>::Element *E=candidates[file].front();E;E=E->next()) {
|
|
|
|
if (E->get()==String()) {
|
|
E->get()=path;
|
|
continue;
|
|
}
|
|
|
|
//must match the best, using subdirs
|
|
String existing=E->get().replace_first("res://","");
|
|
String current=path.replace_first("res://","");
|
|
String lost=E->key().replace_first("res://","");
|
|
|
|
Vector<String> existingv=existing.split("/");
|
|
existingv.invert();
|
|
Vector<String> currentv=current.split("/");
|
|
currentv.invert();
|
|
Vector<String> lostv=lost.split("/");
|
|
lostv.invert();
|
|
|
|
int existing_score=0;
|
|
int current_score=0;
|
|
|
|
for(int j=0;j<lostv.size();j++) {
|
|
|
|
if (j<existingv.size() && lostv[j]==existingv[j]) {
|
|
existing_score++;
|
|
}
|
|
if (j<currentv.size() && lostv[j]==currentv[j]) {
|
|
current_score++;
|
|
}
|
|
}
|
|
|
|
if (current_score > existing_score) {
|
|
|
|
//if it was the same, could track distance to new path but..
|
|
|
|
E->get()=path; //replace by more accurate
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void DependencyEditor::_fix_all(){
|
|
|
|
if (!EditorFileSystem::get_singleton()->get_filesystem())
|
|
return;
|
|
|
|
Map<String,Map<String,String> > candidates;
|
|
|
|
for (List<String>::Element *E=missing.front();E;E=E->next()) {
|
|
|
|
String base = E->get().get_file();
|
|
if (!candidates.has(base)) {
|
|
candidates[base]=Map<String,String>();
|
|
}
|
|
|
|
candidates[base][E->get()]="";
|
|
}
|
|
|
|
_fix_and_find(EditorFileSystem::get_singleton()->get_filesystem(),candidates);
|
|
|
|
Map<String,String> remaps;
|
|
|
|
for (Map<String,Map<String,String> >::Element *E=candidates.front();E;E=E->next()) {
|
|
|
|
for (Map<String,String>::Element *F=E->get().front();F;F=F->next()) {
|
|
|
|
if (F->get()!=String()) {
|
|
remaps[F->key()]=F->get();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (remaps.size()) {
|
|
|
|
ResourceLoader::rename_dependencies(editing,remaps);
|
|
|
|
_update_list();
|
|
_update_file();
|
|
}
|
|
}
|
|
|
|
void DependencyEditor::_update_file() {
|
|
|
|
EditorFileSystem::get_singleton()->update_file(editing);
|
|
|
|
}
|
|
|
|
void DependencyEditor::_update_list() {
|
|
|
|
List<String> deps;
|
|
ResourceLoader::get_dependencies(editing,&deps,true);
|
|
|
|
tree->clear();
|
|
missing.clear();
|
|
|
|
TreeItem *root = tree->create_item();
|
|
|
|
Ref<Texture> folder = get_icon("folder","FileDialog");
|
|
|
|
bool broken=false;
|
|
|
|
for(List<String>::Element *E=deps.front();E;E=E->next()) {
|
|
|
|
TreeItem *item = tree->create_item(root);
|
|
|
|
String n = E->get();
|
|
String path;
|
|
String type;
|
|
|
|
if (n.find("::")!=-1) {
|
|
path = n.get_slice("::",0);
|
|
type = n.get_slice("::",1);
|
|
} else {
|
|
path=n;
|
|
type="Resource";
|
|
}
|
|
String name = path.get_file();
|
|
|
|
Ref<Texture> icon;
|
|
if (has_icon(type,"EditorIcons")) {
|
|
icon=get_icon(type,"EditorIcons");
|
|
} else {
|
|
icon=get_icon("Object","EditorIcons");
|
|
}
|
|
item->set_text(0,name);
|
|
item->set_icon(0,icon);
|
|
item->set_metadata(0,type);
|
|
item->set_text(1,path);
|
|
|
|
if (!FileAccess::exists(path)) {
|
|
item->set_custom_color(1,Color(1,0.4,0.3));
|
|
missing.push_back(path);
|
|
broken=true;
|
|
}
|
|
|
|
item->add_button(1,folder,0);
|
|
}
|
|
|
|
fixdeps->set_disabled(!broken);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DependencyEditor::edit(const String& p_path) {
|
|
|
|
|
|
editing=p_path;
|
|
set_title(TTR("Dependencies For:")+" "+p_path.get_file());
|
|
|
|
_update_list();
|
|
popup_centered_ratio();
|
|
|
|
if (EditorNode::get_singleton()->is_scene_open(p_path)) {
|
|
EditorNode::get_singleton()->show_warning(vformat(TTR("Scene '%s' is currently being edited.\nChanges will not take effect unless reloaded."),p_path.get_file()));
|
|
} else if (ResourceCache::has(p_path)) {
|
|
EditorNode::get_singleton()->show_warning(vformat(TTR("Resource '%s' is in use.\nChanges will take effect when reloaded."),p_path.get_file()));
|
|
}
|
|
}
|
|
|
|
|
|
void DependencyEditor::_bind_methods() {
|
|
|
|
ObjectTypeDB::bind_method(_MD("_searched"),&DependencyEditor::_searched);
|
|
ObjectTypeDB::bind_method(_MD("_load_pressed"),&DependencyEditor::_load_pressed);
|
|
ObjectTypeDB::bind_method(_MD("_fix_all"),&DependencyEditor::_fix_all);
|
|
|
|
}
|
|
|
|
DependencyEditor::DependencyEditor() {
|
|
|
|
VBoxContainer *vb = memnew( VBoxContainer );
|
|
vb->set_name(TTR("Dependencies"));
|
|
add_child(vb);
|
|
set_child_rect(vb);
|
|
|
|
tree = memnew( Tree );
|
|
tree->set_columns(2);
|
|
tree->set_column_titles_visible(true);
|
|
tree->set_column_title(0,"Resource");
|
|
tree->set_column_title(1,"Path");
|
|
tree->set_hide_root(true);
|
|
tree->connect("button_pressed",this,"_load_pressed");
|
|
|
|
HBoxContainer *hbc = memnew( HBoxContainer );
|
|
Label *label = memnew( Label(TTR("Dependencies:")));
|
|
hbc->add_child(label);
|
|
hbc->add_spacer();
|
|
fixdeps = memnew( Button(TTR("Fix Broken")));
|
|
hbc->add_child(fixdeps);
|
|
fixdeps->connect("pressed",this,"_fix_all");
|
|
|
|
vb->add_child(hbc);
|
|
|
|
MarginContainer *mc = memnew( MarginContainer );
|
|
mc->set_v_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
mc->add_child(tree);
|
|
vb->add_child(mc);
|
|
|
|
set_title(TTR("Dependency Editor"));
|
|
search = memnew( EditorFileDialog );
|
|
search->connect("file_selected",this,"_searched");
|
|
search->set_mode(EditorFileDialog::MODE_OPEN_FILE);
|
|
search->set_title(TTR("Search Replacement Resource:"));
|
|
add_child(search);
|
|
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
|
|
|
void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) {
|
|
|
|
if (!efsd)
|
|
return;
|
|
|
|
for(int i=0;i<efsd->get_subdir_count();i++) {
|
|
_fill_owners(efsd->get_subdir(i));
|
|
}
|
|
|
|
for(int i=0;i<efsd->get_file_count();i++) {
|
|
|
|
Vector<String> deps = efsd->get_file_deps(i);
|
|
//print_line(":::"+efsd->get_file_path(i));
|
|
bool found=false;
|
|
for(int j=0;j<deps.size();j++) {
|
|
//print_line("\t"+deps[j]+" vs "+editing);
|
|
if (deps[j]==editing) {
|
|
//print_line("found");
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
continue;
|
|
|
|
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");
|
|
}
|
|
|
|
owners->add_item(efsd->get_file_path(i),icon);
|
|
}
|
|
|
|
}
|
|
|
|
void DependencyEditorOwners::show(const String& p_path) {
|
|
|
|
editing=p_path;
|
|
owners->clear();
|
|
_fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
|
|
popup_centered_ratio();
|
|
|
|
set_title(TTR("Owners Of:")+" "+p_path.get_file());
|
|
|
|
}
|
|
|
|
DependencyEditorOwners::DependencyEditorOwners() {
|
|
|
|
|
|
owners = memnew( ItemList );
|
|
add_child(owners);
|
|
set_child_rect(owners);
|
|
|
|
|
|
}
|
|
|
|
///////////////////////
|
|
|
|
|
|
void DependencyRemoveDialog::_fill_owners(EditorFileSystemDirectory *efsd) {
|
|
|
|
if (!efsd)
|
|
return;
|
|
|
|
for(int i=0;i<efsd->get_subdir_count();i++) {
|
|
_fill_owners(efsd->get_subdir(i));
|
|
}
|
|
|
|
for(int i=0;i<efsd->get_file_count();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())
|
|
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");
|
|
}
|
|
|
|
|
|
for(Set<String>::Element *E=met.front();E;E=E->next()) {
|
|
|
|
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;
|
|
|
|
}
|
|
TreeItem *ti=owners->create_item(files[which]);
|
|
ti->set_text(0,efsd->get_file_path(i));
|
|
ti->set_icon(0,icon);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void DependencyRemoveDialog::show(const Vector<String> &to_erase) {
|
|
|
|
exist=false;
|
|
owners->clear();
|
|
files.clear();
|
|
TreeItem *root=owners->create_item();
|
|
for(int i=0;i<to_erase.size();i++) {
|
|
files[to_erase[i]]=NULL;
|
|
}
|
|
|
|
_fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
|
|
|
|
if (exist) {
|
|
owners->show();
|
|
text->set_text("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 {
|
|
owners->hide();
|
|
text->set_text(TTR("Remove selected files from the project? (no undo)"));
|
|
popup_centered_minsize(Size2(400,100));
|
|
}
|
|
|
|
}
|
|
|
|
void DependencyRemoveDialog::ok_pressed() {
|
|
|
|
|
|
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
|
for (Map<String,TreeItem*>::Element *E=files.front();E;E=E->next()) {
|
|
|
|
da->remove(E->key());
|
|
EditorFileSystem::get_singleton()->update_file(E->key());
|
|
}
|
|
memdelete(da);
|
|
|
|
}
|
|
|
|
DependencyRemoveDialog::DependencyRemoveDialog() {
|
|
|
|
VBoxContainer *vb = memnew( VBoxContainer );
|
|
add_child(vb);
|
|
set_child_rect(vb);
|
|
|
|
text = memnew( Label );
|
|
vb->add_child(text);
|
|
|
|
owners = memnew( Tree );
|
|
owners->set_hide_root(true);
|
|
vb->add_child(owners);
|
|
owners->set_v_size_flags(SIZE_EXPAND_FILL);
|
|
get_ok()->set_text(TTR("Remove"));
|
|
}
|
|
|
|
|
|
//////////////
|
|
|
|
|
|
void DependencyErrorDialog::show(const String& p_for_file,const Vector<String> &report) {
|
|
|
|
|
|
for_file=p_for_file;
|
|
set_title(TTR("Error loading:")+" "+p_for_file.get_file());
|
|
files->clear();
|
|
|
|
TreeItem *root = files->create_item(NULL);
|
|
for(int i=0;i<report.size();i++) {
|
|
|
|
String dep;
|
|
String type="Object";
|
|
dep=report[i].get_slice("::",0);
|
|
if (report[i].get_slice_count("::")>0)
|
|
type=report[i].get_slice("::",1);
|
|
|
|
Ref<Texture> icon;
|
|
if (!has_icon(type,"EditorIcons")) {
|
|
icon=get_icon("Object","EditorIcons");
|
|
} else {
|
|
icon=get_icon(type,"EditorIcons");
|
|
}
|
|
|
|
TreeItem *ti=files->create_item(root);
|
|
ti->set_text(0,dep);
|
|
ti->set_icon(0,icon);
|
|
|
|
}
|
|
|
|
popup_centered_minsize(Size2(500,220));
|
|
|
|
}
|
|
|
|
void DependencyErrorDialog::ok_pressed() {
|
|
|
|
EditorNode::get_singleton()->load_scene(for_file,true);
|
|
}
|
|
|
|
void DependencyErrorDialog::custom_action(const String&) {
|
|
|
|
EditorNode::get_singleton()->fix_dependencies(for_file);
|
|
}
|
|
|
|
DependencyErrorDialog::DependencyErrorDialog() {
|
|
|
|
VBoxContainer *vb = memnew( VBoxContainer );
|
|
add_child(vb);
|
|
set_child_rect(vb);
|
|
|
|
|
|
files = memnew( Tree );
|
|
files->set_hide_root(true);
|
|
vb->add_margin_child(TTR("Scene failed to load due to missing dependencies:"),files,true);
|
|
files->set_v_size_flags(SIZE_EXPAND_FILL);
|
|
get_ok()->set_text(TTR("Open Anyway"));
|
|
|
|
text = memnew( Label );
|
|
vb->add_child(text);
|
|
text->set_text(TTR("Which action should be taken?"));
|
|
|
|
|
|
fdep=add_button(TTR("Fix Dependencies"),true,"fixdeps");
|
|
|
|
set_title(TTR("Errors loading!"));
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void OrphanResourcesDialog::ok_pressed() {
|
|
|
|
paths.clear();
|
|
|
|
_find_to_delete(files->get_root(),paths);
|
|
if (paths.empty())
|
|
return;
|
|
|
|
delete_confirm->set_text(vformat(TTR("Permanently delete %i item(s)? (No undo!)"),paths.size()));
|
|
delete_confirm->popup_centered_minsize();
|
|
}
|
|
|
|
bool OrphanResourcesDialog::_fill_owners(EditorFileSystemDirectory *efsd,HashMap<String,int>& refs,TreeItem* p_parent){
|
|
|
|
|
|
if (!efsd)
|
|
return false;
|
|
|
|
bool has_childs=false;
|
|
|
|
for(int i=0;i<efsd->get_subdir_count();i++) {
|
|
|
|
TreeItem *dir_item=NULL;
|
|
if (p_parent) {
|
|
dir_item = files->create_item(p_parent);
|
|
dir_item->set_text(0,efsd->get_subdir(i)->get_name());
|
|
dir_item->set_icon(0,get_icon("folder","FileDialog"));
|
|
|
|
}
|
|
bool children = _fill_owners(efsd->get_subdir(i),refs,dir_item);
|
|
|
|
if (p_parent) {
|
|
if (!children) {
|
|
memdelete(dir_item);
|
|
} else {
|
|
has_childs=true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
for(int i=0;i<efsd->get_file_count();i++) {
|
|
|
|
if (!p_parent) {
|
|
Vector<String> deps = efsd->get_file_deps(i);
|
|
//print_line(":::"+efsd->get_file_path(i));
|
|
for(int j=0;j<deps.size();j++) {
|
|
|
|
if (!refs.has(deps[j])) {
|
|
refs[deps[j]]=1;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
String path = efsd->get_file_path(i);
|
|
if (!refs.has(path)) {
|
|
TreeItem *ti=files->create_item(p_parent);
|
|
ti->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
|
|
ti->set_text(0,efsd->get_file(i));
|
|
ti->set_editable(0,true);
|
|
|
|
String type=efsd->get_file_type(i);
|
|
|
|
Ref<Texture> icon;
|
|
if (has_icon(type,"EditorIcons")) {
|
|
icon=get_icon(type,"EditorIcons");
|
|
} else {
|
|
icon=get_icon("Object","EditorIcons");
|
|
}
|
|
ti->set_icon(0,icon);
|
|
int ds = efsd->get_file_deps(i).size();
|
|
ti->set_text(1,itos(ds));
|
|
if (ds) {
|
|
ti->add_button(1,get_icon("Visible","EditorIcons"));
|
|
}
|
|
ti->set_metadata(0,path);
|
|
has_childs=true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return has_childs;
|
|
}
|
|
|
|
|
|
void OrphanResourcesDialog::refresh() {
|
|
HashMap<String,int> refs;
|
|
_fill_owners(EditorFileSystem::get_singleton()->get_filesystem(),refs,NULL);
|
|
files->clear();
|
|
TreeItem *root=files->create_item();
|
|
_fill_owners(EditorFileSystem::get_singleton()->get_filesystem(),refs,root);
|
|
}
|
|
|
|
|
|
void OrphanResourcesDialog::show(){
|
|
|
|
refresh();
|
|
popup_centered_ratio();
|
|
}
|
|
|
|
|
|
void OrphanResourcesDialog::_find_to_delete(TreeItem* p_item,List<String>& paths) {
|
|
|
|
while(p_item) {
|
|
|
|
if (p_item->get_cell_mode(0)==TreeItem::CELL_MODE_CHECK && p_item->is_checked(0)) {
|
|
paths.push_back(p_item->get_metadata(0));
|
|
}
|
|
|
|
if (p_item->get_children()) {
|
|
_find_to_delete(p_item->get_children(),paths);
|
|
}
|
|
|
|
p_item=p_item->get_next();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void OrphanResourcesDialog::_delete_confirm() {
|
|
|
|
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
|
for (List<String>::Element *E=paths.front();E;E=E->next()) {
|
|
|
|
da->remove(E->get());
|
|
EditorFileSystem::get_singleton()->update_file(E->get());
|
|
}
|
|
memdelete(da);
|
|
refresh();
|
|
}
|
|
|
|
void OrphanResourcesDialog::_button_pressed(Object *p_item,int p_column, int p_id) {
|
|
|
|
TreeItem *ti=p_item->cast_to<TreeItem>();
|
|
|
|
String path = ti->get_metadata(0);
|
|
dep_edit->edit(path);
|
|
|
|
}
|
|
|
|
void OrphanResourcesDialog::_bind_methods() {
|
|
|
|
ObjectTypeDB::bind_method(_MD("_delete_confirm"),&OrphanResourcesDialog::_delete_confirm);
|
|
ObjectTypeDB::bind_method(_MD("_button_pressed"),&OrphanResourcesDialog::_button_pressed);
|
|
|
|
}
|
|
|
|
OrphanResourcesDialog::OrphanResourcesDialog(){
|
|
|
|
VBoxContainer *vbc = memnew( VBoxContainer );
|
|
add_child(vbc);
|
|
set_child_rect(vbc);
|
|
files = memnew( Tree );
|
|
files->set_columns(2);
|
|
files->set_column_titles_visible(true);
|
|
files->set_column_min_width(1,100);
|
|
files->set_column_expand(0,true);
|
|
files->set_column_expand(1,false);
|
|
files->set_column_title(0,"Resource");
|
|
files->set_column_title(1,TTR("Owns"));
|
|
files->set_hide_root(true);
|
|
vbc->add_margin_child(TTR("Resources Without Explicit Ownership:"),files,true);
|
|
set_title(TTR("Orphan Resource Explorer"));
|
|
delete_confirm = memnew( ConfirmationDialog );
|
|
delete_confirm->set_text(TTR("Delete selected files?"));
|
|
get_ok()->set_text(TTR("Delete"));
|
|
add_child(delete_confirm);
|
|
dep_edit = memnew( DependencyEditor );
|
|
add_child(dep_edit);
|
|
files->connect("button_pressed",this,"_button_pressed");
|
|
delete_confirm->connect("confirmed",this,"_delete_confirm");
|
|
set_hide_on_ok(false);
|
|
|
|
}
|