summaryrefslogtreecommitdiff
path: root/indra/newview/llchatmsgbox.cpp
diff options
context:
space:
mode:
authorMartin Reddy <lynx@lindenlab.com>2009-09-04 12:11:27 +0000
committerMartin Reddy <lynx@lindenlab.com>2009-09-04 12:11:27 +0000
commit330840af7c363accc8e12d073ba91b871c578d27 (patch)
tree4539a78e8201ee2508b6d598639efe9a989b6531 /indra/newview/llchatmsgbox.cpp
parent2273376dffb80af0cbf1bc1d8cc8990e1d1456f3 (diff)
Merging the SLURLs Everywhere branch (viewer-2.0.0-slurls-3) into
Viewer 2.0 (viewer-2.0.0-3). This provides support for clickable Urls in text editors and textboxes, with right-click context menus, tooltips, and alternate link labels. This includes alert boxes, the login progress window, local chat and IM interfaces, etc. As well as context menus for avatars and groups in list widgets. Includes fixes for the following individual JIRAs: DEV-8763 VWR-10636: Hyperlinks in alert dialogs should be selectable (clickable)! DEV-38829 EXT-742: Remove LLLink class DEV-35459 VWR-14679: SLURLs and teleport Links not parsed properly DEV-19842 VWR-8773: Closing parenthesis ")" breaks urls DEV-21577 VWR-9405: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat DEV-37652 SEC-435: Object Chat/IMs are untraceable (VWR-2388) Fix has left flaw DEV-10353: URLs in chat log terminated incorrectly when newline in chat DEV-2925: In chat history, use a teleport hyperlink as source name for object IMs DEV-36192: Need a way to copy Avatar names and Group names DEV-2926: Allow viewer hyperlinks to have different text than the actual url DEV-27253: Add easy way to copy URLs from viewer chat DEV-38274: Make About Second Life window use new Url hyperlinking features DEV-39076: No url support in Text Editors DEV-7476 VWR-2172: Add hyperlinks to chat console for easier access DEV-7475: Add hyperlinks to notecards! DEV-35375 EXT-128: HTTPS urls aren't loaded in the internal browser by click Master JIRA issues: DEV-32819, DEV-323820, DEV-7474 Testing performed against QAR-1789 svn merge -r 131623:131889 svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-slurl-3 svn merge -r 131978:132515 svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-slurl-3
Diffstat (limited to 'indra/newview/llchatmsgbox.cpp')
-rw-r--r--indra/newview/llchatmsgbox.cpp372
1 files changed, 54 insertions, 318 deletions
diff --git a/indra/newview/llchatmsgbox.cpp b/indra/newview/llchatmsgbox.cpp
index fb5ab8ec5a..e6398dd47a 100644
--- a/indra/newview/llchatmsgbox.cpp
+++ b/indra/newview/llchatmsgbox.cpp
@@ -1,10 +1,11 @@
/**
* @file llchatmsgbox.cpp
+ * @author Martin Reddy
* @brief chat history text box, able to show array of strings with separator
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
*
- * Copyright (c) 2004-2009, Linden Research, Inc.
+ * Copyright (c) 2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
@@ -30,361 +31,96 @@
* $/LicenseInfo$
*/
-
#include "llviewerprecompiledheaders.h"
#include "llchatmsgbox.h"
#include "llwindow.h"
-#include "llfocusmgr.h"
-
-static LLDefaultChildRegistry::Register<LLChatMsgBox> r("text_chat");
-LLChatMsgBox::Params::Params()
-: text_color("text_color"),
- highlight_on_hover("hover", false),
- border_visible("border_visible", false),
- border_drop_shadow_visible("border_drop_shadow_visible", false),
- bg_visible("bg_visible", false),
- use_ellipses("use_ellipses"),
- word_wrap("word_wrap", false),
- hover_color("hover_color"),
- disabled_color("disabled_color"),
- background_color("background_color"),
- border_color("border_color"),
- line_spacing("line_spacing", 4),
- block_spacing("block_spacing",10),
- text("text"),
- font_shadow("font_shadow", LLFontGL::NO_SHADOW)
-{}
-LLChatMsgBox::LLChatMsgBox(const LLChatMsgBox::Params& p)
-: LLUICtrl(p),
- mFontGL(p.font),
- mHoverActive( p.highlight_on_hover ),
- mHasHover( FALSE ),
- mBackgroundVisible( p.bg_visible ),
- mBorderVisible( p.border_visible ),
- mShadowType( p.font_shadow ),
- mBorderDropShadowVisible( p.border_drop_shadow_visible ),
- mUseEllipses( p.use_ellipses ),
- mVAlign( LLFontGL::TOP ),
- mClickedCallback(NULL),
- mTextColor(p.text_color()),
- mDisabledColor(p.disabled_color()),
- mBackgroundColor(p.background_color()),
- mBorderColor(p.border_color()),
- mHoverColor(p.hover_color()),
- mHAlign(p.font_halign),
- mLineSpacing(p.line_spacing),
- mBlockSpasing(p.block_spacing),
- mWordWrap( p.word_wrap ),
- mFontStyle(LLFontGL::getStyleFromString(p.font.style))
-{
- setText( p.text() );
-}
+static LLDefaultChildRegistry::Register<LLChatMsgBox> r("text_chat");
-BOOL LLChatMsgBox::handleMouseDown(S32 x, S32 y, MASK mask)
+LLChatMsgBox::Params::Params() :
+ block_spacing("block_spacing", 10)
{
- BOOL handled = FALSE;
-
- // HACK: Only do this if there actually is a click callback, so that
- // overly large text boxes in the older UI won't start eating clicks.
- if (mClickedCallback)
- {
- handled = TRUE;
-
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
- }
-
- return handled;
+ line_spacing = 4;
}
-BOOL LLChatMsgBox::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- BOOL handled = FALSE;
-
- // We only handle the click if the click both started and ended within us
-
- // HACK: Only do this if there actually is a click callback, so that
- // overly large text boxes in the older UI won't start eating clicks.
- if (mClickedCallback
- && hasMouseCapture())
- {
- handled = TRUE;
-
- // Release the mouse
- gFocusMgr.setMouseCapture( NULL );
-
- if (getSoundFlags() & MOUSE_UP)
- {
- make_ui_sound("UISndClickRelease");
- }
-
- // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
- // If mouseup in the widget, it's been clicked
- if (mClickedCallback)
- {
- mClickedCallback();
- }
- }
-
- return handled;
-}
+LLChatMsgBox::LLChatMsgBox(const Params& p) :
+ LLTextBox(p),
+ mBlockSpacing(p.block_spacing)
+{}
-BOOL LLChatMsgBox::handleHover(S32 x, S32 y, MASK mask)
+void LLChatMsgBox::addText( const LLStringExplicit& text )
{
- BOOL handled = LLView::handleHover(x,y,mask);
- if(mHoverActive)
+ LLWString t = mText.getWString();
+ if (! t.empty())
{
- mHasHover = TRUE; // This should be set every frame during a hover.
- getWindow()->setCursor(UI_CURSOR_ARROW);
+ t += '\n';
}
-
- return (handled || mHasHover);
-}
-
-void LLChatMsgBox::addText( const LLStringExplicit& text )
-{
- boost::shared_ptr<text_block> t(new text_block());
- t->text = wrapText(text);
- setLineLengths(*t);
- mTextStrings.push_back(t);
+ t += getWrappedText(text);
+ LLTextBox::setText(wstring_to_utf8str(t));
+ mSeparatorOffset.push_back(getLength());
}
void LLChatMsgBox::setText(const LLStringExplicit& text)
{
- mTextStrings.clear();
-
+ mSeparatorOffset.clear();
+ mText.clear();
addText(text);
-
-}
-
-void LLChatMsgBox::resetLineLengths()
-{
- for(std::vector< boost::shared_ptr<text_block> >::iterator it = mTextStrings.begin();
- it!=mTextStrings.end();++it)
- {
- boost::shared_ptr<text_block> tblock = *it;
- setLineLengths(*tblock);
- }
-}
-
-void LLChatMsgBox::setLineLengths(text_block& t)
-{
- t.lines.clear();
-
- std::string::size_type cur = 0;
- std::string::size_type len = t.text.length();
-
- while (cur < len)
- {
- std::string::size_type end = t.text.getWString().find('\n', cur);
- std::string::size_type runLen;
-
- if (end == std::string::npos)
- {
- runLen = len - cur;
- cur = len;
- }
- else
- {
- runLen = end - cur;
- cur = end + 1; // skip the new line character
- }
-
- t.lines.push_back( (S32)runLen );
- }
-}
-
-std::string LLChatMsgBox::wrapText(const LLStringExplicit& in_text, F32 max_width)
-{
- if (max_width < 0.0f)
- {
- max_width = (F32)getRect().getWidth();
- }
-
- LLWString wtext = utf8str_to_wstring(in_text);
- LLWString final_wtext;
-
- LLWString::size_type cur = 0;;
- LLWString::size_type len = wtext.size();
- while (cur < len)
- {
- LLWString::size_type end = wtext.find('\n', cur);
- if (end == LLWString::npos)
- {
- end = len;
- }
-
- LLWString::size_type runLen = end - cur;
- if (runLen > 0)
- {
- LLWString run(wtext, cur, runLen);
- LLWString::size_type useLen =
- mFontGL->maxDrawableChars(run.c_str(), max_width, runLen, TRUE);
-
- final_wtext.append(wtext, cur, useLen);
- cur += useLen;
- // not enough room to add any more characters
- if (useLen == 0) break;
- }
-
- if (cur < len)
- {
- if (wtext[cur] == '\n')
- cur += 1;
-
- // There is no need to to cut line ending symbols found in origin string, see EXT-702.
- final_wtext += '\n';
- }
- }
-
- std::string final_text = wstring_to_utf8str(final_wtext);
- return final_text;
}
-S32 LLChatMsgBox::getTextLinesNum()
-{
- S32 num_lines = 0;
- for(std::vector< boost::shared_ptr<text_block> >::iterator it = mTextStrings.begin();
- it!=mTextStrings.end();++it)
- {
- boost::shared_ptr<text_block> tblock = *it;
- num_lines+=tblock->lines.size();
- }
-
- if( num_lines < 1 )
- {
- num_lines = 1;
- }
-
- return num_lines;
+void LLChatMsgBox::setValue(const LLSD& value )
+{
+ setText(value.asString());
}
S32 LLChatMsgBox::getTextPixelHeight()
{
+ S32 num_blocks = mSeparatorOffset.size();
S32 num_lines = getTextLinesNum();
- return (S32)(num_lines * mFontGL->getLineHeight() + (num_lines-1)*mLineSpacing + mBlockSpasing*(mTextStrings.size()-1) + 2*mLineSpacing);//some extra space
-}
-
-void LLChatMsgBox::setValue(const LLSD& value )
-{
- setText(value.asString());
+ return (S32)(num_lines * mDefaultFont->getLineHeight() + \
+ (num_lines-1) * mLineSpacing + \
+ (num_blocks-1) * mBlockSpacing + \
+ 2 * mLineSpacing);
}
-
-void LLChatMsgBox::draw()
+S32 LLChatMsgBox::getTextLinesNum()
{
- if (mBorderVisible)
- {
- gl_rect_2d_offset_local(getLocalRect(), 2, FALSE);
- }
-
- if( mBorderDropShadowVisible )
- {
- static LLUICachedControl<LLColor4> color_drop_shadow ("ColorDropShadow", *(new LLColor4));
- static LLUICachedControl<S32> drop_shadow_tooltip ("DropShadowTooltip", 0);
- gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- color_drop_shadow, drop_shadow_tooltip);
- }
-
- if (mBackgroundVisible)
- {
- LLRect r( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- gl_rect_2d( r, mBackgroundColor.get() );
- }
-
- S32 text_x = 0;
- switch( mHAlign )
- {
- case LLFontGL::LEFT:
- break;
- case LLFontGL::HCENTER:
- text_x = getRect().getWidth() / 2;
- break;
- case LLFontGL::RIGHT:
- text_x = getRect().getWidth() ;
- break;
- }
-
- S32 text_y = getRect().getHeight() ;
-
- if ( getEnabled() )
- {
- if(mHasHover)
- {
- drawText( text_x, text_y, mHoverColor.get() );
- }
- else
- {
- drawText( text_x, text_y, mTextColor.get() );
- }
- }
- else
- {
- drawText( text_x, text_y, mDisabledColor.get() );
- }
-
- if (sDebugRects)
+ S32 num_lines = getLineCount();
+ if (num_lines < 1)
{
- drawDebugRect();
+ num_lines = 1;
}
-
- //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview)
- //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);
- //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights))
- //{
- // drawDebugRect();
- //}
-
- mHasHover = FALSE; // This is reset every frame.
-}
-
-void LLChatMsgBox::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- // reparse line lengths
- LLView::reshape(width, height, called_from_parent);
- resetLineLengths();
+
+ return num_lines;
}
-void LLChatMsgBox::drawText( S32 x, S32 y, const LLColor4& color )
+void LLChatMsgBox::drawText(S32 x, S32 y, const LLWString &text, const LLColor4 &color)
{
+ S32 start = 0;
S32 width = getRect().getWidth()-10;
-
- for(std::vector< boost::shared_ptr<text_block> >::iterator it = mTextStrings.begin();
- it!=mTextStrings.end();++it)
+ // iterate through each block of text that has been added
+ y -= mLineSpacing;
+ for (std::vector<S32>::iterator it = mSeparatorOffset.begin(); true ;)
{
- boost::shared_ptr<text_block> tblock = *it;
+ // display the text for this block
+ S32 num_chars = *it - start;
+ LLWString text = mDisplayText.substr(start, num_chars);
+ LLTextBox::drawText(x, y, text, color);
- S32 cur_pos = 0;
- for (std::vector<S32>::iterator iter = tblock->lines.begin();
- iter != tblock->lines.end(); ++iter)
+ // exit the loop if this is the last text block
+ start += num_chars + 1; // skip the newline
+ if (++it == mSeparatorOffset.end())
{
- S32 line_length = *iter;
- mFontGL->render(tblock->text, cur_pos, (F32)x, (F32)y, color,
- mHAlign, mVAlign,
- mFontStyle,
- mShadowType,
- line_length, getRect().getWidth(), NULL, mUseEllipses );
- cur_pos += line_length + 1;
- y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing;
-
- }
- std::vector< boost::shared_ptr<text_block> >::iterator next = it;
- ++next;
- if(next == mTextStrings.end())
break;
- //separator
- gl_line_2d(5,y-mBlockSpasing/2,width,y-mBlockSpasing/2,LLColor4::grey);
- y-=mBlockSpasing;
- }
+ }
+ // output a separator line between blocks
+ S32 num_lines = std::count(text.begin(), text.end(), '\n') + 1;
+ y -= num_lines * (llfloor(mDefaultFont->getLineHeight()) + mLineSpacing);
+ S32 sep_y = y - mBlockSpacing/2 + mLineSpacing/2;
+ gl_line_2d(5, sep_y, width, sep_y, LLColor4::grey);
+ y -= mBlockSpacing;
+ }
}
-