Add url menus to the text editor!!

Hey there, Alpha User! This commit is riddled with complex words,
but here's what matters to you: This adds the ability to right click a
Name, URL, SLURL, or Object Name, or even an Email, and get some awesome
options just like v3 gets! Buuuut, unlike V3, if there was something else
you might want to do, we've still got you covered in the same menu!!

Also make the text editor's default menu an xml
Synced a lot from upstream
Unlike Upstream, actually combine the base menu with the url menu if we have a selection
Unlike Upstream and previously, don't create a new menu per text editor
Unlike Upstream, don't create a separate FriendSignal and ObjectBlockedSignal per text editor we need it in!!
Unlike Upstream, always show a Select All option
This commit is contained in:
Lirusaito
2019-01-20 09:40:56 -05:00
parent 61b1128d76
commit d8c95f844f
78 changed files with 1055 additions and 79 deletions

View File

@@ -65,6 +65,7 @@
#include "lltextparser.h"
#include "lldir.h"
#include <queue>
#include "llmemberlistener.h"
#include "llmenugl.h"
#include "../newview/lgghunspell_wrapper.h"
@@ -243,6 +244,8 @@ private:
///////////////////////////////////////////////////////////////////
LLTextEditor::is_friend_signal_t* LLTextEditor::mIsFriendSignal = nullptr;
LLTextEditor::is_blocked_signal_t* LLTextEditor::mIsObjectBlockedSignal = nullptr;
LLTextEditor::LLTextEditor(
const std::string& name,
@@ -345,32 +348,6 @@ LLTextEditor::LLTextEditor(
resetDirty(); // Update saved text state
// make the popup menu available
//LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", parent_view);
LLMenuGL* menu = new LLMenuGL("rclickmenu");
/*if (!menu)
{
menu = new LLMenuGL(LLStringUtil::null);
}*/
menu->addChild(new LLMenuItemCallGL("Cut", context_cut, NULL, this));
menu->addChild(new LLMenuItemCallGL("Copy", context_copy, NULL, this));
menu->addChild(new LLMenuItemCallGL("Copy Raw", [](void* data) {
if (LLTextEditor* line = static_cast<LLTextEditor*>(data)) line->copyRaw();
}, NULL, this));
menu->addChild(new LLMenuItemCallGL("Copy URL", [](void* data) {
if (std::string* str = static_cast<std::string*>(data))
{
auto url = utf8str_to_wstring(*str);
gClipboard.copyFromSubstring(url, 0, url.size());
}
}, NULL, this));
menu->addChild(new LLMenuItemCallGL("Paste", context_paste, NULL, this));
menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this));
menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this));
menu->addSeparator();
menu->setCanTearOff(FALSE);
menu->setVisible(FALSE);
mPopupMenuHandle = menu->getHandle();
setCommitCallback(boost::bind(&LLTextEditor::setControlValue, this, _2));
}
@@ -387,19 +364,76 @@ LLTextEditor::~LLTextEditor()
// Scrollbar is deleted by LLView
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
//LLView::deleteViewByHandle(mPopupMenuHandle);
mSegments.clear();
auto menu = mPopupMenuHandle.get();
if (menu)
{
menu->die();
mPopupMenuHandle.markDead();
}
/* Singu Note: Static this, we'll use it wherever we can!
delete mIsFriendSignal;
delete mIsObjectBlockedSignal;*/
}
void LLTextEditor::context_cut(void* data)
const std::string& LLTextEditor::getMenuSegmentUrl() const
{
LLTextEditor* line = (LLTextEditor*)data;
if(line)line->cut();
auto segment = getSegmentAtLocalPos(mLastContextMenuX, mLastContextMenuY);
auto style = segment->getStyle();
return style ? style->getLinkHREF() : LLStringUtil::null;
}
void LLTextEditor::context_copy(void* data)
static LLTextEditor* get_focused_text_editor()
{
LLTextEditor* line = (LLTextEditor*)data;
if(line)line->copy();
auto* list = dynamic_cast<LLTextEditor*>(gFocusMgr.getKeyboardFocus());
llassert(list); // This listener only applies to lists
return list;
}
class ContextText : public LLMemberListener<LLView>
{
bool handleEvent(LLPointer<LLOldEvents::LLEvent>, const LLSD& userdata) override
{
auto text = get_focused_text_editor();
const auto& op = userdata.asStringRef();
if (op == "Cut") text->cut();
else if (op == "Copy") text->copy();
else if (op == "CopyRaw") text->copyRaw();
else if (op == "Paste") text->paste();
else if (op == "Delete") text->doDelete();
else if (op == "SelectAll") text->selectAll();
return true;
}
};
class ContextUrl : public LLMemberListener<LLView>
{
static const std::string& get_focused_url()
{
return get_focused_text_editor()->getMenuSegmentUrl();
}
bool handleEvent(LLPointer<LLOldEvents::LLEvent>, const LLSD& userdata) override
{
const auto& url = get_focused_url();
const auto& op = userdata.asStringRef();
if (op == "Open") LLUrlAction::openURL(url);
else if (op == "OpenInternal") LLUrlAction::openURLInternal(url);
else if (op == "OpenExternal") LLUrlAction::openURLExternal(url);
else if (op == "Execute") LLUrlAction::executeSLURL(url, true);
else if (op == "Block") LLUrlAction::blockObject(url);
else if (op == "Unblock") LLUrlAction::unblockObject(url);
else if (op == "Teleport") LLUrlAction::teleportToLocation(url);
else if (op == "ShowProfile") LLUrlAction::showProfile(url);
else if (op == "AddFriend") LLUrlAction::addFriend(url);
else if (op == "RemoveFriend") LLUrlAction::removeFriend(url);
else if (op == "SendIM") LLUrlAction::sendIM(url);
else if (op == "ShowOnMap") LLUrlAction::showLocationOnMap(url);
else if (op == "CopyLabel") LLUrlAction::copyLabelToClipboard(url);
else if (op == "CopyUrl") LLUrlAction::copyURLToClipboard(url);
return true;
}
};
void LLTextEditor::spell_correct(void* data)
{
@@ -477,21 +511,11 @@ void LLTextEditor::spell_add(void* data)
}
}
void LLTextEditor::context_paste(void* data)
//static
void LLTextEditor::addMenuListeners()
{
LLTextEditor* line = (LLTextEditor*)data;
if(line)line->paste();
}
void LLTextEditor::context_delete(void* data)
{
LLTextEditor* line = (LLTextEditor*)data;
if(line)line->doDelete();
}
void LLTextEditor::context_selectall(void* data)
{
LLTextEditor* line = (LLTextEditor*)data;
if(line)line->selectAll();
(new ContextText)->registerListener(LLMenuGL::sMenuContainer, "Text");
(new ContextUrl)->registerListener(LLMenuGL::sMenuContainer, "Text.Url");
}
void LLTextEditor::setTrackColor( const LLColor4& color )
@@ -651,6 +675,56 @@ BOOL LLTextEditor::truncate()
return did_truncate;
}
LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
{
// work out the XUI menu file to use for this url
LLUrlMatch match;
std::string url = in_url;
if (!LLUrlRegistry::instance().findUrl(url, match))
{
return nullptr;
}
std::string xui_file = match.getMenuName();
if (xui_file.empty())
{
return nullptr;
}
// create and return the context menu from the XUI file
llassert(LLMenuGL::sMenuContainer != NULL);
auto menu = LLUICtrlFactory::getInstance()->buildMenu(xui_file, LLMenuGL::sMenuContainer);
if (menu)
{
if (mIsFriendSignal)
{
bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url)));
LLView* addFriendButton = menu->findChild<LLView>("add_friend");
LLView* removeFriendButton = menu->findChild<LLView>("remove_friend");
if (addFriendButton && removeFriendButton)
{
addFriendButton->setEnabled(!isFriend);
removeFriendButton->setEnabled(isFriend);
}
}
if (mIsObjectBlockedSignal)
{
bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url));
LLView* blockButton = menu->findChild<LLView>("block_object");
LLView* unblockButton = menu->findChild<LLView>("unblock_object");
if (blockButton && unblockButton)
{
blockButton->setVisible(!is_blocked);
unblockButton->setVisible(is_blocked);
}
}
}
return menu;
}
void LLTextEditor::setText(const LLStringExplicit &utf8str)
{
// clear out the existing text and segments
@@ -1382,6 +1456,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
}
BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
{
setFocus(TRUE);
//setCursorAtLocalPos( x, y, TRUE );
@@ -1392,43 +1467,56 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
if (menu)
{
for(int i = 0;i<(int)suggestionMenuItems.size();i++)
for (auto tempBind : suggestionMenuItems)
{
SpellMenuBind * tempBind = suggestionMenuItems[i];
if(tempBind)
if (tempBind)
{
menu->removeChild(tempBind->menuItem);
tempBind->menuItem->die();
//delete tempBind->menuItem;
//tempBind->menuItem = NULL;
delete tempBind;
}
}
suggestionMenuItems.clear();
menu->die();
}
auto segment = getSegmentAtLocalPos(x, y);
const LLStyleSP style = segment ? segment->getStyle() : nullptr;
auto submenu = (style && style->isLink()) ? createUrlContextMenu(x, y, style->getLinkHREF()) : nullptr;
// Add url menu to base menu if we have a selection, otherwise make it the menu.
menu = (submenu && !hasSelection()) ? submenu : LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", LLMenuGL::sMenuContainer);
mPopupMenuHandle = menu->getHandle();
if (menu)
{
if (submenu && submenu != menu)
{
submenu->removeChild(submenu->getChild<LLMenuItemCallGL>("Select All")); // There can be only one!
menu->appendMenu(submenu);
}
// spell_check="true" in xui
menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable);
if (!mReadOnly && mSpellCheckable)
{
bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen);
if (is_word_part)
{
const LLWString &text = mWText;
std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen));
std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart, wordLen));
if (!glggHunSpell->isSpelledRight(selectedWord))
{
//misspelled word here, and you have just right clicked on it!
std::vector<std::string> suggs = glggHunSpell->getSuggestionList(selectedWord);
for (int i = 0; i<(int)suggs.size(); i++)
menu->addSeparator();
for (auto word : suggs)
{
SpellMenuBind * tempStruct = new SpellMenuBind;
tempStruct->origin = this;
tempStruct->word = suggs[i];
tempStruct->word = word;
tempStruct->wordPositionEnd = wordStart + wordLen;
tempStruct->wordPositionStart=wordStart;
tempStruct->wordY=y;
tempStruct->wordPositionStart = wordStart;
tempStruct->wordY = y;
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
tempStruct->word, spell_correct, NULL, tempStruct);
tempStruct->menuItem = suggMenuItem;
@@ -1439,8 +1527,8 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
tempStruct->origin = this;
tempStruct->word = selectedWord;
tempStruct->wordPositionEnd = wordStart + wordLen;
tempStruct->wordPositionStart=wordStart;
tempStruct->wordY=y;
tempStruct->wordPositionStart = wordStart;
tempStruct->wordY = y;
LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL(
"Add Word", spell_add, NULL, tempStruct);
tempStruct->menuItem = suggMenuItem;
@@ -1467,17 +1555,6 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
menu->addChild(suggMenuItem);
}
{
const LLStyleSP style = mHoverSegment ? mHoverSegment->getStyle() : nullptr;
auto copy_url = menu->findChild<LLMenuItemCallGL>("Copy URL");
if (mReadOnly && mParseHTML && style && !style->getLinkHREF().empty())
{
copy_url->setVisible(true);
copy_url->setUserData((void*)&style->getLinkHREF()); // Yes, this can be invalidated, but they'll never click it then.
}
else copy_url->setVisible(false);
}
mLastContextMenuX = x;
mLastContextMenuY = y;
menu->buildDrawLabels();
@@ -4975,6 +5052,24 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer )
return TRUE;
}
boost::signals2::connection LLTextEditor::setIsFriendCallback(const is_friend_signal_t::slot_type& cb)
{
if (!mIsFriendSignal)
{
mIsFriendSignal = new is_friend_signal_t();
}
return mIsFriendSignal->connect(cb);
}
boost::signals2::connection LLTextEditor::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb)
{
if (!mIsObjectBlockedSignal)
{
mIsObjectBlockedSignal = new is_blocked_signal_t();
}
return mIsObjectBlockedSignal->connect(cb);
}
//////////////////////////////////////////////////////////////////////////
// LLTextSegment