diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llpreviewscript.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llpreviewscript.cpp')
-rw-r--r-- | indra/newview/llpreviewscript.cpp | 1984 |
1 files changed, 1984 insertions, 0 deletions
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp new file mode 100644 index 0000000000..175824d866 --- /dev/null +++ b/indra/newview/llpreviewscript.cpp @@ -0,0 +1,1984 @@ +/** + * @file llpreviewscript.cpp + * @brief LLPreviewScript class implementation + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpreviewscript.h" + +#include "llassetstorage.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "lldir.h" +#include "llinventorymodel.h" +#include "llkeyboard.h" +#include "lllineeditor.h" + +#include "llresmgr.h" +#include "llscrollbar.h" +#include "llscrollcontainer.h" +#include "llscrolllistctrl.h" +#include "llslider.h" +#include "lscript_rt_interface.h" +#include "lscript_export.h" +#include "lltextbox.h" +#include "lltooldraganddrop.h" +#include "llvfile.h" + +#include "llagent.h" +#include "llnotify.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 "viewer.h" +#include "lldir.h" +#include "llcombobox.h" +//#include "llfloaterchat.h" +#include "llviewerstats.h" +#include "llviewertexteditor.h" +#include "llviewerwindow.h" +#include "llvieweruictrlfactory.h" +#include "lluictrlfactory.h" + +#include "viewer.h" + +#include "llpanelinventory.h" + + +const char 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 char HELP_LSL[] = "lsl_guide.html"; + +const char DEFAULT_SCRIPT_NAME[] = "New Script"; +const char DEFAULT_SCRIPT_DESC[] = "(No Description)"; + +const char ENABLED_RUNNING_CHECKBOX_LABEL[] = "Running"; +const char DISABLED_RUNNING_CHECKBOX_LABEL[] = "Public Objects cannot run scripts"; + +// Description and header information + +const S32 SCRIPT_BORDER = 4; +const S32 SCRIPT_PAD = 5; +const S32 SCRIPT_BUTTON_WIDTH = 128; +const S32 SCRIPT_BUTTON_HEIGHT = 24; // HACK: Use BTN_HEIGHT where possible. +const S32 LINE_COLUMN_HEIGHT = 14; +const S32 BTN_PAD = 8; + +const S32 SCRIPT_EDITOR_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 128; + +const S32 SCRIPT_MIN_WIDTH = + 2 * SCRIPT_BORDER + + 2 * SCRIPT_BUTTON_WIDTH + + SCRIPT_PAD + RESIZE_HANDLE_WIDTH + + SCRIPT_PAD; + +const S32 SCRIPT_MIN_HEIGHT = + 2 * SCRIPT_BORDER + + 3*(SCRIPT_BUTTON_HEIGHT + SCRIPT_PAD) + + LINE_COLUMN_HEIGHT + + SCRIPT_EDITOR_MIN_HEIGHT; + +const S32 MAX_EXPORT_SIZE = 1000; + +const S32 SCRIPT_SEARCH_WIDTH = 300; +const S32 SCRIPT_SEARCH_HEIGHT = 120; +const S32 SCRIPT_SEARCH_LABEL_WIDTH = 50; +const S32 SCRIPT_SEARCH_BUTTON_WIDTH = 80; +const S32 TEXT_EDIT_COLUMN_HEIGHT = 16; +/// --------------------------------------------------------------------------- +/// LLFloaterScriptSearch +/// --------------------------------------------------------------------------- +class LLFloaterScriptSearch : public LLFloater +{ +public: + LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core); + ~LLFloaterScriptSearch(); + + 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; } + + void open(); + +private: + + LLScriptEdCore* mEditorCore; + + static LLFloaterScriptSearch* sInstance; +}; + +LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL; + +LLFloaterScriptSearch::LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core) + : LLFloater("script search",rect,title), mEditorCore(editor_core) +{ + + gUICtrlFactory->buildFloater(this,"floater_script_search.xml"); + + childSetAction("search_btn", onBtnSearch,this); + childSetAction("replace_btn", onBtnReplace,this); + childSetAction("replace_all_btn", onBtnReplaceAll,this); + + setDefaultBtn("search_btn"); + + if (!getHost()) + { + LLRect curRect = getRect(); + translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop); + } + + sInstance = this; + + childSetFocus("search_text", TRUE); +} + +//static +void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core) +{ + if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core) + { + sInstance->close(); + delete sInstance; + } + + if (!sInstance) + { + S32 left = 0; + S32 top = 0; + gFloaterView->getNewFloaterPosition(&left,&top); + + // sInstance will be assigned in the constructor. + new LLFloaterScriptSearch("Script Search",LLRect(left,top,left + SCRIPT_SEARCH_WIDTH,top - SCRIPT_SEARCH_HEIGHT),editor_core); + } + + sInstance->open(); +} + +LLFloaterScriptSearch::~LLFloaterScriptSearch() +{ + sInstance = NULL; +} + +// static +void LLFloaterScriptSearch::onBtnSearch(void *userdata) +{ + LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; + self->handleBtnSearch(); +} + +void LLFloaterScriptSearch::handleBtnSearch() +{ + LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); + mEditorCore->mEditor->selectNext(childGetText("search_text"), caseChk->get()); +} + +// static +void LLFloaterScriptSearch::onBtnReplace(void *userdata) +{ + LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; + self->handleBtnReplace(); +} + +void LLFloaterScriptSearch::handleBtnReplace() +{ + LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); + mEditorCore->mEditor->replaceText(childGetText("search_text"), childGetText("replace_text"), caseChk->get()); +} + +// static +void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata) +{ + LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; + self->handleBtnReplaceAll(); +} + +void LLFloaterScriptSearch::handleBtnReplaceAll() +{ + LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); + mEditorCore->mEditor->replaceTextAll(childGetText("search_text"), childGetText("replace_text"), caseChk->get()); +} + +void LLFloaterScriptSearch::open() +{ + LLFloater::open(); + childSetFocus("search_text", TRUE); +} +/// --------------------------------------------------------------------------- +/// LLScriptEdCore +/// --------------------------------------------------------------------------- + +LLScriptEdCore::LLScriptEdCore( + const std::string& name, + const LLRect& rect, + const std::string& sample, + const std::string& help, + const LLViewHandle& floater_handle, + void (*load_callback)(void*), + void (*save_callback)(void*, BOOL), + void* userdata, + S32 bottom_pad) + : + LLPanel( "name", rect ), + mSampleText(sample), + mHelpFile ( help ), + mEditor( NULL ), + mLoadCallback( load_callback ), + mSaveCallback( save_callback ), + mUserdata( userdata ), + mForceClose( FALSE ) +{ + setFollowsAll(); + setBorderVisible(FALSE); + + + gUICtrlFactory->buildPanel(this, "floater_script_ed_panel.xml"); + + mErrorList = LLUICtrlFactory::getScrollListByName(this, "lsl errors"); + + mFunctions = LLUICtrlFactory::getComboBoxByName(this, "Insert..."); + + childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); + mFunctions->setLabel("Insert..."); + + mEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Script Editor"); + mEditor->setReadOnlyBgColor(gColors.getColor( "ScriptBgReadOnlyColor" ) ); + mEditor->setFollowsAll(); + mEditor->setHandleEditKeysDirectly(TRUE); + mEditor->setEnabled(TRUE); + mEditor->setWordWrap(TRUE); + + LLDynamicArray<const char*> funcs; + LLDynamicArray<const char*> tooltips; + for (S32 i = 0; i < gScriptLibrary.mNextNumber; i++) + { + // Make sure this isn't a god only function, or the agent is a god. + if (!gScriptLibrary.mFunctions[i]->mGodOnly || gAgent.isGodlike()) + { + funcs.put(gScriptLibrary.mFunctions[i]->mName); + tooltips.put(gScriptLibrary.mFunctions[i]->mDesc); + } + } + LLColor3 color(0.5f, 0.0f, 0.15f); + + mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color); + + + LLKeywordToken *token; + LLKeywords::word_token_map_t::iterator token_it; + for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it) + { + token = token_it->second; + if (token->mColor == color) + mFunctions->add(wstring_to_utf8str(token->mToken)); + } + + for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it) + { + token = token_it->second; + if (token->mColor != color) + mFunctions->add(wstring_to_utf8str(token->mToken)); + } + + + childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); + childSetAction("Save_btn", onBtnSave,this); + + initMenu(); + + + // Do the work that addTabPanel() normally does. + //LLRect tab_panel_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 ); + //tab_panel_rect.stretch( -LLPANEL_BORDER_WIDTH ); + //mCodePanel->setFollowsAll(); + //mCodePanel->translate( tab_panel_rect.mLeft - mCodePanel->getRect().mLeft, tab_panel_rect.mBottom - mCodePanel->getRect().mBottom); + //mCodePanel->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE ); + +} + +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->close(); + delete script_search; + } +} + +void LLScriptEdCore::initMenu() +{ + + LLMenuItemCallGL* menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Save"); + menuItem->setMenuCallback(onBtnSave, this); + menuItem->setEnabledCallback(hasChanged); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Revert All Changes"); + menuItem->setMenuCallback(onBtnUndoChanges, this); + menuItem->setEnabledCallback(hasChanged); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Undo"); + menuItem->setMenuCallback(onUndoMenu, this); + menuItem->setEnabledCallback(enableUndoMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Redo"); + menuItem->setMenuCallback(onRedoMenu, this); + menuItem->setEnabledCallback(enableRedoMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Cut"); + menuItem->setMenuCallback(onCutMenu, this); + menuItem->setEnabledCallback(enableCutMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Copy"); + menuItem->setMenuCallback(onCopyMenu, this); + menuItem->setEnabledCallback(enableCopyMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Paste"); + menuItem->setMenuCallback(onPasteMenu, this); + menuItem->setEnabledCallback(enablePasteMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Select All"); + menuItem->setMenuCallback(onSelectAllMenu, this); + menuItem->setEnabledCallback(enableSelectAllMenu); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Search / Replace..."); + menuItem->setMenuCallback(onSearchMenu, this); + menuItem->setEnabledCallback(NULL); + + menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Help..."); + menuItem->setMenuCallback(onBtnHelp, this); + menuItem->setEnabledCallback(NULL); + +} + +BOOL LLScriptEdCore::hasChanged(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + + return !self->mEditor->isPristine(); +} + +void LLScriptEdCore::draw() +{ + BOOL script_changed = !mEditor->isPristine(); + childSetEnabled("Save_btn", script_changed); + + if( mEditor->hasFocus() ) + { + S32 line = 0; + S32 column = 0; + mEditor->getCurrentLineAndColumn( &line, &column, FALSE ); // don't include wordwrap + char cursor_pos[STD_STRING_BUF_SIZE]; + sprintf( cursor_pos, "Line %d, Column %d", line, column ); + childSetText("line_col", cursor_pos); + } + else + { + childSetText("line_col", ""); + } + + LLPanel::draw(); +} + +BOOL LLScriptEdCore::canClose() +{ + if(mForceClose || mEditor->isPristine()) + { + return TRUE; + } + else + { + // Bring up view-modal dialog: Save changes? Yes, No, Cancel + gViewerWindow->alertXml("SaveChanges", LLScriptEdCore::handleSaveChangesDialog, this); + return FALSE; + } +} + +// static +void LLScriptEdCore::handleSaveChangesDialog( S32 option, void* userdata ) +{ + LLScriptEdCore* self = (LLScriptEdCore*) userdata; + switch( option ) + { + case 0: // "Yes" + // close after saving + LLScriptEdCore::doSave( self, TRUE ); + break; + + case 1: // "No" + self->mForceClose = TRUE; + // This will close immediately because mForceClose is true, so we won't + // infinite loop with these dialogs. JC + ((LLFloater*) self->getParent())->close(); + break; + + case 2: // "Cancel" + default: + // If we were quitting, we didn't really mean it. + app_abort_quit(); + break; + } +} + +// static +void LLScriptEdCore::onHelpWebDialog(S32 option, void* userdata) +{ + LLScriptEdCore* corep = (LLScriptEdCore*)userdata; + + switch(option) + { + case 0: + load_url_local_file(corep->mHelpFile.c_str()); + break; + default: + break; + } +} + +// static +void LLScriptEdCore::onBtnHelp(void* userdata) +{ + gViewerWindow->alertXml("WebLaunchLSLGuide", + onHelpWebDialog, + userdata); +} + +// 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::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); +} + +// static +void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save ) +{ + if( gViewerStats ) + { + gViewerStats->incStat( LLViewerStats::ST_LSL_SAVE_COUNT ); + } + + LLScriptEdCore* self = (LLScriptEdCore*) userdata; + + if( self->mSaveCallback ) + { + self->mSaveCallback( self->mUserdata, close_after_save ); + } +} + +// static +void LLScriptEdCore::onBtnSave(void* data) +{ + // do the save, but don't close afterwards + doSave(data, FALSE); +} + +// static +void LLScriptEdCore::onBtnUndoChanges( void* userdata ) +{ + LLScriptEdCore* self = (LLScriptEdCore*) userdata; + if( !self->mEditor->tryToRevertToPristineState() ) + { + gViewerWindow->alertXml("ScriptCannotUndo", + LLScriptEdCore::handleReloadFromServerDialog, self); + } +} + +void LLScriptEdCore::onSearchMenu(void* userdata) +{ + LLScriptEdCore* sec = (LLScriptEdCore*)userdata; + LLFloaterScriptSearch::show(sec); +} + +// static +void LLScriptEdCore::onUndoMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->undo(); +} + +// static +void LLScriptEdCore::onRedoMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->redo(); +} + +// static +void LLScriptEdCore::onCutMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->cut(); +} + +// static +void LLScriptEdCore::onCopyMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->copy(); +} + +// static +void LLScriptEdCore::onPasteMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->paste(); +} + +// static +void LLScriptEdCore::onSelectAllMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->selectAll(); +} + +// static +void LLScriptEdCore::onDeselectMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return; + self->mEditor->deselect(); +} + +// static +BOOL LLScriptEdCore::enableUndoMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canUndo(); +} + +// static +BOOL LLScriptEdCore::enableRedoMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canRedo(); +} + +// static +BOOL LLScriptEdCore::enableCutMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canCut(); +} + +// static +BOOL LLScriptEdCore::enableCopyMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canCopy(); +} + +// static +BOOL LLScriptEdCore::enablePasteMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canPaste(); +} + +// static +BOOL LLScriptEdCore::enableSelectAllMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canSelectAll(); +} + +// static +BOOL LLScriptEdCore::enableDeselectMenu(void* userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + if (!self || !self->mEditor) return FALSE; + return self->mEditor->canDeselect(); +} + +// static +void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) +{ + LLScriptEdCore* self = (LLScriptEdCore*)user_data; + LLScrollListItem* item = self->mErrorList->getFirstSelected(); + if(item) + { + // *FIX: This fucked up little hack is here because we don't + // have a grep library. This is very brittle code. + S32 row = 0; + S32 column = 0; + const LLScrollListCell* cell = item->getColumn(0); + LLString line(cell->getText()); + line.erase(0, 1); + LLString::replaceChar(line, ',',' '); + LLString::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); + } +} + +// static +void LLScriptEdCore::handleReloadFromServerDialog( S32 option, void* userdata ) +{ + LLScriptEdCore* self = (LLScriptEdCore*) userdata; + switch( option ) + { + case 0: // "Yes" + if( self->mLoadCallback ) + { + self->mEditor->setText( "Loading..." ); + self->mLoadCallback( self->mUserdata ); + } + break; + + case 1: // "No" + break; + + default: + llassert(0); + break; + } +} + +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 called_from_parent) +{ + if(getVisible() && getEnabled()) + { + if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL))) + { + if(mSaveCallback) + { + // don't close after saving + mSaveCallback(mUserdata, FALSE); + } + + return TRUE; + } + } + return FALSE; +} + +/// --------------------------------------------------------------------------- +/// LLPreviewLSL +/// --------------------------------------------------------------------------- + +struct LLScriptSaveInfo +{ + LLUUID mItemUUID; + LLString mDescription; + LLTransactionID mTransactionID; + + LLScriptSaveInfo(const LLUUID& uuid, const LLString& desc, LLTransactionID tid) : + mItemUUID(uuid), mDescription(desc), mTransactionID(tid) {} +}; + + + +//static +void* LLPreviewLSL::createScriptEdPanel(void* userdata) +{ + + LLPreviewLSL *self = (LLPreviewLSL*)userdata; + + self->mScriptEd = new LLScriptEdCore("script panel", + LLRect(), + HELLO_LSL, + HELP_LSL, + self->mViewHandle, + LLPreviewLSL::onLoad, + LLPreviewLSL::onSave, + self, + 0); + + return self->mScriptEd; +} + + +LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect, + const std::string& title, const LLUUID& item_id ) +: LLPreview( name, rect, title, item_id, LLUUID::null, TRUE, + SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT ), + mPendingUploads(0) +{ + + LLRect curRect = rect; + + + LLCallbackMap::map_t factory_map; + factory_map["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this); + + + gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map); + + moveResizeHandleToFront(); + + + LLInventoryItem* item = getItem(); + + childSetCommitCallback("desc", LLPreview::onText, this); + childSetText("desc", item->getDescription()); + childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + + LLMultiFloater* hostp = getHost(); + + if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED) + { + loadAsset(); + } + + setTitle(title); + + if (!getHost()) + { + reshape(curRect.getWidth(), curRect.getHeight(), TRUE); + setRect(curRect); + } +} + + +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. + LLInventoryItem* item = gInventory.getItem(mItemUUID); + BOOL is_library = item + && !gInventory.isObjectDescendentOf(mItemUUID, + gAgent.getInventoryRootID()); + if(!item) + { + // do the more generic search. + getItem(); + } + if(item && !(item->getAssetUUID().isNull())) + { + 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->mEditor->setText("You are not allowed to view this script."); + mScriptEd->mEditor->makePristine(); + mScriptEd->mEditor->setEnabled(FALSE); + mScriptEd->mFunctions->setEnabled(FALSE); + mAssetStatus = PREVIEW_ASSET_LOADED; + } + childSetVisible("lock", !is_modifiable); + mScriptEd->childSetEnabled("Insert...", is_modifiable); + } + else + { + mScriptEd->mEditor->setText(HELLO_LSL); + mAssetStatus = PREVIEW_ASSET_LOADED; + } +} + + +BOOL LLPreviewLSL::canClose() +{ + return mScriptEd->canClose(); +} + +//override the llpreview open which attempts to load asset, load after xml ui made +void LLPreviewLSL::open() +{ + LLFloater::open(); +} + +// 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 so that the user doesn't +// get too fucked. +void LLPreviewLSL::saveIfNeeded() +{ + // llinfos << "LLPreviewLSL::save()" << llendl; + if(!mScriptEd->mEditor->isPristine()) + { + mPendingUploads = 0; + mScriptEd->mErrorList->deleteAllItems(); + mScriptEd->mEditor->makePristine(); + + // We need to update the asset information + LLTransactionID tid; + LLAssetID uuid; + tid.generate(); + uuid = tid.makeAssetID(gAgent.getSecureSessionID()); + char uuid_string[UUID_STR_LENGTH]; + uuid.toString(uuid_string); + char filename[LL_MAX_PATH]; + sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + FILE* fp = LLFile::fopen(filename, "wb"); + if(!fp) + { + llwarns << "Unable to write to " << filename << llendl; + LLScrollListItem* item = new LLScrollListItem(); + item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall); + mScriptEd->mErrorList->addItem(item); + return; + } + + LLString utf8text = mScriptEd->mEditor->getText(); + //fprintf(fp, "%s|%s\n", LLAssetType::lookup(LLAssetType::AT_LSL_TEXT), + //uuid_string); + //fprintf(fp,"{\n%s}\n", text.c_str()); + fputs(utf8text.c_str(), fp); + fclose(fp); + fp = NULL; + + // also write it out to the vfs for upload + LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND); + S32 size = utf8text.length() + 1; + + file.setMaxSize(size); + file.write((U8*)utf8text.c_str(), size); + + LLInventoryItem *inv_item = getItem(); + + // save it out to database + if(gAssetStorage && inv_item) + { + getWindow()->incBusyCount(); + mPendingUploads++; + LLScriptSaveInfo* info = NULL; + + LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc"); + + info = new LLScriptSaveInfo(mItemUUID, + descEditor->getText(), + tid); + gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &LLPreviewLSL::onSaveComplete, info); + } + + char dst_filename[LL_MAX_PATH]; + sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + char err_filename[LL_MAX_PATH]; + sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + LLScrollListItem* item = NULL; + const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA); + if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike())) + { + llinfos << "Compile failed!" << llendl; + //char command[256]; + //sprintf(command, "type %s\n", err_filename); + //system(command); + + // load the error file into the error scrolllist + if(NULL != (fp = LLFile::fopen(err_filename, "r"))) + { + char buffer[MAX_STRING]; + LLString line; + while(!feof(fp)) + { + + fgets(buffer, MAX_STRING, fp); + if(feof(fp)) + { + break; + } + else if(!buffer) + { + continue; + } + else + { + line.assign(buffer); + LLString::stripNonprintable(line); + item = new LLScrollListItem(); + item->addColumn(line, err_font); + mScriptEd->mErrorList->addItem(item); + } + } + fclose(fp); + mScriptEd->selectFirstError(); + } + } + else + { + llinfos << "Compile worked!" << llendl; + if(gAssetStorage) + { + // move the compiled file into the vfs for transport + FILE* fp = LLFile::fopen(dst_filename, "rb"); + LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND); + + fseek(fp, 0, SEEK_END); + S32 size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + file.setMaxSize(size); + + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while ((size = fread(copy_buf, 1, buf_size, fp))) + { + file.write(copy_buf, size); + } + fclose(fp); + fp = NULL; + getWindow()->incBusyCount(); + mPendingUploads++; + LLUUID* this_uuid = new LLUUID(mItemUUID); + gAssetStorage->storeAssetData(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) // StoreAssetData callback (fixed) +{ + LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data); + if(0 == status) + { + if (info) + { + LLViewerInventoryItem* item; + item = (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 = (LLPreviewLSL*)LLPreview::find(info->mItemUUID); + if (self) + { + getWindow()->decBusyCount(); + self->mPendingUploads--; + if (self->mPendingUploads <= 0 + && self->mCloseAfterSave) + { + self->close(); + } + } + } + } + else + { + llwarns << "Problem saving script: " << status << llendl; + LLStringBase<char>::format_map_t args; + args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); + gViewerWindow->alertXml("SaveScriptFailReason", args); + } + delete info; +} + +// static +void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) +{ + LLUUID* instance_uuid = (LLUUID*)user_data; + LLPreviewLSL* self = NULL; + if(instance_uuid) + { + self = LLPreviewLSL::getInstance(*instance_uuid); + } + if (0 == status) + { + if (self) + { + LLScrollListItem* item = new LLScrollListItem(); + item->addColumn("Compile successful!", LLFontGL::sSansSerifSmall); + self->mScriptEd->mErrorList->addItem(item); + + // Find our window and close it if requested. + self->getWindow()->decBusyCount(); + self->mPendingUploads--; + if (self->mPendingUploads <= 0 + && self->mCloseAfterSave) + { + self->close(); + } + } + } + else + { + llwarns << "Problem saving LSL Bytecode (Preview)" << llendl; + LLStringBase<char>::format_map_t args; + args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); + gViewerWindow->alertXml("SaveBytecodeFailReason", args); + } + delete instance_uuid; +} + +// static +void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, + void* user_data, S32 status) +{ + LLUUID* item_uuid = (LLUUID*)user_data; + LLPreviewLSL* preview = LLPreviewLSL::getInstance(*item_uuid); + if( preview ) + { + if(0 == status) + { + LLVFile file(vfs, asset_uuid, type); + S32 file_length = file.getSize(); + + char* buffer = new char[file_length+1]; + file.read((U8*)buffer, file_length); + + // put a EOS at the end + buffer[file_length] = 0; + preview->mScriptEd->mEditor->setText(buffer); + preview->mScriptEd->mEditor->makePristine(); + delete [] buffer; + 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 + { + if( gViewerStats ) + { + gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); + } + + if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || + LL_ERR_FILE_EMPTY == status) + { + LLNotifyBox::showXml("ScriptMissing"); + } + else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) + { + LLNotifyBox::showXml("ScriptNoPermissions"); + } + else + { + LLNotifyBox::showXml("UnableToLoadScript"); + } + + preview->mAssetStatus = PREVIEW_ASSET_ERROR; + llwarns << "Problem loading script: " << status << llendl; + } + } + delete item_uuid; +} + +// static +LLPreviewLSL* LLPreviewLSL::getInstance( const LLUUID& item_uuid ) +{ + LLPreview* instance = NULL; + preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid); + if(found_it != LLPreview::sInstances.end()) + { + instance = found_it->second; + } + return (LLPreviewLSL*)instance; +} + +void LLPreviewLSL::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLPreview::reshape( width, height, called_from_parent ); + + if( !isMinimized() ) + { + // So that next time you open a script it will have the same height and width + // (although not the same position). + gSavedSettings.setRect("PreviewScriptRect", mRect); + } +} + +/// --------------------------------------------------------------------------- +/// LLLiveLSLEditor +/// --------------------------------------------------------------------------- + +LLMap<LLUUID, LLLiveLSLEditor*> LLLiveLSLEditor::sInstances; + + + +//static +void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) +{ + + LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata; + + self->mScriptEd = new LLScriptEdCore("script ed panel", + LLRect(), + HELLO_LSL, + HELP_LSL, + self->mViewHandle, + &LLLiveLSLEditor::onLoad, + &LLLiveLSLEditor::onSave, + self, + 0); + + return self->mScriptEd; +} + + +LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name, + const LLRect& rect, + const std::string& title, + const LLUUID& object_id, + const LLUUID& item_id) : + LLFloater(name, rect, title, TRUE, SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT), + mObjectID(object_id), + mItemID(item_id), + mScriptEd(NULL), + mAskedForRunningInfo(FALSE), + mHaveRunningInfo(FALSE), + mCloseAfterSave(FALSE), + mPendingUploads(0) +{ + + + BOOL is_new = FALSE; + if(mItemID.isNull()) + { + mItemID.generate(); + is_new = TRUE; + } + + + LLLiveLSLEditor::sInstances.addData(mItemID ^ mObjectID, this); + + + + LLCallbackMap::map_t factory_map; + factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this); + + moveResizeHandleToFront(); + + gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map); + + + childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this); + childSetEnabled("running", FALSE); + + childSetAction("Reset",&LLLiveLSLEditor::onReset,this); + childSetEnabled("Reset", TRUE); + + + mScriptEd->mEditor->makePristine(); + loadAsset(is_new); + mScriptEd->mEditor->setFocus(TRUE); + + + if (!getHost()) + { + LLRect curRect = getRect(); + translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop); + } + + + setTitle(title); + +} + +LLLiveLSLEditor::~LLLiveLSLEditor() +{ + LLLiveLSLEditor::sInstances.removeData(mItemID ^ mObjectID); +} + + +void LLLiveLSLEditor::loadAsset(BOOL is_new) +{ + //llinfos << "LLLiveLSLEditor::loadAsset()" << llendl; + if(!is_new) + { + LLViewerObject* object = gObjectList.findObject(mObjectID); + if(object) + { + // HACK! we "know" that mItemID refers to a LLViewerInventoryItem... + LLViewerInventoryItem* item = (LLViewerInventoryItem*)object->getInventoryObject(mItemID); + 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)))) + + { + mScriptEd->mEditor->setText("You are not allowed to view this script."); + mScriptEd->mEditor->makePristine(); + mScriptEd->mEditor->setEnabled(FALSE); + } + else if(mItem.notNull() && mItem->getAssetUUID().notNull()) + { + // request the text from the object + LLUUID* user_data = new LLUUID(mItemID ^ mObjectID); + 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, mObjectID); + msg->addUUIDFast(_PREHASH_ItemID, mItemID); + msg->sendReliable(object->getRegion()->getHost()); + mAskedForRunningInfo = TRUE; + } + else + { + mScriptEd->mEditor->setText(""); + mScriptEd->mEditor->makePristine(); + } + + if(item + && !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), + GP_OBJECT_MANIPULATE)) + { + 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", mItemID); + LLHost host(object->getRegion()->getIP(), + object->getRegion()->getPort()); + gMessageSystem->sendReliable(host); + */ + } + } + else + { + mScriptEd->mEditor->setText(HELLO_LSL); + //mScriptEd->mEditor->setText(""); + //mScriptEd->mEditor->makePristine(); + 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(mItemID, + mObjectID, + perm, + LLUUID::null, + LLAssetType::AT_LSL_TEXT, + LLInventoryType::IT_LSL, + DEFAULT_SCRIPT_NAME, + DEFAULT_SCRIPT_DESC, + LLSaleInfo::DEFAULT, + LLInventoryItem::II_FLAGS_NONE, + time_corrected()); + } +} + +// static +void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, + LLAssetType::EType type, + void* user_data, S32 status) +{ + LLLiveLSLEditor* instance = NULL; + LLUUID* xored_id = (LLUUID*)user_data; + + if( LLLiveLSLEditor::sInstances.checkData(*xored_id) ) + { + if( LL_ERR_NOERR == status ) + { + instance = LLLiveLSLEditor::sInstances[*xored_id]; + instance->loadScriptText(vfs, asset_id, type); + } + else + { + if( gViewerStats ) + { + gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); + } + + if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || + LL_ERR_FILE_EMPTY == status) + { + LLNotifyBox::showXml("ScriptMissing"); + } + else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) + { + LLNotifyBox::showXml("ScriptNoPermissions"); + } + else + { + LLNotifyBox::showXml("UnableToLoadScript"); + } + } + } + + delete xored_id; +} + +void LLLiveLSLEditor::loadScriptText(const char* filename) +{ + FILE* file = LLFile::fopen(filename, "rb"); + if(file) + { + // read in the whole file + fseek(file, 0L, SEEK_END); + S32 file_length = ftell(file); + fseek(file, 0L, SEEK_SET); + char* buffer = new char[file_length+1]; + fread(buffer, file_length, 1, file); + fclose(file); + buffer[file_length] = 0; + mScriptEd->mEditor->setText(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(); + char *buffer = new char[file_length + 1]; + file.read((U8*)buffer, file_length); + + if (file.getLastBytesRead() != file_length || + file_length <= 0) + { + llwarns << "Error reading " << uuid << ":" << type << llendl; + } + + buffer[file_length] = '\0'; + + mScriptEd->mEditor->setText(buffer); + mScriptEd->mEditor->makePristine(); + delete[] buffer; + +} + + +void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata ) +{ + LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; + LLViewerObject* object = gObjectList.findObject( self->mObjectID ); + LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(self, "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->mObjectID); + msg->addUUIDFast(_PREHASH_ItemID, self->mItemID); + msg->addBOOLFast(_PREHASH_Running, running); + msg->sendReliable(object->getRegion()->getHost()); + } + else + { + runningCheckbox->set(!running); + gViewerWindow->alertXml("CouldNotStartStopScript"); + } +} + +void LLLiveLSLEditor::onReset(void *userdata) +{ + LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; + + LLViewerObject* object = gObjectList.findObject( self->mObjectID ); + 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->mObjectID); + msg->addUUIDFast(_PREHASH_ItemID, self->mItemID); + msg->sendReliable(object->getRegion()->getHost()); + } + else + { + gViewerWindow->alertXml("CouldNotStartStopScript"); + } +} + +void LLLiveLSLEditor::draw() +{ + if(getVisible()) + { + LLViewerObject* object = gObjectList.findObject(mObjectID); + LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running"); + if(object && mAskedForRunningInfo && mHaveRunningInfo) + { + if(object->permAnyOwner()) + { + runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL); + runningCheckbox->setEnabled(TRUE); + } + else + { + runningCheckbox->setLabel(DISABLED_RUNNING_CHECKBOX_LABEL); + 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); + } + } + else if(!object) + { + // HACK: Display this information in the title bar. + // Really ought to put in main window. + setTitle("Script (object out of range)"); + runningCheckbox->setEnabled(FALSE); + // object may have fallen out of range. + mHaveRunningInfo = FALSE; + } + LLFloater::draw(); + } +} + +struct LLLiveLSLSaveData +{ + LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, BOOL active); + LLUUID mObjectID; + LLPointer<LLViewerInventoryItem> mItem; + BOOL mActive; +}; + +LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id, + const LLViewerInventoryItem* item, + BOOL active) : + mObjectID(id), + mActive(active) +{ + llassert(item); + mItem = new LLViewerInventoryItem(item); +} + +void LLLiveLSLEditor::saveIfNeeded() +{ + llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl; + LLViewerObject* object = gObjectList.findObject(mObjectID); + if(!object) + { + gViewerWindow->alertXml("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. + // HACK! we "know" that mItemID refers to a LLInventoryItem... + LLInventoryItem* inv_item = (LLInventoryItem*)object->getInventoryObject(mItemID); + if(inv_item) + { + mItem->copy(inv_item); + } + + // Don't need to save if we're pristine + if(mScriptEd->mEditor->isPristine()) + { + return; + } + + mPendingUploads = 0; + + // save the script + mScriptEd->mEditor->makePristine(); + mScriptEd->mErrorList->deleteAllItems(); + + // set up the save on the local machine. + mScriptEd->mEditor->makePristine(); + LLTransactionID tid; + LLAssetID uuid; + tid.generate(); + uuid = tid.makeAssetID(gAgent.getSecureSessionID()); + mItem->setAssetUUID(uuid); + mItem->setTransactionID(tid); + + // write out the data, and store it in the asset database + char uuid_string[UUID_STR_LENGTH]; + uuid.toString(uuid_string); + char filename[LL_MAX_PATH]; + sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + FILE* fp = LLFile::fopen(filename, "wb"); + if(!fp) + { + llwarns << "Unable to write to " << filename << llendl; + LLScrollListItem* item = new LLScrollListItem(); + item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall); + mScriptEd->mErrorList->addItem(item); + return; + } + LLString utf8text = mScriptEd->mEditor->getText(); + fputs(utf8text.c_str(), fp); + fclose(fp); + + LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running"); + + // save it out to database + if(gAssetStorage) + { + // write it out to the vfs for upload + LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND); + S32 size = utf8text.length() + 1; + + file.setMaxSize(size); + file.write((U8*)utf8text.c_str(), size); + + getWindow()->incBusyCount(); + mPendingUploads++; + LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectID, + mItem, + runningCheckbox->get()); + gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &onSaveTextComplete, (void*)data, FALSE); + } + +#if LL_WINDOWS + // This major hack was inserted because sometimes compilation + // would fail because it couldn't open this file... I decided + // to make a loop until open was successful. This seems to be + // a problem specific to ntfs. + fp = NULL; + const U32 MAX_TRIES = 20; + U32 tries = MAX_TRIES; + while((!fp) && --tries) + { + ms_sleep(17); + fp = LLFile::fopen(filename, "r"); + if(!fp) + { + llwarns << "Trying to open the source file " << filename + << " again" << llendl; + } + else + { + fclose(fp); + } + } + fp = NULL; +#endif + + char dst_filename[LL_MAX_PATH]; + sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + char err_filename[LL_MAX_PATH]; + sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + LLScrollListItem* item = NULL; + const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA); + if(!lscript_compile(filename, dst_filename, err_filename, 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]; + LLString line; + while(!feof(fp)) + { + + fgets(buffer, MAX_STRING, fp); + if(feof(fp)) + { + break; + } + else if(!buffer) + { + continue; + } + else + { + line.assign(buffer); + LLString::stripNonprintable(line); + item = new LLScrollListItem(); + item->addColumn(line, err_font); + mScriptEd->mErrorList->addItem(item); + } + } + 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->addSimpleItem("Compile successful, saving..."); + if(gAssetStorage) + { + llinfos << "LLLiveLSLEditor::saveAsset " + << mItem->getAssetUUID() << llendl; + + // move the compiled file into the vfs for transport + FILE* fp = LLFile::fopen(dst_filename, "rb"); + LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND); + + fseek(fp, 0, SEEK_END); + S32 size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + file.setMaxSize(size); + + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while ((size = fread(copy_buf, 1, buf_size, fp))) + { + file.write(copy_buf, size); + } + fclose(fp); + fp = NULL; + + getWindow()->incBusyCount(); + mPendingUploads++; + LLLiveLSLSaveData* data = NULL; + data = new LLLiveLSLSaveData(mObjectID, + mItem, + runningCheckbox->get()); + gAssetStorage->storeAssetData(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! + runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL); + runningCheckbox->setEnabled(TRUE); +} + +void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) +{ + LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data; + + if (status) + { + llwarns << "Unable to save text for a script." << llendl; + LLStringBase<char>::format_map_t args; + args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); + gViewerWindow->alertXml("CompileQueueSaveText", args); + } + else + { + LLLiveLSLEditor* self = sInstances.getIfThere(data->mItem->getUUID() ^ data->mObjectID); + if (self) + { + self->getWindow()->decBusyCount(); + self->mPendingUploads--; + if (self->mPendingUploads <= 0 + && self->mCloseAfterSave) + { + self->close(); + } + } + } + delete data; + data = NULL; +} + + +void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) +{ + LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data; + if(!data) + return; + if(0 ==status) + { + llinfos << "LSL Bytecode saved" << llendl; + LLUUID xor_id = data->mItem->getUUID() ^ data->mObjectID; + LLLiveLSLEditor* self = sInstances.getIfThere(xor_id); + if(self) + { + // Tell the user that the compile worked. + self->mScriptEd->mErrorList->addSimpleItem("Save complete."); + // close the window if this completes both uploads + self->getWindow()->decBusyCount(); + self->mPendingUploads--; + if (self->mPendingUploads <= 0 + && self->mCloseAfterSave) + { + self->close(); + } + } + LLViewerObject* object = gObjectList.findObject(data->mObjectID); + 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; + + LLStringBase<char>::format_map_t args; + args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); + gViewerWindow->alertXml("CompileQueueSaveBytecode", args); + } + char uuid_string[UUID_STR_LENGTH]; + data->mItem->getAssetUUID().toString(uuid_string); + char dst_filename[LL_MAX_PATH]; + sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + LLFile::remove(dst_filename); + sprintf(dst_filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); + LLFile::remove(dst_filename); + delete data; +} + +BOOL LLLiveLSLEditor::canClose() +{ + return (mScriptEd->canClose()); +} + +// 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; + self->mCloseAfterSave = close_after_save; + self->saveIfNeeded(); +} + +// static +LLLiveLSLEditor* LLLiveLSLEditor::show(const LLUUID& script_id, const LLUUID& object_id) +{ + LLLiveLSLEditor* instance = NULL; + LLUUID xored_id = script_id ^ object_id; + if(LLLiveLSLEditor::sInstances.checkData(xored_id)) + { + // Move the existing view to the front + instance = LLLiveLSLEditor::sInstances[xored_id]; + instance->open(); + } + return instance; +} + +// static +void LLLiveLSLEditor::hide(const LLUUID& script_id, const LLUUID& object_id) +{ + LLUUID xored_id = script_id ^ object_id; + if( LLLiveLSLEditor::sInstances.checkData( xored_id ) ) + { + LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id]; + if(instance->getParent()) + { + instance->getParent()->removeChild(instance); + } + delete instance; + } +} + +// 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); + LLUUID xored_id = item_id ^ object_id; + if(LLLiveLSLEditor::sInstances.checkData(xored_id)) + { + LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id]; + instance->mHaveRunningInfo = TRUE; + BOOL running; + msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running); + LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(instance, "running"); + runningCheckbox->set(running); + } +} + +void LLLiveLSLEditor::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLFloater::reshape( width, height, called_from_parent ); + + if( !isMinimized() ) + { + // So that next time you open a script it will have the same height and width + // (although not the same position). + gSavedSettings.setRect("PreviewScriptRect", mRect); + } +} |