2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* dependency_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2019-01-01 12:53:14 +01:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2016-06-18 14:46:12 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2015-08-24 01:15:56 +02:00
# include "dependency_editor.h"
2017-01-16 08:04:19 +01:00
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/os/file_access.h"
2017-03-05 16:44:50 +01:00
# include "editor_node.h"
2015-08-24 01:15:56 +02:00
# include "scene/gui/margin_container.h"
2017-03-05 16:44:50 +01:00
void DependencyEditor : : _searched ( const String & p_path ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
Map < String , String > dep_rename ;
dep_rename [ replacing ] = p_path ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
ResourceLoader : : rename_dependencies ( editing , dep_rename ) ;
2015-08-24 01:15:56 +02:00
_update_list ( ) ;
_update_file ( ) ;
}
2017-03-05 16:44:50 +01:00
void DependencyEditor : : _load_pressed ( Object * p_item , int p_cell , int p_button ) {
2015-08-24 01:15:56 +02:00
2017-08-24 22:58:51 +02:00
TreeItem * ti = Object : : cast_to < TreeItem > ( p_item ) ;
2015-08-24 01:15:56 +02:00
String fname = ti - > get_text ( 0 ) ;
replacing = ti - > get_text ( 1 ) ;
2017-03-05 16:44:50 +01:00
search - > set_title ( TTR ( " Search Replacement For: " ) + " " + replacing . get_file ( ) ) ;
2015-08-24 01:15:56 +02:00
search - > clear_filters ( ) ;
List < String > ext ;
2017-03-05 16:44:50 +01:00
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 ( ) ) ;
2015-08-24 01:15:56 +02:00
}
2019-06-15 09:03:28 +02:00
search - > popup_centered_ratio ( 0.65 ) ; // So it doesn't completely cover the dialog below it.
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
void DependencyEditor : : _fix_and_find ( EditorFileSystemDirectory * efsd , Map < String , Map < String , String > > & candidates ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_subdir_count ( ) ; i + + ) {
_fix_and_find ( efsd - > get_subdir ( i ) , candidates ) ;
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_file_count ( ) ; i + + ) {
2015-08-24 01:15:56 +02:00
String file = efsd - > get_file ( i ) ;
if ( ! candidates . has ( file ) )
continue ;
String path = efsd - > get_file_path ( i ) ;
2017-03-05 16:44:50 +01:00
for ( Map < String , String > : : Element * E = candidates [ file ] . front ( ) ; E ; E = E - > next ( ) ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
if ( E - > get ( ) = = String ( ) ) {
E - > get ( ) = path ;
2015-08-24 01:15:56 +02:00
continue ;
}
//must match the best, using subdirs
2017-03-05 16:44:50 +01:00
String existing = E - > get ( ) . replace_first ( " res:// " , " " ) ;
String current = path . replace_first ( " res:// " , " " ) ;
String lost = E - > key ( ) . replace_first ( " res:// " , " " ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
Vector < String > existingv = existing . split ( " / " ) ;
2015-08-24 01:15:56 +02:00
existingv . invert ( ) ;
2017-03-05 16:44:50 +01:00
Vector < String > currentv = current . split ( " / " ) ;
2015-08-24 01:15:56 +02:00
currentv . invert ( ) ;
2017-03-05 16:44:50 +01:00
Vector < String > lostv = lost . split ( " / " ) ;
2015-08-24 01:15:56 +02:00
lostv . invert ( ) ;
2017-03-05 16:44:50 +01:00
int existing_score = 0 ;
int current_score = 0 ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < lostv . size ( ) ; j + + ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
if ( j < existingv . size ( ) & & lostv [ j ] = = existingv [ j ] ) {
2015-08-24 01:15:56 +02:00
existing_score + + ;
}
2017-03-05 16:44:50 +01:00
if ( j < currentv . size ( ) & & lostv [ j ] = = currentv [ j ] ) {
2015-08-24 01:15:56 +02:00
current_score + + ;
}
}
if ( current_score > existing_score ) {
//if it was the same, could track distance to new path but..
2017-03-05 16:44:50 +01:00
E - > get ( ) = path ; //replace by more accurate
2015-08-24 01:15:56 +02:00
}
}
}
}
2017-03-05 16:44:50 +01:00
void DependencyEditor : : _fix_all ( ) {
2015-08-24 01:15:56 +02:00
if ( ! EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) )
return ;
2017-03-05 16:44:50 +01:00
Map < String , Map < String , String > > candidates ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * E = missing . front ( ) ; E ; E = E - > next ( ) ) {
2015-08-24 01:15:56 +02:00
String base = E - > get ( ) . get_file ( ) ;
if ( ! candidates . has ( base ) ) {
2017-03-05 16:44:50 +01:00
candidates [ base ] = Map < String , String > ( ) ;
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
candidates [ base ] [ E - > get ( ) ] = " " ;
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
_fix_and_find ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , candidates ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
Map < String , String > remaps ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( Map < String , Map < String , String > > : : Element * E = candidates . front ( ) ; E ; E = E - > next ( ) ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( Map < String , String > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
if ( F - > get ( ) ! = String ( ) ) {
remaps [ F - > key ( ) ] = F - > get ( ) ;
2015-08-24 01:15:56 +02:00
}
}
}
if ( remaps . size ( ) ) {
2017-03-05 16:44:50 +01:00
ResourceLoader : : rename_dependencies ( editing , remaps ) ;
2015-08-24 01:15:56 +02:00
_update_list ( ) ;
_update_file ( ) ;
}
}
void DependencyEditor : : _update_file ( ) {
EditorFileSystem : : get_singleton ( ) - > update_file ( editing ) ;
}
void DependencyEditor : : _update_list ( ) {
List < String > deps ;
2017-03-05 16:44:50 +01:00
ResourceLoader : : get_dependencies ( editing , & deps , true ) ;
2015-08-24 01:15:56 +02:00
tree - > clear ( ) ;
missing . clear ( ) ;
TreeItem * root = tree - > create_item ( ) ;
2017-03-05 16:44:50 +01:00
Ref < Texture > folder = get_icon ( " folder " , " FileDialog " ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
bool broken = false ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * E = deps . front ( ) ; E ; E = E - > next ( ) ) {
2015-08-24 01:15:56 +02:00
TreeItem * item = tree - > create_item ( root ) ;
String n = E - > get ( ) ;
String path ;
String type ;
2017-03-05 16:44:50 +01:00
if ( n . find ( " :: " ) ! = - 1 ) {
path = n . get_slice ( " :: " , 0 ) ;
type = n . get_slice ( " :: " , 1 ) ;
2015-08-24 01:15:56 +02:00
} else {
2017-03-05 16:44:50 +01:00
path = n ;
type = " Resource " ;
2015-08-24 01:15:56 +02:00
}
String name = path . get_file ( ) ;
2018-09-02 23:40:51 +02:00
Ref < Texture > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( type ) ;
2017-03-05 16:44:50 +01:00
item - > set_text ( 0 , name ) ;
item - > set_icon ( 0 , icon ) ;
item - > set_metadata ( 0 , type ) ;
item - > set_text ( 1 , path ) ;
2015-08-24 01:15:56 +02:00
if ( ! FileAccess : : exists ( path ) ) {
2017-03-05 16:44:50 +01:00
item - > set_custom_color ( 1 , Color ( 1 , 0.4 , 0.3 ) ) ;
2015-08-24 01:15:56 +02:00
missing . push_back ( path ) ;
2017-03-05 16:44:50 +01:00
broken = true ;
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
item - > add_button ( 1 , folder , 0 ) ;
2015-08-24 01:15:56 +02:00
}
fixdeps - > set_disabled ( ! broken ) ;
}
2017-03-05 16:44:50 +01:00
void DependencyEditor : : edit ( const String & p_path ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
editing = p_path ;
set_title ( TTR ( " Dependencies For: " ) + " " + p_path . get_file ( ) ) ;
2015-08-24 01:15:56 +02:00
_update_list ( ) ;
2019-06-15 09:03:28 +02:00
popup_centered_ratio ( 0.7 ) ; // So it doesn't completely cover the dialog below it.
2015-08-24 01:15:56 +02:00
if ( EditorNode : : get_singleton ( ) - > is_scene_open ( p_path ) ) {
2019-03-25 01:54:29 +01:00
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Scene '%s' is currently being edited. \n Changes will only take effect when reloaded. " ) , p_path . get_file ( ) ) ) ;
2015-08-24 01:15:56 +02:00
} else if ( ResourceCache : : has ( p_path ) ) {
2019-03-25 01:54:29 +01:00
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Resource '%s' is in use. \n Changes will only take effect when reloaded. " ) , p_path . get_file ( ) ) ) ;
2015-08-24 01:15:56 +02:00
}
}
void DependencyEditor : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " _searched " ) , & DependencyEditor : : _searched ) ;
ClassDB : : bind_method ( D_METHOD ( " _load_pressed " ) , & DependencyEditor : : _load_pressed ) ;
ClassDB : : bind_method ( D_METHOD ( " _fix_all " ) , & DependencyEditor : : _fix_all ) ;
2015-08-24 01:15:56 +02:00
}
DependencyEditor : : DependencyEditor ( ) {
2017-03-05 16:44:50 +01:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
2016-05-04 03:25:37 +02:00
vb - > set_name ( TTR ( " Dependencies " ) ) ;
2015-08-24 01:15:56 +02:00
add_child ( vb ) ;
2017-01-10 05:49:55 +01:00
2017-03-05 16:44:50 +01:00
tree = memnew ( Tree ) ;
2015-08-24 01:15:56 +02:00
tree - > set_columns ( 2 ) ;
tree - > set_column_titles_visible ( true ) ;
2017-03-05 16:44:50 +01:00
tree - > set_column_title ( 0 , TTR ( " Resource " ) ) ;
tree - > set_column_title ( 1 , TTR ( " Path " ) ) ;
2015-08-24 01:15:56 +02:00
tree - > set_hide_root ( true ) ;
2017-03-05 16:44:50 +01:00
tree - > connect ( " button_pressed " , this , " _load_pressed " ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
HBoxContainer * hbc = memnew ( HBoxContainer ) ;
Label * label = memnew ( Label ( TTR ( " Dependencies: " ) ) ) ;
2015-08-24 01:15:56 +02:00
hbc - > add_child ( label ) ;
hbc - > add_spacer ( ) ;
2017-03-05 16:44:50 +01:00
fixdeps = memnew ( Button ( TTR ( " Fix Broken " ) ) ) ;
2015-08-24 01:15:56 +02:00
hbc - > add_child ( fixdeps ) ;
2017-03-05 16:44:50 +01:00
fixdeps - > connect ( " pressed " , this , " _fix_all " ) ;
2015-08-24 01:15:56 +02:00
vb - > add_child ( hbc ) ;
2017-03-05 16:44:50 +01:00
MarginContainer * mc = memnew ( MarginContainer ) ;
2015-08-24 01:15:56 +02:00
mc - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
mc - > add_child ( tree ) ;
vb - > add_child ( mc ) ;
2016-05-04 03:25:37 +02:00
set_title ( TTR ( " Dependency Editor " ) ) ;
2017-03-05 16:44:50 +01:00
search = memnew ( EditorFileDialog ) ;
search - > connect ( " file_selected " , this , " _searched " ) ;
2015-08-24 01:15:56 +02:00
search - > set_mode ( EditorFileDialog : : MODE_OPEN_FILE ) ;
2016-05-04 03:25:37 +02:00
search - > set_title ( TTR ( " Search Replacement Resource: " ) ) ;
2015-08-24 01:15:56 +02:00
add_child ( search ) ;
}
/////////////////////////////////////
2017-11-15 05:05:49 +01:00
void DependencyEditorOwners : : _list_rmb_select ( int p_item , const Vector2 & p_pos ) {
file_options - > clear ( ) ;
file_options - > set_size ( Size2 ( 1 , 1 ) ) ;
if ( p_item > = 0 ) {
file_options - > add_item ( TTR ( " Open " ) , FILE_OPEN ) ;
}
file_options - > set_position ( owners - > get_global_position ( ) + p_pos ) ;
file_options - > popup ( ) ;
}
void DependencyEditorOwners : : _select_file ( int p_idx ) {
String fpath = owners - > get_item_text ( p_idx ) ;
if ( ResourceLoader : : get_resource_type ( fpath ) = = " PackedScene " ) {
editor - > open_request ( fpath ) ;
hide ( ) ;
emit_signal ( " confirmed " ) ;
}
}
void DependencyEditorOwners : : _file_option ( int p_option ) {
switch ( p_option ) {
case FILE_OPEN : {
int idx = owners - > get_current ( ) ;
if ( idx < 0 | | idx > = owners - > get_item_count ( ) )
break ;
_select_file ( idx ) ;
} break ;
}
}
void DependencyEditorOwners : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " _list_rmb_select " ) , & DependencyEditorOwners : : _list_rmb_select ) ;
ClassDB : : bind_method ( D_METHOD ( " _file_option " ) , & DependencyEditorOwners : : _file_option ) ;
ClassDB : : bind_method ( D_METHOD ( " _select_file " ) , & DependencyEditorOwners : : _select_file ) ;
}
2015-08-24 01:15:56 +02:00
void DependencyEditorOwners : : _fill_owners ( EditorFileSystemDirectory * efsd ) {
if ( ! efsd )
return ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_subdir_count ( ) ; i + + ) {
2015-08-24 01:15:56 +02:00
_fill_owners ( efsd - > get_subdir ( i ) ) ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_file_count ( ) ; i + + ) {
2015-08-24 01:15:56 +02:00
Vector < String > deps = efsd - > get_file_deps ( i ) ;
2017-03-05 16:44:50 +01:00
bool found = false ;
for ( int j = 0 ; j < deps . size ( ) ; j + + ) {
if ( deps [ j ] = = editing ) {
found = true ;
2015-08-24 01:15:56 +02:00
break ;
}
}
if ( ! found )
continue ;
2018-09-02 23:40:51 +02:00
Ref < Texture > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( efsd - > get_file_type ( i ) ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
owners - > add_item ( efsd - > get_file_path ( i ) , icon ) ;
2015-08-24 01:15:56 +02:00
}
}
2017-03-05 16:44:50 +01:00
void DependencyEditorOwners : : show ( const String & p_path ) {
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
editing = p_path ;
2015-08-24 01:15:56 +02:00
owners - > clear ( ) ;
_fill_owners ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) ) ;
popup_centered_ratio ( ) ;
2017-03-05 16:44:50 +01:00
set_title ( TTR ( " Owners Of: " ) + " " + p_path . get_file ( ) ) ;
2015-08-24 01:15:56 +02:00
}
2017-11-15 05:05:49 +01:00
DependencyEditorOwners : : DependencyEditorOwners ( EditorNode * p_editor ) {
editor = p_editor ;
file_options = memnew ( PopupMenu ) ;
add_child ( file_options ) ;
file_options - > connect ( " id_pressed " , this , " _file_option " ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
owners = memnew ( ItemList ) ;
2017-11-15 05:05:49 +01:00
owners - > set_select_mode ( ItemList : : SELECT_SINGLE ) ;
owners - > connect ( " item_rmb_selected " , this , " _list_rmb_select " ) ;
owners - > connect ( " item_activated " , this , " _select_file " ) ;
owners - > set_allow_rmb_select ( true ) ;
2015-08-24 01:15:56 +02:00
add_child ( owners ) ;
}
///////////////////////
2017-10-02 00:24:49 +02:00
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.
}
}
2015-08-24 01:15:56 +02:00
2017-10-02 00:24:49 +02:00
void DependencyRemoveDialog : : _find_all_removed_dependencies ( EditorFileSystemDirectory * efsd , Vector < RemovedDependency > & p_removed ) {
2015-08-24 01:15:56 +02:00
if ( ! efsd )
return ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_subdir_count ( ) ; i + + ) {
2017-10-02 00:24:49 +02:00
_find_all_removed_dependencies ( efsd - > get_subdir ( i ) , p_removed ) ;
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_file_count ( ) ; i + + ) {
2017-10-02 00:24:49 +02:00
const String path = efsd - > get_file_path ( i ) ;
2015-08-24 01:15:56 +02:00
2017-10-02 00:24:49 +02:00
//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 ) )
2015-08-24 01:15:56 +02:00
continue ;
2017-10-02 00:24:49 +02:00
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 ) ;
}
2015-08-24 01:15:56 +02:00
}
2017-10-02 00:24:49 +02:00
}
}
2015-08-24 01:15:56 +02:00
2017-10-02 00:24:49 +02:00
void DependencyRemoveDialog : : _build_removed_dependency_tree ( const Vector < RemovedDependency > & p_removed ) {
owners - > clear ( ) ;
owners - > create_item ( ) ; // root
2015-08-24 01:15:56 +02:00
2017-10-02 00:24:49 +02:00
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 ;
2015-08-24 01:15:56 +02:00
}
}
2017-10-02 00:24:49 +02:00
//List this file under this dependency
2018-09-02 23:40:51 +02:00
Ref < Texture > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( rd . file_type ) ;
2017-10-02 00:24:49 +02:00
TreeItem * file_item = owners - > create_item ( tree_items [ rd . dependency ] ) ;
file_item - > set_text ( 0 , rd . file ) ;
file_item - > set_icon ( 0 , icon ) ;
2015-08-24 01:15:56 +02:00
}
}
2017-10-02 00:24:49 +02:00
void DependencyRemoveDialog : : show ( const Vector < String > & p_folders , const Vector < String > & p_files ) {
all_remove_files . clear ( ) ;
2018-02-26 08:59:49 +01:00
dirs_to_delete . clear ( ) ;
files_to_delete . clear ( ) ;
2015-08-24 01:15:56 +02:00
owners - > clear ( ) ;
2017-10-02 00:24:49 +02:00
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 ) ;
2018-02-26 08:59:49 +01:00
dirs_to_delete . push_back ( folder ) ;
2017-10-02 00:24:49 +02:00
}
for ( int i = 0 ; i < p_files . size ( ) ; + + i ) {
all_remove_files [ p_files [ i ] ] = String ( ) ;
2018-02-26 08:59:49 +01:00
files_to_delete . push_back ( p_files [ i ] ) ;
2015-08-24 01:15:56 +02:00
}
2017-10-02 00:24:49 +02:00
Vector < RemovedDependency > removed_deps ;
_find_all_removed_dependencies ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , removed_deps ) ;
removed_deps . sort ( ) ;
if ( removed_deps . empty ( ) ) {
2015-08-24 01:15:56 +02:00
owners - > hide ( ) ;
2016-05-04 03:25:37 +02:00
text - > set_text ( TTR ( " Remove selected files from the project? (no undo) " ) ) ;
2019-06-15 09:03:28 +02:00
set_size ( Size2 ( ) ) ;
popup_centered ( ) ;
2017-10-02 00:24:49 +02:00
} 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. \n Remove them anyway? (no undo) " ) ) ;
2019-06-15 09:03:28 +02:00
popup_centered ( Size2 ( 500 , 350 ) ) ;
2015-08-24 01:15:56 +02:00
}
2019-02-09 14:36:16 +01:00
EditorFileSystem : : get_singleton ( ) - > scan_changes ( ) ;
2015-08-24 01:15:56 +02:00
}
void DependencyRemoveDialog : : ok_pressed ( ) {
2018-02-26 08:59:49 +01:00
2018-05-11 21:37:08 +02:00
for ( int i = 0 ; i < files_to_delete . size ( ) ; + + i ) {
if ( ResourceCache : : has ( files_to_delete [ i ] ) ) {
Resource * res = ResourceCache : : get ( files_to_delete [ i ] ) ;
res - > set_path ( " " ) ;
}
2018-10-31 03:54:20 +01:00
2019-04-05 17:19:25 +02:00
// If the file we are deleting for e.g. the main scene, default environment,
// or audio bus layout, we must clear its definition in Project Settings.
2019-03-16 22:05:24 +01:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " application/config/icon " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " application/config/icon " , " " ) ;
}
2018-10-31 03:54:20 +01:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " application/run/main_scene " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " application/run/main_scene " , " " ) ;
}
2019-03-16 22:05:24 +01:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " application/boot_splash/image " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " application/boot_splash/image " , " " ) ;
}
2019-02-20 10:40:41 +01:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " rendering/environment/default_environment " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " rendering/environment/default_environment " , " " ) ;
}
2019-03-16 22:05:24 +01:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " display/mouse_cursor/custom_image " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " display/mouse_cursor/custom_image " , " " ) ;
}
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " gui/theme/custom " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " gui/theme/custom " , " " ) ;
}
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " gui/theme/custom_font " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " gui/theme/custom_font " , " " ) ;
}
2019-04-05 17:19:25 +02:00
if ( files_to_delete [ i ] = = ProjectSettings : : get_singleton ( ) - > get ( " audio/default_bus_layout " ) ) {
ProjectSettings : : get_singleton ( ) - > set ( " audio/default_bus_layout " , " " ) ;
}
2019-02-20 10:40:41 +01:00
2018-05-11 21:37:08 +02:00
String path = OS : : get_singleton ( ) - > get_resource_dir ( ) + files_to_delete [ i ] . replace_first ( " res:// " , " / " ) ;
2018-08-24 09:35:07 +02:00
print_verbose ( " Moving to trash: " + path ) ;
2018-05-11 21:37:08 +02:00
Error err = OS : : get_singleton ( ) - > move_to_trash ( path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove: " ) + " \n " + files_to_delete [ i ] + " \n " ) ;
2018-11-13 20:17:33 +01:00
} else {
emit_signal ( " file_removed " , files_to_delete [ i ] ) ;
2018-05-11 21:37:08 +02:00
}
}
2018-02-26 08:59:49 +01:00
if ( dirs_to_delete . size ( ) = = 0 ) {
2018-09-18 14:02:59 +02:00
// If we only deleted files we should only need to tell the file system about the files we touched.
2018-02-26 08:59:49 +01:00
for ( int i = 0 ; i < files_to_delete . size ( ) ; + + i )
EditorFileSystem : : get_singleton ( ) - > update_file ( files_to_delete [ i ] ) ;
} else {
for ( int i = 0 ; i < dirs_to_delete . size ( ) ; + + i ) {
String path = OS : : get_singleton ( ) - > get_resource_dir ( ) + dirs_to_delete [ i ] . replace_first ( " res:// " , " / " ) ;
2018-08-24 09:35:07 +02:00
print_verbose ( " Moving to trash: " + path ) ;
2018-02-26 08:59:49 +01:00
Error err = OS : : get_singleton ( ) - > move_to_trash ( path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove: " ) + " \n " + dirs_to_delete [ i ] + " \n " ) ;
2018-11-13 20:17:33 +01:00
} else {
emit_signal ( " folder_removed " , dirs_to_delete [ i ] ) ;
2018-02-26 08:59:49 +01:00
}
2017-10-02 00:24:49 +02:00
}
2017-09-25 15:15:11 +02:00
2018-09-18 14:02:59 +02:00
EditorFileSystem : : get_singleton ( ) - > scan_changes ( ) ;
}
2018-02-26 08:59:49 +01:00
2018-09-18 14:02:59 +02:00
// If some files/dirs would be deleted, favorite dirs need to be updated
Vector < String > previous_favorites = EditorSettings : : get_singleton ( ) - > get_favorites ( ) ;
Vector < String > new_favorites ;
2018-02-26 08:59:49 +01:00
2018-09-18 14:02:59 +02:00
for ( int i = 0 ; i < previous_favorites . size ( ) ; + + i ) {
if ( previous_favorites [ i ] . ends_with ( " / " ) ) {
if ( dirs_to_delete . find ( previous_favorites [ i ] ) < 0 )
new_favorites . push_back ( previous_favorites [ i ] ) ;
} else {
if ( files_to_delete . find ( previous_favorites [ i ] ) < 0 )
new_favorites . push_back ( previous_favorites [ i ] ) ;
2018-02-26 08:59:49 +01:00
}
2018-09-18 14:02:59 +02:00
}
2018-02-26 08:59:49 +01:00
2018-09-18 14:02:59 +02:00
if ( new_favorites . size ( ) < previous_favorites . size ( ) ) {
EditorSettings : : get_singleton ( ) - > set_favorites ( new_favorites ) ;
2015-08-24 01:15:56 +02:00
}
}
2018-11-13 20:17:33 +01:00
void DependencyRemoveDialog : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " file_removed " , PropertyInfo ( Variant : : STRING , " file " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " folder_removed " , PropertyInfo ( Variant : : STRING , " folder " ) ) ) ;
}
2015-08-24 01:15:56 +02:00
DependencyRemoveDialog : : DependencyRemoveDialog ( ) {
2019-06-15 09:03:28 +02:00
get_ok ( ) - > set_text ( TTR ( " Remove " ) ) ;
2017-03-05 16:44:50 +01:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
2015-08-24 01:15:56 +02:00
add_child ( vb ) ;
2017-01-10 05:49:55 +01:00
2017-03-05 16:44:50 +01:00
text = memnew ( Label ) ;
2015-08-24 01:15:56 +02:00
vb - > add_child ( text ) ;
2017-03-05 16:44:50 +01:00
owners = memnew ( Tree ) ;
2015-08-24 01:15:56 +02:00
owners - > set_hide_root ( true ) ;
vb - > add_child ( owners ) ;
owners - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
}
//////////////
2018-11-02 04:44:40 +01:00
void DependencyErrorDialog : : show ( Mode p_mode , const String & p_for_file , const Vector < String > & report ) {
2015-08-24 01:15:56 +02:00
2018-11-02 04:44:40 +01:00
mode = p_mode ;
2017-03-05 16:44:50 +01:00
for_file = p_for_file ;
set_title ( TTR ( " Error loading: " ) + " " + p_for_file . get_file ( ) ) ;
2015-08-24 01:15:56 +02:00
files - > clear ( ) ;
TreeItem * root = files - > create_item ( NULL ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < report . size ( ) ; i + + ) {
2015-08-24 01:15:56 +02:00
String dep ;
2017-03-05 16:44:50 +01:00
String type = " Object " ;
dep = report [ i ] . get_slice ( " :: " , 0 ) ;
if ( report [ i ] . get_slice_count ( " :: " ) > 0 )
type = report [ i ] . get_slice ( " :: " , 1 ) ;
2015-08-24 01:15:56 +02:00
2018-09-02 23:40:51 +02:00
Ref < Texture > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( type ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
TreeItem * ti = files - > create_item ( root ) ;
ti - > set_text ( 0 , dep ) ;
ti - > set_icon ( 0 , icon ) ;
2015-08-24 01:15:56 +02:00
}
2019-06-15 09:03:28 +02:00
popup_centered ( ) ;
2015-08-24 01:15:56 +02:00
}
void DependencyErrorDialog : : ok_pressed ( ) {
2018-11-02 04:44:40 +01:00
switch ( mode ) {
case MODE_SCENE :
EditorNode : : get_singleton ( ) - > load_scene ( for_file , true ) ;
break ;
case MODE_RESOURCE :
EditorNode : : get_singleton ( ) - > load_resource ( for_file , true ) ;
break ;
}
2015-08-24 01:15:56 +02:00
}
2017-03-05 16:44:50 +01:00
void DependencyErrorDialog : : custom_action ( const String & ) {
2015-08-24 01:15:56 +02:00
EditorNode : : get_singleton ( ) - > fix_dependencies ( for_file ) ;
}
DependencyErrorDialog : : DependencyErrorDialog ( ) {
2017-03-05 16:44:50 +01:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
2015-08-24 01:15:56 +02:00
add_child ( vb ) ;
2017-01-10 05:49:55 +01:00
2017-03-05 16:44:50 +01:00
files = memnew ( Tree ) ;
2015-08-24 01:15:56 +02:00
files - > set_hide_root ( true ) ;
2018-11-02 04:44:40 +01:00
vb - > add_margin_child ( TTR ( " Load failed due to missing dependencies: " ) , files , true ) ;
2015-08-24 01:15:56 +02:00
files - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
2019-06-15 09:03:28 +02:00
set_custom_minimum_size ( Size2 ( 500 , 220 ) ) ;
2016-05-04 03:25:37 +02:00
get_ok ( ) - > set_text ( TTR ( " Open Anyway " ) ) ;
2017-10-11 19:15:44 +02:00
get_cancel ( ) - > set_text ( TTR ( " Close " ) ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
text = memnew ( Label ) ;
2015-08-24 01:15:56 +02:00
vb - > add_child ( text ) ;
2016-05-04 03:25:37 +02:00
text - > set_text ( TTR ( " Which action should be taken? " ) ) ;
2015-08-24 01:15:56 +02:00
2017-03-05 16:44:50 +01:00
fdep = add_button ( TTR ( " Fix Dependencies " ) , true , " fixdeps " ) ;
2015-08-24 01:15:56 +02:00
2016-05-04 03:25:37 +02:00
set_title ( TTR ( " Errors loading! " ) ) ;
2015-08-24 01:15:56 +02:00
}
2015-11-29 05:08:31 +01:00
//////////////////////////////////////////////////////////////////////
void OrphanResourcesDialog : : ok_pressed ( ) {
paths . clear ( ) ;
2017-03-05 16:44:50 +01:00
_find_to_delete ( files - > get_root ( ) , paths ) ;
2015-11-29 05:08:31 +01:00
if ( paths . empty ( ) )
return ;
2017-03-05 16:44:50 +01:00
delete_confirm - > set_text ( vformat ( TTR ( " Permanently delete %d item(s)? (No undo!) " ) , paths . size ( ) ) ) ;
2019-06-15 09:03:28 +02:00
delete_confirm - > popup_centered_clamped ( delete_confirm - > get_minimum_size ( ) ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
bool OrphanResourcesDialog : : _fill_owners ( EditorFileSystemDirectory * efsd , HashMap < String , int > & refs , TreeItem * p_parent ) {
2015-11-29 05:08:31 +01:00
if ( ! efsd )
return false ;
2018-01-18 21:37:17 +01:00
bool has_children = false ;
2015-11-29 05:08:31 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_subdir_count ( ) ; i + + ) {
2015-11-29 05:08:31 +01:00
2017-03-05 16:44:50 +01:00
TreeItem * dir_item = NULL ;
2015-11-29 05:08:31 +01:00
if ( p_parent ) {
dir_item = files - > create_item ( p_parent ) ;
2017-03-05 16:44:50 +01:00
dir_item - > set_text ( 0 , efsd - > get_subdir ( i ) - > get_name ( ) ) ;
dir_item - > set_icon ( 0 , get_icon ( " folder " , " FileDialog " ) ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
bool children = _fill_owners ( efsd - > get_subdir ( i ) , refs , dir_item ) ;
2015-11-29 05:08:31 +01:00
if ( p_parent ) {
if ( ! children ) {
memdelete ( dir_item ) ;
} else {
2018-01-18 21:37:17 +01:00
has_children = true ;
2015-11-29 05:08:31 +01:00
}
}
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < efsd - > get_file_count ( ) ; i + + ) {
2015-11-29 05:08:31 +01:00
if ( ! p_parent ) {
Vector < String > deps = efsd - > get_file_deps ( i ) ;
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < deps . size ( ) ; j + + ) {
2015-11-29 05:08:31 +01:00
if ( ! refs . has ( deps [ j ] ) ) {
2017-03-05 16:44:50 +01:00
refs [ deps [ j ] ] = 1 ;
2015-11-29 05:08:31 +01:00
}
}
} else {
String path = efsd - > get_file_path ( i ) ;
if ( ! refs . has ( path ) ) {
2017-03-05 16:44:50 +01:00
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 ) ;
2015-11-29 05:08:31 +01:00
2017-03-05 16:44:50 +01:00
String type = efsd - > get_file_type ( i ) ;
2015-11-29 05:08:31 +01:00
2018-09-02 23:40:51 +02:00
Ref < Texture > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( type ) ;
2017-03-05 16:44:50 +01:00
ti - > set_icon ( 0 , icon ) ;
2015-11-29 05:08:31 +01:00
int ds = efsd - > get_file_deps ( i ) . size ( ) ;
2017-03-05 16:44:50 +01:00
ti - > set_text ( 1 , itos ( ds ) ) ;
2015-11-29 05:08:31 +01:00
if ( ds ) {
2019-06-06 07:47:56 +02:00
ti - > add_button ( 1 , get_icon ( " GuiVisibilityVisible " , " EditorIcons " ) , - 1 , false , TTR ( " Show Dependencies " ) ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
ti - > set_metadata ( 0 , path ) ;
2018-01-18 21:37:17 +01:00
has_children = true ;
2015-11-29 05:08:31 +01:00
}
}
}
2018-01-18 21:37:17 +01:00
return has_children ;
2015-11-29 05:08:31 +01:00
}
void OrphanResourcesDialog : : refresh ( ) {
2017-03-05 16:44:50 +01:00
HashMap < String , int > refs ;
_fill_owners ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , refs , NULL ) ;
2015-11-29 05:08:31 +01:00
files - > clear ( ) ;
2017-03-05 16:44:50 +01:00
TreeItem * root = files - > create_item ( ) ;
_fill_owners ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , refs , root ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
void OrphanResourcesDialog : : show ( ) {
2015-11-29 05:08:31 +01:00
refresh ( ) ;
popup_centered_ratio ( ) ;
}
2017-03-05 16:44:50 +01:00
void OrphanResourcesDialog : : _find_to_delete ( TreeItem * p_item , List < String > & paths ) {
2015-11-29 05:08:31 +01:00
2017-03-05 16:44:50 +01:00
while ( p_item ) {
2015-11-29 05:08:31 +01:00
2017-03-05 16:44:50 +01:00
if ( p_item - > get_cell_mode ( 0 ) = = TreeItem : : CELL_MODE_CHECK & & p_item - > is_checked ( 0 ) ) {
2015-11-29 05:08:31 +01:00
paths . push_back ( p_item - > get_metadata ( 0 ) ) ;
}
if ( p_item - > get_children ( ) ) {
2017-03-05 16:44:50 +01:00
_find_to_delete ( p_item - > get_children ( ) , paths ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
p_item = p_item - > get_next ( ) ;
2015-11-29 05:08:31 +01:00
}
}
void OrphanResourcesDialog : : _delete_confirm ( ) {
DirAccess * da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * E = paths . front ( ) ; E ; E = E - > next ( ) ) {
2015-11-29 05:08:31 +01:00
da - > remove ( E - > get ( ) ) ;
EditorFileSystem : : get_singleton ( ) - > update_file ( E - > get ( ) ) ;
}
memdelete ( da ) ;
refresh ( ) ;
}
2017-03-05 16:44:50 +01:00
void OrphanResourcesDialog : : _button_pressed ( Object * p_item , int p_column , int p_id ) {
2015-11-29 05:08:31 +01:00
2017-08-24 22:58:51 +02:00
TreeItem * ti = Object : : cast_to < TreeItem > ( p_item ) ;
2015-11-29 05:08:31 +01:00
String path = ti - > get_metadata ( 0 ) ;
dep_edit - > edit ( path ) ;
}
void OrphanResourcesDialog : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " _delete_confirm " ) , & OrphanResourcesDialog : : _delete_confirm ) ;
ClassDB : : bind_method ( D_METHOD ( " _button_pressed " ) , & OrphanResourcesDialog : : _button_pressed ) ;
2015-11-29 05:08:31 +01:00
}
2017-03-05 16:44:50 +01:00
OrphanResourcesDialog : : OrphanResourcesDialog ( ) {
2015-11-29 05:08:31 +01:00
2019-06-15 09:03:28 +02:00
set_title ( TTR ( " Orphan Resource Explorer " ) ) ;
delete_confirm = memnew ( ConfirmationDialog ) ;
get_ok ( ) - > set_text ( TTR ( " Delete " ) ) ;
add_child ( delete_confirm ) ;
dep_edit = memnew ( DependencyEditor ) ;
add_child ( dep_edit ) ;
delete_confirm - > connect ( " confirmed " , this , " _delete_confirm " ) ;
set_hide_on_ok ( false ) ;
2017-03-05 16:44:50 +01:00
VBoxContainer * vbc = memnew ( VBoxContainer ) ;
2015-11-29 05:08:31 +01:00
add_child ( vbc ) ;
2017-01-10 05:49:55 +01:00
2017-03-05 16:44:50 +01:00
files = memnew ( Tree ) ;
2015-11-29 05:08:31 +01:00
files - > set_columns ( 2 ) ;
files - > set_column_titles_visible ( true ) ;
2017-03-05 16:44:50 +01:00
files - > set_column_min_width ( 1 , 100 ) ;
files - > set_column_expand ( 0 , true ) ;
files - > set_column_expand ( 1 , false ) ;
files - > set_column_title ( 0 , TTR ( " Resource " ) ) ;
files - > set_column_title ( 1 , TTR ( " Owns " ) ) ;
2015-11-29 05:08:31 +01:00
files - > set_hide_root ( true ) ;
2017-03-05 16:44:50 +01:00
vbc - > add_margin_child ( TTR ( " Resources Without Explicit Ownership: " ) , files , true ) ;
files - > connect ( " button_pressed " , this , " _button_pressed " ) ;
2015-11-29 05:08:31 +01:00
}