PageRenderTime 363ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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. mScriptE

Large files files are truncated, but you can click here to view the full file