From 269576805b5867b32fac727f40c5a2f798d9bb0e Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 16 Jan 2019 01:01:19 -0500 Subject: [PATCH] Replace URLs everywhere in text editors, like upstream. This works for notifications, and profiles, and chats, and even updates! Amazing, right? SinguReplaceLinks is on by default, URL highlighting works regardless, no rogue labels with this off. Select, right click, Copy Raw lets you copy the link with its hidden url shown and you can paste it back the same way if you're wanting to paste it all over! LLTextEditor overhaul synced the code with upstream LLTextBase as much as we could. appendStyledText is now just appendText Every setText appendText's now and adds a style based on the texteditor's color and font, this could slightly increase the weight of text editors (one extra segment) but it fixes a nasty bug with running past segmentation. Also no longer update the utf8 Removed append() which was no longer being used LLTextEditor and LLViewerTextEditor now have an extra boolean at the end of their constructors to have this replacement turned on at construction time (this also sets them read only) Update TextSegments to have a Tooltip string, like upstream. Hardened notecard previews so they don't accidentally replace the text when they go read only. Thanks to Deltek and Router Gray for helping me test and debug this commit! --- indra/llui/lltexteditor.cpp | 750 ++++++++++-------- indra/llui/lltexteditor.h | 37 +- .../newview/app_settings/settings_ascent.xml | 14 + indra/newview/llfloaterabout.cpp | 4 +- indra/newview/llfloaterchat.cpp | 31 +- indra/newview/llfloateroutbox.cpp | 2 +- indra/newview/llfloatervoiceeffect.cpp | 2 +- indra/newview/llgroupnotify.cpp | 13 +- indra/newview/llimpanel.cpp | 7 +- indra/newview/llnotify.cpp | 2 +- indra/newview/llpreviewnotecard.cpp | 5 +- indra/newview/llviewertexteditor.cpp | 7 +- indra/newview/llviewertexteditor.h | 3 +- 13 files changed, 468 insertions(+), 409 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 1db493c01..7a2b24e7b 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -43,6 +43,7 @@ #include "lluictrlfactory.h" #include "lluiimage.h" #include "llurlaction.h" +#include "llurlregistry.h" #include "llrect.h" #include "llfocusmgr.h" #include "lltimer.h" @@ -246,46 +247,47 @@ private: /////////////////////////////////////////////////////////////////// -LLTextEditor::LLTextEditor( - const std::string& name, - const LLRect& rect, +LLTextEditor::LLTextEditor( + const std::string& name, + const LLRect& rect, S32 max_length, // In bytes - const std::string &default_text, + const std::string &default_text, const LLFontGL* font, - BOOL allow_embedded_items) - : - LLUICtrl( name, rect, TRUE, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ), + BOOL allow_embedded_items, + bool parse_html) + : + LLUICtrl(name, rect, TRUE, NULL, FOLLOWS_TOP | FOLLOWS_LEFT), mTextIsUpToDate(TRUE), - mMaxTextByteLength( max_length ), + mMaxTextByteLength(max_length), mPopupMenuHandle(), mBaseDocIsPristine(TRUE), - mPristineCmd( NULL ), - mLastCmd( NULL ), - mCursorPos( 0 ), - mIsSelecting( FALSE ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mScrolledToBottom( TRUE ), - mOnScrollEndCallback( NULL ), - mOnScrollEndData( NULL ), - mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ), - mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ), - mDefaultColor( LLUI::sColorsGroup->getColor( "TextDefaultColor" ) ), - mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ), - mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ), - mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ), - mFocusBgColor( LLUI::sColorsGroup->getColor( "TextBgFocusColor" ) ), - mReadOnly(FALSE), - mWordWrap( FALSE ), - mShowLineNumbers ( FALSE ), - mTabsToNextField( TRUE ), - mCommitOnFocusLost( FALSE ), - mHideScrollbarForShortDocs( FALSE ), - mTakesNonScrollClicks( TRUE ), - mTrackBottom( FALSE ), - mAllowEmbeddedItems( allow_embedded_items ), + mPristineCmd(NULL), + mLastCmd(NULL), + mCursorPos(0), + mIsSelecting(FALSE), + mSelectionStart(0), + mSelectionEnd(0), + mScrolledToBottom(TRUE), + mOnScrollEndCallback(NULL), + mOnScrollEndData(NULL), + mCursorColor(LLUI::sColorsGroup->getColor("TextCursorColor")), + mFgColor(LLUI::sColorsGroup->getColor("TextFgColor")), + mDefaultColor(LLUI::sColorsGroup->getColor("TextDefaultColor")), + mReadOnlyFgColor(LLUI::sColorsGroup->getColor("TextFgReadOnlyColor")), + mWriteableBgColor(LLUI::sColorsGroup->getColor("TextBgWriteableColor")), + mReadOnlyBgColor(LLUI::sColorsGroup->getColor("TextBgReadOnlyColor")), + mFocusBgColor(LLUI::sColorsGroup->getColor("TextBgFocusColor")), + mReadOnly(parse_html), + mWordWrap(FALSE), + mShowLineNumbers(FALSE), + mTabsToNextField(TRUE), + mCommitOnFocusLost(FALSE), + mHideScrollbarForShortDocs(FALSE), + mTakesNonScrollClicks(TRUE), + mTrackBottom(FALSE), + mAllowEmbeddedItems(allow_embedded_items), mAcceptCallingCardNames(FALSE), - mHandleEditKeysDirectly( FALSE ), + mHandleEditKeysDirectly(FALSE), mMouseDownX(0), mMouseDownY(0), mLastSelectionX(-1), @@ -312,39 +314,39 @@ LLTextEditor::LLTextEditor( updateTextRect(); - S32 line_height = ll_round( mGLFont->getLineHeight() ); + S32 line_height = ll_round(mGLFont->getLineHeight()); S32 page_size = mTextRect.getHeight() / line_height; // Init the scrollbar LLRect scroll_rect; - scroll_rect.setOriginAndSize( + scroll_rect.setOriginAndSize( getRect().getWidth() - SCROLLBAR_SIZE, 1, SCROLLBAR_SIZE, getRect().getHeight() - 1); S32 lines_in_doc = getLineCount(); - mScrollbar = new LLScrollbar( std::string("Scrollbar"), scroll_rect, + mScrollbar = new LLScrollbar(std::string("Scrollbar"), scroll_rect, LLScrollbar::VERTICAL, - lines_in_doc, - 0, + lines_in_doc, + 0, page_size, NULL); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); - mScrollbar->setEnabled( TRUE ); - mScrollbar->setVisible( TRUE ); + mScrollbar->setEnabled(TRUE); + mScrollbar->setVisible(TRUE); mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData); addChild(mScrollbar); - mBorder = new LLViewBorder( std::string("text ed border"), LLRect(0, getRect().getHeight(), getRect().getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER ); - addChild( mBorder ); + mBorder = new LLViewBorder(std::string("text ed border"), LLRect(0, getRect().getHeight(), getRect().getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER); + addChild(mBorder); + mParseHTML = parse_html; appendText(default_text, FALSE, FALSE); - + resetDirty(); // Update saved text state - mParseHTML=FALSE; mHTML.clear(); // make the popup menu available //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", parent_view); @@ -355,6 +357,9 @@ LLTextEditor::LLTextEditor( }*/ 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(data)) line->copyRaw(); + }, 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)); @@ -639,38 +644,30 @@ BOOL LLTextEditor::truncate() void LLTextEditor::setText(const LLStringExplicit &utf8str) { - // LLStringUtil::removeCRLF(utf8str); - mUTF8Text = utf8str_removeCRLF(utf8str); - // mUTF8Text = utf8str; - mWText = utf8str_to_wstring(mUTF8Text); - mTextIsUpToDate = TRUE; + // clear out the existing text and segments + mWText.clear(); - truncate(); - blockUndo(); + clearSegments(); +// createDefaultSegment(); - setCursorPos(0); deselect(); - needsReflow(); + // append the new text (supports Url linking) + std::string text(utf8str); + //LLStringUtil::removeCRLF(text); + + // appendText modifies mCursorPos... + LLStyleSP style(new LLStyle(TRUE, mReadOnly ? mReadOnlyFgColor : mFgColor, LLFontGL::nameFromFont(mGLFont))); + appendText(utf8str, false, false, style); + // ...so move cursor to top after appending text + setCursorPos(0); resetDirty(); } -void LLTextEditor::setWText(const LLWString &wtext) +void LLTextEditor::setWText(const LLWString& text) { - mWText = wtext; - mUTF8Text.clear(); - mTextIsUpToDate = FALSE; - - truncate(); - blockUndo(); - - setCursorPos(0); - deselect(); - - needsReflow(); - - resetDirty(); + setText(wstring_to_utf8str(text)); } // virtual @@ -766,7 +763,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen } // If still -1, then search_text just isn't found. - if (-1 == loc) + if (-1 == loc) { mIsSelecting = FALSE; mSelectionEnd = 0; @@ -889,9 +886,9 @@ S32 LLTextEditor::getLineStart( S32 line ) const { S32 num_lines = getLineCount(); if (num_lines == 0) - { + { return 0; - } + } line = llclamp(line, 0, num_lines-1); S32 segidx = mLineStartList[line].mSegment; @@ -1446,11 +1443,11 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) if (glggHunSpell->getSpellCheckHighlight()) { tempStruct->word = "Hide Misspellings"; - } + } else - { + { tempStruct->word = "Show Misspellings"; - } + } LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( tempStruct->word, spell_show, NULL, tempStruct); @@ -1733,11 +1730,6 @@ S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_ return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) ); } -S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op) -{ - return insert(mWText.length(), wstr, group_with_next_op); -} - S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) { if ((S32)mWText.length() == pos) @@ -2170,7 +2162,7 @@ BOOL LLTextEditor::canCopy() const } // copy selection to clipboard -void LLTextEditor::copy() +void LLTextEditor::copy(bool raw) { if( !canCopy() ) { @@ -2178,7 +2170,55 @@ void LLTextEditor::copy() } S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); + // Does our selection include any Segments with links? + if (mParseHTML && raw) + { + auto begin = std::find_if(mSegments.begin(), mSegments.end(), [left_pos](const LLTextSegmentPtr& ptr) { + return ptr->getEnd() > left_pos; + }); + auto last = mSegments.end(); + if (begin == last || begin->isNull()) + { + gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); + return; + } + S32 right_pos = left_pos + length, offset = 0; + { + // If our selection starts in the middle of a link, set our left_pos to the beginning of its segment. + auto segment = **begin; + if (auto style = segment.getStyle()) + if (!style->getLinkHREF().empty()) + left_pos = llmin(segment.getStart(), left_pos); + } + auto text = mWText.substr(left_pos, length); + for (; begin->notNull() && begin != last && (*begin)->getStart() <= right_pos; ++begin) + { + auto segment = **begin; + //llassert(segment->getStyle()); // If someone is stores the result of the S32 constructor, they're in so much trouble!! + const auto& link = segment.getStyle()->getLinkHREF(); + if (!link.empty()) + { + const S32 label_length = (segment.getEnd() - segment.getStart()); + const S32 start = (segment.getStart()+offset)-left_pos; + const auto label = text.substr(start, label_length); + LL_INFOS() << "Our label is " << wstring_to_utf8str(label) << LL_ENDL; + const auto wlink = utf8str_to_wstring(link); + const auto pos = wlink.find(label); + // Do not replace if normal link, or contains normal link (but may omit protocol) but ends the same way + // (i.e. [http://foo.bar/baz foo.bar] should still be restored here but not foo.bar/baz or foo.bar + if (pos == std::string::npos // Label is not (part of) the url + || (pos != 0 && wlink[pos-1] != '/') || pos+label.size() != wlink.size()) // Label is part of the url but there's more on either side of the url after the protocol + { + constexpr llwchar startchar = '[', space = ' ', endchar = ']'; + const auto raw_html = startchar + wlink + space + label + endchar; + text.replace(start, label_length, raw_html); + offset += raw_html.size() - label_length; // Track how much we've offset the string by replacing labels with their raw html and thus adding characters + } + } + } + gClipboard.copyFromSubstring(text, 0, text.length(), mSourceID); + } + else gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); } BOOL LLTextEditor::canPaste() const @@ -4077,31 +4117,61 @@ void LLTextEditor::appendColoredText(const std::string &new_text, style->setVisible(true); style->setColor(lcolor); style->setFontName(font_name); - appendStyledText(new_text, allow_undo, prepend_newline, style); + appendText(new_text, allow_undo, prepend_newline, style); } -void LLTextEditor::appendStyledText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - LLStyleSP stylep) +static LLTrace::BlockTimerStatHandle FTM_APPEND_TEXT("Append Text"); + +void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, + const LLStyleSP style) { - S32 part = (S32)LLTextParser::WHOLE; - if(mParseHTML) + LL_RECORD_BLOCK_TIME(FTM_APPEND_TEXT); + if (new_text.empty()) + return; + + std::string text = prepend_newline && !mWText.empty() ? ('\n' + new_text) : new_text; + appendTextImpl(text, style); + + if (!allow_undo) { + blockUndo(); + } +} +static LLTrace::BlockTimerStatHandle FTM_PARSE_HTML("Parse HTML"); + +// Appends new text to end of document +void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP style) +{ + std::string text = new_text; + static LLUICachedControl replace_links("SinguReplaceLinks"); + bool is_link = style && !style->getLinkHREF().empty(); // Don't search for URLs inside a link segment (STORM-358). + + S32 part = (S32)LLTextParser::WHOLE; + if (mReadOnly && mParseHTML && !is_link) // Singu Note: Do not replace html if the user is going to edit it. (Like in profiles) + { + LL_RECORD_BLOCK_TIME(FTM_PARSE_HTML); S32 start=0,end=0; - std::string text = new_text; - while ( findHTML(text, &start, &end) ) + LLUrlMatch match; + LLStyleSP link_style(new LLStyle); + if (style) *link_style = *style; + link_style->setColor(LLUI::sConfigGroup->getColor4("HTMLLinkColor")); + auto append_substr = [&](const size_t& pos, const size_t& count) { - LLStyleSP html(new LLStyle); - html->setVisible(true); - html->setColor(mLinkColor); - if (stylep) - { - html->setFontName(stylep->getFontString()); - } - html->mUnderline = TRUE; + appendAndHighlightText(text.substr(pos, count), part, style); + }; + auto append_link = [&](const std::string& link) + { + link_style->setLinkHREF(match.getUrl()); + appendAndHighlightText(link, part, link_style); + }; + while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, + boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3))) + { + start = match.getStart(); + end = match.getEnd()+1; + // output the text before the Url if (start > 0) { if (part == (S32)LLTextParser::WHOLE || @@ -4113,15 +4183,51 @@ void LLTextEditor::appendStyledText(const std::string &new_text, { part = (S32)LLTextParser::MIDDLE; } - std::string subtext=text.substr(0,start); - appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep); + append_substr(0, start); } - - html->setLinkHREF(text.substr(start,end-start)); - appendText(text.substr(start, end-start),allow_undo, prepend_newline, html); - if (end < (S32)text.length()) + + auto url = match.getUrl(); + const auto& label = match.getLabel(); + if (replace_links || url == label) { - text = text.substr(end,text.length() - end); + // add icon before url if need + /* Singu TODO: Icons next to links? + LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted()); + if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) + { + link_style->setImage(match.getIcon()); + setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); + }*/ + + // output the styled url + append_link(label); + bool tooltip_required = !match.getTooltip().empty(); + + // set the tooltip for the Url label + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + } + else if (!replace_links) // Still link the link itself + { + const auto pos = text.find(url); + bool fallback(pos == std::string::npos); // In special cases like no protocol and brackets + bool brackets(fallback && text.find('[') == start); // Brackets + if (fallback == brackets && start < pos) // Leading text, only in special case if brackets present + append_substr(start, brackets ? 1 : pos-start); + // In the special cases, only link exactly the url, this might not have a protocol so calculate the exact string + if (fallback) url = brackets ? text.substr(start+1, text.find(' ', start+2)-start) : text.substr(start, end-start); + append_link(url); // Append the link + const auto url_end = pos + url.size(); + if (fallback == brackets && end > url_end) // Ending text, only in special case if brackets present + append_substr(url_end, end-url_end); + } + + // move on to the rest of the text after the Url + if (end < (S32)text.length()) + { + text = text.substr(end, text.length() - end); end=0; part=(S32)LLTextParser::END; } @@ -4130,51 +4236,126 @@ void LLTextEditor::appendStyledText(const std::string &new_text, break; } } - if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; - if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep); + if (part != (S32)LLTextParser::WHOLE) + part=(S32)LLTextParser::END; + if (end < (S32)text.length()) + appendAndHighlightText(text, part, style); } else { - appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep); + appendAndHighlightText(text, part, style); } } -void LLTextEditor::appendHighlightedText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - S32 highlight_part, - LLStyleSP stylep) +void LLTextEditor::setLastSegmentToolTip(const std::string &tooltip) { - // If LindenUserDir is empty then we didn't login yet. - // In that case we can't instantiate LLTextParser, which - // is initialized per user. - if (mParseHighlights && !gDirUtilp->getLindenUserDir(true).empty()) - { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && stylep) - { - LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), (LLTextParser::EHighlightPosition)highlight_part); - bool lprepend=prepend_newline; - for (S32 i=0;isetColor(lcolor); - if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep); - } - return; - } - } - appendText(new_text, allow_undo, prepend_newline, stylep); + LLTextSegmentPtr segment = mSegments.back(); + segment->setToolTip(tooltip); } -// Appends new text to end of document -void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, - const LLStyleSP stylep) +void LLTextEditor::appendLineBreakSegment(/*const LLStyle::Params& style_params*/) +{ + /* + segment_vec_t segments; + LLStyleSP sp(new LLStyle(style_params)); + const auto& length = getLength(); + segments.push_back(new LLTextSegment(sp, length, length + 1));*/ + + insertStringNoUndo(getLength(), LLWString(1, '\n')); +} + +void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep) +{ + if (new_text.empty()) return; + + std::string::size_type start = 0; + /*std::string::size_type pos = new_text.find('\n',start); + + while(pos!=-1) + { + if(pos!=start) + { + std::string str = std::string(new_text,start,pos-start); + appendAndHighlightTextImpl(str,highlight_part, stylep); + } + appendLineBreakSegment(); + start = pos+1; + pos = new_text.find('\n',start); + }*/ + + std::string str = std::string(new_text,start,new_text.length()-start); + appendAndHighlightTextImpl(str,highlight_part, stylep); +} + + +void LLTextEditor::replaceUrl(const std::string &url, + const std::string &label, + const std::string &icon) +{ + static LLUICachedControl replace_links("SinguReplaceLinks"); + if (!replace_links) return; + + // get the full (wide) text for the editor so we can change it + LLWString text = getWText(); + LLWString wlabel = utf8str_to_wstring(label); + bool modified = false; + S32 seg_start = 0; + + // iterate through each segment looking for ones styled as links + for (auto it = mSegments.begin(); it != mSegments.end(); ++it) + { + LLTextSegment *seg = *it; + LLStyleSP style = seg->getStyle(); + + // update segment start/end length in case we replaced text earlier + S32 seg_length = seg->getEnd() - seg->getStart(); + seg->setStart(seg_start); + seg->setEnd(seg_start + seg_length); + + // if we find a link with our Url, then replace the label + if (style->getLinkHREF() == url) + { + S32 start = seg->getStart(); + S32 end = seg->getEnd(); + text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); + seg->setEnd(start + wlabel.size()); + modified = true; + } + + /* Singu TODO: Icons with Urls? + // Icon might be updated when more avatar or group info + // becomes available + if (style->isImage() && style->getLinkHREF() == url) + { + LLUIImagePtr image = image_from_icon_name( icon ); + if (image) + { + LLStyle::Params icon_params; + icon_params.image = image; + LLStyleConstSP new_style(new LLStyle(icon_params)); + seg->setStyle(new_style); + modified = true; + } + } + */ + + // work out the character offset for the next segment + seg_start = seg->getEnd(); + } + + // update the editor with the new (wide) text string + if (modified) + { + mWText = text; + mTextIsUpToDate = FALSE; + deselect(); + setCursorPos(mCursorPos); + needsReflow(); + } +} + + +void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep) { // Save old state BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); @@ -4189,29 +4370,53 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool setCursorPos(old_length); - // Add carriage return if not first line - if (getLength() != 0 - && prepend_newline) + // This is where we appendHighlightedText + // If LindenUserDir is empty then we didn't login yet. + // In that case we can't instantiate LLTextParser, which is initialized per user. + if (mParseHighlights && !gDirUtilp->getLindenUserDir(true).empty()) { - std::string final_text = "\n"; - final_text += new_text; - append(utf8str_to_wstring(final_text), TRUE); + LLTextParser* highlight = LLTextParser::getInstance(); + + if (highlight && stylep) + { + LLStyleSP highlight_params(new LLStyle(*stylep)); + LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params->getColor(), (LLTextParser::EHighlightPosition)highlight_part); + for (S32 i=0;isetColor(lcolor); + + LLWString wide_text; + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + + S32 cur_length = getLength(); + insertStringNoUndo(cur_length, wide_text); + LLStyleSP sp(new LLStyle(*highlight_params)); + LLTextSegmentPtr segmentp; + segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_text.size()); + mSegments.push_back(segmentp); + } + return; + } } - else + //else { - append(utf8str_to_wstring(new_text), TRUE ); + LLWString wide_text; + wide_text = utf8str_to_wstring(new_text); + + insertStringNoUndo(getLength(), utf8str_to_wstring(new_text)); + + if (stylep) + { + S32 segment_start = old_length; + S32 segment_end = old_length + wide_text.size(); + LLTextSegmentPtr segment = new LLTextSegment(stylep, segment_start, segment_end); + mSegments.push_back(segment); + } } - if (stylep) - { - S32 segment_start = old_length; - S32 segment_end = getLength(); - LLTextSegmentPtr segment = new LLTextSegment(stylep, segment_start, segment_end ); - mSegments.push_back(segment); - } - - needsReflow(); - // Set the cursor and scroll position // Maintain the scroll position unless the scroll was at the end of the doc (in which // case, move it to the new end of the doc) or unless the user was doing actively selecting @@ -4230,9 +4435,6 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool mSelectionStart = selection_start; mSelectionEnd = selection_end; - - - mIsSelecting = was_selecting; setCursorPos(cursor_pos); } @@ -4244,11 +4446,6 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool { setCursorPos(cursor_pos); } - - if( !allow_undo ) - { - blockUndo(); - } } void LLTextEditor::removeTextFromEnd(S32 num_chars) @@ -4281,13 +4478,16 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) mWText.insert(pos, wstr); mTextIsUpToDate = FALSE; - if ( truncate() ) + //HACK: If we are readonly we shouldn't need to truncate + if (!mReadOnly && truncate()) { // The user's not getting everything he's hoping for make_ui_sound("UISndBadKeystroke"); insert_len = mWText.length() - old_len; } + needsReflow(/*pos*/); + return insert_len; } @@ -4409,6 +4609,24 @@ void LLTextEditor::loadKeywords(const std::string& filename, static LLTrace::BlockTimerStatHandle FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting"); static LLTrace::BlockTimerStatHandle FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); + +void LLTextEditor::createDefaultSegment() +{ + if (mSegments.empty()) + { + LLColor4& text_color = (mReadOnly ? mReadOnlyFgColor : mFgColor); + LLTextSegmentPtr default_segment = new LLTextSegment(text_color, 0, mWText.length()); + default_segment->setIsDefault(TRUE); + mSegments.push_back(default_segment); + } +} + +void LLTextEditor::clearSegments() +{ + mSegments.clear(); + createDefaultSegment(); +} + void LLTextEditor::updateSegments() { { @@ -4428,15 +4646,9 @@ void LLTextEditor::updateSegments() // Make sure we have at least one segment if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) { - mSegments.clear(); // create default segment - } - if (mSegments.empty()) - { - LLColor4& text_color = ( mReadOnly ? mReadOnlyFgColor : mFgColor ); - LLTextSegmentPtr default_segment = new LLTextSegment( text_color, 0, mWText.length() ); - default_segment->setIsDefault(TRUE); - mSegments.push_back(default_segment); + clearSegments(); // create default segment } + else createDefaultSegment(); } // Only effective if text was removed from the end of the editor @@ -4740,15 +4952,34 @@ LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) : BOOL LLTextSegment::getToolTip(std::string& msg) const { + // do we have a tooltip for a loaded keyword (for script editor)? if (mToken && !mToken->getToolTip().empty()) { const LLWString& wmsg = mToken->getToolTip(); msg = wstring_to_utf8str(wmsg); return TRUE; } + // or do we have an explicitly set tooltip (e.g., for Urls) + if (!mTooltip.empty()) + { + msg = mTooltip; + return TRUE; + } + return FALSE; } +void LLTextSegment::setToolTip(const std::string& tooltip) +{ + // we cannot replace a keyword tooltip that's loaded from a file + if (mToken) + { + LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL; + return; + } + mTooltip = tooltip; +} + void LLTextSegment::dump() const @@ -4827,173 +5058,6 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node) } } -/////////////////////////////////////////////////////////////////// -// Refactoring note: We may eventually want to replace this with boost::regex or -// boost::tokenizer capabilities since we've already fixed at least two JIRAs -// concerning logic issues associated with this function. -S32 LLTextEditor::findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const -{ - std::string openers=" \t\n('\"[{<>"; - std::string closers=" \t\n)'\"]}><;"; - - if (reverse) - { - for (int index=pos; index >= 0; index--) - { - char c = line[index]; - S32 m2 = openers.find(c); - if (m2 >= 0) - { - return index+1; - } - } - return 0; // index is -1, don't want to return that. - } - else - { - // adjust the search slightly, to allow matching parenthesis inside the URL - S32 paren_count = 0; - for (int index=pos; index<(S32)line.length(); index++) - { - char c = line[index]; - - if (c == '(') - { - paren_count++; - } - else if (c == ')') - { - if (paren_count <= 0) - { - return index; - } - else - { - paren_count--; - } - } - else - { - S32 m2 = closers.find(c); - if (m2 >= 0) - { - return index; - } - } - } - return line.length(); - } -} - -BOOL LLTextEditor::findHTML(const std::string &line, S32 *begin, S32 *end) const -{ - - S32 m1,m2,m3; - BOOL matched = FALSE; - - m1=line.find("://",*end); - - if (m1 >= 0) //Easy match. - { - *begin = findHTMLToken(line, m1, TRUE); - *end = findHTMLToken(line, m1, FALSE); - - //Load_url only handles http and https so don't hilite ftp, smb, etc. - m2 = line.substr(*begin,(m1 - *begin)).find("http"); - m3 = line.substr(*begin,(m1 - *begin)).find("secondlife"); - - std::string badneighbors=".,<>?';\"][}{=-+_)(*&^%$#@!~`\t\r\n\\"; - - if (m2 >= 0 || m3>=0) - { - S32 bn = badneighbors.find(line.substr(m1+3,1)); - - if (bn < 0) - { - matched = TRUE; - } - } - } -/* matches things like secondlife.com (no http://) needs a whitelist to really be effective. - else //Harder match. - { - m1 = line.find(".",*end); - - if (m1 >= 0) - { - *end = findHTMLToken(line, m1, FALSE); - *begin = findHTMLToken(line, m1, TRUE); - - m1 = line.rfind(".",*end); - - if ( ( *end - m1 ) > 2 && m1 > *begin) - { - std::string badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`"; - m2 = badneighbors.find(line.substr(m1+1,1)); - m3 = badneighbors.find(line.substr(m1-1,1)); - if (m3<0 && m2<0) - { - matched = TRUE; - } - } - } - } - */ - - if (matched) - { - S32 strpos, strpos2; - - std::string url = line.substr(*begin,*end - *begin); - std::string slurlID = "slurl.com/secondlife/"; - strpos = url.find(slurlID); - - if (strpos < 0) - { - slurlID="maps.secondlife.com/secondlife/"; - strpos = url.find(slurlID); - } - - if (strpos < 0) - { - slurlID="secondlife://"; - strpos = url.find(slurlID); - } - - if (strpos < 0) - { - slurlID="sl://"; - strpos = url.find(slurlID); - } - - if (strpos >= 0) - { - strpos+=slurlID.length(); - - while ( ( strpos2=url.find("/",strpos) ) == -1 ) - { - if ((*end+2) >= (S32)line.length() || line.substr(*end,1) != " " ) - { - matched=FALSE; - break; - } - - strpos = (*end + 1) - *begin; - - *end = findHTMLToken(line,(*begin + strpos),FALSE); - url = line.substr(*begin,*end - *begin); - } - } - - } - - if (!matched) - { - *begin=*end=0; - } - return matched; -} - void LLTextEditor::updateAllowingLanguageInput() diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 3bfdfb6d6..2244b4c19 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -65,9 +65,10 @@ public: LLTextEditor(const std::string& name, const LLRect& rect, S32 max_length, - const std::string &default_text, + const std::string &default_text, const LLFontGL* glfont = NULL, - BOOL allow_embedded_items = FALSE); + BOOL allow_embedded_items = FALSE, + bool parse_html = false); virtual ~LLTextEditor(); @@ -131,7 +132,9 @@ public: virtual BOOL canRedo() const; virtual void cut(); virtual BOOL canCut() const; - virtual void copy(); + void copy(bool raw); + void copyRaw() { copy(true); } + virtual void copy() { copy(false); } virtual BOOL canCopy() const; virtual void paste(); virtual BOOL canPaste() const; @@ -177,19 +180,23 @@ public: // appends text at end void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline, const LLStyleSP stylep = NULL); + void appendTextImpl(const std::string& new_text, const LLStyleSP style); + + void setLastSegmentToolTip(const std::string& tooltip); + + void appendLineBreakSegment(); + + void appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep); + + void replaceUrl(const std::string& url, const std::string& label, const std::string& icon); void appendColoredText(const std::string &wtext, bool allow_undo, bool prepend_newline, const LLColor4 &color, const std::string& font_name = LLStringUtil::null); - // if styled text starts a line, you need to prepend a newline. - void appendStyledText(const std::string &new_text, bool allow_undo, - bool prepend_newline, - LLStyleSP stylep = NULL); - void appendHighlightedText(const std::string &new_text, bool allow_undo, - bool prepend_newline, S32 highlight_part, - LLStyleSP stylep); + void appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep); + // Removes text from the end of document // Does not change highlight or cursor position. void removeTextFromEnd(S32 num_chars); @@ -308,7 +315,6 @@ protected: void updateTextRect(); const LLRect& getTextRect() const { return mTextRect; } - void assignEmbedded(const std::string &s); BOOL truncate(); // Returns true if truncation occurs void removeCharOrTab(); @@ -364,9 +370,6 @@ protected: virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } virtual void bindEmbeddedChars(const LLFontGL* font) const {} virtual void unbindEmbeddedChars(const LLFontGL* font) const {} - - S32 findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const; - BOOL findHTML(const std::string &line, S32 *begin, S32 *end) const; // Abstract inner base class representing an undoable editor command. // Concrete sub-classes can be defined for operations such as insert, remove, etc. @@ -408,7 +411,6 @@ protected: void removeWord(bool prev); S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op); S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op); - S32 append(const LLWString &wstr, const BOOL group_with_next_op); // Direct operations S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted @@ -480,6 +482,8 @@ private: void pasteHelper(bool is_primary); void onKeyStroke(); + void createDefaultSegment(); + void clearSegments(); void updateSegments(); void pruneSegments(); @@ -615,6 +619,7 @@ public: LLTextSegment( const LLColor3& color, S32 start, S32 end ); S32 getStart() const { return mStart; } + void setStart(S32 start) { mStart = start; } S32 getEnd() const { return mEnd; } void setEnd( S32 end ) { mEnd = end; } const LLColor4& getColor() const { return mStyle->getColor(); } @@ -626,6 +631,7 @@ public: void setToken( LLKeywordToken* token ) { mToken = token; } LLKeywordToken* getToken() const { return mToken; } BOOL getToolTip( std::string& msg ) const; + void setToolTip(const std::string& tooltip); void dump() const; @@ -643,6 +649,7 @@ private: S32 mEnd; LLKeywordToken* mToken; BOOL mIsDefault; + std::string mTooltip; }; diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index 712f038d8..a30d7bfcb 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -907,6 +907,20 @@ RIP Latif Khalifa. Value 0 + SinguReplaceLinks + + Comment + Whether or not to visually replace special links like SLURLs with labels where applicable. +While having this on allows people to mislead you with links that look like other links, it also allows you to use SLURLs that appear as people's names and increasingly more scripts use this to run faster. +You can always select the text, right click and select Copy Raw to copy the hidden contents of the link. +Changing this setting only affects new text. + Persist + 1 + Type + Boolean + Value + 1 + WarnRecommendedUpdate Comment diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 48bf7a836..7c991e4d4 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -144,7 +144,7 @@ LLFloaterAbout::LLFloaterAbout() __DATE__, __TIME__, LLVersionInfo::getChannel().c_str())); support_widget->appendColoredText(version, FALSE, FALSE, gColors.getColor("TextFgReadOnlyColor")); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style); + support_widget->appendText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style); std::string support; support.append("\n\n"); @@ -209,7 +209,7 @@ LLFloaterAbout::LLFloaterAbout() { LLStyleSP server_link_style(new LLStyle(*viewer_link_style)); server_link_style->setLinkHREF(url); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style); + support_widget->appendText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style); } support = "\n\n"; diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 7668a94fa..57b2da648 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -61,7 +61,6 @@ #include "llspeakers.h" #include "llstylemap.h" #include "lluictrlfactory.h" -#include "llurlregistry.h" #include "llviewermessage.h" #include "llviewertexteditor.h" #include "llviewerwindow.h" @@ -177,31 +176,6 @@ void LLFloaterChat::updateConsoleVisibility() || (getHost() && getHost()->isMinimized() )); // are we hosted in a minimized floater? } -void handle_registered_urls(std::string ui_msg, bool prepend_newline, LLStyleSP style, LLViewerTextEditor* edit, const LLColor4& color) -{ - LLUrlMatch match; - auto& registry = LLUrlRegistry::instance(); - while (!ui_msg.empty() && registry.findUrl(ui_msg, match)) - { - if (match.getStart() > 0) - { - style->setColor(color); - edit->appendStyledText(ui_msg.substr(0, match.getStart()), false, prepend_newline, style); - prepend_newline = false; - } - style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); - style->setLinkHREF(match.getUrl()); - edit->appendStyledText(match.getLabel(), false, prepend_newline, style); - style->setLinkHREF(LLStringUtil::null); - prepend_newline = false; - - ui_msg = (ui_msg.size() <= match.getEnd() + 1) ? LLStringUtil::null : ui_msg.substr(match.getEnd() + 1); - } - - style->setColor(color); - if (!ui_msg.empty()) edit->appendStyledText(ui_msg, false, prepend_newline, style); -} - void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& color) { std::string line = chat.mText; @@ -244,12 +218,13 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& line = line.substr(chat.mFromName.length() + 1); LLStyleSP sourceStyle = LLStyleMap::instance().lookup(chat.mFromID, chat.mURL); sourceStyle->mItalic = is_irc; - edit->appendStyledText(start_line, false, prepend_newline, sourceStyle); + edit->appendText(start_line, false, prepend_newline, sourceStyle); prepend_newline = false; } LLStyleSP style(new LLStyle); + style->setColor(color); style->mItalic = is_irc; - handle_registered_urls(line, prepend_newline, style, edit, color); + edit->appendText(line, false, prepend_newline, style); } void log_chat_text(const LLChat& chat) diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 22dbe1d87..4397eb088 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -458,7 +458,7 @@ void LLFloaterOutbox::updateView() LLStyleSP subs_link_style(new LLStyle); subs_link_style->setLinkHREF(subs_link); subs_link_style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); - mInventoryText->appendStyledText(subs_text, false, false, subs_link_style); + mInventoryText->appendText(subs_text, false, false, subs_link_style); mInventoryText->appendColoredText(outbox_text2, false, false, color); mInventoryTitle->setValue(outbox_title); mInventoryPlaceholder->getParent()->setToolTip(outbox_tooltip); diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index a60ca1ca7..3c0fe3af0 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -75,7 +75,7 @@ BOOL LLFloaterVoiceEffect::postBuild() LLStyleSP link(new LLStyle); link->setLinkHREF(LLTrans::getString("voice_morphing_url")); link->setColor(gSavedSettings.getColor4("HTMLLinkColor")); - editor->appendStyledText(text, false, false, link); + editor->appendText(text, false, false, link); } mVoiceEffectList = getChild("voice_effect_list"); diff --git a/indra/newview/llgroupnotify.cpp b/indra/newview/llgroupnotify.cpp index e401e80a1..0250af47e 100644 --- a/indra/newview/llgroupnotify.cpp +++ b/indra/newview/llgroupnotify.cpp @@ -170,19 +170,16 @@ LLGroupNotifyBox::LLGroupNotifyBox(const std::string& subject, DB_GROUP_NOTICE_MSG_STR_LEN, LLStringUtil::null, LLFontGL::getFontSansSerif(), - FALSE); + FALSE, + true); static const LLStyleSP headerstyle(new LLStyle(true,LLColor4::black,"SansSerifBig")); static const LLStyleSP datestyle(new LLStyle(true,LLColor4::black,"serif")); - text->appendStyledText(subject + "\n",false,false,headerstyle); + text->appendText(subject + '\n',false,false,headerstyle); - text->appendStyledText(time_buf,false,false,datestyle); - // Sadly, our LLTextEditor can't handle both styled and unstyled text - // at the same time. Hence this space must be styled. JC - text->appendColoredText(std::string(" "),false,false,LLColor4::grey4); - text->setParseHTML(TRUE); - text->appendColoredText(std::string("\n\n") + message,false,false,LLColor4::grey4); + text->appendText(time_buf,false,false,datestyle); + text->appendColoredText(std::string(" \n\n") + message,false,false,LLColor4::grey4); LLColor4 semi_transparent(1.0f,1.0f,1.0f,0.8f); text->setCursor(0,0); diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index a16908865..7a0b33ec7 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -804,7 +804,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incol // Convert the name to a hotlink and add to message. LLStyleSP source_style = LLStyleMap::instance().lookupAgent(source); source_style->mItalic = is_irc; - mHistoryEditor->appendStyledText(show_name,false,prepend_newline,source_style); + mHistoryEditor->appendText(show_name,false,prepend_newline,source_style); } prepend_newline = false; } @@ -812,11 +812,10 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incol // Append the chat message in style { LLStyleSP style(new LLStyle); + style->setColor(incolor); style->mItalic = is_irc; style->mBold = from_user && gSavedSettings.getBOOL("SingularityBoldGroupModerator") && isModerator(source); - - void handle_registered_urls(std::string ui_msg, bool prepend_newline, LLStyleSP style, LLViewerTextEditor* edit, const LLColor4& color); - handle_registered_urls(utf8msg, prepend_newline, style, mHistoryEditor, incolor); + mHistoryEditor->appendText(utf8msg, false, prepend_newline, style); } if (log_to_file diff --git a/indra/newview/llnotify.cpp b/indra/newview/llnotify.cpp index 2391858a6..3dc774d53 100644 --- a/indra/newview/llnotify.cpp +++ b/indra/newview/llnotify.cpp @@ -234,7 +234,7 @@ LLNotifyBox::LLNotifyBox(LLNotificationPtr notification) const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title. - auto text = new LLTextEditor(std::string("box"), LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), MAX_LENGTH, message, sFont, FALSE); + auto text = new LLTextEditor(std::string("box"), LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), MAX_LENGTH, message, sFont, FALSE, true); text->setWordWrap(TRUE); text->setTabStop(FALSE); text->setMouseOpaque(FALSE); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index e5b8d4898..64248f970 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -286,6 +286,8 @@ void LLPreviewNotecard::loadAsset() // request the asset. if (const LLInventoryItem* item = getItem()) { + bool modify = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); + if (modify) editor->setParseHTML(false); // Don't do the url parsing or we'll lose text! if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike()) @@ -344,8 +346,7 @@ void LLPreviewNotecard::loadAsset() editor->setEnabled(FALSE); mAssetStatus = PREVIEW_ASSET_LOADED; } - if(!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), - GP_OBJECT_MANIPULATE)) + if(!modify) { editor->setEnabled(FALSE); // You can always save in task inventory diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index d646a2b83..404dce4bf 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -578,8 +578,9 @@ LLViewerTextEditor::LLViewerTextEditor(const std::string& name, S32 max_length, const std::string& default_text, const LLFontGL* font, - BOOL allow_embedded_items) - : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items), + BOOL allow_embedded_items, + bool parse_html) + : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items, parse_html), mDragItemChar(0), mDragItemSaved(FALSE), mInventoryCallback(new LLEmbeddedNotecardOpener) @@ -1679,7 +1680,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF text_editor->initFromXML(node, parent); // add text after all parameters have been set - text_editor->appendStyledText(text, FALSE, FALSE); + text_editor->appendText(text, FALSE, FALSE); return text_editor; } diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h index 702b97ca1..a7741502f 100644 --- a/indra/newview/llviewertexteditor.h +++ b/indra/newview/llviewertexteditor.h @@ -48,7 +48,8 @@ public: S32 max_length, const std::string& default_text = std::string(), const LLFontGL* glfont = NULL, - BOOL allow_embedded_items = FALSE); + BOOL allow_embedded_items = FALSE, + bool parse_html = false); virtual ~LLViewerTextEditor();