PageRenderTime 146ms CodeModel.GetById 15ms app.highlight 115ms RepoModel.GetById 3ms 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

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

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