diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llpreviewscript.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
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();
+ }
+}
|