2173 lines
60 KiB
C++
2173 lines
60 KiB
C++
/**
|
|
* @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 "llassetuploadresponders.h"
|
|
#include "llbutton.h"
|
|
#include "llcheckboxctrl.h"
|
|
#include "llcombobox.h"
|
|
#include "lldir.h"
|
|
#include "llfloaterreg.h"
|
|
#include "llinventorydefines.h"
|
|
#include "llinventorymodel.h"
|
|
#include "llkeyboard.h"
|
|
#include "lllineeditor.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 "llslider.h"
|
|
#include "lscript_rt_interface.h"
|
|
#include "lscript_library.h"
|
|
#include "lscript_export.h"
|
|
#include "lltextbox.h"
|
|
#include "lltooldraganddrop.h"
|
|
#include "llvfile.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llmenugl.h"
|
|
#include "roles_constants.h"
|
|
#include "llselectmgr.h"
|
|
#include "llviewerinventory.h"
|
|
#include "llviewermenu.h"
|
|
#include "llviewerobject.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llkeyboard.h"
|
|
#include "llscrollcontainer.h"
|
|
#include "llcheckboxctrl.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 "llviewertexteditor.h"
|
|
#include "llviewerwindow.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llmediactrl.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "lltrans.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llappviewer.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_EXPORT_SIZE = 1000;
|
|
|
|
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());
|
|
}
|
|
|
|
/// ---------------------------------------------------------------------------
|
|
/// 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; }
|
|
|
|
private:
|
|
|
|
LLScriptEdCore* mEditorCore;
|
|
|
|
static LLFloaterScriptSearch* sInstance;
|
|
};
|
|
|
|
LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
|
|
|
|
LLFloaterScriptSearch::LLFloaterScriptSearch(LLScriptEdCore* editor_core)
|
|
: LLFloater(LLSD()),
|
|
mEditorCore(editor_core)
|
|
{
|
|
LLUICtrlFactory::getInstance()->buildFloater(this,"floater_script_search.xml", NULL);
|
|
|
|
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()
|
|
{
|
|
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)
|
|
{
|
|
if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core)
|
|
{
|
|
sInstance->closeFloater();
|
|
delete sInstance;
|
|
}
|
|
|
|
if (!sInstance)
|
|
{
|
|
// sInstance will be assigned in the constructor.
|
|
new LLFloaterScriptSearch(editor_core);
|
|
}
|
|
|
|
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(getChild<LLUICtrl>("search_text")->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(getChild<LLUICtrl>("search_text")->getValue().asString(), getChild<LLUICtrl>("replace_text")->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(getChild<LLUICtrl>("search_text")->getValue().asString(), getChild<LLUICtrl>("replace_text")->getValue().asString(), caseChk->get());
|
|
}
|
|
|
|
|
|
|
|
/// ---------------------------------------------------------------------------
|
|
/// LLScriptEdCore
|
|
/// ---------------------------------------------------------------------------
|
|
|
|
struct LLSECKeywordCompare
|
|
{
|
|
bool operator()(const std::string& lhs, const std::string& rhs)
|
|
{
|
|
return (LLStringUtil::compareDictInsensitive( lhs, rhs ) < 0 );
|
|
}
|
|
};
|
|
|
|
LLScriptEdCore::LLScriptEdCore(
|
|
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,
|
|
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),
|
|
mHasScriptData(FALSE)
|
|
{
|
|
setFollowsAll();
|
|
setBorderVisible(FALSE);
|
|
|
|
setXMLFilename("panel_script_ed.xml");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
BOOL LLScriptEdCore::postBuild()
|
|
{
|
|
mErrorList = getChild<LLScrollListCtrl>("lsl errors");
|
|
|
|
mFunctions = getChild<LLComboBox>( "Insert...");
|
|
|
|
childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);
|
|
|
|
mEditor = getChild<LLViewerTextEditor>("Script Editor");
|
|
|
|
childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
|
|
childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,FALSE));
|
|
|
|
initMenu();
|
|
|
|
|
|
std::vector<std::string> funcs;
|
|
std::vector<std::string> tooltips;
|
|
for (std::vector<LLScriptLibraryFunction>::const_iterator i = gScriptLibrary.mFunctions.begin();
|
|
i != gScriptLibrary.mFunctions.end(); ++i)
|
|
{
|
|
// Make sure this isn't a god only function, or the agent is a god.
|
|
if (!i->mGodOnly || gAgent.isGodlike())
|
|
{
|
|
std::string name = i->mName;
|
|
funcs.push_back(name);
|
|
|
|
std::string desc_name = "LSLTipText_";
|
|
desc_name += name;
|
|
std::string desc = LLTrans::getString(desc_name);
|
|
|
|
F32 sleep_time = i->mSleepTime;
|
|
if( sleep_time )
|
|
{
|
|
desc += "\n";
|
|
|
|
LLStringUtil::format_map_t args;
|
|
args["[SLEEP_TIME]"] = llformat("%.1f", sleep_time );
|
|
desc += LLTrans::getString("LSLTipSleepTime", args);
|
|
}
|
|
|
|
// A \n linefeed is not part of xml. Let's add one to keep all
|
|
// the tips one-per-line in strings.xml
|
|
LLStringUtil::replaceString( desc, "\\n", "\n" );
|
|
|
|
tooltips.push_back(desc);
|
|
}
|
|
}
|
|
|
|
LLColor3 color(0.5f, 0.0f, 0.15f);
|
|
mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color);
|
|
|
|
std::vector<std::string> primary_keywords;
|
|
std::vector<std::string> 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->getColor() == color) // Wow, what a disgusting hack.
|
|
{
|
|
primary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
|
|
}
|
|
else
|
|
{
|
|
secondary_keywords.push_back( wstring_to_utf8str(token->getToken()) );
|
|
}
|
|
}
|
|
|
|
// Case-insensitive dictionary sort for primary keywords. We don't sort the secondary
|
|
// keywords. They're intelligently grouped in keywords.ini.
|
|
std::stable_sort( primary_keywords.begin(), primary_keywords.end(), LLSECKeywordCompare() );
|
|
|
|
for (std::vector<std::string>::const_iterator iter= primary_keywords.begin();
|
|
iter!= primary_keywords.end(); ++iter)
|
|
{
|
|
mFunctions->add(*iter);
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator iter= secondary_keywords.begin();
|
|
iter!= secondary_keywords.end(); ++iter)
|
|
{
|
|
mFunctions->add(*iter);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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>("Search / Replace...");
|
|
menuItem->setClickCallback(boost::bind(&LLFloaterScriptSearch::show, this));
|
|
|
|
menuItem = getChild<LLMenuItemCallGL>("Help...");
|
|
menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnHelp, this));
|
|
|
|
menuItem = getChild<LLMenuItemCallGL>("Keyword Help...");
|
|
menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnDynamicHelp, this));
|
|
}
|
|
|
|
void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid)
|
|
{
|
|
if (mEditor)
|
|
{
|
|
mEditor->setText(text);
|
|
mHasScriptData = is_valid;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
// 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)
|
|
{
|
|
if((*segment_iter)->getToken() && (*segment_iter)->getToken()->getType() == LLKeywordToken::WORD)
|
|
{
|
|
segment = *segment_iter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// then try previous segment in case we just typed it
|
|
if (!segment)
|
|
{
|
|
const LLTextSegmentPtr test_segment = mEditor->getPreviousSegment();
|
|
if(test_segment->getToken() && test_segment->getToken()->getType() == LLKeywordToken::WORD)
|
|
{
|
|
segment = test_segment;
|
|
}
|
|
}
|
|
|
|
if (segment)
|
|
{
|
|
if (segment->getToken() != mLastHelpToken)
|
|
{
|
|
mLastHelpToken = segment->getToken();
|
|
mLiveHelpTimer.start();
|
|
}
|
|
if (immediate || (mLiveHelpTimer.getStarted() && mLiveHelpTimer.getElapsedTimeF32() > LIVE_HELP_REFRESH_TIME))
|
|
{
|
|
std::string help_string = mEditor->getText().substr(segment->getStart(), segment->getEnd() - segment->getStart());
|
|
setHelpPage(help_string);
|
|
mLiveHelpTimer.stop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (immediate)
|
|
{
|
|
setHelpPage(LLStringUtil::null);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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())
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Bring up view-modal dialog: Save changes? Yes, No, Cancel
|
|
LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLScriptEdCore::handleSaveChangesDialog, this, _1, _2));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLSD& response )
|
|
{
|
|
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::onBtnHelp()
|
|
{
|
|
LLUI::sHelpImpl->showTopic(HELP_LSL_PORTAL_TOPIC);
|
|
}
|
|
|
|
void LLScriptEdCore::onBtnDynamicHelp()
|
|
{
|
|
LLFloater* live_help_floater = mLiveHelpHandle.get();
|
|
if (!live_help_floater)
|
|
{
|
|
live_help_floater = new LLFloater(LLSD());
|
|
LLUICtrlFactory::getInstance()->buildFloater(live_help_floater, "floater_lsl_guide.xml", NULL);
|
|
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 )
|
|
{
|
|
LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
|
|
|
|
if( mSaveCallback )
|
|
{
|
|
mSaveCallback( mUserdata, close_after_save );
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
//llinfos << "LLScriptEdCore::onErrorList() - " << row << ", "
|
|
//<< column << llendl;
|
|
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.count();
|
|
LLEntryAndEdCore* eandc;
|
|
for(S32 i = 0; i < count; i++)
|
|
{
|
|
eandc = mBridges.get(i);
|
|
delete eandc;
|
|
mBridges[i] = NULL;
|
|
}
|
|
mBridges.reset();
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
/// ---------------------------------------------------------------------------
|
|
/// 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(
|
|
HELLO_LSL,
|
|
self->getHandle(),
|
|
LLPreviewLSL::onLoad,
|
|
LLPreviewLSL::onSave,
|
|
LLPreviewLSL::onSearchReplace,
|
|
self,
|
|
0);
|
|
|
|
return self->mScriptEd;
|
|
}
|
|
|
|
|
|
LLPreviewLSL::LLPreviewLSL(const LLSD& key )
|
|
: LLPreview( key ),
|
|
mPendingUploads(0)
|
|
{
|
|
mFactoryMap["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this);
|
|
//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this,"floater_script_preview.xml", FALSE);
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLPreviewLSL::postBuild()
|
|
{
|
|
const LLInventoryItem* item = getItem();
|
|
|
|
llassert(item);
|
|
if (item)
|
|
{
|
|
getChild<LLUICtrl>("desc")->setValue(item->getDescription());
|
|
}
|
|
childSetCommitCallback("desc", LLPreview::onText, this);
|
|
getChild<LLLineEditor>("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
|
|
|
|
return LLPreview::postBuild();
|
|
}
|
|
|
|
// virtual
|
|
void LLPreviewLSL::callbackLSLCompileSucceeded()
|
|
{
|
|
llinfos << "LSL Bytecode saved" << llendl;
|
|
mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
|
|
mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
|
|
closeIfNeeded();
|
|
}
|
|
|
|
// virtual
|
|
void LLPreviewLSL::callbackLSLCompileFailed(const LLSD& compile_errors)
|
|
{
|
|
llinfos << "Compile failed!" << llendl;
|
|
|
|
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::invalid,
|
|
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->mEditor->setEnabled(FALSE);
|
|
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);
|
|
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();
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
// llinfos << "LLPreviewLSL::saveIfNeeded()" << llendl;
|
|
if(!mScriptEd->hasChanged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
mPendingUploads = 0;
|
|
mScriptEd->mErrorList->deleteAllItems();
|
|
mScriptEd->mEditor->makePristine();
|
|
|
|
// save off asset into file
|
|
LLTransactionID tid;
|
|
tid.generate();
|
|
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
|
|
std::string filename = filepath + ".lsl";
|
|
|
|
LLFILE* fp = LLFile::fopen(filename, "wb");
|
|
if(!fp)
|
|
{
|
|
llwarns << "Unable to write to " << filename << llendl;
|
|
|
|
LLSD row;
|
|
row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?";
|
|
row["columns"][0]["font"] = "SANSSERIF_SMALL";
|
|
mScriptEd->mErrorList->addElement(row);
|
|
return;
|
|
}
|
|
|
|
std::string utf8text = mScriptEd->mEditor->getText();
|
|
fputs(utf8text.c_str(), fp);
|
|
fclose(fp);
|
|
fp = NULL;
|
|
|
|
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())
|
|
{
|
|
uploadAssetViaCaps(url, filename, mItemUUID);
|
|
}
|
|
else if (gAssetStorage)
|
|
{
|
|
uploadAssetLegacy(filename, mItemUUID, tid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLPreviewLSL::uploadAssetViaCaps(const std::string& url,
|
|
const std::string& filename,
|
|
const LLUUID& item_id)
|
|
{
|
|
llinfos << "Update Agent Inventory via capability" << llendl;
|
|
LLSD body;
|
|
body["item_id"] = item_id;
|
|
body["target"] = "lsl2";
|
|
LLHTTPClient::post(url, body, new LLUpdateAgentInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));
|
|
}
|
|
|
|
void LLPreviewLSL::uploadAssetLegacy(const std::string& filename,
|
|
const LLUUID& item_id,
|
|
const LLTransactionID& tid)
|
|
{
|
|
LLLineEditor* descEditor = getChild<LLLineEditor>("desc");
|
|
LLScriptSaveInfo* info = new LLScriptSaveInfo(item_id,
|
|
descEditor->getText(),
|
|
tid);
|
|
gAssetStorage->storeAssetData(filename, tid,
|
|
LLAssetType::AT_LSL_TEXT,
|
|
&LLPreviewLSL::onSaveComplete,
|
|
info);
|
|
|
|
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
|
|
std::string dst_filename = llformat("%s.lso", filepath.c_str());
|
|
std::string err_filename = llformat("%s.out", filepath.c_str());
|
|
|
|
const BOOL compile_to_mono = FALSE;
|
|
if(!lscript_compile(filename.c_str(),
|
|
dst_filename.c_str(),
|
|
err_filename.c_str(),
|
|
compile_to_mono,
|
|
asset_id.asString().c_str(),
|
|
gAgent.isGodlike()))
|
|
{
|
|
llinfos << "Compile failed!" << llendl;
|
|
//char command[256];
|
|
//sprintf(command, "type %s\n", err_filename.c_str());
|
|
//system(command);
|
|
|
|
// load the error file into the error scrolllist
|
|
LLFILE* fp = LLFile::fopen(err_filename, "r");
|
|
if(fp)
|
|
{
|
|
char buffer[MAX_STRING]; /*Flawfinder: ignore*/
|
|
std::string line;
|
|
while(!feof(fp))
|
|
{
|
|
if (fgets(buffer, MAX_STRING, fp) == NULL)
|
|
{
|
|
buffer[0] = '\0';
|
|
}
|
|
if(feof(fp))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
line.assign(buffer);
|
|
LLStringUtil::stripNonprintable(line);
|
|
|
|
LLSD row;
|
|
row["columns"][0]["value"] = line;
|
|
row["columns"][0]["font"] = "OCRA";
|
|
mScriptEd->mErrorList->addElement(row);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
mScriptEd->selectFirstError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llinfos << "Compile worked!" << llendl;
|
|
if(gAssetStorage)
|
|
{
|
|
getWindow()->incBusyCount();
|
|
mPendingUploads++;
|
|
LLUUID* this_uuid = new LLUUID(mItemUUID);
|
|
gAssetStorage->storeAssetData(dst_filename,
|
|
tid,
|
|
LLAssetType::AT_LSL_BYTECODE,
|
|
&LLPreviewLSL::onSaveBytecodeComplete,
|
|
(void**)this_uuid);
|
|
}
|
|
}
|
|
|
|
// get rid of any temp files left lying around
|
|
LLFile::remove(filename);
|
|
LLFile::remove(err_filename);
|
|
LLFile::remove(dst_filename);
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data);
|
|
if(0 == status)
|
|
{
|
|
if (info)
|
|
{
|
|
const LLViewerInventoryItem* item;
|
|
item = (const LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
|
|
if(item)
|
|
{
|
|
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
|
|
new_item->setAssetUUID(asset_uuid);
|
|
new_item->setTransactionID(info->mTransactionID);
|
|
new_item->updateServer(FALSE);
|
|
gInventory.updateItem(new_item);
|
|
gInventory.notifyObservers();
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Inventory item for script " << info->mItemUUID
|
|
<< " is no longer in agent inventory." << llendl;
|
|
}
|
|
|
|
// Find our window and close it if requested.
|
|
LLPreviewLSL* self = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", info->mItemUUID);
|
|
if (self)
|
|
{
|
|
getWindow()->decBusyCount();
|
|
self->mPendingUploads--;
|
|
if (self->mPendingUploads <= 0
|
|
&& self->mCloseAfterSave)
|
|
{
|
|
self->closeFloater();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Problem saving script: " << status << llendl;
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
|
|
LLNotificationsUtil::add("SaveScriptFailReason", args);
|
|
}
|
|
delete info;
|
|
}
|
|
|
|
// static
|
|
void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLUUID* instance_uuid = (LLUUID*)user_data;
|
|
LLPreviewLSL* self = NULL;
|
|
if(instance_uuid)
|
|
{
|
|
self = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *instance_uuid);
|
|
}
|
|
if (0 == status)
|
|
{
|
|
if (self)
|
|
{
|
|
LLSD row;
|
|
row["columns"][0]["value"] = "Compile successful!";
|
|
row["columns"][0]["font"] = "SANSSERIF_SMALL";
|
|
self->mScriptEd->mErrorList->addElement(row);
|
|
|
|
// Find our window and close it if requested.
|
|
self->getWindow()->decBusyCount();
|
|
self->mPendingUploads--;
|
|
if (self->mPendingUploads <= 0
|
|
&& self->mCloseAfterSave)
|
|
{
|
|
self->closeFloater();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Problem saving LSL Bytecode (Preview)" << llendl;
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
|
|
LLNotificationsUtil::add("SaveBytecodeFailReason", args);
|
|
}
|
|
delete instance_uuid;
|
|
}
|
|
|
|
// static
|
|
void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type,
|
|
void* user_data, S32 status, LLExtStat ext_status)
|
|
{
|
|
lldebugs << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid
|
|
<< llendl;
|
|
LLUUID* item_uuid = (LLUUID*)user_data;
|
|
LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", *item_uuid);
|
|
if( preview )
|
|
{
|
|
if(0 == status)
|
|
{
|
|
LLVFile file(vfs, 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();
|
|
LLInventoryItem* item = gInventory.getItem(*item_uuid);
|
|
BOOL is_modifiable = FALSE;
|
|
if(item
|
|
&& gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
|
|
GP_OBJECT_MANIPULATE))
|
|
{
|
|
is_modifiable = TRUE;
|
|
}
|
|
preview->mScriptEd->mEditor->setEnabled(is_modifiable);
|
|
preview->mAssetStatus = PREVIEW_ASSET_LOADED;
|
|
}
|
|
else
|
|
{
|
|
LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
|
|
|
|
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;
|
|
llwarns << "Problem loading script: " << status << llendl;
|
|
}
|
|
}
|
|
delete item_uuid;
|
|
}
|
|
|
|
|
|
/// ---------------------------------------------------------------------------
|
|
/// LLLiveLSLEditor
|
|
/// ---------------------------------------------------------------------------
|
|
|
|
|
|
//static
|
|
void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
|
|
{
|
|
|
|
LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata;
|
|
|
|
self->mScriptEd = new LLScriptEdCore(
|
|
HELLO_LSL,
|
|
self->getHandle(),
|
|
&LLLiveLSLEditor::onLoad,
|
|
&LLLiveLSLEditor::onSave,
|
|
&LLLiveLSLEditor::onSearchReplace,
|
|
self,
|
|
0);
|
|
|
|
return self->mScriptEd;
|
|
}
|
|
|
|
|
|
LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) :
|
|
LLPreview(key),
|
|
mScriptEd(NULL),
|
|
mAskedForRunningInfo(FALSE),
|
|
mHaveRunningInfo(FALSE),
|
|
mCloseAfterSave(FALSE),
|
|
mPendingUploads(0),
|
|
mIsModifiable(FALSE),
|
|
mIsNew(false)
|
|
{
|
|
mFactoryMap["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
|
|
//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this,"floater_live_lsleditor.xml", FALSE);
|
|
}
|
|
|
|
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(FALSE);
|
|
|
|
mScriptEd->mEditor->makePristine();
|
|
mScriptEd->mEditor->setFocus(TRUE);
|
|
|
|
return LLPreview::postBuild();
|
|
}
|
|
|
|
LLLiveLSLEditor::~LLLiveLSLEditor()
|
|
{
|
|
}
|
|
|
|
// virtual
|
|
void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id,
|
|
const LLUUID& item_id,
|
|
bool is_script_running)
|
|
{
|
|
lldebugs << "LSL Bytecode saved" << llendl;
|
|
mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessful"));
|
|
mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
|
|
closeIfNeeded();
|
|
}
|
|
|
|
// virtual
|
|
void LLLiveLSLEditor::callbackLSLCompileFailed(const LLSD& compile_errors)
|
|
{
|
|
lldebugs << "Compile failed!" << llendl;
|
|
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();
|
|
closeIfNeeded();
|
|
}
|
|
|
|
void LLLiveLSLEditor::loadAsset()
|
|
{
|
|
//llinfos << "LLLiveLSLEditor::loadAsset()" << llendl;
|
|
if(!mIsNew)
|
|
{
|
|
LLViewerObject* object = gObjectList.findObject(mObjectUUID);
|
|
if(object)
|
|
{
|
|
LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID));
|
|
if(item
|
|
&& (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
|
|
|| gAgent.isGodlike()))
|
|
{
|
|
mItem = new LLViewerInventoryItem(item);
|
|
//llinfos << "asset id " << mItem->getAssetUUID() << llendl;
|
|
}
|
|
|
|
if(!gAgent.isGodlike()
|
|
&& (item
|
|
&& (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
|
|
|| !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))))
|
|
{
|
|
mItem = new LLViewerInventoryItem(item);
|
|
mScriptEd->setScriptText(getString("not_allowed"), FALSE);
|
|
mScriptEd->mEditor->makePristine();
|
|
mScriptEd->mEditor->setEnabled(FALSE);
|
|
mScriptEd->enableSave(FALSE);
|
|
mAssetStatus = PREVIEW_ASSET_LOADED;
|
|
}
|
|
else if(item && mItem.notNull())
|
|
{
|
|
// request the text from the object
|
|
LLUUID* user_data = new LLUUID(mItemUUID); // ^ mObjectUUID
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
mScriptEd->setScriptText(LLStringUtil::null, FALSE);
|
|
mScriptEd->mEditor->makePristine();
|
|
mAssetStatus = PREVIEW_ASSET_LOADED;
|
|
}
|
|
|
|
mIsModifiable = item && gAgent.allowOperation(PERM_MODIFY,
|
|
item->getPermissions(),
|
|
GP_OBJECT_MANIPULATE);
|
|
if(!mIsModifiable)
|
|
{
|
|
mScriptEd->mEditor->setEnabled(FALSE);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,
|
|
LLAssetType::EType type,
|
|
void* user_data, S32 status, LLExtStat ext_status)
|
|
{
|
|
lldebugs << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id
|
|
<< llendl;
|
|
LLUUID* xored_id = (LLUUID*)user_data;
|
|
|
|
LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", *xored_id);
|
|
|
|
if(instance )
|
|
{
|
|
if( LL_ERR_NOERR == status )
|
|
{
|
|
instance->loadScriptText(vfs, asset_id, type);
|
|
instance->mAssetStatus = PREVIEW_ASSET_LOADED;
|
|
}
|
|
else
|
|
{
|
|
LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
|
|
|
|
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 xored_id;
|
|
}
|
|
|
|
// unused
|
|
// void LLLiveLSLEditor::loadScriptText(const std::string& filename)
|
|
// {
|
|
// if(!filename)
|
|
// {
|
|
// llerrs << "Filename is Empty!" << llendl;
|
|
// return;
|
|
// }
|
|
// LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
|
|
// if(file)
|
|
// {
|
|
// // read in the whole file
|
|
// fseek(file, 0L, SEEK_END);
|
|
// long file_length = 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 < (size_t) file_length)
|
|
// {
|
|
// llwarns << "Short read" << llendl;
|
|
// }
|
|
// buffer[nread] = '\0';
|
|
// fclose(file);
|
|
// mScriptEd->mEditor->setText(LLStringExplicit(buffer));
|
|
// mScriptEd->mEditor->makePristine();
|
|
// delete[] buffer;
|
|
// }
|
|
// else
|
|
// {
|
|
// llwarns << "Error opening " << filename << llendl;
|
|
// }
|
|
// }
|
|
|
|
void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
|
|
{
|
|
LLVFile file(vfs, 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)
|
|
{
|
|
llwarns << "Error reading " << uuid << ":" << type << llendl;
|
|
}
|
|
|
|
buffer[file_length] = '\0';
|
|
|
|
mScriptEd->setScriptText(LLStringExplicit(&buffer[0]), TRUE);
|
|
mScriptEd->mEditor->makePristine();
|
|
}
|
|
|
|
|
|
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(TRUE);
|
|
|
|
if(object->permAnyOwner())
|
|
{
|
|
runningCheckbox->setLabel(getString("script_running"));
|
|
runningCheckbox->setEnabled(TRUE);
|
|
}
|
|
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
|
|
{
|
|
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->setEnabled(FALSE);
|
|
// object may have fallen out of range.
|
|
mHaveRunningInfo = 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);
|
|
// 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);
|
|
}
|
|
|
|
void LLLiveLSLEditor::saveIfNeeded()
|
|
{
|
|
llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl;
|
|
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();
|
|
|
|
// set up the save on the local machine.
|
|
mScriptEd->mEditor->makePristine();
|
|
LLTransactionID tid;
|
|
tid.generate();
|
|
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
|
|
std::string filename = llformat("%s.lsl", filepath.c_str());
|
|
|
|
mItem->setAssetUUID(asset_id);
|
|
mItem->setTransactionID(tid);
|
|
|
|
// write out the data, and store it in the asset database
|
|
LLFILE* fp = LLFile::fopen(filename, "wb");
|
|
if(!fp)
|
|
{
|
|
llwarns << "Unable to write to " << filename << llendl;
|
|
|
|
LLSD row;
|
|
row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?";
|
|
row["columns"][0]["font"] = "SANSSERIF_SMALL";
|
|
mScriptEd->mErrorList->addElement(row);
|
|
return;
|
|
}
|
|
std::string utf8text = mScriptEd->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);
|
|
fp = NULL;
|
|
|
|
// save it out to asset server
|
|
std::string url = object->getRegion()->getCapability("UpdateScriptTask");
|
|
getWindow()->incBusyCount();
|
|
mPendingUploads++;
|
|
BOOL is_running = getChild<LLCheckBoxCtrl>( "running")->get();
|
|
if (!url.empty())
|
|
{
|
|
uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running);
|
|
}
|
|
else if (gAssetStorage)
|
|
{
|
|
uploadAssetLegacy(filename, object, tid, is_running);
|
|
}
|
|
}
|
|
|
|
void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,
|
|
const std::string& filename,
|
|
const LLUUID& task_id,
|
|
const LLUUID& item_id,
|
|
BOOL is_running)
|
|
{
|
|
llinfos << "Update Task Inventory via capability " << url << llendl;
|
|
LLSD body;
|
|
body["task_id"] = task_id;
|
|
body["item_id"] = item_id;
|
|
body["is_script_running"] = is_running;
|
|
body["target"] = monoChecked() ? "mono" : "lsl2";
|
|
LLHTTPClient::post(url, body,
|
|
new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));
|
|
}
|
|
|
|
void LLLiveLSLEditor::uploadAssetLegacy(const std::string& filename,
|
|
LLViewerObject* object,
|
|
const LLTransactionID& tid,
|
|
BOOL is_running)
|
|
{
|
|
LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectUUID,
|
|
mItem,
|
|
is_running);
|
|
gAssetStorage->storeAssetData(filename, tid,
|
|
LLAssetType::AT_LSL_TEXT,
|
|
&onSaveTextComplete,
|
|
(void*)data,
|
|
FALSE);
|
|
|
|
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString());
|
|
std::string dst_filename = llformat("%s.lso", filepath.c_str());
|
|
std::string err_filename = llformat("%s.out", filepath.c_str());
|
|
|
|
LLFILE *fp;
|
|
const BOOL compile_to_mono = FALSE;
|
|
if(!lscript_compile(filename.c_str(),
|
|
dst_filename.c_str(),
|
|
err_filename.c_str(),
|
|
compile_to_mono,
|
|
asset_id.asString().c_str(),
|
|
gAgent.isGodlike()))
|
|
{
|
|
// load the error file into the error scrolllist
|
|
llinfos << "Compile failed!" << llendl;
|
|
if(NULL != (fp = LLFile::fopen(err_filename, "r")))
|
|
{
|
|
char buffer[MAX_STRING]; /*Flawfinder: ignore*/
|
|
std::string line;
|
|
while(!feof(fp))
|
|
{
|
|
|
|
if (fgets(buffer, MAX_STRING, fp) == NULL)
|
|
{
|
|
buffer[0] = '\0';
|
|
}
|
|
if(feof(fp))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
line.assign(buffer);
|
|
LLStringUtil::stripNonprintable(line);
|
|
|
|
LLSD row;
|
|
row["columns"][0]["value"] = line;
|
|
row["columns"][0]["font"] = "OCRA";
|
|
mScriptEd->mErrorList->addElement(row);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
mScriptEd->selectFirstError();
|
|
// don't set the asset id, because we want to save the
|
|
// script, even though the compile failed.
|
|
//mItem->setAssetUUID(LLUUID::null);
|
|
object->saveScript(mItem, FALSE, false);
|
|
dialog_refresh_all();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llinfos << "Compile worked!" << llendl;
|
|
mScriptEd->mErrorList->setCommentText(LLTrans::getString("CompileSuccessfulSaving"));
|
|
if(gAssetStorage)
|
|
{
|
|
llinfos << "LLLiveLSLEditor::saveAsset "
|
|
<< mItem->getAssetUUID() << llendl;
|
|
getWindow()->incBusyCount();
|
|
mPendingUploads++;
|
|
LLLiveLSLSaveData* data = NULL;
|
|
data = new LLLiveLSLSaveData(mObjectUUID,
|
|
mItem,
|
|
is_running);
|
|
gAssetStorage->storeAssetData(dst_filename,
|
|
tid,
|
|
LLAssetType::AT_LSL_BYTECODE,
|
|
&LLLiveLSLEditor::onSaveBytecodeComplete,
|
|
(void*)data);
|
|
dialog_refresh_all();
|
|
}
|
|
}
|
|
|
|
// get rid of any temp files left lying around
|
|
LLFile::remove(filename);
|
|
LLFile::remove(err_filename);
|
|
LLFile::remove(dst_filename);
|
|
|
|
// If we successfully saved it, then we should be able to check/uncheck the running box!
|
|
LLCheckBoxCtrl* runningCheckbox = getChild<LLCheckBoxCtrl>( "running");
|
|
runningCheckbox->setLabel(getString("script_running"));
|
|
runningCheckbox->setEnabled(TRUE);
|
|
}
|
|
|
|
void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
|
|
|
|
if (status)
|
|
{
|
|
llwarns << "Unable to save text for a script." << llendl;
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
|
|
LLNotificationsUtil::add("CompileQueueSaveText", args);
|
|
}
|
|
else
|
|
{
|
|
LLLiveLSLEditor* self = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", data->mItem->getUUID()); // ^ data->mSaveObjectID
|
|
if (self)
|
|
{
|
|
self->getWindow()->decBusyCount();
|
|
self->mPendingUploads--;
|
|
if (self->mPendingUploads <= 0
|
|
&& self->mCloseAfterSave)
|
|
{
|
|
self->closeFloater();
|
|
}
|
|
}
|
|
}
|
|
delete data;
|
|
data = NULL;
|
|
}
|
|
|
|
|
|
void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
|
|
if(!data)
|
|
return;
|
|
if(0 ==status)
|
|
{
|
|
llinfos << "LSL Bytecode saved" << llendl;
|
|
LLLiveLSLEditor* self = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", data->mItem->getUUID()); // ^ data->mSaveObjectID
|
|
if (self)
|
|
{
|
|
// Tell the user that the compile worked.
|
|
self->mScriptEd->mErrorList->setCommentText(LLTrans::getString("SaveComplete"));
|
|
// close the window if this completes both uploads
|
|
self->getWindow()->decBusyCount();
|
|
self->mPendingUploads--;
|
|
if (self->mPendingUploads <= 0
|
|
&& self->mCloseAfterSave)
|
|
{
|
|
self->closeFloater();
|
|
}
|
|
}
|
|
LLViewerObject* object = gObjectList.findObject(data->mSaveObjectID);
|
|
if(object)
|
|
{
|
|
object->saveScript(data->mItem, data->mActive, false);
|
|
dialog_refresh_all();
|
|
//LLToolDragAndDrop::dropScript(object, ids->first,
|
|
// LLAssetType::AT_LSL_TEXT, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llinfos << "Problem saving LSL Bytecode (Live Editor)" << llendl;
|
|
llwarns << "Unable to save a compiled script." << llendl;
|
|
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
|
|
LLNotificationsUtil::add("CompileQueueSaveBytecode", args);
|
|
}
|
|
|
|
std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_uuid.asString());
|
|
std::string dst_filename = llformat("%s.lso", filepath.c_str());
|
|
LLFile::remove(dst_filename);
|
|
delete data;
|
|
}
|
|
|
|
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)
|
|
{
|
|
LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
|
|
|
|
// [RLVa:KB] - Checked: 2009-10-10 (RLVa-1.0.5a) | Modified: RLVa-1.0.5a
|
|
/*
|
|
if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.isLockedAttachment(gObjectList.findObject(self->mObjectID), RLV_LOCK_REMOVE)) )
|
|
{
|
|
return;
|
|
}
|
|
*/
|
|
// [/RLVa:KB]
|
|
|
|
self->mCloseAfterSave = close_after_save;
|
|
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);
|
|
|
|
LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", item_id); // ^ object_id
|
|
if(instance)
|
|
{
|
|
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
|
|
{
|
|
if(NULL != mMonoCheckbox)
|
|
{
|
|
return mMonoCheckbox->getValue()? TRUE : FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|