rename_dialog.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*************************************************************************/
  2. /* rename_dialog.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "rename_dialog.h"
  31. #include "core/print_string.h"
  32. #include "editor_node.h"
  33. #include "editor_settings.h"
  34. #include "editor_themes.h"
  35. #include "modules/regex/regex.h"
  36. #include "plugins/script_editor_plugin.h"
  37. #include "scene/gui/control.h"
  38. #include "scene/gui/label.h"
  39. #include "scene/gui/tab_container.h"
  40. RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo) {
  41. scene_tree_editor = p_scene_tree_editor;
  42. undo_redo = p_undo_redo;
  43. preview_node = NULL;
  44. set_title(TTR("Batch Rename"));
  45. VBoxContainer *vbc = memnew(VBoxContainer);
  46. add_child(vbc);
  47. // -- Search/Replace Area
  48. GridContainer *grd_main = memnew(GridContainer);
  49. grd_main->set_columns(2);
  50. grd_main->set_v_size_flags(SIZE_EXPAND_FILL);
  51. vbc->add_child(grd_main);
  52. // ---- 1st & 2nd row
  53. Label *lbl_search = memnew(Label);
  54. lbl_search->set_text(TTR("Search"));
  55. lne_search = memnew(LineEdit);
  56. lne_search->set_placeholder(TTR("Search"));
  57. lne_search->set_name("lne_search");
  58. lne_search->set_h_size_flags(SIZE_EXPAND_FILL);
  59. Label *lbl_replace = memnew(Label);
  60. lbl_replace->set_text(TTR("Replace"));
  61. lne_replace = memnew(LineEdit);
  62. lne_replace->set_placeholder(TTR("Replace"));
  63. lne_replace->set_name("lne_replace");
  64. lne_replace->set_h_size_flags(SIZE_EXPAND_FILL);
  65. grd_main->add_child(lbl_search);
  66. grd_main->add_child(lbl_replace);
  67. grd_main->add_child(lne_search);
  68. grd_main->add_child(lne_replace);
  69. // ---- 3rd & 4th row
  70. Label *lbl_prefix = memnew(Label);
  71. lbl_prefix->set_text(TTR("Prefix"));
  72. lne_prefix = memnew(LineEdit);
  73. lne_prefix->set_placeholder(TTR("Prefix"));
  74. lne_prefix->set_name("lne_prefix");
  75. lne_prefix->set_h_size_flags(SIZE_EXPAND_FILL);
  76. Label *lbl_suffix = memnew(Label);
  77. lbl_suffix->set_text(TTR("Suffix"));
  78. lne_suffix = memnew(LineEdit);
  79. lne_suffix->set_placeholder(TTR("Suffix"));
  80. lne_suffix->set_name("lne_suffix");
  81. lne_suffix->set_h_size_flags(SIZE_EXPAND_FILL);
  82. grd_main->add_child(lbl_prefix);
  83. grd_main->add_child(lbl_suffix);
  84. grd_main->add_child(lne_prefix);
  85. grd_main->add_child(lne_suffix);
  86. // -- Feature Tabs
  87. const int feature_min_height = 160;
  88. Ref<Theme> collapse_theme = create_editor_theme();
  89. collapse_theme->set_icon("checked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
  90. collapse_theme->set_icon("unchecked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
  91. CheckBox *chk_collapse_features = memnew(CheckBox);
  92. chk_collapse_features->set_text(TTR("Advanced options"));
  93. chk_collapse_features->set_theme(collapse_theme);
  94. chk_collapse_features->set_focus_mode(FOCUS_NONE);
  95. vbc->add_child(chk_collapse_features);
  96. tabc_features = memnew(TabContainer);
  97. tabc_features->set_tab_align(TabContainer::ALIGN_LEFT);
  98. vbc->add_child(tabc_features);
  99. // ---- Tab Substitute
  100. VBoxContainer *vbc_substitute = memnew(VBoxContainer);
  101. vbc_substitute->set_h_size_flags(SIZE_EXPAND_FILL);
  102. vbc_substitute->set_custom_minimum_size(Size2(0, feature_min_height));
  103. vbc_substitute->set_name(TTR("Substitute"));
  104. tabc_features->add_child(vbc_substitute);
  105. cbut_substitute = memnew(CheckButton);
  106. cbut_substitute->set_text(TTR("Substitute"));
  107. vbc_substitute->add_child(cbut_substitute);
  108. GridContainer *grd_substitute = memnew(GridContainer);
  109. grd_substitute->set_columns(3);
  110. vbc_substitute->add_child(grd_substitute);
  111. // Name
  112. but_insert_name = memnew(Button);
  113. but_insert_name->set_text("NAME");
  114. but_insert_name->set_tooltip(String("${NAME}\n") + TTR("Node name"));
  115. but_insert_name->set_focus_mode(FOCUS_NONE);
  116. but_insert_name->connect("pressed", this, "_insert_text", make_binds("${NAME}"));
  117. but_insert_name->set_h_size_flags(SIZE_EXPAND_FILL);
  118. grd_substitute->add_child(but_insert_name);
  119. // Parent
  120. but_insert_parent = memnew(Button);
  121. but_insert_parent->set_text("PARENT");
  122. but_insert_parent->set_tooltip(String("${PARENT}\n") + TTR("Node's parent name, if available"));
  123. but_insert_parent->set_focus_mode(FOCUS_NONE);
  124. but_insert_parent->connect("pressed", this, "_insert_text", make_binds("${PARENT}"));
  125. but_insert_parent->set_h_size_flags(SIZE_EXPAND_FILL);
  126. grd_substitute->add_child(but_insert_parent);
  127. // Type
  128. but_insert_type = memnew(Button);
  129. but_insert_type->set_text("TYPE");
  130. but_insert_type->set_tooltip(String("${TYPE}\n") + TTR("Node type"));
  131. but_insert_type->set_focus_mode(FOCUS_NONE);
  132. but_insert_type->connect("pressed", this, "_insert_text", make_binds("${TYPE}"));
  133. but_insert_type->set_h_size_flags(SIZE_EXPAND_FILL);
  134. grd_substitute->add_child(but_insert_type);
  135. // Scene
  136. but_insert_scene = memnew(Button);
  137. but_insert_scene->set_text("SCENE");
  138. but_insert_scene->set_tooltip(String("${SCENE}\n") + TTR("Current scene name"));
  139. but_insert_scene->set_focus_mode(FOCUS_NONE);
  140. but_insert_scene->connect("pressed", this, "_insert_text", make_binds("${SCENE}"));
  141. but_insert_scene->set_h_size_flags(SIZE_EXPAND_FILL);
  142. grd_substitute->add_child(but_insert_scene);
  143. // Root
  144. but_insert_root = memnew(Button);
  145. but_insert_root->set_text("ROOT");
  146. but_insert_root->set_tooltip(String("${ROOT}\n") + TTR("Root node name"));
  147. but_insert_root->set_focus_mode(FOCUS_NONE);
  148. but_insert_root->connect("pressed", this, "_insert_text", make_binds("${ROOT}"));
  149. but_insert_root->set_h_size_flags(SIZE_EXPAND_FILL);
  150. grd_substitute->add_child(but_insert_root);
  151. // Count
  152. but_insert_count = memnew(Button);
  153. but_insert_count->set_text("COUNTER");
  154. but_insert_count->set_tooltip(String("${COUNTER}\n") + TTR("Sequential integer counter.\nCompare counter options."));
  155. but_insert_count->set_focus_mode(FOCUS_NONE);
  156. but_insert_count->connect("pressed", this, "_insert_text", make_binds("${COUNTER}"));
  157. but_insert_count->set_h_size_flags(SIZE_EXPAND_FILL);
  158. grd_substitute->add_child(but_insert_count);
  159. chk_per_level_counter = memnew(CheckBox);
  160. chk_per_level_counter->set_text(TTR("Per Level counter"));
  161. chk_per_level_counter->set_tooltip(TTR("If set the counter restarts for each group of child nodes"));
  162. vbc_substitute->add_child(chk_per_level_counter);
  163. HBoxContainer *hbc_count_options = memnew(HBoxContainer);
  164. vbc_substitute->add_child(hbc_count_options);
  165. Label *lbl_count_start = memnew(Label);
  166. lbl_count_start->set_text(TTR("Start"));
  167. lbl_count_start->set_tooltip(TTR("Initial value for the counter"));
  168. hbc_count_options->add_child(lbl_count_start);
  169. spn_count_start = memnew(SpinBox);
  170. spn_count_start->set_tooltip(TTR("Initial value for the counter"));
  171. spn_count_start->set_step(1);
  172. spn_count_start->set_min(0);
  173. hbc_count_options->add_child(spn_count_start);
  174. Label *lbl_count_step = memnew(Label);
  175. lbl_count_step->set_text(TTR("Step"));
  176. lbl_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node"));
  177. hbc_count_options->add_child(lbl_count_step);
  178. spn_count_step = memnew(SpinBox);
  179. spn_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node"));
  180. spn_count_step->set_step(1);
  181. hbc_count_options->add_child(spn_count_step);
  182. Label *lbl_count_padding = memnew(Label);
  183. lbl_count_padding->set_text(TTR("Padding"));
  184. lbl_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros."));
  185. hbc_count_options->add_child(lbl_count_padding);
  186. spn_count_padding = memnew(SpinBox);
  187. spn_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros."));
  188. spn_count_padding->set_step(1);
  189. hbc_count_options->add_child(spn_count_padding);
  190. // ---- Tab RegEx
  191. VBoxContainer *vbc_regex = memnew(VBoxContainer);
  192. vbc_regex->set_h_size_flags(SIZE_EXPAND_FILL);
  193. vbc_regex->set_name(TTR("Regular Expressions"));
  194. vbc_regex->set_custom_minimum_size(Size2(0, feature_min_height));
  195. tabc_features->add_child(vbc_regex);
  196. cbut_regex = memnew(CheckButton);
  197. cbut_regex->set_text(TTR("Regular Expressions"));
  198. vbc_regex->add_child(cbut_regex);
  199. // ---- Tab Process
  200. VBoxContainer *vbc_process = memnew(VBoxContainer);
  201. vbc_process->set_h_size_flags(SIZE_EXPAND_FILL);
  202. vbc_process->set_name(TTR("Post-Process"));
  203. vbc_process->set_custom_minimum_size(Size2(0, feature_min_height));
  204. tabc_features->add_child(vbc_process);
  205. cbut_process = memnew(CheckButton);
  206. cbut_process->set_text(TTR("Post-Process"));
  207. vbc_process->add_child(cbut_process);
  208. // ------ Style
  209. HBoxContainer *hbc_style = memnew(HBoxContainer);
  210. vbc_process->add_child(hbc_style);
  211. Label *lbl_style = memnew(Label);
  212. lbl_style->set_text(TTR("Style"));
  213. hbc_style->add_child(lbl_style);
  214. opt_style = memnew(OptionButton);
  215. opt_style->add_item(TTR("Keep"));
  216. opt_style->add_item(TTR("CamelCase to under_scored"));
  217. opt_style->add_item(TTR("under_scored to CamelCase"));
  218. hbc_style->add_child(opt_style);
  219. // ------ Case
  220. HBoxContainer *hbc_case = memnew(HBoxContainer);
  221. vbc_process->add_child(hbc_case);
  222. Label *lbl_case = memnew(Label);
  223. lbl_case->set_text(TTR("Case"));
  224. hbc_case->add_child(lbl_case);
  225. opt_case = memnew(OptionButton);
  226. opt_case->add_item(TTR("Keep"));
  227. opt_case->add_item(TTR("To Lowercase"));
  228. opt_case->add_item(TTR("To Uppercase"));
  229. hbc_case->add_child(opt_case);
  230. // -- Preview
  231. HSeparator *sep_preview = memnew(HSeparator);
  232. sep_preview->set_custom_minimum_size(Size2(10, 20));
  233. vbc->add_child(sep_preview);
  234. lbl_preview_title = memnew(Label);
  235. lbl_preview_title->set_text(TTR("Preview"));
  236. vbc->add_child(lbl_preview_title);
  237. lbl_preview = memnew(Label);
  238. lbl_preview->set_text("");
  239. lbl_preview->add_color_override("font_color", Color(1, 0.5f, 0, 1));
  240. vbc->add_child(lbl_preview);
  241. // ---- Dialog related
  242. set_custom_minimum_size(Size2(383, 0));
  243. set_as_toplevel(true);
  244. get_ok()->set_text(TTR("Rename"));
  245. Button *but_reset = add_button(TTR("Reset"));
  246. eh.errfunc = _error_handler;
  247. eh.userdata = this;
  248. // ---- Connections
  249. chk_collapse_features->connect("toggled", this, "_features_toggled");
  250. // Substitite Buttons
  251. lne_search->connect("focus_entered", this, "_update_substitute");
  252. lne_search->connect("focus_exited", this, "_update_substitute");
  253. lne_replace->connect("focus_entered", this, "_update_substitute");
  254. lne_replace->connect("focus_exited", this, "_update_substitute");
  255. lne_prefix->connect("focus_entered", this, "_update_substitute");
  256. lne_prefix->connect("focus_exited", this, "_update_substitute");
  257. lne_suffix->connect("focus_entered", this, "_update_substitute");
  258. lne_suffix->connect("focus_exited", this, "_update_substitute");
  259. // Preview
  260. lne_prefix->connect("text_changed", this, "_update_preview");
  261. lne_suffix->connect("text_changed", this, "_update_preview");
  262. lne_search->connect("text_changed", this, "_update_preview");
  263. lne_replace->connect("text_changed", this, "_update_preview");
  264. spn_count_start->connect("value_changed", this, "_update_preview_int");
  265. spn_count_step->connect("value_changed", this, "_update_preview_int");
  266. spn_count_padding->connect("value_changed", this, "_update_preview_int");
  267. opt_style->connect("item_selected", this, "_update_preview_int");
  268. opt_case->connect("item_selected", this, "_update_preview_int");
  269. cbut_substitute->connect("pressed", this, "_update_preview", varray(""));
  270. cbut_regex->connect("pressed", this, "_update_preview", varray(""));
  271. cbut_process->connect("pressed", this, "_update_preview", varray(""));
  272. but_reset->connect("pressed", this, "reset");
  273. reset();
  274. _features_toggled(false);
  275. }
  276. void RenameDialog::_bind_methods() {
  277. ClassDB::bind_method("_features_toggled", &RenameDialog::_features_toggled);
  278. ClassDB::bind_method("_update_preview", &RenameDialog::_update_preview);
  279. ClassDB::bind_method("_update_preview_int", &RenameDialog::_update_preview_int);
  280. ClassDB::bind_method("_insert_text", &RenameDialog::_insert_text);
  281. ClassDB::bind_method("_update_substitute", &RenameDialog::_update_substitute);
  282. ClassDB::bind_method("reset", &RenameDialog::reset);
  283. ClassDB::bind_method("rename", &RenameDialog::rename);
  284. }
  285. void RenameDialog::_update_substitute() {
  286. LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_focus_owner());
  287. bool is_main_field = _is_main_field(focus_owner_line_edit);
  288. but_insert_name->set_disabled(!is_main_field);
  289. but_insert_parent->set_disabled(!is_main_field);
  290. but_insert_type->set_disabled(!is_main_field);
  291. but_insert_scene->set_disabled(!is_main_field);
  292. but_insert_root->set_disabled(!is_main_field);
  293. but_insert_count->set_disabled(!is_main_field);
  294. // The focus mode seems to be reset when disabling/re-enabling
  295. but_insert_name->set_focus_mode(FOCUS_NONE);
  296. but_insert_parent->set_focus_mode(FOCUS_NONE);
  297. but_insert_type->set_focus_mode(FOCUS_NONE);
  298. but_insert_scene->set_focus_mode(FOCUS_NONE);
  299. but_insert_root->set_focus_mode(FOCUS_NONE);
  300. but_insert_count->set_focus_mode(FOCUS_NONE);
  301. }
  302. void RenameDialog::_post_popup() {
  303. EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
  304. preview_node = NULL;
  305. Array selected_node_list = editor_selection->get_selected_nodes();
  306. ERR_FAIL_COND(selected_node_list.size() == 0);
  307. preview_node = selected_node_list[0];
  308. _update_preview();
  309. _update_substitute();
  310. }
  311. void RenameDialog::_update_preview_int(int new_value) {
  312. _update_preview();
  313. }
  314. void RenameDialog::_update_preview(String new_text) {
  315. if (lock_preview_update || preview_node == NULL)
  316. return;
  317. has_errors = false;
  318. add_error_handler(&eh);
  319. String new_name = _apply_rename(preview_node, spn_count_start->get_value());
  320. if (!has_errors) {
  321. lbl_preview_title->set_text(TTR("Preview"));
  322. lbl_preview->set_text(new_name);
  323. if (new_name == preview_node->get_name()) {
  324. lbl_preview->add_color_override("font_color", Color(0, 0.5f, 0.25f, 1));
  325. } else {
  326. lbl_preview->add_color_override("font_color", Color(0, 1, 0.5f, 1));
  327. }
  328. }
  329. remove_error_handler(&eh);
  330. }
  331. String RenameDialog::_apply_rename(const Node *node, int count) {
  332. String search = lne_search->get_text();
  333. String replace = lne_replace->get_text();
  334. String prefix = lne_prefix->get_text();
  335. String suffix = lne_suffix->get_text();
  336. String new_name = node->get_name();
  337. if (cbut_substitute->is_pressed()) {
  338. search = _substitute(search, node, count);
  339. replace = _substitute(replace, node, count);
  340. prefix = _substitute(prefix, node, count);
  341. suffix = _substitute(suffix, node, count);
  342. }
  343. if (cbut_regex->is_pressed()) {
  344. new_name = _regex(search, new_name, replace);
  345. } else {
  346. new_name = new_name.replace(search, replace);
  347. }
  348. new_name = prefix + new_name + suffix;
  349. if (cbut_process->is_pressed()) {
  350. new_name = _postprocess(new_name);
  351. }
  352. return new_name;
  353. }
  354. String RenameDialog::_substitute(const String &subject, const Node *node, int count) {
  355. String result = subject.replace("${COUNTER}", vformat("%0" + itos(spn_count_padding->get_value()) + "d", count));
  356. if (node) {
  357. result = result.replace("${NAME}", node->get_name());
  358. result = result.replace("${TYPE}", node->get_class());
  359. }
  360. int current = EditorNode::get_singleton()->get_editor_data().get_edited_scene();
  361. result = result.replace("${SCENE}", EditorNode::get_singleton()->get_editor_data().get_scene_title(current));
  362. Node *root_node = SceneTree::get_singleton()->get_edited_scene_root();
  363. if (root_node) {
  364. result = result.replace("${ROOT}", root_node->get_name());
  365. }
  366. Node *parent_node = node->get_parent();
  367. if (parent_node) {
  368. if (node == root_node) {
  369. // Can not substitute parent of root.
  370. result = result.replace("${PARENT}", "");
  371. } else {
  372. result = result.replace("${PARENT}", parent_node->get_name());
  373. }
  374. }
  375. return result;
  376. }
  377. void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) {
  378. RenameDialog *self = (RenameDialog *)p_self;
  379. String source_file(p_file);
  380. // Only show first error that is related to "regex"
  381. if (self->has_errors || source_file.find("regex") < 0)
  382. return;
  383. String err_str;
  384. if (p_errorexp && p_errorexp[0]) {
  385. err_str = p_errorexp;
  386. } else {
  387. err_str = p_error;
  388. }
  389. self->has_errors = true;
  390. self->lbl_preview_title->set_text(TTR("Error"));
  391. self->lbl_preview->add_color_override("font_color", Color(1, 0.25f, 0, 1));
  392. self->lbl_preview->set_text(err_str);
  393. }
  394. String RenameDialog::_regex(const String &pattern, const String &subject, const String &replacement) {
  395. RegEx regex(pattern);
  396. return regex.sub(subject, replacement, true);
  397. }
  398. String RenameDialog::_postprocess(const String &subject) {
  399. int style_id = opt_style->get_selected();
  400. String result = subject;
  401. if (style_id == 1) {
  402. // CamelCase to Under_Line
  403. result = result.camelcase_to_underscore(true);
  404. result = _regex("_+", result, "_");
  405. } else if (style_id == 2) {
  406. // Under_Line to CamelCase
  407. RegEx pattern("_+(.?)");
  408. Array matches = pattern.search_all(result);
  409. // _ name would become empty. Ignore
  410. if (matches.size() && result != "_") {
  411. String buffer;
  412. int start = 0;
  413. int end = 0;
  414. for (int i = 0; i < matches.size(); ++i) {
  415. start = ((Ref<RegExMatch>)matches[i])->get_start(1);
  416. buffer += result.substr(end, start - end - 1);
  417. buffer += result.substr(start, 1).to_upper();
  418. end = start + 1;
  419. }
  420. buffer += result.substr(end, result.size() - (end + 1));
  421. result = buffer.replace("_", "").capitalize();
  422. }
  423. }
  424. int case_id = opt_case->get_selected();
  425. if (case_id == 1) {
  426. // To Lowercase
  427. result = result.to_lower();
  428. } else if (case_id == 2) {
  429. // To Upercase
  430. result = result.to_upper();
  431. }
  432. return result;
  433. }
  434. void RenameDialog::_iterate_scene(const Node *node, const Array &selection, int *counter) {
  435. if (!node)
  436. return;
  437. if (selection.has(node)) {
  438. String new_name = _apply_rename(node, *counter);
  439. if (node->get_name() != new_name) {
  440. Pair<NodePath, String> rename_item;
  441. rename_item.first = node->get_path();
  442. rename_item.second = new_name;
  443. to_rename.push_back(rename_item);
  444. }
  445. *counter += spn_count_step->get_value();
  446. }
  447. int *cur_counter = counter;
  448. int level_counter = spn_count_start->get_value();
  449. if (chk_per_level_counter->is_pressed()) {
  450. cur_counter = &level_counter;
  451. }
  452. for (int i = 0; i < node->get_child_count(); ++i) {
  453. _iterate_scene(node->get_child(i), selection, cur_counter);
  454. }
  455. }
  456. void RenameDialog::rename() {
  457. // Editor selection is not ordered via scene tree. Instead iterate
  458. // over scene tree until all selected nodes are found in order.
  459. EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
  460. Array selected_node_list = editor_selection->get_selected_nodes();
  461. Node *root_node = SceneTree::get_singleton()->get_edited_scene_root();
  462. global_count = spn_count_start->get_value();
  463. to_rename.clear();
  464. // Forward recursive as opposed to the actual renaming.
  465. _iterate_scene(root_node, selected_node_list, &global_count);
  466. if (undo_redo && !to_rename.empty()) {
  467. undo_redo->create_action(TTR("Batch Rename"));
  468. // Make sure to iterate reversed so that child nodes will find parents.
  469. for (int i = to_rename.size() - 1; i >= 0; --i) {
  470. Node *n = root_node->get_node(to_rename[i].first);
  471. const String &new_name = to_rename[i].second;
  472. if (!n) {
  473. ERR_PRINTS("Skipping missing node: " + to_rename[i].first.get_concatenated_subnames());
  474. continue;
  475. }
  476. scene_tree_editor->emit_signal("node_prerename", n, new_name);
  477. undo_redo->add_do_method(scene_tree_editor, "_rename_node", n->get_instance_id(), new_name);
  478. undo_redo->add_undo_method(scene_tree_editor, "_rename_node", n->get_instance_id(), n->get_name());
  479. }
  480. undo_redo->commit_action();
  481. }
  482. }
  483. void RenameDialog::reset() {
  484. lock_preview_update = true;
  485. lne_prefix->clear();
  486. lne_suffix->clear();
  487. lne_search->clear();
  488. lne_replace->clear();
  489. cbut_substitute->set_pressed(false);
  490. cbut_regex->set_pressed(false);
  491. cbut_process->set_pressed(false);
  492. chk_per_level_counter->set_pressed(true);
  493. spn_count_start->set_value(1);
  494. spn_count_step->set_value(1);
  495. spn_count_padding->set_value(1);
  496. opt_style->select(0);
  497. opt_case->select(0);
  498. lock_preview_update = false;
  499. _update_preview();
  500. }
  501. bool RenameDialog::_is_main_field(LineEdit *line_edit) {
  502. return line_edit &&
  503. (line_edit == lne_search || line_edit == lne_replace || line_edit == lne_prefix || line_edit == lne_suffix);
  504. }
  505. void RenameDialog::_insert_text(String text) {
  506. LineEdit *focus_owner = Object::cast_to<LineEdit>(get_focus_owner());
  507. if (_is_main_field(focus_owner)) {
  508. focus_owner->selection_delete();
  509. focus_owner->append_at_cursor(text);
  510. _update_preview();
  511. }
  512. }
  513. void RenameDialog::_features_toggled(bool pressed) {
  514. if (pressed) {
  515. tabc_features->show();
  516. } else {
  517. tabc_features->hide();
  518. }
  519. // Adjust to minimum size in y
  520. Size2i size = get_size();
  521. size.y = 0;
  522. set_size(size);
  523. }