diff options
Diffstat (limited to 'indra/newview/llpreviewscript.cpp')
| -rw-r--r-- | indra/newview/llpreviewscript.cpp | 4982 | 
1 files changed, 2491 insertions, 2491 deletions
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 4be3f6c742..202b34a59f 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1,2491 +1,2491 @@ -/**  - * @file llpreviewscript.cpp - * @brief LLPreviewScript class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llpreviewscript.h" - -#include "llassetstorage.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "lldir.h" -#include "llexternaleditor.h" -#include "llfilepicker.h" -#include "llfloaterreg.h" -#include "llinventorydefines.h" -#include "llinventorymodel.h" -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmd5.h" -#include "llhelp.h" -#include "llnotificationsutil.h" -#include "llresmgr.h" -#include "llscrollbar.h" -#include "llscrollcontainer.h" -#include "llscrolllistctrl.h" -#include "llscrolllistitem.h" -#include "llscrolllistcell.h" -#include "llsdserialize.h" -#include "llslider.h" -#include "lltooldraganddrop.h" -#include "llfilesystem.h" - -#include "llagent.h" -#include "llmenugl.h" -#include "roles_constants.h" -#include "llselectmgr.h" -#include "llviewerinventory.h" -#include "llviewermenu.h" -#include "llviewermenufile.h" // LLFilePickerReplyThread -#include "llviewerobject.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llkeyboard.h" -#include "llscrollcontainer.h" -#include "llcheckboxctrl.h" -#include "llscripteditor.h" -#include "llselectmgr.h" -#include "lltooldraganddrop.h" -#include "llscrolllistctrl.h" -#include "lltextbox.h" -#include "llslider.h" -#include "lldir.h" -#include "llcombobox.h" -#include "llviewerstats.h" -#include "llviewerwindow.h" -#include "lluictrlfactory.h" -#include "llmediactrl.h" -#include "lluictrlfactory.h" -#include "lltrans.h" -#include "llviewercontrol.h" -#include "llappviewer.h" -#include "llfloatergotoline.h" -#include "llexperiencecache.h" -#include "llfloaterexperienceprofile.h" -#include "llviewerassetupload.h" -#include "lltoggleablemenu.h" -#include "llmenubutton.h" -#include "llinventoryfunctions.h" - -const std::string HELLO_LSL = -	"default\n" -	"{\n" -	"	state_entry()\n" -	"	{\n" -	"		llSay(0, \"Hello, Avatar!\");\n" -	"	}\n" -	"\n" -	"	touch_start(integer total_number)\n" -	"	{\n" -	"		llSay(0, \"Touched.\");\n" -	"	}\n" -	"}\n"; -const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal"; - -const std::string DEFAULT_SCRIPT_NAME = "New Script"; // *TODO:Translate? -const std::string DEFAULT_SCRIPT_DESC = "(No Description)"; // *TODO:Translate? - -// Description and header information -const S32 MAX_HISTORY_COUNT = 10; -const F32 LIVE_HELP_REFRESH_TIME = 1.f; - -static bool have_script_upload_cap(LLUUID& object_id) -{ -	LLViewerObject* object = gObjectList.findObject(object_id); -	return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty()); -} - -/// --------------------------------------------------------------------------- -/// LLLiveLSLFile -/// --------------------------------------------------------------------------- - -LLLiveLSLFile::LLLiveLSLFile(std::string file_path, change_callback_t change_cb) -:	mOnChangeCallback(change_cb) -,	mIgnoreNextUpdate(false) -,	LLLiveFile(file_path, 1.0) -{ -	llassert(mOnChangeCallback); -} - -LLLiveLSLFile::~LLLiveLSLFile() -{ -	LLFile::remove(filename()); -} - -bool LLLiveLSLFile::loadFile() -{ -	if (mIgnoreNextUpdate) -	{ -		mIgnoreNextUpdate = false; -		return true; -	} - -	return mOnChangeCallback(filename()); -} - -/// --------------------------------------------------------------------------- -/// LLFloaterScriptSearch -/// --------------------------------------------------------------------------- -class LLFloaterScriptSearch : public LLFloater -{ -public: -	LLFloaterScriptSearch(LLScriptEdCore* editor_core); -	~LLFloaterScriptSearch(); - -	/*virtual*/	bool	postBuild(); -	static void show(LLScriptEdCore* editor_core); -	static void onBtnSearch(void* userdata); -	void handleBtnSearch(); - -	static void onBtnReplace(void* userdata); -	void handleBtnReplace(); - -	static void onBtnReplaceAll(void* userdata); -	void handleBtnReplaceAll(); - -	LLScriptEdCore* getEditorCore() { return mEditorCore; } -	static LLFloaterScriptSearch* getInstance() { return sInstance; } - -	virtual bool hasAccelerators() const; -	virtual bool handleKeyHere(KEY key, MASK mask); - -private: - -	LLScriptEdCore* mEditorCore; -	static LLFloaterScriptSearch*	sInstance; - -protected: -	LLLineEditor*			mSearchBox; -	LLLineEditor*			mReplaceBox; -		void onSearchBoxCommit(); -}; - -LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL; - -LLFloaterScriptSearch::LLFloaterScriptSearch(LLScriptEdCore* editor_core) -:	LLFloater(LLSD()), -	mSearchBox(NULL), -	mReplaceBox(NULL), -	mEditorCore(editor_core) -{ -	buildFromFile("floater_script_search.xml"); - -	sInstance = this; -	 -	// find floater in which script panel is embedded -	LLView* viewp = (LLView*)editor_core; -	while(viewp) -	{ -		LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp); -		if (floaterp) -		{ -			floaterp->addDependentFloater(this); -			break; -		} -		viewp = viewp->getParent(); -	} -} - -bool LLFloaterScriptSearch::postBuild() -{ -	mReplaceBox = getChild<LLLineEditor>("replace_text"); -	mSearchBox = getChild<LLLineEditor>("search_text"); -	mSearchBox->setCommitCallback(boost::bind(&LLFloaterScriptSearch::onSearchBoxCommit, this)); -	mSearchBox->setCommitOnFocusLost(false); -	childSetAction("search_btn", onBtnSearch,this); -	childSetAction("replace_btn", onBtnReplace,this); -	childSetAction("replace_all_btn", onBtnReplaceAll,this); - -	setDefaultBtn("search_btn"); - -	return true; -} - -//static  -void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core) -{ -	LLSD::String search_text; -	LLSD::String replace_text; -	if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core) -	{ -		search_text=sInstance->mSearchBox->getValue().asString(); -		replace_text=sInstance->mReplaceBox->getValue().asString(); -		sInstance->closeFloater(); -		delete sInstance; -	} - -	if (!sInstance) -	{ -		// sInstance will be assigned in the constructor. -		new LLFloaterScriptSearch(editor_core); -		sInstance->mSearchBox->setValue(search_text); -		sInstance->mReplaceBox->setValue(replace_text); -	} - -	sInstance->openFloater(); -} - -LLFloaterScriptSearch::~LLFloaterScriptSearch() -{ -	sInstance = NULL; -} - -// static  -void LLFloaterScriptSearch::onBtnSearch(void *userdata) -{ -	LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; -	self->handleBtnSearch(); -} - -void LLFloaterScriptSearch::handleBtnSearch() -{ -	LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text"); -	mEditorCore->mEditor->selectNext(mSearchBox->getValue().asString(), caseChk->get()); -} - -// static  -void LLFloaterScriptSearch::onBtnReplace(void *userdata) -{ -	LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; -	self->handleBtnReplace(); -} - -void LLFloaterScriptSearch::handleBtnReplace() -{ -	LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text"); -	mEditorCore->mEditor->replaceText(mSearchBox->getValue().asString(), mReplaceBox->getValue().asString(), caseChk->get()); -} - -// static  -void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata) -{ -	LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; -	self->handleBtnReplaceAll(); -} - -void LLFloaterScriptSearch::handleBtnReplaceAll() -{ -	LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text"); -	mEditorCore->mEditor->replaceTextAll(mSearchBox->getValue().asString(), mReplaceBox->getValue().asString(), caseChk->get()); -} - -bool LLFloaterScriptSearch::hasAccelerators() const -{ -	if (mEditorCore) -	{ -		return mEditorCore->hasAccelerators(); -	} -	return false; -} - -bool LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask) -{ -	if (mEditorCore) -	{ -		bool handled = mEditorCore->handleKeyHere(key, mask); -		if (!handled) -		{ -			LLFloater::handleKeyHere(key, mask); -		} -	} - -	return false; -} - -void LLFloaterScriptSearch::onSearchBoxCommit() -{ -	if (mEditorCore && mEditorCore->mEditor) -	{ -		LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text"); -		mEditorCore->mEditor->selectNext(mSearchBox->getValue().asString(), caseChk->get()); -	} -} - -/// --------------------------------------------------------------------------- - -class LLScriptMovedObserver : public LLInventoryObserver -{ -  public: -    LLScriptMovedObserver(LLPreviewLSL *floater) : mPreview(floater) { gInventory.addObserver(this); } -    virtual ~LLScriptMovedObserver() { gInventory.removeObserver(this); } -    virtual void changed(U32 mask); - -  private: -    LLPreviewLSL *mPreview; -}; - -void LLScriptMovedObserver::changed(U32 mask) -{ -    const std::set<LLUUID> &mChangedItemIDs = gInventory.getChangedIDs(); -    std::set<LLUUID>::const_iterator it; - -    const LLUUID &item_id = mPreview->getScriptID(); - -    for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) -    { -        if (*it == item_id) -        { -            if ((mask & (LLInventoryObserver::STRUCTURE)) != 0) -            { -                mPreview->setDirty(); -            } -        } -    } -} - -/// --------------------------------------------------------------------------- -/// LLScriptEdCore -/// --------------------------------------------------------------------------- - -struct LLSECKeywordCompare -{ -	bool operator()(const std::string& lhs, const std::string& rhs) -	{ -		return (LLStringUtil::compareDictInsensitive( lhs, rhs ) < 0 ); -	} -}; - -LLScriptEdCore::LLScriptEdCore( -	LLScriptEdContainer* container, -	const std::string& sample, -	const LLHandle<LLFloater>& floater_handle, -	void (*load_callback)(void*), -	void (*save_callback)(void*, bool), -	void (*search_replace_callback) (void* userdata), -	void* userdata, -	bool live, -	S32 bottom_pad) -	: -	LLPanel(), -	mSampleText(sample), -	mEditor( NULL ), -	mLoadCallback( load_callback ), -	mSaveCallback( save_callback ), -	mSearchReplaceCallback( search_replace_callback ), -	mUserdata( userdata ), -	mForceClose( false ), -	mLastHelpToken(NULL), -	mLiveHelpHistorySize(0), -	mEnableSave(false), -	mLiveFile(NULL), -	mLive(live), -	mContainer(container), -	mHasScriptData(false), -	mScriptRemoved(false), -	mSaveDialogShown(false) -{ -	setFollowsAll(); -	setBorderVisible(false); - -	setXMLFilename("panel_script_ed.xml"); -	llassert_always(mContainer != NULL); -} - -LLScriptEdCore::~LLScriptEdCore() -{ -	deleteBridges(); - -	// If the search window is up for this editor, close it. -	LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance(); -	if (script_search && script_search->getEditorCore() == this) -	{ -		script_search->closeFloater(); -		delete script_search; -	} - -	delete mLiveFile; -	if (mSyntaxIDConnection.connected()) -	{ -		mSyntaxIDConnection.disconnect(); -	} -} - -void LLLiveLSLEditor::experienceChanged() -{ -	if(mScriptEd->getAssociatedExperience() != mExperiences->getSelectedValue().asUUID()) -	{ -		mScriptEd->enableSave(getIsModifiable()); -		//getChildView("Save_btn")->setEnabled(true); -		mScriptEd->setAssociatedExperience(mExperiences->getSelectedValue().asUUID()); -		updateExperiencePanel(); -	} -} - -void LLLiveLSLEditor::onViewProfile( LLUICtrl *ui, void* userdata ) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - -	LLUUID id; -	if(self->mExperienceEnabled->get()) -	{ -		id=self->mScriptEd->getAssociatedExperience(); -		if(id.notNull()) -		{ -			 LLFloaterReg::showInstance("experience_profile", id, true); -		} -	} - -} - -void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata ) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - -	LLUUID id; -	if(self->mExperienceEnabled->get()) -	{ -		if(self->mScriptEd->getAssociatedExperience().isNull()) -		{ -			id=self->mExperienceIds.beginArray()->asUUID(); -		} -	} - -	if(id != self->mScriptEd->getAssociatedExperience()) -	{ -		self->mScriptEd->enableSave(self->getIsModifiable()); -	} -	self->mScriptEd->setAssociatedExperience(id); - -	self->updateExperiencePanel(); -} - -bool LLScriptEdCore::postBuild() -{ -	mErrorList = getChild<LLScrollListCtrl>("lsl errors"); - -	mFunctions = getChild<LLComboBox>("Insert..."); - -	childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); - -	mEditor = getChild<LLScriptEditor>("Script Editor"); - -	childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); -	childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,false)); -	childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this)); - -	initMenu(); - -	mSyntaxIDConnection = LLSyntaxIdLSL::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this)); - -	// Intialise keyword highlighting for the current simulator's version of LSL -	LLSyntaxIdLSL::getInstance()->initialize(); -	processKeywords(); - -    mCommitCallbackRegistrar.add("FontSize.Set", boost::bind(&LLScriptEdCore::onChangeFontSize, this, _2)); -    mEnableCallbackRegistrar.add("FontSize.Check", boost::bind(&LLScriptEdCore::isFontSizeChecked, this, _2)); - -    LLToggleableMenu *context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( -        "menu_lsl_font_size.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); -    getChild<LLMenuButton>("font_btn")->setMenu(context_menu, LLMenuButton::MP_BOTTOM_LEFT, true); - -	return true; -} - -void LLScriptEdCore::processKeywords() -{ -	LL_DEBUGS("SyntaxLSL") << "Processing keywords" << LL_ENDL; -	mEditor->clearSegments(); -	mEditor->initKeywords(); -	mEditor->loadKeywords(); -	 -	string_vec_t primary_keywords; -	string_vec_t secondary_keywords; -	LLKeywordToken *token; -	LLKeywords::keyword_iterator_t token_it; -	for (token_it = mEditor->keywordsBegin(); token_it != mEditor->keywordsEnd(); ++token_it) -	{ -		token = token_it->second; -		if (token->getType() == LLKeywordToken::TT_FUNCTION) -		{ -			primary_keywords.push_back( wstring_to_utf8str(token->getToken()) ); -		} -		else -		{ -			secondary_keywords.push_back( wstring_to_utf8str(token->getToken()) ); -		} -	} -	for (string_vec_t::const_iterator iter = primary_keywords.begin(); -		 iter!= primary_keywords.end(); ++iter) -	{ -		mFunctions->add(*iter); -	} -	for (string_vec_t::const_iterator iter = secondary_keywords.begin(); -		 iter!= secondary_keywords.end(); ++iter) -	{ -		mFunctions->add(*iter); -	} -} - -void LLScriptEdCore::initMenu() -{ -	// *TODO: Skinning - make these callbacks data driven -	LLMenuItemCallGL* menuItem; -	 -	menuItem = getChild<LLMenuItemCallGL>("Save"); -	menuItem->setClickCallback(boost::bind(&LLScriptEdCore::doSave, this, false)); -	menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this)); -	 -	menuItem = getChild<LLMenuItemCallGL>("Revert All Changes"); -	menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnUndoChanges, this)); -	menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this)); - -	menuItem = getChild<LLMenuItemCallGL>("Undo"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::undo, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canUndo, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Redo"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::redo, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canRedo, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Cut"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::cut, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCut, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Copy"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::copy, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCopy, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Paste"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::paste, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canPaste, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Select All"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::selectAll, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canSelectAll, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Deselect"); -	menuItem->setClickCallback(boost::bind(&LLTextEditor::deselect, mEditor)); -	menuItem->setEnableCallback(boost::bind(&LLTextEditor::canDeselect, mEditor)); - -	menuItem = getChild<LLMenuItemCallGL>("Search / Replace..."); -	menuItem->setClickCallback(boost::bind(&LLFloaterScriptSearch::show, this)); - -	menuItem = getChild<LLMenuItemCallGL>("Go to line..."); -	menuItem->setClickCallback(boost::bind(&LLFloaterGotoLine::show, this)); - -	menuItem = getChild<LLMenuItemCallGL>("Keyword Help..."); -	menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnDynamicHelp, this)); - -	menuItem = getChild<LLMenuItemCallGL>("LoadFromFile"); -	menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnLoadFromFile, this)); -	menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableLoadFromFileMenu, this)); - -	menuItem = getChild<LLMenuItemCallGL>("SaveToFile"); -	menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnSaveToFile, this)); -	menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableSaveToFileMenu, this)); -} - -void LLScriptEdCore::setScriptText(const std::string& text, bool is_valid) -{ -	if (mEditor) -	{ -		mEditor->setText(text); -		mHasScriptData = is_valid; -	} -} - -void LLScriptEdCore::makeEditorPristine() -{ -	if (mEditor) -	{ -		mEditor->makePristine(); -	} -} - -bool LLScriptEdCore::loadScriptText(const std::string& filename) -{ -	if (filename.empty()) -	{ -		LL_WARNS() << "Empty file name" << LL_ENDL; -		return false; -	} - -	LLFILE* file = LLFile::fopen(filename, "rb");		/*Flawfinder: ignore*/ -	if (!file) -	{ -		LL_WARNS() << "Error opening " << filename << LL_ENDL; -		return false; -	} - -	// read in the whole file -	fseek(file, 0L, SEEK_END); -	size_t file_length = (size_t) ftell(file); -	fseek(file, 0L, SEEK_SET); -	char* buffer = new char[file_length+1]; -	size_t nread = fread(buffer, 1, file_length, file); -	if (nread < file_length) -	{ -		LL_WARNS() << "Short read" << LL_ENDL; -	} -	buffer[nread] = '\0'; -	fclose(file); - -    std::string text = std::string(buffer); -    LLStringUtil::replaceTabsWithSpaces(text, LLTextEditor::spacesPerTab()); - -    mEditor->setText(text); -	delete[] buffer; - -	return true; -} - -bool LLScriptEdCore::writeToFile(const std::string& filename) -{ -	LLFILE* fp = LLFile::fopen(filename, "wb"); -	if (!fp) -	{ -		LL_WARNS() << "Unable to write to " << filename << LL_ENDL; - -		LLSD row; -		row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?"; -		row["columns"][0]["font"] = "SANSSERIF_SMALL"; -		mErrorList->addElement(row); -		return false; -	} - -	std::string utf8text = mEditor->getText(); - -	// Special case for a completely empty script - stuff in one space so it can store properly.  See SL-46889 -	if (utf8text.size() == 0) -	{ -		utf8text = " "; -	} - -	fputs(utf8text.c_str(), fp); -	fclose(fp); -	return true; -} - -void LLScriptEdCore::sync() -{ -    // Sync with external editor. -    if (mLiveFile) -    { -        std::string tmp_file = mLiveFile->filename(); -        llstat s; -        if (LLFile::stat(tmp_file, &s) == 0) // file exists -        { -            mLiveFile->ignoreNextUpdate(); -            writeToFile(tmp_file); -        } -    } -} - -bool LLScriptEdCore::hasChanged() -{ -	if (!mEditor) return false; - -	return ((!mEditor->isPristine() || mEnableSave) && mHasScriptData); -} - -void LLScriptEdCore::draw() -{ -	bool script_changed	= hasChanged(); -	getChildView("Save_btn")->setEnabled(script_changed && !mScriptRemoved); - -	if( mEditor->hasFocus() ) -	{ -		S32 line = 0; -		S32 column = 0; -		mEditor->getCurrentLineAndColumn( &line, &column, false );  // don't include wordwrap -		LLStringUtil::format_map_t args; -		std::string cursor_pos; -		args["[LINE]"] = llformat ("%d", line); -		args["[COLUMN]"] = llformat ("%d", column); -		cursor_pos = LLTrans::getString("CursorPos", args); -		getChild<LLUICtrl>("line_col")->setValue(cursor_pos); -	} -	else -	{ -		getChild<LLUICtrl>("line_col")->setValue(LLStringUtil::null); -	} - -	updateDynamicHelp(); - -	LLPanel::draw(); -} - -void LLScriptEdCore::updateDynamicHelp(bool immediate) -{ -	LLFloater* help_floater = mLiveHelpHandle.get(); -	if (!help_floater) return; - -	// update back and forward buttons -	LLButton* fwd_button = help_floater->getChild<LLButton>("fwd_btn"); -	LLButton* back_button = help_floater->getChild<LLButton>("back_btn"); -	LLMediaCtrl* browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -	back_button->setEnabled(browser->canNavigateBack()); -	fwd_button->setEnabled(browser->canNavigateForward()); - -	if (!immediate && !gSavedSettings.getBOOL("ScriptHelpFollowsCursor")) -	{ -		return; -	} - -	LLTextSegmentPtr segment = NULL; -	std::vector<LLTextSegmentPtr> selected_segments; -	mEditor->getSelectedSegments(selected_segments); -	LLKeywordToken* token; -	// try segments in selection range first -	std::vector<LLTextSegmentPtr>::iterator segment_iter; -	for (segment_iter = selected_segments.begin(); segment_iter != selected_segments.end(); ++segment_iter) -	{ -		token = (*segment_iter)->getToken(); -		if(token && isKeyword(token)) -		{ -			segment = *segment_iter; -			break; -		} -	} - -	// then try previous segment in case we just typed it -	if (!segment) -	{ -		const LLTextSegmentPtr test_segment = mEditor->getPreviousSegment(); -		token = test_segment->getToken(); -		if(token && isKeyword(token)) -		{ -			segment = test_segment; -		} -	} - -	if (segment) -	{ -		if (segment->getToken() != mLastHelpToken) -		{ -			mLastHelpToken = segment->getToken(); -			mLiveHelpTimer.start(); -		} -		if (immediate || (mLiveHelpTimer.getStarted() && mLiveHelpTimer.getElapsedTimeF32() > LIVE_HELP_REFRESH_TIME)) -		{ -			// Use Wtext since segment's start/end are made for wstring and will -			// result in a shift for case of multi-byte symbols inside std::string. -			LLWString segment_text = mEditor->getWText().substr(segment->getStart(), segment->getEnd() - segment->getStart()); -			std::string help_string = wstring_to_utf8str(segment_text); -			setHelpPage(help_string); -			mLiveHelpTimer.stop(); -		} -	} -	else -	{ -		if (immediate) -		{ -			setHelpPage(LLStringUtil::null); -		} -	} -} - -bool LLScriptEdCore::isKeyword(LLKeywordToken* token) -{ -	switch(token->getType()) -	{ -		case LLKeywordToken::TT_CONSTANT: -		case LLKeywordToken::TT_CONTROL: -		case LLKeywordToken::TT_EVENT: -		case LLKeywordToken::TT_FUNCTION: -		case LLKeywordToken::TT_SECTION: -		case LLKeywordToken::TT_TYPE: -		case LLKeywordToken::TT_WORD: -			return true; - -		default: -			return false; -	} -} - -void LLScriptEdCore::setHelpPage(const std::string& help_string) -{ -	LLFloater* help_floater = mLiveHelpHandle.get(); -	if (!help_floater) return; -	 -	LLMediaCtrl* web_browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -	if (!web_browser) return; - -	LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo"); -	if (!history_combo) return; - -	LLUIString url_string = gSavedSettings.getString("LSLHelpURL"); - -	url_string.setArg("[LSL_STRING]", help_string.empty() ? HELP_LSL_PORTAL_TOPIC : help_string); - -	addHelpItemToHistory(help_string); - -	web_browser->navigateTo(url_string); - -} - - -void LLScriptEdCore::addHelpItemToHistory(const std::string& help_string) -{ -	if (help_string.empty()) return; - -	LLFloater* help_floater = mLiveHelpHandle.get(); -	if (!help_floater) return; - -	LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo"); -	if (!history_combo) return; - -	// separate history items from full item list -	if (mLiveHelpHistorySize == 0) -	{ -		history_combo->addSeparator(ADD_TOP); -	} -	// delete all history items over history limit -	while(mLiveHelpHistorySize > MAX_HISTORY_COUNT - 1) -	{ -		history_combo->remove(mLiveHelpHistorySize - 1); -		mLiveHelpHistorySize--; -	} - -	history_combo->setSimple(help_string); -	S32 index = history_combo->getCurrentIndex(); - -	// if help string exists in the combo box -	if (index >= 0) -	{ -		S32 cur_index = history_combo->getCurrentIndex(); -		if (cur_index < mLiveHelpHistorySize) -		{ -			// item found in history, bubble up to top -			history_combo->remove(history_combo->getCurrentIndex()); -			mLiveHelpHistorySize--; -		} -	} -	history_combo->add(help_string, LLSD(help_string), ADD_TOP); -	history_combo->selectFirstItem(); -	mLiveHelpHistorySize++; -} - -bool LLScriptEdCore::canClose() -{ -	if(mForceClose || !hasChanged() || mScriptRemoved) -	{ -		return true; -	} -	else -	{ -		if(!mSaveDialogShown) -		{ -			mSaveDialogShown = true; -			// Bring up view-modal dialog: Save changes? Yes, No, Cancel -			LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleSaveChangesDialog, this, _1, _2)); -		} -		return false; -	} -} - -void LLScriptEdCore::setEnableEditing(bool enable) -{ -	mEditor->setEnabled(enable); -	getChildView("Edit_btn")->setEnabled(enable); -} - -bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLSD& response ) -{ -	mSaveDialogShown = false; -	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); -	switch( option ) -	{ -	case 0:  // "Yes" -		// close after saving -			doSave( true ); -		break; - -	case 1:  // "No" -		mForceClose = true; -		// This will close immediately because mForceClose is true, so we won't -		// infinite loop with these dialogs. JC -		((LLFloater*) getParent())->closeFloater(); -		break; - -	case 2: // "Cancel" -	default: -		// If we were quitting, we didn't really mean it. -		LLAppViewer::instance()->abortQuit(); -		break; -	} -	return false; -} - -void LLScriptEdCore::onBtnDynamicHelp() -{ -	LLFloater* live_help_floater = mLiveHelpHandle.get(); -	if (!live_help_floater) -	{ -		live_help_floater = new LLFloater(LLSD()); -		live_help_floater->buildFromFile("floater_lsl_guide.xml"); -		LLFloater* parent = dynamic_cast<LLFloater*>(getParent()); -		llassert(parent); -		if (parent) -			parent->addDependentFloater(live_help_floater, true); -		live_help_floater->childSetCommitCallback("lock_check", onCheckLock, this); -		live_help_floater->getChild<LLUICtrl>("lock_check")->setValue(gSavedSettings.getBOOL("ScriptHelpFollowsCursor")); -		live_help_floater->childSetCommitCallback("history_combo", onHelpComboCommit, this); -		live_help_floater->childSetAction("back_btn", onClickBack, this); -		live_help_floater->childSetAction("fwd_btn", onClickForward, this); - -		LLMediaCtrl* browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -		browser->setAlwaysRefresh(true); - -		LLComboBox* help_combo = live_help_floater->getChild<LLComboBox>("history_combo"); -		LLKeywordToken *token; -		LLKeywords::keyword_iterator_t token_it; -		for (token_it = mEditor->keywordsBegin();  -			 token_it != mEditor->keywordsEnd();  -			 ++token_it) -		{ -			token = token_it->second; -			help_combo->add(wstring_to_utf8str(token->getToken())); -		} -		help_combo->sortByName(); - -		// re-initialize help variables -		mLastHelpToken = NULL; -		mLiveHelpHandle = live_help_floater->getHandle(); -		mLiveHelpHistorySize = 0; -	} - -	bool visible = true; -	bool take_focus = true; -	live_help_floater->setVisible(visible); -	live_help_floater->setFrontmost(take_focus); - -	updateDynamicHelp(true); -} - -//static  -void LLScriptEdCore::onClickBack(void* userdata) -{ -	LLScriptEdCore* corep = (LLScriptEdCore*)userdata; -	LLFloater* live_help_floater = corep->mLiveHelpHandle.get(); -	if (live_help_floater) -	{ -		LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -		if (browserp) -		{ -			browserp->navigateBack(); -		} -	} -} - -//static  -void LLScriptEdCore::onClickForward(void* userdata) -{ -	LLScriptEdCore* corep = (LLScriptEdCore*)userdata; -	LLFloater* live_help_floater = corep->mLiveHelpHandle.get(); -	if (live_help_floater) -	{ -		LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -		if (browserp) -		{ -			browserp->navigateForward(); -		} -	} -} - -// static -void LLScriptEdCore::onCheckLock(LLUICtrl* ctrl, void* userdata) -{ -	LLScriptEdCore* corep = (LLScriptEdCore*)userdata; - -	// clear out token any time we lock the frame, so we will refresh web page immediately when unlocked -	gSavedSettings.setBOOL("ScriptHelpFollowsCursor", ctrl->getValue().asBoolean()); - -	corep->mLastHelpToken = NULL; -} - -// static  -void LLScriptEdCore::onBtnInsertSample(void* userdata) -{ -	LLScriptEdCore* self = (LLScriptEdCore*) userdata; - -	// Insert sample code -	self->mEditor->selectAll(); -	self->mEditor->cut(); -	self->mEditor->insertText(self->mSampleText); -} - -// static  -void LLScriptEdCore::onHelpComboCommit(LLUICtrl* ctrl, void* userdata) -{ -	LLScriptEdCore* corep = (LLScriptEdCore*)userdata; - -	LLFloater* live_help_floater = corep->mLiveHelpHandle.get(); -	if (live_help_floater) -	{ -		std::string help_string = ctrl->getValue().asString(); - -		corep->addHelpItemToHistory(help_string); - -		LLMediaCtrl* web_browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); -		LLUIString url_string = gSavedSettings.getString("LSLHelpURL"); -		url_string.setArg("[LSL_STRING]", help_string); -		web_browser->navigateTo(url_string); -	} -} - -// static  -void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata) -{ -	LLScriptEdCore* self = (LLScriptEdCore*) userdata; - -	// Insert sample code -	if(self->mEditor->getEnabled()) -	{ -		self->mEditor->insertText(self->mFunctions->getSimple()); -	} -	self->mEditor->setFocus(true); -	self->setHelpPage(self->mFunctions->getSimple()); -} - -void LLScriptEdCore::doSave( bool close_after_save ) -{ -	add(LLStatViewer::LSL_SAVES, 1); - -	if( mSaveCallback ) -	{ -		mSaveCallback( mUserdata, close_after_save ); -	} -} - -void LLScriptEdCore::openInExternalEditor() -{ -	delete mLiveFile; // deletes file - -	// Generate a suitable filename -    std::string script_name = mScriptName; -    std::string forbidden_chars = "<>:\"\\/|?*"; -    for (std::string::iterator c = forbidden_chars.begin(); c != forbidden_chars.end(); c++) -    { -        script_name.erase(std::remove(script_name.begin(), script_name.end(), *c), script_name.end()); -    } -	std::string filename = mContainer->getTmpFileName(script_name); - -    // Save the script to a temporary file. -    if (!writeToFile(filename)) -    { -        // In case some characters from script name are forbidden -        // and not accounted for, name is too long or some other issue, -        // try file that doesn't include script name -        script_name.clear(); -        filename = mContainer->getTmpFileName(script_name); -        writeToFile(filename); -    } - -	// Start watching file changes. -	mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); -	mLiveFile->addToEventTimer(); - -	// Open it in external editor. -	{ -		LLExternalEditor ed; -		LLExternalEditor::EErrorCode status; -		std::string msg; - -		status = ed.setCommand("LL_SCRIPT_EDITOR"); -		if (status != LLExternalEditor::EC_SUCCESS) -		{ -			if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error. -			{ -				msg = LLTrans::getString("ExternalEditorNotSet"); -			} -			else -			{ -				msg = LLExternalEditor::getErrorMessage(status); -			} - -			LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg)); -			return; -		} - -		status = ed.run(filename); -		if (status != LLExternalEditor::EC_SUCCESS) -		{ -			msg = LLExternalEditor::getErrorMessage(status); -			LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg)); -		} -	} -} - -void LLScriptEdCore::onBtnUndoChanges() -{ -	if( !mEditor->tryToRevertToPristineState() ) -	{ -		LLNotificationsUtil::add("ScriptCannotUndo", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleReloadFromServerDialog, this, _1, _2)); -	} -} - -// static -void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) -{ -	LLScriptEdCore* self = (LLScriptEdCore*)user_data; -	LLScrollListItem* item = self->mErrorList->getFirstSelected(); -	if(item) -	{ -		// *FIX: replace with boost grep -		S32 row = 0; -		S32 column = 0; -		const LLScrollListCell* cell = item->getColumn(0); -		std::string line(cell->getValue().asString()); -		line.erase(0, 1); -		LLStringUtil::replaceChar(line, ',',' '); -		LLStringUtil::replaceChar(line, ')',' '); -		sscanf(line.c_str(), "%d %d", &row, &column); -		//LL_INFOS() << "LLScriptEdCore::onErrorList() - " << row << ", " -		//<< column << LL_ENDL; -		self->mEditor->setCursor(row, column); -		self->mEditor->setFocus(true); -	} -} - -bool LLScriptEdCore::handleReloadFromServerDialog(const LLSD& notification, const LLSD& response ) -{ -	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); -	switch( option ) -	{ -	case 0: // "Yes" -		if( mLoadCallback ) -		{ -			setScriptText(getString("loading"), false); -			mLoadCallback(mUserdata); -		} -		break; - -	case 1: // "No" -		break; - -	default: -		llassert(0); -		break; -	} -	return false; -} - -void LLScriptEdCore::selectFirstError() -{ -	// Select the first item; -	mErrorList->selectFirstItem(); -	onErrorList(mErrorList, this); -} - - -struct LLEntryAndEdCore -{ -	LLScriptEdCore* mCore; -	LLEntryAndEdCore(LLScriptEdCore* core) : -		mCore(core) -		{} -}; - -void LLScriptEdCore::deleteBridges() -{ -	S32 count = mBridges.size(); -	LLEntryAndEdCore* eandc; -	for(S32 i = 0; i < count; i++) -	{ -		eandc = mBridges.at(i); -		delete eandc; -		mBridges[i] = NULL; -	} -	mBridges.clear(); -} - -// virtual -bool LLScriptEdCore::handleKeyHere(KEY key, MASK mask) -{ -	bool just_control = MASK_CONTROL == (mask & MASK_MODIFIERS); - -	if(('S' == key) && just_control) -	{ -		if(mSaveCallback) -		{ -			// don't close after saving -			mSaveCallback(mUserdata, false); -		} - -		return true; -	} - -	if(('F' == key) && just_control) -	{ -		if(mSearchReplaceCallback) -		{ -			mSearchReplaceCallback(mUserdata); -		} - -		return true; -	} - -	return false; -} - -void LLScriptEdCore::onBtnLoadFromFile( void* data ) -{ -	LLFilePickerReplyThread::startPicker(boost::bind(&LLScriptEdCore::loadScriptFromFile, _1, data), LLFilePicker::FFLOAD_SCRIPT, false); -} - -void LLScriptEdCore::loadScriptFromFile(const std::vector<std::string>& filenames, void* data) -{ -	std::string filename = filenames[0]; - -	llifstream fin(filename.c_str()); - -	std::string line; -	std::string text; -	std::string linetotal; -	while (!fin.eof()) -	{ -		getline(fin, line); -		text += line; -		if (!fin.eof()) -		{ -			text += "\n"; -		} -	} -	fin.close(); - -	// Only replace the script if there is something to replace with. -	LLScriptEdCore* self = (LLScriptEdCore*)data; -	if (self && (text.length() > 0)) -	{ -		self->mEditor->selectAll(); -		LLWString script(utf8str_to_wstring(text)); -		self->mEditor->insertText(script); -	} -} - -void LLScriptEdCore::onBtnSaveToFile( void* userdata ) -{ -	add(LLStatViewer::LSL_SAVES, 1); - -	LLScriptEdCore* self = (LLScriptEdCore*) userdata; - -	if( self->mSaveCallback ) -	{ -		LLFilePickerReplyThread::startPicker(boost::bind(&LLScriptEdCore::saveScriptToFile, _1, userdata), LLFilePicker::FFSAVE_SCRIPT, self->mScriptName); -	} -} - -void LLScriptEdCore::saveScriptToFile(const std::vector<std::string>& filenames, void* data) -{ -	LLScriptEdCore* self = (LLScriptEdCore*)data; -	if (self) -	{ -		std::string filename = filenames[0]; -		std::string scriptText = self->mEditor->getText(); -		llofstream fout(filename.c_str()); -		fout << (scriptText); -		fout.close(); -		self->mSaveCallback(self->mUserdata, false); -	} -} - -bool LLScriptEdCore::canLoadOrSaveToFile( void* userdata ) -{ -	LLScriptEdCore* self = (LLScriptEdCore*) userdata; -	return self->mEditor->canLoadOrSaveToFile(); -} - -// static -bool LLScriptEdCore::enableSaveToFileMenu(void* userdata) -{ -	LLScriptEdCore* self = (LLScriptEdCore*)userdata; -	if (!self || !self->mEditor) return false; -	return self->mEditor->canLoadOrSaveToFile(); -} - -// static  -bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata) -{ -	LLScriptEdCore* self = (LLScriptEdCore*)userdata; -	return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : false; -} - -LLUUID LLScriptEdCore::getAssociatedExperience()const -{ -	return mAssociatedExperience; -} - -void LLScriptEdCore::onChangeFontSize(const LLSD &userdata) -{ -    const std::string font_name = userdata.asString(); -    gSavedSettings.setString("LSLFontSizeName", font_name); -} - -bool LLScriptEdCore::isFontSizeChecked(const LLSD &userdata) -{ -    const std::string current_size_name = LLScriptEditor::getScriptFontSize(); -    const std::string size_name = userdata.asString(); - -    return (size_name == current_size_name); -} - -    void LLLiveLSLEditor::setExperienceIds( const LLSD& experience_ids ) -{ -	mExperienceIds=experience_ids; -	updateExperiencePanel(); -} - - -void LLLiveLSLEditor::updateExperiencePanel() -{ -	if(mScriptEd->getAssociatedExperience().isNull()) -	{ -		mExperienceEnabled->set(false); -		mExperiences->setVisible(false); -		if(mExperienceIds.size()>0) -		{ -			mExperienceEnabled->setEnabled(true); -			mExperienceEnabled->setToolTip(getString("add_experiences")); -		} -		else -		{ -			mExperienceEnabled->setEnabled(false); -			mExperienceEnabled->setToolTip(getString("no_experiences")); -		} -		getChild<LLButton>("view_profile")->setVisible(false); -	} -	else -	{ -		mExperienceEnabled->setToolTip(getString("experience_enabled")); -		mExperienceEnabled->setEnabled(getIsModifiable()); -		mExperiences->setVisible(true); -		mExperienceEnabled->set(true); -		getChild<LLButton>("view_profile")->setToolTip(getString("show_experience_profile")); -		buildExperienceList(); -	} -} - -void LLLiveLSLEditor::buildExperienceList() -{ -	mExperiences->clearRows(); -	bool foundAssociated=false; -	const LLUUID& associated = mScriptEd->getAssociatedExperience(); -	LLUUID last; -	LLScrollListItem* item; -	for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) -	{ -		LLUUID id = it->asUUID(); -		EAddPosition position = ADD_BOTTOM; -		if(id == associated) -		{ -			foundAssociated = true; -			position = ADD_TOP; -		} -		 -        const LLSD& experience = LLExperienceCache::instance().get(id); -		if(experience.isUndefined()) -		{ -			mExperiences->add(getString("loading"), id, position); -			last = id; -		} -		else -		{ -			std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); -			if (experience_name_string.empty()) -			{ -				experience_name_string = LLTrans::getString("ExperienceNameUntitled"); -			} -			mExperiences->add(experience_name_string, id, position); -		}  -	} - -	if(!foundAssociated ) -	{ -        const LLSD& experience = LLExperienceCache::instance().get(associated); -		if(experience.isDefined()) -		{ -			std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); -			if (experience_name_string.empty()) -			{ -				experience_name_string = LLTrans::getString("ExperienceNameUntitled"); -			} -			item=mExperiences->add(experience_name_string, associated, ADD_TOP); -		}  -		else -		{ -			item=mExperiences->add(getString("loading"), associated, ADD_TOP); -			last = associated; -		} -		item->setEnabled(false); -	} - -	if(last.notNull()) -	{ -		mExperiences->setEnabled(false); -        LLExperienceCache::instance().get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this)); -	} -	else -	{ -		mExperiences->setEnabled(true); -		mExperiences->sortByName(true); -		mExperiences->setCurrentByIndex(mExperiences->getCurrentIndex()); -		getChild<LLButton>("view_profile")->setVisible(true); -	} -} - - -void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) -{ -	mAssociatedExperience = experience_id; -} - - - -void LLLiveLSLEditor::requestExperiences() -{ -	if (!getIsModifiable()) -	{ -		return; -	} - -	LLViewerRegion* region = gAgent.getRegion(); -	if (region) -	{ -		std::string lookup_url=region->getCapability("GetCreatorExperiences");  -		if(!lookup_url.empty()) -		{ -            LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success = -                boost::bind(&LLLiveLSLEditor::receiveExperienceIds, _1, getDerivedHandle<LLLiveLSLEditor>()); - -            LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url, success); -		} -	} -} - -/*static*/  -void LLLiveLSLEditor::receiveExperienceIds(LLSD result, LLHandle<LLLiveLSLEditor> hparent) -{ -    LLLiveLSLEditor* parent = hparent.get(); -    if (!parent) -        return; - -    parent->setExperienceIds(result["experience_ids"]); -} - - -/// --------------------------------------------------------------------------- -/// LLScriptEdContainer -/// --------------------------------------------------------------------------- - -LLScriptEdContainer::LLScriptEdContainer(const LLSD& key) : -	LLPreview(key) -,	mScriptEd(NULL) -{ -} - -std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name) -{ -	// Take script inventory item id (within the object inventory) -	// to consideration so that it's possible to edit multiple scripts -	// in the same object inventory simultaneously (STORM-781). -	std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); - -	// Use MD5 sum to make the file name shorter and not exceed maximum path length. -	char script_id_hash_str[33];			   /* Flawfinder: ignore */ -	LLMD5 script_id_hash((const U8 *)script_id.c_str()); -	script_id_hash.hex_digest(script_id_hash_str); - -    if (script_name.empty()) -    { -        return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl"; -    } -    else -    { -        return std::string(LLFile::tmpdir()) + "sl_script_" + script_name + "_" + script_id_hash_str + ".lsl"; -    } -} - -bool LLScriptEdContainer::onExternalChange(const std::string& filename) -{ -	if (!mScriptEd->loadScriptText(filename)) -	{ -		return false; -	} - -	// Disable sync to avoid recursive load->save->load calls. -	saveIfNeeded(false); -	return true; -} - -bool LLScriptEdContainer::handleKeyHere(KEY key, MASK mask)  -{ -    if (('A' == key) && (MASK_CONTROL == (mask & MASK_MODIFIERS))) -    { -        mScriptEd->selectAll(); -        return true; -    } - -    if (!LLPreview::handleKeyHere(key, mask))  -    { -        return mScriptEd->handleKeyHere(key, mask); -    } -    return true; -} - -/// --------------------------------------------------------------------------- -/// LLPreviewLSL -/// --------------------------------------------------------------------------- - -struct LLScriptSaveInfo -{ -	LLUUID mItemUUID; -	std::string mDescription; -	LLTransactionID mTransactionID; - -	LLScriptSaveInfo(const LLUUID& uuid, const std::string& desc, LLTransactionID tid) : -		mItemUUID(uuid), mDescription(desc),  mTransactionID(tid) {} -}; - - - -//static -void* LLPreviewLSL::createScriptEdPanel(void* userdata) -{ -	 -	LLPreviewLSL *self = (LLPreviewLSL*)userdata; - -	self->mScriptEd =  new LLScriptEdCore( -								   self, -								   HELLO_LSL, -								   self->getHandle(), -								   LLPreviewLSL::onLoad, -								   LLPreviewLSL::onSave, -								   LLPreviewLSL::onSearchReplace, -								   self, -								   false, -								   0); -	return self->mScriptEd; -} - - -LLPreviewLSL::LLPreviewLSL(const LLSD& key ) -:	LLScriptEdContainer(key), -	mPendingUploads(0) -{ -	mFactoryMap["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this); - -    mItemObserver = new LLScriptMovedObserver(this); -} - -LLPreviewLSL::~LLPreviewLSL()  -{  -    delete mItemObserver; -    mItemObserver = NULL; -} - -// virtual -bool LLPreviewLSL::postBuild() -{ -	const LLInventoryItem* item = getItem(); - -	llassert(item); -	if (item) -	{ -		getChild<LLUICtrl>("desc")->setValue(item->getDescription()); - -        std::string item_path = get_category_path(item->getParentUUID()); -        getChild<LLUICtrl>("path_txt")->setValue(item_path); -        getChild<LLUICtrl>("path_txt")->setToolTip(item_path); -	} -	childSetCommitCallback("desc", LLPreview::onText, this); -	getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); -  -	return LLPreview::postBuild(); -} - -void LLPreviewLSL::draw() -{ -	const LLInventoryItem* item = getItem(); -	if(!item) -	{ -		setTitle(LLTrans::getString("ScriptWasDeleted")); -		mScriptEd->setItemRemoved(true); -	} -    else if (mDirty)  -    { -        std::string item_path = get_category_path(item->getParentUUID()); -        getChild<LLUICtrl>("path_txt")->setValue(item_path); -        getChild<LLUICtrl>("path_txt")->setToolTip(item_path); -    } -	LLPreview::draw(); -} -// virtual -void LLPreviewLSL::callbackLSLCompileSucceeded() -{ -	LL_INFOS() << "LSL Bytecode saved" << LL_ENDL; -	mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful")); -	mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete")); -	closeIfNeeded(); -} - -// virtual -void LLPreviewLSL::callbackLSLCompileFailed(const LLSD& compile_errors) -{ -	LL_INFOS() << "Compile failed!" << LL_ENDL; - -	for(LLSD::array_const_iterator line = compile_errors.beginArray(); -		line < compile_errors.endArray(); -		line++) -	{ -		LLSD row; -		std::string error_message = line->asString(); -		LLStringUtil::stripNonprintable(error_message); -		row["columns"][0]["value"] = error_message; -		row["columns"][0]["font"] = "OCRA"; -		mScriptEd->mErrorList->addElement(row); -	} -	mScriptEd->selectFirstError(); -	closeIfNeeded(); -} - -void LLPreviewLSL::loadAsset() -{ -	// *HACK: we poke into inventory to see if it's there, and if so, -	// then it might be part of the inventory library. If it's in the -	// library, then you can see the script, but not modify it. -	const LLInventoryItem* item = gInventory.getItem(mItemUUID); -	bool is_library = item -		&& !gInventory.isObjectDescendentOf(mItemUUID, -											gInventory.getRootFolderID()); -	if(!item) -	{ -		// do the more generic search. -		getItem(); -	} -	if(item) -	{ -		bool is_copyable = gAgent.allowOperation(PERM_COPY,  -								item->getPermissions(), GP_OBJECT_MANIPULATE); -		bool is_modifiable = gAgent.allowOperation(PERM_MODIFY, -								item->getPermissions(), GP_OBJECT_MANIPULATE); -		if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library))) -		{ -			LLUUID* new_uuid = new LLUUID(mItemUUID); -			gAssetStorage->getInvItemAsset(LLHost(), -										gAgent.getID(), -										gAgent.getSessionID(), -										item->getPermissions().getOwner(), -										LLUUID::null, -										item->getUUID(), -										item->getAssetUUID(), -										item->getType(), -										&LLPreviewLSL::onLoadComplete, -										(void*)new_uuid, -										true); -			mAssetStatus = PREVIEW_ASSET_LOADING; -		} -		else -		{ -			mScriptEd->setScriptText(mScriptEd->getString("can_not_view"), false); -			mScriptEd->mEditor->makePristine(); -			mScriptEd->mFunctions->setEnabled(false); -			mAssetStatus = PREVIEW_ASSET_LOADED; -		} -		getChildView("lock")->setVisible( !is_modifiable); -		mScriptEd->getChildView("Insert...")->setEnabled(is_modifiable); -	} -	else -	{ -		mScriptEd->setScriptText(std::string(HELLO_LSL), true); -		mScriptEd->setEnableEditing(true); -		mAssetStatus = PREVIEW_ASSET_LOADED; -	} -} - - -bool LLPreviewLSL::canClose() -{ -	return mScriptEd->canClose(); -} - -void LLPreviewLSL::closeIfNeeded() -{ -	// Find our window and close it if requested. -	getWindow()->decBusyCount(); -	mPendingUploads--; -	if (mPendingUploads <= 0 && mCloseAfterSave) -	{ -		closeFloater(); -	} -} - -void LLPreviewLSL::onSearchReplace(void* userdata) -{ -	LLPreviewLSL* self = (LLPreviewLSL*)userdata; -	LLScriptEdCore* sec = self->mScriptEd;  -	LLFloaterScriptSearch::show(sec); -} - -// static -void LLPreviewLSL::onLoad(void* userdata) -{ -	LLPreviewLSL* self = (LLPreviewLSL*)userdata; -	self->loadAsset(); -} - -// static -void LLPreviewLSL::onSave(void* userdata, bool close_after_save) -{ -	LLPreviewLSL* self = (LLPreviewLSL*)userdata; -	self->mCloseAfterSave = close_after_save; -	self->saveIfNeeded(); -} - -/*static*/ -void LLPreviewLSL::finishedLSLUpload(LLUUID itemId, LLSD response) -{ -    // Find our window and close it if requested. -    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(itemId)); -    if (preview) -    { -        // Bytecode save completed -        if (response["compiled"]) -        { -            preview->callbackLSLCompileSucceeded(); -        } -        else -        { -            preview->callbackLSLCompileFailed(response["errors"]); -        } -    } -} - -bool LLPreviewLSL::failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, std::string reason) -{ -    LLSD floater_key; -    if (taskId.notNull()) -    { -        floater_key["taskid"] = taskId; -        floater_key["itemid"] = itemId; -    } -    else -    { -        floater_key = LLSD(itemId); -    } - -    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", floater_key); -    if (preview) -    { -        // unfreeze floater -        LLSD errors; -        errors.append(LLTrans::getString("UploadFailed") + reason); -        preview->callbackLSLCompileFailed(errors); -        return true; -    } - -    return false; -} - -// Save needs to compile the text in the buffer. If the compile -// succeeds, then save both assets out to the database. If the compile -// fails, go ahead and save the text anyway. -void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) -{ -    if (!mScriptEd->hasChanged()) -    { -        return; -    } - -    mPendingUploads = 0; -    mScriptEd->mErrorList->deleteAllItems(); -    mScriptEd->mEditor->makePristine(); - -    if (sync) -    { -        mScriptEd->sync(); -    } - -    if (!gAgent.getRegion()) return; -    const LLInventoryItem *inv_item = getItem(); -    // save it out to asset server -    std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent"); -    if(inv_item) -    { -        getWindow()->incBusyCount(); -        mPendingUploads++; -        if (!url.empty()) -        { -            std::string buffer(mScriptEd->mEditor->getText()); - -            LLUUID old_asset_id = inv_item->getAssetUUID().isNull() ? mScriptEd->getAssetID() : inv_item->getAssetUUID(); - -            LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mItemUUID, buffer,  -                [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) { -                    LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); -                    LLPreviewLSL::finishedLSLUpload(itemId, response); -                }, -                LLPreviewLSL::failedLSLUpload)); - -            LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); -        } -    } -} - -// static -void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, -								  void* user_data, S32 status, LLExtStat ext_status) -{ -	LL_DEBUGS() << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid -		 << LL_ENDL; -	LLUUID* item_uuid = (LLUUID*)user_data; -	LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *item_uuid); -	if( preview ) -	{ -		if(0 == status) -		{ -			LLFileSystem file(asset_uuid, type); -			S32 file_length = file.getSize(); - -			std::vector<char> buffer(file_length+1); -			file.read((U8*)&buffer[0], file_length); - -			// put a EOS at the end -			buffer[file_length] = 0; -			preview->mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), true); -			preview->mScriptEd->mEditor->makePristine(); - -			std::string script_name = DEFAULT_SCRIPT_NAME; -			LLInventoryItem* item = gInventory.getItem(*item_uuid); -			bool is_modifiable = false; -			if (item) -			{ -				if (!item->getName().empty()) -				{ -					script_name = item->getName(); -				} -				if (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) -				{ -					is_modifiable = true; -				} -			} -			preview->mScriptEd->setScriptName(script_name); -			preview->mScriptEd->setEnableEditing(is_modifiable); -            preview->mScriptEd->setAssetID(asset_uuid); -			preview->mAssetStatus = PREVIEW_ASSET_LOADED; -		} -		else -		{ -			if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || -				LL_ERR_FILE_EMPTY == status) -			{ -				LLNotificationsUtil::add("ScriptMissing"); -			} -			else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) -			{ -				LLNotificationsUtil::add("ScriptNoPermissions"); -			} -			else -			{ -				LLNotificationsUtil::add("UnableToLoadScript"); -			} - -			preview->mAssetStatus = PREVIEW_ASSET_ERROR; -			LL_WARNS() << "Problem loading script: " << status << LL_ENDL; -		} -	} -	delete item_uuid; -} - - -/// --------------------------------------------------------------------------- -/// LLLiveLSLEditor -/// --------------------------------------------------------------------------- - - -//static  -void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) -{ -	LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata; - -	self->mScriptEd =  new LLScriptEdCore( -								   self, -								   HELLO_LSL, -								   self->getHandle(), -								   &LLLiveLSLEditor::onLoad, -								   &LLLiveLSLEditor::onSave, -								   &LLLiveLSLEditor::onSearchReplace, -								   self, -								   true, -								   0); -	return self->mScriptEd; -} - - -LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) : -	LLScriptEdContainer(key), -	mAskedForRunningInfo(false), -	mHaveRunningInfo(false), -	mCloseAfterSave(false), -	mPendingUploads(0), -	mIsModifiable(false), -	mIsNew(false), -	mIsSaving(false), -    mObjectName("") -{ -	mFactoryMap["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this); -} - -bool LLLiveLSLEditor::postBuild() -{ -	childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this); -	getChildView("running")->setEnabled(false); - -	childSetAction("Reset",&LLLiveLSLEditor::onReset,this); -	getChildView("Reset")->setEnabled(true); - -	mMonoCheckbox =	getChild<LLCheckBoxCtrl>("mono"); -	childSetCommitCallback("mono", &LLLiveLSLEditor::onMonoCheckboxClicked, this); -	getChildView("mono")->setEnabled(true); - -	mScriptEd->mEditor->makePristine(); -	mScriptEd->mEditor->setFocus(true); - - -	mExperiences = getChild<LLComboBox>("Experiences..."); -	mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this)); -	 -	mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp"); -	 -	childSetCommitCallback("enable_xp", onToggleExperience, this); -	childSetCommitCallback("view_profile", onViewProfile, this); -	 - -	return LLPreview::postBuild(); -} - -// virtual -void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id, -												  const LLUUID& item_id, -												  bool is_script_running) -{ -	LL_DEBUGS() << "LSL Bytecode saved" << LL_ENDL; -	mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful")); -	mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete")); -	getChild<LLCheckBoxCtrl>("running")->set(is_script_running); -	mIsSaving = false; -	closeIfNeeded(); -} - -// virtual -void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors) -{ -	LL_DEBUGS() << "Compile failed!" << LL_ENDL; -	for(LLSD::array_const_iterator line = compile_errors.beginArray(); -		line < compile_errors.endArray(); -		line++) -	{ -		LLSD row; -		std::string error_message = line->asString(); -		LLStringUtil::stripNonprintable(error_message); -		row["columns"][0]["value"] = error_message; -		// *TODO: change to "MONOSPACE" and change llfontgl.cpp? -		row["columns"][0]["font"] = "OCRA"; -		mScriptEd->mErrorList->addElement(row); -	} -	mScriptEd->selectFirstError(); -	mIsSaving = false; -	closeIfNeeded(); -} - -void LLLiveLSLEditor::loadAsset() -{ -	//LL_INFOS() << "LLLiveLSLEditor::loadAsset()" << LL_ENDL; -	if(!mIsNew) -	{ -		LLViewerObject* object = gObjectList.findObject(mObjectUUID); -		if(object) -		{ -			LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID)); - -			if(item) -			{ -				LLViewerRegion* region = object->getRegion(); -				std::string url = std::string(); -				if(region) -				{ -					url = region->getCapability("GetMetadata"); -				} -				LLExperienceCache::instance().fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), url, -					boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1)); - -				bool isGodlike = gAgent.isGodlike(); -				bool copyManipulate = gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); -				mIsModifiable = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); -				 -				if(!isGodlike && (!copyManipulate || !mIsModifiable)) -				{ -					mItem = new LLViewerInventoryItem(item); -					mScriptEd->setScriptText(getString("not_allowed"), false); -					mScriptEd->mEditor->makePristine(); -					mScriptEd->enableSave(false); -					mAssetStatus = PREVIEW_ASSET_LOADED; -				} -				else if(copyManipulate || isGodlike) -				{ -					mItem = new LLViewerInventoryItem(item); -					// request the text from the object -				LLSD* user_data = new LLSD(); -				user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID); -					gAssetStorage->getInvItemAsset(object->getRegion()->getHost(), -						gAgent.getID(), -						gAgent.getSessionID(), -						item->getPermissions().getOwner(), -						object->getID(), -						item->getUUID(), -						item->getAssetUUID(), -						item->getType(), -						&LLLiveLSLEditor::onLoadComplete, -						(void*)user_data, -						true); -					LLMessageSystem* msg = gMessageSystem; -					msg->newMessageFast(_PREHASH_GetScriptRunning); -					msg->nextBlockFast(_PREHASH_Script); -					msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID); -					msg->addUUIDFast(_PREHASH_ItemID, mItemUUID); -					msg->sendReliable(object->getRegion()->getHost()); -					mAskedForRunningInfo = true; -					mAssetStatus = PREVIEW_ASSET_LOADING; -				} -			} -			 -			if(mItem.isNull()) -			{ -				mScriptEd->setScriptText(LLStringUtil::null, false); -				mScriptEd->mEditor->makePristine(); -				mAssetStatus = PREVIEW_ASSET_LOADED; -				mIsModifiable = false; -			} - -			refreshFromItem(); -            getChild<LLUICtrl>("obj_name")->setValue(mObjectName); -			// This is commented out, because we don't completely -			// handle script exports yet. -			/* -			// request the exports from the object -			gMessageSystem->newMessage("GetScriptExports"); -			gMessageSystem->nextBlock("ScriptBlock"); -			gMessageSystem->addUUID("AgentID", gAgent.getID()); -			U32 local_id = object->getLocalID(); -			gMessageSystem->addData("LocalID", &local_id); -			gMessageSystem->addUUID("ItemID", mItemUUID); -			LLHost host(object->getRegion()->getIP(), -						object->getRegion()->getPort()); -			gMessageSystem->sendReliable(host); -			*/ -		} -	} -	else -	{ -		mScriptEd->setScriptText(std::string(HELLO_LSL), true); -		mScriptEd->enableSave(false); -		LLPermissions perm; -		perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID()); -		perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER); -		mItem = new LLViewerInventoryItem(mItemUUID, -										  mObjectUUID, -										  perm, -										  LLUUID::null, -										  LLAssetType::AT_LSL_TEXT, -										  LLInventoryType::IT_LSL, -										  DEFAULT_SCRIPT_NAME, -										  DEFAULT_SCRIPT_DESC, -										  LLSaleInfo::DEFAULT, -										  LLInventoryItemFlags::II_FLAGS_NONE, -										  time_corrected()); -		mAssetStatus = PREVIEW_ASSET_LOADED; -	} - -	requestExperiences(); -} - -// static -void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, -									 LLAssetType::EType type, -									 void* user_data, S32 status, LLExtStat ext_status) -{ -	LL_DEBUGS() << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id -		 << LL_ENDL; -	LLSD* floater_key = (LLSD*)user_data; -	 -	LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", *floater_key); -	 -	if(instance ) -	{ -		if( LL_ERR_NOERR == status ) -		{ -			instance->loadScriptText(asset_id, type); -			instance->mScriptEd->setEnableEditing(true); -			instance->mAssetStatus = PREVIEW_ASSET_LOADED; -            instance->mScriptEd->setAssetID(asset_id); -		} -		else -		{ -			if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || -				LL_ERR_FILE_EMPTY == status) -			{ -				LLNotificationsUtil::add("ScriptMissing"); -			} -			else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) -			{ -				LLNotificationsUtil::add("ScriptNoPermissions"); -			} -			else -			{ -				LLNotificationsUtil::add("UnableToLoadScript"); -			} -			instance->mAssetStatus = PREVIEW_ASSET_ERROR; -		} -	} - -	delete floater_key; -} - -void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type) -{ -	LLFileSystem file(uuid, type); -	S32 file_length = file.getSize(); -	std::vector<char> buffer(file_length + 1); -	file.read((U8*)&buffer[0], file_length); - -	if (file.getLastBytesRead() != file_length || -		file_length <= 0) -	{ -		LL_WARNS() << "Error reading " << uuid << ":" << type << LL_ENDL; -	} - -	buffer[file_length] = '\0'; - -	mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), true); -	mScriptEd->makeEditorPristine(); - -	std::string script_name = DEFAULT_SCRIPT_NAME; -	const LLInventoryItem* inv_item = getItem(); - -	if(inv_item) -	{ -		script_name = inv_item->getName(); -	} -	mScriptEd->setScriptName(script_name); -} - - -void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata ) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; -	LLViewerObject* object = gObjectList.findObject( self->mObjectUUID ); -	LLCheckBoxCtrl* runningCheckbox = self->getChild<LLCheckBoxCtrl>("running"); -	bool running =  runningCheckbox->get(); -	//self->mRunningCheckbox->get(); -	if( object ) -	{ -		LLMessageSystem* msg = gMessageSystem; -		msg->newMessageFast(_PREHASH_SetScriptRunning); -		msg->nextBlockFast(_PREHASH_AgentData); -		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -		msg->nextBlockFast(_PREHASH_Script); -		msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID); -		msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID); -		msg->addBOOLFast(_PREHASH_Running, running); -		msg->sendReliable(object->getRegion()->getHost()); -	} -	else -	{ -		runningCheckbox->set(!running); -		LLNotificationsUtil::add("CouldNotStartStopScript"); -	} -} - -void LLLiveLSLEditor::onReset(void *userdata) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; - -	LLViewerObject* object = gObjectList.findObject( self->mObjectUUID ); -	if(object) -	{ -		LLMessageSystem* msg = gMessageSystem; -		msg->newMessageFast(_PREHASH_ScriptReset); -		msg->nextBlockFast(_PREHASH_AgentData); -		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -		msg->nextBlockFast(_PREHASH_Script); -		msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID); -		msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID); -		msg->sendReliable(object->getRegion()->getHost()); -	} -	else -	{ -		LLNotificationsUtil::add("CouldNotStartStopScript");  -	} -} - -void LLLiveLSLEditor::draw() -{ -	LLViewerObject* object = gObjectList.findObject(mObjectUUID); -	LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running"); -	if(object && mAskedForRunningInfo && mHaveRunningInfo) -	{ -		if(object->permAnyOwner()) -		{ -			runningCheckbox->setLabel(getString("script_running")); -			runningCheckbox->setEnabled(!mIsSaving); -		} -		else -		{ -			runningCheckbox->setLabel(getString("public_objects_can_not_run")); -			runningCheckbox->setEnabled(false); - -			// *FIX: Set it to false so that the ui is correct for -			// a box that is released to public. It could be -			// incorrect after a release/claim cycle, but will be -			// correct after clicking on it. -			runningCheckbox->set(false); -			mMonoCheckbox->set(false); -		} -	} -	else if(!object) -	{ -		// HACK: Display this information in the title bar. -		// Really ought to put in main window. -		setTitle(LLTrans::getString("ObjectOutOfRange")); -		runningCheckbox->setEnabled(false); -		mMonoCheckbox->setEnabled(false); -		// object may have fallen out of range. -		mHaveRunningInfo = false; -	} - -	LLPreview::draw(); -} - - -void LLLiveLSLEditor::onSearchReplace(void* userdata) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - -	LLScriptEdCore* sec = self->mScriptEd;  -	LLFloaterScriptSearch::show(sec); -} - -struct LLLiveLSLSaveData -{ -	LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, bool active); -	LLUUID mSaveObjectID; -	LLPointer<LLViewerInventoryItem> mItem; -	bool mActive; -}; - -LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id, -									 const LLViewerInventoryItem* item, -									 bool active) : -	mSaveObjectID(id), -	mActive(active) -{ -	llassert(item); -	mItem = new LLViewerInventoryItem(item); -} - -/*static*/ -void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, bool isRunning) -{ -    LLSD floater_key; -    floater_key["taskid"] = taskId; -    floater_key["itemid"] = itemId; - -    LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key); -    if (preview) -    { -        preview->mItem->setAssetUUID(newAssetId); -        preview->mScriptEd->setAssetID(newAssetId); - -        // Bytecode save completed -        if (response["compiled"]) -        { -            preview->callbackLSLCompileSucceeded(taskId, itemId, isRunning); -        } -        else -        { -            preview->callbackLSLCompileFailed(response["errors"]); -        } -    } -} - -// virtual -void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) -{ -	LLViewerObject* object = gObjectList.findObject(mObjectUUID); -	if (!object) -	{ -		LLNotificationsUtil::add("SaveScriptFailObjectNotFound"); -		return; -	} - -    if (mItem.isNull() || !mItem->isFinished()) -	{ -		// $NOTE: While the error message may not be exactly correct, -		// it's pretty close. -		LLNotificationsUtil::add("SaveScriptFailObjectNotFound"); -		return; -	} - -    // get the latest info about it. We used to be losing the script -    // name on save, because the viewer object version of the item, -    // and the editor version would get out of synch. Here's a good -    // place to synch them back up. -    LLInventoryItem* inv_item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID)); -    if (inv_item) -    { -        mItem->copyItem(inv_item); -    } - -    // Don't need to save if we're pristine -    if (!mScriptEd->hasChanged()) -    { -        return; -    } - -    mPendingUploads = 0; - -    // save the script -    mScriptEd->enableSave(false); -    mScriptEd->mEditor->makePristine(); -    mScriptEd->mErrorList->deleteAllItems(); -    mScriptEd->mEditor->makePristine(); - -    if (sync) -    { -        mScriptEd->sync(); -    } - -    bool isRunning = getChild<LLCheckBoxCtrl>("running")->get(); -    getWindow()->incBusyCount(); -    mPendingUploads++; - -    std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - -    if (!url.empty()) -    { -        std::string buffer(mScriptEd->mEditor->getText()); -        LLUUID old_asset_id = mScriptEd->getAssetID(); - -        LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mObjectUUID, mItemUUID,  -                monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2,  -                isRunning, mScriptEd->getAssociatedExperience(), buffer,  -                [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) {  -                        LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); -                        LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); -                }, -                nullptr)); // needs failure handling? - -        LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); -    } -} - -bool LLLiveLSLEditor::canClose() -{ -	return mScriptEd->canClose(); -} - -void LLLiveLSLEditor::closeIfNeeded() -{ -	getWindow()->decBusyCount(); -	mPendingUploads--; -	if ((mPendingUploads <= 0) && mCloseAfterSave) -	{ -		closeFloater(); -	} -} - -// static -void LLLiveLSLEditor::onLoad(void* userdata) -{ -	LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; -	self->loadAsset(); -} - -// static -void LLLiveLSLEditor::onSave(void* userdata, bool close_after_save) -{ -	if (LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata) -	{ -		self->mCloseAfterSave = close_after_save; -		self->mScriptEd->mErrorList->setCommentText(""); -		self->saveIfNeeded(); -	} -} - -// static -void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**) -{ -	LLUUID item_id; -	LLUUID object_id; -	msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id); -	msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id); - -	LLSD floater_key; -	floater_key["taskid"] = object_id; -	floater_key["itemid"] = item_id; -	if (LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key)) -	{ -		instance->mHaveRunningInfo = true; -		bool running; -		msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running); -		LLCheckBoxCtrl* runningCheckbox = instance->getChild<LLCheckBoxCtrl>("running"); -		runningCheckbox->set(running); -		bool mono; -		msg->getBOOLFast(_PREHASH_Script, "Mono", mono); -		LLCheckBoxCtrl* monoCheckbox = instance->getChild<LLCheckBoxCtrl>("mono"); -		monoCheckbox->setEnabled(instance->getIsModifiable() && have_script_upload_cap(object_id)); -		monoCheckbox->set(mono); -	} -} - -void LLLiveLSLEditor::onMonoCheckboxClicked(LLUICtrl*, void* userdata) -{ -	LLLiveLSLEditor* self = static_cast<LLLiveLSLEditor*>(userdata); -	self->mMonoCheckbox->setEnabled(have_script_upload_cap(self->mObjectUUID)); -	self->mScriptEd->enableSave(self->getIsModifiable()); -} - -bool LLLiveLSLEditor::monoChecked() const -{ -	return mMonoCheckbox && mMonoCheckbox->getValue(); -} - -void LLLiveLSLEditor::setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience ) -{ -	if (LLLiveLSLEditor* scriptEd = editor.get()) -	{ -		LLUUID id; -		if (experience.has(LLExperienceCache::EXPERIENCE_ID)) -		{ -			id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); -		} -		scriptEd->mScriptEd->setAssociatedExperience(id); -		scriptEd->updateExperiencePanel(); -	} -} +/**
 + * @file llpreviewscript.cpp
 + * @brief LLPreviewScript class implementation
 + *
 + * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, Inc.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation;
 + * version 2.1 of the License only.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 + *
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +#include "llviewerprecompiledheaders.h"
 +
 +#include "llpreviewscript.h"
 +
 +#include "llassetstorage.h"
 +#include "llbutton.h"
 +#include "llcheckboxctrl.h"
 +#include "llcombobox.h"
 +#include "lldir.h"
 +#include "llexternaleditor.h"
 +#include "llfilepicker.h"
 +#include "llfloaterreg.h"
 +#include "llinventorydefines.h"
 +#include "llinventorymodel.h"
 +#include "llkeyboard.h"
 +#include "lllineeditor.h"
 +#include "llmd5.h"
 +#include "llhelp.h"
 +#include "llnotificationsutil.h"
 +#include "llresmgr.h"
 +#include "llscrollbar.h"
 +#include "llscrollcontainer.h"
 +#include "llscrolllistctrl.h"
 +#include "llscrolllistitem.h"
 +#include "llscrolllistcell.h"
 +#include "llsdserialize.h"
 +#include "llslider.h"
 +#include "lltooldraganddrop.h"
 +#include "llfilesystem.h"
 +
 +#include "llagent.h"
 +#include "llmenugl.h"
 +#include "roles_constants.h"
 +#include "llselectmgr.h"
 +#include "llviewerinventory.h"
 +#include "llviewermenu.h"
 +#include "llviewermenufile.h" // LLFilePickerReplyThread
 +#include "llviewerobject.h"
 +#include "llviewerobjectlist.h"
 +#include "llviewerregion.h"
 +#include "llkeyboard.h"
 +#include "llscrollcontainer.h"
 +#include "llcheckboxctrl.h"
 +#include "llscripteditor.h"
 +#include "llselectmgr.h"
 +#include "lltooldraganddrop.h"
 +#include "llscrolllistctrl.h"
 +#include "lltextbox.h"
 +#include "llslider.h"
 +#include "lldir.h"
 +#include "llcombobox.h"
 +#include "llviewerstats.h"
 +#include "llviewerwindow.h"
 +#include "lluictrlfactory.h"
 +#include "llmediactrl.h"
 +#include "lluictrlfactory.h"
 +#include "lltrans.h"
 +#include "llviewercontrol.h"
 +#include "llappviewer.h"
 +#include "llfloatergotoline.h"
 +#include "llexperiencecache.h"
 +#include "llfloaterexperienceprofile.h"
 +#include "llviewerassetupload.h"
 +#include "lltoggleablemenu.h"
 +#include "llmenubutton.h"
 +#include "llinventoryfunctions.h"
 +
 +const std::string HELLO_LSL =
 +    "default\n"
 +    "{\n"
 +    "   state_entry()\n"
 +    "   {\n"
 +    "       llSay(0, \"Hello, Avatar!\");\n"
 +    "   }\n"
 +    "\n"
 +    "   touch_start(integer total_number)\n"
 +    "   {\n"
 +    "       llSay(0, \"Touched.\");\n"
 +    "   }\n"
 +    "}\n";
 +const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal";
 +
 +const std::string DEFAULT_SCRIPT_NAME = "New Script"; // *TODO:Translate?
 +const std::string DEFAULT_SCRIPT_DESC = "(No Description)"; // *TODO:Translate?
 +
 +// Description and header information
 +const S32 MAX_HISTORY_COUNT = 10;
 +const F32 LIVE_HELP_REFRESH_TIME = 1.f;
 +
 +static bool have_script_upload_cap(LLUUID& object_id)
 +{
 +    LLViewerObject* object = gObjectList.findObject(object_id);
 +    return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());
 +}
 +
 +/// ---------------------------------------------------------------------------
 +/// LLLiveLSLFile
 +/// ---------------------------------------------------------------------------
 +
 +LLLiveLSLFile::LLLiveLSLFile(std::string file_path, change_callback_t change_cb)
 +:   mOnChangeCallback(change_cb)
 +,   mIgnoreNextUpdate(false)
 +,   LLLiveFile(file_path, 1.0)
 +{
 +    llassert(mOnChangeCallback);
 +}
 +
 +LLLiveLSLFile::~LLLiveLSLFile()
 +{
 +    LLFile::remove(filename());
 +}
 +
 +bool LLLiveLSLFile::loadFile()
 +{
 +    if (mIgnoreNextUpdate)
 +    {
 +        mIgnoreNextUpdate = false;
 +        return true;
 +    }
 +
 +    return mOnChangeCallback(filename());
 +}
 +
 +/// ---------------------------------------------------------------------------
 +/// LLFloaterScriptSearch
 +/// ---------------------------------------------------------------------------
 +class LLFloaterScriptSearch : public LLFloater
 +{
 +public:
 +    LLFloaterScriptSearch(LLScriptEdCore* editor_core);
 +    ~LLFloaterScriptSearch();
 +
 +    /*virtual*/ bool    postBuild();
 +    static void show(LLScriptEdCore* editor_core);
 +    static void onBtnSearch(void* userdata);
 +    void handleBtnSearch();
 +
 +    static void onBtnReplace(void* userdata);
 +    void handleBtnReplace();
 +
 +    static void onBtnReplaceAll(void* userdata);
 +    void handleBtnReplaceAll();
 +
 +    LLScriptEdCore* getEditorCore() { return mEditorCore; }
 +    static LLFloaterScriptSearch* getInstance() { return sInstance; }
 +
 +    virtual bool hasAccelerators() const;
 +    virtual bool handleKeyHere(KEY key, MASK mask);
 +
 +private:
 +
 +    LLScriptEdCore* mEditorCore;
 +    static LLFloaterScriptSearch*   sInstance;
 +
 +protected:
 +    LLLineEditor*           mSearchBox;
 +    LLLineEditor*           mReplaceBox;
 +        void onSearchBoxCommit();
 +};
 +
 +LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
 +
 +LLFloaterScriptSearch::LLFloaterScriptSearch(LLScriptEdCore* editor_core)
 +:   LLFloater(LLSD()),
 +    mSearchBox(NULL),
 +    mReplaceBox(NULL),
 +    mEditorCore(editor_core)
 +{
 +    buildFromFile("floater_script_search.xml");
 +
 +    sInstance = this;
 +
 +    // find floater in which script panel is embedded
 +    LLView* viewp = (LLView*)editor_core;
 +    while(viewp)
 +    {
 +        LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
 +        if (floaterp)
 +        {
 +            floaterp->addDependentFloater(this);
 +            break;
 +        }
 +        viewp = viewp->getParent();
 +    }
 +}
 +
 +bool LLFloaterScriptSearch::postBuild()
 +{
 +    mReplaceBox = getChild<LLLineEditor>("replace_text");
 +    mSearchBox = getChild<LLLineEditor>("search_text");
 +    mSearchBox->setCommitCallback(boost::bind(&LLFloaterScriptSearch::onSearchBoxCommit, this));
 +    mSearchBox->setCommitOnFocusLost(false);
 +    childSetAction("search_btn", onBtnSearch,this);
 +    childSetAction("replace_btn", onBtnReplace,this);
 +    childSetAction("replace_all_btn", onBtnReplaceAll,this);
 +
 +    setDefaultBtn("search_btn");
 +
 +    return true;
 +}
 +
 +//static
 +void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core)
 +{
 +    LLSD::String search_text;
 +    LLSD::String replace_text;
 +    if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core)
 +    {
 +        search_text=sInstance->mSearchBox->getValue().asString();
 +        replace_text=sInstance->mReplaceBox->getValue().asString();
 +        sInstance->closeFloater();
 +        delete sInstance;
 +    }
 +
 +    if (!sInstance)
 +    {
 +        // sInstance will be assigned in the constructor.
 +        new LLFloaterScriptSearch(editor_core);
 +        sInstance->mSearchBox->setValue(search_text);
 +        sInstance->mReplaceBox->setValue(replace_text);
 +    }
 +
 +    sInstance->openFloater();
 +}
 +
 +LLFloaterScriptSearch::~LLFloaterScriptSearch()
 +{
 +    sInstance = NULL;
 +}
 +
 +// static
 +void LLFloaterScriptSearch::onBtnSearch(void *userdata)
 +{
 +    LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
 +    self->handleBtnSearch();
 +}
 +
 +void LLFloaterScriptSearch::handleBtnSearch()
 +{
 +    LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
 +    mEditorCore->mEditor->selectNext(mSearchBox->getValue().asString(), caseChk->get());
 +}
 +
 +// static
 +void LLFloaterScriptSearch::onBtnReplace(void *userdata)
 +{
 +    LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
 +    self->handleBtnReplace();
 +}
 +
 +void LLFloaterScriptSearch::handleBtnReplace()
 +{
 +    LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
 +    mEditorCore->mEditor->replaceText(mSearchBox->getValue().asString(), mReplaceBox->getValue().asString(), caseChk->get());
 +}
 +
 +// static
 +void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata)
 +{
 +    LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
 +    self->handleBtnReplaceAll();
 +}
 +
 +void LLFloaterScriptSearch::handleBtnReplaceAll()
 +{
 +    LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
 +    mEditorCore->mEditor->replaceTextAll(mSearchBox->getValue().asString(), mReplaceBox->getValue().asString(), caseChk->get());
 +}
 +
 +bool LLFloaterScriptSearch::hasAccelerators() const
 +{
 +    if (mEditorCore)
 +    {
 +        return mEditorCore->hasAccelerators();
 +    }
 +    return false;
 +}
 +
 +bool LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask)
 +{
 +    if (mEditorCore)
 +    {
 +        bool handled = mEditorCore->handleKeyHere(key, mask);
 +        if (!handled)
 +        {
 +            LLFloater::handleKeyHere(key, mask);
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +void LLFloaterScriptSearch::onSearchBoxCommit()
 +{
 +    if (mEditorCore && mEditorCore->mEditor)
 +    {
 +        LLCheckBoxCtrl* caseChk = getChild<LLCheckBoxCtrl>("case_text");
 +        mEditorCore->mEditor->selectNext(mSearchBox->getValue().asString(), caseChk->get());
 +    }
 +}
 +
 +/// ---------------------------------------------------------------------------
 +
 +class LLScriptMovedObserver : public LLInventoryObserver
 +{
 +  public:
 +    LLScriptMovedObserver(LLPreviewLSL *floater) : mPreview(floater) { gInventory.addObserver(this); }
 +    virtual ~LLScriptMovedObserver() { gInventory.removeObserver(this); }
 +    virtual void changed(U32 mask);
 +
 +  private:
 +    LLPreviewLSL *mPreview;
 +};
 +
 +void LLScriptMovedObserver::changed(U32 mask)
 +{
 +    const std::set<LLUUID> &mChangedItemIDs = gInventory.getChangedIDs();
 +    std::set<LLUUID>::const_iterator it;
 +
 +    const LLUUID &item_id = mPreview->getScriptID();
 +
 +    for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
 +    {
 +        if (*it == item_id)
 +        {
 +            if ((mask & (LLInventoryObserver::STRUCTURE)) != 0)
 +            {
 +                mPreview->setDirty();
 +            }
 +        }
 +    }
 +}
 +
 +/// ---------------------------------------------------------------------------
 +/// LLScriptEdCore
 +/// ---------------------------------------------------------------------------
 +
 +struct LLSECKeywordCompare
 +{
 +    bool operator()(const std::string& lhs, const std::string& rhs)
 +    {
 +        return (LLStringUtil::compareDictInsensitive( lhs, rhs ) < 0 );
 +    }
 +};
 +
 +LLScriptEdCore::LLScriptEdCore(
 +    LLScriptEdContainer* container,
 +    const std::string& sample,
 +    const LLHandle<LLFloater>& floater_handle,
 +    void (*load_callback)(void*),
 +    void (*save_callback)(void*, bool),
 +    void (*search_replace_callback) (void* userdata),
 +    void* userdata,
 +    bool live,
 +    S32 bottom_pad)
 +    :
 +    LLPanel(),
 +    mSampleText(sample),
 +    mEditor( NULL ),
 +    mLoadCallback( load_callback ),
 +    mSaveCallback( save_callback ),
 +    mSearchReplaceCallback( search_replace_callback ),
 +    mUserdata( userdata ),
 +    mForceClose( false ),
 +    mLastHelpToken(NULL),
 +    mLiveHelpHistorySize(0),
 +    mEnableSave(false),
 +    mLiveFile(NULL),
 +    mLive(live),
 +    mContainer(container),
 +    mHasScriptData(false),
 +    mScriptRemoved(false),
 +    mSaveDialogShown(false)
 +{
 +    setFollowsAll();
 +    setBorderVisible(false);
 +
 +    setXMLFilename("panel_script_ed.xml");
 +    llassert_always(mContainer != NULL);
 +}
 +
 +LLScriptEdCore::~LLScriptEdCore()
 +{
 +    deleteBridges();
 +
 +    // If the search window is up for this editor, close it.
 +    LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance();
 +    if (script_search && script_search->getEditorCore() == this)
 +    {
 +        script_search->closeFloater();
 +        delete script_search;
 +    }
 +
 +    delete mLiveFile;
 +    if (mSyntaxIDConnection.connected())
 +    {
 +        mSyntaxIDConnection.disconnect();
 +    }
 +}
 +
 +void LLLiveLSLEditor::experienceChanged()
 +{
 +    if(mScriptEd->getAssociatedExperience() != mExperiences->getSelectedValue().asUUID())
 +    {
 +        mScriptEd->enableSave(getIsModifiable());
 +        //getChildView("Save_btn")->setEnabled(true);
 +        mScriptEd->setAssociatedExperience(mExperiences->getSelectedValue().asUUID());
 +        updateExperiencePanel();
 +    }
 +}
 +
 +void LLLiveLSLEditor::onViewProfile( LLUICtrl *ui, void* userdata )
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
 +
 +    LLUUID id;
 +    if(self->mExperienceEnabled->get())
 +    {
 +        id=self->mScriptEd->getAssociatedExperience();
 +        if(id.notNull())
 +        {
 +             LLFloaterReg::showInstance("experience_profile", id, true);
 +        }
 +    }
 +
 +}
 +
 +void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata )
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
 +
 +    LLUUID id;
 +    if(self->mExperienceEnabled->get())
 +    {
 +        if(self->mScriptEd->getAssociatedExperience().isNull())
 +        {
 +            id=self->mExperienceIds.beginArray()->asUUID();
 +        }
 +    }
 +
 +    if(id != self->mScriptEd->getAssociatedExperience())
 +    {
 +        self->mScriptEd->enableSave(self->getIsModifiable());
 +    }
 +    self->mScriptEd->setAssociatedExperience(id);
 +
 +    self->updateExperiencePanel();
 +}
 +
 +bool LLScriptEdCore::postBuild()
 +{
 +    mErrorList = getChild<LLScrollListCtrl>("lsl errors");
 +
 +    mFunctions = getChild<LLComboBox>("Insert...");
 +
 +    childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);
 +
 +    mEditor = getChild<LLScriptEditor>("Script Editor");
 +
 +    childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
 +    childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,false));
 +    childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this));
 +
 +    initMenu();
 +
 +    mSyntaxIDConnection = LLSyntaxIdLSL::getInstance()->addSyntaxIDCallback(boost::bind(&LLScriptEdCore::processKeywords, this));
 +
 +    // Intialise keyword highlighting for the current simulator's version of LSL
 +    LLSyntaxIdLSL::getInstance()->initialize();
 +    processKeywords();
 +
 +    mCommitCallbackRegistrar.add("FontSize.Set", boost::bind(&LLScriptEdCore::onChangeFontSize, this, _2));
 +    mEnableCallbackRegistrar.add("FontSize.Check", boost::bind(&LLScriptEdCore::isFontSizeChecked, this, _2));
 +
 +    LLToggleableMenu *context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
 +        "menu_lsl_font_size.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 +    getChild<LLMenuButton>("font_btn")->setMenu(context_menu, LLMenuButton::MP_BOTTOM_LEFT, true);
 +
 +    return true;
 +}
 +
 +void LLScriptEdCore::processKeywords()
 +{
 +    LL_DEBUGS("SyntaxLSL") << "Processing keywords" << LL_ENDL;
 +    mEditor->clearSegments();
 +    mEditor->initKeywords();
 +    mEditor->loadKeywords();
 +
 +    string_vec_t primary_keywords;
 +    string_vec_t secondary_keywords;
 +    LLKeywordToken *token;
 +    LLKeywords::keyword_iterator_t token_it;
 +    for (token_it = mEditor->keywordsBegin(); token_it != mEditor->keywordsEnd(); ++token_it)
 +    {
 +        token = token_it->second;
 +        if (token->getType() == LLKeywordToken::TT_FUNCTION)
 +        {
 +            primary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
 +        }
 +        else
 +        {
 +            secondary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
 +        }
 +    }
 +    for (string_vec_t::const_iterator iter = primary_keywords.begin();
 +         iter!= primary_keywords.end(); ++iter)
 +    {
 +        mFunctions->add(*iter);
 +    }
 +    for (string_vec_t::const_iterator iter = secondary_keywords.begin();
 +         iter!= secondary_keywords.end(); ++iter)
 +    {
 +        mFunctions->add(*iter);
 +    }
 +}
 +
 +void LLScriptEdCore::initMenu()
 +{
 +    // *TODO: Skinning - make these callbacks data driven
 +    LLMenuItemCallGL* menuItem;
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Save");
 +    menuItem->setClickCallback(boost::bind(&LLScriptEdCore::doSave, this, false));
 +    menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Revert All Changes");
 +    menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnUndoChanges, this));
 +    menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::hasChanged, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Undo");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::undo, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canUndo, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Redo");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::redo, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canRedo, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Cut");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::cut, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCut, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Copy");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::copy, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canCopy, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Paste");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::paste, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canPaste, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Select All");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::selectAll, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canSelectAll, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Deselect");
 +    menuItem->setClickCallback(boost::bind(&LLTextEditor::deselect, mEditor));
 +    menuItem->setEnableCallback(boost::bind(&LLTextEditor::canDeselect, mEditor));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Search / Replace...");
 +    menuItem->setClickCallback(boost::bind(&LLFloaterScriptSearch::show, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Go to line...");
 +    menuItem->setClickCallback(boost::bind(&LLFloaterGotoLine::show, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("Keyword Help...");
 +    menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnDynamicHelp, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("LoadFromFile");
 +    menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnLoadFromFile, this));
 +    menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableLoadFromFileMenu, this));
 +
 +    menuItem = getChild<LLMenuItemCallGL>("SaveToFile");
 +    menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnSaveToFile, this));
 +    menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableSaveToFileMenu, this));
 +}
 +
 +void LLScriptEdCore::setScriptText(const std::string& text, bool is_valid)
 +{
 +    if (mEditor)
 +    {
 +        mEditor->setText(text);
 +        mHasScriptData = is_valid;
 +    }
 +}
 +
 +void LLScriptEdCore::makeEditorPristine()
 +{
 +    if (mEditor)
 +    {
 +        mEditor->makePristine();
 +    }
 +}
 +
 +bool LLScriptEdCore::loadScriptText(const std::string& filename)
 +{
 +    if (filename.empty())
 +    {
 +        LL_WARNS() << "Empty file name" << LL_ENDL;
 +        return false;
 +    }
 +
 +    LLFILE* file = LLFile::fopen(filename, "rb");       /*Flawfinder: ignore*/
 +    if (!file)
 +    {
 +        LL_WARNS() << "Error opening " << filename << LL_ENDL;
 +        return false;
 +    }
 +
 +    // read in the whole file
 +    fseek(file, 0L, SEEK_END);
 +    size_t file_length = (size_t) ftell(file);
 +    fseek(file, 0L, SEEK_SET);
 +    char* buffer = new char[file_length+1];
 +    size_t nread = fread(buffer, 1, file_length, file);
 +    if (nread < file_length)
 +    {
 +        LL_WARNS() << "Short read" << LL_ENDL;
 +    }
 +    buffer[nread] = '\0';
 +    fclose(file);
 +
 +    std::string text = std::string(buffer);
 +    LLStringUtil::replaceTabsWithSpaces(text, LLTextEditor::spacesPerTab());
 +
 +    mEditor->setText(text);
 +    delete[] buffer;
 +
 +    return true;
 +}
 +
 +bool LLScriptEdCore::writeToFile(const std::string& filename)
 +{
 +    LLFILE* fp = LLFile::fopen(filename, "wb");
 +    if (!fp)
 +    {
 +        LL_WARNS() << "Unable to write to " << filename << LL_ENDL;
 +
 +        LLSD row;
 +        row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?";
 +        row["columns"][0]["font"] = "SANSSERIF_SMALL";
 +        mErrorList->addElement(row);
 +        return false;
 +    }
 +
 +    std::string utf8text = mEditor->getText();
 +
 +    // Special case for a completely empty script - stuff in one space so it can store properly.  See SL-46889
 +    if (utf8text.size() == 0)
 +    {
 +        utf8text = " ";
 +    }
 +
 +    fputs(utf8text.c_str(), fp);
 +    fclose(fp);
 +    return true;
 +}
 +
 +void LLScriptEdCore::sync()
 +{
 +    // Sync with external editor.
 +    if (mLiveFile)
 +    {
 +        std::string tmp_file = mLiveFile->filename();
 +        llstat s;
 +        if (LLFile::stat(tmp_file, &s) == 0) // file exists
 +        {
 +            mLiveFile->ignoreNextUpdate();
 +            writeToFile(tmp_file);
 +        }
 +    }
 +}
 +
 +bool LLScriptEdCore::hasChanged()
 +{
 +    if (!mEditor) return false;
 +
 +    return ((!mEditor->isPristine() || mEnableSave) && mHasScriptData);
 +}
 +
 +void LLScriptEdCore::draw()
 +{
 +    bool script_changed = hasChanged();
 +    getChildView("Save_btn")->setEnabled(script_changed && !mScriptRemoved);
 +
 +    if( mEditor->hasFocus() )
 +    {
 +        S32 line = 0;
 +        S32 column = 0;
 +        mEditor->getCurrentLineAndColumn( &line, &column, false );  // don't include wordwrap
 +        LLStringUtil::format_map_t args;
 +        std::string cursor_pos;
 +        args["[LINE]"] = llformat ("%d", line);
 +        args["[COLUMN]"] = llformat ("%d", column);
 +        cursor_pos = LLTrans::getString("CursorPos", args);
 +        getChild<LLUICtrl>("line_col")->setValue(cursor_pos);
 +    }
 +    else
 +    {
 +        getChild<LLUICtrl>("line_col")->setValue(LLStringUtil::null);
 +    }
 +
 +    updateDynamicHelp();
 +
 +    LLPanel::draw();
 +}
 +
 +void LLScriptEdCore::updateDynamicHelp(bool immediate)
 +{
 +    LLFloater* help_floater = mLiveHelpHandle.get();
 +    if (!help_floater) return;
 +
 +    // update back and forward buttons
 +    LLButton* fwd_button = help_floater->getChild<LLButton>("fwd_btn");
 +    LLButton* back_button = help_floater->getChild<LLButton>("back_btn");
 +    LLMediaCtrl* browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +    back_button->setEnabled(browser->canNavigateBack());
 +    fwd_button->setEnabled(browser->canNavigateForward());
 +
 +    if (!immediate && !gSavedSettings.getBOOL("ScriptHelpFollowsCursor"))
 +    {
 +        return;
 +    }
 +
 +    LLTextSegmentPtr segment = NULL;
 +    std::vector<LLTextSegmentPtr> selected_segments;
 +    mEditor->getSelectedSegments(selected_segments);
 +    LLKeywordToken* token;
 +    // try segments in selection range first
 +    std::vector<LLTextSegmentPtr>::iterator segment_iter;
 +    for (segment_iter = selected_segments.begin(); segment_iter != selected_segments.end(); ++segment_iter)
 +    {
 +        token = (*segment_iter)->getToken();
 +        if(token && isKeyword(token))
 +        {
 +            segment = *segment_iter;
 +            break;
 +        }
 +    }
 +
 +    // then try previous segment in case we just typed it
 +    if (!segment)
 +    {
 +        const LLTextSegmentPtr test_segment = mEditor->getPreviousSegment();
 +        token = test_segment->getToken();
 +        if(token && isKeyword(token))
 +        {
 +            segment = test_segment;
 +        }
 +    }
 +
 +    if (segment)
 +    {
 +        if (segment->getToken() != mLastHelpToken)
 +        {
 +            mLastHelpToken = segment->getToken();
 +            mLiveHelpTimer.start();
 +        }
 +        if (immediate || (mLiveHelpTimer.getStarted() && mLiveHelpTimer.getElapsedTimeF32() > LIVE_HELP_REFRESH_TIME))
 +        {
 +            // Use Wtext since segment's start/end are made for wstring and will
 +            // result in a shift for case of multi-byte symbols inside std::string.
 +            LLWString segment_text = mEditor->getWText().substr(segment->getStart(), segment->getEnd() - segment->getStart());
 +            std::string help_string = wstring_to_utf8str(segment_text);
 +            setHelpPage(help_string);
 +            mLiveHelpTimer.stop();
 +        }
 +    }
 +    else
 +    {
 +        if (immediate)
 +        {
 +            setHelpPage(LLStringUtil::null);
 +        }
 +    }
 +}
 +
 +bool LLScriptEdCore::isKeyword(LLKeywordToken* token)
 +{
 +    switch(token->getType())
 +    {
 +        case LLKeywordToken::TT_CONSTANT:
 +        case LLKeywordToken::TT_CONTROL:
 +        case LLKeywordToken::TT_EVENT:
 +        case LLKeywordToken::TT_FUNCTION:
 +        case LLKeywordToken::TT_SECTION:
 +        case LLKeywordToken::TT_TYPE:
 +        case LLKeywordToken::TT_WORD:
 +            return true;
 +
 +        default:
 +            return false;
 +    }
 +}
 +
 +void LLScriptEdCore::setHelpPage(const std::string& help_string)
 +{
 +    LLFloater* help_floater = mLiveHelpHandle.get();
 +    if (!help_floater) return;
 +
 +    LLMediaCtrl* web_browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +    if (!web_browser) return;
 +
 +    LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo");
 +    if (!history_combo) return;
 +
 +    LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
 +
 +    url_string.setArg("[LSL_STRING]", help_string.empty() ? HELP_LSL_PORTAL_TOPIC : help_string);
 +
 +    addHelpItemToHistory(help_string);
 +
 +    web_browser->navigateTo(url_string);
 +
 +}
 +
 +
 +void LLScriptEdCore::addHelpItemToHistory(const std::string& help_string)
 +{
 +    if (help_string.empty()) return;
 +
 +    LLFloater* help_floater = mLiveHelpHandle.get();
 +    if (!help_floater) return;
 +
 +    LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo");
 +    if (!history_combo) return;
 +
 +    // separate history items from full item list
 +    if (mLiveHelpHistorySize == 0)
 +    {
 +        history_combo->addSeparator(ADD_TOP);
 +    }
 +    // delete all history items over history limit
 +    while(mLiveHelpHistorySize > MAX_HISTORY_COUNT - 1)
 +    {
 +        history_combo->remove(mLiveHelpHistorySize - 1);
 +        mLiveHelpHistorySize--;
 +    }
 +
 +    history_combo->setSimple(help_string);
 +    S32 index = history_combo->getCurrentIndex();
 +
 +    // if help string exists in the combo box
 +    if (index >= 0)
 +    {
 +        S32 cur_index = history_combo->getCurrentIndex();
 +        if (cur_index < mLiveHelpHistorySize)
 +        {
 +            // item found in history, bubble up to top
 +            history_combo->remove(history_combo->getCurrentIndex());
 +            mLiveHelpHistorySize--;
 +        }
 +    }
 +    history_combo->add(help_string, LLSD(help_string), ADD_TOP);
 +    history_combo->selectFirstItem();
 +    mLiveHelpHistorySize++;
 +}
 +
 +bool LLScriptEdCore::canClose()
 +{
 +    if(mForceClose || !hasChanged() || mScriptRemoved)
 +    {
 +        return true;
 +    }
 +    else
 +    {
 +        if(!mSaveDialogShown)
 +        {
 +            mSaveDialogShown = true;
 +            // Bring up view-modal dialog: Save changes? Yes, No, Cancel
 +            LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleSaveChangesDialog, this, _1, _2));
 +        }
 +        return false;
 +    }
 +}
 +
 +void LLScriptEdCore::setEnableEditing(bool enable)
 +{
 +    mEditor->setEnabled(enable);
 +    getChildView("Edit_btn")->setEnabled(enable);
 +}
 +
 +bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLSD& response )
 +{
 +    mSaveDialogShown = false;
 +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 +    switch( option )
 +    {
 +    case 0:  // "Yes"
 +        // close after saving
 +            doSave( true );
 +        break;
 +
 +    case 1:  // "No"
 +        mForceClose = true;
 +        // This will close immediately because mForceClose is true, so we won't
 +        // infinite loop with these dialogs. JC
 +        ((LLFloater*) getParent())->closeFloater();
 +        break;
 +
 +    case 2: // "Cancel"
 +    default:
 +        // If we were quitting, we didn't really mean it.
 +        LLAppViewer::instance()->abortQuit();
 +        break;
 +    }
 +    return false;
 +}
 +
 +void LLScriptEdCore::onBtnDynamicHelp()
 +{
 +    LLFloater* live_help_floater = mLiveHelpHandle.get();
 +    if (!live_help_floater)
 +    {
 +        live_help_floater = new LLFloater(LLSD());
 +        live_help_floater->buildFromFile("floater_lsl_guide.xml");
 +        LLFloater* parent = dynamic_cast<LLFloater*>(getParent());
 +        llassert(parent);
 +        if (parent)
 +            parent->addDependentFloater(live_help_floater, true);
 +        live_help_floater->childSetCommitCallback("lock_check", onCheckLock, this);
 +        live_help_floater->getChild<LLUICtrl>("lock_check")->setValue(gSavedSettings.getBOOL("ScriptHelpFollowsCursor"));
 +        live_help_floater->childSetCommitCallback("history_combo", onHelpComboCommit, this);
 +        live_help_floater->childSetAction("back_btn", onClickBack, this);
 +        live_help_floater->childSetAction("fwd_btn", onClickForward, this);
 +
 +        LLMediaCtrl* browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +        browser->setAlwaysRefresh(true);
 +
 +        LLComboBox* help_combo = live_help_floater->getChild<LLComboBox>("history_combo");
 +        LLKeywordToken *token;
 +        LLKeywords::keyword_iterator_t token_it;
 +        for (token_it = mEditor->keywordsBegin();
 +             token_it != mEditor->keywordsEnd();
 +             ++token_it)
 +        {
 +            token = token_it->second;
 +            help_combo->add(wstring_to_utf8str(token->getToken()));
 +        }
 +        help_combo->sortByName();
 +
 +        // re-initialize help variables
 +        mLastHelpToken = NULL;
 +        mLiveHelpHandle = live_help_floater->getHandle();
 +        mLiveHelpHistorySize = 0;
 +    }
 +
 +    bool visible = true;
 +    bool take_focus = true;
 +    live_help_floater->setVisible(visible);
 +    live_help_floater->setFrontmost(take_focus);
 +
 +    updateDynamicHelp(true);
 +}
 +
 +//static
 +void LLScriptEdCore::onClickBack(void* userdata)
 +{
 +    LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
 +    LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
 +    if (live_help_floater)
 +    {
 +        LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +        if (browserp)
 +        {
 +            browserp->navigateBack();
 +        }
 +    }
 +}
 +
 +//static
 +void LLScriptEdCore::onClickForward(void* userdata)
 +{
 +    LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
 +    LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
 +    if (live_help_floater)
 +    {
 +        LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +        if (browserp)
 +        {
 +            browserp->navigateForward();
 +        }
 +    }
 +}
 +
 +// static
 +void LLScriptEdCore::onCheckLock(LLUICtrl* ctrl, void* userdata)
 +{
 +    LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
 +
 +    // clear out token any time we lock the frame, so we will refresh web page immediately when unlocked
 +    gSavedSettings.setBOOL("ScriptHelpFollowsCursor", ctrl->getValue().asBoolean());
 +
 +    corep->mLastHelpToken = NULL;
 +}
 +
 +// static
 +void LLScriptEdCore::onBtnInsertSample(void* userdata)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*) userdata;
 +
 +    // Insert sample code
 +    self->mEditor->selectAll();
 +    self->mEditor->cut();
 +    self->mEditor->insertText(self->mSampleText);
 +}
 +
 +// static
 +void LLScriptEdCore::onHelpComboCommit(LLUICtrl* ctrl, void* userdata)
 +{
 +    LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
 +
 +    LLFloater* live_help_floater = corep->mLiveHelpHandle.get();
 +    if (live_help_floater)
 +    {
 +        std::string help_string = ctrl->getValue().asString();
 +
 +        corep->addHelpItemToHistory(help_string);
 +
 +        LLMediaCtrl* web_browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html");
 +        LLUIString url_string = gSavedSettings.getString("LSLHelpURL");
 +        url_string.setArg("[LSL_STRING]", help_string);
 +        web_browser->navigateTo(url_string);
 +    }
 +}
 +
 +// static
 +void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*) userdata;
 +
 +    // Insert sample code
 +    if(self->mEditor->getEnabled())
 +    {
 +        self->mEditor->insertText(self->mFunctions->getSimple());
 +    }
 +    self->mEditor->setFocus(true);
 +    self->setHelpPage(self->mFunctions->getSimple());
 +}
 +
 +void LLScriptEdCore::doSave( bool close_after_save )
 +{
 +    add(LLStatViewer::LSL_SAVES, 1);
 +
 +    if( mSaveCallback )
 +    {
 +        mSaveCallback( mUserdata, close_after_save );
 +    }
 +}
 +
 +void LLScriptEdCore::openInExternalEditor()
 +{
 +    delete mLiveFile; // deletes file
 +
 +    // Generate a suitable filename
 +    std::string script_name = mScriptName;
 +    std::string forbidden_chars = "<>:\"\\/|?*";
 +    for (std::string::iterator c = forbidden_chars.begin(); c != forbidden_chars.end(); c++)
 +    {
 +        script_name.erase(std::remove(script_name.begin(), script_name.end(), *c), script_name.end());
 +    }
 +    std::string filename = mContainer->getTmpFileName(script_name);
 +
 +    // Save the script to a temporary file.
 +    if (!writeToFile(filename))
 +    {
 +        // In case some characters from script name are forbidden
 +        // and not accounted for, name is too long or some other issue,
 +        // try file that doesn't include script name
 +        script_name.clear();
 +        filename = mContainer->getTmpFileName(script_name);
 +        writeToFile(filename);
 +    }
 +
 +    // Start watching file changes.
 +    mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1));
 +    mLiveFile->addToEventTimer();
 +
 +    // Open it in external editor.
 +    {
 +        LLExternalEditor ed;
 +        LLExternalEditor::EErrorCode status;
 +        std::string msg;
 +
 +        status = ed.setCommand("LL_SCRIPT_EDITOR");
 +        if (status != LLExternalEditor::EC_SUCCESS)
 +        {
 +            if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error.
 +            {
 +                msg = LLTrans::getString("ExternalEditorNotSet");
 +            }
 +            else
 +            {
 +                msg = LLExternalEditor::getErrorMessage(status);
 +            }
 +
 +            LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg));
 +            return;
 +        }
 +
 +        status = ed.run(filename);
 +        if (status != LLExternalEditor::EC_SUCCESS)
 +        {
 +            msg = LLExternalEditor::getErrorMessage(status);
 +            LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg));
 +        }
 +    }
 +}
 +
 +void LLScriptEdCore::onBtnUndoChanges()
 +{
 +    if( !mEditor->tryToRevertToPristineState() )
 +    {
 +        LLNotificationsUtil::add("ScriptCannotUndo", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleReloadFromServerDialog, this, _1, _2));
 +    }
 +}
 +
 +// static
 +void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*)user_data;
 +    LLScrollListItem* item = self->mErrorList->getFirstSelected();
 +    if(item)
 +    {
 +        // *FIX: replace with boost grep
 +        S32 row = 0;
 +        S32 column = 0;
 +        const LLScrollListCell* cell = item->getColumn(0);
 +        std::string line(cell->getValue().asString());
 +        line.erase(0, 1);
 +        LLStringUtil::replaceChar(line, ',',' ');
 +        LLStringUtil::replaceChar(line, ')',' ');
 +        sscanf(line.c_str(), "%d %d", &row, &column);
 +        //LL_INFOS() << "LLScriptEdCore::onErrorList() - " << row << ", "
 +        //<< column << LL_ENDL;
 +        self->mEditor->setCursor(row, column);
 +        self->mEditor->setFocus(true);
 +    }
 +}
 +
 +bool LLScriptEdCore::handleReloadFromServerDialog(const LLSD& notification, const LLSD& response )
 +{
 +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 +    switch( option )
 +    {
 +    case 0: // "Yes"
 +        if( mLoadCallback )
 +        {
 +            setScriptText(getString("loading"), false);
 +            mLoadCallback(mUserdata);
 +        }
 +        break;
 +
 +    case 1: // "No"
 +        break;
 +
 +    default:
 +        llassert(0);
 +        break;
 +    }
 +    return false;
 +}
 +
 +void LLScriptEdCore::selectFirstError()
 +{
 +    // Select the first item;
 +    mErrorList->selectFirstItem();
 +    onErrorList(mErrorList, this);
 +}
 +
 +
 +struct LLEntryAndEdCore
 +{
 +    LLScriptEdCore* mCore;
 +    LLEntryAndEdCore(LLScriptEdCore* core) :
 +        mCore(core)
 +        {}
 +};
 +
 +void LLScriptEdCore::deleteBridges()
 +{
 +    S32 count = mBridges.size();
 +    LLEntryAndEdCore* eandc;
 +    for(S32 i = 0; i < count; i++)
 +    {
 +        eandc = mBridges.at(i);
 +        delete eandc;
 +        mBridges[i] = NULL;
 +    }
 +    mBridges.clear();
 +}
 +
 +// virtual
 +bool LLScriptEdCore::handleKeyHere(KEY key, MASK mask)
 +{
 +    bool just_control = MASK_CONTROL == (mask & MASK_MODIFIERS);
 +
 +    if(('S' == key) && just_control)
 +    {
 +        if(mSaveCallback)
 +        {
 +            // don't close after saving
 +            mSaveCallback(mUserdata, false);
 +        }
 +
 +        return true;
 +    }
 +
 +    if(('F' == key) && just_control)
 +    {
 +        if(mSearchReplaceCallback)
 +        {
 +            mSearchReplaceCallback(mUserdata);
 +        }
 +
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +void LLScriptEdCore::onBtnLoadFromFile( void* data )
 +{
 +    LLFilePickerReplyThread::startPicker(boost::bind(&LLScriptEdCore::loadScriptFromFile, _1, data), LLFilePicker::FFLOAD_SCRIPT, false);
 +}
 +
 +void LLScriptEdCore::loadScriptFromFile(const std::vector<std::string>& filenames, void* data)
 +{
 +    std::string filename = filenames[0];
 +
 +    llifstream fin(filename.c_str());
 +
 +    std::string line;
 +    std::string text;
 +    std::string linetotal;
 +    while (!fin.eof())
 +    {
 +        getline(fin, line);
 +        text += line;
 +        if (!fin.eof())
 +        {
 +            text += "\n";
 +        }
 +    }
 +    fin.close();
 +
 +    // Only replace the script if there is something to replace with.
 +    LLScriptEdCore* self = (LLScriptEdCore*)data;
 +    if (self && (text.length() > 0))
 +    {
 +        self->mEditor->selectAll();
 +        LLWString script(utf8str_to_wstring(text));
 +        self->mEditor->insertText(script);
 +    }
 +}
 +
 +void LLScriptEdCore::onBtnSaveToFile( void* userdata )
 +{
 +    add(LLStatViewer::LSL_SAVES, 1);
 +
 +    LLScriptEdCore* self = (LLScriptEdCore*) userdata;
 +
 +    if( self->mSaveCallback )
 +    {
 +        LLFilePickerReplyThread::startPicker(boost::bind(&LLScriptEdCore::saveScriptToFile, _1, userdata), LLFilePicker::FFSAVE_SCRIPT, self->mScriptName);
 +    }
 +}
 +
 +void LLScriptEdCore::saveScriptToFile(const std::vector<std::string>& filenames, void* data)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*)data;
 +    if (self)
 +    {
 +        std::string filename = filenames[0];
 +        std::string scriptText = self->mEditor->getText();
 +        llofstream fout(filename.c_str());
 +        fout << (scriptText);
 +        fout.close();
 +        self->mSaveCallback(self->mUserdata, false);
 +    }
 +}
 +
 +bool LLScriptEdCore::canLoadOrSaveToFile( void* userdata )
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*) userdata;
 +    return self->mEditor->canLoadOrSaveToFile();
 +}
 +
 +// static
 +bool LLScriptEdCore::enableSaveToFileMenu(void* userdata)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*)userdata;
 +    if (!self || !self->mEditor) return false;
 +    return self->mEditor->canLoadOrSaveToFile();
 +}
 +
 +// static
 +bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata)
 +{
 +    LLScriptEdCore* self = (LLScriptEdCore*)userdata;
 +    return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : false;
 +}
 +
 +LLUUID LLScriptEdCore::getAssociatedExperience()const
 +{
 +    return mAssociatedExperience;
 +}
 +
 +void LLScriptEdCore::onChangeFontSize(const LLSD &userdata)
 +{
 +    const std::string font_name = userdata.asString();
 +    gSavedSettings.setString("LSLFontSizeName", font_name);
 +}
 +
 +bool LLScriptEdCore::isFontSizeChecked(const LLSD &userdata)
 +{
 +    const std::string current_size_name = LLScriptEditor::getScriptFontSize();
 +    const std::string size_name = userdata.asString();
 +
 +    return (size_name == current_size_name);
 +}
 +
 +    void LLLiveLSLEditor::setExperienceIds( const LLSD& experience_ids )
 +{
 +    mExperienceIds=experience_ids;
 +    updateExperiencePanel();
 +}
 +
 +
 +void LLLiveLSLEditor::updateExperiencePanel()
 +{
 +    if(mScriptEd->getAssociatedExperience().isNull())
 +    {
 +        mExperienceEnabled->set(false);
 +        mExperiences->setVisible(false);
 +        if(mExperienceIds.size()>0)
 +        {
 +            mExperienceEnabled->setEnabled(true);
 +            mExperienceEnabled->setToolTip(getString("add_experiences"));
 +        }
 +        else
 +        {
 +            mExperienceEnabled->setEnabled(false);
 +            mExperienceEnabled->setToolTip(getString("no_experiences"));
 +        }
 +        getChild<LLButton>("view_profile")->setVisible(false);
 +    }
 +    else
 +    {
 +        mExperienceEnabled->setToolTip(getString("experience_enabled"));
 +        mExperienceEnabled->setEnabled(getIsModifiable());
 +        mExperiences->setVisible(true);
 +        mExperienceEnabled->set(true);
 +        getChild<LLButton>("view_profile")->setToolTip(getString("show_experience_profile"));
 +        buildExperienceList();
 +    }
 +}
 +
 +void LLLiveLSLEditor::buildExperienceList()
 +{
 +    mExperiences->clearRows();
 +    bool foundAssociated=false;
 +    const LLUUID& associated = mScriptEd->getAssociatedExperience();
 +    LLUUID last;
 +    LLScrollListItem* item;
 +    for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it)
 +    {
 +        LLUUID id = it->asUUID();
 +        EAddPosition position = ADD_BOTTOM;
 +        if(id == associated)
 +        {
 +            foundAssociated = true;
 +            position = ADD_TOP;
 +        }
 +
 +        const LLSD& experience = LLExperienceCache::instance().get(id);
 +        if(experience.isUndefined())
 +        {
 +            mExperiences->add(getString("loading"), id, position);
 +            last = id;
 +        }
 +        else
 +        {
 +            std::string experience_name_string = experience[LLExperienceCache::NAME].asString();
 +            if (experience_name_string.empty())
 +            {
 +                experience_name_string = LLTrans::getString("ExperienceNameUntitled");
 +            }
 +            mExperiences->add(experience_name_string, id, position);
 +        }
 +    }
 +
 +    if(!foundAssociated )
 +    {
 +        const LLSD& experience = LLExperienceCache::instance().get(associated);
 +        if(experience.isDefined())
 +        {
 +            std::string experience_name_string = experience[LLExperienceCache::NAME].asString();
 +            if (experience_name_string.empty())
 +            {
 +                experience_name_string = LLTrans::getString("ExperienceNameUntitled");
 +            }
 +            item=mExperiences->add(experience_name_string, associated, ADD_TOP);
 +        }
 +        else
 +        {
 +            item=mExperiences->add(getString("loading"), associated, ADD_TOP);
 +            last = associated;
 +        }
 +        item->setEnabled(false);
 +    }
 +
 +    if(last.notNull())
 +    {
 +        mExperiences->setEnabled(false);
 +        LLExperienceCache::instance().get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this));
 +    }
 +    else
 +    {
 +        mExperiences->setEnabled(true);
 +        mExperiences->sortByName(true);
 +        mExperiences->setCurrentByIndex(mExperiences->getCurrentIndex());
 +        getChild<LLButton>("view_profile")->setVisible(true);
 +    }
 +}
 +
 +
 +void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id )
 +{
 +    mAssociatedExperience = experience_id;
 +}
 +
 +
 +
 +void LLLiveLSLEditor::requestExperiences()
 +{
 +    if (!getIsModifiable())
 +    {
 +        return;
 +    }
 +
 +    LLViewerRegion* region = gAgent.getRegion();
 +    if (region)
 +    {
 +        std::string lookup_url=region->getCapability("GetCreatorExperiences");
 +        if(!lookup_url.empty())
 +        {
 +            LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success =
 +                boost::bind(&LLLiveLSLEditor::receiveExperienceIds, _1, getDerivedHandle<LLLiveLSLEditor>());
 +
 +            LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url, success);
 +        }
 +    }
 +}
 +
 +/*static*/
 +void LLLiveLSLEditor::receiveExperienceIds(LLSD result, LLHandle<LLLiveLSLEditor> hparent)
 +{
 +    LLLiveLSLEditor* parent = hparent.get();
 +    if (!parent)
 +        return;
 +
 +    parent->setExperienceIds(result["experience_ids"]);
 +}
 +
 +
 +/// ---------------------------------------------------------------------------
 +/// LLScriptEdContainer
 +/// ---------------------------------------------------------------------------
 +
 +LLScriptEdContainer::LLScriptEdContainer(const LLSD& key) :
 +    LLPreview(key)
 +,   mScriptEd(NULL)
 +{
 +}
 +
 +std::string LLScriptEdContainer::getTmpFileName(const std::string& script_name)
 +{
 +    // Take script inventory item id (within the object inventory)
 +    // to consideration so that it's possible to edit multiple scripts
 +    // in the same object inventory simultaneously (STORM-781).
 +    std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString();
 +
 +    // Use MD5 sum to make the file name shorter and not exceed maximum path length.
 +    char script_id_hash_str[33];               /* Flawfinder: ignore */
 +    LLMD5 script_id_hash((const U8 *)script_id.c_str());
 +    script_id_hash.hex_digest(script_id_hash_str);
 +
 +    if (script_name.empty())
 +    {
 +        return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl";
 +    }
 +    else
 +    {
 +        return std::string(LLFile::tmpdir()) + "sl_script_" + script_name + "_" + script_id_hash_str + ".lsl";
 +    }
 +}
 +
 +bool LLScriptEdContainer::onExternalChange(const std::string& filename)
 +{
 +    if (!mScriptEd->loadScriptText(filename))
 +    {
 +        return false;
 +    }
 +
 +    // Disable sync to avoid recursive load->save->load calls.
 +    saveIfNeeded(false);
 +    return true;
 +}
 +
 +bool LLScriptEdContainer::handleKeyHere(KEY key, MASK mask)
 +{
 +    if (('A' == key) && (MASK_CONTROL == (mask & MASK_MODIFIERS)))
 +    {
 +        mScriptEd->selectAll();
 +        return true;
 +    }
 +
 +    if (!LLPreview::handleKeyHere(key, mask))
 +    {
 +        return mScriptEd->handleKeyHere(key, mask);
 +    }
 +    return true;
 +}
 +
 +/// ---------------------------------------------------------------------------
 +/// LLPreviewLSL
 +/// ---------------------------------------------------------------------------
 +
 +struct LLScriptSaveInfo
 +{
 +    LLUUID mItemUUID;
 +    std::string mDescription;
 +    LLTransactionID mTransactionID;
 +
 +    LLScriptSaveInfo(const LLUUID& uuid, const std::string& desc, LLTransactionID tid) :
 +        mItemUUID(uuid), mDescription(desc),  mTransactionID(tid) {}
 +};
 +
 +
 +
 +//static
 +void* LLPreviewLSL::createScriptEdPanel(void* userdata)
 +{
 +
 +    LLPreviewLSL *self = (LLPreviewLSL*)userdata;
 +
 +    self->mScriptEd =  new LLScriptEdCore(
 +                                   self,
 +                                   HELLO_LSL,
 +                                   self->getHandle(),
 +                                   LLPreviewLSL::onLoad,
 +                                   LLPreviewLSL::onSave,
 +                                   LLPreviewLSL::onSearchReplace,
 +                                   self,
 +                                   false,
 +                                   0);
 +    return self->mScriptEd;
 +}
 +
 +
 +LLPreviewLSL::LLPreviewLSL(const LLSD& key )
 +:   LLScriptEdContainer(key),
 +    mPendingUploads(0)
 +{
 +    mFactoryMap["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this);
 +
 +    mItemObserver = new LLScriptMovedObserver(this);
 +}
 +
 +LLPreviewLSL::~LLPreviewLSL()
 +{
 +    delete mItemObserver;
 +    mItemObserver = NULL;
 +}
 +
 +// virtual
 +bool LLPreviewLSL::postBuild()
 +{
 +    const LLInventoryItem* item = getItem();
 +
 +    llassert(item);
 +    if (item)
 +    {
 +        getChild<LLUICtrl>("desc")->setValue(item->getDescription());
 +
 +        std::string item_path = get_category_path(item->getParentUUID());
 +        getChild<LLUICtrl>("path_txt")->setValue(item_path);
 +        getChild<LLUICtrl>("path_txt")->setToolTip(item_path);
 +    }
 +    childSetCommitCallback("desc", LLPreview::onText, this);
 +    getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
 +
 +    return LLPreview::postBuild();
 +}
 +
 +void LLPreviewLSL::draw()
 +{
 +    const LLInventoryItem* item = getItem();
 +    if(!item)
 +    {
 +        setTitle(LLTrans::getString("ScriptWasDeleted"));
 +        mScriptEd->setItemRemoved(true);
 +    }
 +    else if (mDirty)
 +    {
 +        std::string item_path = get_category_path(item->getParentUUID());
 +        getChild<LLUICtrl>("path_txt")->setValue(item_path);
 +        getChild<LLUICtrl>("path_txt")->setToolTip(item_path);
 +    }
 +    LLPreview::draw();
 +}
 +// virtual
 +void LLPreviewLSL::callbackLSLCompileSucceeded()
 +{
 +    LL_INFOS() << "LSL Bytecode saved" << LL_ENDL;
 +    mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
 +    mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
 +    closeIfNeeded();
 +}
 +
 +// virtual
 +void LLPreviewLSL::callbackLSLCompileFailed(const LLSD& compile_errors)
 +{
 +    LL_INFOS() << "Compile failed!" << LL_ENDL;
 +
 +    for(LLSD::array_const_iterator line = compile_errors.beginArray();
 +        line < compile_errors.endArray();
 +        line++)
 +    {
 +        LLSD row;
 +        std::string error_message = line->asString();
 +        LLStringUtil::stripNonprintable(error_message);
 +        row["columns"][0]["value"] = error_message;
 +        row["columns"][0]["font"] = "OCRA";
 +        mScriptEd->mErrorList->addElement(row);
 +    }
 +    mScriptEd->selectFirstError();
 +    closeIfNeeded();
 +}
 +
 +void LLPreviewLSL::loadAsset()
 +{
 +    // *HACK: we poke into inventory to see if it's there, and if so,
 +    // then it might be part of the inventory library. If it's in the
 +    // library, then you can see the script, but not modify it.
 +    const LLInventoryItem* item = gInventory.getItem(mItemUUID);
 +    bool is_library = item
 +        && !gInventory.isObjectDescendentOf(mItemUUID,
 +                                            gInventory.getRootFolderID());
 +    if(!item)
 +    {
 +        // do the more generic search.
 +        getItem();
 +    }
 +    if(item)
 +    {
 +        bool is_copyable = gAgent.allowOperation(PERM_COPY,
 +                                item->getPermissions(), GP_OBJECT_MANIPULATE);
 +        bool is_modifiable = gAgent.allowOperation(PERM_MODIFY,
 +                                item->getPermissions(), GP_OBJECT_MANIPULATE);
 +        if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library)))
 +        {
 +            LLUUID* new_uuid = new LLUUID(mItemUUID);
 +            gAssetStorage->getInvItemAsset(LLHost(),
 +                                        gAgent.getID(),
 +                                        gAgent.getSessionID(),
 +                                        item->getPermissions().getOwner(),
 +                                        LLUUID::null,
 +                                        item->getUUID(),
 +                                        item->getAssetUUID(),
 +                                        item->getType(),
 +                                        &LLPreviewLSL::onLoadComplete,
 +                                        (void*)new_uuid,
 +                                        true);
 +            mAssetStatus = PREVIEW_ASSET_LOADING;
 +        }
 +        else
 +        {
 +            mScriptEd->setScriptText(mScriptEd->getString("can_not_view"), false);
 +            mScriptEd->mEditor->makePristine();
 +            mScriptEd->mFunctions->setEnabled(false);
 +            mAssetStatus = PREVIEW_ASSET_LOADED;
 +        }
 +        getChildView("lock")->setVisible( !is_modifiable);
 +        mScriptEd->getChildView("Insert...")->setEnabled(is_modifiable);
 +    }
 +    else
 +    {
 +        mScriptEd->setScriptText(std::string(HELLO_LSL), true);
 +        mScriptEd->setEnableEditing(true);
 +        mAssetStatus = PREVIEW_ASSET_LOADED;
 +    }
 +}
 +
 +
 +bool LLPreviewLSL::canClose()
 +{
 +    return mScriptEd->canClose();
 +}
 +
 +void LLPreviewLSL::closeIfNeeded()
 +{
 +    // Find our window and close it if requested.
 +    getWindow()->decBusyCount();
 +    mPendingUploads--;
 +    if (mPendingUploads <= 0 && mCloseAfterSave)
 +    {
 +        closeFloater();
 +    }
 +}
 +
 +void LLPreviewLSL::onSearchReplace(void* userdata)
 +{
 +    LLPreviewLSL* self = (LLPreviewLSL*)userdata;
 +    LLScriptEdCore* sec = self->mScriptEd;
 +    LLFloaterScriptSearch::show(sec);
 +}
 +
 +// static
 +void LLPreviewLSL::onLoad(void* userdata)
 +{
 +    LLPreviewLSL* self = (LLPreviewLSL*)userdata;
 +    self->loadAsset();
 +}
 +
 +// static
 +void LLPreviewLSL::onSave(void* userdata, bool close_after_save)
 +{
 +    LLPreviewLSL* self = (LLPreviewLSL*)userdata;
 +    self->mCloseAfterSave = close_after_save;
 +    self->saveIfNeeded();
 +}
 +
 +/*static*/
 +void LLPreviewLSL::finishedLSLUpload(LLUUID itemId, LLSD response)
 +{
 +    // Find our window and close it if requested.
 +    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(itemId));
 +    if (preview)
 +    {
 +        // Bytecode save completed
 +        if (response["compiled"])
 +        {
 +            preview->callbackLSLCompileSucceeded();
 +        }
 +        else
 +        {
 +            preview->callbackLSLCompileFailed(response["errors"]);
 +        }
 +    }
 +}
 +
 +bool LLPreviewLSL::failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, std::string reason)
 +{
 +    LLSD floater_key;
 +    if (taskId.notNull())
 +    {
 +        floater_key["taskid"] = taskId;
 +        floater_key["itemid"] = itemId;
 +    }
 +    else
 +    {
 +        floater_key = LLSD(itemId);
 +    }
 +
 +    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", floater_key);
 +    if (preview)
 +    {
 +        // unfreeze floater
 +        LLSD errors;
 +        errors.append(LLTrans::getString("UploadFailed") + reason);
 +        preview->callbackLSLCompileFailed(errors);
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +// Save needs to compile the text in the buffer. If the compile
 +// succeeds, then save both assets out to the database. If the compile
 +// fails, go ahead and save the text anyway.
 +void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/)
 +{
 +    if (!mScriptEd->hasChanged())
 +    {
 +        return;
 +    }
 +
 +    mPendingUploads = 0;
 +    mScriptEd->mErrorList->deleteAllItems();
 +    mScriptEd->mEditor->makePristine();
 +
 +    if (sync)
 +    {
 +        mScriptEd->sync();
 +    }
 +
 +    if (!gAgent.getRegion()) return;
 +    const LLInventoryItem *inv_item = getItem();
 +    // save it out to asset server
 +    std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent");
 +    if(inv_item)
 +    {
 +        getWindow()->incBusyCount();
 +        mPendingUploads++;
 +        if (!url.empty())
 +        {
 +            std::string buffer(mScriptEd->mEditor->getText());
 +
 +            LLUUID old_asset_id = inv_item->getAssetUUID().isNull() ? mScriptEd->getAssetID() : inv_item->getAssetUUID();
 +
 +            LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mItemUUID, buffer,
 +                [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) {
 +                    LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT);
 +                    LLPreviewLSL::finishedLSLUpload(itemId, response);
 +                },
 +                LLPreviewLSL::failedLSLUpload));
 +
 +            LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
 +        }
 +    }
 +}
 +
 +// static
 +void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type,
 +                                  void* user_data, S32 status, LLExtStat ext_status)
 +{
 +    LL_DEBUGS() << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid
 +         << LL_ENDL;
 +    LLUUID* item_uuid = (LLUUID*)user_data;
 +    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *item_uuid);
 +    if( preview )
 +    {
 +        if(0 == status)
 +        {
 +            LLFileSystem file(asset_uuid, type);
 +            S32 file_length = file.getSize();
 +
 +            std::vector<char> buffer(file_length+1);
 +            file.read((U8*)&buffer[0], file_length);
 +
 +            // put a EOS at the end
 +            buffer[file_length] = 0;
 +            preview->mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), true);
 +            preview->mScriptEd->mEditor->makePristine();
 +
 +            std::string script_name = DEFAULT_SCRIPT_NAME;
 +            LLInventoryItem* item = gInventory.getItem(*item_uuid);
 +            bool is_modifiable = false;
 +            if (item)
 +            {
 +                if (!item->getName().empty())
 +                {
 +                    script_name = item->getName();
 +                }
 +                if (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))
 +                {
 +                    is_modifiable = true;
 +                }
 +            }
 +            preview->mScriptEd->setScriptName(script_name);
 +            preview->mScriptEd->setEnableEditing(is_modifiable);
 +            preview->mScriptEd->setAssetID(asset_uuid);
 +            preview->mAssetStatus = PREVIEW_ASSET_LOADED;
 +        }
 +        else
 +        {
 +            if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
 +                LL_ERR_FILE_EMPTY == status)
 +            {
 +                LLNotificationsUtil::add("ScriptMissing");
 +            }
 +            else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
 +            {
 +                LLNotificationsUtil::add("ScriptNoPermissions");
 +            }
 +            else
 +            {
 +                LLNotificationsUtil::add("UnableToLoadScript");
 +            }
 +
 +            preview->mAssetStatus = PREVIEW_ASSET_ERROR;
 +            LL_WARNS() << "Problem loading script: " << status << LL_ENDL;
 +        }
 +    }
 +    delete item_uuid;
 +}
 +
 +
 +/// ---------------------------------------------------------------------------
 +/// LLLiveLSLEditor
 +/// ---------------------------------------------------------------------------
 +
 +
 +//static
 +void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
 +{
 +    LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata;
 +
 +    self->mScriptEd =  new LLScriptEdCore(
 +                                   self,
 +                                   HELLO_LSL,
 +                                   self->getHandle(),
 +                                   &LLLiveLSLEditor::onLoad,
 +                                   &LLLiveLSLEditor::onSave,
 +                                   &LLLiveLSLEditor::onSearchReplace,
 +                                   self,
 +                                   true,
 +                                   0);
 +    return self->mScriptEd;
 +}
 +
 +
 +LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) :
 +    LLScriptEdContainer(key),
 +    mAskedForRunningInfo(false),
 +    mHaveRunningInfo(false),
 +    mCloseAfterSave(false),
 +    mPendingUploads(0),
 +    mIsModifiable(false),
 +    mIsNew(false),
 +    mIsSaving(false),
 +    mObjectName("")
 +{
 +    mFactoryMap["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
 +}
 +
 +bool LLLiveLSLEditor::postBuild()
 +{
 +    childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this);
 +    getChildView("running")->setEnabled(false);
 +
 +    childSetAction("Reset",&LLLiveLSLEditor::onReset,this);
 +    getChildView("Reset")->setEnabled(true);
 +
 +    mMonoCheckbox = getChild<LLCheckBoxCtrl>("mono");
 +    childSetCommitCallback("mono", &LLLiveLSLEditor::onMonoCheckboxClicked, this);
 +    getChildView("mono")->setEnabled(true);
 +
 +    mScriptEd->mEditor->makePristine();
 +    mScriptEd->mEditor->setFocus(true);
 +
 +
 +    mExperiences = getChild<LLComboBox>("Experiences...");
 +    mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this));
 +
 +    mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp");
 +
 +    childSetCommitCallback("enable_xp", onToggleExperience, this);
 +    childSetCommitCallback("view_profile", onViewProfile, this);
 +
 +
 +    return LLPreview::postBuild();
 +}
 +
 +// virtual
 +void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id,
 +                                                  const LLUUID& item_id,
 +                                                  bool is_script_running)
 +{
 +    LL_DEBUGS() << "LSL Bytecode saved" << LL_ENDL;
 +    mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
 +    mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
 +    getChild<LLCheckBoxCtrl>("running")->set(is_script_running);
 +    mIsSaving = false;
 +    closeIfNeeded();
 +}
 +
 +// virtual
 +void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors)
 +{
 +    LL_DEBUGS() << "Compile failed!" << LL_ENDL;
 +    for(LLSD::array_const_iterator line = compile_errors.beginArray();
 +        line < compile_errors.endArray();
 +        line++)
 +    {
 +        LLSD row;
 +        std::string error_message = line->asString();
 +        LLStringUtil::stripNonprintable(error_message);
 +        row["columns"][0]["value"] = error_message;
 +        // *TODO: change to "MONOSPACE" and change llfontgl.cpp?
 +        row["columns"][0]["font"] = "OCRA";
 +        mScriptEd->mErrorList->addElement(row);
 +    }
 +    mScriptEd->selectFirstError();
 +    mIsSaving = false;
 +    closeIfNeeded();
 +}
 +
 +void LLLiveLSLEditor::loadAsset()
 +{
 +    //LL_INFOS() << "LLLiveLSLEditor::loadAsset()" << LL_ENDL;
 +    if(!mIsNew)
 +    {
 +        LLViewerObject* object = gObjectList.findObject(mObjectUUID);
 +        if(object)
 +        {
 +            LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID));
 +
 +            if(item)
 +            {
 +                LLViewerRegion* region = object->getRegion();
 +                std::string url = std::string();
 +                if(region)
 +                {
 +                    url = region->getCapability("GetMetadata");
 +                }
 +                LLExperienceCache::instance().fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), url,
 +                    boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1));
 +
 +                bool isGodlike = gAgent.isGodlike();
 +                bool copyManipulate = gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE);
 +                mIsModifiable = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE);
 +
 +                if(!isGodlike && (!copyManipulate || !mIsModifiable))
 +                {
 +                    mItem = new LLViewerInventoryItem(item);
 +                    mScriptEd->setScriptText(getString("not_allowed"), false);
 +                    mScriptEd->mEditor->makePristine();
 +                    mScriptEd->enableSave(false);
 +                    mAssetStatus = PREVIEW_ASSET_LOADED;
 +                }
 +                else if(copyManipulate || isGodlike)
 +                {
 +                    mItem = new LLViewerInventoryItem(item);
 +                    // request the text from the object
 +                LLSD* user_data = new LLSD();
 +                user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID);
 +                    gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
 +                        gAgent.getID(),
 +                        gAgent.getSessionID(),
 +                        item->getPermissions().getOwner(),
 +                        object->getID(),
 +                        item->getUUID(),
 +                        item->getAssetUUID(),
 +                        item->getType(),
 +                        &LLLiveLSLEditor::onLoadComplete,
 +                        (void*)user_data,
 +                        true);
 +                    LLMessageSystem* msg = gMessageSystem;
 +                    msg->newMessageFast(_PREHASH_GetScriptRunning);
 +                    msg->nextBlockFast(_PREHASH_Script);
 +                    msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID);
 +                    msg->addUUIDFast(_PREHASH_ItemID, mItemUUID);
 +                    msg->sendReliable(object->getRegion()->getHost());
 +                    mAskedForRunningInfo = true;
 +                    mAssetStatus = PREVIEW_ASSET_LOADING;
 +                }
 +            }
 +
 +            if(mItem.isNull())
 +            {
 +                mScriptEd->setScriptText(LLStringUtil::null, false);
 +                mScriptEd->mEditor->makePristine();
 +                mAssetStatus = PREVIEW_ASSET_LOADED;
 +                mIsModifiable = false;
 +            }
 +
 +            refreshFromItem();
 +            getChild<LLUICtrl>("obj_name")->setValue(mObjectName);
 +            // This is commented out, because we don't completely
 +            // handle script exports yet.
 +            /*
 +            // request the exports from the object
 +            gMessageSystem->newMessage("GetScriptExports");
 +            gMessageSystem->nextBlock("ScriptBlock");
 +            gMessageSystem->addUUID("AgentID", gAgent.getID());
 +            U32 local_id = object->getLocalID();
 +            gMessageSystem->addData("LocalID", &local_id);
 +            gMessageSystem->addUUID("ItemID", mItemUUID);
 +            LLHost host(object->getRegion()->getIP(),
 +                        object->getRegion()->getPort());
 +            gMessageSystem->sendReliable(host);
 +            */
 +        }
 +    }
 +    else
 +    {
 +        mScriptEd->setScriptText(std::string(HELLO_LSL), true);
 +        mScriptEd->enableSave(false);
 +        LLPermissions perm;
 +        perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID());
 +        perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
 +        mItem = new LLViewerInventoryItem(mItemUUID,
 +                                          mObjectUUID,
 +                                          perm,
 +                                          LLUUID::null,
 +                                          LLAssetType::AT_LSL_TEXT,
 +                                          LLInventoryType::IT_LSL,
 +                                          DEFAULT_SCRIPT_NAME,
 +                                          DEFAULT_SCRIPT_DESC,
 +                                          LLSaleInfo::DEFAULT,
 +                                          LLInventoryItemFlags::II_FLAGS_NONE,
 +                                          time_corrected());
 +        mAssetStatus = PREVIEW_ASSET_LOADED;
 +    }
 +
 +    requestExperiences();
 +}
 +
 +// static
 +void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id,
 +                                     LLAssetType::EType type,
 +                                     void* user_data, S32 status, LLExtStat ext_status)
 +{
 +    LL_DEBUGS() << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id
 +         << LL_ENDL;
 +    LLSD* floater_key = (LLSD*)user_data;
 +
 +    LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", *floater_key);
 +
 +    if(instance )
 +    {
 +        if( LL_ERR_NOERR == status )
 +        {
 +            instance->loadScriptText(asset_id, type);
 +            instance->mScriptEd->setEnableEditing(true);
 +            instance->mAssetStatus = PREVIEW_ASSET_LOADED;
 +            instance->mScriptEd->setAssetID(asset_id);
 +        }
 +        else
 +        {
 +            if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
 +                LL_ERR_FILE_EMPTY == status)
 +            {
 +                LLNotificationsUtil::add("ScriptMissing");
 +            }
 +            else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
 +            {
 +                LLNotificationsUtil::add("ScriptNoPermissions");
 +            }
 +            else
 +            {
 +                LLNotificationsUtil::add("UnableToLoadScript");
 +            }
 +            instance->mAssetStatus = PREVIEW_ASSET_ERROR;
 +        }
 +    }
 +
 +    delete floater_key;
 +}
 +
 +void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type)
 +{
 +    LLFileSystem file(uuid, type);
 +    S32 file_length = file.getSize();
 +    std::vector<char> buffer(file_length + 1);
 +    file.read((U8*)&buffer[0], file_length);
 +
 +    if (file.getLastBytesRead() != file_length ||
 +        file_length <= 0)
 +    {
 +        LL_WARNS() << "Error reading " << uuid << ":" << type << LL_ENDL;
 +    }
 +
 +    buffer[file_length] = '\0';
 +
 +    mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), true);
 +    mScriptEd->makeEditorPristine();
 +
 +    std::string script_name = DEFAULT_SCRIPT_NAME;
 +    const LLInventoryItem* inv_item = getItem();
 +
 +    if(inv_item)
 +    {
 +        script_name = inv_item->getName();
 +    }
 +    mScriptEd->setScriptName(script_name);
 +}
 +
 +
 +void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata )
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
 +    LLViewerObject* object = gObjectList.findObject( self->mObjectUUID );
 +    LLCheckBoxCtrl* runningCheckbox = self->getChild<LLCheckBoxCtrl>("running");
 +    bool running =  runningCheckbox->get();
 +    //self->mRunningCheckbox->get();
 +    if( object )
 +    {
 +        LLMessageSystem* msg = gMessageSystem;
 +        msg->newMessageFast(_PREHASH_SetScriptRunning);
 +        msg->nextBlockFast(_PREHASH_AgentData);
 +        msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 +        msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 +        msg->nextBlockFast(_PREHASH_Script);
 +        msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID);
 +        msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID);
 +        msg->addBOOLFast(_PREHASH_Running, running);
 +        msg->sendReliable(object->getRegion()->getHost());
 +    }
 +    else
 +    {
 +        runningCheckbox->set(!running);
 +        LLNotificationsUtil::add("CouldNotStartStopScript");
 +    }
 +}
 +
 +void LLLiveLSLEditor::onReset(void *userdata)
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
 +
 +    LLViewerObject* object = gObjectList.findObject( self->mObjectUUID );
 +    if(object)
 +    {
 +        LLMessageSystem* msg = gMessageSystem;
 +        msg->newMessageFast(_PREHASH_ScriptReset);
 +        msg->nextBlockFast(_PREHASH_AgentData);
 +        msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 +        msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 +        msg->nextBlockFast(_PREHASH_Script);
 +        msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectUUID);
 +        msg->addUUIDFast(_PREHASH_ItemID, self->mItemUUID);
 +        msg->sendReliable(object->getRegion()->getHost());
 +    }
 +    else
 +    {
 +        LLNotificationsUtil::add("CouldNotStartStopScript");
 +    }
 +}
 +
 +void LLLiveLSLEditor::draw()
 +{
 +    LLViewerObject* object = gObjectList.findObject(mObjectUUID);
 +    LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running");
 +    if(object && mAskedForRunningInfo && mHaveRunningInfo)
 +    {
 +        if(object->permAnyOwner())
 +        {
 +            runningCheckbox->setLabel(getString("script_running"));
 +            runningCheckbox->setEnabled(!mIsSaving);
 +        }
 +        else
 +        {
 +            runningCheckbox->setLabel(getString("public_objects_can_not_run"));
 +            runningCheckbox->setEnabled(false);
 +
 +            // *FIX: Set it to false so that the ui is correct for
 +            // a box that is released to public. It could be
 +            // incorrect after a release/claim cycle, but will be
 +            // correct after clicking on it.
 +            runningCheckbox->set(false);
 +            mMonoCheckbox->set(false);
 +        }
 +    }
 +    else if(!object)
 +    {
 +        // HACK: Display this information in the title bar.
 +        // Really ought to put in main window.
 +        setTitle(LLTrans::getString("ObjectOutOfRange"));
 +        runningCheckbox->setEnabled(false);
 +        mMonoCheckbox->setEnabled(false);
 +        // object may have fallen out of range.
 +        mHaveRunningInfo = false;
 +    }
 +
 +    LLPreview::draw();
 +}
 +
 +
 +void LLLiveLSLEditor::onSearchReplace(void* userdata)
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
 +
 +    LLScriptEdCore* sec = self->mScriptEd;
 +    LLFloaterScriptSearch::show(sec);
 +}
 +
 +struct LLLiveLSLSaveData
 +{
 +    LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, bool active);
 +    LLUUID mSaveObjectID;
 +    LLPointer<LLViewerInventoryItem> mItem;
 +    bool mActive;
 +};
 +
 +LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id,
 +                                     const LLViewerInventoryItem* item,
 +                                     bool active) :
 +    mSaveObjectID(id),
 +    mActive(active)
 +{
 +    llassert(item);
 +    mItem = new LLViewerInventoryItem(item);
 +}
 +
 +/*static*/
 +void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, bool isRunning)
 +{
 +    LLSD floater_key;
 +    floater_key["taskid"] = taskId;
 +    floater_key["itemid"] = itemId;
 +
 +    LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key);
 +    if (preview)
 +    {
 +        preview->mItem->setAssetUUID(newAssetId);
 +        preview->mScriptEd->setAssetID(newAssetId);
 +
 +        // Bytecode save completed
 +        if (response["compiled"])
 +        {
 +            preview->callbackLSLCompileSucceeded(taskId, itemId, isRunning);
 +        }
 +        else
 +        {
 +            preview->callbackLSLCompileFailed(response["errors"]);
 +        }
 +    }
 +}
 +
 +// virtual
 +void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)
 +{
 +    LLViewerObject* object = gObjectList.findObject(mObjectUUID);
 +    if (!object)
 +    {
 +        LLNotificationsUtil::add("SaveScriptFailObjectNotFound");
 +        return;
 +    }
 +
 +    if (mItem.isNull() || !mItem->isFinished())
 +    {
 +        // $NOTE: While the error message may not be exactly correct,
 +        // it's pretty close.
 +        LLNotificationsUtil::add("SaveScriptFailObjectNotFound");
 +        return;
 +    }
 +
 +    // get the latest info about it. We used to be losing the script
 +    // name on save, because the viewer object version of the item,
 +    // and the editor version would get out of synch. Here's a good
 +    // place to synch them back up.
 +    LLInventoryItem* inv_item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID));
 +    if (inv_item)
 +    {
 +        mItem->copyItem(inv_item);
 +    }
 +
 +    // Don't need to save if we're pristine
 +    if (!mScriptEd->hasChanged())
 +    {
 +        return;
 +    }
 +
 +    mPendingUploads = 0;
 +
 +    // save the script
 +    mScriptEd->enableSave(false);
 +    mScriptEd->mEditor->makePristine();
 +    mScriptEd->mErrorList->deleteAllItems();
 +    mScriptEd->mEditor->makePristine();
 +
 +    if (sync)
 +    {
 +        mScriptEd->sync();
 +    }
 +
 +    bool isRunning = getChild<LLCheckBoxCtrl>("running")->get();
 +    getWindow()->incBusyCount();
 +    mPendingUploads++;
 +
 +    std::string url = object->getRegion()->getCapability("UpdateScriptTask");
 +
 +    if (!url.empty())
 +    {
 +        std::string buffer(mScriptEd->mEditor->getText());
 +        LLUUID old_asset_id = mScriptEd->getAssetID();
 +
 +        LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLScriptAssetUpload>(mObjectUUID, mItemUUID,
 +                monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2,
 +                isRunning, mScriptEd->getAssociatedExperience(), buffer,
 +                [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) {
 +                        LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT);
 +                        LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning);
 +                },
 +                nullptr)); // needs failure handling?
 +
 +        LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
 +    }
 +}
 +
 +bool LLLiveLSLEditor::canClose()
 +{
 +    return mScriptEd->canClose();
 +}
 +
 +void LLLiveLSLEditor::closeIfNeeded()
 +{
 +    getWindow()->decBusyCount();
 +    mPendingUploads--;
 +    if ((mPendingUploads <= 0) && mCloseAfterSave)
 +    {
 +        closeFloater();
 +    }
 +}
 +
 +// static
 +void LLLiveLSLEditor::onLoad(void* userdata)
 +{
 +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
 +    self->loadAsset();
 +}
 +
 +// static
 +void LLLiveLSLEditor::onSave(void* userdata, bool close_after_save)
 +{
 +    if (LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata)
 +    {
 +        self->mCloseAfterSave = close_after_save;
 +        self->mScriptEd->mErrorList->setCommentText("");
 +        self->saveIfNeeded();
 +    }
 +}
 +
 +// static
 +void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**)
 +{
 +    LLUUID item_id;
 +    LLUUID object_id;
 +    msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id);
 +    msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id);
 +
 +    LLSD floater_key;
 +    floater_key["taskid"] = object_id;
 +    floater_key["itemid"] = item_id;
 +    if (LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key))
 +    {
 +        instance->mHaveRunningInfo = true;
 +        bool running;
 +        msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running);
 +        LLCheckBoxCtrl* runningCheckbox = instance->getChild<LLCheckBoxCtrl>("running");
 +        runningCheckbox->set(running);
 +        bool mono;
 +        msg->getBOOLFast(_PREHASH_Script, "Mono", mono);
 +        LLCheckBoxCtrl* monoCheckbox = instance->getChild<LLCheckBoxCtrl>("mono");
 +        monoCheckbox->setEnabled(instance->getIsModifiable() && have_script_upload_cap(object_id));
 +        monoCheckbox->set(mono);
 +    }
 +}
 +
 +void LLLiveLSLEditor::onMonoCheckboxClicked(LLUICtrl*, void* userdata)
 +{
 +    LLLiveLSLEditor* self = static_cast<LLLiveLSLEditor*>(userdata);
 +    self->mMonoCheckbox->setEnabled(have_script_upload_cap(self->mObjectUUID));
 +    self->mScriptEd->enableSave(self->getIsModifiable());
 +}
 +
 +bool LLLiveLSLEditor::monoChecked() const
 +{
 +    return mMonoCheckbox && mMonoCheckbox->getValue();
 +}
 +
 +void LLLiveLSLEditor::setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience )
 +{
 +    if (LLLiveLSLEditor* scriptEd = editor.get())
 +    {
 +        LLUUID id;
 +        if (experience.has(LLExperienceCache::EXPERIENCE_ID))
 +        {
 +            id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
 +        }
 +        scriptEd->mScriptEd->setAssociatedExperience(id);
 +        scriptEd->updateExperiencePanel();
 +    }
 +}
  | 
