From ce65f098dff3f995759fb285c23e0e75abf0fbd8 Mon Sep 17 00:00:00 2001 From: McCabe Maxsted Date: Tue, 13 Sep 2011 11:26:38 -0700 Subject: Ported external script editor patch from Phoenix, added some cleanup, and made it use menus (also added autosave and fixed the 'reset' button alignment being off while I was there) --- linden/indra/newview/app_settings/settings.xml | 11 + linden/indra/newview/llfilepicker.cpp | 24 ++ linden/indra/newview/llfilepicker.h | 1 + linden/indra/newview/llpreviewscript.cpp | 284 ++++++++++++++++++++- linden/indra/newview/llpreviewscript.h | 17 +- .../default/xui/en-us/floater_live_lsleditor.xml | 2 +- .../default/xui/en-us/floater_script_ed_panel.xml | 14 +- 7 files changed, 341 insertions(+), 12 deletions(-) (limited to 'linden/indra') diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml index 3279b6b..439459c 100644 --- a/linden/indra/newview/app_settings/settings.xml +++ b/linden/indra/newview/app_settings/settings.xml @@ -795,6 +795,17 @@ Value 30 + LSLExternalEditor + + Comment + Controls the location of the External editor + Persist + 1 + Type + String + Value + + MapServerURL Comment diff --git a/linden/indra/newview/llfilepicker.cpp b/linden/indra/newview/llfilepicker.cpp index ec4e294..a562b17 100644 --- a/linden/indra/newview/llfilepicker.cpp +++ b/linden/indra/newview/llfilepicker.cpp @@ -60,6 +60,12 @@ LLFilePicker LLFilePicker::sInstance; #define XML_FILTER L"XML files (*.xml)\0*.xml\0" #define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0" #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0" +#ifdef LL_WINDOWS + #define APP_FILTER L"Executable files (*.exe)\0*.exe\0" +#else + // If we can, add any mac/linux binary searching here -- MC + #define APP_FILTER L"Executable files (*.*)\0*.*\0" +#endif // LL_WINDOOWS #endif // @@ -192,6 +198,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter) mOFN.lpstrFilter = RAW_FILTER \ L"\0"; break; + case FFLOAD_APP: + mOFN.lpstrFilter = APP_FILTER \ + L"\0"; + break; default: res = FALSE; break; @@ -601,6 +611,17 @@ Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callB AEDisposeDesc(&desc); } } + else if(filter == FFLOAD_APP) + { + // App bundles are of type APPL; ???? is a folder, and 0 is something going wrong. + if((int)navInfo->fileAndFolder.folderInfo.folderType != FOUR_CHAR_CODE('APPL') && + (int)navInfo->fileAndFolder.folderInfo.folderType != FOUR_CHAR_CODE('\?\?\?\?') && + (int)navInfo->fileAndFolder.folderInfo.folderType != 0 + ) + { + result = false; + } + } } return result; } @@ -844,6 +865,9 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter) reset(); mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; + if(filter == FFLOAD_APP) + mNavOptions.optionFlags |= kNavSupportPackages; + // Modal, so pause agent send_agent_pause(); { diff --git a/linden/indra/newview/llfilepicker.h b/linden/indra/newview/llfilepicker.h index fb20ed3..3925ae7 100644 --- a/linden/indra/newview/llfilepicker.h +++ b/linden/indra/newview/llfilepicker.h @@ -92,6 +92,7 @@ public: FFLOAD_SLOBJECT = 7, FFLOAD_RAW = 8, FFLOAD_TEXT = 9, + FFLOAD_APP = 10 }; enum ESaveFilter diff --git a/linden/indra/newview/llpreviewscript.cpp b/linden/indra/newview/llpreviewscript.cpp index d2d9ed5..a1c05c7 100644 --- a/linden/indra/newview/llpreviewscript.cpp +++ b/linden/indra/newview/llpreviewscript.cpp @@ -178,7 +178,11 @@ LLScriptEdCore::LLScriptEdCore( mLastHelpToken(NULL), mLiveHelpHistorySize(0), mEnableSave(FALSE), - mHasScriptData(FALSE) + mEnableXEd(FALSE), + mHasScriptData(FALSE), + // We need to check for a new file every five seconds, or autosave every 60. + // There's probably a better solution to both of the above. + LLEventTimer((gSavedSettings.getString("LSLExternalEditor").length() < 3) ? 60 : 5) { setFollowsAll(); setBorderVisible(FALSE); @@ -209,11 +213,30 @@ LLScriptEdCore::LLScriptEdCore( tooltips.push_back(ll_safe_string(gScriptLibrary.mFunctions[i]->mDesc)); } } + + //gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini") + std::string keyword_path = gDirUtilp->getUserSkinDir() + gDirUtilp->getDirDelimiter() + "keywords.ini"; + if(!LLFile::isfile(keyword_path)) + { + llinfos << "nothing at " << keyword_path << llendl; + keyword_path = gDirUtilp->getSkinDir() + gDirUtilp->getDirDelimiter() + "keywords.ini"; + if(!LLFile::isfile(keyword_path)) + { + llinfos << "nothing at " << keyword_path << " ; will use default" << llendl; + keyword_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keywords.ini"); + } + else + { + llinfos << "loaded skin-specific keywords from " << keyword_path << llendl; + } + } + else + { + llinfos << "loaded skin-specific keywords from " << keyword_path << llendl; + } LLColor3 color(0.5f, 0.0f, 0.15f); - - mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color); + mEditor->loadKeywords(keyword_path, funcs, tooltips, color); - LLKeywordToken *token; LLKeywords::keyword_iterator_t token_it; for (token_it = mEditor->keywordsBegin(); token_it != mEditor->keywordsEnd(); ++token_it) @@ -233,7 +256,6 @@ LLScriptEdCore::LLScriptEdCore( childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); childSetAction("Save_btn", onBtnSave,this); - initMenu(); // Do the work that addTabPanel() normally does. @@ -250,6 +272,23 @@ LLScriptEdCore::~LLScriptEdCore() deleteBridges(); } +BOOL LLScriptEdCore::tick() +{ + //autoSave(); + if (gSavedSettings.getString("LSLExternalEditor").length() < 3) + { + if (hasChanged(this)) + { + autoSave(); + } + } + else + { + XedUpd(); + } + return FALSE; +} + void LLScriptEdCore::initMenu() { @@ -297,6 +336,14 @@ void LLScriptEdCore::initMenu() menuItem->setMenuCallback(onBtnHelp, this); menuItem->setEnabledCallback(NULL); + menuItem = getChild("Set External Editor..."); + menuItem->setMenuCallback(onSetExternalEditor, this); + menuItem->setEnabledCallback(NULL); + + menuItem = getChild("Open in External Editor"); + menuItem->setMenuCallback(onBtnXEd, this); + menuItem->setEnabledCallback(enableExternalEditor); + menuItem = getChild("Import Script..."); menuItem->setMenuCallback(onBtnLoadFromDisc, this); menuItem->setEnabledCallback(NULL); @@ -318,7 +365,9 @@ void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid) { if (mEditor) { - mEditor->setText(text); + std::string new_text(text); + LLStringUtil::replaceTabsWithSpaces(new_text, 4); // fix tabs in text + mEditor->setText(new_text); mHasScriptData = is_valid; } } @@ -416,6 +465,175 @@ void LLScriptEdCore::updateDynamicHelp(BOOL immediate) setHelpPage(LLStringUtil::null); } } +//dim +void LLScriptEdCore::xedLaunch() +{ + //llinfos << "LLScriptEdCore::autoSave()" << llendl; + + std::string editor = gSavedSettings.getString("LSLExternalEditor"); + if (!gDirUtilp->fileExists(editor)) + { + llwarns << "External editor " + editor + " not found" << llendl; + + LLSD row; + row["columns"][0]["value"] = "Couldn't open external editor '" + editor + "'. File not found."; + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + mErrorList->addElement(row); + return; + } + + //std::string filepath = gDirUtilp->getExpandedFilename(gDirUtilp->getTempDir(),asset_id.asString()); + if( mXfname.empty() ) + { + std::string asfilename = gDirUtilp->getTempFilename(); + asfilename.replace( asfilename.length()-4, 12, "_Xed.lsl" ); + mXfname = asfilename; + //mAutosaveFilename = llformat("%s.lsl", asfilename.c_str()); + } + + FILE* fp = LLFile::fopen(mXfname.c_str(), "wb"); + if(!fp) + { + llwarns << "Unable to write to " << mXfname << llendl; + + LLSD row; + row["columns"][0]["value"] = "Error writing to temp file. Is your hard drive full?"; + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + mErrorList->addElement(row); + return; + } + mEditor->setEnabled(FALSE); + std::string utf8text = mEditor->getText(); + fputs(utf8text.c_str(), fp); + fclose(fp); + fp = NULL; + llinfos << "XEditor: " << mXfname << llendl; + //record the stat + stat(mXfname.c_str(), &mXstbuf); + //launch +#if LL_WINDOWS + //just to get rid of the pesky black window + std::string exe = gSavedSettings.getString("LSLExternalEditor"); + S32 spaces=0; + for(S32 i=0; i!=exe.size(); ++i) + { + spaces+=( exe.at(i)==' '); + } + if(spaces > 0) + { + exe = "\""+exe+"\""; + } + std::string theCMD("%COMSPEC% /c START \"External Editor\" " + exe + " " + mXfname + " & exit"); + llinfos << "FINAL COMMAND IS :"<< + theCMD.c_str() << llendl; + + std::system(theCMD.c_str()); +#elif LL_DARWIN + // Use Launch Services for this - launching another instance is fail (and incorrect on OS X) + CFStringRef strPath = CFStringCreateWithCString(kCFAllocatorDefault, mXfname.c_str(), kCFStringEncodingUTF8); + CFURLRef tempPath = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, strPath, kCFURLPOSIXPathStyle, false); + CFURLRef tempPathArray[1] = { tempPath }; + CFArrayRef arguments = CFArrayCreate(kCFAllocatorDefault, (const void **)tempPathArray, 1, NULL); + LSApplicationParameters appParams; + memset(&appParams, 0, sizeof(appParams)); + FSRef ref; + FSPathMakeRef((UInt8*)gSavedSettings.getString("LSLExternalEditor").c_str(), &ref, NULL); + appParams.application = &ref; + appParams.flags = kLSLaunchAsync | kLSLaunchStartClassic; + LSOpenURLsWithRole(arguments, kLSRolesAll, NULL, &appParams, NULL, 0); + CFRelease(arguments); + CFRelease(tempPath); + CFRelease(strPath); +#else + //std::system(std::string(gSavedSettings.getString("LSLExternalEditor") + " " + mXfname).c_str()); + + // Any approach involving std::system will fail because SL eats signals. + // This was stolen from floaterskinfinder.cpp. + std::string exe = gSavedSettings.getString("LSLExternalEditor"); + const char *zargv[] = {exe.c_str(), mXfname.c_str(), NULL}; + fflush(NULL); + pid_t id = vfork(); + if(id == 0) + { + execv(exe.c_str(), (char **)zargv); + _exit(0); // This shouldn't ever be reached. + } +#endif +} + +void LLScriptEdCore::XedUpd() +{ + struct stat stbuf; + stat(this->mXfname.c_str() , &stbuf); + if (this->mXstbuf.st_mtime != stbuf.st_mtime) + { + this->mErrorList->addCommentText(std::string("Change Detected... Updating")); + + this->mXstbuf = stbuf; + LLFILE* file = LLFile::fopen(this->mXfname, "rb"); /*Flawfinder: ignore*/ + if(file) + { + // read in the whole file + fseek(file, 0L, SEEK_END); + S64 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); + std::string ttext = LLStringExplicit(buffer); + LLStringUtil::replaceTabsWithSpaces(ttext, 4); + mEditor->setText(ttext); + LLScriptEdCore::doSave( this, FALSE ); + //mEditor->makePristine(); + delete[] buffer; + buffer = NULL; + } + else + { + llwarns << "Error opening " << this->mXfname << llendl; + } + } +} +//end dim +void LLScriptEdCore::autoSave() +{ + //llinfos << "LLScriptEdCore::autoSave()" << llendl; + if(mEditor->isPristine()) + { + return; + } + //std::string filepath = gDirUtilp->getExpandedFilename(gDirUtilp->getTempDir(),asset_id.asString()); + if( mAutosaveFilename.empty() ) + { + std::string asfilename = gDirUtilp->getTempFilename(); + asfilename.replace( asfilename.length()-4, 12, "_autosave.lsl" ); + mAutosaveFilename = asfilename; + //mAutosaveFilename = llformat("%s.lsl", asfilename.c_str()); + } + + FILE* fp = LLFile::fopen(mAutosaveFilename.c_str(), "wb"); + if(!fp) + { + llwarns << "Unable to write to " << mAutosaveFilename << llendl; + + LLSD row; + row["columns"][0]["value"] = "Error writing to temp file. Is your hard drive full?"; + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + mErrorList->addElement(row); + return; + } + + std::string utf8text = mEditor->getText(); + fputs(utf8text.c_str(), fp); + fclose(fp); + fp = NULL; + llinfos << "autosave: " << mAutosaveFilename << llendl; +} void LLScriptEdCore::setHelpPage(const std::string& help_string) { @@ -507,6 +725,11 @@ bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLS break; case 1: // "No" + if( !mXfname.empty()) + { + llinfos << "remove autosave: " << mXfname << llendl; + LLFile::remove(mXfname.c_str()); + } mForceClose = TRUE; // This will close immediately because mForceClose is true, so we won't // infinite loop with these dialogs. JC @@ -724,6 +947,34 @@ void LLScriptEdCore::onBtnSave(void* data) } // static +void LLScriptEdCore::onSetExternalEditor(void* data) +{ + std::string cur_name(gSavedSettings.getString("LSLExternalEditor")); + + LLFilePicker& picker = LLFilePicker::instance(); + if (! picker.getOpenFile(LLFilePicker::FFLOAD_APP) ) + { + return; //Canceled! + } + std::string file_name = picker.getFirstFile(); + if (!file_name.empty() && file_name != cur_name) + { + gSavedSettings.setString("LSLExternalEditor", file_name); + } + else + { + gSavedSettings.setString("LSLExternalEditor", ""); + } +} + +//static +void LLScriptEdCore::onBtnXEd(void* data) +{ + LLScriptEdCore* self = (LLScriptEdCore*)data; + self->xedLaunch(); +} + +// static void LLScriptEdCore::onBtnUndoChanges( void* userdata ) { LLScriptEdCore* self = (LLScriptEdCore*) userdata; @@ -904,6 +1155,12 @@ BOOL LLScriptEdCore::enableDeselectMenu(void* userdata) } // static +BOOL LLScriptEdCore::enableExternalEditor(void* userdata) +{ + return (gSavedSettings.getString("LSLExternalEditor").length() > 3); +} + +// static void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) { LLScriptEdCore* self = (LLScriptEdCore*)user_data; @@ -989,6 +1246,11 @@ BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask) if(mSaveCallback) { // don't close after saving + if (!hasChanged(this)) + { + llinfos << "Save Not Needed" << llendl; + return TRUE; + } mSaveCallback(mUserdata, FALSE); } @@ -1181,6 +1443,11 @@ void LLPreviewLSL::closeIfNeeded() mPendingUploads--; if (mPendingUploads <= 0 && mCloseAfterSave) { + if( !mScriptEd->mXfname.empty()) + { + llinfos << "remove autosave: " << mScriptEd->mXfname << llendl; + LLFile::remove(mScriptEd->mXfname.c_str()); + } close(); } } @@ -2349,6 +2616,11 @@ void LLLiveLSLEditor::closeIfNeeded() mPendingUploads--; if (mPendingUploads <= 0 && mCloseAfterSave) { + if( !mScriptEd->mXfname.empty()) + { + llinfos << "remove autosave: " << mScriptEd->mXfname << llendl; + LLFile::remove(mScriptEd->mXfname.c_str()); + } close(); } } diff --git a/linden/indra/newview/llpreviewscript.h b/linden/indra/newview/llpreviewscript.h index 8e61435..4de886e 100644 --- a/linden/indra/newview/llpreviewscript.h +++ b/linden/indra/newview/llpreviewscript.h @@ -40,6 +40,7 @@ #include "llcombobox.h" #include "lliconctrl.h" #include "llframetimer.h" +#include "lltimer.h" class LLMessageSystem; @@ -53,7 +54,7 @@ class LLMenuBarGL; class LLKeywordToken; // Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these. -class LLScriptEdCore : public LLPanel +class LLScriptEdCore : public LLPanel, public LLEventTimer { friend class LLPreviewScript; friend class LLPreviewLSL; @@ -96,6 +97,8 @@ public: static void onBtnInsertFunction(LLUICtrl*, void*); static void doSave( void* userdata, BOOL close_after_save ); static void onBtnSave(void*); + static void onSetExternalEditor(void* data); + static void onBtnXEd(void*); static void onBtnUndoChanges(void*); static void onBtnSaveToDisc(void*); static void onBtnLoadFromDisc(void*); @@ -117,13 +120,21 @@ public: static BOOL enablePasteMenu(void* userdata); static BOOL enableSelectAllMenu(void* userdata); static BOOL enableDeselectMenu(void* userdata); + static BOOL enableExternalEditor(void* userdata); static BOOL hasChanged(void* userdata); void selectFirstError(); + + void autoSave(); + //dim external ed + void XedUpd(); + void xedLaunch(); virtual BOOL handleKeyHere(KEY key, MASK mask); + virtual BOOL tick(); + void enableSave(BOOL b) {mEnableSave = b;} protected: @@ -137,6 +148,9 @@ protected: private: std::string mSampleText; + std::string mAutosaveFilename; + std::string mXfname; + struct stat mXstbuf; std::string mHelpURL; std::string mScriptTitle; LLTextEditor* mEditor; @@ -155,6 +169,7 @@ private: LLFrameTimer mLiveHelpTimer; S32 mLiveHelpHistorySize; BOOL mEnableSave; + BOOL mEnableXEd; BOOL mHasScriptData; }; diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_live_lsleditor.xml b/linden/indra/newview/skins/default/xui/en-us/floater_live_lsleditor.xml index 2e91a82..9dda4ec 100644 --- a/linden/indra/newview/skins/default/xui/en-us/floater_live_lsleditor.xml +++ b/linden/indra/newview/skins/default/xui/en-us/floater_live_lsleditor.xml @@ -4,7 +4,7 @@ min_height="271" min_width="290" mouse_opaque="true" name="script ed float" rect_control="FloaterOpenObjectRect" title="Script: New Script" width="500" border_drop_shadow_visible="false" border_visible="false" bevel_style="none" border_style="line" border_thickness="0">