SL-10782 External edit button for Notecards

meow-7.2.2
maxim_productengine 2019-04-03 10:58:35 +03:00
parent 65bf63c973
commit 584a05797d
5 changed files with 203 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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