summaryrefslogtreecommitdiff
path: root/indra/newview/llexpandabletextbox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llexpandabletextbox.cpp')
-rw-r--r--indra/newview/llexpandabletextbox.cpp508
1 files changed, 508 insertions, 0 deletions
diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp
new file mode 100644
index 0000000000..131f9ceaf0
--- /dev/null
+++ b/indra/newview/llexpandabletextbox.cpp
@@ -0,0 +1,508 @@
+/**
+ * @file llexpandabletextbox.cpp
+ * @brief LLExpandableTextBox and related class implementations
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llexpandabletextbox.h"
+
+#include "llscrollcontainer.h"
+
+static LLDefaultChildRegistry::Register<LLExpandableTextBox> t1("expandable_text");
+
+LLExpandableTextBox::LLTextBoxEx::Params::Params()
+: expand_textbox("expand_textbox")
+{
+}
+
+LLExpandableTextBox::LLTextBoxEx::LLTextBoxEx(const Params& p)
+: LLTextBox(p)
+{
+ setIsChrome(TRUE);
+
+ LLTextBox::Params params = p.expand_textbox;
+ mExpandTextBox = LLUICtrlFactory::create<LLTextBox>(params);
+ addChild(mExpandTextBox);
+
+ LLRect rc = getLocalRect();
+ rc.mRight -= getHPad();
+ rc.mLeft = rc.mRight - mExpandTextBox->getTextPixelWidth();
+ rc.mTop = mExpandTextBox->getTextPixelHeight();
+ mExpandTextBox->setRect(rc);
+}
+
+BOOL LLExpandableTextBox::LLTextBoxEx::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL ret = LLTextBox::handleMouseUp(x, y, mask);
+
+ if(mExpandTextBox->getRect().pointInRect(x, y))
+ {
+ onCommit();
+ }
+
+ return ret;
+}
+
+void LLExpandableTextBox::LLTextBoxEx::draw()
+{
+ // draw text box
+ LLTextBox::draw();
+ // force text box to draw children
+ LLUICtrl::draw();
+}
+
+void LLExpandableTextBox::LLTextBoxEx::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color )
+{
+ // *NOTE:dzaporozhan:
+ // Copy/paste from LLTextBox::drawText in order to modify last
+ // line width if needed and who "More" link
+ F32 alpha = getDrawContext().mAlpha;
+ if (mSegments.size() > 1)
+ {
+ // we have Urls (or other multi-styled segments)
+ drawTextSegments(x, y, text);
+ }
+ else if( mLineLengthList.empty() )
+ {
+ // simple case of 1 line of text in one style
+ mDefaultFont->render(text, 0, (F32)x, (F32)y, color % alpha,
+ mHAlign, mVAlign,
+ 0,
+ mShadowType,
+ S32_MAX, getRect().getWidth(), NULL, mUseEllipses);
+
+ mExpandTextBox->setVisible(FALSE);
+ }
+ else
+ {
+ // simple case of multiple lines of text, all in the same style
+ S32 cur_pos = 0;
+ for (std::vector<S32>::iterator iter = mLineLengthList.begin();
+ iter != mLineLengthList.end(); ++iter)
+ {
+ S32 line_length = *iter;
+ S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing;
+ S32 max_pixels = getRect().getWidth();
+
+ if(iter + 1 != mLineLengthList.end()
+ && y - line_height < line_height)
+ {
+ max_pixels = getCropTextWidth();
+ }
+
+ mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha,
+ mHAlign, mVAlign,
+ 0,
+ mShadowType,
+ line_length, max_pixels, NULL, mUseEllipses );
+
+ cur_pos += line_length + 1;
+
+ y -= line_height;
+ if(y < line_height)
+ {
+ if( mLineLengthList.end() != iter + 1 )
+ {
+ showExpandText(y);
+ }
+ else
+ {
+ hideExpandText();
+ }
+ break;
+ }
+ }
+ }
+}
+
+void LLExpandableTextBox::LLTextBoxEx::showExpandText(S32 y)
+{
+ LLRect rc = mExpandTextBox->getRect();
+ rc.mTop = y + mExpandTextBox->getTextPixelHeight();
+ rc.mBottom = y;
+ mExpandTextBox->setRect(rc);
+ mExpandTextBox->setVisible(TRUE);
+}
+
+void LLExpandableTextBox::LLTextBoxEx::hideExpandText()
+{
+ mExpandTextBox->setVisible(FALSE);
+}
+
+S32 LLExpandableTextBox::LLTextBoxEx::getCropTextWidth()
+{
+ return mExpandTextBox->getRect().mLeft - getHPad() * 2;
+}
+
+void LLExpandableTextBox::LLTextBoxEx::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text)
+{
+ // *NOTE:dzaporozhan:
+ // Copy/paste from LLTextBox::drawTextSegments in order to modify last
+ // line width if needed and who "More" link
+ F32 alpha = getDrawContext().mAlpha;
+
+ const S32 text_len = text.length();
+ if (text_len <= 0)
+ {
+ return;
+ }
+
+ S32 cur_line = 0;
+ S32 num_lines = getLineCount();
+ S32 line_start = getLineStart(cur_line);
+ S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing;
+ F32 text_y = (F32) init_y;
+ segment_set_t::iterator cur_seg = mSegments.begin();
+
+ // render a line of text at a time
+ const LLRect textRect = getLocalRect();
+ while((textRect.mBottom <= text_y) && (cur_line < num_lines))
+ {
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if ((cur_line + 1) < num_lines)
+ {
+ next_start = getLineStart(cur_line + 1);
+ line_end = next_start;
+ }
+ if ( text[line_end-1] == '\n' )
+ {
+ --line_end;
+ }
+
+ // render all segments on this line
+ F32 text_x = init_x;
+ S32 seg_start = line_start;
+ while (seg_start < line_end && cur_seg != mSegments.end())
+ {
+ // move to the next segment (or continue the previous one)
+ LLTextSegment *cur_segment = *cur_seg;
+ while (cur_segment->getEnd() <= seg_start)
+ {
+ if (++cur_seg == mSegments.end())
+ {
+ return;
+ }
+ cur_segment = *cur_seg;
+ }
+
+ // Draw a segment within the line
+ S32 clipped_end = llmin( line_end, cur_segment->getEnd() );
+ S32 clipped_len = clipped_end - seg_start;
+ if( clipped_len > 0 )
+ {
+ LLStyleSP style = cur_segment->getStyle();
+ if (style && style->isVisible())
+ {
+ // work out the color for the segment
+ LLColor4 color ;
+ if (getEnabled())
+ {
+ color = style->isLink() ? mLinkColor.get() : mTextColor.get();
+ }
+ else
+ {
+ color = mDisabledColor.get();
+ }
+ color = color % alpha;
+
+ S32 max_pixels = textRect.getWidth();
+
+ if(cur_line + 1 < num_lines
+ && text_y - line_height < line_height)
+ {
+ max_pixels = getCropTextWidth();
+ }
+
+ // render a single line worth for this segment
+ mDefaultFont->render(text, seg_start, text_x, text_y, color,
+ mHAlign, mVAlign, 0, mShadowType, clipped_len,
+ max_pixels, &text_x, mUseEllipses);
+ }
+
+ seg_start += clipped_len;
+ }
+ }
+
+ // move down one line
+ text_y -= (F32)line_height;
+ line_start = next_start;
+ cur_line++;
+ if(text_y < line_height)
+ {
+ if( cur_line < num_lines )
+ {
+ showExpandText((S32)text_y);
+ }
+ else
+ {
+ hideExpandText();
+ }
+ break;
+ }
+ }
+}
+
+S32 LLExpandableTextBox::LLTextBoxEx::getVerticalTextDelta()
+{
+ S32 text_height = getTextPixelHeight();
+ S32 textbox_height = getRect().getHeight();
+
+ return text_height - textbox_height;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLExpandableTextBox::Params::Params()
+: textbox("textbox")
+, scroll("scroll")
+, max_height("max_height", 0)
+, bg_visible("bg_visible", false)
+, expanded_bg_visible("expanded_bg_visible", true)
+, bg_color("bg_color", LLColor4::black)
+, expanded_bg_color("expanded_bg_color", LLColor4::black)
+{
+}
+
+LLExpandableTextBox::LLExpandableTextBox(const Params& p)
+: LLUICtrl(p)
+, mMaxHeight(p.max_height)
+, mBGVisible(p.bg_visible)
+, mExpandedBGVisible(p.expanded_bg_visible)
+, mBGColor(p.bg_color)
+, mExpandedBGColor(p.expanded_bg_color)
+, mExpanded(false)
+{
+ LLRect rc = getLocalRect();
+
+ LLScrollContainer::Params scroll_params = p.scroll;
+ scroll_params.rect(rc);
+ mScroll = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
+ addChild(mScroll);
+
+ LLTextBoxEx::Params textbox_params = p.textbox;
+ textbox_params.rect(rc);
+ mTextBox = LLUICtrlFactory::create<LLTextBoxEx>(textbox_params);
+ mScroll->addChild(mTextBox);
+
+ updateTextBoxRect();
+
+ mTextBox->setCommitCallback(boost::bind(&LLExpandableTextBox::onExpandClicked, this));
+}
+
+void LLExpandableTextBox::draw()
+{
+ if(mBGVisible && !mExpanded)
+ {
+ gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE);
+ }
+ if(mExpandedBGVisible && mExpanded)
+ {
+ gl_rect_2d(getLocalRect(), mExpandedBGColor.get(), TRUE);
+ }
+
+ collapseIfPosChanged();
+
+ LLUICtrl::draw();
+}
+
+void LLExpandableTextBox::collapseIfPosChanged()
+{
+ if(mExpanded)
+ {
+ LLView* parentp = getParent();
+ LLRect parent_rect = parentp->getRect();
+ parentp->localRectToOtherView(parent_rect, &parent_rect, getRootView());
+
+ if(parent_rect.mLeft != mParentRect.mLeft
+ || parent_rect.mTop != mParentRect.mTop)
+ {
+ collapseTextBox();
+ }
+ }
+}
+
+void LLExpandableTextBox::onExpandClicked()
+{
+ expandTextBox();
+}
+
+void LLExpandableTextBox::updateTextBoxRect()
+{
+ LLRect rc = getLocalRect();
+
+ rc.mLeft += mScroll->getBorderWidth();
+ rc.mRight -= mScroll->getBorderWidth();
+ rc.mTop -= mScroll->getBorderWidth();
+ rc.mBottom += mScroll->getBorderWidth();
+
+ mTextBox->reshape(rc.getWidth(), rc.getHeight());
+ mTextBox->setRect(rc);
+}
+
+S32 LLExpandableTextBox::recalculateTextDelta(S32 text_delta)
+{
+ LLRect expanded_rect = getLocalRect();
+ LLView* root_view = getRootView();
+ LLRect window_rect = root_view->getRect();
+
+ LLRect expanded_screen_rect;
+ localRectToOtherView(expanded_rect, &expanded_screen_rect, root_view);
+
+ // don't allow expanded text box bottom go off screen
+ if(expanded_screen_rect.mBottom - text_delta < window_rect.mBottom)
+ {
+ text_delta = expanded_screen_rect.mBottom - window_rect.mBottom;
+ }
+ // show scroll bar if max_height is valid
+ // and expanded size is greater that max_height
+ else if(mMaxHeight > 0 && expanded_rect.getHeight() + text_delta > mMaxHeight)
+ {
+ text_delta = mMaxHeight - expanded_rect.getHeight();
+ }
+
+ return text_delta;
+}
+
+void LLExpandableTextBox::expandTextBox()
+{
+ S32 text_delta = mTextBox->getVerticalTextDelta();
+ text_delta += mTextBox->getVPad() * 2 + mScroll->getBorderWidth() * 2;
+ // no need to expand
+ if(text_delta <= 0)
+ {
+ return;
+ }
+
+ saveCollapsedState();
+
+ LLRect expanded_rect = getLocalRect();
+ LLRect expanded_screen_rect;
+
+ S32 updated_text_delta = recalculateTextDelta(text_delta);
+ // actual expand
+ expanded_rect.mBottom -= updated_text_delta;
+
+ LLRect text_box_rect = mTextBox->getRect();
+
+ // check if we need to show scrollbar
+ if(text_delta != updated_text_delta)
+ {
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ // disable horizontal scrollbar
+ text_box_rect.mRight -= scrollbar_size;
+ // text box size has changed - redo text wrap
+ mTextBox->setWrappedText(mText, text_box_rect.getWidth());
+ // recalculate text delta since text wrap changed text height
+ text_delta = mTextBox->getVerticalTextDelta() + mTextBox->getVPad() * 2;
+ }
+
+ // expand text
+ text_box_rect.mBottom -= text_delta;
+ mTextBox->reshape(text_box_rect.getWidth(), text_box_rect.getHeight());
+ mTextBox->setRect(text_box_rect);
+
+ // expand text box
+ localRectToOtherView(expanded_rect, &expanded_screen_rect, getParent());
+ reshape(expanded_screen_rect.getWidth(), expanded_screen_rect.getHeight(), FALSE);
+ setRect(expanded_screen_rect);
+
+ setFocus(TRUE);
+ // this lets us receive top_lost event(needed to collapse text box)
+ // it also draws text box above all other ui elements
+ gFocusMgr.setTopCtrl(this);
+
+ mExpanded = true;
+}
+
+void LLExpandableTextBox::collapseTextBox()
+{
+ if(!mExpanded)
+ {
+ return;
+ }
+
+ mExpanded = false;
+
+ reshape(mCollapsedRect.getWidth(), mCollapsedRect.getHeight(), FALSE);
+ setRect(mCollapsedRect);
+
+ updateTextBoxRect();
+
+ mTextBox->setWrappedText(mText);
+ if(gFocusMgr.getTopCtrl() == this)
+ {
+ gFocusMgr.setTopCtrl(NULL);
+ }
+}
+
+void LLExpandableTextBox::onFocusLost()
+{
+ collapseTextBox();
+
+ LLUICtrl::onFocusLost();
+}
+
+void LLExpandableTextBox::onTopLost()
+{
+ collapseTextBox();
+
+ LLUICtrl::onTopLost();
+}
+
+void LLExpandableTextBox::setValue(const LLSD& value)
+{
+ collapseTextBox();
+
+ mText = value.asString();
+ mTextBox->setValue(value);
+}
+
+void LLExpandableTextBox::setText(const std::string& str)
+{
+ collapseTextBox();
+
+ mText = str;
+ mTextBox->setText(str);
+}
+
+void LLExpandableTextBox::saveCollapsedState()
+{
+ mCollapsedRect = getRect();
+
+ mParentRect = getParent()->getRect();
+ // convert parent rect to screen coordinates,
+ // this will allow to track parent's position change
+ getParent()->localRectToOtherView(mParentRect, &mParentRect, getRootView());
+}