PageRenderTime 60ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llpreviewscript.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2377 lines | 1871 code | 317 blank | 189 comment | 219 complexity | 22ebad57c0b25deb2791d72f3db87911 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llpreviewscript.cpp
  3. * @brief LLPreviewScript class implementation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llpreviewscript.h"
  28. #include "llassetstorage.h"
  29. #include "llassetuploadresponders.h"
  30. #include "llbutton.h"
  31. #include "llcheckboxctrl.h"
  32. #include "llcombobox.h"
  33. #include "lldir.h"
  34. #include "llexternaleditor.h"
  35. #include "llfilepicker.h"
  36. #include "llfloaterreg.h"
  37. #include "llinventorydefines.h"
  38. #include "llinventorymodel.h"
  39. #include "llkeyboard.h"
  40. #include "lllineeditor.h"
  41. #include "lllivefile.h"
  42. #include "llhelp.h"
  43. #include "llnotificationsutil.h"
  44. #include "llresmgr.h"
  45. #include "llscrollbar.h"
  46. #include "llscrollcontainer.h"
  47. #include "llscrolllistctrl.h"
  48. #include "llscrolllistitem.h"
  49. #include "llscrolllistcell.h"
  50. #include "llslider.h"
  51. #include "lscript_rt_interface.h"
  52. #include "lscript_library.h"
  53. #include "lscript_export.h"
  54. #include "lltextbox.h"
  55. #include "lltooldraganddrop.h"
  56. #include "llvfile.h"
  57. #include "llagent.h"
  58. #include "llmenugl.h"
  59. #include "roles_constants.h"
  60. #include "llselectmgr.h"
  61. #include "llviewerinventory.h"
  62. #include "llviewermenu.h"
  63. #include "llviewerobject.h"
  64. #include "llviewerobjectlist.h"
  65. #include "llviewerregion.h"
  66. #include "llkeyboard.h"
  67. #include "llscrollcontainer.h"
  68. #include "llcheckboxctrl.h"
  69. #include "llselectmgr.h"
  70. #include "lltooldraganddrop.h"
  71. #include "llscrolllistctrl.h"
  72. #include "lltextbox.h"
  73. #include "llslider.h"
  74. #include "lldir.h"
  75. #include "llcombobox.h"
  76. #include "llviewerstats.h"
  77. #include "llviewertexteditor.h"
  78. #include "llviewerwindow.h"
  79. #include "lluictrlfactory.h"
  80. #include "llmediactrl.h"
  81. #include "lluictrlfactory.h"
  82. #include "lltrans.h"
  83. #include "llviewercontrol.h"
  84. #include "llappviewer.h"
  85. const std::string HELLO_LSL =
  86. "default\n"
  87. "{\n"
  88. " state_entry()\n"
  89. " {\n"
  90. " llSay(0, \"Hello, Avatar!\");\n"
  91. " }\n"
  92. "\n"
  93. " touch_start(integer total_number)\n"
  94. " {\n"
  95. " llSay(0, \"Touched.\");\n"
  96. " }\n"
  97. "}\n";
  98. const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal";
  99. const std::string DEFAULT_SCRIPT_NAME = "New Script"; // *TODO:Translate?
  100. const std::string DEFAULT_SCRIPT_DESC = "(No Description)"; // *TODO:Translate?
  101. // Description and header information
  102. const S32 MAX_EXPORT_SIZE = 1000;
  103. const S32 MAX_HISTORY_COUNT = 10;
  104. const F32 LIVE_HELP_REFRESH_TIME = 1.f;
  105. static bool have_script_upload_cap(LLUUID& object_id)
  106. {
  107. LLViewerObject* object = gObjectList.findObject(object_id);
  108. return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());
  109. }
  110. /// ---------------------------------------------------------------------------
  111. /// LLLiveLSLFile
  112. /// ---------------------------------------------------------------------------
  113. class LLLiveLSLFile : public LLLiveFile
  114. {
  115. public:
  116. typedef boost::function<bool (const std::string& filename)> change_callback_t;
  117. LLLiveLSLFile(std::string file_path, change_callback_t change_cb);
  118. ~LLLiveLSLFile();
  119. void ignoreNextUpdate() { mIgnoreNextUpdate = true; }
  120. protected:
  121. /*virtual*/ bool loadFile();
  122. change_callback_t mOnChangeCallback;
  123. bool mIgnoreNextUpdate;
  124. };
  125. LLLiveLSLFile::LLLiveLSLFile(std::string file_path, change_callback_t change_cb)
  126. : mOnChangeCallback(change_cb)
  127. , mIgnoreNextUpdate(false)
  128. , LLLiveFile(file_path, 1.0)
  129. {
  130. llassert(mOnChangeCallback);
  131. }
  132. LLLiveLSLFile::~LLLiveLSLFile()
  133. {
  134. LLFile::remove(filename());
  135. }
  136. bool LLLiveLSLFile::loadFile()
  137. {
  138. if (mIgnoreNextUpdate)
  139. {
  140. mIgnoreNextUpdate = false;
  141. return true;
  142. }
  143. return mOnChangeCallback(filename());
  144. }
  145. /// ---------------------------------------------------------------------------
  146. /// LLFloaterScriptSearch
  147. /// ---------------------------------------------------------------------------
  148. class LLFloaterScriptSearch : public LLFloater
  149. {
  150. public:
  151. LLFloaterScriptSearch(LLScriptEdCore* editor_core);
  152. ~LLFloaterScriptSearch();
  153. /*virtual*/ BOOL postBuild();
  154. static void show(LLScriptEdCore* editor_core);
  155. static void onBtnSearch(void* userdata);
  156. void handleBtnSearch();
  157. static void onBtnReplace(void* userdata);
  158. void handleBtnReplace();
  159. static void onBtnReplaceAll(void* userdata);
  160. void handleBtnReplaceAll();
  161. LLScriptEdCore* getEditorCore() { return mEditorCore; }
  162. static LLFloaterScriptSearch* getInstance() { return sInstance; }
  163. virtual bool hasAccelerators() const;
  164. virtual BOOL handleKeyHere(KEY key, MASK mask);
  165. private:
  166. LLScriptEdCore* mEditorCore;
  167. static LLFloaterScriptSearch* sInstance;
  168. };
  169. LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
  170. LLFloaterScriptSearch::LLFloaterScriptSearch(LLScriptEdCore* editor_core)
  171. : LLFloater(LLSD()),
  172. mEditorCore(editor_core)
  173. {
  174. buildFromFile("floater_script_search.xml");
  175. sInstance = this;
  176. // find floater in which script panel is embedded
  177. LLView* viewp = (LLView*)editor_core;
  178. while(viewp)
  179. {
  180. LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
  181. if (floaterp)
  182. {
  183. floaterp->addDependentFloater(this);
  184. break;
  185. }
  186. viewp = viewp->getParent();
  187. }
  188. }
  189. BOOL LLFloaterScriptSearch::postBuild()
  190. {
  191. childSetAction("search_btn", onBtnSearch,this);
  192. childSetAction("replace_btn", onBtnReplace,this);
  193. childSetAction("replace_all_btn", onBtnReplaceAll,this);
  194. setDefaultBtn("search_btn");
  195. return TRUE;
  196. }
  197. //static
  198. void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core)
  199. {
  200. if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core)
  201. {
  202. sInstance->closeFloater();
  203. delete sInstance;
  204. }
  205. if (!sInstance)
  206. {
  207. // sInstance will be assigned in the constructor.
  208. new LLFloaterScriptSearch(editor_core);
  209. }
  210. sInstance->openFloater();
  211. }
  212. LLFloaterScriptSearch::~LLFloaterScriptSearch()
  213. {
  214. sInstance = NULL;
  215. }
  216. // static
  217. void LLFloaterScriptSearch::onBtnSearch(void *userdata)
  218. {
  219. LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
  220. self->handleBtnSearch();
  221. }
  222. void LLFloaterScriptSearch::handleBtnSearch()
  223. {
  224. LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
  225. mEditorCore->mEditor->selectNext(getChild<LLUICtrl>("search_text")->getValue().asString(), caseChk->get());
  226. }
  227. // static
  228. void LLFloaterScriptSearch::onBtnReplace(void *userdata)
  229. {
  230. LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
  231. self->handleBtnReplace();
  232. }
  233. void LLFloaterScriptSearch::handleBtnReplace()
  234. {
  235. LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
  236. mEditorCore->mEditor->replaceText(getChild<LLUICtrl>("search_text")->getValue().asString(), getChild<LLUICtrl>("replace_text")->getValue().asString(), caseChk->get());
  237. }
  238. // static
  239. void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata)
  240. {
  241. LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
  242. self->handleBtnReplaceAll();
  243. }
  244. void LLFloaterScriptSearch::handleBtnReplaceAll()
  245. {
  246. LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
  247. mEditorCore->mEditor->replaceTextAll(getChild<LLUICtrl>("search_text")->getValue().asString(), getChild<LLUICtrl>("replace_text")->getValue().asString(), caseChk->get());
  248. }
  249. bool LLFloaterScriptSearch::hasAccelerators() const
  250. {
  251. if (mEditorCore)
  252. {
  253. return mEditorCore->hasAccelerators();
  254. }
  255. return FALSE;
  256. }
  257. BOOL LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask)
  258. {
  259. if (mEditorCore)
  260. {
  261. return mEditorCore->handleKeyHere(key, mask);
  262. }
  263. return FALSE;
  264. }
  265. /// ---------------------------------------------------------------------------
  266. /// LLScriptEdCore
  267. /// ---------------------------------------------------------------------------
  268. struct LLSECKeywordCompare
  269. {
  270. bool operator()(const std::string& lhs, const std::string& rhs)
  271. {
  272. return (LLStringUtil::compareDictInsensitive( lhs, rhs ) < 0 );
  273. }
  274. };
  275. LLScriptEdCore::LLScriptEdCore(
  276. LLScriptEdContainer* container,
  277. const std::string& sample,
  278. const LLHandle<LLFloater>& floater_handle,
  279. void (*load_callback)(void*),
  280. void (*save_callback)(void*, BOOL),
  281. void (*search_replace_callback) (void* userdata),
  282. void* userdata,
  283. S32 bottom_pad)
  284. :
  285. LLPanel(),
  286. mSampleText(sample),
  287. mEditor( NULL ),
  288. mLoadCallback( load_callback ),
  289. mSaveCallback( save_callback ),
  290. mSearchReplaceCallback( search_replace_callback ),
  291. mUserdata( userdata ),
  292. mForceClose( FALSE ),
  293. mLastHelpToken(NULL),
  294. mLiveHelpHistorySize(0),
  295. mEnableSave(FALSE),
  296. mLiveFile(NULL),
  297. mContainer(container),
  298. mHasScriptData(FALSE)
  299. {
  300. setFollowsAll();
  301. setBorderVisible(FALSE);
  302. setXMLFilename("panel_script_ed.xml");
  303. llassert_always(mContainer != NULL);
  304. }
  305. LLScriptEdCore::~LLScriptEdCore()
  306. {
  307. deleteBridges();
  308. // If the search window is up for this editor, close it.
  309. LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance();
  310. if (script_search && script_search->getEditorCore() == this)
  311. {
  312. script_search->closeFloater();
  313. delete script_search;
  314. }
  315. delete mLiveFile;
  316. }
  317. BOOL LLScriptEdCore::postBuild()
  318. {
  319. mErrorList = getChild<LLScrollListCtrl>("lsl errors");
  320. mFunctions = getChild<LLComboBox>( "Insert...");
  321. childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);
  322. mEditor = getChild<LLViewerTextEditor>("Script Editor");
  323. childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
  324. childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,FALSE));
  325. childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this));
  326. initMenu();
  327. std::vector<std::string> funcs;
  328. std::vector<std::string> tooltips;
  329. for (std::vector<LLScriptLibraryFunction>::const_iterator i = gScriptLibrary.mFunctions.begin();
  330. i != gScriptLibrary.mFunctions.end(); ++i)
  331. {
  332. // Make sure this isn't a god only function, or the agent is a god.
  333. if (!i->mGodOnly || gAgent.isGodlike())
  334. {
  335. std::string name = i->mName;
  336. funcs.push_back(name);
  337. std::string desc_name = "LSLTipText_";
  338. desc_name += name;
  339. std::string desc = LLTrans::getString(desc_name);
  340. F32 sleep_time = i->mSleepTime;
  341. if( sleep_time )
  342. {
  343. desc += "\n";
  344. LLStringUtil::format_map_t args;
  345. args["[SLEEP_TIME]"] = llformat("%.1f", sleep_time );
  346. desc += LLTrans::getString("LSLTipSleepTime", args);
  347. }
  348. // A \n linefeed is not part of xml. Let's add one to keep all
  349. // the tips one-per-line in strings.xml
  350. LLStringUtil::replaceString( desc, "\\n", "\n" );
  351. tooltips.push_back(desc);
  352. }
  353. }
  354. LLColor3 color(0.5f, 0.0f, 0.15f);
  355. mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color);
  356. std::vector<std::string> primary_keywords;
  357. std::vector<std::string> secondary_keywords;
  358. LLKeywordToken *token;
  359. LLKeywords::keyword_iterator_t token_it;
  360. for (token_it = mEditor->keywordsBegin(); token_it != mEditor->keywordsEnd(); ++token_it)
  361. {
  362. token = token_it->second;
  363. if (token->getColor() == color) // Wow, what a disgusting hack.
  364. {
  365. primary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
  366. }
  367. else
  368. {
  369. secondary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
  370. }
  371. }
  372. // Case-insensitive dictionary sort for primary keywords. We don't sort the secondary
  373. // keywords. They're intelligently grouped in keywords.ini.
  374. std::stable_sort( primary_keywords.begin(), primary_keywords.end(), LLSECKeywordCompare() );
  375. for (std::vector<std::string>::const_iterator iter= primary_keywords.begin();
  376. iter!= primary_keywords.end(); ++iter)
  377. {
  378. mFunctions->add(*iter);
  379. }
  380. for (std::vector<std::string>::const_iterator iter= secondary_keywords.begin();
  381. iter!= secondary_keywords.end(); ++iter)
  382. {
  383. mFunctions->add(*iter);
  384. }
  385. return TRUE;
  386. }
  387. void LLScriptEdCore::initMenu()
  388. {
  389. // *TODO: Skinning - make these callbacks data driven
  390. LLMenuItemCallGL* menuItem;
  391. menuItem = getChild<LLMenuItemCallGL>("Save");
  392. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::doSave, this, FALSE));
  393. menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this));
  394. menuItem = getChild<LLMenuItemCallGL>("Revert All Changes");
  395. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnUndoChanges, this));
  396. menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this));
  397. menuItem = getChild<LLMenuItemCallGL>("Undo");
  398. menuItem->setClickCallback(boost::bind(&LLTextEditor::undo, mEditor));
  399. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canUndo, mEditor));
  400. menuItem = getChild<LLMenuItemCallGL>("Redo");
  401. menuItem->setClickCallback(boost::bind(&LLTextEditor::redo, mEditor));
  402. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canRedo, mEditor));
  403. menuItem = getChild<LLMenuItemCallGL>("Cut");
  404. menuItem->setClickCallback(boost::bind(&LLTextEditor::cut, mEditor));
  405. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCut, mEditor));
  406. menuItem = getChild<LLMenuItemCallGL>("Copy");
  407. menuItem->setClickCallback(boost::bind(&LLTextEditor::copy, mEditor));
  408. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCopy, mEditor));
  409. menuItem = getChild<LLMenuItemCallGL>("Paste");
  410. menuItem->setClickCallback(boost::bind(&LLTextEditor::paste, mEditor));
  411. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canPaste, mEditor));
  412. menuItem = getChild<LLMenuItemCallGL>("Select All");
  413. menuItem->setClickCallback(boost::bind(&LLTextEditor::selectAll, mEditor));
  414. menuItem->setEnableCallback(boost::bind(&LLTextEditor::canSelectAll, mEditor));
  415. menuItem = getChild<LLMenuItemCallGL>("Search / Replace...");
  416. menuItem->setClickCallback(boost::bind(&LLFloaterScriptSearch::show, this));
  417. menuItem = getChild<LLMenuItemCallGL>("Help...");
  418. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnHelp, this));
  419. menuItem = getChild<LLMenuItemCallGL>("Keyword Help...");
  420. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnDynamicHelp, this));
  421. menuItem = getChild<LLMenuItemCallGL>("LoadFromFile");
  422. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnLoadFromFile, this));
  423. menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableLoadFromFileMenu, this));
  424. menuItem = getChild<LLMenuItemCallGL>("SaveToFile");
  425. menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnSaveToFile, this));
  426. menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableSaveToFileMenu, this));
  427. }
  428. void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid)
  429. {
  430. if (mEditor)
  431. {
  432. mEditor->setText(text);
  433. mHasScriptData = is_valid;
  434. }
  435. }
  436. bool LLScriptEdCore::loadScriptText(const std::string& filename)
  437. {
  438. if (filename.empty())
  439. {
  440. llwarns << "Empty file name" << llendl;
  441. return false;
  442. }
  443. LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
  444. if (!file)
  445. {
  446. llwarns << "Error opening " << filename << llendl;
  447. return false;
  448. }
  449. // read in the whole file
  450. fseek(file, 0L, SEEK_END);
  451. size_t file_length = (size_t) ftell(file);
  452. fseek(file, 0L, SEEK_SET);
  453. char* buffer = new char[file_length+1];
  454. size_t nread = fread(buffer, 1, file_length, file);
  455. if (nread < file_length)
  456. {
  457. llwarns << "Short read" << llendl;
  458. }
  459. buffer[nread] = '\0';
  460. fclose(file);
  461. mEditor->setText(LLStringExplicit(buffer));
  462. delete[] buffer;
  463. return true;
  464. }
  465. bool LLScriptEdCore::writeToFile(const std::string& filename)
  466. {
  467. LLFILE* fp = LLFile::fopen(filename, "wb");
  468. if (!fp)
  469. {
  470. llwarns << "Unable to write to " << filename << llendl;
  471. LLSD row;
  472. row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?";
  473. row["columns"][0]["font"] = "SANSSERIF_SMALL";
  474. mErrorList->addElement(row);
  475. return false;
  476. }
  477. std::string utf8text = mEditor->getText();
  478. // Special case for a completely empty script - stuff in one space so it can store properly. See SL-46889
  479. if (utf8text.size() == 0)
  480. {
  481. utf8text = " ";
  482. }
  483. fputs(utf8text.c_str(), fp);
  484. fclose(fp);
  485. return true;
  486. }
  487. void LLScriptEdCore::sync()
  488. {
  489. // Sync with external editor.
  490. std::string tmp_file = mContainer->getTmpFileName();
  491. llstat s;
  492. if (LLFile::stat(tmp_file, &s) == 0) // file exists
  493. {
  494. if (mLiveFile) mLiveFile->ignoreNextUpdate();
  495. writeToFile(tmp_file);
  496. }
  497. }
  498. bool LLScriptEdCore::hasChanged()
  499. {
  500. if (!mEditor) return false;
  501. return ((!mEditor->isPristine() || mEnableSave) && mHasScriptData);
  502. }
  503. void LLScriptEdCore::draw()
  504. {
  505. BOOL script_changed = hasChanged();
  506. getChildView("Save_btn")->setEnabled(script_changed);
  507. if( mEditor->hasFocus() )
  508. {
  509. S32 line = 0;
  510. S32 column = 0;
  511. mEditor->getCurrentLineAndColumn( &line, &column, FALSE ); // don't include wordwrap
  512. LLStringUtil::format_map_t args;
  513. std::string cursor_pos;
  514. args["[LINE]"] = llformat ("%d", line);
  515. args["[COLUMN]"] = llformat ("%d", column);
  516. cursor_pos = LLTrans::getString("CursorPos", args);
  517. getChild<LLUICtrl>("line_col")->setValue(cursor_pos);
  518. }
  519. else
  520. {
  521. getChild<LLUICtrl>("line_col")->setValue(LLStringUtil::null);
  522. }
  523. updateDynamicHelp();
  524. LLPanel::draw();
  525. }
  526. void LLScriptEdCore::updateDynamicHelp(BOOL immediate)
  527. {
  528. LLFloater* help_floater = mLiveHelpHandle.get();
  529. if (!help_floater) return;
  530. // update back and forward buttons
  531. LLButton* fwd_button = help_floater->getChild<LLButton>("fwd_btn");
  532. LLButton* back_button = help_floater->getChild<LLButton>("back_btn");
  533. LLMediaCtrl* browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  534. back_button->setEnabled(browser->canNavigateBack());
  535. fwd_button->setEnabled(browser->canNavigateForward());
  536. if (!immediate && !gSavedSettings.getBOOL("ScriptHelpFollowsCursor"))
  537. {
  538. return;
  539. }
  540. LLTextSegmentPtr segment = NULL;
  541. std::vector<LLTextSegmentPtr> selected_segments;
  542. mEditor->getSelectedSegments(selected_segments);
  543. // try segments in selection range first
  544. std::vector<LLTextSegmentPtr>::iterator segment_iter;
  545. for (segment_iter = selected_segments.begin(); segment_iter != selected_segments.end(); ++segment_iter)
  546. {
  547. if((*segment_iter)->getToken() && (*segment_iter)->getToken()->getType() == LLKeywordToken::WORD)
  548. {
  549. segment = *segment_iter;
  550. break;
  551. }
  552. }
  553. // then try previous segment in case we just typed it
  554. if (!segment)
  555. {
  556. const LLTextSegmentPtr test_segment = mEditor->getPreviousSegment();
  557. if(test_segment->getToken() && test_segment->getToken()->getType() == LLKeywordToken::WORD)
  558. {
  559. segment = test_segment;
  560. }
  561. }
  562. if (segment)
  563. {
  564. if (segment->getToken() != mLastHelpToken)
  565. {
  566. mLastHelpToken = segment->getToken();
  567. mLiveHelpTimer.start();
  568. }
  569. if (immediate || (mLiveHelpTimer.getStarted() && mLiveHelpTimer.getElapsedTimeF32() > LIVE_HELP_REFRESH_TIME))
  570. {
  571. std::string help_string = mEditor->getText().substr(segment->getStart(), segment->getEnd() - segment->getStart());
  572. setHelpPage(help_string);
  573. mLiveHelpTimer.stop();
  574. }
  575. }
  576. else
  577. {
  578. if (immediate)
  579. {
  580. setHelpPage(LLStringUtil::null);
  581. }
  582. }
  583. }
  584. void LLScriptEdCore::setHelpPage(const std::string& help_string)
  585. {
  586. LLFloater* help_floater = mLiveHelpHandle.get();
  587. if (!help_floater) return;
  588. LLMediaCtrl* web_browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  589. if (!web_browser) return;
  590. LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo");
  591. if (!history_combo) return;
  592. LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
  593. url_string.setArg("[LSL_STRING]", help_string);
  594. addHelpItemToHistory(help_string);
  595. web_browser->navigateTo(url_string);
  596. }
  597. void LLScriptEdCore::addHelpItemToHistory(const std::string& help_string)
  598. {
  599. if (help_string.empty()) return;
  600. LLFloater* help_floater = mLiveHelpHandle.get();
  601. if (!help_floater) return;
  602. LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo");
  603. if (!history_combo) return;
  604. // separate history items from full item list
  605. if (mLiveHelpHistorySize == 0)
  606. {
  607. history_combo->addSeparator(ADD_TOP);
  608. }
  609. // delete all history items over history limit
  610. while(mLiveHelpHistorySize > MAX_HISTORY_COUNT - 1)
  611. {
  612. history_combo->remove(mLiveHelpHistorySize - 1);
  613. mLiveHelpHistorySize--;
  614. }
  615. history_combo->setSimple(help_string);
  616. S32 index = history_combo->getCurrentIndex();
  617. // if help string exists in the combo box
  618. if (index >= 0)
  619. {
  620. S32 cur_index = history_combo->getCurrentIndex();
  621. if (cur_index < mLiveHelpHistorySize)
  622. {
  623. // item found in history, bubble up to top
  624. history_combo->remove(history_combo->getCurrentIndex());
  625. mLiveHelpHistorySize--;
  626. }
  627. }
  628. history_combo->add(help_string, LLSD(help_string), ADD_TOP);
  629. history_combo->selectFirstItem();
  630. mLiveHelpHistorySize++;
  631. }
  632. BOOL LLScriptEdCore::canClose()
  633. {
  634. if(mForceClose || !hasChanged())
  635. {
  636. return TRUE;
  637. }
  638. else
  639. {
  640. // Bring up view-modal dialog: Save changes? Yes, No, Cancel
  641. LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleSaveChangesDialog, this, _1, _2));
  642. return FALSE;
  643. }
  644. }
  645. void LLScriptEdCore::setEnableEditing(bool enable)
  646. {
  647. mEditor->setEnabled(enable);
  648. getChildView("Edit_btn")->setEnabled(enable);
  649. }
  650. bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLSD& response )
  651. {
  652. S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
  653. switch( option )
  654. {
  655. case 0: // "Yes"
  656. // close after saving
  657. doSave( TRUE );
  658. break;
  659. case 1: // "No"
  660. mForceClose = TRUE;
  661. // This will close immediately because mForceClose is true, so we won't
  662. // infinite loop with these dialogs. JC
  663. ((LLFloater*) getParent())->closeFloater();
  664. break;
  665. case 2: // "Cancel"
  666. default:
  667. // If we were quitting, we didn't really mean it.
  668. LLAppViewer::instance()->abortQuit();
  669. break;
  670. }
  671. return false;
  672. }
  673. void LLScriptEdCore::onBtnHelp()
  674. {
  675. LLUI::sHelpImpl->showTopic(HELP_LSL_PORTAL_TOPIC);
  676. }
  677. void LLScriptEdCore::onBtnDynamicHelp()
  678. {
  679. LLFloater* live_help_floater = mLiveHelpHandle.get();
  680. if (!live_help_floater)
  681. {
  682. live_help_floater = new LLFloater(LLSD());
  683. live_help_floater->buildFromFile("floater_lsl_guide.xml", NULL);
  684. LLFloater* parent = dynamic_cast<LLFloater*>(getParent());
  685. llassert(parent);
  686. if (parent)
  687. parent->addDependentFloater(live_help_floater, TRUE);
  688. live_help_floater->childSetCommitCallback("lock_check", onCheckLock, this);
  689. live_help_floater->getChild<LLUICtrl>("lock_check")->setValue(gSavedSettings.getBOOL("ScriptHelpFollowsCursor"));
  690. live_help_floater->childSetCommitCallback("history_combo", onHelpComboCommit, this);
  691. live_help_floater->childSetAction("back_btn", onClickBack, this);
  692. live_help_floater->childSetAction("fwd_btn", onClickForward, this);
  693. LLMediaCtrl* browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  694. browser->setAlwaysRefresh(TRUE);
  695. LLComboBox* help_combo = live_help_floater->getChild<LLComboBox>("history_combo");
  696. LLKeywordToken *token;
  697. LLKeywords::keyword_iterator_t token_it;
  698. for (token_it = mEditor->keywordsBegin();
  699. token_it != mEditor->keywordsEnd();
  700. ++token_it)
  701. {
  702. token = token_it->second;
  703. help_combo->add(wstring_to_utf8str(token->getToken()));
  704. }
  705. help_combo->sortByName();
  706. // re-initialize help variables
  707. mLastHelpToken = NULL;
  708. mLiveHelpHandle = live_help_floater->getHandle();
  709. mLiveHelpHistorySize = 0;
  710. }
  711. BOOL visible = TRUE;
  712. BOOL take_focus = TRUE;
  713. live_help_floater->setVisible(visible);
  714. live_help_floater->setFrontmost(take_focus);
  715. updateDynamicHelp(TRUE);
  716. }
  717. //static
  718. void LLScriptEdCore::onClickBack(void* userdata)
  719. {
  720. LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
  721. LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
  722. if (live_help_floater)
  723. {
  724. LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  725. if (browserp)
  726. {
  727. browserp->navigateBack();
  728. }
  729. }
  730. }
  731. //static
  732. void LLScriptEdCore::onClickForward(void* userdata)
  733. {
  734. LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
  735. LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
  736. if (live_help_floater)
  737. {
  738. LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  739. if (browserp)
  740. {
  741. browserp->navigateForward();
  742. }
  743. }
  744. }
  745. // static
  746. void LLScriptEdCore::onCheckLock(LLUICtrl* ctrl, void* userdata)
  747. {
  748. LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
  749. // clear out token any time we lock the frame, so we will refresh web page immediately when unlocked
  750. gSavedSettings.setBOOL("ScriptHelpFollowsCursor", ctrl->getValue().asBoolean());
  751. corep->mLastHelpToken = NULL;
  752. }
  753. // static
  754. void LLScriptEdCore::onBtnInsertSample(void* userdata)
  755. {
  756. LLScriptEdCore* self = (LLScriptEdCore*) userdata;
  757. // Insert sample code
  758. self->mEditor->selectAll();
  759. self->mEditor->cut();
  760. self->mEditor->insertText(self->mSampleText);
  761. }
  762. // static
  763. void LLScriptEdCore::onHelpComboCommit(LLUICtrl* ctrl, void* userdata)
  764. {
  765. LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
  766. LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
  767. if (live_help_floater)
  768. {
  769. std::string help_string = ctrl->getValue().asString();
  770. corep->addHelpItemToHistory(help_string);
  771. LLMediaCtrl* web_browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
  772. LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
  773. url_string.setArg("[LSL_STRING]", help_string);
  774. web_browser->navigateTo(url_string);
  775. }
  776. }
  777. // static
  778. void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata)
  779. {
  780. LLScriptEdCore* self = (LLScriptEdCore*) userdata;
  781. // Insert sample code
  782. if(self->mEditor->getEnabled())
  783. {
  784. self->mEditor->insertText(self->mFunctions->getSimple());
  785. }
  786. self->mEditor->setFocus(TRUE);
  787. self->setHelpPage(self->mFunctions->getSimple());
  788. }
  789. void LLScriptEdCore::doSave( BOOL close_after_save )
  790. {
  791. LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
  792. if( mSaveCallback )
  793. {
  794. mSaveCallback( mUserdata, close_after_save );
  795. }
  796. }
  797. void LLScriptEdCore::openInExternalEditor()
  798. {
  799. delete mLiveFile; // deletes file
  800. // Save the script to a temporary file.
  801. std::string filename = mContainer->getTmpFileName();
  802. writeToFile(filename);
  803. // Start watching file changes.
  804. mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1));
  805. mLiveFile->addToEventTimer();
  806. // Open it in external editor.
  807. {
  808. LLExternalEditor ed;
  809. LLExternalEditor::EErrorCode status;
  810. std::string msg;
  811. status = ed.setCommand("LL_SCRIPT_EDITOR");
  812. if (status != LLExternalEditor::EC_SUCCESS)
  813. {
  814. if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error.
  815. {
  816. msg = getString("external_editor_not_set");
  817. }
  818. else
  819. {
  820. msg = LLExternalEditor::getErrorMessage(status);
  821. }
  822. LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg));
  823. return;
  824. }
  825. status = ed.run(filename);
  826. if (status != LLExternalEditor::EC_SUCCESS)
  827. {
  828. msg = LLExternalEditor::getErrorMessage(status);
  829. LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg));
  830. }
  831. }
  832. }
  833. void LLScriptEdCore::onBtnUndoChanges()
  834. {
  835. if( !mEditor->tryToRevertToPristineState() )
  836. {
  837. LLNotificationsUtil::add("ScriptCannotUndo", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleReloadFromServerDialog, this, _1, _2));
  838. }
  839. }
  840. // static
  841. void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data)
  842. {
  843. LLScriptEdCore* self = (LLScriptEdCore*)user_data;
  844. LLScrollListItem* item = self->mErrorList->getFirstSelected();
  845. if(item)
  846. {
  847. // *FIX: replace with boost grep
  848. S32 row = 0;
  849. S32 column = 0;
  850. const LLScrollListCell* cell = item->getColumn(0);
  851. std::string line(cell->getValue().asString());
  852. line.erase(0, 1);
  853. LLStringUtil::replaceChar(line, ',',' ');
  854. LLStringUtil::replaceChar(line, ')',' ');
  855. sscanf(line.c_str(), "%d %d", &row, &column);
  856. //llinfos << "LLScriptEdCore::onErrorList() - " << row << ", "
  857. //<< column << llendl;
  858. self->mEditor->setCursor(row, column);
  859. self->mEditor->setFocus(TRUE);
  860. }
  861. }
  862. bool LLScriptEdCore::handleReloadFromServerDialog(const LLSD& notification, const LLSD& response )
  863. {
  864. S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
  865. switch( option )
  866. {
  867. case 0: // "Yes"
  868. if( mLoadCallback )
  869. {
  870. setScriptText(getString("loading"), FALSE);
  871. mLoadCallback(mUserdata);
  872. }
  873. break;
  874. case 1: // "No"
  875. break;
  876. default:
  877. llassert(0);
  878. break;
  879. }
  880. return false;
  881. }
  882. void LLScriptEdCore::selectFirstError()
  883. {
  884. // Select the first item;
  885. mErrorList->selectFirstItem();
  886. onErrorList(mErrorList, this);
  887. }
  888. struct LLEntryAndEdCore
  889. {
  890. LLScriptEdCore* mCore;
  891. LLEntryAndEdCore(LLScriptEdCore* core) :
  892. mCore(core)
  893. {}
  894. };
  895. void LLScriptEdCore::deleteBridges()
  896. {
  897. S32 count = mBridges.count();
  898. LLEntryAndEdCore* eandc;
  899. for(S32 i = 0; i < count; i++)
  900. {
  901. eandc = mBridges.get(i);
  902. delete eandc;
  903. mBridges[i] = NULL;
  904. }
  905. mBridges.reset();
  906. }
  907. // virtual
  908. BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask)
  909. {
  910. bool just_control = MASK_CONTROL == (mask & MASK_MODIFIERS);
  911. if(('S' == key) && just_control)
  912. {
  913. if(mSaveCallback)
  914. {
  915. // don't close after saving
  916. mSaveCallback(mUserdata, FALSE);
  917. }
  918. return TRUE;
  919. }
  920. if(('F' == key) && just_control)
  921. {
  922. if(mSearchReplaceCallback)
  923. {
  924. mSearchReplaceCallback(mUserdata);
  925. }
  926. return TRUE;
  927. }
  928. return FALSE;
  929. }
  930. void LLScriptEdCore::onBtnLoadFromFile( void* data )
  931. {
  932. LLScriptEdCore* self = (LLScriptEdCore*) data;
  933. // TODO Maybe add a dialogue warning here if the current file has unsaved changes.
  934. LLFilePicker& file_picker = LLFilePicker::instance();
  935. if( !file_picker.getOpenFile( LLFilePicker::FFLOAD_SCRIPT ) )
  936. {
  937. //File picking cancelled by user, so nothing to do.
  938. return;
  939. }
  940. std::string filename = file_picker.getFirstFile();
  941. std::ifstream fin(filename.c_str());
  942. std::string line;
  943. std::string text;
  944. std::string linetotal;
  945. while (!fin.eof())
  946. {
  947. getline(fin,line);
  948. text += line;
  949. if (!fin.eof())
  950. {
  951. text += "\n";
  952. }
  953. }
  954. fin.close();
  955. // Only replace the script if there is something to replace with.
  956. if (text.length() > 0)
  957. {
  958. self->mEditor->selectAll();
  959. LLWString script(utf8str_to_wstring(text));
  960. self->mEditor->insertText(script);
  961. }
  962. }
  963. void LLScriptEdCore::onBtnSaveToFile( void* userdata )
  964. {
  965. LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
  966. LLScriptEdCore* self = (LLScriptEdCore*) userdata;
  967. if( self->mSaveCallback )
  968. {
  969. LLFilePicker& file_picker = LLFilePicker::instance();
  970. if( file_picker.getSaveFile( LLFilePicker::FFSAVE_SCRIPT ) )
  971. {
  972. std::string filename = file_picker.getFirstFile();
  973. std::string scriptText=self->mEditor->getText();
  974. std::ofstream fout(filename.c_str());
  975. fout<<(scriptText);
  976. fout.close();
  977. self->mSaveCallback( self->mUserdata, FALSE );
  978. }
  979. }
  980. }
  981. bool LLScriptEdCore::canLoadOrSaveToFile( void* userdata )
  982. {
  983. LLScriptEdCore* self = (LLScriptEdCore*) userdata;
  984. return self->mEditor->canLoadOrSaveToFile();
  985. }
  986. // static
  987. bool LLScriptEdCore::enableSaveToFileMenu(void* userdata)
  988. {
  989. LLScriptEdCore* self = (LLScriptEdCore*)userdata;
  990. if (!self || !self->mEditor) return FALSE;
  991. return self->mEditor->canLoadOrSaveToFile();
  992. }
  993. // static
  994. bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata)
  995. {
  996. LLScriptEdCore* self = (LLScriptEdCore*)userdata;
  997. return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE;
  998. }
  999. /// ---------------------------------------------------------------------------
  1000. /// LLScriptEdContainer
  1001. /// ---------------------------------------------------------------------------
  1002. LLScriptEdContainer::LLScriptEdContainer(const LLSD& key)
  1003. : LLPreview(key)
  1004. , mScriptEd(NULL)
  1005. {
  1006. }
  1007. std::string LLScriptEdContainer::getTmpFileName()
  1008. {
  1009. // Take script inventory item id (within the object inventory)
  1010. // to consideration so that it's possible to edit multiple scripts
  1011. // in the same object inventory simultaneously (STORM-781).
  1012. std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString();
  1013. // Use MD5 sum to make the file name shorter and not exceed maximum path length.
  1014. char script_id_hash_str[33]; /* Flawfinder: ignore */
  1015. LLMD5 script_id_hash((const U8 *)script_id.c_str());
  1016. script_id_hash.hex_digest(script_id_hash_str);
  1017. return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl";
  1018. }
  1019. bool LLScriptEdContainer::onExternalChange(const std::string& filename)
  1020. {
  1021. if (!mScriptEd->loadScriptText(filename))
  1022. {
  1023. return false;
  1024. }
  1025. // Disable sync to avoid recursive load->save->load calls.
  1026. saveIfNeeded(false);
  1027. return true;
  1028. }
  1029. /// ---------------------------------------------------------------------------
  1030. /// LLPreviewLSL
  1031. /// ---------------------------------------------------------------------------
  1032. struct LLScriptSaveInfo
  1033. {
  1034. LLUUID mItemUUID;
  1035. std::string mDescription;
  1036. LLTransactionID mTransactionID;
  1037. LLScriptSaveInfo(const LLUUID& uuid, const std::string& desc, LLTransactionID tid) :
  1038. mItemUUID(uuid), mDescription(desc), mTransactionID(tid) {}
  1039. };
  1040. //static
  1041. void* LLPreviewLSL::createScriptEdPanel(void* userdata)
  1042. {
  1043. LLPreviewLSL *self = (LLPreviewLSL*)userdata;
  1044. self->mScriptEd = new LLScriptEdCore(
  1045. self,
  1046. HELLO_LSL,
  1047. self->getHandle(),
  1048. LLPreviewLSL::onLoad,
  1049. LLPreviewLSL::onSave,
  1050. LLPreviewLSL::onSearchReplace,
  1051. self,
  1052. 0);
  1053. return self->mScriptEd;
  1054. }
  1055. LLPreviewLSL::LLPreviewLSL(const LLSD& key )
  1056. : LLScriptEdContainer(key),
  1057. mPendingUploads(0)
  1058. {
  1059. mFactoryMap["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this);
  1060. }
  1061. // virtual
  1062. BOOL LLPreviewLSL::postBuild()
  1063. {
  1064. const LLInventoryItem* item = getItem();
  1065. llassert(item);
  1066. if (item)
  1067. {
  1068. getChild<LLUICtrl>("desc")->setValue(item->getDescription());
  1069. }
  1070. childSetCommitCallback("desc", LLPreview::onText, this);
  1071. getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
  1072. return LLPreview::postBuild();
  1073. }
  1074. // virtual
  1075. void LLPreviewLSL::callbackLSLCompileSucceeded()
  1076. {
  1077. llinfos << "LSL Bytecode saved" << llendl;
  1078. mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
  1079. mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
  1080. closeIfNeeded();
  1081. }
  1082. // virtual
  1083. void LLPreviewLSL::callbackLSLCompileFailed(const LLSD& compile_errors)
  1084. {
  1085. llinfos << "Compile failed!" << llendl;
  1086. for(LLSD::array_const_iterator line = compile_errors.beginArray();
  1087. line < compile_errors.endArray();
  1088. line++)
  1089. {
  1090. LLSD row;
  1091. std::string error_message = line->asString();
  1092. LLStringUtil::stripNonprintable(error_message);
  1093. row["columns"][0]["value"] = error_message;
  1094. row["columns"][0]["font"] = "OCRA";
  1095. mScriptEd->mErrorList->addElement(row);
  1096. }
  1097. mScriptEd->selectFirstError();
  1098. closeIfNeeded();
  1099. }
  1100. void LLPreviewLSL::loadAsset()
  1101. {
  1102. // *HACK: we poke into inventory to see if it's there, and if so,
  1103. // then it might be part of the inventory library. If it's in the
  1104. // library, then you can see the script, but not modify it.
  1105. const LLInventoryItem* item = gInventory.getItem(mItemUUID);
  1106. BOOL is_library = item
  1107. && !gInventory.isObjectDescendentOf(mItemUUID,
  1108. gInventory.getRootFolderID());
  1109. if(!item)
  1110. {
  1111. // do the more generic search.
  1112. getItem();
  1113. }
  1114. if(item)
  1115. {
  1116. BOOL is_copyable = gAgent.allowOperation(PERM_COPY,
  1117. item->getPermissions(), GP_OBJECT_MANIPULATE);
  1118. BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY,
  1119. item->getPermissions(), GP_OBJECT_MANIPULATE);
  1120. if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library)))
  1121. {
  1122. LLUUID* new_uuid = new LLUUID(mItemUUID);
  1123. gAssetStorage->getInvItemAsset(LLHost::invalid,
  1124. gAgent.getID(),
  1125. gAgent.getSessionID(),
  1126. item->getPermissions().getOwner(),
  1127. LLUUID::null,
  1128. item->getUUID(),
  1129. item->getAssetUUID(),
  1130. item->getType(),
  1131. &LLPreviewLSL::onLoadComplete,
  1132. (void*)new_uuid,
  1133. TRUE);
  1134. mAssetStatus = PREVIEW_ASSET_LOADING;
  1135. }
  1136. else
  1137. {
  1138. mScriptEd->setScriptText(mScriptEd->getString("can_not_view"), FALSE);
  1139. mScriptEd->mEditor->makePristine();
  1140. mScriptEd->mFunctions->setEnabled(FALSE);
  1141. mAssetStatus = PREVIEW_ASSET_LOADED;
  1142. }
  1143. getChildView("lock")->setVisible( !is_modifiable);
  1144. mScriptEd->getChildView("Insert...")->setEnabled(is_modifiable);
  1145. }
  1146. else
  1147. {
  1148. mScriptEd->setScriptText(std::string(HELLO_LSL), TRUE);
  1149. mScriptEd->setEnableEditing(TRUE);
  1150. mAssetStatus = PREVIEW_ASSET_LOADED;
  1151. }
  1152. }
  1153. BOOL LLPreviewLSL::canClose()
  1154. {
  1155. return mScriptEd->canClose();
  1156. }
  1157. void LLPreviewLSL::closeIfNeeded()
  1158. {
  1159. // Find our window and close it if requested.
  1160. getWindow()->decBusyCount();
  1161. mPendingUploads--;
  1162. if (mPendingUploads <= 0 && mCloseAfterSave)
  1163. {
  1164. closeFloater();
  1165. }
  1166. }
  1167. void LLPreviewLSL::onSearchReplace(void* userdata)
  1168. {
  1169. LLPreviewLSL* self = (LLPreviewLSL*)userdata;
  1170. LLScriptEdCore* sec = self->mScriptEd;
  1171. LLFloaterScriptSearch::show(sec);
  1172. }
  1173. // static
  1174. void LLPreviewLSL::onLoad(void* userdata)
  1175. {
  1176. LLPreviewLSL* self = (LLPreviewLSL*)userdata;
  1177. self->loadAsset();
  1178. }
  1179. // static
  1180. void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save)
  1181. {
  1182. LLPreviewLSL* self = (LLPreviewLSL*)userdata;
  1183. self->mCloseAfterSave = close_after_save;
  1184. self->saveIfNeeded();
  1185. }
  1186. // Save needs to compile the text in the buffer. If the compile
  1187. // succeeds, then save both assets out to the database. If the compile
  1188. // fails, go ahead and save the text anyway.
  1189. void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/)
  1190. {
  1191. // llinfos << "LLPreviewLSL::saveIfNeeded()" << llendl;
  1192. if(!mScriptEd->hasChanged())
  1193. {
  1194. return;
  1195. }
  1196. mPendingUploads = 0;
  1197. mScriptEd->mErrorList->deleteAllItems();
  1198. mScriptEd->mEditor->makePristine();
  1199. // save off asset into file
  1200. LLTransactionID tid;
  1201. tid.generate();
  1202. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  1203. std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
  1204. std::string filename = filepath + ".lsl";
  1205. mScriptEd->writeToFile(filename);
  1206. if (sync)
  1207. {
  1208. mScriptEd->sync();
  1209. }
  1210. const LLInventoryItem *inv_item = getItem();
  1211. // save it out to asset server
  1212. std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent");
  1213. if(inv_item)
  1214. {
  1215. getWindow()->incBusyCount();
  1216. mPendingUploads++;
  1217. if (!url.empty())
  1218. {
  1219. uploadAssetViaCaps(url, filename, mItemUUID);
  1220. }
  1221. else if (gAssetStorage)
  1222. {
  1223. uploadAssetLegacy(filename, mItemUUID, tid);
  1224. }
  1225. }
  1226. }
  1227. void LLPreviewLSL::uploadAssetViaCaps(const std::string& url,
  1228. const std::string& filename,
  1229. const LLUUID& item_id)
  1230. {
  1231. llinfos << "Update Agent Inventory via capability" << llendl;
  1232. LLSD body;
  1233. body["item_id"] = item_id;
  1234. body["target"] = "lsl2";
  1235. LLHTTPClient::post(url, body, new LLUpdateAgentInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));
  1236. }
  1237. void LLPreviewLSL::uploadAssetLegacy(const std::string& filename,
  1238. const LLUUID& item_id,
  1239. const LLTransactionID& tid)
  1240. {
  1241. LLLineEditor* descEditor = getChild<LLLineEditor>("desc");
  1242. LLScriptSaveInfo* info = new LLScriptSaveInfo(item_id,
  1243. descEditor->getText(),
  1244. tid);
  1245. gAssetStorage->storeAssetData(filename, tid,
  1246. LLAssetType::AT_LSL_TEXT,
  1247. &LLPreviewLSL::onSaveComplete,
  1248. info);
  1249. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  1250. std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
  1251. std::string dst_filename = llformat("%s.lso", filepath.c_str());
  1252. std::string err_filename = llformat("%s.out", filepath.c_str());
  1253. const BOOL compile_to_mono = FALSE;
  1254. if(!lscript_compile(filename.c_str(),
  1255. dst_filename.c_str(),
  1256. err_filename.c_str(),
  1257. compile_to_mono,
  1258. asset_id.asString().c_str(),
  1259. gAgent.isGodlike()))
  1260. {
  1261. llinfos << "Compile failed!" << llendl;
  1262. //char command[256];
  1263. //sprintf(command, "type %s\n", err_filename.c_str());
  1264. //system(command);
  1265. // load the error file into the error scrolllist
  1266. LLFILE* fp = LLFile::fopen(err_filename, "r");
  1267. if(fp)
  1268. {
  1269. char buffer[MAX_STRING]; /*Flawfinder: ignore*/
  1270. std::string line;
  1271. while(!feof(fp))
  1272. {
  1273. if (fgets(buffer, MAX_STRING, fp) == NULL)
  1274. {
  1275. buffer[0] = '\0';
  1276. }
  1277. if(feof(fp))
  1278. {
  1279. break;
  1280. }
  1281. else
  1282. {
  1283. line.assign(buffer);
  1284. LLStringUtil::stripNonprintable(line);
  1285. LLSD row;
  1286. row["columns"][0]["value"] = line;
  1287. row["columns"][0]["font"] = "OCRA";
  1288. mScriptEd->mErrorList->addElement(row);
  1289. }
  1290. }
  1291. fclose(fp);
  1292. mScriptEd->selectFirstError();
  1293. }
  1294. }
  1295. else
  1296. {
  1297. llinfos << "Compile worked!" << llendl;
  1298. if(gAssetStorage)
  1299. {
  1300. getWindow()->incBusyCount();
  1301. mPendingUploads++;
  1302. LLUUID* this_uuid = new LLUUID(mItemUUID);
  1303. gAssetStorage->storeAssetData(dst_filename,
  1304. tid,
  1305. LLAssetType::AT_LSL_BYTECODE,
  1306. &LLPreviewLSL::onSaveBytecodeComplete,
  1307. (void**)this_uuid);
  1308. }
  1309. }
  1310. // get rid of any temp files left lying around
  1311. LLFile::remove(filename);
  1312. LLFile::remove(err_filename);
  1313. LLFile::remove(dst_filename);
  1314. }
  1315. // static
  1316. void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
  1317. {
  1318. LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data);
  1319. if(0 == status)
  1320. {
  1321. if (info)
  1322. {
  1323. const LLViewerInventoryItem* item;
  1324. item = (const LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
  1325. if(item)
  1326. {
  1327. LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
  1328. new_item->setAssetUUID(asset_uuid);
  1329. new_item->setTransactionID(info->mTransactionID);
  1330. new_item->updateServer(FALSE);
  1331. gInventory.updateItem(new_item);
  1332. gInventory.notifyObservers();
  1333. }
  1334. else
  1335. {
  1336. llwarns << "Inventory item for script " << info->mItemUUID
  1337. << " is no longer in agent inventory." << llendl;
  1338. }
  1339. // Find our window and close it if requested.
  1340. LLPreviewLSL* self = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", info->mItemUUID);
  1341. if (self)
  1342. {
  1343. getWindow()->decBusyCount();
  1344. self->mPendingUploads--;
  1345. if (self->mPendingUploads <= 0
  1346. && self->mCloseAfterSave)
  1347. {
  1348. self->closeFloater();
  1349. }
  1350. }
  1351. }
  1352. }
  1353. else
  1354. {
  1355. llwarns << "Problem saving script: " << status << llendl;
  1356. LLSD args;
  1357. args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
  1358. LLNotificationsUtil::add("SaveScriptFailReason", args);
  1359. }
  1360. delete info;
  1361. }
  1362. // static
  1363. void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
  1364. {
  1365. LLUUID* instance_uuid = (LLUUID*)user_data;
  1366. LLPreviewLSL* self = NULL;
  1367. if(instance_uuid)
  1368. {
  1369. self = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *instance_uuid);
  1370. }
  1371. if (0 == status)
  1372. {
  1373. if (self)
  1374. {
  1375. LLSD row;
  1376. row["columns"][0]["value"] = "Compile successful!";
  1377. row["columns"][0]["font"] = "SANSSERIF_SMALL";
  1378. self->mScriptEd->mErrorList->addElement(row);
  1379. // Find our window and close it if requested.
  1380. self->getWindow()->decBusyCount();
  1381. self->mPendingUploads--;
  1382. if (self->mPendingUploads <= 0
  1383. && self->mCloseAfterSave)
  1384. {
  1385. self->closeFloater();
  1386. }
  1387. }
  1388. }
  1389. else
  1390. {
  1391. llwarns << "Problem saving LSL Bytecode (Preview)" << llendl;
  1392. LLSD args;
  1393. args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
  1394. LLNotificationsUtil::add("SaveBytecodeFailReason", args);
  1395. }
  1396. delete instance_uuid;
  1397. }
  1398. // static
  1399. void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type,
  1400. void* user_data, S32 status, LLExtStat ext_status)
  1401. {
  1402. lldebugs << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid
  1403. << llendl;
  1404. LLUUID* item_uuid = (LLUUID*)user_data;
  1405. LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *item_uuid);
  1406. if( preview )
  1407. {
  1408. if(0 == status)
  1409. {
  1410. LLVFile file(vfs, asset_uuid, type);
  1411. S32 file_length = file.getSize();
  1412. std::vector<char> buffer(file_length+1);
  1413. file.read((U8*)&buffer[0], file_length);
  1414. // put a EOS at the end
  1415. buffer[file_length] = 0;
  1416. preview->mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), TRUE);
  1417. preview->mScriptEd->mEditor->makePristine();
  1418. LLInventoryItem* item = gInventory.getItem(*item_uuid);
  1419. BOOL is_modifiable = FALSE;
  1420. if(item
  1421. && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
  1422. GP_OBJECT_MANIPULATE))
  1423. {
  1424. is_modifiable = TRUE;
  1425. }
  1426. preview->mScriptEd->setEnableEditing(is_modifiable);
  1427. preview->mAssetStatus = PREVIEW_ASSET_LOADED;
  1428. }
  1429. else
  1430. {
  1431. LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
  1432. if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
  1433. LL_ERR_FILE_EMPTY == status)
  1434. {
  1435. LLNotificationsUtil::add("ScriptMissing");
  1436. }
  1437. else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
  1438. {
  1439. LLNotificationsUtil::add("ScriptNoPermissions");
  1440. }
  1441. else
  1442. {
  1443. LLNotificationsUtil::add("UnableToLoadScript");
  1444. }
  1445. preview->mAssetStatus = PREVIEW_ASSET_ERROR;
  1446. llwarns << "Problem loading script: " << status << llendl;
  1447. }
  1448. }
  1449. delete item_uuid;
  1450. }
  1451. /// ---------------------------------------------------------------------------
  1452. /// LLLiveLSLEditor
  1453. /// ---------------------------------------------------------------------------
  1454. //static
  1455. void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
  1456. {
  1457. LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata;
  1458. self->mScriptEd = new LLScriptEdCore(
  1459. self,
  1460. HELLO_LSL,
  1461. self->getHandle(),
  1462. &LLLiveLSLEditor::onLoad,
  1463. &LLLiveLSLEditor::onSave,
  1464. &LLLiveLSLEditor::onSearchReplace,
  1465. self,
  1466. 0);
  1467. return self->mScriptEd;
  1468. }
  1469. LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) :
  1470. LLScriptEdContainer(key),
  1471. mAskedForRunningInfo(FALSE),
  1472. mHaveRunningInfo(FALSE),
  1473. mCloseAfterSave(FALSE),
  1474. mPendingUploads(0),
  1475. mIsModifiable(FALSE),
  1476. mIsNew(false)
  1477. {
  1478. mFactoryMap["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
  1479. }
  1480. BOOL LLLiveLSLEditor::postBuild()
  1481. {
  1482. childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this);
  1483. getChildView("running")->setEnabled(FALSE);
  1484. childSetAction("Reset",&LLLiveLSLEditor::onReset,this);
  1485. getChildView("Reset")->setEnabled(TRUE);
  1486. mMonoCheckbox = getChild<LLCheckBoxCtrl>("mono");
  1487. childSetCommitCallback("mono", &LLLiveLSLEditor::onMonoCheckboxClicked, this);
  1488. getChildView("mono")->setEnabled(FALSE);
  1489. mScriptEd->mEditor->makePristine();
  1490. mScriptEd->mEditor->setFocus(TRUE);
  1491. return LLPreview::postBuild();
  1492. }
  1493. // virtual
  1494. void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id,
  1495. const LLUUID& item_id,
  1496. bool is_script_running)
  1497. {
  1498. lldebugs << "LSL Bytecode saved" << llendl;
  1499. mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
  1500. mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
  1501. closeIfNeeded();
  1502. }
  1503. // virtual
  1504. void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors)
  1505. {
  1506. lldebugs << "Compile failed!" << llendl;
  1507. for(LLSD::array_const_iterator line = compile_errors.beginArray();
  1508. line < compile_errors.endArray();
  1509. line++)
  1510. {
  1511. LLSD row;
  1512. std::string error_message = line->asString();
  1513. LLStringUtil::stripNonprintable(error_message);
  1514. row["columns"][0]["value"] = error_message;
  1515. // *TODO: change to "MONOSPACE" and change llfontgl.cpp?
  1516. row["columns"][0]["font"] = "OCRA";
  1517. mScriptEd->mErrorList->addElement(row);
  1518. }
  1519. mScriptEd->selectFirstError();
  1520. closeIfNeeded();
  1521. }
  1522. void LLLiveLSLEditor::loadAsset()
  1523. {
  1524. //llinfos << "LLLiveLSLEditor::loadAsset()" << llendl;
  1525. if(!mIsNew)
  1526. {
  1527. LLViewerObject* object = gObjectList.findObject(mObjectUUID);
  1528. if(object)
  1529. {
  1530. LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID));
  1531. if(item
  1532. && (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
  1533. || gAgent.isGodlike()))
  1534. {
  1535. mItem = new LLViewerInventoryItem(item);
  1536. //llinfos << "asset id " << mItem->getAssetUUID() << llendl;
  1537. }
  1538. if(!gAgent.isGodlike()
  1539. && (item
  1540. && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
  1541. || !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))))
  1542. {
  1543. mItem = new LLViewerInventoryItem(item);
  1544. mScriptEd->setScriptText(getString("not_allowed"), FALSE);
  1545. mScriptEd->mEditor->makePristine();
  1546. mScriptEd->enableSave(FALSE);
  1547. mAssetStatus = PREVIEW_ASSET_LOADED;
  1548. }
  1549. else if(item && mItem.notNull())
  1550. {
  1551. // request the text from the object
  1552. LLUUID* user_data = new LLUUID(mItemUUID); // ^ mObjectUUID
  1553. gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
  1554. gAgent.getID(),
  1555. gAgent.getSessionID(),
  1556. item->getPermissions().getOwner(),
  1557. object->getID(),
  1558. item->getUUID(),
  1559. item->getAssetUUID(),
  1560. item->getType(),
  1561. &LLLiveLSLEditor::onLoadComplete,
  1562. (void*)user_data,
  1563. TRUE);
  1564. LLMessageSystem* msg = gMessageSystem;
  1565. msg->newMessageFast(_PREHASH_GetScriptRunning);
  1566. msg->nextBlockFast(_PREHASH_Script);
  1567. msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID);
  1568. msg->addUUIDFast(_PREHASH_ItemID, mItemUUID);
  1569. msg->sendReliable(object->getRegion()->getHost());
  1570. mAskedForRunningInfo = TRUE;
  1571. mAssetStatus = PREVIEW_ASSET_LOADING;
  1572. }
  1573. else
  1574. {
  1575. mScriptEd->setScriptText(LLStringUtil::null, FALSE);
  1576. mScriptEd->mEditor->makePristine();
  1577. mAssetStatus = PREVIEW_ASSET_LOADED;
  1578. }
  1579. mIsModifiable = item && gAgent.allowOperation(PERM_MODIFY,
  1580. item->getPermissions(),
  1581. GP_OBJECT_MANIPULATE);
  1582. // This is commented out, because we don't completely
  1583. // handle script exports yet.
  1584. /*
  1585. // request the exports from the object
  1586. gMessageSystem->newMessage("GetScriptExports");
  1587. gMessageSystem->nextBlock("ScriptBlock");
  1588. gMessageSystem->addUUID("AgentID", gAgent.getID());
  1589. U32 local_id = object->getLocalID();
  1590. gMessageSystem->addData("LocalID", &local_id);
  1591. gMessageSystem->addUUID("ItemID", mItemUUID);
  1592. LLHost host(object->getRegion()->getIP(),
  1593. object->getRegion()->getPort());
  1594. gMessageSystem->sendReliable(host);
  1595. */
  1596. }
  1597. }
  1598. else
  1599. {
  1600. mScriptEd->setScriptText(std::string(HELLO_LSL), TRUE);
  1601. mScriptEd->enableSave(FALSE);
  1602. LLPermissions perm;
  1603. perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID());
  1604. perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
  1605. mItem = new LLViewerInventoryItem(mItemUUID,
  1606. mObjectUUID,
  1607. perm,
  1608. LLUUID::null,
  1609. LLAssetType::AT_LSL_TEXT,
  1610. LLInventoryType::IT_LSL,
  1611. DEFAULT_SCRIPT_NAME,
  1612. DEFAULT_SCRIPT_DESC,
  1613. LLSaleInfo::DEFAULT,
  1614. LLInventoryItemFlags::II_FLAGS_NONE,
  1615. time_corrected());
  1616. mAssetStatus = PREVIEW_ASSET_LOADED;
  1617. }
  1618. }
  1619. // static
  1620. void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,
  1621. LLAssetType::EType type,
  1622. void* user_data, S32 status, LLExtStat ext_status)
  1623. {
  1624. lldebugs << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id
  1625. << llendl;
  1626. LLUUID* xored_id = (LLUUID*)user_data;
  1627. LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", *xored_id);
  1628. if(instance )
  1629. {
  1630. if( LL_ERR_NOERR == status )
  1631. {
  1632. instance->loadScriptText(vfs, asset_id, type);
  1633. instance->mScriptEd->setEnableEditing(TRUE);
  1634. instance->mAssetStatus = PREVIEW_ASSET_LOADED;
  1635. }
  1636. else
  1637. {
  1638. LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
  1639. if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
  1640. LL_ERR_FILE_EMPTY == status)
  1641. {
  1642. LLNotificationsUtil::add("ScriptMissing");
  1643. }
  1644. else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
  1645. {
  1646. LLNotificationsUtil::add("ScriptNoPermissions");
  1647. }
  1648. else
  1649. {
  1650. LLNotificationsUtil::add("UnableToLoadScript");
  1651. }
  1652. instance->mAssetStatus = PREVIEW_ASSET_ERROR;
  1653. }
  1654. }
  1655. delete xored_id;
  1656. }
  1657. void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
  1658. {
  1659. LLVFile file(vfs, uuid, type);
  1660. S32 file_length = file.getSize();
  1661. std::vector<char> buffer(file_length + 1);
  1662. file.read((U8*)&buffer[0], file_length);
  1663. if (file.getLastBytesRead() != file_length ||
  1664. file_length <= 0)
  1665. {
  1666. llwarns << "Error reading " << uuid << ":" << type << llendl;
  1667. }
  1668. buffer[file_length] = '\0';
  1669. mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), TRUE);
  1670. mScriptEd->mEditor->makePristine();
  1671. }
  1672. void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata )
  1673. {
  1674. LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
  1675. LLViewerObject* object = gObjectList.findObject( self->mObjectUUID );
  1676. LLCheckBoxCtrl* runningCheckbox = self->getChild<LLCheckBoxCtrl>("running");
  1677. BOOL running = runningCheckbox->get();
  1678. //self->mRunningCheckbox->get();
  1679. if( object )
  1680. {
  1681. LLMessageSystem* msg = gMessageSystem;
  1682. msg->newMessageFast(_PREHASH_SetScriptRunning);
  1683. msg->nextBlockFast(_PREHASH_AgentData);
  1684. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  1685. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  1686. msg->nextBlockFast(_PREHASH_Script);
  1687. msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID);
  1688. msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID);
  1689. msg->addBOOLFast(_PREHASH_Running, running);
  1690. msg->sendReliable(object->getRegion()->getHost());
  1691. }
  1692. else
  1693. {
  1694. runningCheckbox->set(!running);
  1695. LLNotificationsUtil::add("CouldNotStartStopScript");
  1696. }
  1697. }
  1698. void LLLiveLSLEditor::onReset(void *userdata)
  1699. {
  1700. LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
  1701. LLViewerObject* object = gObjectList.findObject( self->mObjectUUID );
  1702. if(object)
  1703. {
  1704. LLMessageSystem* msg = gMessageSystem;
  1705. msg->newMessageFast(_PREHASH_ScriptReset);
  1706. msg->nextBlockFast(_PREHASH_AgentData);
  1707. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  1708. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  1709. msg->nextBlockFast(_PREHASH_Script);
  1710. msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID);
  1711. msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID);
  1712. msg->sendReliable(object->getRegion()->getHost());
  1713. }
  1714. else
  1715. {
  1716. LLNotificationsUtil::add("CouldNotStartStopScript");
  1717. }
  1718. }
  1719. void LLLiveLSLEditor::draw()
  1720. {
  1721. LLViewerObject* object = gObjectList.findObject(mObjectUUID);
  1722. LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running");
  1723. if(object && mAskedForRunningInfo && mHaveRunningInfo)
  1724. {
  1725. if(object->permAnyOwner())
  1726. {
  1727. runningCheckbox->setLabel(getString("script_running"));
  1728. runningCheckbox->setEnabled(TRUE);
  1729. if(object->permAnyOwner())
  1730. {
  1731. runningCheckbox->setLabel(getString("script_running"));
  1732. runningCheckbox->setEnabled(TRUE);
  1733. }
  1734. else
  1735. {
  1736. runningCheckbox->setLabel(getString("public_objects_can_not_run"));
  1737. runningCheckbox->setEnabled(FALSE);
  1738. // *FIX: Set it to false so that the ui is correct for
  1739. // a box that is released to public. It could be
  1740. // incorrect after a release/claim cycle, but will be
  1741. // correct after clicking on it.
  1742. runningCheckbox->set(FALSE);
  1743. mMonoCheckbox->set(FALSE);
  1744. }
  1745. }
  1746. else
  1747. {
  1748. runningCheckbox->setLabel(getString("public_objects_can_not_run"));
  1749. runningCheckbox->setEnabled(FALSE);
  1750. // *FIX: Set it to false so that the ui is correct for
  1751. // a box that is released to public. It could be
  1752. // incorrect after a release/claim cycle, but will be
  1753. // correct after clicking on it.
  1754. runningCheckbox->set(FALSE);
  1755. mMonoCheckbox->setEnabled(FALSE);
  1756. // object may have fallen out of range.
  1757. mHaveRunningInfo = FALSE;
  1758. }
  1759. }
  1760. else if(!object)
  1761. {
  1762. // HACK: Display this information in the title bar.
  1763. // Really ought to put in main window.
  1764. setTitle(LLTrans::getString("ObjectOutOfRange"));
  1765. runningCheckbox->setEnabled(FALSE);
  1766. // object may have fallen out of range.
  1767. mHaveRunningInfo = FALSE;
  1768. }
  1769. LLPreview::draw();
  1770. }
  1771. void LLLiveLSLEditor::onSearchReplace(void* userdata)
  1772. {
  1773. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  1774. LLScriptEdCore* sec = self->mScriptEd;
  1775. LLFloaterScriptSearch::show(sec);
  1776. }
  1777. struct LLLiveLSLSaveData
  1778. {
  1779. LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, BOOL active);
  1780. LLUUID mSaveObjectID;
  1781. LLPointer<LLViewerInventoryItem> mItem;
  1782. BOOL mActive;
  1783. };
  1784. LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id,
  1785. const LLViewerInventoryItem* item,
  1786. BOOL active) :
  1787. mSaveObjectID(id),
  1788. mActive(active)
  1789. {
  1790. llassert(item);
  1791. mItem = new LLViewerInventoryItem(item);
  1792. }
  1793. // virtual
  1794. void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)
  1795. {
  1796. LLViewerObject* object = gObjectList.findObject(mObjectUUID);
  1797. if(!object)
  1798. {
  1799. LLNotificationsUtil::add("SaveScriptFailObjectNotFound");
  1800. return;
  1801. }
  1802. if(mItem.isNull() || !mItem->isFinished())
  1803. {
  1804. // $NOTE: While the error message may not be exactly correct,
  1805. // it's pretty close.
  1806. LLNotificationsUtil::add("SaveScriptFailObjectNotFound");
  1807. return;
  1808. }
  1809. // get the latest info about it. We used to be losing the script
  1810. // name on save, because the viewer object version of the item,
  1811. // and the editor version would get out of synch. Here's a good
  1812. // place to synch them back up.
  1813. LLInventoryItem* inv_item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID));
  1814. if(inv_item)
  1815. {
  1816. mItem->copyItem(inv_item);
  1817. }
  1818. // Don't need to save if we're pristine
  1819. if(!mScriptEd->hasChanged())
  1820. {
  1821. return;
  1822. }
  1823. mPendingUploads = 0;
  1824. // save the script
  1825. mScriptEd->enableSave(FALSE);
  1826. mScriptEd->mEditor->makePristine();
  1827. mScriptEd->mErrorList->deleteAllItems();
  1828. // set up the save on the local machine.
  1829. mScriptEd->mEditor->makePristine();
  1830. LLTransactionID tid;
  1831. tid.generate();
  1832. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  1833. std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
  1834. std::string filename = llformat("%s.lsl", filepath.c_str());
  1835. mItem->setAssetUUID(asset_id);
  1836. mItem->setTransactionID(tid);
  1837. mScriptEd->writeToFile(filename);
  1838. if (sync)
  1839. {
  1840. mScriptEd->sync();
  1841. }
  1842. // save it out to asset server
  1843. std::string url = object->getRegion()->getCapability("UpdateScriptTask");
  1844. getWindow()->incBusyCount();
  1845. mPendingUploads++;
  1846. BOOL is_running = getChild<LLCheckBoxCtrl>( "running")->get();
  1847. if (!url.empty())
  1848. {
  1849. uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running);
  1850. }
  1851. else if (gAssetStorage)
  1852. {
  1853. uploadAssetLegacy(filename, object, tid, is_running);
  1854. }
  1855. }
  1856. void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,
  1857. const std::string& filename,
  1858. const LLUUID& task_id,
  1859. const LLUUID& item_id,
  1860. BOOL is_running)
  1861. {
  1862. llinfos << "Update Task Inventory via capability " << url << llendl;
  1863. LLSD body;
  1864. body["task_id"] = task_id;
  1865. body["item_id"] = item_id;
  1866. body["is_script_running"] = is_running;
  1867. body["target"] = monoChecked() ? "mono" : "lsl2";
  1868. LLHTTPClient::post(url, body,
  1869. new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));
  1870. }
  1871. void LLLiveLSLEditor::uploadAssetLegacy(const std::string& filename,
  1872. LLViewerObject* object,
  1873. const LLTransactionID& tid,
  1874. BOOL is_running)
  1875. {
  1876. LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectUUID,
  1877. mItem,
  1878. is_running);
  1879. gAssetStorage->storeAssetData(filename, tid,
  1880. LLAssetType::AT_LSL_TEXT,
  1881. &onSaveTextComplete,
  1882. (void*)data,
  1883. FALSE);
  1884. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  1885. std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
  1886. std::string dst_filename = llformat("%s.lso", filepath.c_str());
  1887. std::string err_filename = llformat("%s.out", filepath.c_str());
  1888. LLFILE *fp;
  1889. const BOOL compile_to_mono = FALSE;
  1890. if(!lscript_compile(filename.c_str(),
  1891. dst_filename.c_str(),
  1892. err_filename.c_str(),
  1893. compile_to_mono,
  1894. asset_id.asString().c_str(),
  1895. gAgent.isGodlike()))
  1896. {
  1897. // load the error file into the error scrolllist
  1898. llinfos << "Compile failed!" << llendl;
  1899. if(NULL != (fp = LLFile::fopen(err_filename, "r")))
  1900. {
  1901. char buffer[MAX_STRING]; /*Flawfinder: ignore*/
  1902. std::string line;
  1903. while(!feof(fp))
  1904. {
  1905. if (fgets(buffer, MAX_STRING, fp) == NULL)
  1906. {
  1907. buffer[0] = '\0';
  1908. }
  1909. if(feof(fp))
  1910. {
  1911. break;
  1912. }
  1913. else
  1914. {
  1915. line.assign(buffer);
  1916. LLStringUtil::stripNonprintable(line);
  1917. LLSD row;
  1918. row["columns"][0]["value"] = line;
  1919. row["columns"][0]["font"] = "OCRA";
  1920. mScriptEd->mErrorList->addElement(row);
  1921. }
  1922. }
  1923. fclose(fp);
  1924. mScriptEd->selectFirstError();
  1925. // don't set the asset id, because we want to save the
  1926. // script, even though the compile failed.
  1927. //mItem->setAssetUUID(LLUUID::null);
  1928. object->saveScript(mItem, FALSE, false);
  1929. dialog_refresh_all();
  1930. }
  1931. }
  1932. else
  1933. {
  1934. llinfos << "Compile worked!" << llendl;
  1935. mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessfulSaving"));
  1936. if(gAssetStorage)
  1937. {
  1938. llinfos << "LLLiveLSLEditor::saveAsset "
  1939. << mItem->getAssetUUID() << llendl;
  1940. getWindow()->incBusyCount();
  1941. mPendingUploads++;
  1942. LLLiveLSLSaveData* data = NULL;
  1943. data = new LLLiveLSLSaveData(mObjectUUID,
  1944. mItem,
  1945. is_running);
  1946. gAssetStorage->storeAssetData(dst_filename,
  1947. tid,
  1948. LLAssetType::AT_LSL_BYTECODE,
  1949. &LLLiveLSLEditor::onSaveBytecodeComplete,
  1950. (void*)data);
  1951. dialog_refresh_all();
  1952. }
  1953. }
  1954. // get rid of any temp files left lying around
  1955. LLFile::remove(filename);
  1956. LLFile::remove(err_filename);
  1957. LLFile::remove(dst_filename);
  1958. // If we successfully saved it, then we should be able to check/uncheck the running box!
  1959. LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running");
  1960. runningCheckbox->setLabel(getString("script_running"));
  1961. runningCheckbox->setEnabled(TRUE);
  1962. }
  1963. void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
  1964. {
  1965. LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
  1966. if (status)
  1967. {
  1968. llwarns << "Unable to save text for a script." << llendl;
  1969. LLSD args;
  1970. args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
  1971. LLNotificationsUtil::add("CompileQueueSaveText", args);
  1972. }
  1973. else
  1974. {
  1975. LLLiveLSLEditor* self = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", data->mItem->getUUID()); // ^ data->mSaveObjectID
  1976. if (self)
  1977. {
  1978. self->getWindow()->decBusyCount();
  1979. self->mPendingUploads--;
  1980. if (self->mPendingUploads <= 0
  1981. && self->mCloseAfterSave)
  1982. {
  1983. self->closeFloater();
  1984. }
  1985. }
  1986. }
  1987. delete data;
  1988. data = NULL;
  1989. }
  1990. void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
  1991. {
  1992. LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
  1993. if(!data)
  1994. return;
  1995. if(0 ==status)
  1996. {
  1997. llinfos << "LSL Bytecode saved" << llendl;
  1998. LLLiveLSLEditor* self = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", data->mItem->getUUID()); // ^ data->mSaveObjectID
  1999. if (self)
  2000. {
  2001. // Tell the user that the compile worked.
  2002. self->mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
  2003. // close the window if this completes both uploads
  2004. self->getWindow()->decBusyCount();
  2005. self->mPendingUploads--;
  2006. if (self->mPendingUploads <= 0
  2007. && self->mCloseAfterSave)
  2008. {
  2009. self->closeFloater();
  2010. }
  2011. }
  2012. LLViewerObject* object = gObjectList.findObject(data->mSaveObjectID);
  2013. if(object)
  2014. {
  2015. object->saveScript(data->mItem, data->mActive, false);
  2016. dialog_refresh_all();
  2017. //LLToolDragAndDrop::dropScript(object, ids->first,
  2018. // LLAssetType::AT_LSL_TEXT, FALSE);
  2019. }
  2020. }
  2021. else
  2022. {
  2023. llinfos << "Problem saving LSL Bytecode (Live Editor)" << llendl;
  2024. llwarns << "Unable to save a compiled script." << llendl;
  2025. LLSD args;
  2026. args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
  2027. LLNotificationsUtil::add("CompileQueueSaveBytecode", args);
  2028. }
  2029. std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_uuid.asString());
  2030. std::string dst_filename = llformat("%s.lso", filepath.c_str());
  2031. LLFile::remove(dst_filename);
  2032. delete data;
  2033. }
  2034. BOOL LLLiveLSLEditor::canClose()
  2035. {
  2036. return (mScriptEd->canClose());
  2037. }
  2038. void LLLiveLSLEditor::closeIfNeeded()
  2039. {
  2040. getWindow()->decBusyCount();
  2041. mPendingUploads--;
  2042. if (mPendingUploads <= 0 && mCloseAfterSave)
  2043. {
  2044. closeFloater();
  2045. }
  2046. }
  2047. // static
  2048. void LLLiveLSLEditor::onLoad(void* userdata)
  2049. {
  2050. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  2051. self->loadAsset();
  2052. }
  2053. // static
  2054. void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save)
  2055. {
  2056. LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
  2057. self->mCloseAfterSave = close_after_save;
  2058. self->saveIfNeeded();
  2059. }
  2060. // stati