diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp index e672099fc..1f7d0432a 100644 --- a/indra/llcommon/llfasttimer_class.cpp +++ b/indra/llcommon/llfasttimer_class.cpp @@ -130,8 +130,16 @@ public: mActiveTimerRoot->setCollapsed(false); mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot); - mRootFrameState->mParent = &mTimerRoot->getFrameState(); - mActiveTimerRoot->setParent(mTimerRoot); + // getFrameState and setParent recursively call this function, + // so we have to work around that by using a specialized implementation + // for the special case were mTimerRoot != mActiveTimerRoot -- Aleric + mRootFrameState->mParent = &LLFastTimer::getFrameStateList()[0]; // &mTimerRoot->getFrameState() + // And the following four lines are mActiveTimerRoot->setParent(mTimerRoot); + llassert(!mActiveTimerRoot->mParent); + mActiveTimerRoot->mParent = mTimerRoot; // mParent = parent; + mRootFrameState->mParent = mRootFrameState->mParent; // getFrameState().mParent = &parent->getFrameState(); + mTimerRoot->getChildren().push_back(mActiveTimerRoot); // parent->getChildren().push_back(this); + mTimerRoot->mNeedsSorting = true; // parent->mNeedsSorting = true; mAppTimer = new LLFastTimer(mRootFrameState); } diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 49d99f2cd..6fac2c8a9 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -151,9 +151,10 @@ public: */ static void deleteSingleton() { - delete getData().mSingletonInstance; - getData().mSingletonInstance = NULL; + DERIVED_TYPE* instance = getData().mSingletonInstance; getData().mInitState = DELETED; + getData().mSingletonInstance = NULL; + delete instance; } static SingletonInstanceData& getData() @@ -175,25 +176,11 @@ public: { SingletonInstanceData& data = getData(); - if (data.mInitState == CONSTRUCTING) + if (data.mInitState != INITIALIZED) { - llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; + createInstance(data); } - if (data.mInitState == DELETED) - { - llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; - } - - if (!data.mSingletonInstance) - { - data.mInitState = CONSTRUCTING; - data.mSingletonInstance = new DERIVED_TYPE(); - data.mInitState = INITIALIZING; - data.mSingletonInstance->initSingleton(); - data.mInitState = INITIALIZED; - } - return data.mSingletonInstance; } @@ -219,7 +206,35 @@ public: } private: + static void createInstance(SingletonInstanceData& data); virtual void initSingleton() {} }; +// Moved this here cause it's too big to be inlined --Aleric. +template +void LLSingleton::createInstance(SingletonInstanceData& data) +{ + if (data.mInitState == CONSTRUCTING) + { + llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; + } + + if (data.mInitState == DELETED) + { + llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; + } + + if (data.mInitState == INITIALIZING) + { + llwarns << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl; + return; + } + + data.mInitState = CONSTRUCTING; + data.mSingletonInstance = new DERIVED_TYPE(); + data.mInitState = INITIALIZING; + data.mSingletonInstance->initSingleton(); + data.mInitState = INITIALIZED; +} + #endif diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index fc5012b41..b58dd5931 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -330,7 +330,7 @@ public: virtual void setLastError(const std::string& message, const std::string& filename = std::string()); protected: - BOOL copyData(U8 *data, S32 size); // calls updateData() + BOOL copyData(U8 *data, S32 size); protected: S8 mCodec; diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index a4df354c5..3c74e50a4 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -117,7 +117,7 @@ LLDir_Linux::LLDir_Linux() mOSUserAppDir = ""; mLindenUserDir = ""; - char path [32]; /* Flawfinder: ignore */ + char path [MAX_PATH]; /* Flawfinder: ignore */ // *NOTE: /proc/%d/exe doesn't work on FreeBSD. But that's ok, // because this is the linux implementation. diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp index 7e60c0fd5..419d1b76c 100644 --- a/indra/llvfs/llvfile.cpp +++ b/indra/llvfs/llvfile.cpp @@ -130,7 +130,7 @@ BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) } //static -U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) +U8* LLVFile::readFile(LLVFS *vfs, LLPrivateMemoryPool* poolp, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) { U8 *data; LLVFile file(vfs, uuid, type, LLVFile::READ); @@ -142,12 +142,12 @@ U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S } else { - data = new U8[file_size]; + data = (U8*)ALLOCATE_MEM(poolp, file_size); file.read(data, file_size); /* Flawfinder: ignore */ if (file.getLastBytesRead() != (S32)file_size) { - delete[] data; + FREE_MEM(poolp, data); data = NULL; file_size = 0; } diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h index c3bca8c73..6a112cc45 100644 --- a/indra/llvfs/llvfile.h +++ b/indra/llvfs/llvfile.h @@ -38,6 +38,8 @@ #include "llvfs.h" #include "llvfsthread.h" +class LLPrivateMemoryPool; + class LLVFile { public: @@ -45,7 +47,7 @@ public: ~LLVFile(); BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ - static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); + static U8* readFile(LLVFS *vfs, LLPrivateMemoryPool* poolp, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); void setReadPriority(const F32 priority); BOOL isReadComplete(); S32 getLastBytesRead(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d6e26d94c..454cbfd94 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -165,6 +165,7 @@ set(viewer_SOURCE_FILES lleventinfo.cpp lleventnotifier.cpp lleventpoll.cpp + llexternaleditor.cpp llface.cpp llfasttimerview.cpp llfeaturemanager.cpp @@ -645,6 +646,7 @@ set(viewer_HEADER_FILES lleventinfo.h lleventnotifier.h lleventpoll.h + llexternaleditor.h llface.h llfasttimerview.h llfeaturemanager.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 55f6a70f0..f9365d8da 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7399,7 +7399,7 @@ Comment Translate incoming chat messages Persist - 1 + 0 Type Boolean Value @@ -14436,6 +14436,17 @@ Value 150000.0 + ExternalEditor + + Comment + Path to program used to edit LSL scripts and XUI files, e.g.: /usr/bin/gedit --new-window "%s" + Persist + 1 + Type + String + Value + + YawFromMousePosition Comment diff --git a/indra/newview/llexternaleditor.cpp b/indra/newview/llexternaleditor.cpp new file mode 100644 index 000000000..c016b8da6 --- /dev/null +++ b/indra/newview/llexternaleditor.cpp @@ -0,0 +1,208 @@ +/** + * @file llexternaleditor.cpp + * @brief A convenient class to run external editor. + * + * $LicenseInfo:firstyear=2010&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 "llexternaleditor.h" + +#include "lltrans.h" +#include "llui.h" + +// static +const std::string LLExternalEditor::sFilenameMarker = "%s"; + +// static +const std::string LLExternalEditor::sSetting = "ExternalEditor"; + +LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env_var, const std::string& override) +{ + std::string cmd = findCommand(env_var, override); + if (cmd.empty()) + { + llwarns << "Editor command is empty or not set" << llendl; + return EC_NOT_SPECIFIED; + } + + // Add the filename marker if missing. + if (cmd.find(sFilenameMarker) == std::string::npos) + { + cmd += " \"" + sFilenameMarker + "\""; + llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl; + } + + string_vec_t tokens; + if (tokenize(tokens, cmd) < 2) // 2 = bin + at least one arg (%s) + { + llwarns << "Error parsing editor command" << llendl; + return EC_PARSE_ERROR; + } + + // Check executable for existence. + std::string bin_path = tokens[0]; + if (!LLFile::isfile(bin_path)) + { + llwarns << "Editor binary [" << bin_path << "] not found" << llendl; + return EC_BINARY_NOT_FOUND; + } + + // Save command. + mProcess.setExecutable(bin_path); + mArgs.clear(); + for (size_t i = 1; i < tokens.size(); ++i) + { + if (i > 1) mArgs += " "; + mArgs += "\"" + tokens[i] + "\""; + } + llinfos << "Setting command [" << bin_path << " " << mArgs << "]" << llendl; + + return EC_SUCCESS; +} + +LLExternalEditor::EErrorCode LLExternalEditor::run(const std::string& file_path) +{ + std::string args = mArgs; + if (mProcess.getExecutable().empty() || args.empty()) + { + llwarns << "Editor command not set" << llendl; + return EC_NOT_SPECIFIED; + } + + // Substitute the filename marker in the command with the actual passed file name. + LLStringUtil::replaceString(args, sFilenameMarker, file_path); + + // Split command into separate tokens. + string_vec_t tokens; + tokenize(tokens, args); + + // Set process arguments taken from the command. + mProcess.clearArguments(); + for (string_vec_t::const_iterator arg_it = tokens.begin(); arg_it != tokens.end(); ++arg_it) + { + mProcess.addArgument(*arg_it); + } + + // Run the editor. + llinfos << "Running editor command [" << mProcess.getExecutable() + " " + args << "]" << llendl; + int result = mProcess.launch(); + if (result == 0) + { + // Prevent killing the process in destructor (will add it to the zombies list). + mProcess.orphan(); + } + + return result == 0 ? EC_SUCCESS : EC_FAILED_TO_RUN; +} + +// static +std::string LLExternalEditor::getErrorMessage(EErrorCode code) +{ + switch (code) + { + case EC_SUCCESS: return LLTrans::getString("ok"); + case EC_NOT_SPECIFIED: return LLTrans::getString("ExternalEditorNotSet"); + case EC_PARSE_ERROR: return LLTrans::getString("ExternalEditorCommandParseError"); + case EC_BINARY_NOT_FOUND: return LLTrans::getString("ExternalEditorNotFound"); + case EC_FAILED_TO_RUN: return LLTrans::getString("ExternalEditorFailedToRun"); + } + + return LLTrans::getString("Unknown"); +} + +// static +size_t LLExternalEditor::tokenize(string_vec_t& tokens, const std::string& str) +{ + tokens.clear(); + + // Split the argument string into separate strings for each argument + typedef boost::tokenizer< boost::char_separator > tokenizer; + boost::char_separator sep("", "\" ", boost::drop_empty_tokens); + + tokenizer tokens_list(str, sep); + tokenizer::iterator token_iter; + BOOL inside_quotes = FALSE; + BOOL last_was_space = FALSE; + for (token_iter = tokens_list.begin(); token_iter != tokens_list.end(); ++token_iter) + { + if (!strncmp("\"",(*token_iter).c_str(),2)) + { + inside_quotes = !inside_quotes; + } + else if (!strncmp(" ",(*token_iter).c_str(),2)) + { + if(inside_quotes) + { + tokens.back().append(std::string(" ")); + last_was_space = TRUE; + } + } + else + { + std::string to_push = *token_iter; + if (last_was_space) + { + tokens.back().append(to_push); + last_was_space = FALSE; + } + else + { + tokens.push_back(to_push); + } + } + } + + return tokens.size(); +} + +// static +std::string LLExternalEditor::findCommand( + const std::string& env_var, + const std::string& override) +{ + std::string cmd; + + // Get executable path. + if (!override.empty()) // try the supplied override first + { + cmd = override; + llinfos << "Using override" << llendl; + } + else if (!gSavedSettings.getString(sSetting).empty()) + { + cmd = gSavedSettings.getString(sSetting); + llinfos << "Using setting" << llendl; + } + else // otherwise use the path specified by the environment variable + { + char* env_var_val = getenv(env_var.c_str()); + if (env_var_val) + { + cmd = env_var_val; + llinfos << "Using env var " << env_var << llendl; + } + } + + llinfos << "Found command [" << cmd << "]" << llendl; + return cmd; +} diff --git a/indra/newview/llexternaleditor.h b/indra/newview/llexternaleditor.h new file mode 100644 index 000000000..ef5db56c6 --- /dev/null +++ b/indra/newview/llexternaleditor.h @@ -0,0 +1,105 @@ +/** + * @file llexternaleditor.h + * @brief A convenient class to run external editor. + * + * $LicenseInfo:firstyear=2010&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$ + */ + +#ifndef LL_LLEXTERNALEDITOR_H +#define LL_LLEXTERNALEDITOR_H + +#include + +/** + * Usage: + * LLExternalEditor ed; + * ed.setCommand("MY_EXTERNAL_EDITOR_VAR"); + * ed.run("/path/to/file1"); + * ed.run("/other/path/to/file2"); + */ +class LLExternalEditor +{ + typedef std::vector string_vec_t; + +public: + + typedef enum e_error_code { + EC_SUCCESS, /// No error. + EC_NOT_SPECIFIED, /// Editor path not specified. + EC_PARSE_ERROR, /// Editor command parsing error. + EC_BINARY_NOT_FOUND, /// Could find the editor binary (missing or not quoted). + EC_FAILED_TO_RUN, /// Could not execute the editor binary. + } EErrorCode; + + /** + * Set editor command. + * + * @param env_var Environment variable of the same purpose. + * @param override Optional override. + * + * First tries the override, then a predefined setting (sSetting), + * then the environment variable. + * + * @return EC_SUCCESS if command is valid and refers to an existing executable, + * EC_NOT_SPECIFIED or EC_FAILED_TO_RUNan on error. + * + * @see sSetting + */ + EErrorCode setCommand(const std::string& env_var, const std::string& override = LLStringUtil::null); + + /** + * Run the editor with the given file. + * + * @param file_path File to edit. + * @return EC_SUCCESS on success, error code on error. + */ + EErrorCode run(const std::string& file_path); + + /** + * Get a meaningful error message for the given status code. + */ + static std::string getErrorMessage(EErrorCode code); + +private: + + static std::string findCommand( + const std::string& env_var, + const std::string& override); + + static size_t tokenize(string_vec_t& tokens, const std::string& str); + + /** + * Filename placeholder that gets replaced with an actual file name. + */ + static const std::string sFilenameMarker; + + /** + * Setting that can specify the editor command. + */ + static const std::string sSetting; + + + std::string mArgs; + LLProcessLauncher mProcess; +}; + +#endif // LL_LLEXTERNALEDITOR_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 446a96979..6682a6e8c 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -45,6 +45,8 @@ #include "llkeyboard.h" #include "lllineeditor.h" +#include "lllivefile.h" +#include "llexternaleditor.h" #include "llnotificationsutil.h" #include "llresmgr.h" #include "llscrollbar.h" @@ -147,6 +149,50 @@ static bool have_script_upload_cap(LLUUID& object_id) return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty()); } +/// --------------------------------------------------------------------------- +/// LLLiveLSLFile +/// --------------------------------------------------------------------------- +class LLLiveLSLFile : public LLLiveFile +{ +public: + typedef boost::function change_callback_t; + + LLLiveLSLFile(std::string file_path, change_callback_t change_cb); + ~LLLiveLSLFile(); + + void ignoreNextUpdate() { mIgnoreNextUpdate = true; } + +protected: + /*virtual*/ bool loadFile(); + + change_callback_t mOnChangeCallback; + bool mIgnoreNextUpdate; +}; + +LLLiveLSLFile::LLLiveLSLFile(std::string file_path, change_callback_t change_cb) + : mOnChangeCallback(change_cb) + , mIgnoreNextUpdate(false) + , LLLiveFile(file_path, 1.0) +{ + llassert(mOnChangeCallback); +} + +LLLiveLSLFile::~LLLiveLSLFile() +{ + LLFile::remove(filename()); +} + +bool LLLiveLSLFile::loadFile() +{ + if (mIgnoreNextUpdate) + { + mIgnoreNextUpdate = false; + return true; + } + + return mOnChangeCallback(filename()); +} + /// --------------------------------------------------------------------------- /// LLScriptEdCore /// --------------------------------------------------------------------------- @@ -169,6 +215,8 @@ LLScriptEdCore::LLScriptEdCore( void (*save_callback)(void*, BOOL), void (*search_replace_callback) (void* userdata), void* userdata, + LLUUID objectUUID, + LLUUID itemUUID, S32 bottom_pad) : LLPanel( std::string("name"), rect ), @@ -183,8 +231,11 @@ LLScriptEdCore::LLScriptEdCore( mLastHelpToken(NULL), mLiveHelpHistorySize(0), mEnableSave(FALSE), + mLiveFile(NULL), mHasScriptData(FALSE), - LLEventTimer(60) + LLEventTimer(60), + mObjectUUID(objectUUID), + mItemUUID(itemUUID) { setFollowsAll(); setBorderVisible(FALSE); @@ -275,6 +326,7 @@ LLScriptEdCore::LLScriptEdCore( childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); childSetAction("Save_btn", onBtnSave,this); + childSetAction("Edit_btn", openInExternalEditor, this); initMenu(); @@ -290,6 +342,7 @@ LLScriptEdCore::LLScriptEdCore( LLScriptEdCore::~LLScriptEdCore() { deleteBridges(); + delete mLiveFile; } BOOL LLScriptEdCore::tick() @@ -359,6 +412,106 @@ void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid) } } +bool LLScriptEdCore::loadScriptText(const std::string& filename) +{ + if (filename.empty()) + { + llwarns << "Empty file name" << llendl; + return false; + } + + LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ + if (!file) + { + llwarns << "Error opening " << filename << llendl; + return false; + } + + // read in the whole file + fseek(file, 0L, SEEK_END); + size_t file_length = (size_t) ftell(file); + fseek(file, 0L, SEEK_SET); + char* buffer = new char[file_length+1]; + size_t nread = fread(buffer, 1, file_length, file); + if (nread < file_length) + { + llwarns << "Short read" << llendl; + } + buffer[nread] = '\0'; + fclose(file); + + mEditor->setText(LLStringExplicit(buffer)); + delete[] buffer; + + return true; +} + +bool LLScriptEdCore::writeToFile(const std::string& filename) +{ + 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"; + mErrorList->addElement(row); + return false; + } + + std::string utf8text = mEditor->getText(); + + // Special case for a completely empty script - stuff in one space so it can store properly. See SL-46889 + if (utf8text.size() == 0) + { + utf8text = " "; + } + + fputs(utf8text.c_str(), fp); + fclose(fp); + return true; +} + +void LLScriptEdCore::sync() +{ + // Sync with external editor. + std::string tmp_file = getTmpFileName(); + llstat s; + if (LLFile::stat(tmp_file, &s) == 0) // file exists + { + if (mLiveFile) mLiveFile->ignoreNextUpdate(); + writeToFile(tmp_file); + } +} + +std::string LLScriptEdCore::getTmpFileName() +{ + // Take script inventory item id (within the object inventory) + // to consideration so that it's possible to edit multiple scripts + // in the same object inventory simultaneously (STORM-781). + std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); + + // Use MD5 sum to make the file name shorter and not exceed maximum path length. + char script_id_hash_str[33]; /* Flawfinder: ignore */ + LLMD5 script_id_hash((const U8 *)script_id.c_str()); + script_id_hash.hex_digest(script_id_hash_str); + + return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl"; +} + +bool LLScriptEdCore::onExternalChange(const std::string& filename) +{ + if (!loadScriptText(filename)) + { + return false; + } + + // Avoid recursive save/compile loop + doSave(this, false, false); + return true; +} + BOOL LLScriptEdCore::hasChanged(void* userdata) { LLScriptEdCore* self = (LLScriptEdCore*)userdata; @@ -752,7 +905,7 @@ void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata) } // static -void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save ) +void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save, BOOL sync_external_editor) { LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT ); @@ -762,6 +915,56 @@ void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save ) { self->mSaveCallback( self->mUserdata, close_after_save ); } + if ( sync_external_editor ) + { + self->sync(); + } +} + +void LLScriptEdCore::openInExternalEditor(void *userdata) +{ + LLScriptEdCore* self = (LLScriptEdCore*) userdata; + + delete self->mLiveFile; // deletes file + + // Save the script to a temporary file. + std::string filename = self->getTmpFileName(); + self->writeToFile(filename); + + // Start watching file changes. + self->mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdCore::onExternalChange, self, _1)); + self->mLiveFile->ignoreNextUpdate(); + self->mLiveFile->addToEventTimer(); + + // Open it in external editor. + { + LLExternalEditor ed; + LLExternalEditor::EErrorCode status; + std::string msg; + + status = ed.setCommand("LL_SCRIPT_EDITOR"); + if (status != LLExternalEditor::EC_SUCCESS) + { + if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error. + { + msg = "External editor not set"; + } + else + { + msg = LLExternalEditor::getErrorMessage(status); + } + + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg)); + return; + } + + status = ed.run(filename); + if (status != LLExternalEditor::EC_SUCCESS) + { + msg = LLExternalEditor::getErrorMessage(status); + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg)); + } + } } // static @@ -1038,6 +1241,8 @@ void* LLPreviewLSL::createScriptEdPanel(void* userdata) LLPreviewLSL::onSave, LLPreviewLSL::onSearchReplace, self, + self->mObjectID, + self->mItemUUID, 0); return self->mScriptEd; @@ -1602,6 +1807,8 @@ void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) &LLLiveLSLEditor::onSave, &LLLiveLSLEditor::onSearchReplace, self, + self->mObjectID, + self->mItemUUID, 0); return self->mScriptEd; diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 436d68d4f..4a42332db 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -42,7 +42,7 @@ #include "llframetimer.h" #include "lleventtimer.h" - +class LLLiveLSLFile; class LLMessageSystem; class LLTextEditor; class LLButton; @@ -72,6 +72,8 @@ public: void (*save_callback)(void* userdata, BOOL close_after_save), void (*search_replace_callback)(void* userdata), void* userdata, + LLUUID objectUUID, + LLUUID itemUUID, S32 bottom_pad = 0); // pad below bottom row of buttons ~LLScriptEdCore(); @@ -82,6 +84,12 @@ public: BOOL canClose(); void setScriptText(const std::string& text, BOOL is_valid); + bool loadScriptText(const std::string& filename); + bool writeToFile(const std::string& filename); + void sync(); + std::string getTmpFileName(); + static void openInExternalEditor(void* userdata); + bool onExternalChange(const std::string& filename); bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response); bool handleReloadFromServerDialog(const LLSD& notification, const LLSD& response); @@ -95,7 +103,7 @@ public: static void onClickForward(void* userdata); static void onBtnInsertSample(void*); static void onBtnInsertFunction(LLUICtrl*, void*); - static void doSave( void* userdata, BOOL close_after_save ); + static void doSave( void* userdata, BOOL close_after_save, BOOL sync_external_editor = TRUE ); static void onBtnSave(void*); static void onBtnUndoChanges(void*); static void onSearchMenu(void* userdata); @@ -158,6 +166,9 @@ private: S32 mLiveHelpHistorySize; BOOL mEnableSave; BOOL mHasScriptData; + LLLiveLSLFile* mLiveFile; + LLUUID mObjectUUID; + LLUUID mItemUUID; }; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b5e02fe8f..618aeaf37 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2766,6 +2766,11 @@ bool idle_startup() LL_DEBUGS("AppInit") << "Initialization complete" << LL_ENDL; gRenderStartTime.reset(); + // We're not allowed to call reset() when paused, and we might or might not be paused depending on + // whether or not the main window lost focus before we get here (see LLViewerWindow::handleFocusLost). + // The simplest, legal way to make sure we're unpaused is to just pause/unpause here. + gForegroundTime.pause(); + gForegroundTime.unpause(); gForegroundTime.reset(); if (gSavedSettings.getBOOL("FetchInventoryOnLogin") diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 06f72c774..9bd5ebf94 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -402,7 +402,7 @@ void LLTexLayerSetBuffer::readBackAndUpload() BOOL valid = FALSE; LLPointer integrity_test = new LLImageJ2C; S32 file_size = 0; - U8* data = LLVFile::readFile(gVFS, asset_id, LLAssetType::AT_TEXTURE, &file_size); + U8* data = LLVFile::readFile(gVFS, LLImageBase::getPrivatePool(), asset_id, LLAssetType::AT_TEXTURE, &file_size); if (data) { valid = integrity_test->validate(data, file_size); // integrity_test will delete 'data' diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 5fcd17768..96b1f0b6c 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -885,7 +885,7 @@ BOOL LLTextureCache::isInLocal(const LLUUID& id) //static const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB -F32 LLTextureCache::sHeaderCacheVersion = 1.4f; +F32 LLTextureCache::sHeaderCacheVersion = 1.5f; U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit const char* entries_filename = "texture.entries"; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 854e12414..aa807c282 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3258,6 +3258,29 @@ void LLVOAvatar::getClientInfo(std::string& client, LLColor4& color, BOOL useCom return; std::string uuid_str = getTE(TEX_HEAD_BODYPAINT)->getID().asString(); //UUID of the head texture + if(isFullyLoaded()) + { + //Zwagoth's new client identification - HgB + // Overwrite the current tag/color settings if new method + // exists -- charbl. + const LLTextureEntry* texentry = getTE(0); + if(texentry->getGlow() > 0.0) + { + ///llinfos << "Using new client identifier." << llendl; + U8 tag_buffer[UUID_BYTES+1]; + memset(&tag_buffer, 0, UUID_BYTES); + memcpy(&tag_buffer[0], &texentry->getID().mData, UUID_BYTES); + tag_buffer[UUID_BYTES] = 0; + U32 tag_len = strlen((const char*)&tag_buffer[0]); + tag_len = (tag_len>UUID_BYTES) ? (UUID_BYTES) : tag_len; + client = std::string((char*)&tag_buffer[0], tag_len); + LLStringFn::replace_ascii_controlchars(mClientTag, LL_UNKNOWN_CHAR); + mNameString.clear(); + color = texentry->getColor(); + return; + } + } + static const LLCachedControl avatar_name_color(gColors,"AvatarNameColor",LLColor4(LLColor4U(251, 175, 93, 255)) ); if (isSelf()) { @@ -3553,38 +3576,15 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) static const LLCachedControl avatar_name_color(gColors, "AvatarNameColor" ); + //As pointed out by Zwagoth, we really shouldn't be doing this per-frame. Skip if we already have the data. -HgB if (mClientTag == "") { mClientColor = avatar_name_color; - if(isFullyLoaded()) + getClientInfo(mClientTag,mClientColor); + if(mClientTag == "") { - //Zwagoth's new client identification - HgB - // Overwrite the current tag/color settings if new method - // exists -- charbl. - const LLTextureEntry* texentry = getTE(0); - if(texentry->getGlow() > 0.0) - { - llinfos << "Using new client identifier." << llendl; - U8 tag_buffer[UUID_BYTES+1]; - memset(&tag_buffer, 0, UUID_BYTES); - memcpy(&tag_buffer[0], &texentry->getID().mData, UUID_BYTES); - tag_buffer[UUID_BYTES] = 0; - U32 tag_len = strlen((const char*)&tag_buffer[0]); - tag_len = (tag_len>UUID_BYTES) ? (UUID_BYTES) : tag_len; - mClientTag = std::string((char*)&tag_buffer[0], tag_len); - LLStringFn::replace_ascii_controlchars(mClientTag, LL_UNKNOWN_CHAR); - mNameString.clear(); - mClientColor = texentry->getColor(); - } - else - { - //llinfos << "Using Emerald-style client identifier." << llendl; - //The old client identification. Used only if the new method doesn't exist, so that it isn't automatically overwritten. -HgB - getClientInfo(mClientTag,mClientColor); - if(mClientTag == "") - client = "?"; //prevent console spam.. - } + client = "?"; //prevent console spam.. } // Overwrite the tag/color shit yet again if we want to see diff --git a/indra/newview/skins/default/xui/en-us/floater_chat_history.xml b/indra/newview/skins/default/xui/en-us/floater_chat_history.xml index cb93cd143..3d685ebee 100644 --- a/indra/newview/skins/default/xui/en-us/floater_chat_history.xml +++ b/indra/newview/skins/default/xui/en-us/floater_chat_history.xml @@ -69,38 +69,38 @@ orientation="horizontal" width="430" name="panels"> - Gestures - - + name="translate chat" radio_style="false" width="100" /--> -