diff options
author | Mark Palange (Mani) <palange@lindenlab.com> | 2009-10-05 13:39:53 -0700 |
---|---|---|
committer | Mark Palange (Mani) <palange@lindenlab.com> | 2009-10-05 13:39:53 -0700 |
commit | 81a63ac0886a31a566535a3483e5013f5bc0b424 (patch) | |
tree | 1bb0319755a5e26e0bc7f9f20632ae8f31538971 /indra/llui/lltextbox.cpp | |
parent | 0f5bbec3747f3ff2b1d580506d35dc080fcd1a98 (diff) | |
parent | 9818f158366a0df980a2e4b9251177d9a9209cfb (diff) |
merge with latest from lindenlab/svn-imports-viewer-20
Diffstat (limited to 'indra/llui/lltextbox.cpp')
-rw-r--r-- | indra/llui/lltextbox.cpp | 636 |
1 files changed, 25 insertions, 611 deletions
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 132bef0296..3feca136be 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -40,47 +40,10 @@ static LLDefaultChildRegistry::Register<LLTextBox> r("text"); -LLTextBox::Params::Params() -: text_color("text_color"), - length("length"), - type("type"), - 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), - drop_shadow_visible("drop_shadow_visible"), - disabled_color("disabled_color"), - background_color("background_color"), - v_pad("v_pad", 0), - h_pad("h_pad", 0), - line_spacing("line_spacing", 0), - text("text"), - font_shadow("font_shadow", LLFontGL::NO_SHADOW) -{} - LLTextBox::LLTextBox(const LLTextBox::Params& p) -: LLUICtrl(p), - LLTextBase(p), - mBackgroundVisible( p.bg_visible ), - mBorderVisible( p.border_visible ), - mShadowType( p.font_shadow ), - mBorderDropShadowVisible( p.border_drop_shadow_visible ), - mUseEllipses( p.use_ellipses ), - mHPad(p.h_pad), - mVPad(p.v_pad), - mVAlign( LLFontGL::TOP ), - mClickedCallback(NULL), - mTextColor(p.text_color()), - mDisabledColor(p.disabled_color()), - mBackgroundColor(p.background_color()), - mHAlign(p.font_halign), - mLineSpacing(p.line_spacing), - mDidWordWrap(FALSE) -{ - mWordWrap = p.word_wrap; - setText( p.text() ); -} +: LLTextBase(p), + mClickedCallback(NULL) +{} BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) { @@ -101,6 +64,11 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) } } + if (!handled) + { + handled = LLTextBase::handleMouseDown(x, y, mask); + } + return handled; } @@ -125,528 +93,60 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) } // handle clicks on Urls in the textbox first - if (! handleMouseUpOverUrl(x, y)) + handled = LLTextBase::handleMouseUp(x, y, mask); + + // 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 && !handled) { - // 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 && ! handled) - { - mClickedCallback(); - } + mClickedCallback(); } } return handled; } -BOOL LLTextBox::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // pop up a context menu for any Url under the cursor - return handleRightMouseDownOverUrl(this, x, y); -} - -BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask) -{ - // Check to see if we're over an HTML-style link - if (handleHoverOverUrl(x, y)) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - return TRUE; - } - - return LLView::handleHover(x,y,mask); -} - -BOOL LLTextBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) -{ - if (handleToolTipForUrl(this, x, y, msg, sticky_rect_screen)) - { - return TRUE; - } - - return LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen); -} - void LLTextBox::setText(const LLStringExplicit& text) { - if(mWordWrap && !mDidWordWrap) - { - setWrappedText(text); - } - else - { - mText.assign(text); - updateDisplayTextAndSegments(); - } -} - -void LLTextBox::setLineLengths() -{ - mLineLengthList.clear(); + // does string argument insertion + mText.assign(text); - std::string::size_type cur = 0; - std::string::size_type len = mDisplayText.size(); - - while (cur < len) - { - std::string::size_type end = mDisplayText.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 - } - - mLineLengthList.push_back( (S32)runLen ); - } -} - -LLWString LLTextBox::wrapText(const LLWString &wtext, S32 &hoffset, S32 &line_num, F32 max_width) -{ - 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; - } - - bool charsRemaining = true; - LLWString::size_type runLen = end - cur; - if (runLen > 0) - { - // work out how many chars can fit onto the current line - LLWString run(wtext, cur, runLen); - LLWString::size_type useLen = - mDefaultFont->maxDrawableChars(run.c_str(), max_width-hoffset, runLen, TRUE); - charsRemaining = (cur + useLen < len); - - // try to break lines on word boundaries - if (useLen < run.size()) - { - LLWString::size_type prev_use_len = useLen; - while (useLen > 0 && ! isspace(run[useLen-1]) && ! ispunct(run[useLen-1])) - { - --useLen; - } - if (useLen == 0) - { - useLen = prev_use_len; - } - } - - // add the chars that could fit onto one line to our result - final_wtext.append(wtext, cur, useLen); - cur += useLen; - hoffset += mDefaultFont->getWidth(run.substr(0, useLen).c_str()); - - // abort if not enough room to add any more characters - if (useLen == 0) - { - break; - } - } - - if (charsRemaining) - { - if (wtext[cur] == '\n') - { - cur += 1; - } - final_wtext += '\n'; - hoffset = 0; - line_num += 1; - } - } - - return final_wtext; -} - -void LLTextBox::setWrappedText(const LLStringExplicit& in_text, F32 max_width) -{ - mDidWordWrap = TRUE; - setText(wstring_to_utf8str(getWrappedText(in_text, max_width))); -} - -LLWString LLTextBox::getWrappedText(const LLStringExplicit& in_text, F32 max_width) -{ - // - // we don't want to wrap Urls otherwise we won't be able to detect their - // presence for hyperlinking. So we look for all Urls, and then word wrap - // the text before and after, but never break a Url in the middle. We - // also need to consider that the Url will be displayed as a label (not - // necessary the actual Url string). - // - - if (max_width < 0.0f) - { - max_width = (F32)getRect().getWidth(); - } - - LLWString wtext = utf8str_to_wstring(in_text); - LLWString final_wtext; - S32 line_num = 1; - S32 hoffset = 0; - - // find the next Url in the text string - LLUrlMatch match; - while ( LLUrlRegistry::instance().findUrl(wtext, match)) - { - S32 start = match.getStart(); - S32 end = match.getEnd() + 1; - - // perform word wrap on the text before the Url - final_wtext += wrapText(wtext.substr(0, start), hoffset, line_num, max_width); - - // add the Url (but compute width based on its label) - S32 label_width = mDefaultFont->getWidth(match.getLabel()); - if (hoffset > 0 && hoffset + label_width > max_width) - { - final_wtext += '\n'; - line_num++; - hoffset = 0; - } - final_wtext += wtext.substr(start, end-start); - hoffset += label_width; - if (hoffset > max_width) - { - final_wtext += '\n'; - line_num++; - hoffset = 0; - // eat any leading whitespace on the next line - while (isspace(wtext[end]) && end < (S32)wtext.size()) - { - end++; - } - } - - // move on to the rest of the text after the Url - wtext = wtext.substr(end, wtext.size() - end + 1); - } - - final_wtext += wrapText(wtext, hoffset, line_num, max_width); - return final_wtext; + LLTextBase::setText(mText.getString()); } S32 LLTextBox::getTextPixelWidth() { - S32 max_line_width = 0; - if( mLineLengthList.size() > 0 ) - { - S32 cur_pos = 0; - for (std::vector<S32>::iterator iter = mLineLengthList.begin(); - iter != mLineLengthList.end(); ++iter) - { - S32 line_length = *iter; - S32 line_width = mDefaultFont->getWidth( mDisplayText.c_str(), cur_pos, line_length ); - if( line_width > max_line_width ) - { - max_line_width = line_width; - } - cur_pos += line_length+1; - } - } - else - { - max_line_width = mDefaultFont->getWidth(mDisplayText.c_str()); - } - return max_line_width; + return getContentsRect().getWidth(); } S32 LLTextBox::getTextPixelHeight() { - S32 num_lines = mLineLengthList.size(); - if( num_lines < 1 ) - { - num_lines = 1; - } - return (S32)(num_lines * mDefaultFont->getLineHeight()); -} - -void LLTextBox::setValue(const LLSD& value ) -{ - mDidWordWrap = FALSE; - setText(value.asString()); + return getContentsRect().getHeight(); } BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); - updateDisplayTextAndSegments(); - return TRUE; -} - -void LLTextBox::draw() -{ - F32 alpha = getDrawContext().mAlpha; + LLTextBase::setText(mText.getString()); - if (mBorderVisible) - { - gl_rect_2d_offset_local(getLocalRect(), 2, FALSE); - } - - if( mBorderDropShadowVisible ) - { - static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); - static LLUICachedControl<S32> drop_shadow_tooltip ("DropShadowTooltip", 0); - gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow % alpha, drop_shadow_tooltip); - } - - if (mBackgroundVisible) - { - LLRect r( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - gl_rect_2d( r, mBackgroundColor.get() % alpha ); - } - - S32 text_x = 0; - switch( mHAlign ) - { - case LLFontGL::LEFT: - text_x = mHPad; - break; - case LLFontGL::HCENTER: - text_x = getRect().getWidth() / 2; - break; - case LLFontGL::RIGHT: - text_x = getRect().getWidth() - mHPad; - break; - } - - S32 text_y = getRect().getHeight() - mVPad; - - if ( getEnabled() ) - { - drawText( text_x, text_y, mDisplayText, mTextColor.get() ); - } - else - { - drawText( text_x, text_y, mDisplayText, mDisabledColor.get() ); - } - - if (sDebugRects) - { - drawDebugRect(); - } - - //// *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(); - //} + return TRUE; } -void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - // reparse line lengths (don't need to recalculate the display text) - setLineLengths(); - LLView::reshape(width, height, called_from_parent); -} - -void LLTextBox::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ) -{ - 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); - } - 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; - mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha, - mHAlign, mVAlign, - 0, - mShadowType, - line_length, getRect().getWidth(), NULL, mUseEllipses ); - cur_pos += line_length + 1; - S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing; - y -= line_height; - if(y < line_height) - break; - } - } -} void LLTextBox::reshapeToFitText() { - // wrap remaining lines that did not fit on call to setWrappedText() - setLineLengths(); + reflow(); S32 width = getTextPixelWidth(); S32 height = getTextPixelHeight(); - reshape( width + 2 * mHPad, height + 2 * mVPad ); -} - -S32 LLTextBox::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const -{ - // Returns the character offset for the character under the local (x, y) coordinate. - // When round is true, if the position is on the right half of a character, the cursor - // will be put to its right. If round is false, the cursor will always be put to the - // character's left. - - LLRect rect = getLocalRect(); - rect.mLeft += mHPad; - rect.mRight -= mHPad; - rect.mTop += mVPad; - rect.mBottom -= mVPad; - - // Figure out which line we're nearest to. - S32 total_lines = getLineCount(); - S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing; - S32 line = (rect.mTop - 1 - local_y) / line_height; - if (line >= total_lines) - { - return getLength(); // past the end - } - - line = llclamp( line, 0, total_lines ); - S32 line_start = getLineStart(line); - S32 next_start = getLineStart(line+1); - S32 line_end = (next_start != line_start) ? next_start - 1 : getLength(); - if (line_start == -1) - { - return 0; - } - - S32 line_len = line_end - line_start; - S32 pos = mDefaultFont->charFromPixelOffset(mDisplayText.c_str(), line_start, - (F32)(local_x - rect.mLeft), - (F32)rect.getWidth(), - line_len, round); - - return line_start + pos; + reshape( width + 2 * mHPad, height + 2 * mVPad, FALSE ); } -S32 LLTextBox::getLineStart( S32 line ) const -{ - line = llclamp(line, 0, getLineCount()-1); - - S32 result = 0; - for (int i = 0; i < line; i++) - { - result += mLineLengthList[i] + 1 /* add newline */; - } - - return result; -} - -void LLTextBox::updateDisplayTextAndSegments() -{ - // remove any previous segment list - clearSegments(); - - // if URL parsing is turned off, then not much to bo - if (! mParseHTML) - { - mDisplayText = mText.getWString(); - setLineLengths(); - return; - } - - // create unique text segments for Urls - mDisplayText.clear(); - S32 end = 0; - LLUrlMatch match; - LLWString text = mText.getWString(); - - // find the next Url in the text string - while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBox::onUrlLabelUpdated, this, _1, _2)) ) - { - // work out the char offset for the start/end of the url - S32 url_start = match.getStart(); - S32 url_end = match.getEnd(); - - // and the char offset for the label in the display text - S32 seg_start = mDisplayText.size(); - S32 start = seg_start + url_start; - S32 end = start + match.getLabel().size(); - - // create a segment for the text before the Url - mSegments.insert(new LLNormalTextSegment(new LLStyle(), seg_start, start, *this)); - mDisplayText += text.substr(0, url_start); - - // create a segment for the Url text - LLStyleSP html(new LLStyle); - html->setVisible(true); - html->setColor(mLinkColor); - html->mUnderline = TRUE; - html->setLinkHREF(match.getUrl()); - - LLNormalTextSegment *html_seg = new LLNormalTextSegment(html, start, end, *this); - html_seg->setToolTip(match.getTooltip()); - - mSegments.insert(html_seg); - mDisplayText += utf8str_to_wstring(match.getLabel()); - - // move on to the rest of the text after the Url - text = text.substr(url_end+1, text.size() - url_end); - } - - // output a segment for the remaining text - if (text.size() > 0) - { - mSegments.insert(new LLNormalTextSegment(new LLStyle(), end, end + text.size(), *this)); - mDisplayText += text; - } - - // strip whitespace from the end of the text - while (mDisplayText.size() > 0 && isspace(mDisplayText[mDisplayText.size()-1])) - { - mDisplayText = mDisplayText.substr(0, mDisplayText.size() - 1); - - segment_set_t::iterator it = getSegIterContaining(mDisplayText.size()); - if (it != mSegments.end()) - { - LLTextSegmentPtr seg = *it; - seg->setEnd(seg->getEnd()-1); - } - } - - // we may have changed the line lengths, so recalculate them - setLineLengths(); -} void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label) { - if (mDidWordWrap) - { - // re-word wrap as the url label lengths may have changed - setWrappedText(mText.getString()); - } - else - { - // or just update the display text with the latest Url labels - updateDisplayTextAndSegments(); - } + needsReflow(); } bool LLTextBox::isClickable() const @@ -676,89 +176,3 @@ bool LLTextBox::isClickable() const return false; } -void LLTextBox::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text) -{ - 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; - - // render a single line worth for this segment - mDefaultFont->render(text, seg_start, text_x, text_y, color, - mHAlign, mVAlign, 0, mShadowType, clipped_len, - textRect.getWidth(), &text_x, mUseEllipses); - } - - seg_start += clipped_len; - } - } - - // move down one line - text_y -= (F32)line_height; - line_start = next_start; - cur_line++; - } -} |