Merge branch 'master' of git://github.com/lkalif/SingularityViewer
This commit is contained in:
@@ -117,7 +117,7 @@ LLDir_Linux::LLDir_Linux()
|
|||||||
mOSUserAppDir = "";
|
mOSUserAppDir = "";
|
||||||
mLindenUserDir = "";
|
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,
|
// *NOTE: /proc/%d/exe doesn't work on FreeBSD. But that's ok,
|
||||||
// because this is the linux implementation.
|
// because this is the linux implementation.
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ set(viewer_SOURCE_FILES
|
|||||||
lleventinfo.cpp
|
lleventinfo.cpp
|
||||||
lleventnotifier.cpp
|
lleventnotifier.cpp
|
||||||
lleventpoll.cpp
|
lleventpoll.cpp
|
||||||
|
llexternaleditor.cpp
|
||||||
llface.cpp
|
llface.cpp
|
||||||
llfasttimerview.cpp
|
llfasttimerview.cpp
|
||||||
llfeaturemanager.cpp
|
llfeaturemanager.cpp
|
||||||
@@ -643,6 +644,7 @@ set(viewer_HEADER_FILES
|
|||||||
lleventinfo.h
|
lleventinfo.h
|
||||||
lleventnotifier.h
|
lleventnotifier.h
|
||||||
lleventpoll.h
|
lleventpoll.h
|
||||||
|
llexternaleditor.h
|
||||||
llface.h
|
llface.h
|
||||||
llfasttimerview.h
|
llfasttimerview.h
|
||||||
llfeaturemanager.h
|
llfeaturemanager.h
|
||||||
|
|||||||
@@ -14261,6 +14261,17 @@
|
|||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<real>150000.0</real>
|
<real>150000.0</real>
|
||||||
</map>
|
</map>
|
||||||
|
<key>ExternalEditor</key>
|
||||||
|
<map>
|
||||||
|
<key>Comment</key>
|
||||||
|
<string>Path to program used to edit LSL scripts and XUI files, e.g.: /usr/bin/gedit --new-window "%s"</string>
|
||||||
|
<key>Persist</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>String</string>
|
||||||
|
<key>Value</key>
|
||||||
|
<string />
|
||||||
|
</map>
|
||||||
<key>YawFromMousePosition</key>
|
<key>YawFromMousePosition</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|||||||
208
indra/newview/llexternaleditor.cpp
Normal file
208
indra/newview/llexternaleditor.cpp
Normal file
@@ -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<char> > tokenizer;
|
||||||
|
boost::char_separator<char> 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;
|
||||||
|
}
|
||||||
105
indra/newview/llexternaleditor.h
Normal file
105
indra/newview/llexternaleditor.h
Normal file
@@ -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 <llprocesslauncher.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<std::string> 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
|
||||||
@@ -45,6 +45,8 @@
|
|||||||
#include "llkeyboard.h"
|
#include "llkeyboard.h"
|
||||||
#include "lllineeditor.h"
|
#include "lllineeditor.h"
|
||||||
|
|
||||||
|
#include "lllivefile.h"
|
||||||
|
#include "llexternaleditor.h"
|
||||||
#include "llnotificationsutil.h"
|
#include "llnotificationsutil.h"
|
||||||
#include "llresmgr.h"
|
#include "llresmgr.h"
|
||||||
#include "llscrollbar.h"
|
#include "llscrollbar.h"
|
||||||
@@ -147,6 +149,50 @@ static bool have_script_upload_cap(LLUUID& object_id)
|
|||||||
return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());
|
return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ---------------------------------------------------------------------------
|
||||||
|
/// LLLiveLSLFile
|
||||||
|
/// ---------------------------------------------------------------------------
|
||||||
|
class LLLiveLSLFile : public LLLiveFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::function<bool (const std::string& filename)> 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
|
/// LLScriptEdCore
|
||||||
/// ---------------------------------------------------------------------------
|
/// ---------------------------------------------------------------------------
|
||||||
@@ -169,6 +215,8 @@ LLScriptEdCore::LLScriptEdCore(
|
|||||||
void (*save_callback)(void*, BOOL),
|
void (*save_callback)(void*, BOOL),
|
||||||
void (*search_replace_callback) (void* userdata),
|
void (*search_replace_callback) (void* userdata),
|
||||||
void* userdata,
|
void* userdata,
|
||||||
|
LLUUID objectUUID,
|
||||||
|
LLUUID itemUUID,
|
||||||
S32 bottom_pad)
|
S32 bottom_pad)
|
||||||
:
|
:
|
||||||
LLPanel( std::string("name"), rect ),
|
LLPanel( std::string("name"), rect ),
|
||||||
@@ -183,8 +231,11 @@ LLScriptEdCore::LLScriptEdCore(
|
|||||||
mLastHelpToken(NULL),
|
mLastHelpToken(NULL),
|
||||||
mLiveHelpHistorySize(0),
|
mLiveHelpHistorySize(0),
|
||||||
mEnableSave(FALSE),
|
mEnableSave(FALSE),
|
||||||
|
mLiveFile(NULL),
|
||||||
mHasScriptData(FALSE),
|
mHasScriptData(FALSE),
|
||||||
LLEventTimer(60)
|
LLEventTimer(60),
|
||||||
|
mObjectUUID(objectUUID),
|
||||||
|
mItemUUID(itemUUID)
|
||||||
{
|
{
|
||||||
setFollowsAll();
|
setFollowsAll();
|
||||||
setBorderVisible(FALSE);
|
setBorderVisible(FALSE);
|
||||||
@@ -275,6 +326,7 @@ LLScriptEdCore::LLScriptEdCore(
|
|||||||
|
|
||||||
childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
|
childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
|
||||||
childSetAction("Save_btn", onBtnSave,this);
|
childSetAction("Save_btn", onBtnSave,this);
|
||||||
|
childSetAction("Edit_btn", openInExternalEditor, this);
|
||||||
|
|
||||||
initMenu();
|
initMenu();
|
||||||
|
|
||||||
@@ -290,6 +342,7 @@ LLScriptEdCore::LLScriptEdCore(
|
|||||||
LLScriptEdCore::~LLScriptEdCore()
|
LLScriptEdCore::~LLScriptEdCore()
|
||||||
{
|
{
|
||||||
deleteBridges();
|
deleteBridges();
|
||||||
|
delete mLiveFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL LLScriptEdCore::tick()
|
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)
|
BOOL LLScriptEdCore::hasChanged(void* userdata)
|
||||||
{
|
{
|
||||||
LLScriptEdCore* self = (LLScriptEdCore*)userdata;
|
LLScriptEdCore* self = (LLScriptEdCore*)userdata;
|
||||||
@@ -752,7 +905,7 @@ void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// 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 );
|
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 );
|
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
|
// static
|
||||||
@@ -1038,6 +1241,8 @@ void* LLPreviewLSL::createScriptEdPanel(void* userdata)
|
|||||||
LLPreviewLSL::onSave,
|
LLPreviewLSL::onSave,
|
||||||
LLPreviewLSL::onSearchReplace,
|
LLPreviewLSL::onSearchReplace,
|
||||||
self,
|
self,
|
||||||
|
self->mObjectID,
|
||||||
|
self->mItemUUID,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
return self->mScriptEd;
|
return self->mScriptEd;
|
||||||
@@ -1602,6 +1807,8 @@ void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
|
|||||||
&LLLiveLSLEditor::onSave,
|
&LLLiveLSLEditor::onSave,
|
||||||
&LLLiveLSLEditor::onSearchReplace,
|
&LLLiveLSLEditor::onSearchReplace,
|
||||||
self,
|
self,
|
||||||
|
self->mObjectID,
|
||||||
|
self->mItemUUID,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
return self->mScriptEd;
|
return self->mScriptEd;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
#include "llframetimer.h"
|
#include "llframetimer.h"
|
||||||
#include "lleventtimer.h"
|
#include "lleventtimer.h"
|
||||||
|
|
||||||
|
class LLLiveLSLFile;
|
||||||
class LLMessageSystem;
|
class LLMessageSystem;
|
||||||
class LLTextEditor;
|
class LLTextEditor;
|
||||||
class LLButton;
|
class LLButton;
|
||||||
@@ -72,6 +72,8 @@ public:
|
|||||||
void (*save_callback)(void* userdata, BOOL close_after_save),
|
void (*save_callback)(void* userdata, BOOL close_after_save),
|
||||||
void (*search_replace_callback)(void* userdata),
|
void (*search_replace_callback)(void* userdata),
|
||||||
void* userdata,
|
void* userdata,
|
||||||
|
LLUUID objectUUID,
|
||||||
|
LLUUID itemUUID,
|
||||||
S32 bottom_pad = 0); // pad below bottom row of buttons
|
S32 bottom_pad = 0); // pad below bottom row of buttons
|
||||||
~LLScriptEdCore();
|
~LLScriptEdCore();
|
||||||
|
|
||||||
@@ -82,6 +84,12 @@ public:
|
|||||||
BOOL canClose();
|
BOOL canClose();
|
||||||
|
|
||||||
void setScriptText(const std::string& text, BOOL is_valid);
|
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 handleSaveChangesDialog(const LLSD& notification, const LLSD& response);
|
||||||
bool handleReloadFromServerDialog(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 onClickForward(void* userdata);
|
||||||
static void onBtnInsertSample(void*);
|
static void onBtnInsertSample(void*);
|
||||||
static void onBtnInsertFunction(LLUICtrl*, 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 onBtnSave(void*);
|
||||||
static void onBtnUndoChanges(void*);
|
static void onBtnUndoChanges(void*);
|
||||||
static void onSearchMenu(void* userdata);
|
static void onSearchMenu(void* userdata);
|
||||||
@@ -158,6 +166,9 @@ private:
|
|||||||
S32 mLiveHelpHistorySize;
|
S32 mLiveHelpHistorySize;
|
||||||
BOOL mEnableSave;
|
BOOL mEnableSave;
|
||||||
BOOL mHasScriptData;
|
BOOL mHasScriptData;
|
||||||
|
LLLiveLSLFile* mLiveFile;
|
||||||
|
LLUUID mObjectUUID;
|
||||||
|
LLUUID mItemUUID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@
|
|||||||
word_wrap="true" show_line_numbers="true">
|
word_wrap="true" show_line_numbers="true">
|
||||||
Loading...
|
Loading...
|
||||||
</text_editor>
|
</text_editor>
|
||||||
<button bottom="-499" enabled="true" follows="right|bottom" font="SansSerif"
|
<button bottom="-499" enabled="true" follows="right|bottom" font="SansSerif"
|
||||||
|
halign="center" height="20" label="Edit..." label_selected="Edit..." left="230"
|
||||||
|
mouse_opaque="true" name="Edit_btn" width="128" />
|
||||||
|
<button bottom="-499" enabled="true" follows="right|bottom" font="SansSerif"
|
||||||
halign="center" height="20" label="Save" label_selected="Save" left="360"
|
halign="center" height="20" label="Save" label_selected="Save" left="360"
|
||||||
mouse_opaque="true" name="Save_btn" width="128" />
|
mouse_opaque="true" name="Save_btn" width="128" />
|
||||||
<scroll_list background_visible="true" bottom="-457" column_padding="5" draw_border="true"
|
<scroll_list background_visible="true" bottom="-457" column_padding="5" draw_border="true"
|
||||||
|
|||||||
Reference in New Issue
Block a user