llpreviewscript.cpp 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676
  1. /**
  2. * @file llpreviewscript.cpp
  3. * @brief LLPreviewScript and LLLiveLSLEditor classes implementation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, Linden Research, Inc.
  8. * Preprocessor code (c) 2019 Henri Beauchamp.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. // "error: 'get_temporary_buffer<...>' is deprecated" seen with clang 18 and
  35. // gcc 12.3 libstdc++ implementation of std::stable_sort(). HB
  36. #if CLANG_VERSION >= 180000
  37. # pragma clang diagnostic ignored "-Wdeprecated-declarations"
  38. #endif
  39. #include <deque>
  40. #include "llpreviewscript.h"
  41. #include "llassetstorage.h"
  42. #include "llbutton.h"
  43. #include "llcallbacklist.h"
  44. #include "llcheckboxctrl.h"
  45. #include "llcombobox.h"
  46. #include "lldir.h"
  47. #include "lleventtimer.h"
  48. #include "llexperiencecache.h"
  49. #include "hbexternaleditor.h"
  50. #include "llfilesystem.h"
  51. #include "llfontgl.h"
  52. #include "lllineeditor.h"
  53. #include "llmenugl.h"
  54. #include "llnotifications.h"
  55. #include "llscrolllistctrl.h"
  56. #include "llsdserialize.h"
  57. #include "llsdutil.h"
  58. #include "lltabcontainer.h"
  59. #include "lltextbox.h"
  60. #include "lltrans.h"
  61. #include "lluictrlfactory.h"
  62. #include "llwindow.h"
  63. #include "llagent.h"
  64. #include "llappviewer.h"
  65. #include "llfloaterexperienceprofile.h"
  66. #include "llfloatersearchreplace.h"
  67. #include "llgridmanager.h"
  68. #include "llinventorymodel.h"
  69. #include "llmediactrl.h"
  70. #include "hbpreprocessor.h"
  71. //MK
  72. #include "mkrlinterface.h"
  73. //mk
  74. #include "llselectmgr.h"
  75. #include "lltooldraganddrop.h"
  76. #include "llviewerassetupload.h"
  77. #include "llviewercontrol.h"
  78. #include "llviewerinventory.h"
  79. #include "llviewermenu.h"
  80. #include "llviewerobjectlist.h"
  81. #include "llviewerregion.h"
  82. #include "llviewerstats.h"
  83. #include "llviewertexteditor.h"
  84. #include "llweb.h"
  85. #include "roles_constants.h"
  86. const std::string HELLO_LSL =
  87. "default {\n"
  88. " state_entry() {\n"
  89. " llOwnerSay(llGetScriptName() + \": Hello, Avatar !\");\n"
  90. " }\n"
  91. "\n"
  92. " touch_start(integer total_number) {\n"
  93. " llWhisper(0, llGetScriptName() + \": Touched.\");\n"
  94. " }\n"
  95. "}\n";
  96. // *TODO: translate ?
  97. const std::string DEFAULT_SCRIPT_NAME = "New script";
  98. const std::string ESCAPED_SOURCES_MARKER =
  99. "//********** Escaped, original, non-preprocessed sources **********//\n";
  100. const std::string ESCAPE_STRING = "//* ";
  101. const std::string ESCAPED_INCLUDE_MARKER =
  102. "//********** Non-preprocessed include sources **********//\n";
  103. const std::string ESCAPED_INCLUDE_FOOTER =
  104. "//********* End of non-preprocessed include sources *********//\n";
  105. const std::string DUMMY_STATE =
  106. "\ndefault { state_entry() { llOwnerSay(\"This is an #include script.\"); } }\n";
  107. const std::string ALIEN_ESCAPED_START_MARKER = "//start_unprocessed_text\n/*";
  108. const std::string ALIEN_ESCAPED_END_MARKER = "*/\n//end_unprocessed_text";
  109. constexpr S32 SCRIPT_BORDER = 4;
  110. constexpr S32 SCRIPT_PAD = 5;
  111. constexpr S32 SCRIPT_BUTTON_WIDTH = 128;
  112. constexpr S32 SCRIPT_BUTTON_HEIGHT = 24; // HACK: Use gBtnHeight where possible.
  113. constexpr S32 LINE_COLUMN_HEIGHT = 14;
  114. constexpr S32 SCRIPT_EDITOR_MIN_HEIGHT = 2 * SCROLLBAR_SIZE +
  115. 2 * LLPANEL_BORDER_WIDTH + 128;
  116. constexpr S32 SCRIPT_MIN_WIDTH = 2 * SCRIPT_BORDER + 2 * SCRIPT_BUTTON_WIDTH +
  117. SCRIPT_PAD + RESIZE_HANDLE_WIDTH + SCRIPT_PAD;
  118. constexpr S32 SCRIPT_MIN_HEIGHT = 2 * SCRIPT_BORDER +
  119. 3 * (SCRIPT_BUTTON_HEIGHT + SCRIPT_PAD) +
  120. LINE_COLUMN_HEIGHT + SCRIPT_EDITOR_MIN_HEIGHT;
  121. constexpr S32 MAX_HISTORY_COUNT = 10;
  122. constexpr F32 LIVE_HELP_REFRESH_TIME = 1.f;
  123. constexpr F32 AUTO_SAVE_INTERVAL = 60.f;
  124. static bool have_script_upload_cap(LLUUID object_id)
  125. {
  126. LLViewerRegion* region = NULL;
  127. if (object_id.isNull())
  128. {
  129. region = gAgent.getRegion();
  130. }
  131. else
  132. {
  133. LLViewerObject* object = gObjectList.findObject(object_id);
  134. if (object)
  135. {
  136. region = object->getRegion();
  137. }
  138. }
  139. return region && !region->getCapability("UpdateScriptTask").empty();
  140. }
  141. // ----------------------------------------------------------------------------
  142. // LLScriptEditor class
  143. // Inner implementation class for use by LLPreviewScript and LLLiveLSLEditor.
  144. // ----------------------------------------------------------------------------
  145. class LLScriptEditor : public LLPanel, public LLEventTimer
  146. {
  147. friend class LLPreviewScript;
  148. friend class LLLiveLSLEditor;
  149. protected:
  150. LOG_CLASS(LLScriptEditor);
  151. public:
  152. LLScriptEditor(const LLUUID& item_id, void (*load_cb)(void*),
  153. void (*save_cb)(void*, bool), void (*search_cb)(void*),
  154. void* userdata);
  155. ~LLScriptEditor() override;
  156. void draw() override;
  157. virtual bool canClose();
  158. bool handleKeyHere(KEY key, MASK mask) override;
  159. void autoSave();
  160. // LLEventTimer API
  161. bool tick() override;
  162. void setScriptName(std::string name);
  163. std::string getItemPath();
  164. void setScriptText(std::string text, bool is_valid, bool set_saved = true);
  165. void setEditedTextFromSaved();
  166. void loadFile(const std::string& filename);
  167. bool handleSaveChangesDialog(const LLSD& notification,
  168. const LLSD& response);
  169. bool handleReloadFromServerDialog(const LLSD& notification,
  170. const LLSD& response);
  171. void addComment(const std::string& comment, bool is_error = false);
  172. LL_INLINE LLCheckBoxCtrl* getMonoCheckBox() { return mMonoCheckbox; }
  173. LL_INLINE bool monoChecked() const { return mMonoCheckbox->get(); }
  174. void selectFirstError();
  175. LL_INLINE void enableSave(bool b) { mEnableSave = b; }
  176. void enableEdit(bool enable);
  177. LL_INLINE LLUUID getAssociatedExperience() const { return mAssociatedExperience; }
  178. LL_INLINE void setAssociatedExperience(const LLUUID& exp_id)
  179. {
  180. mAssociatedExperience = exp_id;
  181. }
  182. LL_INLINE const char* getTitleName() const
  183. {
  184. return "Script";
  185. }
  186. LL_INLINE bool hasChanged()
  187. {
  188. return mHasScriptData && (mEnableSave || !mEditor->isPristine());
  189. }
  190. static void loadFunctions(const std::string& filename);
  191. private:
  192. enum
  193. {
  194. PREPROCESS_START = 0,
  195. PREPROCESS_WAITING,
  196. PREPROCESS_RESUME,
  197. PREPROCESS_DONE
  198. };
  199. void doSave(bool close_after_save, bool check_preprocessing = true);
  200. void preprocess();
  201. bool loadAsset(LLViewerInventoryItem* item);
  202. void setHelpPage(const std::string& help_string);
  203. void updateDynamicHelp(bool immediate = false);
  204. void addHelpItemToHistory(const std::string& help_string);
  205. static void onIdle(void* userdata);
  206. static LLViewerInventoryItem* getScriptItem(const std::string& name);
  207. static void onLoadComplete(const LLUUID& asset_id, LLAssetType::EType type,
  208. void* userdata, S32 status, LLExtStat);
  209. static S32 loadInclude(std::string& include_name, const std::string& path,
  210. std::string& buffer, void* userdata);
  211. static void preprocessorMessage(const std::string& message,
  212. bool is_warning, void* userdata);
  213. static std::string escapeSources(const std::string& sources);
  214. static std::string unescapeSources(const std::string& sources);
  215. static std::string removeEscapedSources(const std::string& sources);
  216. static std::string setIncludeSources(const std::string& sources);
  217. static std::string getIncludeSources(const std::string& sources);
  218. static std::string convertSources(const std::string& sources);
  219. static void loadFromFileCallback(HBFileSelector::ELoadFilter type,
  220. std::string& filename, void* userdata);
  221. static void saveToFileCallback(HBFileSelector::ESaveFilter type,
  222. std::string& filename, void* userdata);
  223. static void onEditedFileChanged(const std::string& filename,
  224. void* userdata);
  225. static bool onHelpWebDialog(const LLSD& notification,
  226. const LLSD& response);
  227. static void onBtnHelp(void* userdata);
  228. static void onBtnDynamicHelp(void* userdata);
  229. static void onHelpFollowCursor(void*);
  230. static void onCheckLock(LLUICtrl* ctrl, void* userdata);
  231. static void onHelpComboCommit(LLUICtrl* ctrl, void* userdata);
  232. static void onClickBack(void* userdata);
  233. static void onClickForward(void* userdata);
  234. static void onBtnInsertFunction(LLUICtrl*, void* userdata);
  235. static void onMonoCheckboxClicked(LLUICtrl*, void* userdata);
  236. static void onBtnLoadFromFile(void* userdata);
  237. static void onBtnSaveToFile(void* userdata);
  238. static void onEditExternal(void* userdata);
  239. static void onEditRaw(void* userdata);
  240. static void onBtnSave(void* userdata);
  241. static void onFlyoutBtnSave(LLUICtrl* ctrl, void* userdata);
  242. static void onBtnUndoChanges(void* userdata);
  243. static void onSearchMenu(void* userdata);
  244. static void onUndoMenu(void* userdata);
  245. static void onRedoMenu(void* userdata);
  246. static void onCutMenu(void* userdata);
  247. static void onCopyMenu(void* userdata);
  248. static void onPasteMenu(void* userdata);
  249. static void onSelectAllMenu(void* userdata);
  250. static void onDeselectMenu(void* userdata);
  251. static void onErrorList(LLUICtrl*, void* user_data);
  252. static bool enableLoadFile(void* userdata);
  253. static bool enableSaveFile(void* userdata);
  254. static bool enableRaw(void* userdata);
  255. static bool enableCallback(void* userdata);
  256. static bool enableUndoMenu(void* userdata);
  257. static bool enableRedoMenu(void* userdata);
  258. static bool enableCutMenu(void* userdata);
  259. static bool enableCopyMenu(void* userdata);
  260. static bool enablePasteMenu(void* userdata);
  261. static bool enableSelectAllMenu(void* userdata);
  262. static bool enableDeselectMenu(void* userdata);
  263. static bool enableHelp(void* userdata);
  264. public:
  265. static LLFontGL* sCustomFont;
  266. private:
  267. void (*mLoadCallback)(void* userdata);
  268. void (*mSaveCallback)(void* userdata,
  269. bool close_after_save);
  270. void (*mSearchReplaceCallback) (void* userdata);
  271. void* mUserdata;
  272. HBPreprocessor* mPreprocessor;
  273. LLTabContainer* mTabContainer;
  274. LLButton* mSaveButton;
  275. LLFlyoutButton* mSaveFlyoutButton;
  276. LLTextBox* mLineColText;
  277. LLComboBox* mFunctions;
  278. LLTextEditor* mEditor;
  279. LLTextEditor* mSavedSources;
  280. LLCheckBoxCtrl* mMonoCheckbox;
  281. LLScrollListCtrl* mErrorList;
  282. LLKeywordToken* mLastHelpToken;
  283. S32 mLiveHelpHistorySize;
  284. LLHandle<LLFloater> mLiveHelpHandle;
  285. HBExternalEditor* mExternalEditor;
  286. LLUUID mItemUUID;
  287. LLUUID mAssociatedExperience;
  288. S32 mPreprocessState;
  289. F32 mLastPosUpdate;
  290. F32 mLastHelpUpdate;
  291. std::string mScriptName;
  292. std::string mAutosaveFilename;
  293. bool mForceClose;
  294. bool mCloseAfterSave;
  295. bool mNeedSaving;
  296. bool mEnableSave;
  297. bool mIsSaving;
  298. bool mHasScriptData;
  299. bool mSaveDialogShown;
  300. typedef fast_hset<LLScriptEditor*> instances_list_t;
  301. static instances_list_t sInstances;
  302. struct LSLFunctionProps
  303. {
  304. LSLFunctionProps(const std::string& name, const std::string& tooltip,
  305. F32 sleep_time, bool god_only)
  306. : mName(name),
  307. mTooltip(tooltip),
  308. mSleepTime(sleep_time),
  309. mGodOnly(god_only)
  310. {
  311. }
  312. F32 mSleepTime;
  313. std::string mName;
  314. std::string mTooltip;
  315. bool mGodOnly;
  316. };
  317. typedef std::vector<LSLFunctionProps> functions_table_t;
  318. static functions_table_t sParsedFunctions;
  319. };
  320. // static members
  321. LLScriptEditor::instances_list_t LLScriptEditor::sInstances;
  322. LLScriptEditor::functions_table_t LLScriptEditor::sParsedFunctions;
  323. LLFontGL* LLScriptEditor::sCustomFont = NULL;
  324. struct LLSECKeywordCompare
  325. {
  326. LL_INLINE bool operator()(const std::string& lhs, const std::string& rhs)
  327. {
  328. return LLStringUtil::compareDictInsensitive(lhs, rhs) < 0;
  329. }
  330. };
  331. LLScriptEditor::LLScriptEditor(const LLUUID& item_id, void (*load_cb)(void*),
  332. void (*save_cb)(void*, bool),
  333. void (*search_cb)(void*), void* userdata)
  334. : LLPanel("panel_script_editor"),
  335. mItemUUID(item_id),
  336. mScriptName("untitled"),
  337. mLoadCallback(load_cb),
  338. mSaveCallback(save_cb),
  339. mSearchReplaceCallback(search_cb),
  340. mUserdata(userdata),
  341. mPreprocessor(NULL),
  342. mPreprocessState(PREPROCESS_WAITING),
  343. mLastHelpToken(NULL),
  344. mExternalEditor(NULL),
  345. mLiveHelpHistorySize(0),
  346. mCloseAfterSave(false),
  347. mForceClose(false),
  348. mNeedSaving(false),
  349. mEnableSave(false),
  350. mIsSaving(false),
  351. mHasScriptData(false),
  352. mSaveDialogShown(false),
  353. mLastPosUpdate(0.f),
  354. mLastHelpUpdate(0.f),
  355. LLEventTimer(AUTO_SAVE_INTERVAL)
  356. {
  357. sInstances.insert(this);
  358. LLUICtrlFactory::getInstance()->buildPanel(this, "panel_script_edit.xml");
  359. mTabContainer = getChild<LLTabContainer>("sources");
  360. mErrorList = getChild<LLScrollListCtrl>("lsl errors");
  361. mErrorList->setCommitCallback(onErrorList);
  362. mErrorList->setCallbackUserData(this);
  363. mFunctions = getChild<LLComboBox>("insert_combo");
  364. mFunctions->setCommitCallback(onBtnInsertFunction);
  365. mFunctions->setCallbackUserData(this);
  366. mEditor = getChild<LLViewerTextEditor>("unprocessed_script");
  367. mEditor->setHandleEditKeysDirectly(true);
  368. if (sCustomFont)
  369. {
  370. mEditor->setFont(sCustomFont);
  371. }
  372. mSavedSources = getChild<LLViewerTextEditor>("preprocessed_script");
  373. mSavedSources->setHandleEditKeysDirectly(true);
  374. if (sCustomFont)
  375. {
  376. mSavedSources->setFont(sCustomFont);
  377. }
  378. mMonoCheckbox = getChild<LLCheckBoxCtrl>("mono");
  379. mMonoCheckbox->setCommitCallback(onMonoCheckboxClicked);
  380. mMonoCheckbox->setCallbackUserData(this);
  381. mMonoCheckbox->setEnabled(false);
  382. mMonoCheckbox->setVisible(gIsInSecondLife);
  383. std::vector<std::string> funcs, tooltips;
  384. for (functions_table_t::const_iterator it = sParsedFunctions.begin(),
  385. end = sParsedFunctions.end();
  386. it != end; ++it)
  387. {
  388. // Make sure this is not a god only function, or the agent is a god.
  389. if (!it->mGodOnly || gAgent.isGodlike())
  390. {
  391. std::string name = it->mName;
  392. funcs.emplace_back(name);
  393. std::string desc = it->mTooltip;
  394. F32 sleep_time = it->mSleepTime;
  395. if (sleep_time)
  396. {
  397. desc += "\n";
  398. LLStringUtil::format_map_t args;
  399. args["[SLEEP_TIME]"] = llformat("%.1f", sleep_time);
  400. desc += LLTrans::getString("LSLTipSleepTime", args);
  401. }
  402. // A \n linefeed is not part of xml. Let's add one to keep all
  403. // the tips one-per-line in strings.xml
  404. LLStringUtil::replaceString(desc, "\\n", "\n");
  405. tooltips.emplace_back(desc);
  406. }
  407. }
  408. LLColor3 color = LLColor3(gColors.getColor("LslFunctionTextFgColor"));
  409. std::string keysfile = gDirUtil.getFullPath(LL_PATH_APP_SETTINGS,
  410. "keywords.ini");
  411. mEditor->loadKeywords(keysfile, funcs, tooltips, color);
  412. mSavedSources->loadKeywords(keysfile, funcs, tooltips, color);
  413. std::vector<std::string> primary_keywords, secondary_keywords;
  414. for (LLKeywords::keyword_iterator_t it = mEditor->keywordsBegin(),
  415. end = mEditor->keywordsEnd();
  416. it != end; ++it)
  417. {
  418. LLKeywordToken* token = it->second;
  419. if (!token) continue; // Paranoia
  420. // *HACK: sort tokens based on their highligthing colors... Better not
  421. // using the same highligthing color for all...
  422. if (token->getColor() == color)
  423. {
  424. primary_keywords.emplace_back(wstring_to_utf8str(token->getToken()));
  425. }
  426. else
  427. {
  428. secondary_keywords.emplace_back(wstring_to_utf8str(token->getToken()));
  429. }
  430. }
  431. // Case-insensitive dictionary sort for primary keywords. We do not sort
  432. // the secondary keywords. They are intelligently grouped in keywords.ini.
  433. std::stable_sort(primary_keywords.begin(), primary_keywords.end(),
  434. LLSECKeywordCompare());
  435. for (std::vector<std::string>::const_iterator
  436. it = primary_keywords.begin(), end = primary_keywords.end();
  437. it != end; ++it)
  438. {
  439. mFunctions->add(*it);
  440. }
  441. for (std::vector<std::string>::const_iterator
  442. it = secondary_keywords.begin(), end = secondary_keywords.end();
  443. it != end; ++it)
  444. {
  445. mFunctions->add(*it);
  446. }
  447. mSaveButton = getChild<LLButton>("save_btn");
  448. mSaveButton->setClickedCallback(onBtnSave, this);
  449. mSaveFlyoutButton = getChild<LLFlyoutButton>("save_flyout_btn");
  450. mSaveFlyoutButton->setCommitCallback(onFlyoutBtnSave);
  451. mSaveFlyoutButton->setCallbackUserData(this);
  452. bool is_inventory = gInventory.getItem(mItemUUID) != NULL;
  453. mSaveButton->setVisible(!is_inventory);
  454. mSaveFlyoutButton->setVisible(is_inventory);
  455. mLineColText = getChild<LLTextBox>("line_col");
  456. LLMenuItemCallGL* item = getChild<LLMenuItemCallGL>("load");
  457. item->setMenuCallback(onBtnLoadFromFile, this);
  458. item->setEnabledCallback(enableLoadFile);
  459. item = getChild<LLMenuItemCallGL>("save");
  460. item->setMenuCallback(onBtnSaveToFile, this);
  461. item->setEnabledCallback(enableSaveFile);
  462. item = getChild<LLMenuItemCallGL>("external");
  463. item->setMenuCallback(onEditExternal, this);
  464. item->setEnabledCallback(enableLoadFile);
  465. item = getChild<LLMenuItemCallGL>("raw");
  466. item->setMenuCallback(onEditRaw, this);
  467. item->setEnabledCallback(enableRaw);
  468. item = getChild<LLMenuItemCallGL>("revert");
  469. item->setMenuCallback(onBtnUndoChanges, this);
  470. item->setEnabledCallback(enableCallback);
  471. item = getChild<LLMenuItemCallGL>("undo");
  472. item->setMenuCallback(onUndoMenu, this);
  473. item->setEnabledCallback(enableUndoMenu);
  474. item = getChild<LLMenuItemCallGL>("redo");
  475. item->setMenuCallback(onRedoMenu, this);
  476. item->setEnabledCallback(enableRedoMenu);
  477. item = getChild<LLMenuItemCallGL>("cut");
  478. item->setMenuCallback(onCutMenu, this);
  479. item->setEnabledCallback(enableCutMenu);
  480. item = getChild<LLMenuItemCallGL>("copy");
  481. item->setMenuCallback(onCopyMenu, this);
  482. item->setEnabledCallback(enableCopyMenu);
  483. item = getChild<LLMenuItemCallGL>("paste");
  484. item->setMenuCallback(onPasteMenu, this);
  485. item->setEnabledCallback(enablePasteMenu);
  486. item = getChild<LLMenuItemCallGL>("select_all");
  487. item->setMenuCallback(onSelectAllMenu, this);
  488. item->setEnabledCallback(enableSelectAllMenu);
  489. item = getChild<LLMenuItemCallGL>("deselect");
  490. item->setMenuCallback(onDeselectMenu, this);
  491. item->setEnabledCallback(enableDeselectMenu);
  492. item = getChild<LLMenuItemCallGL>("search");
  493. item->setMenuCallback(onSearchMenu, this);
  494. item->setEnabledCallback(NULL);
  495. item = getChild<LLMenuItemCallGL>("wiki");
  496. item->setMenuCallback(onBtnHelp, this);
  497. item->setEnabledCallback(NULL);
  498. item = getChild<LLMenuItemCallGL>("help");
  499. item->setMenuCallback(onBtnDynamicHelp, this);
  500. item->setEnabledCallback(enableHelp);
  501. LLMenuItemCheckGL* check = getChild<LLMenuItemCheckGL>("dynamic");
  502. check->setMenuCallback(onHelpFollowCursor, this);
  503. check->setEnabledCallback(enableHelp);
  504. // Tell LLEditMenuHandler about our editor type: this will trigger a Lua
  505. // callback if one is configured for context menus. HB
  506. mEditor->setCustomMenuType("script");
  507. }
  508. //virtual
  509. LLScriptEditor::~LLScriptEditor()
  510. {
  511. sInstances.erase(this);
  512. gIdleCallbacks.deleteFunction(onIdle, this);
  513. if (mPreprocessor)
  514. {
  515. delete mPreprocessor;
  516. }
  517. if (mExternalEditor)
  518. {
  519. delete mExternalEditor;
  520. }
  521. }
  522. //virtual
  523. void LLScriptEditor::draw()
  524. {
  525. bool changed = hasChanged();
  526. mSaveButton->setEnabled(changed);
  527. mSaveFlyoutButton->setEnabled(changed);
  528. // Do not do this every frame !
  529. if (gFrameTimeSeconds > mLastPosUpdate + 0.25f)
  530. {
  531. if (mEditor->hasFocus())
  532. {
  533. S32 row = 0;
  534. S32 col = 0;
  535. // false = do not include wordwrap
  536. mEditor->getCurrentLineAndColumn(&row, &col, false);
  537. mLineColText->setText(llformat("Line %d, Column %d", row, col));
  538. }
  539. else
  540. {
  541. mLineColText->setText(LLStringUtil::null);
  542. }
  543. mLastPosUpdate = gFrameTimeSeconds;
  544. }
  545. // Do not do this every frame !
  546. if (gFrameTimeSeconds > mLastHelpUpdate + LIVE_HELP_REFRESH_TIME)
  547. {
  548. updateDynamicHelp();
  549. }
  550. LLPanel::draw();
  551. }
  552. //virtual
  553. bool LLScriptEditor::canClose()
  554. {
  555. if (mForceClose || !hasChanged())
  556. {
  557. return true;
  558. }
  559. if (!mSaveDialogShown)
  560. {
  561. mSaveDialogShown = true;
  562. // Bring up view-modal dialog: Save changes ? Yes, No, Cancel
  563. gNotifications.add("SaveChanges", LLSD(), LLSD(),
  564. boost::bind(&LLScriptEditor::handleSaveChangesDialog,
  565. this, _1, _2));
  566. }
  567. return false;
  568. }
  569. //virtual
  570. bool LLScriptEditor::handleKeyHere(KEY key, MASK mask)
  571. {
  572. if ((mask & MASK_MODIFIERS) == MASK_CONTROL)
  573. {
  574. if (key == 'S')
  575. {
  576. // false = do not close after saving
  577. doSave(false);
  578. return true;
  579. }
  580. if (key == 'F')
  581. {
  582. if (mSearchReplaceCallback)
  583. {
  584. mSearchReplaceCallback(mUserdata);
  585. }
  586. return true;
  587. }
  588. }
  589. return false;
  590. }
  591. void LLScriptEditor::autoSave()
  592. {
  593. if (mAutosaveFilename.empty())
  594. {
  595. std::string filename = gDirUtil.getTempFilename(false) + ".lsl";
  596. mAutosaveFilename = filename;
  597. }
  598. if (mExternalEditor)
  599. {
  600. // Do not cause a file changed event for something we trigger ourselves
  601. // (the external editor will cause a file access read event, which is
  602. // considered a changed event, and would cause HBExternalEditor to call
  603. // our own changed file event, which we do not want to happen here).
  604. mExternalEditor->ignoreNextUpdate();
  605. }
  606. LLFILE* fp = LLFile::open(mAutosaveFilename, "wb");
  607. if (!fp)
  608. {
  609. llwarns << "Unable to write to " << mAutosaveFilename << llendl;
  610. addComment(getString("cannot_write"), true);
  611. return;
  612. }
  613. // Note: we save the edited (not (yet) preprocessed) text, not the saved
  614. // (and preprocessed) one.
  615. std::string text = mEditor->getText();
  616. if (text.empty())
  617. {
  618. // Special case for a completely empty script; stuff in one new line so
  619. // that it can store properly. See SL-46889
  620. text = "\n";
  621. }
  622. fputs(text.c_str(), fp);
  623. LLFile::close(fp);
  624. llinfos << "Auto-saved: " << mAutosaveFilename << llendl;
  625. }
  626. //virtual
  627. bool LLScriptEditor::tick()
  628. {
  629. // Do not auto-save when nothing changed or the text is being edited in an
  630. // external text editor.
  631. if (!mEditor->isPristine() &&
  632. !(mExternalEditor && mExternalEditor->running()))
  633. {
  634. autoSave();
  635. }
  636. return false;
  637. }
  638. void LLScriptEditor::addComment(const std::string& comment, bool is_error)
  639. {
  640. if (is_error)
  641. {
  642. LLSD row;
  643. LLSD& column = row["columns"][0];
  644. column["value"] = comment;
  645. column["font"] = "SMALL";
  646. column["color"] = LLColor4::red2.getValue();
  647. mErrorList->addElement(row);
  648. }
  649. else
  650. {
  651. mErrorList->addCommentText(comment);
  652. }
  653. mErrorList->scrollToShowLast();
  654. }
  655. void LLScriptEditor::enableEdit(bool enable)
  656. {
  657. mIsSaving = !enable;
  658. mEditor->setEnabled(enable);
  659. }
  660. std::string LLScriptEditor::getItemPath()
  661. {
  662. std::string path;
  663. LLInventoryItem* item = gInventory.getItem(mItemUUID);
  664. if (!item)
  665. {
  666. // Not in inventory
  667. return path;
  668. }
  669. if (!gInventory.isObjectDescendentOf(mItemUUID,
  670. gInventory.getRootFolderID()))
  671. {
  672. // Not in user inventory (i.e. it is a library item)
  673. return path;
  674. }
  675. // Find the full inventory path for the item
  676. path = "|"; // Start at root inventory
  677. const LLUUID& root_id = gInventory.getRootFolderID();
  678. LLUUID cat_id = item->getParentUUID();
  679. while (cat_id != root_id)
  680. {
  681. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
  682. if (!cat)
  683. {
  684. // Something is very wrong... Give up !
  685. path.clear();
  686. break;
  687. }
  688. path = "|" + cat->getName() + path;
  689. cat_id = cat->getParentUUID();
  690. }
  691. return path;
  692. }
  693. void LLScriptEditor::preprocess()
  694. {
  695. if (mPreprocessState == PREPROCESS_START)
  696. {
  697. enableEdit(false);
  698. addComment(getString("preprocessing"));
  699. if (!mPreprocessor)
  700. {
  701. std::string item_path = getItemPath();
  702. mPreprocessor = new HBPreprocessor(item_path + mScriptName,
  703. loadInclude, this);
  704. mPreprocessor->setMessageCallback(preprocessorMessage);
  705. }
  706. if (mPreprocessor->preprocess(mEditor->getText()) ==
  707. HBPreprocessor::PAUSED)
  708. {
  709. // We need to wait till an #include script asset gets loaded...
  710. mPreprocessState = PREPROCESS_WAITING;
  711. gIdleCallbacks.addFunction(onIdle, this);
  712. return;
  713. }
  714. // Note: we are also done in case of error
  715. mPreprocessState = PREPROCESS_DONE;
  716. }
  717. // mPreprocessState is set to PREPROCESS_RESUME when an #included asset has
  718. // successfully loaded. Should it fail to load, the state would be set to
  719. // PREPROCESS_DONE.
  720. if (mPreprocessState == PREPROCESS_RESUME)
  721. {
  722. if (mPreprocessor && mPreprocessor->resume() == HBPreprocessor::PAUSED)
  723. {
  724. mPreprocessState = PREPROCESS_WAITING;
  725. return;
  726. }
  727. // Note: we are also done in case of error
  728. mPreprocessState = PREPROCESS_DONE;
  729. }
  730. if (mPreprocessState == PREPROCESS_DONE)
  731. {
  732. gIdleCallbacks.deleteFunction(onIdle, this);
  733. mSavedSources->setText(mPreprocessor->getResult() +
  734. escapeSources(mEditor->getText()));
  735. addComment(getString("done"));
  736. enableEdit(true);
  737. mPreprocessState = PREPROCESS_WAITING;
  738. if (mNeedSaving)
  739. {
  740. doSave(mCloseAfterSave, false);
  741. }
  742. }
  743. }
  744. void LLScriptEditor::setScriptText(std::string text, bool is_valid,
  745. bool set_saved)
  746. {
  747. mHasScriptData = is_valid;
  748. mErrorList->deleteAllItems();
  749. if (set_saved)
  750. {
  751. // Set sources "as is" in the "Saved script" tab editor
  752. mSavedSources->setText(text);
  753. }
  754. if (text.find(ALIEN_ESCAPED_START_MARKER) != std::string::npos)
  755. {
  756. text = convertSources(text);
  757. }
  758. if (text.find(ESCAPED_INCLUDE_MARKER) != std::string::npos)
  759. {
  760. text = getIncludeSources(text);
  761. }
  762. else if (text.find(ESCAPED_SOURCES_MARKER) != std::string::npos)
  763. {
  764. text = unescapeSources(text);
  765. }
  766. // Set cleaned up, non-processed sources in the "Edited script" tab editor
  767. mEditor->setText(text);
  768. }
  769. void LLScriptEditor::setEditedTextFromSaved()
  770. {
  771. if (mHasScriptData)
  772. {
  773. mEditor->setText(mSavedSources->getText());
  774. }
  775. }
  776. void LLScriptEditor::setScriptName(std::string name)
  777. {
  778. if (name.find("Script: ") == 0)
  779. {
  780. name = name.substr(8);
  781. }
  782. if (name.empty())
  783. {
  784. name = "untitled";
  785. }
  786. mScriptName = name;
  787. if (mPreprocessor)
  788. {
  789. mPreprocessor->setFilename(name);
  790. }
  791. }
  792. void LLScriptEditor::doSave(bool close_after_save, bool check_preprocessing)
  793. {
  794. mCloseAfterSave = close_after_save;
  795. mIsSaving = true;
  796. std::string text = mEditor->getText();
  797. if (!mHasScriptData || text.empty())
  798. {
  799. llwarns << "Nothing to save" << llendl;
  800. return;
  801. }
  802. if (!mSaveCallback)
  803. {
  804. llwarns << "No save callback !" << llendl;
  805. return;
  806. }
  807. if (check_preprocessing)
  808. {
  809. mErrorList->deleteAllItems();
  810. if (HBPreprocessor::needsPreprocessing(text))
  811. {
  812. mNeedSaving = true;
  813. mPreprocessState = PREPROCESS_START;
  814. preprocess();
  815. return;
  816. }
  817. mSavedSources->setText(text);
  818. if (mPreprocessor)
  819. {
  820. mPreprocessor->clear();
  821. }
  822. }
  823. else
  824. {
  825. mNeedSaving = false;
  826. }
  827. if (!close_after_save && mExternalEditor && mExternalEditor->running())
  828. {
  829. autoSave();
  830. }
  831. addComment(getString("compiling"));
  832. gViewerStats.incStat(LLViewerStats::ST_LSL_SAVE_COUNT);
  833. mSaveCallback(mUserdata, mCloseAfterSave);
  834. }
  835. void LLScriptEditor::loadFile(const std::string& filename)
  836. {
  837. if (!filename.empty())
  838. {
  839. llifstream file(filename.c_str());
  840. if (file.is_open())
  841. {
  842. mEditor->clear();
  843. std::string line, text;
  844. while (!file.eof())
  845. {
  846. getline(file, line);
  847. text += line + "\n";
  848. }
  849. file.close();
  850. LLWString wtext = utf8str_to_wstring(text);
  851. LLWStringUtil::replaceTabsWithSpaces(wtext, 4);
  852. text = wstring_to_utf8str(wtext);
  853. setScriptText(text, true, false);
  854. enableSave(true);
  855. }
  856. }
  857. }
  858. void LLScriptEditor::updateDynamicHelp(bool immediate)
  859. {
  860. mLastHelpUpdate = gFrameTimeSeconds;
  861. LLFloater* help_floater = mLiveHelpHandle.get();
  862. if (!help_floater || ! help_floater->getVisible()) return;
  863. // Update back and forward buttons
  864. LLButton* fwd_button = help_floater->getChild<LLButton>("fwd_btn");
  865. LLButton* back_button = help_floater->getChild<LLButton>("back_btn");
  866. LLMediaCtrl* browser =
  867. help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  868. back_button->setEnabled(browser->canNavigateBack());
  869. fwd_button->setEnabled(browser->canNavigateForward());
  870. static LLCachedControl<bool> help_follow_cursor(gSavedSettings,
  871. "ScriptHelpFollowsCursor");
  872. help_floater->childSetValue("lock_check", (bool)help_follow_cursor);
  873. if (!immediate && !help_follow_cursor)
  874. {
  875. return;
  876. }
  877. const LLTextSegment* segment = NULL;
  878. std::vector<const LLTextSegment*> selected_segments;
  879. mEditor->getSelectedSegments(selected_segments);
  880. // Try segments in selection range first
  881. for (std::vector<const LLTextSegment*>::iterator
  882. segment_iter = selected_segments.begin(),
  883. end = selected_segments.end();
  884. segment_iter != end; ++segment_iter)
  885. {
  886. if ((*segment_iter)->getToken() &&
  887. (*segment_iter)->getToken()->getType() == LLKeywordToken::WORD)
  888. {
  889. segment = *segment_iter;
  890. break;
  891. }
  892. }
  893. // Then try previous segment in case we just typed it
  894. if (!segment)
  895. {
  896. const LLTextSegment* test_segment = mEditor->getPreviousSegment();
  897. if (test_segment->getToken() &&
  898. test_segment->getToken()->getType() == LLKeywordToken::WORD)
  899. {
  900. segment = test_segment;
  901. }
  902. }
  903. if (segment && segment->getToken() != mLastHelpToken)
  904. {
  905. mLastHelpToken = segment->getToken();
  906. // Use Wtext since segment's start/end are made for wstring and will
  907. // result in a shift for case of multi-byte symbols inside std::string.
  908. LLWString seg_txt = mEditor->getWText().substr(segment->getStart(),
  909. segment->getEnd() -
  910. segment->getStart());
  911. setHelpPage(wstring_to_utf8str(seg_txt));
  912. }
  913. else if (immediate)
  914. {
  915. setHelpPage(LLStringUtil::null);
  916. }
  917. }
  918. void LLScriptEditor::setHelpPage(const std::string& help_string)
  919. {
  920. LLFloater* help_floater = mLiveHelpHandle.get();
  921. if (!help_floater) return;
  922. LLMediaCtrl* web_browser =
  923. help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  924. LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
  925. std::string topic = help_string;
  926. if (topic.empty())
  927. {
  928. topic = gSavedSettings.getString("LSLHelpDefaultTopic");
  929. }
  930. url_string.setArg("[LSL_STRING]", topic);
  931. addHelpItemToHistory(help_string);
  932. web_browser->navigateTo(url_string);
  933. }
  934. void LLScriptEditor::addHelpItemToHistory(const std::string& help_string)
  935. {
  936. if (help_string.empty()) return;
  937. LLFloater* help_floater = mLiveHelpHandle.get();
  938. if (!help_floater) return;
  939. LLComboBox* history_combo =
  940. help_floater->getChild<LLComboBox>("history_combo");
  941. // Separate history items from full item list
  942. if (mLiveHelpHistorySize == 0)
  943. {
  944. LLSD row;
  945. row["columns"][0]["type"] = "separator";
  946. history_combo->addElement(row, ADD_TOP);
  947. }
  948. // Delete all history items over history limit
  949. while (mLiveHelpHistorySize > MAX_HISTORY_COUNT - 1)
  950. {
  951. history_combo->remove(--mLiveHelpHistorySize);
  952. }
  953. history_combo->setSimple(help_string);
  954. S32 index = history_combo->getCurrentIndex();
  955. // If help string exists in the combo box
  956. if (index >= 0)
  957. {
  958. S32 cur_index = history_combo->getCurrentIndex();
  959. if (cur_index < mLiveHelpHistorySize)
  960. {
  961. // Item found in history, bubble up to top
  962. history_combo->remove(history_combo->getCurrentIndex());
  963. --mLiveHelpHistorySize;
  964. }
  965. }
  966. history_combo->add(help_string, LLSD(help_string), ADD_TOP);
  967. history_combo->selectFirstItem();
  968. ++mLiveHelpHistorySize;
  969. }
  970. bool LLScriptEditor::handleSaveChangesDialog(const LLSD& notification,
  971. const LLSD& response)
  972. {
  973. mSaveDialogShown = false;
  974. S32 option = LLNotification::getSelectedOption(notification, response);
  975. switch (option)
  976. {
  977. case 0: // "Yes"
  978. // Close after saving
  979. doSave(true);
  980. break;
  981. case 1: // "No"
  982. if (!mAutosaveFilename.empty())
  983. {
  984. llinfos << "Remove autosave: " << mAutosaveFilename << llendl;
  985. LLFile::remove(mAutosaveFilename);
  986. }
  987. mForceClose = true;
  988. // This will close immediately because mForceClose is true, so we
  989. // would not go into an infinite loop with these dialogs. JC
  990. ((LLFloater*) getParent())->close();
  991. break;
  992. case 2: // "Cancel"
  993. default:
  994. // If we were quitting, we did not really mean it.
  995. gAppViewerp->abortQuit();
  996. }
  997. return false;
  998. }
  999. bool LLScriptEditor::handleReloadFromServerDialog(const LLSD& notification,
  1000. const LLSD& response)
  1001. {
  1002. if (LLNotification::getSelectedOption(notification, response) == 1)
  1003. {
  1004. if (mLoadCallback)
  1005. {
  1006. setScriptText(getString("loading"), false);
  1007. mLoadCallback(mUserdata);
  1008. }
  1009. }
  1010. return false;
  1011. }
  1012. void LLScriptEditor::selectFirstError()
  1013. {
  1014. // Select the first item
  1015. mErrorList->selectFirstItem();
  1016. onErrorList(mErrorList, this);
  1017. }
  1018. struct LLScriptAssetData
  1019. {
  1020. LLScriptEditor* instance;
  1021. LLUUID item_id;
  1022. };
  1023. bool LLScriptEditor::loadAsset(LLViewerInventoryItem* item)
  1024. {
  1025. if (!gAgent.allowOperation(PERM_COPY, item->getPermissions(),
  1026. GP_OBJECT_MANIPULATE) ||
  1027. !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
  1028. GP_OBJECT_MANIPULATE))
  1029. {
  1030. return false;
  1031. }
  1032. LLScriptAssetData* data = new LLScriptAssetData();
  1033. data->instance = this;
  1034. data->item_id = item->getUUID();
  1035. gAssetStoragep->getInvItemAsset(LLHost(), gAgentID, gAgentSessionID,
  1036. item->getPermissions().getOwner(),
  1037. LLUUID::null, data->item_id,
  1038. item->getAssetUUID(), item->getType(),
  1039. onLoadComplete, (void*)data, true);
  1040. return true;
  1041. }
  1042. //static
  1043. void LLScriptEditor::onLoadComplete(const LLUUID& asset_id,
  1044. LLAssetType::EType type, void* userdata,
  1045. S32 status, LLExtStat)
  1046. {
  1047. LLScriptAssetData* data = (LLScriptAssetData*)userdata;
  1048. if (!data) return;
  1049. LLScriptEditor* self = data->instance;
  1050. LLUUID item_id = data->item_id;
  1051. delete data;
  1052. if (!self || !sInstances.count(self)) return;
  1053. LLInventoryItem* item = gInventory.getItem(item_id);
  1054. if (!item)
  1055. {
  1056. llwarns << "Script inventory item " << item_id << " is gone" << llendl;
  1057. return;
  1058. }
  1059. if (status == 0)
  1060. {
  1061. LL_DEBUGS("ScriptEditor") << "Got #include asset Id " << asset_id
  1062. << " for item Id " << item_id << LL_ENDL;
  1063. // At this point, the asset data has been loaded into the cache
  1064. item->setAssetUUID(asset_id);
  1065. // Resume the preprocessing when paused
  1066. if (self->mPreprocessState == PREPROCESS_WAITING)
  1067. {
  1068. self->mPreprocessState = PREPROCESS_RESUME;
  1069. }
  1070. }
  1071. else if (self->mPreprocessState == PREPROCESS_WAITING)
  1072. {
  1073. LL_DEBUGS("ScriptEditor") << "#include asset Id " << asset_id
  1074. << " for item Id " << item_id
  1075. << " not available" << LL_ENDL;
  1076. // Abort the preprocessing when paused
  1077. self->mPreprocessState = PREPROCESS_DONE;
  1078. }
  1079. }
  1080. //static
  1081. LLViewerInventoryItem* LLScriptEditor::getScriptItem(const std::string& name)
  1082. {
  1083. if (name.empty() || name == "|" || name[name.length() - 1] == '|')
  1084. {
  1085. llwarns << "Invalid script item inventory name: " << name << llendl;
  1086. return NULL;
  1087. }
  1088. // Split the string into path elements
  1089. std::string item_name = name;
  1090. std::string cat_name;
  1091. std::deque<std::string> path;
  1092. size_t i;
  1093. while ((i = item_name.find('|')) != std::string::npos)
  1094. {
  1095. cat_name = item_name.substr(0, i);
  1096. item_name = item_name.substr(i + 1);
  1097. // cat_name is empty when 2+ successive '|' exist in path, or when one
  1098. // is leading the full path. In both cases, skip the empty element.
  1099. if (!cat_name.empty())
  1100. {
  1101. LL_DEBUGS("ScriptEditor") << "Pushing category name: " << cat_name
  1102. << LL_ENDL;
  1103. path.emplace_back(cat_name);
  1104. }
  1105. }
  1106. LL_DEBUGS("ScriptEditor") << "Searching for item named: " << item_name
  1107. << LL_ENDL;
  1108. // Search for the category where the script should be located
  1109. LLUUID cat_id = gInventory.getRootFolderID();
  1110. LLInventoryModel::cat_array_t* cats;
  1111. LLInventoryModel::item_array_t* items;
  1112. gInventory.getDirectDescendentsOf(cat_id, cats, items);
  1113. while (!path.empty())
  1114. {
  1115. cat_name = path.front();
  1116. path.pop_front();
  1117. LL_DEBUGS("ScriptEditor") << "Searching category named: " << cat_name
  1118. << " in category " << cat_id << LL_ENDL;
  1119. // Search for next category down the path
  1120. LLViewerInventoryCategory* cat = NULL;
  1121. for (S32 i = 0, count = cats->size(); i < count; ++i)
  1122. {
  1123. cat = (*cats)[i];
  1124. if (cat && cat->getName() == cat_name)
  1125. {
  1126. cat_id = cat->getUUID();
  1127. LL_DEBUGS("ScriptEditor") << "Found category " << cat_id
  1128. << LL_ENDL;
  1129. break;
  1130. }
  1131. cat = NULL;
  1132. }
  1133. if (!cat)
  1134. {
  1135. LL_DEBUGS("ScriptEditor") << "Category " << cat_name
  1136. << " not found" << LL_ENDL;
  1137. // Next category in path not found...
  1138. return NULL;
  1139. }
  1140. gInventory.getDirectDescendentsOf(cat_id, cats, items);
  1141. }
  1142. LL_DEBUGS("ScriptEditor") << "Searching for item named: " << item_name
  1143. << " in category " << cat_id << LL_ENDL;
  1144. // We reached the deepest category, and should find the script here
  1145. for (S32 i = 0, count = items->size(); i < count; ++i)
  1146. {
  1147. LLViewerInventoryItem* item = (*items)[i];
  1148. if (item && item->getType() == LLAssetType::AT_LSL_TEXT &&
  1149. item->getName() == item_name)
  1150. {
  1151. return item;
  1152. }
  1153. }
  1154. return NULL;
  1155. }
  1156. //static
  1157. S32 LLScriptEditor::loadInclude(std::string& include_name,
  1158. const std::string& path, std::string& buffer,
  1159. void* userdata)
  1160. {
  1161. buffer.clear();
  1162. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1163. if (!self)
  1164. {
  1165. return HBPreprocessor::FAILURE;
  1166. }
  1167. std::string item_path;
  1168. // Check whether we want to include files from the file system instead of
  1169. // script assets from the inventory
  1170. bool in_home_dir = path.find("~/") == 0;
  1171. if (in_home_dir || path.find("./") == 0)
  1172. {
  1173. LL_DEBUGS("ScriptEditor") << "Including from file system with path: "
  1174. << path << LL_ENDL;
  1175. if (in_home_dir)
  1176. {
  1177. // Search in user "home" directory, without fallback sub-directory
  1178. item_path = gDirUtil.getUserFilename(path, "", include_name);
  1179. }
  1180. else
  1181. {
  1182. item_path = gDirUtil.getUserFilename(path, "include",
  1183. include_name);
  1184. }
  1185. if (item_path.empty())
  1186. {
  1187. LL_DEBUGS("ScriptEditor") << "File not found" << LL_ENDL;
  1188. return HBPreprocessor::FAILURE;
  1189. }
  1190. LL_DEBUGS("ScriptEditor") << "File found: " << item_path << LL_ENDL;
  1191. llifstream include_file(item_path.c_str());
  1192. if (!include_file.is_open())
  1193. {
  1194. llwarns << "Failure to open file: " << item_path << llendl;
  1195. return HBPreprocessor::FAILURE;
  1196. }
  1197. // Return the full path of the include file we opened successfully
  1198. include_name = item_path;
  1199. self->addComment(self->getString("including_file") + " " + item_path);
  1200. std::string line;
  1201. while (!include_file.eof())
  1202. {
  1203. getline(include_file, line);
  1204. buffer += line + "\n";
  1205. }
  1206. include_file.close();
  1207. return HBPreprocessor::SUCCESS;
  1208. }
  1209. // Get item current path in inventory
  1210. item_path = self->getItemPath();
  1211. std::string real_path;
  1212. LLViewerInventoryItem* item = NULL;
  1213. if (!path.empty()) // check any path set with #pragma include-from:
  1214. {
  1215. real_path = path;
  1216. if (path.back() != '|')
  1217. {
  1218. // Add a separator at the end when missing
  1219. real_path += "|";
  1220. }
  1221. if (path.front() != '|')
  1222. {
  1223. // This is a relative path
  1224. if (item_path.empty())
  1225. {
  1226. // But with an empty item path, it is relative to the inventory
  1227. // root ...
  1228. real_path = "|" + real_path;
  1229. }
  1230. else
  1231. {
  1232. real_path = item_path + real_path;
  1233. }
  1234. }
  1235. LL_DEBUGS("ScriptEditor") << "Searching for inventory item "
  1236. << include_name << " in inventory folder: "
  1237. << real_path << LL_ENDL;
  1238. item = getScriptItem(real_path + include_name);
  1239. }
  1240. if (!item && !item_path.empty())
  1241. {
  1242. // Retry with the item folder
  1243. real_path = item_path;
  1244. LL_DEBUGS("ScriptEditor") << "Searching for inventory item "
  1245. << include_name << " in inventory folder: "
  1246. << real_path << LL_ENDL;
  1247. item = getScriptItem(real_path + include_name);
  1248. }
  1249. if (!item && item_path != "|Scripts|")
  1250. {
  1251. // Retry with the Scripts folder
  1252. real_path = "|Scripts|";
  1253. LL_DEBUGS("ScriptEditor") << "Searching for inventory item "
  1254. << include_name << " in inventory folder: "
  1255. << real_path << LL_ENDL;
  1256. item = getScriptItem(real_path + include_name);
  1257. }
  1258. if (!item)
  1259. {
  1260. LL_DEBUGS("ScriptEditor") << "Item for #include " << include_name
  1261. << " not found" << LL_ENDL;
  1262. return HBPreprocessor::FAILURE;
  1263. }
  1264. // asset_id is LLUUID::null unless it just got fetched and we are actually
  1265. // in a HBPreprocessor::resume() call.
  1266. const LLUUID& asset_id = item->getAssetUUID();
  1267. if (asset_id.notNull())
  1268. {
  1269. // Try and find the asset in the cache
  1270. LLFileSystem file(asset_id);
  1271. S32 file_length = file.getSize();
  1272. if (file_length > 0)
  1273. {
  1274. // Get the asset data (the included script text)
  1275. char* data = new char[file_length + 1];
  1276. file.read((U8*)data, file_length);
  1277. data[file_length] = 0;
  1278. buffer.assign(&data[0]);
  1279. delete[] data;
  1280. // If it is an escaped include script, convert it to its
  1281. // non-escaped version.
  1282. if (buffer.find(ESCAPED_INCLUDE_MARKER) != std::string::npos)
  1283. {
  1284. buffer = getIncludeSources(buffer);
  1285. }
  1286. // If it is a preprocessed script, remove the escaped sources
  1287. else if (buffer.find(ESCAPED_SOURCES_MARKER) != std::string::npos)
  1288. {
  1289. buffer = removeEscapedSources(buffer);
  1290. }
  1291. // Remove the asset data from the cache to ensure that it will be
  1292. // re-fetched next time and kept up to date with any change.
  1293. LLFileSystem::removeFile(asset_id);
  1294. // And reset the asset UUID for this inventory item.
  1295. item->setAssetUUID(LLUUID::null);
  1296. return HBPreprocessor::SUCCESS;
  1297. }
  1298. }
  1299. self->addComment(self->getString("including_script") + " " + real_path +
  1300. include_name);
  1301. self->loadAsset(item);
  1302. return HBPreprocessor::PAUSED;
  1303. }
  1304. //static
  1305. void LLScriptEditor::preprocessorMessage(const std::string& message,
  1306. bool is_warning, void* userdata)
  1307. {
  1308. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1309. if (self)
  1310. {
  1311. self->addComment(message, !is_warning);
  1312. }
  1313. }
  1314. //static
  1315. void LLScriptEditor::onIdle(void* userdata)
  1316. {
  1317. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1318. if (self)
  1319. {
  1320. self->preprocess();
  1321. }
  1322. }
  1323. //static
  1324. std::string LLScriptEditor::escapeSources(const std::string& sources)
  1325. {
  1326. size_t len = sources.length();
  1327. if (!len)
  1328. {
  1329. return "";
  1330. }
  1331. std::string result = "\n" + ESCAPED_SOURCES_MARKER;
  1332. size_t pos = 0;
  1333. while (pos < len)
  1334. {
  1335. result += ESCAPE_STRING + get_one_line(sources, pos);
  1336. }
  1337. len = result.length();
  1338. if (result[len - 1] != '\n')
  1339. {
  1340. result += '\n';
  1341. }
  1342. return result;
  1343. }
  1344. //static
  1345. std::string LLScriptEditor::unescapeSources(const std::string& sources)
  1346. {
  1347. size_t len = sources.length();
  1348. if (!len)
  1349. {
  1350. return "";
  1351. }
  1352. std::string result, line;
  1353. size_t pos = 0;
  1354. while (pos < len && get_one_line(sources, pos) != ESCAPED_SOURCES_MARKER);
  1355. size_t esc_len = ESCAPE_STRING.length();
  1356. while (pos < len)
  1357. {
  1358. line = get_one_line(sources, pos);
  1359. if (line.find(ESCAPE_STRING) != 0)
  1360. {
  1361. break;
  1362. }
  1363. result += line.substr(esc_len);
  1364. }
  1365. len = result.length();
  1366. if (result[len - 1] != '\n')
  1367. {
  1368. result += '\n';
  1369. }
  1370. return result;
  1371. }
  1372. //static
  1373. std::string LLScriptEditor::removeEscapedSources(const std::string& sources)
  1374. {
  1375. size_t len = sources.length();
  1376. if (!len)
  1377. {
  1378. return "";
  1379. }
  1380. std::string result, line;
  1381. size_t pos = 0;
  1382. while (pos < len)
  1383. {
  1384. line = get_one_line(sources, pos);
  1385. if (line == ESCAPED_SOURCES_MARKER)
  1386. {
  1387. break;
  1388. }
  1389. result += line;
  1390. }
  1391. len = result.length();
  1392. if (result[len - 1] != '\n')
  1393. {
  1394. result += '\n';
  1395. }
  1396. return result;
  1397. }
  1398. //static
  1399. std::string LLScriptEditor::setIncludeSources(const std::string& sources)
  1400. {
  1401. size_t len = sources.length();
  1402. if (!len)
  1403. {
  1404. return "";
  1405. }
  1406. std::string result = ESCAPED_INCLUDE_MARKER;
  1407. size_t pos = 0;
  1408. while (pos < len)
  1409. {
  1410. result += ESCAPE_STRING + get_one_line(sources, pos);
  1411. }
  1412. len = result.length();
  1413. if (result[len - 1] != '\n')
  1414. {
  1415. result += '\n';
  1416. }
  1417. return result + ESCAPED_INCLUDE_FOOTER + DUMMY_STATE;
  1418. }
  1419. //static
  1420. std::string LLScriptEditor::getIncludeSources(const std::string& sources)
  1421. {
  1422. size_t len = sources.length();
  1423. if (!len)
  1424. {
  1425. return "";
  1426. }
  1427. std::string result, line;
  1428. size_t pos = 0;
  1429. while (pos < len &&
  1430. get_one_line(sources, pos) != ESCAPED_INCLUDE_MARKER);
  1431. size_t esc_len = ESCAPE_STRING.length();
  1432. while (pos < len)
  1433. {
  1434. line = get_one_line(sources, pos);
  1435. if (line.find(ESCAPE_STRING) != 0)
  1436. {
  1437. break;
  1438. }
  1439. result += line.substr(esc_len);
  1440. }
  1441. len = result.length();
  1442. if (result[len - 1] != '\n')
  1443. {
  1444. result += '\n';
  1445. }
  1446. return result;
  1447. }
  1448. //static
  1449. std::string LLScriptEditor::convertSources(const std::string& sources)
  1450. {
  1451. static size_t start_len = ALIEN_ESCAPED_START_MARKER.length();
  1452. size_t pos = sources.find(ALIEN_ESCAPED_START_MARKER);
  1453. if (pos == std::string::npos)
  1454. {
  1455. return sources;
  1456. }
  1457. std::string result = sources.substr(pos + start_len);
  1458. pos = result.find(ALIEN_ESCAPED_END_MARKER);
  1459. if (pos == std::string::npos)
  1460. {
  1461. llwarns << "Missing marker for end of preprocessed source in script text"
  1462. << llendl;
  1463. }
  1464. else
  1465. {
  1466. result = result.substr(0, pos);
  1467. }
  1468. pos = result.length();
  1469. if (pos == 0 || result[pos - 1] != '\n')
  1470. {
  1471. result += "\n";
  1472. }
  1473. // Unescape comments
  1474. LLStringUtil::replaceString(result, "/|/", "//");
  1475. LLStringUtil::replaceString(result, "/|*", "/*");
  1476. LLStringUtil::replaceString(result, "*|/", "*/");
  1477. // Also convert special defines
  1478. LLStringUtil::replaceString(result, "__AGENTID__", "__AGENT_ID__");
  1479. LLStringUtil::replaceString(result, "__AGENTKEY__", "__AGENT_ID__");
  1480. LLStringUtil::replaceString(result, "__AGENTNAME__", "__AGENT_NAME__");
  1481. // Approximatively equivalent
  1482. LLStringUtil::replaceString(result, "__AGENTIDRAW__", "__AGENT_ID__");
  1483. LLStringUtil::replaceString(result, "__SHORTFILE__", "__FILE__");
  1484. return result;
  1485. }
  1486. //static
  1487. bool LLScriptEditor::onHelpWebDialog(const LLSD& notification,
  1488. const LLSD& response)
  1489. {
  1490. if (LLNotification::getSelectedOption(notification, response) == 0)
  1491. {
  1492. LLWeb::loadURL(notification["payload"]["help_url"]);
  1493. }
  1494. return false;
  1495. }
  1496. //static
  1497. void LLScriptEditor::onBtnHelp(void* userdata)
  1498. {
  1499. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1500. if (self)
  1501. {
  1502. LLSD payload;
  1503. payload["help_url"] = LSL_DOC_URL;
  1504. gNotifications.add("WebLaunchLSLGuide", LLSD(), payload,
  1505. onHelpWebDialog);
  1506. }
  1507. }
  1508. //static
  1509. void LLScriptEditor::onBtnDynamicHelp(void* userdata)
  1510. {
  1511. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1512. if (!self) return;
  1513. LLFloater* floater = self->mLiveHelpHandle.get();
  1514. if (floater)
  1515. {
  1516. floater->setFocus(true);
  1517. self->updateDynamicHelp(true);
  1518. return;
  1519. }
  1520. floater = new LLFloater("lsl help");
  1521. LLUICtrlFactory::getInstance()->buildFloater(floater,
  1522. "floater_lsl_guide.xml");
  1523. ((LLFloater*)self->getParent())->addDependentFloater(floater);
  1524. floater->childSetCommitCallback("lock_check", onCheckLock, userdata);
  1525. floater->childSetValue("lock_check",
  1526. gSavedSettings.getBool("ScriptHelpFollowsCursor"));
  1527. floater->childSetCommitCallback("history_combo", onHelpComboCommit,
  1528. userdata);
  1529. floater->childSetAction("back_btn", onClickBack, userdata);
  1530. floater->childSetAction("fwd_btn", onClickForward, userdata);
  1531. LLMediaCtrl* browser = floater->getChild<LLMediaCtrl>("lsl_guide_html");
  1532. browser->setAlwaysRefresh(true);
  1533. LLColor3 color = LLColor3(gColors.getColor("LslPreprocessorTextFgColor"));
  1534. LLComboBox* help_combo = floater->getChild<LLComboBox>("history_combo");
  1535. for (LLKeywords::keyword_iterator_t it = self->mEditor->keywordsBegin(),
  1536. end = self->mEditor->keywordsEnd();
  1537. it != end; ++it)
  1538. {
  1539. LLKeywordToken* token = it->second;
  1540. // *HACK: do not register preprocessor directives or macros/defines
  1541. if (token && token->getColor() != color)
  1542. {
  1543. help_combo->add(wstring_to_utf8str(token->getToken()));
  1544. }
  1545. }
  1546. help_combo->sortByName();
  1547. // Re-initialize help variables
  1548. self->mLastHelpToken = NULL;
  1549. self->mLiveHelpHandle = floater->getHandle();
  1550. self->mLiveHelpHistorySize = 0;
  1551. self->updateDynamicHelp(true);
  1552. }
  1553. //static
  1554. void LLScriptEditor::onHelpFollowCursor(void*)
  1555. {
  1556. gSavedSettings.setBool("ScriptHelpFollowsCursor",
  1557. !gSavedSettings.getBool("ScriptHelpFollowsCursor"));
  1558. }
  1559. //static
  1560. void LLScriptEditor::onClickBack(void* userdata)
  1561. {
  1562. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1563. if (!self) return;
  1564. LLFloater* floater = self->mLiveHelpHandle.get();
  1565. if (floater)
  1566. {
  1567. LLMediaCtrl* browserp =
  1568. floater->getChild<LLMediaCtrl>("lsl_guide_html");
  1569. if (browserp)
  1570. {
  1571. browserp->navigateBack();
  1572. }
  1573. }
  1574. }
  1575. //static
  1576. void LLScriptEditor::onClickForward(void* userdata)
  1577. {
  1578. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1579. if (!self) return;
  1580. LLFloater* floater = self->mLiveHelpHandle.get();
  1581. if (floater)
  1582. {
  1583. LLMediaCtrl* browserp =
  1584. floater->getChild<LLMediaCtrl>("lsl_guide_html");
  1585. if (browserp)
  1586. {
  1587. browserp->navigateForward();
  1588. }
  1589. }
  1590. }
  1591. //static
  1592. void LLScriptEditor::onCheckLock(LLUICtrl* ctrl, void* userdata)
  1593. {
  1594. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1595. if (self)
  1596. {
  1597. // Clear out token any time we lock the frame, so we will refresh web
  1598. // page immediately when unlocked
  1599. gSavedSettings.setBool("ScriptHelpFollowsCursor",
  1600. ctrl->getValue().asBoolean());
  1601. self->mLastHelpToken = NULL;
  1602. }
  1603. }
  1604. //static
  1605. void LLScriptEditor::onHelpComboCommit(LLUICtrl* ctrl, void* userdata)
  1606. {
  1607. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1608. LLFloater* floater = self->mLiveHelpHandle.get();
  1609. if (floater)
  1610. {
  1611. std::string help_string = ctrl->getValue().asString();
  1612. self->addHelpItemToHistory(help_string);
  1613. LLMediaCtrl* web_browser =
  1614. floater->getChild<LLMediaCtrl>("lsl_guide_html");
  1615. LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
  1616. url_string.setArg("[LSL_STRING]", help_string);
  1617. web_browser->navigateTo(url_string);
  1618. }
  1619. }
  1620. //static
  1621. void LLScriptEditor::onBtnInsertFunction(LLUICtrl*, void* userdata)
  1622. {
  1623. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1624. if (!self) return;
  1625. // Insert sample code
  1626. if (self->mEditor->getEnabled())
  1627. {
  1628. self->mEditor->insertText(self->mFunctions->getSimple());
  1629. }
  1630. self->mEditor->setFocus(true);
  1631. self->setHelpPage(self->mFunctions->getSimple());
  1632. }
  1633. //static
  1634. bool LLScriptEditor::enableLoadFile(void* userdata)
  1635. {
  1636. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1637. return !HBFileSelector::isInUse() && self && self->mHasScriptData &&
  1638. !self->mIsSaving &&
  1639. self->mTabContainer->getCurrentPanelIndex() == 0;
  1640. }
  1641. //static
  1642. bool LLScriptEditor::enableSaveFile(void* userdata)
  1643. {
  1644. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1645. return !HBFileSelector::isInUse() && self && self->mHasScriptData &&
  1646. !self->mIsSaving;
  1647. }
  1648. //static
  1649. bool LLScriptEditor::enableRaw(void* userdata)
  1650. {
  1651. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1652. return self && self->mHasScriptData && !self->mIsSaving &&
  1653. self->mTabContainer->getCurrentPanelIndex() == 0;
  1654. }
  1655. //static
  1656. bool LLScriptEditor::enableCallback(void* userdata)
  1657. {
  1658. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1659. return self && self->hasChanged() && !self->mIsSaving;
  1660. }
  1661. //static
  1662. void LLScriptEditor::loadFromFileCallback(HBFileSelector::ELoadFilter type,
  1663. std::string& filename,
  1664. void* userdata)
  1665. {
  1666. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1667. if (self && sInstances.count(self))
  1668. {
  1669. self->loadFile(filename);
  1670. }
  1671. else
  1672. {
  1673. gNotifications.add("LoadScriptAborted");
  1674. }
  1675. }
  1676. //static
  1677. void LLScriptEditor::onBtnLoadFromFile(void* userdata)
  1678. {
  1679. HBFileSelector::loadFile(HBFileSelector::FFLOAD_SCRIPT,
  1680. loadFromFileCallback, userdata);
  1681. }
  1682. struct LLSaveToFileData
  1683. {
  1684. LLScriptEditor* instance;
  1685. std::string sources;
  1686. };
  1687. //static
  1688. void LLScriptEditor::saveToFileCallback(HBFileSelector::ESaveFilter type,
  1689. std::string& filename,
  1690. void* userdata)
  1691. {
  1692. LLSaveToFileData* data = (LLSaveToFileData*)userdata;
  1693. if (!data) return; // Paranoia
  1694. LLScriptEditor* self = data->instance;
  1695. if (!self || !sInstances.count(self))
  1696. {
  1697. delete data;
  1698. gNotifications.add("SaveScriptAborted");
  1699. return;
  1700. }
  1701. if (!filename.empty())
  1702. {
  1703. std::string lcname = filename;
  1704. LLStringUtil::toLower(lcname);
  1705. if (lcname.find(".lsl") != lcname.length() - 4 &&
  1706. lcname.find(".txt") != lcname.length() - 4)
  1707. {
  1708. filename += ".lsl";
  1709. }
  1710. llofstream file(filename.c_str());
  1711. if (file.is_open())
  1712. {
  1713. file << data->sources;
  1714. file.close();
  1715. }
  1716. }
  1717. delete data;
  1718. }
  1719. //static
  1720. void LLScriptEditor::onBtnSaveToFile(void* userdata)
  1721. {
  1722. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1723. if (self && sInstances.count(self))
  1724. {
  1725. S32 active_tab = self->mTabContainer->getCurrentPanelIndex();
  1726. LLSaveToFileData* data = new LLSaveToFileData();
  1727. data->instance = self;
  1728. data->sources = active_tab == 0 ? self->mEditor->getText()
  1729. : self->mSavedSources->getText();
  1730. std::string suggestion = self->mScriptName + ".lsl";
  1731. HBFileSelector::saveFile(HBFileSelector::FFSAVE_LSL, suggestion,
  1732. saveToFileCallback, data);
  1733. }
  1734. }
  1735. //static
  1736. void LLScriptEditor::onEditedFileChanged(const std::string& filename,
  1737. void* userdata)
  1738. {
  1739. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1740. if (self && sInstances.count(self))
  1741. {
  1742. if (filename == self->mAutosaveFilename)
  1743. {
  1744. self->loadFile(filename);
  1745. }
  1746. else
  1747. {
  1748. llwarns << "Watched file (" << filename
  1749. << ") and auto-saved file (" << self->mAutosaveFilename
  1750. << ") do not match !" << llendl;
  1751. }
  1752. }
  1753. }
  1754. //static
  1755. void LLScriptEditor::onEditExternal(void* userdata)
  1756. {
  1757. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1758. if (self && sInstances.count(self))
  1759. {
  1760. self->autoSave();
  1761. if (self->mExternalEditor)
  1762. {
  1763. self->mExternalEditor->kill();
  1764. }
  1765. else
  1766. {
  1767. self->mExternalEditor = new HBExternalEditor(onEditedFileChanged,
  1768. self);
  1769. }
  1770. if (!self->mExternalEditor->open(self->mAutosaveFilename))
  1771. {
  1772. self->addComment(self->mExternalEditor->getErrorMessage(), true);
  1773. }
  1774. }
  1775. }
  1776. //static
  1777. void LLScriptEditor::onEditRaw(void* userdata)
  1778. {
  1779. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1780. if (self && sInstances.count(self))
  1781. {
  1782. self->setEditedTextFromSaved();
  1783. }
  1784. }
  1785. //static
  1786. void LLScriptEditor::onBtnSave(void* userdata)
  1787. {
  1788. onFlyoutBtnSave(NULL, userdata);
  1789. }
  1790. //static
  1791. void LLScriptEditor::onFlyoutBtnSave(LLUICtrl* ctrl, void* userdata)
  1792. {
  1793. // Do the save, but do not close afterwards
  1794. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1795. if (!self) return;
  1796. if (ctrl && ctrl->getValue().asString() == "save_include")
  1797. {
  1798. self->mSavedSources->setText(setIncludeSources(self->mEditor->getText()));
  1799. self->doSave(false, false);
  1800. }
  1801. else
  1802. {
  1803. self->doSave(false);
  1804. }
  1805. }
  1806. //static
  1807. void LLScriptEditor::onBtnUndoChanges(void* userdata)
  1808. {
  1809. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1810. if (self && !self->mEditor->tryToRevertToPristineState())
  1811. {
  1812. gNotifications.add("ScriptCannotUndo", LLSD(), LLSD(),
  1813. boost::bind(&LLScriptEditor::handleReloadFromServerDialog,
  1814. self, _1, _2));
  1815. }
  1816. }
  1817. //static
  1818. void LLScriptEditor::onSearchMenu(void* userdata)
  1819. {
  1820. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1821. if (self)
  1822. {
  1823. S32 active_tab = self->mTabContainer->getCurrentPanelIndex();
  1824. LLFloaterSearchReplace::show(active_tab == 0 ? self->mEditor
  1825. : self->mSavedSources);
  1826. }
  1827. }
  1828. //static
  1829. void LLScriptEditor::onUndoMenu(void* userdata)
  1830. {
  1831. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1832. if (self && self->mTabContainer->getCurrentPanelIndex() == 0)
  1833. {
  1834. self->mEditor->undo();
  1835. }
  1836. }
  1837. //static
  1838. void LLScriptEditor::onRedoMenu(void* userdata)
  1839. {
  1840. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1841. if (self && self->mTabContainer->getCurrentPanelIndex() == 0)
  1842. {
  1843. self->mEditor->redo();
  1844. }
  1845. }
  1846. //static
  1847. void LLScriptEditor::onCutMenu(void* userdata)
  1848. {
  1849. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1850. if (self && self->mTabContainer->getCurrentPanelIndex() == 0)
  1851. {
  1852. self->mEditor->cut();
  1853. }
  1854. }
  1855. //static
  1856. void LLScriptEditor::onCopyMenu(void* userdata)
  1857. {
  1858. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1859. if (!self) return;
  1860. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1861. if (currrent_tab == 0)
  1862. {
  1863. self->mEditor->copy();
  1864. }
  1865. else
  1866. {
  1867. self->mSavedSources->copy();
  1868. }
  1869. }
  1870. //static
  1871. void LLScriptEditor::onPasteMenu(void* userdata)
  1872. {
  1873. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1874. if (self && self->mTabContainer->getCurrentPanelIndex() == 0)
  1875. {
  1876. self->mEditor->paste();
  1877. }
  1878. }
  1879. //static
  1880. void LLScriptEditor::onSelectAllMenu(void* userdata)
  1881. {
  1882. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1883. if (!self) return;
  1884. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1885. if (currrent_tab == 0)
  1886. {
  1887. self->mEditor->selectAll();
  1888. }
  1889. else
  1890. {
  1891. self->mSavedSources->selectAll();
  1892. }
  1893. }
  1894. //static
  1895. void LLScriptEditor::onDeselectMenu(void* userdata)
  1896. {
  1897. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1898. if (!self) return;
  1899. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1900. if (currrent_tab == 0)
  1901. {
  1902. self->mEditor->deselect();
  1903. }
  1904. else
  1905. {
  1906. self->mSavedSources->deselect();
  1907. }
  1908. }
  1909. //static
  1910. bool LLScriptEditor::enableUndoMenu(void* userdata)
  1911. {
  1912. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1913. return self && self->mTabContainer->getCurrentPanelIndex() == 0 &&
  1914. self->mEditor->canUndo();
  1915. }
  1916. //static
  1917. bool LLScriptEditor::enableRedoMenu(void* userdata)
  1918. {
  1919. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1920. return self && self->mTabContainer->getCurrentPanelIndex() == 0 &&
  1921. self->mEditor->canRedo();
  1922. }
  1923. //static
  1924. bool LLScriptEditor::enableCutMenu(void* userdata)
  1925. {
  1926. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1927. return self && self->mTabContainer->getCurrentPanelIndex() == 0 &&
  1928. self->mEditor->canCut();
  1929. }
  1930. //static
  1931. bool LLScriptEditor::enableCopyMenu(void* userdata)
  1932. {
  1933. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1934. if (!self) return false;
  1935. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1936. return (currrent_tab == 0 && self->mEditor->canCopy()) ||
  1937. (currrent_tab == 1 && self->mSavedSources->canCopy());
  1938. }
  1939. //static
  1940. bool LLScriptEditor::enablePasteMenu(void* userdata)
  1941. {
  1942. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1943. return self && self->mTabContainer->getCurrentPanelIndex() == 0 &&
  1944. self->mEditor->canPaste();
  1945. }
  1946. //static
  1947. bool LLScriptEditor::enableSelectAllMenu(void* userdata)
  1948. {
  1949. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1950. if (!self) return false;
  1951. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1952. return (currrent_tab == 0 && self->mEditor->canSelectAll()) ||
  1953. (currrent_tab == 1 && self->mSavedSources->canSelectAll());
  1954. }
  1955. //static
  1956. bool LLScriptEditor::enableDeselectMenu(void* userdata)
  1957. {
  1958. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1959. if (!self) return false;
  1960. S32 currrent_tab = self->mTabContainer->getCurrentPanelIndex();
  1961. return (currrent_tab == 0 && self->mEditor->canDeselect()) ||
  1962. (currrent_tab == 1 && self->mSavedSources->canDeselect());
  1963. }
  1964. //static
  1965. bool LLScriptEditor::enableHelp(void* userdata)
  1966. {
  1967. LLScriptEditor* self = (LLScriptEditor*)userdata;
  1968. return self && self->mTabContainer->getCurrentPanelIndex() == 0;
  1969. }
  1970. //static
  1971. void LLScriptEditor::onErrorList(LLUICtrl*, void* user_data)
  1972. {
  1973. LLScriptEditor* self = (LLScriptEditor*)user_data;
  1974. if (!self) return;
  1975. LLScrollListItem* item = self->mErrorList->getFirstSelected();
  1976. if (item)
  1977. {
  1978. // *FIXME: This fucked up little hack is here because we do not have a
  1979. // grep library. This is very brittle code.
  1980. const LLScrollListCell* cell = item->getColumn(0);
  1981. std::string text = cell->getValue().asString();
  1982. text.erase(0, 1);
  1983. LLStringUtil::replaceChar(text, ',',' ');
  1984. LLStringUtil::replaceChar(text, ')',' ');
  1985. S32 row = 0;
  1986. S32 column = 0;
  1987. if (sscanf(text.c_str(), "%d %d", &row, &column) <= 0)
  1988. {
  1989. // Not an error with row/column indicator: abort now.
  1990. return;
  1991. }
  1992. // The row and column do always map to the saved sources.
  1993. self->mSavedSources->setCursor(row, column);
  1994. // Make it obvious to the user despite the lack of a cursor in a
  1995. // disabled text editor
  1996. S32 pos = self->mSavedSources->getCursorPos();
  1997. self->mSavedSources->setSelection(pos, pos + 1);
  1998. // If the sources have been preprocessed, then the compilation error
  1999. // line is likely not the one that was reported in the message and we
  2000. // need to find the corresponding line in the original non-preprocessed
  2001. // source. HBPreprocessor provides this facility.
  2002. // NOTE: the column number might also be invalid, if the line contained
  2003. // a #defined symbol, but we cannot track such changes as easily...
  2004. if (self->mPreprocessor)
  2005. {
  2006. // NOTE: the script editor first line is row 0, while the
  2007. // preprocessor counts from line 1 upwards.
  2008. S32 line = self->mPreprocessor->getOriginalLine(row + 1);
  2009. if (line > 0)
  2010. {
  2011. row = line - 1;
  2012. }
  2013. }
  2014. self->mEditor->setCursor(row, column);
  2015. self->mEditor->setFocus(true);
  2016. }
  2017. }
  2018. //static
  2019. void LLScriptEditor::onMonoCheckboxClicked(LLUICtrl*, void* userdata)
  2020. {
  2021. LLScriptEditor* self = (LLScriptEditor*)userdata;
  2022. if (self)
  2023. {
  2024. self->enableSave(true);
  2025. }
  2026. }
  2027. //static
  2028. void LLScriptEditor::loadFunctions(const std::string& filename)
  2029. {
  2030. std::string filepath = gDirUtil.getFullPath(LL_PATH_APP_SETTINGS,
  2031. filename);
  2032. if (!LLFile::isfile(filepath))
  2033. {
  2034. llwarns << "Failed to load LSL functions table from: " << filename
  2035. << " - File does not exist ! " << llendl;
  2036. return;
  2037. }
  2038. llifstream importer(filepath.c_str());
  2039. if (!importer.is_open())
  2040. {
  2041. llwarns << "Failed to load LSL functions table from: " << filename
  2042. << " - Could not open and read that file ! " << llendl;
  2043. return;
  2044. }
  2045. LLSD function_list;
  2046. LLSDSerialize::fromXMLDocument(function_list, importer);
  2047. importer.close();
  2048. for (LLSD::map_const_iterator it = function_list.beginMap(),
  2049. end = function_list.endMap();
  2050. it != end; ++it)
  2051. {
  2052. sParsedFunctions.emplace_back(it->first,
  2053. it->second["tooltip"].asString(),
  2054. it->second["sleep_time"].asReal(),
  2055. it->second["god_only"].asBoolean());
  2056. }
  2057. llinfos << "Loaded LSL functions table from: " << filename << llendl;
  2058. }
  2059. // ----------------------------------------------------------------------------
  2060. // LLPreviewScript class
  2061. // ----------------------------------------------------------------------------
  2062. // Wrapper method, to avoid having to expose LLScriptEditor class definition
  2063. // in llpreviewscript.h just for a couple of calls in llstartup.cpp...
  2064. void LLPreviewScript::loadFunctions(const std::string& filename)
  2065. {
  2066. LLScriptEditor::loadFunctions(filename);
  2067. }
  2068. // Wrapper method to set the custom font for LLScriptEditor. Called from
  2069. // LLViewerWindow::initFonts() (i.e. after the fonts system has been properly
  2070. // initialized), and from llviewercontrol.cpp on setting change.
  2071. //static
  2072. void LLPreviewScript::refreshCachedSettings()
  2073. {
  2074. std::string font_name = gSavedSettings.getString("ScriptEditorFont");
  2075. if (font_name.empty())
  2076. {
  2077. LLScriptEditor::sCustomFont = NULL;
  2078. }
  2079. else
  2080. {
  2081. LLScriptEditor::sCustomFont = LLFontGL::getFont(font_name.c_str());
  2082. }
  2083. }
  2084. //static
  2085. LLFontGL* LLPreviewScript::getCustomFont()
  2086. {
  2087. return LLScriptEditor::sCustomFont;
  2088. }
  2089. //static
  2090. void* LLPreviewScript::createScriptEdPanel(void* userdata)
  2091. {
  2092. LLPreviewScript* self = (LLPreviewScript*)userdata;
  2093. self->mScriptEd = new LLScriptEditor(self->mItemUUID, onLoad, onSave,
  2094. onSearchReplace, self);
  2095. return self->mScriptEd;
  2096. }
  2097. LLPreviewScript::LLPreviewScript(const std::string& name, const LLRect& rect,
  2098. const std::string& title,
  2099. const LLUUID& item_id)
  2100. : LLPreview(name, rect, title, item_id, LLUUID::null, true,
  2101. SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT)
  2102. {
  2103. LLRect cur_rect = rect;
  2104. LLCallbackMap::map_t factory_map;
  2105. factory_map["script panel"] = LLCallbackMap(createScriptEdPanel, this);
  2106. LLUICtrlFactory::getInstance()->buildFloater(this,
  2107. "floater_script_preview.xml",
  2108. &factory_map);
  2109. const LLInventoryItem* item = getItem();
  2110. childSetCommitCallback("desc", LLPreview::onText, this);
  2111. childSetText("desc", item->getDescription());
  2112. childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
  2113. LLCheckBoxCtrl* mono_check = mScriptEd->getMonoCheckBox();
  2114. bool use_mono = gIsInSecondLife && have_script_upload_cap(LLUUID::null);
  2115. mono_check->setEnabled(use_mono);
  2116. mono_check->set(use_mono);
  2117. if (!getFloaterHost() && !getHost() &&
  2118. getAssetStatus() == PREVIEW_ASSET_UNLOADED)
  2119. {
  2120. loadAsset();
  2121. }
  2122. setTitle(title);
  2123. mScriptEd->setScriptName(title);
  2124. if (!getHost())
  2125. {
  2126. reshape(cur_rect.getWidth(), cur_rect.getHeight());
  2127. setRect(cur_rect);
  2128. }
  2129. }
  2130. LLTextEditor* LLPreviewScript::getEditor()
  2131. {
  2132. return mScriptEd->mEditor;
  2133. }
  2134. void LLPreviewScript::callbackLSLCompileSucceeded()
  2135. {
  2136. llinfos << "LSL byte-code saved" << llendl;
  2137. mScriptEd->addComment(getString("compile_success"));
  2138. mScriptEd->addComment(getString("save_complete"));
  2139. mScriptEd->enableEdit(true);
  2140. closeIfNeeded();
  2141. }
  2142. void LLPreviewScript::callbackLSLCompileFailed(const LLSD& compile_errors)
  2143. {
  2144. llwarns << "Compile failed !" << llendl;
  2145. for (LLSD::array_const_iterator line = compile_errors.beginArray();
  2146. line < compile_errors.endArray(); ++line)
  2147. {
  2148. std::string error_message = line->asString();
  2149. LLStringUtil::stripNonprintable(error_message);
  2150. mScriptEd->addComment(error_message, true);
  2151. }
  2152. mScriptEd->selectFirstError();
  2153. mScriptEd->enableEdit(true);
  2154. closeIfNeeded();
  2155. }
  2156. void LLPreviewScript::loadAsset()
  2157. {
  2158. // *HACK: we poke into inventory to see if it is there, and if so, then it
  2159. // might be part of the inventory library. If it is in the library, then
  2160. // you can see the script, but not modify it.
  2161. const LLInventoryItem* item = gInventory.getItem(mItemUUID);
  2162. if (!item)
  2163. {
  2164. // Do the more generic search.
  2165. getItem();
  2166. }
  2167. if (!item)
  2168. {
  2169. mScriptEd->setScriptText(HELLO_LSL, true);
  2170. mAssetStatus = PREVIEW_ASSET_LOADED;
  2171. return;
  2172. }
  2173. bool is_library =
  2174. !gInventory.isObjectDescendentOf(mItemUUID,
  2175. gInventory.getRootFolderID());
  2176. bool is_copyable = gAgent.allowOperation(PERM_COPY, item->getPermissions(),
  2177. GP_OBJECT_MANIPULATE);
  2178. bool is_modifiable = gAgent.allowOperation(PERM_MODIFY,
  2179. item->getPermissions(),
  2180. GP_OBJECT_MANIPULATE);
  2181. mScriptEd->setScriptName(item->getName());
  2182. if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library)))
  2183. {
  2184. LLUUID* new_uuid = new LLUUID(mItemUUID);
  2185. gAssetStoragep->getInvItemAsset(LLHost(), gAgentID, gAgentSessionID,
  2186. item->getPermissions().getOwner(),
  2187. LLUUID::null, item->getUUID(),
  2188. item->getAssetUUID(), item->getType(),
  2189. onLoadComplete, (void*)new_uuid, true);
  2190. mAssetStatus = PREVIEW_ASSET_LOADING;
  2191. }
  2192. else
  2193. {
  2194. mScriptEd->setScriptText(mScriptEd->getString("can_not_view"), false);
  2195. mScriptEd->mEditor->makePristine();
  2196. mScriptEd->mEditor->setEnabled(false);
  2197. mScriptEd->mFunctions->setEnabled(false);
  2198. mAssetStatus = PREVIEW_ASSET_LOADED;
  2199. }
  2200. childSetVisible("lock", !is_modifiable);
  2201. mScriptEd->mFunctions->setEnabled(is_modifiable);
  2202. }
  2203. bool LLPreviewScript::canClose()
  2204. {
  2205. return mScriptEd->canClose();
  2206. }
  2207. void LLPreviewScript::closeIfNeeded()
  2208. {
  2209. // Find our window and close it if requested.
  2210. gWindowp->decBusyCount();
  2211. if (mCloseAfterSave)
  2212. {
  2213. if (!mScriptEd->mAutosaveFilename.empty())
  2214. {
  2215. llinfos << "Remove autosave: " << mScriptEd->mAutosaveFilename
  2216. << llendl;
  2217. LLFile::remove(mScriptEd->mAutosaveFilename);
  2218. }
  2219. close();
  2220. }
  2221. }
  2222. // Overrides the LLPreview open which attempts to load asset (since we did it
  2223. // already)
  2224. void LLPreviewScript::open()
  2225. {
  2226. LLFloater::open();
  2227. }
  2228. //static
  2229. void LLPreviewScript::onSearchReplace(void* userdata)
  2230. {
  2231. LLPreviewScript* self = (LLPreviewScript*)userdata;
  2232. if (!self) return;
  2233. LLScriptEditor* sed = self->mScriptEd;
  2234. if (sed)
  2235. {
  2236. LLFloaterSearchReplace::show(sed->mEditor);
  2237. }
  2238. }
  2239. //static
  2240. void LLPreviewScript::onLoad(void* userdata)
  2241. {
  2242. LLPreviewScript* self = (LLPreviewScript*)userdata;
  2243. if (self)
  2244. {
  2245. self->loadAsset();
  2246. }
  2247. }
  2248. //static
  2249. void LLPreviewScript::onSave(void* userdata, bool close_after_save)
  2250. {
  2251. LLPreviewScript* self = (LLPreviewScript*)userdata;
  2252. if (self)
  2253. {
  2254. self->mCloseAfterSave = close_after_save;
  2255. self->saveIfNeeded();
  2256. }
  2257. }
  2258. //static
  2259. void LLPreviewScript::finishLSLUpload(LLUUID item_id, LLSD response)
  2260. {
  2261. // Find our window and close it if requested.
  2262. LLPreviewScript* self = LLPreviewScript::getInstance(item_id);
  2263. if (self)
  2264. {
  2265. // Bytecode save completed
  2266. if (response.has("compiled") && response["compiled"])
  2267. {
  2268. self->callbackLSLCompileSucceeded();
  2269. }
  2270. else
  2271. {
  2272. self->callbackLSLCompileFailed(response["errors"]);
  2273. }
  2274. }
  2275. }
  2276. //static
  2277. void LLPreviewScript::failedLSLUpload(LLUUID item_id, std::string reason)
  2278. {
  2279. // Find our window and close it if requested.
  2280. LLPreviewScript* self = LLPreviewScript::getInstance(item_id);
  2281. if (self)
  2282. {
  2283. LLSD errors;
  2284. errors.append(LLTrans::getString("AssetUploadFailed") + reason);
  2285. self->callbackLSLCompileFailed(errors);
  2286. }
  2287. }
  2288. // Save needs to compile the text in the buffer. If the compile succeeds, then
  2289. // save both assets out to the database. If the compile fails, go ahead and
  2290. // save the text anyway so that the user does not get too fucked.
  2291. void LLPreviewScript::saveIfNeeded()
  2292. {
  2293. if (!mScriptEd->hasChanged())
  2294. {
  2295. return;
  2296. }
  2297. const LLInventoryItem* inv_item = getItem();
  2298. if (!inv_item)
  2299. {
  2300. llwarns << "Missing inventory item: " << mItemUUID << llendl;
  2301. return;
  2302. }
  2303. // Save it out to asset server
  2304. const std::string& url = gAgent.getRegionCapability("UpdateScriptAgent");
  2305. if (url.empty())
  2306. {
  2307. LLSD args;
  2308. args["REASON"] = "missing UpdateScriptAgent capability";
  2309. gNotifications.add("SaveScriptFailReason", args);
  2310. return;
  2311. }
  2312. std::string buffer = mScriptEd->mSavedSources->getText();
  2313. if (buffer.empty())
  2314. {
  2315. llwarns << "Empty or invalid script sources." << llendl;
  2316. return;
  2317. }
  2318. mScriptEd->mEditor->makePristine();
  2319. mScriptEd->enableEdit(false);
  2320. mScriptEd->enableSave(false);
  2321. gWindowp->incBusyCount();
  2322. LLBufferedAssetUploadInfo::inv_uploaded_cb_t proc_ok =
  2323. boost::bind(&LLPreviewScript::finishLSLUpload, _1, _4);
  2324. LLBufferedAssetUploadInfo::failed_cb_t proc_ko =
  2325. boost::bind(&LLPreviewScript::failedLSLUpload, _1, _4);
  2326. bool mono_checked = mScriptEd->monoChecked();
  2327. LLScriptAssetUpload::TargetType_t type;
  2328. if (!gIsInSecondLife || mono_checked)
  2329. {
  2330. type = LLScriptAssetUpload::MONO;
  2331. }
  2332. else
  2333. {
  2334. type = LLScriptAssetUpload::LSL2;
  2335. }
  2336. LLResourceUploadInfo::ptr_t info(new LLScriptAssetUpload(mItemUUID, buffer,
  2337. type, proc_ok,
  2338. proc_ko));
  2339. LLViewerAssetUpload::enqueueInventoryUpload(url, info);
  2340. }
  2341. //static
  2342. void LLPreviewScript::onLoadComplete(const LLUUID& asset_id,
  2343. LLAssetType::EType type, void* user_data,
  2344. S32 status, LLExtStat)
  2345. {
  2346. LL_DEBUGS("ScriptEditor") << "Got uuid " << asset_id << LL_ENDL;
  2347. LLUUID* item_uuid = (LLUUID*)user_data;
  2348. LLPreviewScript* preview = LLPreviewScript::getInstance(*item_uuid);
  2349. if (!preview)
  2350. {
  2351. delete item_uuid;
  2352. return;
  2353. }
  2354. if (status == 0)
  2355. {
  2356. // Get the script text
  2357. LLFileSystem file(asset_id);
  2358. S32 file_length = file.getSize();
  2359. char* buffer = new char[file_length + 1];
  2360. file.read((U8*)buffer, file_length);
  2361. // Put a EOS at the end
  2362. buffer[file_length] = 0;
  2363. preview->mScriptEd->setScriptText(std::string(&buffer[0]), true);
  2364. preview->mScriptEd->mEditor->makePristine();
  2365. delete[] buffer;
  2366. bool is_modifiable = false;
  2367. LLInventoryItem* item = gInventory.getItem(*item_uuid);
  2368. if (item)
  2369. {
  2370. if (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
  2371. GP_OBJECT_MANIPULATE))
  2372. {
  2373. is_modifiable = true;
  2374. }
  2375. }
  2376. preview->mScriptEd->mEditor->setEnabled(is_modifiable);
  2377. preview->mAssetStatus = PREVIEW_ASSET_LOADED;
  2378. }
  2379. else
  2380. {
  2381. gViewerStats.incStat(LLViewerStats::ST_DOWNLOAD_FAILED);
  2382. if (status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE ||
  2383. status == LL_ERR_FILE_EMPTY)
  2384. {
  2385. gNotifications.add("ScriptMissing");
  2386. }
  2387. else if (status == LL_ERR_INSUFFICIENT_PERMISSIONS)
  2388. {
  2389. gNotifications.add("ScriptNoPermissions");
  2390. }
  2391. else
  2392. {
  2393. gNotifications.add("UnableToLoadScript");
  2394. }
  2395. preview->mAssetStatus = PREVIEW_ASSET_ERROR;
  2396. llwarns << "Problem loading script " << *item_uuid << ": status = "
  2397. << status << llendl;
  2398. }
  2399. delete item_uuid;
  2400. }
  2401. //static
  2402. LLPreviewScript* LLPreviewScript::getInstance(const LLUUID& item_uuid)
  2403. {
  2404. LLPreview* instance = NULL;
  2405. preview_map_t::iterator it = LLPreview::sInstances.find(item_uuid);
  2406. if (it != LLPreview::sInstances.end())
  2407. {
  2408. instance = it->second;
  2409. }
  2410. return (LLPreviewScript*)instance;
  2411. }
  2412. void LLPreviewScript::reshape(S32 width, S32 height, bool called_from_parent)
  2413. {
  2414. LLPreview::reshape(width, height, called_from_parent);
  2415. if (!isMinimized())
  2416. {
  2417. // So that next time you open a script it will have the same height and
  2418. // width (although not the same position).
  2419. gSavedSettings.setRect("PreviewScriptRect", getRect());
  2420. }
  2421. }
  2422. // ----------------------------------------------------------------------------
  2423. // LLLiveLSLEditor class
  2424. // ----------------------------------------------------------------------------
  2425. LLLiveLSLEditor::instances_map_t LLLiveLSLEditor::sInstances;
  2426. //static
  2427. LLLiveLSLEditor* LLLiveLSLEditor::show(const LLUUID& script_id,
  2428. const LLUUID& object_id)
  2429. {
  2430. LLLiveLSLEditor* self = NULL;
  2431. LLUUID xored_id = script_id ^ object_id;
  2432. instances_map_t::iterator it = sInstances.find(xored_id);
  2433. if (it != sInstances.end())
  2434. {
  2435. // Move the existing view to the front
  2436. self = it->second;
  2437. self->open();
  2438. }
  2439. return self;
  2440. }
  2441. //static
  2442. void LLLiveLSLEditor::hide(const LLUUID& script_id, const LLUUID& object_id)
  2443. {
  2444. LLUUID xored_id = script_id ^ object_id;
  2445. instances_map_t::iterator it = sInstances.find(xored_id);
  2446. if (it != sInstances.end())
  2447. {
  2448. LLLiveLSLEditor* self = it->second;
  2449. if (self->getParent())
  2450. {
  2451. self->getParent()->removeChild(self);
  2452. }
  2453. delete self;
  2454. }
  2455. }
  2456. //static
  2457. LLLiveLSLEditor* LLLiveLSLEditor::find(const LLUUID& script_id,
  2458. const LLUUID& object_id)
  2459. {
  2460. LLUUID xored_id = script_id ^ object_id;
  2461. return get_ptr_in_map(sInstances, xored_id);
  2462. }
  2463. //static
  2464. void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
  2465. {
  2466. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  2467. self->mScriptEd = new LLScriptEditor(self->mItemUUID, onLoad, onSave,
  2468. onSearchReplace, self);
  2469. return self->mScriptEd;
  2470. }
  2471. LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name, const LLRect& rect,
  2472. const std::string& title,
  2473. const LLUUID& obj_id, const LLUUID& item_id)
  2474. : LLPreview(name, rect, title, item_id, obj_id, true, SCRIPT_MIN_WIDTH,
  2475. SCRIPT_MIN_HEIGHT),
  2476. mObjectID(obj_id),
  2477. mItemID(item_id),
  2478. mScriptEd(NULL),
  2479. mAskedForRunningInfo(false),
  2480. mHaveRunningInfo(false),
  2481. mCloseAfterSave(false),
  2482. mIsModifiable(false),
  2483. mIsSaving(false)
  2484. {
  2485. bool is_new = false;
  2486. if (mItemID.isNull())
  2487. {
  2488. mItemID.generate();
  2489. is_new = true;
  2490. }
  2491. LLLiveLSLEditor::sInstances[mItemID ^ mObjectID] = this;
  2492. LLCallbackMap::map_t factory_map;
  2493. factory_map["script ed panel"] = LLCallbackMap(createScriptEdPanel, this);
  2494. LLUICtrlFactory::getInstance()->buildFloater(this,
  2495. "floater_live_lsleditor.xml",
  2496. &factory_map);
  2497. mRunningCheckbox = getChild<LLCheckBoxCtrl>("running");
  2498. mRunningCheckbox->setCommitCallback(onRunningCheckboxClicked);
  2499. mRunningCheckbox->setCallbackUserData(this);
  2500. mRunningCheckbox->setEnabled(false);
  2501. childSetAction("Reset", onReset, this);
  2502. childSetEnabled("Reset", true);
  2503. mScriptEd->mEditor->makePristine();
  2504. loadAsset(is_new);
  2505. mScriptEd->mEditor->setFocus(true);
  2506. if (!getHost())
  2507. {
  2508. LLRect cur_rect = getRect();
  2509. translate(rect.mLeft - cur_rect.mLeft, rect.mTop - cur_rect.mTop);
  2510. }
  2511. setTitle(title);
  2512. mScriptEd->setScriptName(title);
  2513. mScriptRunningText = getString("script_running");
  2514. mCannotRunText = getString("public_objects_can_not_run");
  2515. mOutOfRange = getString("out_of_range");
  2516. mExperiences = getChild<LLComboBox>("Experiences...");
  2517. mExperiences->setCommitCallback(experienceChanged);
  2518. mExperiences->setCallbackUserData(this);
  2519. mExperiences->setVisible(false);
  2520. mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp");
  2521. mExperienceEnabled->set(false);
  2522. mExperienceEnabled->setCommitCallback(onToggleExperience);
  2523. mExperienceEnabled->setCallbackUserData(this);
  2524. mExperienceEnabled->setEnabled(false);
  2525. mViewProfileButton = getChild<LLButton>("view_profile");
  2526. mViewProfileButton->setClickedCallback(onViewProfile, this);
  2527. mViewProfileButton->setVisible(false);
  2528. }
  2529. LLLiveLSLEditor::~LLLiveLSLEditor()
  2530. {
  2531. LLLiveLSLEditor::sInstances.erase(mItemID ^ mObjectID);
  2532. }
  2533. //virtual
  2534. void LLLiveLSLEditor::open()
  2535. {
  2536. LLFloater::open();
  2537. }
  2538. //virtual
  2539. bool LLLiveLSLEditor::canClose()
  2540. {
  2541. return mScriptEd->canClose();
  2542. }
  2543. //virtual
  2544. void LLLiveLSLEditor::draw()
  2545. {
  2546. LLViewerObject* object = gObjectList.findObject(mObjectID);
  2547. if (object && mAskedForRunningInfo && mHaveRunningInfo)
  2548. {
  2549. if (object->permAnyOwner())
  2550. {
  2551. mRunningCheckbox->setLabel(mScriptRunningText);
  2552. mRunningCheckbox->setEnabled(!mIsSaving);
  2553. if (object->permAnyOwner())
  2554. {
  2555. mRunningCheckbox->setLabel(mScriptRunningText);
  2556. mRunningCheckbox->setEnabled(!mIsSaving);
  2557. }
  2558. else
  2559. {
  2560. mRunningCheckbox->setLabel(mCannotRunText);
  2561. mRunningCheckbox->setEnabled(false);
  2562. // *FIX: Set it to false so that the UI is correct for a box
  2563. // that is released to public. It could be incorrect after a
  2564. // release/claim cycle, but will be correct after clicking on
  2565. // it.
  2566. mRunningCheckbox->set(false);
  2567. if (mScriptEd)
  2568. {
  2569. mScriptEd->getMonoCheckBox()->set(false);
  2570. }
  2571. }
  2572. }
  2573. else
  2574. {
  2575. mRunningCheckbox->setLabel(mCannotRunText);
  2576. mRunningCheckbox->setEnabled(false);
  2577. // *FIX: Set it to false so that the UI is correct for a box that
  2578. // is released to public. It could be incorrect after a release/
  2579. // claim cycle, but will be correct after clicking on it.
  2580. mRunningCheckbox->set(false);
  2581. if (mScriptEd)
  2582. {
  2583. mScriptEd->getMonoCheckBox()->setEnabled(false);
  2584. }
  2585. // object may have fallen out of range.
  2586. mHaveRunningInfo = false;
  2587. }
  2588. }
  2589. else if (!object)
  2590. {
  2591. setTitle(mOutOfRange);
  2592. mRunningCheckbox->setEnabled(false);
  2593. // Object may have fallen out of range.
  2594. mHaveRunningInfo = false;
  2595. }
  2596. LLFloater::draw();
  2597. }
  2598. //virtual
  2599. void LLLiveLSLEditor::reshape(S32 width, S32 height, bool called_from_parent)
  2600. {
  2601. LLFloater::reshape(width, height, called_from_parent);
  2602. if (!isMinimized())
  2603. {
  2604. // So that next time you open a script it will have the same height
  2605. // and width (although not the same position).
  2606. gSavedSettings.setRect("PreviewScriptRect", getRect());
  2607. }
  2608. }
  2609. void LLLiveLSLEditor::closeIfNeeded()
  2610. {
  2611. gWindowp->decBusyCount();
  2612. if (mCloseAfterSave)
  2613. {
  2614. if (!mScriptEd->mAutosaveFilename.empty())
  2615. {
  2616. llinfos << "Remove autosave: " << mScriptEd->mAutosaveFilename
  2617. << llendl;
  2618. LLFile::remove(mScriptEd->mAutosaveFilename);
  2619. }
  2620. close();
  2621. }
  2622. }
  2623. void LLLiveLSLEditor::saveIfNeeded()
  2624. {
  2625. LLViewerObject* object = gObjectList.findObject(mObjectID);
  2626. if (!object)
  2627. {
  2628. gNotifications.add("SaveScriptFailObjectNotFound");
  2629. return;
  2630. }
  2631. if (mItem.isNull() || !mItem->isFinished())
  2632. {
  2633. // NOTE: While the error message may not be exactly correct, it is
  2634. // pretty close.
  2635. gNotifications.add("SaveScriptFailObjectNotFound");
  2636. return;
  2637. }
  2638. // Get the latest info about it. We used to be losing the script name on
  2639. // save, because the viewer object version of the item, and the editor
  2640. // version would get out of sync. Here's a good place to sync them back
  2641. // up. *HACK: we "know" that mItemID refers to a LLInventoryItem...
  2642. LLInventoryItem* inv_item =
  2643. (LLInventoryItem*)object->getInventoryObject(mItemID);
  2644. if (inv_item)
  2645. {
  2646. mItem->copyItem(inv_item);
  2647. }
  2648. // Do not need to save if we are pristine
  2649. if (!mScriptEd->hasChanged())
  2650. {
  2651. return;
  2652. }
  2653. LLViewerRegion* regionp = object->getRegion();
  2654. if (!regionp)
  2655. {
  2656. LLSD args;
  2657. args["REASON"] = "cannot determine object region";
  2658. gNotifications.add("SaveScriptFailReason", args);
  2659. return;
  2660. }
  2661. const std::string& url = regionp->getCapability("UpdateScriptTask");
  2662. if (url.empty())
  2663. {
  2664. LLSD args;
  2665. args["REASON"] = "missing UpdateScriptTask capability";
  2666. gNotifications.add("SaveScriptFailReason", args);
  2667. return;
  2668. }
  2669. std::string buffer = mScriptEd->mSavedSources->getText();
  2670. if (buffer.empty())
  2671. {
  2672. llwarns << "Empty or invalid script sources." << llendl;
  2673. return;
  2674. }
  2675. // Save the script to asset server
  2676. mScriptEd->mEditor->makePristine();
  2677. mScriptEd->enableEdit(false);
  2678. mScriptEd->enableSave(false);
  2679. gWindowp->incBusyCount();
  2680. mIsSaving = true;
  2681. bool is_running = getChild<LLCheckBoxCtrl>("running")->get();
  2682. LLBufferedAssetUploadInfo::task_uploaded_cb_t proc_ok =
  2683. boost::bind(&LLLiveLSLEditor::finishLSLUpload, _1, _2, _3, _4,
  2684. is_running);
  2685. LLBufferedAssetUploadInfo::failed_cb_t proc_ko =
  2686. boost::bind(&LLLiveLSLEditor::failedLSLUpload, _1, _2, _4);
  2687. bool mono_checked = mScriptEd->monoChecked();
  2688. LLScriptAssetUpload::TargetType_t type;
  2689. if (!gIsInSecondLife || mono_checked)
  2690. {
  2691. type = LLScriptAssetUpload::MONO;
  2692. }
  2693. else
  2694. {
  2695. type = LLScriptAssetUpload::LSL2;
  2696. }
  2697. LLResourceUploadInfo::ptr_t
  2698. info(new LLScriptAssetUpload(mObjectUUID, mItemUUID, type,
  2699. is_running, mScriptEd->getAssociatedExperience(), buffer, proc_ok,
  2700. proc_ko));
  2701. LLViewerAssetUpload::enqueueInventoryUpload(url, info);
  2702. }
  2703. void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id,
  2704. const LLUUID& item_id,
  2705. bool is_script_running)
  2706. {
  2707. LL_DEBUGS("ScriptEditor") << "LSL Bytecode saved" << LL_ENDL;
  2708. mScriptEd->addComment(getString("compile_success"));
  2709. mScriptEd->addComment(getString("save_complete"));
  2710. mScriptEd->enableEdit(true);
  2711. mIsSaving = false;
  2712. closeIfNeeded();
  2713. }
  2714. void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors)
  2715. {
  2716. llwarns << "Compile failed !" << llendl;
  2717. for (LLSD::array_const_iterator line = compile_errors.beginArray();
  2718. line < compile_errors.endArray(); ++line)
  2719. {
  2720. std::string error_message = line->asString();
  2721. LLStringUtil::stripNonprintable(error_message);
  2722. mScriptEd->addComment(error_message, true);
  2723. }
  2724. mScriptEd->selectFirstError();
  2725. mScriptEd->enableEdit(true);
  2726. mIsSaving = false;
  2727. closeIfNeeded();
  2728. }
  2729. void LLLiveLSLEditor::loadAsset(bool is_new)
  2730. {
  2731. if (is_new)
  2732. {
  2733. mScriptEd->setScriptText(HELLO_LSL, true);
  2734. mScriptEd->enableSave(false);
  2735. LLPermissions perm;
  2736. perm.init(gAgentID, gAgentID, LLUUID::null, gAgent.getGroupID());
  2737. perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE,
  2738. PERM_MOVE | PERM_TRANSFER);
  2739. mItem = new LLViewerInventoryItem(mItemID, mObjectID, perm,
  2740. LLUUID::null,
  2741. LLAssetType::AT_LSL_TEXT,
  2742. LLInventoryType::IT_LSL,
  2743. DEFAULT_SCRIPT_NAME,
  2744. LLStringUtil::null,
  2745. LLSaleInfo::DEFAULT,
  2746. LLInventoryItem::II_FLAGS_NONE,
  2747. time_corrected());
  2748. mAssetStatus = PREVIEW_ASSET_LOADED;
  2749. requestExperiences();
  2750. return;
  2751. }
  2752. LLViewerObject* object = gObjectList.findObject(mObjectID);
  2753. if (!object)
  2754. {
  2755. llwarns << "Cannot find object " << mObjectID
  2756. << " in the viewer object list. Aborted." << llendl;
  2757. return;
  2758. }
  2759. // HACK ! We "know" that mItemID refers to a LLViewerInventoryItem
  2760. LLViewerInventoryItem* item =
  2761. (LLViewerInventoryItem*)object->getInventoryObject(mItemID);
  2762. if (item)
  2763. {
  2764. LLViewerRegion* regionp = object->getRegion();
  2765. const std::string& url =
  2766. regionp ? regionp->getCapability("GetMetadata")
  2767. : gAgent.getRegionCapability("GetMetadata");
  2768. LLExperienceCache* ecache = LLExperienceCache::getInstance();
  2769. ecache->fetchAssociatedExperience(item->getParentUUID(),
  2770. item->getUUID(), url,
  2771. boost::bind(&LLLiveLSLEditor::setAssociatedExperience,
  2772. getDerivedHandle<LLLiveLSLEditor>(),
  2773. _1));
  2774. bool god_like = gAgent.isGodlike();
  2775. bool is_copyable = gAgent.allowOperation(PERM_COPY,
  2776. item->getPermissions(),
  2777. GP_OBJECT_MANIPULATE);
  2778. mIsModifiable = gAgent.allowOperation(PERM_MODIFY,
  2779. item->getPermissions(),
  2780. GP_OBJECT_MANIPULATE);
  2781. if (!god_like && (!is_copyable || !mIsModifiable))
  2782. {
  2783. mItem = new LLViewerInventoryItem();
  2784. mScriptEd->setScriptText(LLStringUtil::null, false);
  2785. mScriptEd->mEditor->makePristine();
  2786. mScriptEd->mEditor->setEnabled(false);
  2787. mAssetStatus = PREVIEW_ASSET_LOADED;
  2788. }
  2789. else if (is_copyable || god_like)
  2790. {
  2791. mItem = new LLViewerInventoryItem(item);
  2792. // Request the text from the object
  2793. LLUUID* user_data = new LLUUID(mItemID ^ mObjectID);
  2794. gAssetStoragep->getInvItemAsset(object->getRegion()->getHost(),
  2795. gAgentID, gAgentSessionID,
  2796. item->getPermissions().getOwner(),
  2797. object->getID(),
  2798. item->getUUID(),
  2799. item->getAssetUUID(),
  2800. item->getType(),
  2801. onLoadComplete,
  2802. (void*)user_data, true);
  2803. LLMessageSystem* msg = gMessageSystemp;
  2804. msg->newMessageFast(_PREHASH_GetScriptRunning);
  2805. msg->nextBlockFast(_PREHASH_Script);
  2806. msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
  2807. msg->addUUIDFast(_PREHASH_ItemID, mItemID);
  2808. msg->sendReliable(object->getRegion()->getHost());
  2809. mAskedForRunningInfo = true;
  2810. mAssetStatus = PREVIEW_ASSET_LOADING;
  2811. }
  2812. }
  2813. if (mItem.isNull())
  2814. {
  2815. mScriptEd->setScriptText(LLStringUtil::null, false);
  2816. mScriptEd->mEditor->makePristine();
  2817. mAssetStatus = PREVIEW_ASSET_LOADED;
  2818. mIsModifiable = false;
  2819. }
  2820. requestExperiences();
  2821. }
  2822. void LLLiveLSLEditor::loadScriptText(const LLUUID& uuid,
  2823. LLAssetType::EType type)
  2824. {
  2825. LLFileSystem file(uuid);
  2826. S32 file_length = file.getSize();
  2827. char* buffer = new char[file_length + 1];
  2828. file.read((U8*)buffer, file_length);
  2829. if (file.getLastBytesRead() != file_length || file_length <= 0)
  2830. {
  2831. llwarns << "Error reading " << uuid << ":" << type << llendl;
  2832. }
  2833. buffer[file_length] = '\0';
  2834. mScriptEd->setScriptText(std::string(&buffer[0]), true);
  2835. mScriptEd->mEditor->makePristine();
  2836. delete[] buffer;
  2837. const LLInventoryItem* item = getItem();
  2838. if (item)
  2839. {
  2840. mScriptEd->setScriptName(item->getName());
  2841. }
  2842. }
  2843. void LLLiveLSLEditor::setExperienceIds(const LLSD& experience_ids)
  2844. {
  2845. mExperienceIds = experience_ids;
  2846. updateExperienceControls();
  2847. }
  2848. void LLLiveLSLEditor::updateExperienceControls()
  2849. {
  2850. if (mScriptEd->getAssociatedExperience().isNull())
  2851. {
  2852. mExperienceEnabled->set(false);
  2853. mExperiences->setVisible(false);
  2854. if (mExperienceIds.size() > 0)
  2855. {
  2856. mExperienceEnabled->setEnabled(true);
  2857. mExperienceEnabled->setToolTip(getString("add_experiences"));
  2858. }
  2859. else
  2860. {
  2861. mExperienceEnabled->setEnabled(false);
  2862. mExperienceEnabled->setToolTip(getString("no_experiences"));
  2863. }
  2864. mViewProfileButton->setVisible(false);
  2865. }
  2866. else
  2867. {
  2868. mExperienceEnabled->setToolTip(getString("experience_enabled"));
  2869. mExperienceEnabled->setEnabled(getIsModifiable());
  2870. mExperiences->setVisible(true);
  2871. mExperienceEnabled->set(true);
  2872. buildExperienceList();
  2873. }
  2874. }
  2875. void LLLiveLSLEditor::buildExperienceList()
  2876. {
  2877. mExperiences->clearRows();
  2878. bool found = false;
  2879. const LLUUID& associated = mScriptEd->getAssociatedExperience();
  2880. LLUUID last;
  2881. std::string name;
  2882. LLScrollListItem* item;
  2883. LLExperienceCache* expcache = LLExperienceCache::getInstance();
  2884. for (LLSD::array_const_iterator it = mExperienceIds.beginArray(),
  2885. end = mExperienceIds.endArray();
  2886. it != end; ++it)
  2887. {
  2888. LLUUID id = it->asUUID();
  2889. EAddPosition position = ADD_BOTTOM;
  2890. if (id == associated)
  2891. {
  2892. found = true;
  2893. position = ADD_TOP;
  2894. }
  2895. const LLSD& experience = expcache->get(id);
  2896. if (experience.isUndefined())
  2897. {
  2898. mExperiences->add(getString("loading"), id, position);
  2899. last = id;
  2900. }
  2901. else
  2902. {
  2903. name = experience[LLExperienceCache::NAME].asString();
  2904. if (name.empty())
  2905. {
  2906. name = LLTrans::getString("ExperienceNameUntitled");
  2907. }
  2908. mExperiences->add(name, id, position);
  2909. }
  2910. }
  2911. if (!found)
  2912. {
  2913. const LLSD& experience = expcache->get(associated);
  2914. if (experience.isDefined())
  2915. {
  2916. name = experience[LLExperienceCache::NAME].asString();
  2917. if (name.empty())
  2918. {
  2919. name = LLTrans::getString("ExperienceNameUntitled");
  2920. }
  2921. item = mExperiences->add(name, associated, ADD_TOP);
  2922. }
  2923. else
  2924. {
  2925. item = mExperiences->add(getString("loading"), associated,
  2926. ADD_TOP);
  2927. last = associated;
  2928. }
  2929. item->setEnabled(false);
  2930. }
  2931. if (last.notNull())
  2932. {
  2933. mExperiences->setEnabled(false);
  2934. expcache->get(last,
  2935. boost::bind(&LLLiveLSLEditor::buildExperienceList,
  2936. this));
  2937. }
  2938. else
  2939. {
  2940. mExperiences->setEnabled(true);
  2941. mExperiences->sortByName(true);
  2942. mExperiences->setCurrentByIndex(mExperiences->getCurrentIndex());
  2943. mViewProfileButton->setVisible(true);
  2944. }
  2945. }
  2946. void LLLiveLSLEditor::requestExperiences()
  2947. {
  2948. if (!getIsModifiable())
  2949. {
  2950. return;
  2951. }
  2952. const std::string& url =
  2953. gAgent.getRegionCapability("GetCreatorExperiences");
  2954. if (url.empty())
  2955. {
  2956. return;
  2957. }
  2958. LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t succ =
  2959. boost::bind(&LLLiveLSLEditor::receiveExperienceIds, _1,
  2960. getDerivedHandle<LLLiveLSLEditor>());
  2961. LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(url, succ);
  2962. }
  2963. //static
  2964. void LLLiveLSLEditor::receiveExperienceIds(LLSD result,
  2965. LLHandle<LLLiveLSLEditor> hparent)
  2966. {
  2967. LLLiveLSLEditor* parent = hparent.get();
  2968. if (parent)
  2969. {
  2970. parent->setExperienceIds(result["experience_ids"]);
  2971. }
  2972. }
  2973. //static
  2974. void LLLiveLSLEditor::experienceChanged(LLUICtrl*, void* data)
  2975. {
  2976. LLLiveLSLEditor* self = (LLLiveLSLEditor*)data;
  2977. if (!self) return;
  2978. LLScriptEditor* sed = self->mScriptEd;
  2979. if (sed->getAssociatedExperience() !=
  2980. self->mExperiences->getSelectedValue().asUUID())
  2981. {
  2982. sed->enableSave(self->getIsModifiable());
  2983. sed->setAssociatedExperience(self->mExperiences->getSelectedValue().asUUID());
  2984. self->updateExperienceControls();
  2985. }
  2986. }
  2987. //static
  2988. void LLLiveLSLEditor::setAssociatedExperience(LLHandle<LLLiveLSLEditor> editor,
  2989. const LLSD& experience)
  2990. {
  2991. LLLiveLSLEditor* self = editor.get();
  2992. if (self)
  2993. {
  2994. LLUUID id;
  2995. if (experience.has(LLExperienceCache::EXPERIENCE_ID))
  2996. {
  2997. id = experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
  2998. }
  2999. self->mScriptEd->setAssociatedExperience(id);
  3000. self->updateExperienceControls();
  3001. }
  3002. }
  3003. //static
  3004. void LLLiveLSLEditor::onToggleExperience(LLUICtrl*, void* userdata)
  3005. {
  3006. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3007. if (!self) return;
  3008. LLUUID id;
  3009. if (self->mExperienceEnabled->get() &&
  3010. self->mScriptEd->getAssociatedExperience().isNull() &&
  3011. self->mExperienceIds.size() > 0)
  3012. {
  3013. id = self->mExperienceIds.beginArray()->asUUID();
  3014. }
  3015. if (id != self->mScriptEd->getAssociatedExperience())
  3016. {
  3017. self->mScriptEd->enableSave(self->getIsModifiable());
  3018. }
  3019. self->mScriptEd->setAssociatedExperience(id);
  3020. self->updateExperienceControls();
  3021. }
  3022. //static
  3023. void LLLiveLSLEditor::onRunningCheckboxClicked(LLUICtrl*, void* userdata)
  3024. {
  3025. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3026. if (!self) return;
  3027. LLViewerObject* object = gObjectList.findObject(self->mObjectID);
  3028. bool running = self->mRunningCheckbox->get();
  3029. //MK
  3030. if (gRLenabled && !gRLInterface.canDetach(object))
  3031. {
  3032. self->mRunningCheckbox->set(!running);
  3033. return;
  3034. }
  3035. //mk
  3036. if (object)
  3037. {
  3038. LLMessageSystem* msg = gMessageSystemp;
  3039. msg->newMessageFast(_PREHASH_SetScriptRunning);
  3040. msg->nextBlockFast(_PREHASH_AgentData);
  3041. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  3042. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  3043. msg->nextBlockFast(_PREHASH_Script);
  3044. msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
  3045. msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
  3046. msg->addBoolFast(_PREHASH_Running, running);
  3047. msg->sendReliable(object->getRegion()->getHost());
  3048. }
  3049. else
  3050. {
  3051. self->mRunningCheckbox->set(!running);
  3052. gNotifications.add("CouldNotStartStopScript");
  3053. }
  3054. }
  3055. //static
  3056. void LLLiveLSLEditor::onReset(void* userdata)
  3057. {
  3058. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3059. if (!self) return;
  3060. LLViewerObject* object = gObjectList.findObject(self->mObjectID);
  3061. //MK
  3062. if (gRLenabled && !gRLInterface.canDetach(object))
  3063. {
  3064. return;
  3065. }
  3066. //mk
  3067. if (object)
  3068. {
  3069. LLMessageSystem* msg = gMessageSystemp;
  3070. msg->newMessageFast(_PREHASH_ScriptReset);
  3071. msg->nextBlockFast(_PREHASH_AgentData);
  3072. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  3073. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  3074. msg->nextBlockFast(_PREHASH_Script);
  3075. msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
  3076. msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
  3077. msg->sendReliable(object->getRegion()->getHost());
  3078. }
  3079. else
  3080. {
  3081. gNotifications.add("CouldNotStartStopScript");
  3082. }
  3083. }
  3084. //static
  3085. void LLLiveLSLEditor::onLoad(void* userdata)
  3086. {
  3087. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3088. if (self)
  3089. {
  3090. self->loadAsset();
  3091. }
  3092. }
  3093. //static
  3094. void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id,
  3095. LLAssetType::EType type, void* user_data,
  3096. S32 status, LLExtStat)
  3097. {
  3098. LL_DEBUGS("ScriptEditor") << "Got asset UUID " << asset_id << LL_ENDL;
  3099. LLUUID* xored_id = (LLUUID*)user_data;
  3100. instances_map_t::iterator it = sInstances.find(*xored_id);
  3101. delete xored_id;
  3102. if (it == sInstances.end())
  3103. {
  3104. LL_DEBUGS("ScriptEditor") << "Stale callback, preview floater gone, aborted."
  3105. << LL_ENDL;
  3106. }
  3107. LLLiveLSLEditor* self = it->second;
  3108. bool item_valid = self->getItem() != NULL;
  3109. if (item_valid && status == LL_ERR_NOERR)
  3110. {
  3111. // All good
  3112. self->loadScriptText(asset_id, type);
  3113. self->mAssetStatus = PREVIEW_ASSET_LOADED;
  3114. return;
  3115. }
  3116. gViewerStats.incStat(LLViewerStats::ST_DOWNLOAD_FAILED);
  3117. self->mAssetStatus = PREVIEW_ASSET_ERROR;
  3118. if (!item_valid)
  3119. {
  3120. gNotifications.add("LoadScriptFailObjectNotFound");
  3121. }
  3122. else if (status == LL_ERR_FILE_EMPTY ||
  3123. status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE)
  3124. {
  3125. gNotifications.add("ScriptMissing");
  3126. }
  3127. else if (status == LL_ERR_INSUFFICIENT_PERMISSIONS)
  3128. {
  3129. gNotifications.add("ScriptNoPermissions");
  3130. }
  3131. else
  3132. {
  3133. gNotifications.add("UnableToLoadScript");
  3134. }
  3135. }
  3136. //static
  3137. void LLLiveLSLEditor::onSave(void* userdata, bool close_after_save)
  3138. {
  3139. LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
  3140. if (self)
  3141. {
  3142. //MK
  3143. if (gRLenabled)
  3144. {
  3145. LLViewerObject* object = gObjectList.findObject(self->mObjectID);
  3146. if (!gRLInterface.canDetach(object))
  3147. {
  3148. return;
  3149. }
  3150. }
  3151. // mk
  3152. self->mCloseAfterSave = close_after_save;
  3153. self->saveIfNeeded();
  3154. }
  3155. }
  3156. //static
  3157. void LLLiveLSLEditor::onSearchReplace(void* userdata)
  3158. {
  3159. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3160. if (!self) return;
  3161. LLScriptEditor* sed = self->mScriptEd;
  3162. if (sed)
  3163. {
  3164. LLFloaterSearchReplace::show(sed->mEditor);
  3165. }
  3166. }
  3167. //static
  3168. void LLLiveLSLEditor::finishLSLUpload(LLUUID item_id, LLUUID task_id,
  3169. LLUUID new_asset_id, LLSD response,
  3170. bool running)
  3171. {
  3172. LLLiveLSLEditor* self = find(item_id, task_id);
  3173. if (self)
  3174. {
  3175. self->mItem->setAssetUUID(new_asset_id);
  3176. // Bytecode save completed
  3177. if (response.has("compiled") && response["compiled"])
  3178. {
  3179. self->callbackLSLCompileSucceeded(task_id, item_id, running);
  3180. }
  3181. else
  3182. {
  3183. self->callbackLSLCompileFailed(response["errors"]);
  3184. }
  3185. }
  3186. }
  3187. //static
  3188. void LLLiveLSLEditor::failedLSLUpload(LLUUID item_id, LLUUID task_id,
  3189. std::string reason)
  3190. {
  3191. LLLiveLSLEditor* self = find(item_id, task_id);
  3192. if (self)
  3193. {
  3194. LLSD errors;
  3195. errors.append(LLTrans::getString("AssetUploadFailed") + reason);
  3196. self->callbackLSLCompileFailed(errors);
  3197. }
  3198. }
  3199. //static
  3200. void LLLiveLSLEditor::onViewProfile(void* userdata)
  3201. {
  3202. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  3203. if (self && self->mExperienceEnabled->get())
  3204. {
  3205. LLUUID id = self->mScriptEd->getAssociatedExperience();
  3206. if (id.notNull())
  3207. {
  3208. LLFloaterExperienceProfile::show(id);
  3209. }
  3210. }
  3211. }
  3212. //static
  3213. void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**)
  3214. {
  3215. LLUUID object_id;
  3216. msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id);
  3217. LLUUID item_id;
  3218. msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id);
  3219. LLUUID xored_id = item_id ^ object_id;
  3220. instances_map_t::iterator it = sInstances.find(xored_id);
  3221. if (it != sInstances.end())
  3222. {
  3223. LLLiveLSLEditor* self = it->second;
  3224. self->mHaveRunningInfo = true;
  3225. bool running;
  3226. msg->getBoolFast(_PREHASH_Script, _PREHASH_Running, running);
  3227. self->mRunningCheckbox->set(running);
  3228. bool mono;
  3229. msg->getBoolFast(_PREHASH_Script, "Mono", mono);
  3230. LLCheckBoxCtrl* mono_check = self->mScriptEd->getMonoCheckBox();
  3231. bool can_use_mono = gIsInSecondLife && self->getIsModifiable() &&
  3232. have_script_upload_cap(object_id);
  3233. mono_check->setEnabled(can_use_mono);
  3234. mono_check->set(mono);
  3235. }
  3236. }