bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) { String assembly_file = p_assembly_name + ".dll"; String assembly_src = p_src_dir.plus_file(assembly_file); String assembly_dst = p_dst_dir.plus_file(assembly_file); if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) WARN_PRINTS("Failed to copy " + xml_file); String pdb_file = p_assembly_name + ".pdb"; if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK) WARN_PRINTS("Failed to copy " + pdb_file); Error err = da->copy(assembly_src, assembly_dst); memdelete(da); if (err != OK) { show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll"); return false; } } return true; }
bool _test_path() { error->set_text(""); get_ok()->set_disabled(true); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (project_path->get_text() != "" && d->change_dir(project_path->get_text())!=OK) { error->set_text(TTR("Invalid project path, the path must exist!")); memdelete(d); return false; } if (mode!=MODE_IMPORT) { if (d->file_exists("engine.cfg")) { error->set_text(TTR("Invalid project path, engine.cfg must not exist.")); memdelete(d); return false; } } else { if (project_path->get_text() != "" && !d->file_exists("engine.cfg")) { error->set_text(TTR("Invalid project path, engine.cfg must exist.")); memdelete(d); return false; } } memdelete(d); get_ok()->set_disabled(false); return true; }
EditorFileSystem::EditorFileSystem() { reimport_on_missing_imported_files = GLOBAL_DEF("editor/reimport_missing_imported_files", true); singleton = this; filesystem = memnew(EditorFileSystemDirectory); //like, empty filesystem->parent = NULL; thread = NULL; scanning = false; importing = false; use_threads = true; thread_sources = NULL; new_filesystem = NULL; abort_scan = false; scanning_changes = false; scanning_changes_done = false; ResourceSaver::set_save_callback(_resource_saved); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->change_dir("res://.import") != OK) { da->make_dir("res://.import"); } memdelete(da); scan_total = 0; }
NewProjectDialog() { VBoxContainer *vb = memnew( VBoxContainer ); add_child(vb); set_child_rect(vb); Label* l = memnew(Label); l->set_text("Project Path:"); vb->add_child(l); pp=l; project_path = memnew( LineEdit ); MarginContainer *mc = memnew( MarginContainer ); vb->add_child(mc); HBoxContainer *pphb = memnew( HBoxContainer ); mc->add_child(pphb); pphb->add_child(project_path); project_path->set_h_size_flags(SIZE_EXPAND_FILL); Button* browse = memnew( Button ); pphb->add_child(browse); browse->set_text("Browse"); browse->connect("pressed", this,"_browse_path"); l = memnew(Label); l->set_text("Project Name:"); l->set_pos(Point2(5,50)); vb->add_child(l); pn=l; project_name = memnew( LineEdit ); mc = memnew( MarginContainer ); vb->add_child(mc); mc->add_child(project_name); project_name->set_text("New Game Project"); l = memnew(Label); l->set_text("That's a BINGO!"); vb->add_child(l); error=l; l->add_color_override("font_color",Color(1,0.4,0.3,0.8)); l->set_align(Label::ALIGN_CENTER); get_ok()->set_text("Create"); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); project_path->set_text(d->get_current_dir()); memdelete(d); fdialog = memnew( FileDialog ); add_child(fdialog); fdialog->set_access(FileDialog::ACCESS_FILESYSTEM); project_name->connect("text_changed", this,"_text_changed"); project_path->connect("text_changed", this,"_path_text_changed"); fdialog->connect("dir_selected", this,"_path_selected"); fdialog->connect("file_selected", this,"_file_selected"); set_hide_on_ok(false); import_mode=false; }
void EditorAssetLibraryItemDownload::_close() { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->remove(download->get_download_file()); //clean up removed file memdelete(da); queue_delete(); }
void RotatedFileLogger::rotate_file() { close_file(); if (FileAccess::exists(base_path)) { if (max_files > 1) { char timestamp[21]; OS::Date date = OS::get_singleton()->get_date(); OS::Time time = OS::get_singleton()->get_time(); sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); String backup_name = base_path.get_basename() + timestamp + "." + base_path.get_extension(); DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (da) { da->copy(base_path, backup_name); memdelete(da); } clear_old_backups(); } } else { DirAccess *da = DirAccess::create(DirAccess::ACCESS_USERDATA); if (da) { da->make_dir_recursive(base_path.get_base_dir()); memdelete(da); } } file = FileAccess::open(base_path, FileAccess::WRITE); }
bool DirAccess::exists(String p_dir) { DirAccess *da = DirAccess::create_for_path(p_dir); bool valid = da->change_dir(p_dir) == OK; memdelete(da); return valid; }
bool _test_path() { error->set_text(""); get_ok()->set_disabled(true); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (d->change_dir(project_path->get_text())!=OK) { error->set_text("Invalid Path for Project, Path Must Exist!"); memdelete(d); return false; } if (!import_mode) { if (d->file_exists("engine.cfg")) { error->set_text("Invalid Project Path (engine.cfg must not exist)."); memdelete(d); return false; } } else { if (!d->file_exists("engine.cfg")) { error->set_text("Invalid Project Path (engine.cfg must exist)."); memdelete(d); return false; } } memdelete(d); get_ok()->set_disabled(false); return true; }
void ExportTemplateManager::_uninstall_template_confirm() { DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = d->change_dir(EditorSettings::get_singleton()->get_templates_dir()); ERR_FAIL_COND(err != OK); err = d->change_dir(to_remove); ERR_FAIL_COND(err != OK); Vector<String> files; d->list_dir_begin(); bool isdir; String c = d->get_next(&isdir); while (c != String()) { if (!isdir) { files.push_back(c); } c = d->get_next(&isdir); } d->list_dir_end(); for (int i = 0; i < files.size(); i++) { d->remove(files[i]); } d->change_dir(".."); d->remove(to_remove); _update_template_list(); }
void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) { DirAccess *dir = DirAccess::open(path); if (dir == NULL) { print_line("Cannot open directory! " + path); return; } dir->list_dir_begin(); for (int i = 0; i < 1000; ++i) { String file = dir->get_next(); if (file == "") break; // Ignore special dirs and hidden dirs (such as .git and .import) if (file == "." || file == ".." || file.begins_with(".")) continue; if (dir->current_is_dir()) out_folders.append(file); else { String file_ext = file.get_extension(); if (_extension_filter.has(file_ext)) { _files_to_scan.push_back(path.plus_file(file)); } } } }
void OSIPhone::set_data_dir(String p_dir) { DirAccess *da = DirAccess::open(p_dir); data_dir = da->get_current_dir(); printf("setting data dir to %ls from %ls\n", data_dir.c_str(), p_dir.c_str()); memdelete(da); };
void OSIPhone::set_data_dir(String p_dir) { DirAccess* da = DirAccess::open(p_dir); data_dir = da->get_current_dir(); memdelete(da); };
// scan localpath, add or update child nodes, call recursively for folder nodes // localpath must be prefixed with Sync bool Sync::scan(string* localpath, FileAccess* fa) { if (localpath->size() < localdebris.size() || memcmp(localpath->data(), localdebris.data(), localdebris.size()) || (localpath->size() != localdebris.size() && memcmp(localpath->data() + localdebris.size(), client->fsaccess->localseparator.data(), client->fsaccess->localseparator.size()))) { DirAccess* da; string localname, name; bool success; da = client->fsaccess->newdiraccess(); // scan the dir, mark all items with a unique identifier if ((success = da->dopen(localpath, fa, false))) { size_t t = localpath->size(); while (da->dnext(&localname)) { name = localname; client->fsaccess->local2name(&name); // check if this record is to be ignored if (client->app->sync_syncable(name.c_str(), localpath, &localname)) { if (t) { localpath->append(client->fsaccess->localseparator); } localpath->append(localname); // skip the sync's debris folder if ((localpath->size() < localdebris.size()) || memcmp(localpath->data(), localdebris.data(), localdebris.size()) || ((localpath->size() != localdebris.size()) && memcmp(localpath->data() + localdebris.size(), client->fsaccess->localseparator.data(), client->fsaccess->localseparator.size()))) { // new or existing record: place scan result in notification queue dirnotify->notify(DirNotify::DIREVENTS, NULL, localpath->data(), localpath->size(), true); } localpath->resize(t); } } } delete da; return success; } else return false; }
void PluginConfigDialog::_on_confirmed() { String path = "res://addons/" + subfolder_edit->get_text(); if (!_edit_mode) { DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (!d || d->make_dir_recursive(path) != OK) return; } Ref<ConfigFile> cf = memnew(ConfigFile); cf->set_value("plugin", "name", name_edit->get_text()); cf->set_value("plugin", "description", desc_edit->get_text()); cf->set_value("plugin", "author", author_edit->get_text()); cf->set_value("plugin", "version", version_edit->get_text()); cf->set_value("plugin", "script", script_edit->get_text()); cf->save(path.plus_file("plugin.cfg")); if (!_edit_mode) { int lang_idx = script_option_edit->get_selected(); String lang_name = ScriptServer::get_language(lang_idx)->get_name(); Ref<Script> script; // TODO Use script templates. Right now, this code won't add the 'tool' annotation to other languages. // TODO Better support script languages with named classes (has_named_classes). if (lang_name == GDScriptLanguage::get_singleton()->get_name()) { // Hard-coded GDScript template to keep usability until we use script templates. Ref<GDScript> gdscript = memnew(GDScript); gdscript->set_source_code( "tool\n" "extends EditorPlugin\n" "\n" "func _enter_tree():\n" "\tpass\n" "\n" "func _exit_tree():\n" "\tpass\n"); String script_path = path.plus_file(script_edit->get_text()); gdscript->set_path(script_path); ResourceSaver::save(script_path, gdscript); script = gdscript; } else { String script_path = path.plus_file(script_edit->get_text()); String class_name = script_path.get_file().get_basename(); script = ScriptServer::get_language(lang_idx)->get_template(class_name, "EditorPlugin"); script->set_path(script_path); ResourceSaver::save(script_path, script); } emit_signal("plugin_ready", script.operator->(), active_edit->is_pressed() ? subfolder_edit->get_text() : ""); } else { EditorNode::get_singleton()->get_project_settings()->update_plugins(); } _clear_fields(); }
String DirAccess::get_full_path(const String &p_path, AccessType p_access) { DirAccess *d = DirAccess::create(p_access); if (!d) return p_path; d->change_dir(p_path); String full = d->get_current_dir(); memdelete(d); return full; }
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 EditorFileSystem::_delete_internal_files(String p_file) { if (FileAccess::exists(p_file + ".import")) { List<String> paths; ResourceFormatImporter::get_singleton()->get_internal_resource_path_list(p_file, &paths); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); for (List<String>::Element *E = paths.front(); E; E = E->next()) { da->remove(E->get()); } da->remove(p_file + ".import"); memdelete(da); } }
void ScenesDock::_go_to_dir(const String& p_dir){ DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->change_dir(p_dir)==OK) { path=da->get_current_dir(); _update_files(false); } current_path->set_text(path); memdelete(da); }
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); }
String ProjectSettings::localize_path(const String &p_path) const { if (resource_path == "") return p_path; //not initialized yet if (p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_abs_path() && !p_path.begins_with(resource_path))) return p_path.simplify_path(); DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String path = p_path.replace("\\", "/").simplify_path(); if (dir->change_dir(path) == OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\", "/"); memdelete(dir); // Ensure that we end with a '/'. // This is important to ensure that we do not wrongly localize the resource path // in an absolute path that just happens to contain this string but points to a // different folder (e.g. "/my/project" as resource_path would be contained in // "/my/project_data", even though the latter is not part of res://. // `plus_file("")` is an easy way to ensure we have a trailing '/'. const String res_path = resource_path.plus_file(""); if (!cwd.begins_with(res_path)) { return p_path; }; return cwd.replace_first(res_path, "res://"); } else { memdelete(dir); int sep = path.find_last("/"); if (sep == -1) { return "res://" + path; }; String parent = path.substr(0, sep); String plocal = localize_path(parent); if (plocal == "") { return ""; }; return plocal + path.substr(sep, path.size() - sep); }; }
DirAccess *DirAccess::open(const String &p_path, Error *r_error) { DirAccess *da = create_for_path(p_path); ERR_FAIL_COND_V(!da, NULL); Error err = da->change_dir(p_path); if (r_error) *r_error = err; if (err != OK) { memdelete(da); return NULL; } return da; }
String Globals::localize_path(const String& p_path) const { if (resource_path=="") return p_path; //not initialied yet if (p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_abs_path() && !p_path.begins_with(resource_path))) return p_path.simplify_path(); DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String path = p_path.replace("\\","/").simplify_path(); if (dir->change_dir(path)==OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\","/"); memdelete(dir); if (!cwd.begins_with(resource_path)) { return p_path; }; return cwd.replace_first(resource_path, "res:/"); } else { memdelete(dir); int sep = path.find_last("/"); if (sep == -1) { return "res://"+path; }; String parent = path.substr(0, sep); String plocal = localize_path(parent); if (plocal == "") { return ""; }; return plocal + path.substr(sep, path.size() - sep); }; }
void RotatedFileLogger::clear_old_backups() { int max_backups = max_files - 1; // -1 for the current file String basename = base_path.get_file().get_basename(); String extension = "." + base_path.get_extension(); DirAccess *da = DirAccess::open(base_path.get_base_dir()); if (!da) { return; } da->list_dir_begin(); String f = da->get_next(); Set<String> backups; while (f != String()) { if (!da->current_is_dir() && f.begins_with(basename) && f.ends_with(extension) && f != base_path.get_file()) { backups.insert(f); } f = da->get_next(); } da->list_dir_end(); if (backups.size() > max_backups) { // since backups are appended with timestamp and Set iterates them in sorted order, // first backups are the oldest int to_delete = backups.size() - max_backups; for (Set<String>::Element *E = backups.front(); E && to_delete > 0; E = E->next(), --to_delete) { da->remove(E->get()); } } memdelete(da); }
bool PowerX11::GetPowerInfo_Linux_proc_acpi() { String node; DirAccess *dirp = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); bool have_battery = false; bool have_ac = false; bool charging = false; this->nsecs_left = -1; this->percent_left = -1; this->power_state = OS::POWERSTATE_UNKNOWN; dirp->change_dir(proc_acpi_battery_path); Error err = dirp->list_dir_begin(); if (err != OK) { return false; /* can't use this interface. */ } else { node = dirp->get_next(); while (node != "") { check_proc_acpi_battery(node.utf8().get_data(), &have_battery, &charging /*, seconds, percent*/); node = dirp->get_next(); } memdelete(dirp); } dirp->change_dir(proc_acpi_ac_adapter_path); err = dirp->list_dir_begin(); if (err != OK) { return false; /* can't use this interface. */ } else { node = dirp->get_next(); while (node != "") { check_proc_acpi_ac_adapter(node.utf8().get_data(), &have_ac); node = dirp->get_next(); } memdelete(dirp); } if (!have_battery) { this->power_state = OS::POWERSTATE_NO_BATTERY; } else if (charging) { this->power_state = OS::POWERSTATE_CHARGING; } else if (have_ac) { this->power_state = OS::POWERSTATE_CHARGED; } else { this->power_state = OS::POWERSTATE_ON_BATTERY; } return true; /* definitive answer. */ }
void ProjectManager::_scan_begin(const String& p_base) { print_line("SCAN PROJECTS AT: "+p_base); List<String> projects; DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->change_dir(p_base); _scan_dir(da,0,1,&projects); memdelete(da); print_line("found: "+itos(projects.size())+" projects."); for(List<String>::Element *E=projects.front();E;E=E->next()) { String proj=E->get().replace("/","::"); EditorSettings::get_singleton()->set("projects/"+proj,E->get()); } EditorSettings::get_singleton()->save(); _load_recent_projects(); }
void DependencyRemoveDialog::ok_pressed() { DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); for (Map<String, TreeItem *>::Element *E = files.front(); E; E = E->next()) { if (da->dir_exists(E->key())) { String path = OS::get_singleton()->get_resource_dir() + E->key().replace_first("res://", "/"); OS::get_singleton()->move_path_to_trash(path); EditorFileSystem::get_singleton()->scan(); } else { if (ResourceCache::has(E->key())) { Resource *res = ResourceCache::get(E->key()); res->set_path(""); //clear reference to path } da->remove(E->key()); EditorFileSystem::get_singleton()->update_file(E->key()); } } memdelete(da); }
String _test_path() { error->set_text(""); get_ok()->set_disabled(true); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String valid_path; if (d->change_dir(project_path->get_text())==OK){ valid_path=project_path->get_text(); } else if (d->change_dir(project_path->get_text().strip_edges())==OK) { valid_path=project_path->get_text().strip_edges(); } if (valid_path == "") { error->set_text(TTR("Invalid project path, the path must exist!")); memdelete(d); return ""; } if (mode!=MODE_IMPORT) { if (d->file_exists("godot.cfg")) { error->set_text(TTR("Invalid project path, godot.cfg must not exist.")); memdelete(d); return ""; } } else { if (valid_path != "" && !d->file_exists("godot.cfg")) { error->set_text(TTR("Invalid project path, godot.cfg must exist.")); memdelete(d); return ""; } } memdelete(d); get_ok()->set_disabled(false); return valid_path; }
void ProjectManager::_files_dropped(StringArray p_files, int p_screen) { Set<String> folders_set; DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < p_files.size(); i++) { String file = p_files[i]; folders_set.insert(da->dir_exists(file) ? file : file.get_base_dir()); } memdelete(da); if (folders_set.size()>0) { StringArray folders; for (Set<String>::Element *E=folders_set.front();E;E=E->next()) { folders.append(E->get()); } bool confirm = true; if (folders.size()==1) { DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (dir->change_dir(folders[0])==OK) { dir->list_dir_begin(); String file = dir->get_next(); while(confirm && file!=String()) { if (!da->current_is_dir() && file.ends_with("engine.cfg")) { confirm = false; } file = dir->get_next(); } dir->list_dir_end(); } memdelete(dir); } if (confirm) { multi_scan_ask->get_ok()->disconnect("pressed", this, "_scan_multiple_folders"); multi_scan_ask->get_ok()->connect("pressed", this, "_scan_multiple_folders", varray(folders)); multi_scan_ask->set_text(vformat(TTR("You are about the scan %s folders for existing Godot projects. Do you confirm?"), folders.size())); multi_scan_ask->popup_centered_minsize(); } else { _scan_multiple_folders(folders); } } }
MainLoop *test() { print_line("this is test io"); DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->change_dir("."); print_line("Opening current dir " + da->get_current_dir()); String entry; da->list_dir_begin(); while ((entry = da->get_next()) != "") { print_line("entry " + entry + " is dir: " + Variant(da->current_is_dir())); }; da->list_dir_end(); RES texture = ResourceLoader::load("test_data/rock.png"); ERR_FAIL_COND_V(texture.is_null(), NULL); ResourceSaver::save("test_data/rock.xml", texture); print_line("localize paths"); print_line(ProjectSettings::get_singleton()->localize_path("algo.xml")); print_line(ProjectSettings::get_singleton()->localize_path("c:\\windows\\algo.xml")); print_line(ProjectSettings::get_singleton()->localize_path(ProjectSettings::get_singleton()->get_resource_path() + "/something/something.xml")); print_line(ProjectSettings::get_singleton()->localize_path("somedir/algo.xml")); { FileAccess *z = FileAccess::open("test_data/archive.zip", FileAccess::READ); int len = z->get_len(); Vector<uint8_t> zip; zip.resize(len); z->get_buffer(&zip[0], len); z->close(); memdelete(z); FileAccessMemory::register_file("a_package", zip); FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_FILESYSTEM); FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_USERDATA); print_line("archive test"); }; print_line("test done"); return memnew(TestMainLoop); }
void ExportTemplateManager::_update_template_list() { while (current_hb->get_child_count()) { memdelete(current_hb->get_child(0)); } while (installed_vb->get_child_count()) { memdelete(installed_vb->get_child(0)); } DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = d->change_dir(EditorSettings::get_singleton()->get_templates_dir()); d->list_dir_begin(); Set<String> templates; if (err == OK) { bool isdir; String c = d->get_next(&isdir); while (c != String()) { if (isdir && !c.begins_with(".")) { templates.insert(c); } c = d->get_next(&isdir); } } d->list_dir_end(); memdelete(d); String current_version = VERSION_FULL_CONFIG; Label *current = memnew(Label); current->set_h_size_flags(SIZE_EXPAND_FILL); current_hb->add_child(current); if (templates.has(current_version)) { current->add_color_override("font_color", get_color("success_color", "Editor")); Button *redownload = memnew(Button); redownload->set_text(TTR("Re-Download")); current_hb->add_child(redownload); redownload->connect("pressed", this, "_download_template", varray(current_version)); Button *uninstall = memnew(Button); uninstall->set_text(TTR("Uninstall")); current_hb->add_child(uninstall); current->set_text(current_version + " " + TTR("(Installed)")); uninstall->connect("pressed", this, "_uninstall_template", varray(current_version)); } else { current->add_color_override("font_color", get_color("error_color", "Editor")); Button *redownload = memnew(Button); redownload->set_text(TTR("Download")); redownload->connect("pressed", this, "_download_template", varray(current_version)); current_hb->add_child(redownload); current->set_text(current_version + " " + TTR("(Missing)")); } for (Set<String>::Element *E = templates.back(); E; E = E->prev()) { HBoxContainer *hbc = memnew(HBoxContainer); Label *version = memnew(Label); version->set_modulate(get_color("disabled_font_color", "Editor")); String text = E->get(); if (text == current_version) { text += " " + TTR("(Current)"); } version->set_text(text); version->set_h_size_flags(SIZE_EXPAND_FILL); hbc->add_child(version); Button *uninstall = memnew(Button); uninstall->set_text(TTR("Uninstall")); hbc->add_child(uninstall); uninstall->connect("pressed", this, "_uninstall_template", varray(E->get())); installed_vb->add_child(hbc); } }