Merge branch 'master' of git://github.com/lkalif/SingularityViewer

This commit is contained in:
Lirusaito
2012-01-26 23:30:41 -05:00
8 changed files with 553 additions and 6 deletions

View File

@@ -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.

View File

@@ -165,6 +165,7 @@ set(viewer_SOURCE_FILES
lleventinfo.cpp
lleventnotifier.cpp
lleventpoll.cpp
llexternaleditor.cpp
llface.cpp
llfasttimerview.cpp
llfeaturemanager.cpp
@@ -643,6 +644,7 @@ set(viewer_HEADER_FILES
lleventinfo.h
lleventnotifier.h
lleventpoll.h
llexternaleditor.h
llface.h
llfasttimerview.h
llfeaturemanager.h

View File

@@ -14261,6 +14261,17 @@
<key>Value</key>
<real>150000.0</real>
</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>
<map>
<key>Comment</key>

View 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;
}

View 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

View File

@@ -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<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
/// ---------------------------------------------------------------------------
@@ -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;

View File

@@ -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;
};

View File

@@ -8,7 +8,10 @@
word_wrap="true" show_line_numbers="true">
Loading...
</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"
mouse_opaque="true" name="Save_btn" width="128" />
<scroll_list background_visible="true" bottom="-457" column_padding="5" draw_border="true"