script_editor_plugin.cpp 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440
  1. /*************************************************************************/
  2. /* script_editor_plugin.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* */
  10. /* Permission is hereby granted, free of charge, to any person obtaining */
  11. /* a copy of this software and associated documentation files (the */
  12. /* "Software"), to deal in the Software without restriction, including */
  13. /* without limitation the rights to use, copy, modify, merge, publish, */
  14. /* distribute, sublicense, and/or sell copies of the Software, and to */
  15. /* permit persons to whom the Software is furnished to do so, subject to */
  16. /* the following conditions: */
  17. /* */
  18. /* The above copyright notice and this permission notice shall be */
  19. /* included in all copies or substantial portions of the Software. */
  20. /* */
  21. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  22. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  23. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  24. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  25. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  26. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  27. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  28. /*************************************************************************/
  29. #include "script_editor_plugin.h"
  30. #include "tools/editor/editor_settings.h"
  31. #include "io/resource_loader.h"
  32. #include "io/resource_saver.h"
  33. #include "os/keyboard.h"
  34. #include "os/os.h"
  35. #include "tools/editor/editor_node.h"
  36. #include "tools/editor/script_editor_debugger.h"
  37. #include "globals.h"
  38. #include "os/file_access.h"
  39. #include "scene/main/viewport.h"
  40. #include "os/keyboard.h"
  41. #include "os/input.h"
  42. /*** SCRIPT EDITOR ****/
  43. void ScriptEditorBase::_bind_methods() {
  44. ADD_SIGNAL(MethodInfo("name_changed"));
  45. ADD_SIGNAL(MethodInfo("request_help_search",PropertyInfo(Variant::STRING,"topic")));
  46. ADD_SIGNAL(MethodInfo("request_open_script_at_line",PropertyInfo(Variant::OBJECT,"script"),PropertyInfo(Variant::INT,"line")));
  47. ADD_SIGNAL(MethodInfo("request_save_history"));
  48. ADD_SIGNAL(MethodInfo("go_to_help",PropertyInfo(Variant::STRING,"what")));
  49. }
  50. static bool _can_open_in_editor(Script* p_script) {
  51. String path = p_script->get_path();
  52. if (path.find("::")!=-1) {
  53. //refuse handling this if it can't be edited
  54. bool valid=false;
  55. for(int i=0;i<EditorNode::get_singleton()->get_editor_data().get_edited_scene_count();i++) {
  56. if (path.begins_with(EditorNode::get_singleton()->get_editor_data().get_scene_path(i))) {
  57. valid=true;
  58. break;
  59. }
  60. }
  61. return valid;
  62. }
  63. return true;
  64. }
  65. class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
  66. struct Cache {
  67. uint64_t time_loaded;
  68. RES cache;
  69. };
  70. Map<String,Cache> cached;
  71. public:
  72. uint64_t max_time_cache;
  73. int max_cache_size;
  74. void cleanup() {
  75. List< Map<String,Cache>::Element * > to_clean;
  76. Map<String,Cache>::Element *I=cached.front();
  77. while(I) {
  78. if ((OS::get_singleton()->get_ticks_msec()-I->get().time_loaded)>max_time_cache) {
  79. to_clean.push_back(I);
  80. }
  81. I=I->next();
  82. }
  83. while(to_clean.front()) {
  84. cached.erase(to_clean.front()->get());
  85. to_clean.pop_front();
  86. }
  87. }
  88. RES get_cached_resource(const String& p_path) {
  89. Map<String,Cache>::Element *E=cached.find(p_path);
  90. if (!E) {
  91. Cache c;
  92. c.cache=ResourceLoader::load(p_path);
  93. E=cached.insert(p_path,c);
  94. }
  95. E->get().time_loaded=OS::get_singleton()->get_ticks_msec();
  96. if (cached.size()>max_cache_size) {
  97. uint64_t older;
  98. Map<String,Cache>::Element *O=cached.front();
  99. older=O->get().time_loaded;
  100. Map<String,Cache>::Element *I=O;
  101. while(I) {
  102. if (I->get().time_loaded<older) {
  103. older = I->get().time_loaded;
  104. O=I;
  105. }
  106. I=I->next();
  107. }
  108. if (O!=E) {//should never heppane..
  109. cached.erase(O);
  110. }
  111. }
  112. return E->get().cache;
  113. }
  114. EditorScriptCodeCompletionCache() {
  115. max_cache_size=128;
  116. max_time_cache=5*60*1000; //minutes, five
  117. }
  118. };
  119. #define SORT_SCRIPT_LIST
  120. void ScriptEditorQuickOpen::popup(const Vector<String>& p_functions, bool p_dontclear) {
  121. popup_centered_ratio(0.6);
  122. if (p_dontclear)
  123. search_box->select_all();
  124. else
  125. search_box->clear();
  126. search_box->grab_focus();
  127. functions=p_functions;
  128. _update_search();
  129. }
  130. void ScriptEditorQuickOpen::_text_changed(const String& p_newtext) {
  131. _update_search();
  132. }
  133. void ScriptEditorQuickOpen::_sbox_input(const InputEvent& p_ie) {
  134. if (p_ie.type==InputEvent::KEY && (
  135. p_ie.key.scancode == KEY_UP ||
  136. p_ie.key.scancode == KEY_DOWN ||
  137. p_ie.key.scancode == KEY_PAGEUP ||
  138. p_ie.key.scancode == KEY_PAGEDOWN ) ) {
  139. search_options->call("_gui_input",p_ie);
  140. search_box->accept_event();
  141. }
  142. }
  143. void ScriptEditorQuickOpen::_update_search() {
  144. search_options->clear();
  145. TreeItem *root = search_options->create_item();
  146. for(int i=0;i<functions.size();i++) {
  147. String file = functions[i];
  148. if ((search_box->get_text()=="" || file.findn(search_box->get_text())!=-1)) {
  149. TreeItem *ti = search_options->create_item(root);
  150. ti->set_text(0,file);
  151. if (root->get_children()==ti)
  152. ti->select(0);
  153. }
  154. }
  155. get_ok()->set_disabled(root->get_children()==NULL);
  156. }
  157. void ScriptEditorQuickOpen::_confirmed() {
  158. TreeItem *ti = search_options->get_selected();
  159. if (!ti)
  160. return;
  161. int line = ti->get_text(0).get_slice(":",1).to_int();
  162. emit_signal("goto_line",line-1);
  163. hide();
  164. }
  165. void ScriptEditorQuickOpen::_notification(int p_what) {
  166. if (p_what==NOTIFICATION_ENTER_TREE) {
  167. connect("confirmed",this,"_confirmed");
  168. }
  169. }
  170. void ScriptEditorQuickOpen::_bind_methods() {
  171. ClassDB::bind_method(_MD("_text_changed"),&ScriptEditorQuickOpen::_text_changed);
  172. ClassDB::bind_method(_MD("_confirmed"),&ScriptEditorQuickOpen::_confirmed);
  173. ClassDB::bind_method(_MD("_sbox_input"),&ScriptEditorQuickOpen::_sbox_input);
  174. ADD_SIGNAL(MethodInfo("goto_line",PropertyInfo(Variant::INT,"line")));
  175. }
  176. ScriptEditorQuickOpen::ScriptEditorQuickOpen() {
  177. VBoxContainer *vbc = memnew( VBoxContainer );
  178. add_child(vbc);
  179. //set_child_rect(vbc);
  180. search_box = memnew( LineEdit );
  181. vbc->add_margin_child(TTR("Search:"),search_box);
  182. search_box->connect("text_changed",this,"_text_changed");
  183. search_box->connect("gui_input",this,"_sbox_input");
  184. search_options = memnew( Tree );
  185. vbc->add_margin_child(TTR("Matches:"),search_options,true);
  186. get_ok()->set_text(TTR("Open"));
  187. get_ok()->set_disabled(true);
  188. register_text_enter(search_box);
  189. set_hide_on_ok(false);
  190. search_options->connect("item_activated",this,"_confirmed");
  191. search_options->set_hide_root(true);
  192. }
  193. /////////////////////////////////
  194. ScriptEditor *ScriptEditor::script_editor=NULL;
  195. /*** SCRIPT EDITOR ******/
  196. String ScriptEditor::_get_debug_tooltip(const String&p_text,Node *_se) {
  197. // ScriptEditorBase *se=_se->cast_to<ScriptEditorBase>();
  198. String val = debugger->get_var_value(p_text);
  199. if (val!=String()) {
  200. return p_text+": "+val;
  201. } else {
  202. return String();
  203. }
  204. }
  205. void ScriptEditor::_breaked(bool p_breaked,bool p_can_debug) {
  206. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug));
  207. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug) );
  208. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked );
  209. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked );
  210. for(int i=0;i<tab_container->get_child_count();i++) {
  211. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  212. if (!se) {
  213. continue;
  214. }
  215. se->set_debugger_active(p_breaked);
  216. }
  217. }
  218. void ScriptEditor::_show_debugger(bool p_show) {
  219. // debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show);
  220. }
  221. void ScriptEditor::_script_created(Ref<Script> p_script) {
  222. editor->push_item(p_script.operator->());
  223. }
  224. void ScriptEditor::_goto_script_line2(int p_line) {
  225. int selected = tab_container->get_current_tab();
  226. if (selected<0 || selected>=tab_container->get_child_count())
  227. return;
  228. ScriptEditorBase *current = tab_container->get_child(selected)->cast_to<ScriptEditorBase>();
  229. if (!current)
  230. return;
  231. current->goto_line(p_line);
  232. }
  233. void ScriptEditor::_goto_script_line(REF p_script,int p_line) {
  234. editor->push_item(p_script.ptr());
  235. int selected = tab_container->get_current_tab();
  236. if (selected<0 || selected>=tab_container->get_child_count())
  237. return;
  238. ScriptEditorBase *current = tab_container->get_child(selected)->cast_to<ScriptEditorBase>();
  239. if (!current)
  240. return;
  241. current->goto_line(p_line,true);
  242. }
  243. void ScriptEditor::_update_history_arrows() {
  244. script_back->set_disabled( history_pos<=0 );
  245. script_forward->set_disabled( history_pos>=history.size()-1 );
  246. }
  247. void ScriptEditor::_save_history() {
  248. if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) {
  249. Node *n = tab_container->get_current_tab_control();
  250. if (n->cast_to<ScriptEditorBase>()) {
  251. history[history_pos].state=n->cast_to<ScriptEditorBase>()->get_edit_state();
  252. }
  253. if (n->cast_to<EditorHelp>()) {
  254. history[history_pos].state=n->cast_to<EditorHelp>()->get_scroll();
  255. }
  256. }
  257. history.resize(history_pos+1);
  258. ScriptHistory sh;
  259. sh.control=tab_container->get_current_tab_control();
  260. sh.state=Variant();
  261. history.push_back(sh);
  262. history_pos++;
  263. _update_history_arrows();
  264. }
  265. void ScriptEditor::_go_to_tab(int p_idx) {
  266. Node *cn = tab_container->get_child(p_idx);
  267. if (!cn)
  268. return;
  269. Control *c = cn->cast_to<Control>();
  270. if (!c)
  271. return;
  272. if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) {
  273. Node *n = tab_container->get_current_tab_control();
  274. if (n->cast_to<ScriptEditorBase>()) {
  275. history[history_pos].state=n->cast_to<ScriptEditorBase>()->get_edit_state();
  276. }
  277. if (n->cast_to<EditorHelp>()) {
  278. history[history_pos].state=n->cast_to<EditorHelp>()->get_scroll();
  279. }
  280. }
  281. history.resize(history_pos+1);
  282. ScriptHistory sh;
  283. sh.control=c;
  284. sh.state=Variant();
  285. history.push_back(sh);
  286. history_pos++;
  287. tab_container->set_current_tab(p_idx);
  288. c = tab_container->get_current_tab_control();
  289. if (c->cast_to<ScriptEditorBase>()) {
  290. script_name_label->set_text(c->cast_to<ScriptEditorBase>()->get_name());
  291. script_icon->set_texture(c->cast_to<ScriptEditorBase>()->get_icon());
  292. if (is_visible())
  293. c->cast_to<ScriptEditorBase>()->ensure_focus();
  294. }
  295. if (c->cast_to<EditorHelp>()) {
  296. script_name_label->set_text(c->cast_to<EditorHelp>()->get_class());
  297. script_icon->set_texture(get_icon("Help","EditorIcons"));
  298. if (is_visible())
  299. c->cast_to<EditorHelp>()->set_focused();
  300. }
  301. c->set_meta("__editor_pass",++edit_pass);
  302. _update_history_arrows();
  303. _update_script_colors();
  304. _update_selected_editor_menu();
  305. }
  306. void ScriptEditor::_close_tab(int p_idx) {
  307. int selected = p_idx;
  308. if (selected<0 || selected>=tab_container->get_child_count())
  309. return;
  310. Node *tselected = tab_container->get_child(selected);
  311. ScriptEditorBase *current = tab_container->get_child(selected)->cast_to<ScriptEditorBase>();
  312. if (current) {
  313. apply_scripts();
  314. if (current->get_edit_menu()) {
  315. memdelete(current->get_edit_menu());
  316. }
  317. }
  318. //remove from history
  319. history.resize(history_pos+1);
  320. for(int i=0;i<history.size();i++) {
  321. if (history[i].control==tselected) {
  322. history.remove(i);
  323. i--;
  324. history_pos--;
  325. }
  326. }
  327. if (history_pos>=history.size()) {
  328. history_pos=history.size()-1;
  329. }
  330. int idx = tab_container->get_current_tab();
  331. memdelete(tselected);
  332. if (idx>=tab_container->get_child_count())
  333. idx=tab_container->get_child_count()-1;
  334. if (idx>=0) {
  335. if (history_pos>=0) {
  336. idx = history[history_pos].control->get_index();
  337. }
  338. tab_container->set_current_tab(idx);
  339. //script_list->select(idx);
  340. }
  341. _update_history_arrows();
  342. _update_script_names();
  343. _save_layout();
  344. }
  345. void ScriptEditor::_close_current_tab() {
  346. _close_tab(tab_container->get_current_tab());
  347. }
  348. void ScriptEditor::_close_docs_tab() {
  349. int child_count = tab_container->get_child_count();
  350. for (int i = child_count-1; i>=0; i--) {
  351. EditorHelp *se = tab_container->get_child(i)->cast_to<EditorHelp>();
  352. if (se) {
  353. _close_tab(i);
  354. }
  355. }
  356. }
  357. void ScriptEditor::_close_all_tabs() {
  358. int child_count = tab_container->get_child_count();
  359. for (int i = child_count-1; i>=0; i--) {
  360. tab_container->set_current_tab(i);
  361. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  362. if (se) {
  363. // Maybe there are unsaved changes
  364. if (se->is_unsaved()) {
  365. _ask_close_current_unsaved_tab(se);
  366. continue;
  367. }
  368. }
  369. _close_current_tab();
  370. }
  371. }
  372. void ScriptEditor::_ask_close_current_unsaved_tab(ScriptEditorBase *current) {
  373. erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\"");
  374. erase_tab_confirm->popup_centered_minsize();
  375. }
  376. void ScriptEditor::_resave_scripts(const String& p_str) {
  377. apply_scripts();
  378. for(int i=0;i<tab_container->get_child_count();i++) {
  379. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  380. if (!se)
  381. continue;
  382. Ref<Script> script = se->get_edited_script();
  383. if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1)
  384. continue; //internal script, who cares
  385. if (trim_trailing_whitespace_on_save) {
  386. se->trim_trailing_whitespace();
  387. }
  388. editor->save_resource(script);
  389. se->tag_saved_version();
  390. }
  391. disk_changed->hide();
  392. }
  393. void ScriptEditor::_reload_scripts(){
  394. for(int i=0;i<tab_container->get_child_count();i++) {
  395. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  396. if (!se) {
  397. continue;
  398. }
  399. Ref<Script> script = se->get_edited_script();
  400. if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) {
  401. continue; //internal script, who cares
  402. }
  403. uint64_t last_date = script->get_last_modified_time();
  404. uint64_t date = FileAccess::get_modified_time(script->get_path());
  405. //printf("last date: %lli vs date: %lli\n",last_date,date);
  406. if (last_date==date) {
  407. continue;
  408. }
  409. Ref<Script> rel_script = ResourceLoader::load(script->get_path(),script->get_class(),true);
  410. ERR_CONTINUE(!rel_script.is_valid());
  411. script->set_source_code( rel_script->get_source_code() );
  412. script->set_last_modified_time( rel_script->get_last_modified_time() );
  413. script->reload();
  414. se->reload_text();
  415. }
  416. disk_changed->hide();
  417. _update_script_names();
  418. }
  419. void ScriptEditor::_res_saved_callback(const Ref<Resource>& p_res) {
  420. for(int i=0;i<tab_container->get_child_count();i++) {
  421. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  422. if (!se) {
  423. continue;
  424. }
  425. Ref<Script> script = se->get_edited_script();
  426. if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) {
  427. continue; //internal script, who cares
  428. }
  429. if (script==p_res) {
  430. se->tag_saved_version();
  431. }
  432. }
  433. _update_script_names();
  434. if (!pending_auto_reload && auto_reload_running_scripts) {
  435. call_deferred("_live_auto_reload_running_scripts");
  436. pending_auto_reload=true;
  437. }
  438. }
  439. void ScriptEditor::_live_auto_reload_running_scripts() {
  440. pending_auto_reload=false;
  441. debugger->reload_scripts();
  442. }
  443. bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) {
  444. disk_changed_list->clear();
  445. TreeItem *r = disk_changed_list->create_item();
  446. disk_changed_list->set_hide_root(true);
  447. bool need_ask=false;
  448. bool need_reload=false;
  449. bool use_autoreload=bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change",false));
  450. for(int i=0;i<tab_container->get_child_count();i++) {
  451. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  452. if (se) {
  453. Ref<Script> script = se->get_edited_script();
  454. if (p_for_script.is_valid() && p_for_script!=script)
  455. continue;
  456. if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1)
  457. continue; //internal script, who cares
  458. uint64_t last_date = script->get_last_modified_time();
  459. uint64_t date = FileAccess::get_modified_time(script->get_path());
  460. //printf("last date: %lli vs date: %lli\n",last_date,date);
  461. if (last_date!=date) {
  462. TreeItem *ti = disk_changed_list->create_item(r);
  463. ti->set_text(0,script->get_path().get_file());
  464. if (!use_autoreload || se->is_unsaved()) {
  465. need_ask=true;
  466. }
  467. need_reload=true;
  468. //r->set_metadata(0,);
  469. }
  470. }
  471. }
  472. if (need_reload) {
  473. if (!need_ask) {
  474. script_editor->_reload_scripts();
  475. need_reload=false;
  476. } else {
  477. disk_changed->call_deferred("popup_centered_ratio",0.5);
  478. }
  479. }
  480. return need_reload;
  481. }
  482. void ScriptEditor::_file_dialog_action(String p_file) {
  483. switch (file_dialog_option) {
  484. case FILE_SAVE_THEME_AS: {
  485. if(!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
  486. editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
  487. }
  488. } break;
  489. case FILE_IMPORT_THEME: {
  490. if(!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) {
  491. editor->show_warning(TTR("Error importing theme"), TTR("Error importing"));
  492. }
  493. } break;
  494. }
  495. file_dialog_option = -1;
  496. }
  497. void ScriptEditor::_menu_option(int p_option) {
  498. switch(p_option) {
  499. case FILE_NEW: {
  500. script_create_dialog->config("Node", ".gd");
  501. script_create_dialog->popup_centered(Size2(300, 300)*EDSCALE);
  502. } break;
  503. case FILE_OPEN: {
  504. editor->open_resource("Script");
  505. return;
  506. } break;
  507. case FILE_SAVE_ALL: {
  508. if (_test_script_times_on_disk())
  509. return;
  510. save_all_scripts();
  511. } break;
  512. case FILE_IMPORT_THEME: {
  513. file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
  514. file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  515. file_dialog_option = FILE_IMPORT_THEME;
  516. file_dialog->clear_filters();
  517. file_dialog->add_filter("*.tet");
  518. file_dialog->popup_centered_ratio();
  519. file_dialog->set_title(TTR("Import Theme"));
  520. } break;
  521. case FILE_RELOAD_THEME: {
  522. EditorSettings::get_singleton()->load_text_editor_theme();
  523. } break;
  524. case FILE_SAVE_THEME: {
  525. if(!EditorSettings::get_singleton()->save_text_editor_theme()) {
  526. editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
  527. }
  528. } break;
  529. case FILE_SAVE_THEME_AS: {
  530. file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
  531. file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  532. file_dialog_option = FILE_SAVE_THEME_AS;
  533. file_dialog->clear_filters();
  534. file_dialog->add_filter("*.tet");
  535. file_dialog->set_current_path(EditorSettings::get_singleton()->get_settings_path() + "/text_editor_themes/" + EditorSettings::get_singleton()->get("text_editor/theme/color_theme"));
  536. file_dialog->popup_centered_ratio();
  537. file_dialog->set_title(TTR("Save Theme As.."));
  538. } break;
  539. case SEARCH_HELP: {
  540. help_search_dialog->popup();
  541. } break;
  542. case SEARCH_CLASSES: {
  543. String current;
  544. if (tab_container->get_tab_count()>0) {
  545. EditorHelp *eh = tab_container->get_child( tab_container->get_current_tab() )->cast_to<EditorHelp>();
  546. if (eh) {
  547. current=eh->get_class();
  548. }
  549. }
  550. help_index->popup();
  551. if (current!="") {
  552. help_index->call_deferred("select_class",current);
  553. }
  554. } break;
  555. case SEARCH_WEBSITE: {
  556. OS::get_singleton()->shell_open("http://docs.godotengine.org/");
  557. } break;
  558. case WINDOW_NEXT: {
  559. _history_forward();
  560. } break;
  561. case WINDOW_PREV: {
  562. _history_back();
  563. } break;
  564. case DEBUG_SHOW: {
  565. if (debugger) {
  566. bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) );
  567. debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
  568. if (visible)
  569. debugger->hide();
  570. else
  571. debugger->show();
  572. }
  573. } break;
  574. case DEBUG_SHOW_KEEP_OPEN: {
  575. bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN) );
  576. if (debugger)
  577. debugger->set_hide_on_stop(visible);
  578. debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
  579. } break;
  580. }
  581. int selected = tab_container->get_current_tab();
  582. if (selected<0 || selected>=tab_container->get_child_count())
  583. return;
  584. ScriptEditorBase *current = tab_container->get_child(selected)->cast_to<ScriptEditorBase>();
  585. if (current) {
  586. switch(p_option) {
  587. case FILE_NEW: {
  588. script_create_dialog->config("Node", ".gd");
  589. script_create_dialog->popup_centered(Size2(300, 300)*EDSCALE);
  590. } break;
  591. case FILE_SAVE: {
  592. if (_test_script_times_on_disk())
  593. return;
  594. if (trim_trailing_whitespace_on_save)
  595. current->trim_trailing_whitespace();
  596. editor->save_resource( current->get_edited_script() );
  597. } break;
  598. case FILE_SAVE_AS: {
  599. current->trim_trailing_whitespace();
  600. editor->push_item(current->get_edited_script()->cast_to<Object>());
  601. editor->save_resource_as( current->get_edited_script() );
  602. } break;
  603. case FILE_TOOL_RELOAD:
  604. case FILE_TOOL_RELOAD_SOFT: {
  605. current->reload(p_option==FILE_TOOL_RELOAD_SOFT);
  606. } break;
  607. case FILE_CLOSE: {
  608. if (current->is_unsaved()) {
  609. _ask_close_current_unsaved_tab(current);
  610. } else {
  611. _close_current_tab();
  612. }
  613. } break;
  614. case CLOSE_DOCS: {
  615. _close_docs_tab();
  616. } break;
  617. case CLOSE_ALL: {
  618. _close_all_tabs();
  619. } break;
  620. case DEBUG_NEXT: {
  621. if (debugger)
  622. debugger->debug_next();
  623. } break;
  624. case DEBUG_STEP: {
  625. if (debugger)
  626. debugger->debug_step();
  627. } break;
  628. case DEBUG_BREAK: {
  629. if (debugger)
  630. debugger->debug_break();
  631. } break;
  632. case DEBUG_CONTINUE: {
  633. if (debugger)
  634. debugger->debug_continue();
  635. } break;
  636. case WINDOW_MOVE_LEFT: {
  637. if (tab_container->get_current_tab()>0) {
  638. tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1);
  639. script_list->call_deferred("select",tab_container->get_current_tab()-1);
  640. tab_container->move_child(current,tab_container->get_current_tab()-1);
  641. _update_script_names();
  642. }
  643. } break;
  644. case WINDOW_MOVE_RIGHT: {
  645. if (tab_container->get_current_tab()<tab_container->get_child_count()-1) {
  646. tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1);
  647. script_list->call_deferred("select",tab_container->get_current_tab()+1);
  648. tab_container->move_child(current,tab_container->get_current_tab()+1);
  649. _update_script_names();
  650. }
  651. } break;
  652. default: {
  653. if (p_option>=WINDOW_SELECT_BASE) {
  654. tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE);
  655. script_list->select(p_option-WINDOW_SELECT_BASE);
  656. }
  657. }
  658. }
  659. }
  660. EditorHelp *help = tab_container->get_current_tab_control()->cast_to<EditorHelp>();
  661. if (help) {
  662. switch(p_option) {
  663. case HELP_SEARCH_FIND: {
  664. help->popup_search();
  665. } break;
  666. case HELP_SEARCH_FIND_NEXT: {
  667. help->search_again();
  668. } break;
  669. case FILE_CLOSE: {
  670. _close_current_tab();
  671. } break;
  672. case CLOSE_DOCS: {
  673. _close_docs_tab();
  674. } break;
  675. case CLOSE_ALL: {
  676. _close_all_tabs();
  677. } break;
  678. }
  679. }
  680. }
  681. void ScriptEditor::_tab_changed(int p_which) {
  682. ensure_select_current();
  683. }
  684. void ScriptEditor::_notification(int p_what) {
  685. if (p_what==NOTIFICATION_ENTER_TREE) {
  686. editor->connect("play_pressed",this,"_editor_play");
  687. editor->connect("pause_pressed",this,"_editor_pause");
  688. editor->connect("stop_pressed",this,"_editor_stop");
  689. editor->connect("script_add_function_request",this,"_add_callback");
  690. editor->connect("resource_saved",this,"_res_saved_callback");
  691. script_list->connect("item_selected",this,"_script_selected");
  692. script_split->connect("dragged",this,"_script_split_dragged");
  693. autosave_timer->connect("timeout",this,"_autosave_scripts");
  694. {
  695. float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs");
  696. if (autosave_time>0) {
  697. autosave_timer->set_wait_time(autosave_time);
  698. autosave_timer->start();
  699. } else {
  700. autosave_timer->stop();
  701. }
  702. }
  703. EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed");
  704. help_search->set_icon(get_icon("Help","EditorIcons"));
  705. site_search->set_icon(get_icon("Godot","EditorIcons"));
  706. class_search->set_icon(get_icon("ClassList","EditorIcons"));
  707. script_forward->set_icon(get_icon("Forward","EditorIcons"));
  708. script_back->set_icon(get_icon("Back","EditorIcons"));
  709. }
  710. if (p_what==NOTIFICATION_READY) {
  711. get_tree()->connect("tree_changed",this,"_tree_changed");
  712. editor->connect("request_help",this,"_request_help");
  713. }
  714. if (p_what==NOTIFICATION_EXIT_TREE) {
  715. editor->disconnect("play_pressed",this,"_editor_play");
  716. editor->disconnect("pause_pressed",this,"_editor_pause");
  717. editor->disconnect("stop_pressed",this,"_editor_stop");
  718. }
  719. if (p_what==MainLoop::NOTIFICATION_WM_FOCUS_IN) {
  720. _test_script_times_on_disk();
  721. _update_modified_scripts_for_external_editor();
  722. }
  723. if (p_what==NOTIFICATION_PROCESS) {
  724. }
  725. }
  726. bool ScriptEditor::can_take_away_focus() const {
  727. int selected = tab_container->get_current_tab();
  728. if (selected<0 || selected>=tab_container->get_child_count())
  729. return true;
  730. ScriptEditorBase *current = tab_container->get_child(selected)->cast_to<ScriptEditorBase>();
  731. if (!current)
  732. return true;
  733. return current->can_lose_focus_on_node_selection();
  734. }
  735. void ScriptEditor::close_builtin_scripts_from_scene(const String& p_scene) {
  736. for(int i=0;i<tab_container->get_child_count();i++) {
  737. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  738. if (se) {
  739. Ref<Script> script = se->get_edited_script();
  740. if (!script.is_valid())
  741. continue;
  742. if (script->get_path().find("::")!=-1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
  743. _close_tab(i);
  744. i--;
  745. }
  746. }
  747. }
  748. }
  749. void ScriptEditor::edited_scene_changed() {
  750. _update_modified_scripts_for_external_editor();
  751. }
  752. static const Node * _find_node_with_script(const Node* p_node, const RefPtr & p_script) {
  753. if (p_node->get_script()==p_script)
  754. return p_node;
  755. for(int i=0;i<p_node->get_child_count();i++) {
  756. const Node *result = _find_node_with_script(p_node->get_child(i),p_script);
  757. if (result)
  758. return result;
  759. }
  760. return NULL;
  761. }
  762. Dictionary ScriptEditor::get_state() const {
  763. // apply_scripts();
  764. Dictionary state;
  765. #if 0
  766. Array paths;
  767. int open=-1;
  768. for(int i=0;i<tab_container->get_child_count();i++) {
  769. ScriptTextEditor *se = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
  770. if (!se)
  771. continue;
  772. Ref<Script> script = se->get_edited_script();
  773. if (script->get_path()!="" && script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) {
  774. paths.push_back(script->get_path());
  775. } else {
  776. const Node *owner = _find_node_with_script(get_tree()->get_root(),script.get_ref_ptr());
  777. if (owner)
  778. paths.push_back(owner->get_path());
  779. }
  780. if (i==tab_container->get_current_tab())
  781. open=i;
  782. }
  783. if (paths.size())
  784. state["sources"]=paths;
  785. if (open!=-1)
  786. state["current"]=open;
  787. #endif
  788. return state;
  789. }
  790. void ScriptEditor::set_state(const Dictionary& p_state) {
  791. #if 0
  792. print_line("attempt set state: "+String(Variant(p_state)));
  793. if (!p_state.has("sources"))
  794. return; //bleh
  795. Array sources = p_state["sources"];
  796. for(int i=0;i<sources.size();i++) {
  797. Variant source=sources[i];
  798. Ref<Script> script;
  799. if (source.get_type()==Variant::NODE_PATH) {
  800. Node *owner=get_tree()->get_root()->get_node(source);
  801. if (!owner)
  802. continue;
  803. script = owner->get_script();
  804. } else if (source.get_type()==Variant::STRING) {
  805. script = ResourceLoader::load(source,"Script");
  806. }
  807. if (script.is_null()) //ah well..
  808. continue;
  809. editor->call("_resource_selected",script);
  810. }
  811. if (p_state.has("current")) {
  812. tab_container->set_current_tab(p_state["current"]);
  813. }
  814. #endif
  815. }
  816. void ScriptEditor::clear() {
  817. #if 0
  818. List<ScriptTextEditor*> stes;
  819. for(int i=0;i<tab_container->get_child_count();i++) {
  820. ScriptTextEditor *se = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
  821. if (!se)
  822. continue;
  823. stes.push_back(se);
  824. }
  825. while(stes.size()) {
  826. memdelete(stes.front()->get());
  827. stes.pop_front();
  828. }
  829. int idx = tab_container->get_current_tab();
  830. if (idx>=tab_container->get_child_count())
  831. idx=tab_container->get_child_count()-1;
  832. if (idx>=0) {
  833. tab_container->set_current_tab(idx);
  834. script_list->select( script_list->find_metadata(idx) );
  835. }
  836. #endif
  837. }
  838. void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
  839. for(int i=0;i<tab_container->get_child_count();i++) {
  840. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  841. if (!se)
  842. continue;
  843. List<int> bpoints;
  844. se->get_breakpoints(&bpoints);
  845. Ref<Script> script = se->get_edited_script();
  846. String base = script->get_path();
  847. ERR_CONTINUE( base.begins_with("local://") || base=="" );
  848. for(List<int>::Element *E=bpoints.front();E;E=E->next()) {
  849. p_breakpoints->push_back(base+":"+itos(E->get()+1));
  850. }
  851. }
  852. }
  853. void ScriptEditor::ensure_focus_current() {
  854. if (!is_inside_tree())
  855. return;
  856. int cidx = tab_container->get_current_tab();
  857. if (cidx<0 || cidx>=tab_container->get_tab_count());
  858. Control *c = tab_container->get_child(cidx)->cast_to<Control>();
  859. if (!c)
  860. return;
  861. ScriptEditorBase *se = c->cast_to<ScriptEditorBase>();
  862. if (!se)
  863. return;
  864. se->ensure_focus();
  865. }
  866. void ScriptEditor::_script_selected(int p_idx) {
  867. grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing
  868. _go_to_tab(script_list->get_item_metadata(p_idx));
  869. grab_focus_block=false;
  870. }
  871. void ScriptEditor::ensure_select_current() {
  872. if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) {
  873. Node *current = tab_container->get_child(tab_container->get_current_tab());
  874. ScriptEditorBase *se = current->cast_to<ScriptEditorBase>();
  875. if (se) {
  876. Ref<Script> script = se->get_edited_script();
  877. if (!grab_focus_block && is_visible())
  878. se->ensure_focus();
  879. //edit_menu->show();
  880. //search_menu->show();
  881. }
  882. EditorHelp *eh = current->cast_to<EditorHelp>();
  883. if (eh) {
  884. //edit_menu->hide();
  885. //search_menu->hide();
  886. //script_search_menu->show();
  887. }
  888. }
  889. _update_selected_editor_menu();
  890. }
  891. void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> > &used) {
  892. if (p_current!=p_base && p_current->get_owner()!=p_base)
  893. return;
  894. if (p_current->get_script_instance()) {
  895. Ref<Script> scr = p_current->get_script();
  896. if (scr.is_valid())
  897. used.insert(scr);
  898. }
  899. for(int i=0;i<p_current->get_child_count();i++) {
  900. _find_scripts(p_base,p_current->get_child(i),used);
  901. }
  902. }
  903. struct _ScriptEditorItemData {
  904. String name;
  905. Ref<Texture> icon;
  906. int index;
  907. String tooltip;
  908. bool used;
  909. int category;
  910. bool operator<(const _ScriptEditorItemData& id) const {
  911. return category==id.category?name.nocasecmp_to(id.name)<0:category<id.category;
  912. }
  913. };
  914. void ScriptEditor::_update_script_colors() {
  915. bool script_temperature_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/script_temperature_enabled");
  916. bool highlight_current = EditorSettings::get_singleton()->get("text_editor/open_scripts/highlight_current_script");
  917. int hist_size = EditorSettings::get_singleton()->get("text_editor/open_scripts/script_temperature_history_size");
  918. Color hot_color=EditorSettings::get_singleton()->get("text_editor/open_scripts/script_temperature_hot_color");
  919. Color cold_color=EditorSettings::get_singleton()->get("text_editor/open_scripts/script_temperature_cold_color");
  920. for(int i=0;i<script_list->get_item_count();i++) {
  921. int c = script_list->get_item_metadata(i);
  922. Node *n = tab_container->get_child(c);
  923. if (!n)
  924. continue;
  925. script_list->set_item_custom_bg_color(i,Color(0,0,0,0));
  926. bool current = tab_container->get_current_tab() == c;
  927. if (current && highlight_current) {
  928. script_list->set_item_custom_bg_color(i, EditorSettings::get_singleton()->get("text_editor/open_scripts/current_script_background_color"));
  929. } else if (script_temperature_enabled) {
  930. if (!n->has_meta("__editor_pass")) {
  931. continue;
  932. }
  933. int pass=n->get_meta("__editor_pass");
  934. int h = edit_pass - pass;
  935. if (h>hist_size) {
  936. continue;
  937. }
  938. int non_zero_hist_size = ( hist_size == 0 ) ? 1 : hist_size;
  939. float v = Math::ease((edit_pass-pass)/float(non_zero_hist_size),0.4);
  940. script_list->set_item_custom_bg_color(i,hot_color.linear_interpolate(cold_color,v));
  941. }
  942. }
  943. }
  944. void ScriptEditor::_update_script_names() {
  945. if (restoring_layout)
  946. return;
  947. waiting_update_names=false;
  948. Set<Ref<Script> > used;
  949. Node* edited = EditorNode::get_singleton()->get_edited_scene();
  950. if (edited) {
  951. _find_scripts(edited,edited,used);
  952. }
  953. script_list->clear();
  954. bool split_script_help = EditorSettings::get_singleton()->get("text_editor/open_scripts/group_help_pages");
  955. Vector<_ScriptEditorItemData> sedata;
  956. for(int i=0;i<tab_container->get_child_count();i++) {
  957. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  958. if (se) {
  959. String name = se->get_name();
  960. Ref<Texture> icon = se->get_icon();
  961. String tooltip = se->get_edited_script()->get_path();
  962. _ScriptEditorItemData sd;
  963. sd.icon=icon;
  964. sd.name=name;
  965. sd.tooltip=tooltip;
  966. sd.index=i;
  967. sd.used=used.has(se->get_edited_script());
  968. sd.category=0;
  969. sedata.push_back(sd);
  970. }
  971. EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
  972. if (eh) {
  973. String name = eh->get_class();
  974. Ref<Texture> icon = get_icon("Help","EditorIcons");
  975. String tooltip = name+" Class Reference";
  976. _ScriptEditorItemData sd;
  977. sd.icon=icon;
  978. sd.name=name;
  979. sd.tooltip=tooltip;
  980. sd.index=i;
  981. sd.used=false;
  982. sd.category=split_script_help?1:0;
  983. sedata.push_back(sd);
  984. }
  985. }
  986. sedata.sort();
  987. for(int i=0;i<sedata.size();i++) {
  988. script_list->add_item(sedata[i].name,sedata[i].icon);
  989. int index = script_list->get_item_count()-1;
  990. script_list->set_item_tooltip(index,sedata[i].tooltip);
  991. script_list->set_item_metadata(index,sedata[i].index);
  992. if (sedata[i].used) {
  993. script_list->set_item_custom_bg_color(index,Color(88/255.0,88/255.0,60/255.0));
  994. }
  995. if (tab_container->get_current_tab()==sedata[i].index) {
  996. script_list->select(index);
  997. script_name_label->set_text(sedata[i].name);
  998. script_icon->set_texture(sedata[i].icon);
  999. }
  1000. }
  1001. _update_script_colors();
  1002. }
  1003. void ScriptEditor::edit(const Ref<Script>& p_script, bool p_grab_focus) {
  1004. if (p_script.is_null())
  1005. return;
  1006. // refuse to open built-in if scene is not loaded
  1007. // see if already has it
  1008. bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
  1009. if (p_script->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
  1010. String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
  1011. String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
  1012. List<String> args;
  1013. flags=flags.strip_edges();
  1014. if (flags!=String()) {
  1015. Vector<String> flagss = flags.split(" ",false);
  1016. for(int i=0;i<flagss.size();i++)
  1017. args.push_back(flagss[i]);
  1018. }
  1019. args.push_back(GlobalConfig::get_singleton()->globalize_path(p_script->get_path()));
  1020. Error err = OS::get_singleton()->execute(path,args,false);
  1021. if (err==OK)
  1022. return;
  1023. WARN_PRINT("Couldn't open external text editor, using internal");
  1024. }
  1025. for(int i=0;i<tab_container->get_child_count();i++) {
  1026. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1027. if (!se)
  1028. continue;
  1029. if (se->get_edited_script()==p_script) {
  1030. if (open_dominant || !EditorNode::get_singleton()->is_changing_scene()) {
  1031. if (tab_container->get_current_tab()!=i) {
  1032. _go_to_tab(i);
  1033. script_list->select( script_list->find_metadata(i) );
  1034. }
  1035. if (is_visible())
  1036. se->ensure_focus();
  1037. }
  1038. return;
  1039. }
  1040. }
  1041. // doesn't have it, make a new one
  1042. ScriptEditorBase *se;
  1043. for(int i=script_editor_func_count-1;i>=0;i--) {
  1044. se = script_editor_funcs[i](p_script);
  1045. if (se)
  1046. break;
  1047. }
  1048. ERR_FAIL_COND(!se);
  1049. tab_container->add_child(se);
  1050. se->set_edited_script(p_script);
  1051. se->set_tooltip_request_func("_get_debug_tooltip",this);
  1052. if (se->get_edit_menu()) {
  1053. se->get_edit_menu()->hide();
  1054. menu_hb->add_child(se->get_edit_menu());
  1055. menu_hb->move_child(se->get_edit_menu(),1);
  1056. }
  1057. if (p_grab_focus) {
  1058. _go_to_tab(tab_container->get_tab_count()-1);
  1059. }
  1060. _update_script_names();
  1061. _save_layout();
  1062. se->connect("name_changed",this,"_update_script_names");
  1063. se->connect("request_help_search",this,"_help_search");
  1064. se->connect("request_open_script_at_line",this,"_goto_script_line");
  1065. se->connect("go_to_help",this,"_help_class_goto");
  1066. se->connect("request_save_history",this,"_save_history");
  1067. //test for modification, maybe the script was not edited but was loaded
  1068. _test_script_times_on_disk(p_script);
  1069. _update_modified_scripts_for_external_editor(p_script);
  1070. }
  1071. void ScriptEditor::save_all_scripts() {
  1072. for(int i=0;i<tab_container->get_child_count();i++) {
  1073. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1074. if (!se)
  1075. continue;
  1076. if (!se->is_unsaved())
  1077. continue;
  1078. if (trim_trailing_whitespace_on_save) {
  1079. se->trim_trailing_whitespace();
  1080. }
  1081. Ref<Script> script = se->get_edited_script();
  1082. if (script.is_valid())
  1083. se->apply_code();
  1084. if (script->get_path()!="" && script->get_path().find("local://")==-1 &&script->get_path().find("::")==-1) {
  1085. //external script, save it
  1086. editor->save_resource(script);
  1087. //ResourceSaver::save(script->get_path(),script);
  1088. }
  1089. }
  1090. _update_script_names();
  1091. }
  1092. void ScriptEditor::apply_scripts() const {
  1093. for(int i=0;i<tab_container->get_child_count();i++) {
  1094. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1095. if (!se)
  1096. continue;
  1097. se->apply_code();
  1098. }
  1099. }
  1100. void ScriptEditor::_editor_play() {
  1101. debugger->start();
  1102. debug_menu->get_popup()->grab_focus();
  1103. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true );
  1104. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true );
  1105. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false );
  1106. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true );
  1107. //debugger_gui->start_listening(Globals::get_singleton()->get("debug/debug_port"));
  1108. }
  1109. void ScriptEditor::_editor_pause() {
  1110. }
  1111. void ScriptEditor::_editor_stop() {
  1112. debugger->stop();
  1113. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true );
  1114. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true );
  1115. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true );
  1116. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true );
  1117. for(int i=0;i<tab_container->get_child_count();i++) {
  1118. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1119. if (!se) {
  1120. continue;
  1121. }
  1122. se->set_debugger_active(false);
  1123. }
  1124. }
  1125. void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const PoolStringArray& p_args) {
  1126. //print_line("add callback! hohoho"); kinda sad to remove this
  1127. ERR_FAIL_COND(!p_obj);
  1128. Ref<Script> script = p_obj->get_script();
  1129. ERR_FAIL_COND( !script.is_valid() );
  1130. editor->push_item(script.ptr());
  1131. for(int i=0;i<tab_container->get_child_count();i++) {
  1132. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1133. if (!se)
  1134. continue;
  1135. if (se->get_edited_script()!=script)
  1136. continue;
  1137. se->add_callback(p_function,p_args);
  1138. _go_to_tab(i);
  1139. script_list->select( script_list->find_metadata(i) );
  1140. break;
  1141. }
  1142. }
  1143. void ScriptEditor::_save_layout() {
  1144. if (restoring_layout) {
  1145. return;
  1146. }
  1147. editor->save_layout();
  1148. }
  1149. void ScriptEditor::_editor_settings_changed() {
  1150. trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
  1151. float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs");
  1152. if (autosave_time>0) {
  1153. autosave_timer->set_wait_time(autosave_time);
  1154. autosave_timer->start();
  1155. } else {
  1156. autosave_timer->stop();
  1157. }
  1158. if (current_theme == "") {
  1159. current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
  1160. } else if (current_theme != EditorSettings::get_singleton()->get("text_editor/theme/color_theme")) {
  1161. current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
  1162. EditorSettings::get_singleton()->load_text_editor_theme();
  1163. }
  1164. for(int i=0;i<tab_container->get_child_count();i++) {
  1165. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1166. if (!se)
  1167. continue;
  1168. se->update_settings();
  1169. }
  1170. _update_script_colors();
  1171. ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save",true));
  1172. }
  1173. void ScriptEditor::_autosave_scripts() {
  1174. save_all_scripts();
  1175. }
  1176. void ScriptEditor::_tree_changed() {
  1177. if (waiting_update_names)
  1178. return;
  1179. waiting_update_names=true;
  1180. call_deferred("_update_script_names");
  1181. }
  1182. void ScriptEditor::_script_split_dragged(float) {
  1183. _save_layout();
  1184. }
  1185. void ScriptEditor::_unhandled_input(const InputEvent& p_event) {
  1186. if (p_event.key.pressed || !is_visible()) return;
  1187. if (ED_IS_SHORTCUT("script_editor/next_script", p_event)) {
  1188. int next_tab = script_list->get_current() + 1;
  1189. next_tab %= script_list->get_item_count();
  1190. _go_to_tab(script_list->get_item_metadata(next_tab));
  1191. _update_script_names();
  1192. }
  1193. if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {
  1194. int next_tab = script_list->get_current() - 1;
  1195. next_tab = next_tab >= 0 ? next_tab : script_list->get_item_count() - 1;
  1196. _go_to_tab(script_list->get_item_metadata(next_tab));
  1197. _update_script_names();
  1198. }
  1199. }
  1200. void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
  1201. if (!bool(EDITOR_DEF("text_editor/files/restore_scripts_on_load",true))) {
  1202. return;
  1203. }
  1204. if (!p_layout->has_section_key("ScriptEditor","open_scripts") && !p_layout->has_section_key("ScriptEditor","open_help"))
  1205. return;
  1206. Array scripts = p_layout->get_value("ScriptEditor","open_scripts");
  1207. Array helps;
  1208. if (p_layout->has_section_key("ScriptEditor","open_help"))
  1209. helps=p_layout->get_value("ScriptEditor","open_help");
  1210. restoring_layout=true;
  1211. for(int i=0;i<scripts.size();i++) {
  1212. String path = scripts[i];
  1213. if (!FileAccess::exists(path))
  1214. continue;
  1215. Ref<Script> scr = ResourceLoader::load(path);
  1216. if (scr.is_valid()) {
  1217. edit(scr);
  1218. }
  1219. }
  1220. for(int i=0;i<helps.size();i++) {
  1221. String path = helps[i];
  1222. _help_class_open(path);
  1223. }
  1224. for(int i=0;i<tab_container->get_child_count();i++) {
  1225. tab_container->get_child(i)->set_meta("__editor_pass",Variant());
  1226. }
  1227. if (p_layout->has_section_key("ScriptEditor","split_offset")) {
  1228. script_split->set_split_offset(p_layout->get_value("ScriptEditor","split_offset"));
  1229. }
  1230. restoring_layout=false;
  1231. _update_script_names();
  1232. }
  1233. void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
  1234. Array scripts;
  1235. Array helps;
  1236. for(int i=0;i<tab_container->get_child_count();i++) {
  1237. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1238. if (se) {
  1239. String path = se->get_edited_script()->get_path();
  1240. if (!path.is_resource_file())
  1241. continue;
  1242. scripts.push_back(path);
  1243. }
  1244. EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
  1245. if (eh) {
  1246. helps.push_back(eh->get_class());
  1247. }
  1248. }
  1249. p_layout->set_value("ScriptEditor","open_scripts",scripts);
  1250. p_layout->set_value("ScriptEditor","open_help",helps);
  1251. p_layout->set_value("ScriptEditor","split_offset",script_split->get_split_offset());
  1252. }
  1253. void ScriptEditor::_help_class_open(const String& p_class) {
  1254. if (p_class=="")
  1255. return;
  1256. for(int i=0;i<tab_container->get_child_count();i++) {
  1257. EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
  1258. if (eh && eh->get_class()==p_class) {
  1259. _go_to_tab(i);
  1260. _update_script_names();
  1261. return;
  1262. }
  1263. }
  1264. EditorHelp * eh = memnew( EditorHelp );
  1265. eh->set_name(p_class);
  1266. tab_container->add_child(eh);
  1267. _go_to_tab(tab_container->get_tab_count()-1);
  1268. eh->go_to_class(p_class,0);
  1269. eh->connect("go_to_help",this,"_help_class_goto");
  1270. _update_script_names();
  1271. _save_layout();
  1272. }
  1273. void ScriptEditor::_help_class_goto(const String& p_desc) {
  1274. String cname=p_desc.get_slice(":",1);
  1275. for(int i=0;i<tab_container->get_child_count();i++) {
  1276. EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
  1277. if (eh && eh->get_class()==cname) {
  1278. _go_to_tab(i);
  1279. eh->go_to_help(p_desc);
  1280. _update_script_names();
  1281. return;
  1282. }
  1283. }
  1284. EditorHelp * eh = memnew( EditorHelp );
  1285. eh->set_name(cname);
  1286. tab_container->add_child(eh);
  1287. _go_to_tab(tab_container->get_tab_count()-1);
  1288. eh->go_to_help(p_desc);
  1289. eh->connect("go_to_help",this,"_help_class_goto");
  1290. _update_script_names();
  1291. _save_layout();
  1292. }
  1293. void ScriptEditor::_update_selected_editor_menu() {
  1294. for(int i=0;i<tab_container->get_child_count();i++) {
  1295. bool current = tab_container->get_current_tab() == i;
  1296. ScriptEditorBase *se = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1297. if (se && se->get_edit_menu()) {
  1298. if (current)
  1299. se->get_edit_menu()->show();
  1300. else
  1301. se->get_edit_menu()->hide();
  1302. }
  1303. }
  1304. EditorHelp *eh=tab_container->get_current_tab_control()->cast_to<EditorHelp>();
  1305. if (eh) {
  1306. script_search_menu->show();
  1307. } else {
  1308. script_search_menu->hide();
  1309. }
  1310. }
  1311. void ScriptEditor::_update_history_pos(int p_new_pos) {
  1312. Node *n = tab_container->get_current_tab_control();
  1313. if (n->cast_to<ScriptEditorBase>()) {
  1314. history[history_pos].state=n->cast_to<ScriptEditorBase>()->get_edit_state();
  1315. }
  1316. if (n->cast_to<EditorHelp>()) {
  1317. history[history_pos].state=n->cast_to<EditorHelp>()->get_scroll();
  1318. }
  1319. history_pos=p_new_pos;
  1320. tab_container->set_current_tab(history[history_pos].control->get_index());
  1321. n = history[history_pos].control;
  1322. if (n->cast_to<ScriptEditorBase>()) {
  1323. n->cast_to<ScriptEditorBase>()->set_edit_state(history[history_pos].state);
  1324. n->cast_to<ScriptEditorBase>()->ensure_focus();
  1325. }
  1326. if (n->cast_to<EditorHelp>()) {
  1327. n->cast_to<EditorHelp>()->set_scroll(history[history_pos].state);
  1328. n->cast_to<EditorHelp>()->set_focused();
  1329. }
  1330. n->set_meta("__editor_pass",++edit_pass);
  1331. _update_script_names();
  1332. _update_history_arrows();
  1333. _update_selected_editor_menu();
  1334. }
  1335. void ScriptEditor::_history_forward() {
  1336. if (history_pos<history.size()-1) {
  1337. _update_history_pos(history_pos+1);
  1338. }
  1339. }
  1340. void ScriptEditor::_history_back(){
  1341. if (history_pos>0) {
  1342. _update_history_pos(history_pos-1);
  1343. }
  1344. }
  1345. void ScriptEditor::set_scene_root_script( Ref<Script> p_script ) {
  1346. bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
  1347. if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")))
  1348. return;
  1349. if (open_dominant && p_script.is_valid() && _can_open_in_editor(p_script.ptr())) {
  1350. edit(p_script);
  1351. }
  1352. }
  1353. bool ScriptEditor::script_go_to_method(Ref<Script> p_script, const String& p_method) {
  1354. for (int i=0;i<tab_container->get_child_count();i++) {
  1355. ScriptEditorBase *current = tab_container->get_child(i)->cast_to<ScriptEditorBase>();
  1356. if (current && current->get_edited_script()==p_script) {
  1357. if (current->goto_method(p_method)) {
  1358. edit(p_script);
  1359. return true;
  1360. }
  1361. break;
  1362. }
  1363. }
  1364. return false;
  1365. }
  1366. void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
  1367. auto_reload_running_scripts=p_enabled;
  1368. }
  1369. void ScriptEditor::_help_search(String p_text) {
  1370. help_search_dialog->popup(p_text);
  1371. }
  1372. void ScriptEditor::_open_script_request(const String& p_path) {
  1373. Ref<Script> script = ResourceLoader::load(p_path);
  1374. if (script.is_valid()) {
  1375. script_editor->edit(script,false);
  1376. }
  1377. }
  1378. int ScriptEditor::script_editor_func_count=0;
  1379. CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
  1380. void ScriptEditor::register_create_script_editor_function(CreateScriptEditorFunc p_func) {
  1381. ERR_FAIL_COND(script_editor_func_count==SCRIPT_EDITOR_FUNC_MAX);
  1382. script_editor_funcs[script_editor_func_count++]=p_func;
  1383. }
  1384. void ScriptEditor::_bind_methods() {
  1385. ClassDB::bind_method("_file_dialog_action",&ScriptEditor::_file_dialog_action);
  1386. ClassDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed);
  1387. ClassDB::bind_method("_menu_option",&ScriptEditor::_menu_option);
  1388. ClassDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab);
  1389. ClassDB::bind_method("_close_docs_tab", &ScriptEditor::_close_docs_tab);
  1390. ClassDB::bind_method("_close_all_tabs", &ScriptEditor::_close_all_tabs);
  1391. ClassDB::bind_method("_editor_play",&ScriptEditor::_editor_play);
  1392. ClassDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause);
  1393. ClassDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop);
  1394. ClassDB::bind_method("_add_callback",&ScriptEditor::_add_callback);
  1395. ClassDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts);
  1396. ClassDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts);
  1397. ClassDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback);
  1398. ClassDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line);
  1399. ClassDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2);
  1400. ClassDB::bind_method("_help_search",&ScriptEditor::_help_search);
  1401. ClassDB::bind_method("_save_history",&ScriptEditor::_save_history);
  1402. ClassDB::bind_method("_breaked",&ScriptEditor::_breaked);
  1403. ClassDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger);
  1404. ClassDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip);
  1405. ClassDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts);
  1406. ClassDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed);
  1407. ClassDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names);
  1408. ClassDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed);
  1409. ClassDB::bind_method("_script_selected",&ScriptEditor::_script_selected);
  1410. ClassDB::bind_method("_script_created",&ScriptEditor::_script_created);
  1411. ClassDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged);
  1412. ClassDB::bind_method("_help_class_open",&ScriptEditor::_help_class_open);
  1413. ClassDB::bind_method("_help_class_goto",&ScriptEditor::_help_class_goto);
  1414. ClassDB::bind_method("_request_help",&ScriptEditor::_help_class_open);
  1415. ClassDB::bind_method("_history_forward",&ScriptEditor::_history_forward);
  1416. ClassDB::bind_method("_history_back",&ScriptEditor::_history_back);
  1417. ClassDB::bind_method("_live_auto_reload_running_scripts",&ScriptEditor::_live_auto_reload_running_scripts);
  1418. ClassDB::bind_method("_unhandled_input",&ScriptEditor::_unhandled_input);
  1419. }
  1420. ScriptEditor::ScriptEditor(EditorNode *p_editor) {
  1421. current_theme = "";
  1422. completion_cache = memnew( EditorScriptCodeCompletionCache );
  1423. restoring_layout=false;
  1424. waiting_update_names=false;
  1425. pending_auto_reload=false;
  1426. auto_reload_running_scripts=false;
  1427. editor=p_editor;
  1428. menu_hb = memnew( HBoxContainer );
  1429. add_child(menu_hb);
  1430. script_split = memnew( HSplitContainer );
  1431. add_child(script_split);
  1432. script_split->set_v_size_flags(SIZE_EXPAND_FILL);
  1433. script_list = memnew( ItemList );
  1434. script_split->add_child(script_list);
  1435. script_list->set_custom_minimum_size(Size2(0,0));
  1436. script_split->set_split_offset(140);
  1437. tab_container = memnew( TabContainer );
  1438. tab_container->set_tabs_visible(false);
  1439. script_split->add_child(tab_container);
  1440. tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
  1441. ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_GREATER);
  1442. ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_LESS);
  1443. set_process_unhandled_input(true);
  1444. file_menu = memnew( MenuButton );
  1445. menu_hb->add_child(file_menu);
  1446. file_menu->set_text(TTR("File"));
  1447. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New")), FILE_NEW);
  1448. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN);
  1449. file_menu->get_popup()->add_separator();
  1450. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT|KEY_MASK_CMD|KEY_S), FILE_SAVE);
  1451. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..")), FILE_SAVE_AS);
  1452. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_MASK_ALT|KEY_S), FILE_SAVE_ALL);
  1453. file_menu->get_popup()->add_separator();
  1454. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_R), FILE_TOOL_RELOAD_SOFT);
  1455. file_menu->get_popup()->add_separator();
  1456. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_ALT|KEY_LEFT), WINDOW_PREV);
  1457. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT|KEY_RIGHT), WINDOW_NEXT);
  1458. file_menu->get_popup()->add_separator();
  1459. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme")), FILE_IMPORT_THEME);
  1460. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), FILE_RELOAD_THEME);
  1461. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), FILE_SAVE_THEME);
  1462. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As")), FILE_SAVE_THEME_AS);
  1463. file_menu->get_popup()->add_separator();
  1464. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
  1465. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
  1466. file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_all", TTR("Close All")), CLOSE_ALL);
  1467. file_menu->get_popup()->connect("id_pressed", this,"_menu_option");
  1468. script_search_menu = memnew( MenuButton );
  1469. menu_hb->add_child(script_search_menu);
  1470. script_search_menu->set_text(TTR("Search"));
  1471. script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD|KEY_F), HELP_SEARCH_FIND);
  1472. script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
  1473. script_search_menu->get_popup()->connect("id_pressed", this,"_menu_option");
  1474. script_search_menu->hide();
  1475. debug_menu = memnew( MenuButton );
  1476. menu_hb->add_child(debug_menu);
  1477. debug_menu->set_text(TTR("Debug"));
  1478. debug_menu->get_popup()->add_separator();
  1479. debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
  1480. debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
  1481. debug_menu->get_popup()->add_separator();
  1482. debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
  1483. debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue")), DEBUG_CONTINUE);
  1484. debug_menu->get_popup()->add_separator();
  1485. //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
  1486. debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
  1487. debug_menu->get_popup()->connect("id_pressed", this,"_menu_option");
  1488. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
  1489. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true );
  1490. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true );
  1491. debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true );
  1492. #if 0
  1493. window_menu = memnew( MenuButton );
  1494. menu_hb->add_child(window_menu);
  1495. window_menu->set_text(TTR("Window"));
  1496. window_menu->get_popup()->add_item(TTR("Close"),WINDOW_CLOSE,KEY_MASK_CMD|KEY_W);
  1497. window_menu->get_popup()->add_separator();
  1498. window_menu->get_popup()->add_item(TTR("Move Left"),WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT);
  1499. window_menu->get_popup()->add_item(TTR("Move Right"),WINDOW_MOVE_RIGHT,KEY_MASK_CMD|KEY_RIGHT);
  1500. window_menu->get_popup()->add_separator();
  1501. window_menu->get_popup()->connect("id_pressed", this,"_menu_option");
  1502. #endif
  1503. menu_hb->add_spacer();
  1504. script_icon = memnew( TextureRect );
  1505. menu_hb->add_child(script_icon);
  1506. script_name_label = memnew( Label );
  1507. menu_hb->add_child(script_name_label);
  1508. script_icon->hide();
  1509. script_name_label->hide();
  1510. menu_hb->add_spacer();
  1511. site_search = memnew( ToolButton );
  1512. site_search->set_text(TTR("Tutorials"));
  1513. site_search->connect("pressed",this,"_menu_option",varray(SEARCH_WEBSITE));
  1514. menu_hb->add_child(site_search);
  1515. site_search->set_tooltip(TTR("Open https://godotengine.org at tutorials section."));
  1516. class_search = memnew( ToolButton );
  1517. class_search->set_text(TTR("Classes"));
  1518. class_search->connect("pressed",this,"_menu_option",varray(SEARCH_CLASSES));
  1519. menu_hb->add_child(class_search);
  1520. class_search->set_tooltip(TTR("Search the class hierarchy."));
  1521. help_search = memnew( ToolButton );
  1522. help_search->set_text(TTR("Search Help"));
  1523. help_search->connect("pressed",this,"_menu_option",varray(SEARCH_HELP));
  1524. menu_hb->add_child(help_search);
  1525. help_search->set_tooltip(TTR("Search the reference documentation."));
  1526. menu_hb->add_child( memnew( VSeparator) );
  1527. script_back = memnew( ToolButton );
  1528. script_back->connect("pressed",this,"_history_back");
  1529. menu_hb->add_child(script_back);
  1530. script_back->set_disabled(true);
  1531. script_back->set_tooltip(TTR("Go to previous edited document."));
  1532. script_forward = memnew( ToolButton );
  1533. script_forward->connect("pressed",this,"_history_forward");
  1534. menu_hb->add_child(script_forward);
  1535. script_forward->set_disabled(true);
  1536. script_forward->set_tooltip(TTR("Go to next edited document."));
  1537. tab_container->connect("tab_changed", this,"_tab_changed");
  1538. erase_tab_confirm = memnew( ConfirmationDialog );
  1539. add_child(erase_tab_confirm);
  1540. erase_tab_confirm->connect("confirmed", this,"_close_current_tab");
  1541. script_create_dialog = memnew(ScriptCreateDialog);
  1542. script_create_dialog->set_title(TTR("Create Script"));
  1543. add_child(script_create_dialog);
  1544. script_create_dialog->connect("script_created", this, "_script_created");
  1545. file_dialog_option = -1;
  1546. file_dialog = memnew( EditorFileDialog );
  1547. add_child(file_dialog);
  1548. file_dialog->connect("file_selected", this,"_file_dialog_action");
  1549. debugger = memnew( ScriptEditorDebugger(editor) );
  1550. debugger->connect("goto_script_line",this,"_goto_script_line");
  1551. debugger->connect("show_debugger",this,"_show_debugger");
  1552. disk_changed = memnew( ConfirmationDialog );
  1553. {
  1554. VBoxContainer *vbc = memnew( VBoxContainer );
  1555. disk_changed->add_child(vbc);
  1556. // disk_changed->set_child_rect(vbc);
  1557. Label *dl = memnew( Label );
  1558. dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:"));
  1559. vbc->add_child(dl);
  1560. disk_changed_list = memnew( Tree );
  1561. vbc->add_child(disk_changed_list);
  1562. disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL);
  1563. disk_changed->connect("confirmed",this,"_reload_scripts");
  1564. disk_changed->get_ok()->set_text(TTR("Reload"));
  1565. disk_changed->add_button(TTR("Resave"),!OS::get_singleton()->get_swap_ok_cancel(),"resave");
  1566. disk_changed->connect("custom_action",this,"_resave_scripts");
  1567. }
  1568. add_child(disk_changed);
  1569. script_editor=this;
  1570. Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"),debugger);
  1571. debugger->set_tool_button(db);
  1572. debugger->connect("breaked",this,"_breaked");
  1573. autosave_timer = memnew( Timer );
  1574. autosave_timer->set_one_shot(false);
  1575. add_child(autosave_timer);
  1576. grab_focus_block=false;
  1577. help_search_dialog = memnew( EditorHelpSearch );
  1578. add_child(help_search_dialog);
  1579. help_search_dialog->connect("go_to_help",this,"_help_class_goto");
  1580. help_index = memnew( EditorHelpIndex );
  1581. add_child(help_index);
  1582. help_index->connect("open_class",this,"_help_class_open");
  1583. history_pos=-1;
  1584. // debugger_gui->hide();
  1585. edit_pass=0;
  1586. trim_trailing_whitespace_on_save = false;
  1587. ScriptServer::edit_request_func=_open_script_request;
  1588. }
  1589. ScriptEditor::~ScriptEditor() {
  1590. memdelete(completion_cache);
  1591. }
  1592. void ScriptEditorPlugin::edit(Object *p_object) {
  1593. if (!p_object->cast_to<Script>())
  1594. return;
  1595. script_editor->edit(p_object->cast_to<Script>());
  1596. }
  1597. bool ScriptEditorPlugin::handles(Object *p_object) const {
  1598. if (p_object->cast_to<Script>()) {
  1599. bool valid = _can_open_in_editor(p_object->cast_to<Script>());
  1600. if (!valid) { //user tried to open it by clicking
  1601. EditorNode::get_singleton()->show_warning(TTR("Built-in scripts can only be edited when the scene they belong to is loaded"));
  1602. }
  1603. return valid;
  1604. }
  1605. return p_object->is_class("Script");
  1606. }
  1607. void ScriptEditorPlugin::make_visible(bool p_visible) {
  1608. if (p_visible) {
  1609. script_editor->show();
  1610. script_editor->set_process(true);
  1611. script_editor->ensure_select_current();
  1612. } else {
  1613. script_editor->hide();
  1614. script_editor->set_process(false);
  1615. }
  1616. }
  1617. void ScriptEditorPlugin::selected_notify() {
  1618. script_editor->ensure_select_current();
  1619. }
  1620. Dictionary ScriptEditorPlugin::get_state() const {
  1621. return script_editor->get_state();
  1622. }
  1623. void ScriptEditorPlugin::set_state(const Dictionary& p_state) {
  1624. script_editor->set_state(p_state);
  1625. }
  1626. void ScriptEditorPlugin::clear() {
  1627. script_editor->clear();
  1628. }
  1629. void ScriptEditorPlugin::save_external_data() {
  1630. script_editor->save_all_scripts();
  1631. }
  1632. void ScriptEditorPlugin::apply_changes() {
  1633. script_editor->apply_scripts();
  1634. }
  1635. void ScriptEditorPlugin::restore_global_state() {
  1636. }
  1637. void ScriptEditorPlugin::save_global_state() {
  1638. }
  1639. void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
  1640. script_editor->set_window_layout(p_layout);
  1641. }
  1642. void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){
  1643. script_editor->get_window_layout(p_layout);
  1644. }
  1645. void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
  1646. return script_editor->get_breakpoints(p_breakpoints);
  1647. }
  1648. void ScriptEditorPlugin::edited_scene_changed() {
  1649. script_editor->edited_scene_changed();
  1650. }
  1651. ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
  1652. editor=p_node;
  1653. script_editor = memnew( ScriptEditor(p_node) );
  1654. editor->get_viewport()->add_child(script_editor);
  1655. script_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1656. script_editor->hide();
  1657. EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change",true);
  1658. ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save",true));
  1659. EDITOR_DEF("text_editor/files/open_dominant_script_on_scene_change",true);
  1660. EDITOR_DEF("text_editor/external/use_external_editor",false);
  1661. EDITOR_DEF("text_editor/external/exec_path","");
  1662. EDITOR_DEF("text_editor/open_scripts/script_temperature_enabled",true);
  1663. EDITOR_DEF("text_editor/open_scripts/highlight_current_script", true);
  1664. EDITOR_DEF("text_editor/open_scripts/script_temperature_history_size",15);
  1665. EDITOR_DEF("text_editor/open_scripts/script_temperature_hot_color",Color(1,0,0,0.3));
  1666. EDITOR_DEF("text_editor/open_scripts/script_temperature_cold_color",Color(0,0,1,0.3));
  1667. EDITOR_DEF("text_editor/open_scripts/current_script_background_color",Color(0.81,0.81,0.14,0.63));
  1668. EDITOR_DEF("text_editor/open_scripts/group_help_pages",true);
  1669. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"text_editor/external/exec_path",PROPERTY_HINT_GLOBAL_FILE));
  1670. EDITOR_DEF("text_editor/external/exec_flags","");
  1671. }
  1672. ScriptEditorPlugin::~ScriptEditorPlugin()
  1673. {
  1674. }