SL-10782 External edit button for Notecards
parent
65bf63c973
commit
584a05797d
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "llagent.h"
|
||||
#include "lldraghandle.h"
|
||||
#include "llexternaleditor.h"
|
||||
#include "llviewerwindow.h"
|
||||
#include "llbutton.h"
|
||||
#include "llfloaterreg.h"
|
||||
|
|
@ -63,7 +64,8 @@
|
|||
|
||||
// Default constructor
|
||||
LLPreviewNotecard::LLPreviewNotecard(const LLSD& key) //const LLUUID& item_id,
|
||||
: LLPreview( key )
|
||||
: LLPreview( key ),
|
||||
mLiveFile(NULL)
|
||||
{
|
||||
const LLInventoryItem *item = getItem();
|
||||
if (item)
|
||||
|
|
@ -74,13 +76,14 @@ LLPreviewNotecard::LLPreviewNotecard(const LLSD& key) //const LLUUID& item_id,
|
|||
|
||||
LLPreviewNotecard::~LLPreviewNotecard()
|
||||
{
|
||||
delete mLiveFile;
|
||||
}
|
||||
|
||||
BOOL LLPreviewNotecard::postBuild()
|
||||
{
|
||||
LLViewerTextEditor *ed = getChild<LLViewerTextEditor>("Notecard Editor");
|
||||
ed->setNotecardInfo(mItemUUID, mObjectID, getKey());
|
||||
ed->makePristine();
|
||||
mEditor = getChild<LLViewerTextEditor>("Notecard Editor");
|
||||
mEditor->setNotecardInfo(mItemUUID, mObjectID, getKey());
|
||||
mEditor->makePristine();
|
||||
|
||||
childSetAction("Save", onClickSave, this);
|
||||
getChildView("lock")->setVisible( FALSE);
|
||||
|
|
@ -88,6 +91,8 @@ BOOL LLPreviewNotecard::postBuild()
|
|||
childSetAction("Delete", onClickDelete, this);
|
||||
getChildView("Delete")->setEnabled(false);
|
||||
|
||||
childSetAction("Edit", onClickEdit, this);
|
||||
|
||||
const LLInventoryItem* item = getItem();
|
||||
|
||||
childSetCommitCallback("desc", LLPreview::onText, this);
|
||||
|
|
@ -408,6 +413,16 @@ void LLPreviewNotecard::onClickDelete(void* user_data)
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void LLPreviewNotecard::onClickEdit(void* user_data)
|
||||
{
|
||||
LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data;
|
||||
if (preview)
|
||||
{
|
||||
preview->openInExternalEditor();
|
||||
}
|
||||
}
|
||||
|
||||
struct LLSaveNotecardInfo
|
||||
{
|
||||
LLPreviewNotecard* mSelf;
|
||||
|
|
@ -468,7 +483,7 @@ void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUI
|
|||
}
|
||||
}
|
||||
|
||||
bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
|
||||
bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync)
|
||||
{
|
||||
LLViewerTextEditor* editor = getChild<LLViewerTextEditor>("Notecard Editor");
|
||||
|
||||
|
|
@ -487,7 +502,10 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
|
|||
}
|
||||
|
||||
editor->makePristine();
|
||||
|
||||
if (sync)
|
||||
{
|
||||
syncExternal();
|
||||
}
|
||||
const LLInventoryItem* item = getItem();
|
||||
// save it out to database
|
||||
if (item)
|
||||
|
|
@ -566,6 +584,18 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLPreviewNotecard::syncExternal()
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void LLPreviewNotecard::deleteNotecard()
|
||||
{
|
||||
LLNotificationsUtil::add("DeleteNotecard", LLSD(), LLSD(), boost::bind(&LLPreviewNotecard::handleConfirmDeleteDialog,this, _1, _2));
|
||||
|
|
@ -714,4 +744,128 @@ bool LLPreviewNotecard::handleConfirmDeleteDialog(const LLSD& notification, cons
|
|||
return false;
|
||||
}
|
||||
|
||||
void LLPreviewNotecard::openInExternalEditor()
|
||||
{
|
||||
delete mLiveFile; // deletes file
|
||||
|
||||
// Save the notecard to a temporary file.
|
||||
std::string filename = getTmpFileName();
|
||||
writeToFile(filename);
|
||||
|
||||
// Start watching file changes.
|
||||
mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLPreviewNotecard::onExternalChange, this, _1));
|
||||
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 = getString("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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LLPreviewNotecard::onExternalChange(const std::string& filename)
|
||||
{
|
||||
if (!loadNotecardText(filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable sync to avoid recursive load->save->load calls.
|
||||
saveIfNeeded(NULL, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLPreviewNotecard::loadNotecardText(const std::string& filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
{
|
||||
LL_WARNS() << "Empty file name" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
|
||||
if (!file)
|
||||
{
|
||||
LL_WARNS() << "Error opening " << filename << LL_ENDL;
|
||||
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)
|
||||
{
|
||||
LL_WARNS() << "Short read" << LL_ENDL;
|
||||
}
|
||||
buffer[nread] = '\0';
|
||||
fclose(file);
|
||||
|
||||
mEditor->setText(LLStringExplicit(buffer));
|
||||
delete[] buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLPreviewNotecard::writeToFile(const std::string& filename)
|
||||
{
|
||||
LLFILE* fp = LLFile::fopen(filename, "wb");
|
||||
if (!fp)
|
||||
{
|
||||
LL_WARNS() << "Unable to write to " << filename << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string utf8text = mEditor->getText();
|
||||
|
||||
if (utf8text.size() == 0)
|
||||
{
|
||||
utf8text = " ";
|
||||
}
|
||||
|
||||
fputs(utf8text.c_str(), fp);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string LLPreviewNotecard::getTmpFileName()
|
||||
{
|
||||
std::string notecard_id = mObjectID.asString() + "_" + mItemUUID.asString();
|
||||
|
||||
// Use MD5 sum to make the file name shorter and not exceed maximum path length.
|
||||
char notecard_id_hash_str[33]; /* Flawfinder: ignore */
|
||||
LLMD5 notecard_id_hash((const U8 *)notecard_id.c_str());
|
||||
notecard_id_hash.hex_digest(notecard_id_hash_str);
|
||||
|
||||
return std::string(LLFile::tmpdir()) + "sl_notecard_" + notecard_id_hash_str + ".txt";
|
||||
}
|
||||
|
||||
|
||||
// EOF
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "llpreview.h"
|
||||
#include "llassetstorage.h"
|
||||
#include "llpreviewscript.h"
|
||||
#include "lliconctrl.h"
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -72,11 +73,13 @@ public:
|
|||
// asset system. :(
|
||||
void refreshFromInventory(const LLUUID& item_id = LLUUID::null);
|
||||
|
||||
void syncExternal();
|
||||
|
||||
protected:
|
||||
|
||||
void updateTitleButtons() override;
|
||||
void loadAsset() override;
|
||||
bool saveIfNeeded(LLInventoryItem* copyitem = NULL);
|
||||
bool saveIfNeeded(LLInventoryItem* copyitem = NULL, bool sync = true);
|
||||
|
||||
void deleteNotecard();
|
||||
|
||||
|
|
@ -89,6 +92,8 @@ protected:
|
|||
|
||||
static void onClickDelete(void* data);
|
||||
|
||||
static void onClickEdit(void* data);
|
||||
|
||||
static void onSaveComplete(const LLUUID& asset_uuid,
|
||||
void* user_data,
|
||||
S32 status, LLExtStat ext_status);
|
||||
|
|
@ -99,6 +104,12 @@ protected:
|
|||
static void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId);
|
||||
static void finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUID taskId);
|
||||
|
||||
void openInExternalEditor();
|
||||
bool onExternalChange(const std::string& filename);
|
||||
bool loadNotecardText(const std::string& filename);
|
||||
bool writeToFile(const std::string& filename);
|
||||
std::string getTmpFileName();
|
||||
|
||||
protected:
|
||||
LLViewerTextEditor* mEditor;
|
||||
LLButton* mSaveBtn;
|
||||
|
|
@ -106,6 +117,8 @@ protected:
|
|||
LLUUID mAssetID;
|
||||
|
||||
LLUUID mObjectID;
|
||||
|
||||
LLLiveLSLFile* mLiveFile;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
#include "llinventorymodel.h"
|
||||
#include "llkeyboard.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "lllivefile.h"
|
||||
#include "llhelp.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llresmgr.h"
|
||||
|
|
@ -120,22 +119,6 @@ static bool have_script_upload_cap(LLUUID& object_id)
|
|||
/// ---------------------------------------------------------------------------
|
||||
/// 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)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "lliconctrl.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llfloatergotoline.h"
|
||||
#include "lllivefile.h"
|
||||
#include "llsyntaxid.h"
|
||||
|
||||
class LLLiveLSLFile;
|
||||
|
|
@ -53,6 +54,23 @@ class LLScriptEdContainer;
|
|||
class LLFloaterGotoLine;
|
||||
class LLFloaterExperienceProfile;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these.
|
||||
class LLScriptEdCore : public LLPanel
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
height="361"
|
||||
layout="topleft"
|
||||
min_height="243"
|
||||
min_width="234"
|
||||
min_width="330"
|
||||
name="preview notecard"
|
||||
help_topic="preview_notecard"
|
||||
title="NOTECARD:"
|
||||
|
|
@ -77,6 +77,16 @@
|
|||
word_wrap="true">
|
||||
Loading...
|
||||
</text_editor>
|
||||
<button
|
||||
follows="left|bottom"
|
||||
height="22"
|
||||
label="Edit..."
|
||||
label_selected="Edit"
|
||||
layout="topleft"
|
||||
left="4"
|
||||
name="Edit"
|
||||
top="332"
|
||||
width="100" />
|
||||
<button
|
||||
follows="right|bottom"
|
||||
height="22"
|
||||
|
|
|
|||
Loading…
Reference in New Issue