summaryrefslogtreecommitdiff
path: root/indra/llui/lltextbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/lltextbase.cpp')
-rw-r--r--indra/llui/lltextbase.cpp236
1 files changed, 148 insertions, 88 deletions
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 49537ef78f..349dbc3405 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -157,6 +157,7 @@ LLTextBase::Params::Params()
read_only("read_only", false),
v_pad("v_pad", 0),
h_pad("h_pad", 0),
+ clip("clip", true),
clip_partial("clip_partial", true),
line_spacing("line_spacing"),
max_text_length("max_length", 255),
@@ -199,6 +200,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mVAlign(p.font_valign),
mLineSpacingMult(p.line_spacing.multiple),
mLineSpacingPixels(p.line_spacing.pixels),
+ mClip(p.clip),
mClipPartial(p.clip_partial && !p.allow_scroll),
mTrackEnd( p.track_end ),
mScrollIndex(-1),
@@ -278,7 +280,7 @@ bool LLTextBase::truncate()
if (getLength() >= S32(mMaxTextByteLength / 4))
{
// Have to check actual byte size
- LLWString text(getWText());
+ LLWString text(getWText());
S32 utf8_byte_size = wstring_utf8_length(text);
if ( utf8_byte_size > mMaxTextByteLength )
{
@@ -334,7 +336,7 @@ void LLTextBase::drawSelectionBackground()
// binary search for line that starts before top of visible buffer
line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom());
- line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
+ line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
bool done = false;
@@ -512,7 +514,6 @@ void LLTextBase::drawText()
selection_right = llmax( mSelectionStart, mSelectionEnd );
}
- LLRect scrolled_view_rect = getVisibleDocumentRect();
std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
S32 first_line = line_range.first;
S32 last_line = line_range.second;
@@ -545,10 +546,9 @@ void LLTextBase::drawText()
line_end = next_start;
}
- LLRect text_rect(line.mRect.mLeft + mVisibleTextRect.mLeft - scrolled_view_rect.mLeft,
- line.mRect.mTop - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom,
- llmin(mDocumentView->getRect().getWidth(), line.mRect.mRight) - scrolled_view_rect.mLeft,
- line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom);
+ LLRect text_rect(line.mRect);
+ text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position
// draw a single line of text
S32 seg_start = line_start;
@@ -654,7 +654,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
}
text.insert(pos, wstr);
- getViewModel()->setDisplay(text);
+ getViewModel()->setDisplay(text);
if ( truncate() )
{
@@ -669,7 +669,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
- LLWString text(getWText());
+ LLWString text(getWText());
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
{
@@ -716,7 +716,7 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
}
text.erase(pos, length);
- getViewModel()->setDisplay(text);
+ getViewModel()->setDisplay(text);
// recreate default segment in case we erased everything
createDefaultSegment();
@@ -733,9 +733,9 @@ S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
{
return 0;
}
- LLWString text(getWText());
+ LLWString text(getWText());
text[pos] = wc;
- getViewModel()->setDisplay(text);
+ getViewModel()->setDisplay(text);
onValueChange(pos, pos + 1);
needsReflow(pos);
@@ -856,7 +856,7 @@ BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
// Did we just click on a link?
if (mURLClickSignal
&& cur_segment->getStyle()
- && cur_segment->getStyle()->isLink())
+ && cur_segment->getStyle()->isLink())
{
// *TODO: send URL here?
(*mURLClickSignal)(this, LLSD() );
@@ -993,14 +993,28 @@ void LLTextBase::draw()
updateScrollFromCursor();
}
- LLRect doc_rect;
+ LLRect text_rect;
if (mScroller)
{
- mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &doc_rect, this);
+ mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this);
}
else
{
- doc_rect = getLocalRect();
+ LLRect visible_lines_rect;
+ std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
+ for (S32 i = line_range.first; i < line_range.second; i++)
+ {
+ if (visible_lines_rect.isEmpty())
+ {
+ visible_lines_rect = mLineInfoList[i].mRect;
+ }
+ else
+ {
+ visible_lines_rect.unionWith(mLineInfoList[i].mRect);
+ }
+ }
+ text_rect = visible_lines_rect;
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom);
}
if (mBGVisible)
@@ -1010,28 +1024,37 @@ void LLTextBase::draw()
LLRect bg_rect = mVisibleTextRect;
if (mScroller)
{
- bg_rect.intersectWith(doc_rect);
+ bg_rect.intersectWith(text_rect);
}
LLColor4 bg_color = mReadOnly
? mReadOnlyBgColor.get()
: hasFocus()
? mFocusBgColor.get()
: mWriteableBgColor.get();
- gl_rect_2d(doc_rect, bg_color % alpha, TRUE);
+ gl_rect_2d(text_rect, bg_color % alpha, TRUE);
}
- // draw document view
- LLUICtrl::draw();
-
- {
- // only clip if we support scrolling...
- // since convention is that text boxes never vertically truncate their contents
- // regardless of rect bounds
- LLLocalClipRect clip(doc_rect, mScroller != NULL);
+ bool should_clip = mClip || mScroller != NULL;
+ { LLLocalClipRect clip(text_rect, should_clip);
+
+ // draw document view
+ if (mScroller)
+ {
+ drawChild(mScroller);
+ }
+ else
+ {
+ drawChild(mDocumentView);
+ }
+
drawSelectionBackground();
drawText();
drawCursor();
}
+
+ mDocumentView->setVisible(FALSE);
+ LLUICtrl::draw();
+ mDocumentView->setVisible(TRUE);
}
@@ -1095,8 +1118,7 @@ void LLTextBase::updateScrollFromCursor()
// scroll so that the cursor is at the top of the page
LLRect scroller_doc_window = getVisibleDocumentRect();
- LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos);
- cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom);
+ LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
}
@@ -1140,7 +1162,7 @@ void LLTextBase::reflow()
S32 first_line = getFirstVisibleLine();
// if scroll anchor not on first line, update it to first character of first line
- if (!mLineInfoList.empty()
+ if ((first_line < mLineInfoList.size())
&& (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
|| mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
{
@@ -1342,9 +1364,9 @@ S32 LLTextBase::getLineStart( S32 line ) const
{
S32 num_lines = getLineCount();
if (num_lines == 0)
- {
+ {
return 0;
- }
+ }
line = llclamp(line, 0, num_lines-1);
return mLineInfoList[line].mDocIndexStart;
@@ -1354,9 +1376,9 @@ S32 LLTextBase::getLineEnd( S32 line ) const
{
S32 num_lines = getLineCount();
if (num_lines == 0)
- {
+ {
return 0;
- }
+ }
line = llclamp(line, 0, num_lines-1);
return mLineInfoList[line].mDocIndexEnd;
@@ -1415,7 +1437,7 @@ S32 LLTextBase::getFirstVisibleLine() const
return iter - mLineInfoList.begin();
}
-std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible)
+std::pair<S32, S32> LLTextBase::getVisibleLines(bool require_fully_visible)
{
LLRect visible_region = getVisibleDocumentRect();
line_list_t::const_iterator first_iter;
@@ -1424,14 +1446,14 @@ std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible)
// make sure we have an up-to-date mLineInfoList
reflow();
- if (fully_visible)
+ if (require_fully_visible)
{
first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top());
- last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom());
+ last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom());
}
else
{
- first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
+ first_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top());
}
return std::pair<S32, S32>(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin());
@@ -1632,8 +1654,11 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
LLUrlMatch match;
std::string text = new_text;
while ( LLUrlRegistry::instance().findUrl(text, match,
- boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )
+ boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )
{
+
+ LLTextUtil::processUrlMatch(&match,this);
+
start = match.getStart();
end = match.getEnd()+1;
@@ -1655,10 +1680,6 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
std::string subtext=text.substr(0,start);
appendAndHighlightText(subtext, part, style_params);
}
-
- // inserts an avatar icon preceding the Url if appropriate
- LLTextUtil::processUrlMatch(&match,this);
-
// output the styled Url
appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
@@ -1926,7 +1947,7 @@ void LLTextBase::setWText(const LLWString& text)
const LLWString& LLTextBase::getWText() const
{
- return getViewModel()->getDisplay();
+ return getViewModel()->getDisplay();
}
// If round is true, if the position is on the right half of a character, the cursor
@@ -1937,9 +1958,12 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
{
// Figure out which line we're nearest to.
LLRect visible_region = getVisibleDocumentRect();
+ LLRect doc_rect = mDocumentView->getRect();
+
+ S32 doc_y = local_y - doc_rect.mBottom;
// binary search for line that starts before local_y
- line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mVisibleTextRect.mBottom + visible_region.mBottom, compare_bottom());
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_y, compare_bottom());
if (line_iter == mLineInfoList.end())
{
@@ -1947,7 +1971,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
}
S32 pos = getLength();
- S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft - visible_region.mLeft;
+ S32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft;
segment_set_t::iterator line_seg_iter;
S32 line_seg_offset;
@@ -1969,7 +1993,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
}
// if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
- if (hit_past_end_of_line && local_y - mVisibleTextRect.mBottom + visible_region.mBottom > line_iter->mRect.mTop)
+ if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop)
{
pos = segment_line_start;
break;
@@ -1998,11 +2022,10 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
pos = segment_line_start + offset;
break;
}
- else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)
+ else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd)
{
// segment wraps to next line, so just set doc pos to the end of the line
- // segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd)
- pos = llmin(getLength(), line_iter->mDocIndexEnd);
+ pos = llclamp(line_iter->mDocIndexEnd - 1, 0, getLength());
break;
}
start_x += text_width;
@@ -2405,10 +2428,37 @@ LLRect LLTextBase::getVisibleDocumentRect() const
{
return mScroller->getVisibleContentRect();
}
- else
+ else if (mClip)
{
- // entire document rect is visible when not scrolling
+ LLRect visible_text_rect = getVisibleTextRect();
+ LLRect doc_rect = mDocumentView->getRect();
+ visible_text_rect.translate(-doc_rect.mLeft, -doc_rect.mBottom);
+
+ // reject partially visible lines
+ LLRect visible_lines_rect;
+ for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end();
+ it != end_it;
+ ++it)
+ {
+ bool line_visible = mClipPartial ? visible_text_rect.contains(it->mRect) : visible_text_rect.overlaps(it->mRect);
+ if (line_visible)
+ {
+ if (visible_lines_rect.isEmpty())
+ {
+ visible_lines_rect = it->mRect;
+ }
+ else
+ {
+ visible_lines_rect.unionWith(it->mRect);
+ }
+ }
+ }
+ return visible_lines_rect;
+ }
+ else
+ { // entire document rect is visible
// but offset according to height of widget
+
LLRect doc_rect = mDocumentView->getLocalRect();
doc_rect.mLeft -= mDocumentView->getRect().mLeft;
// adjust for height of text above widget baseline
@@ -2526,21 +2576,21 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
- if( selection_start > seg_start )
+ if( selection_start > seg_start )
{
// Draw normally
S32 start = seg_start;
S32 end = llmin( selection_start, seg_end );
S32 length = end - start;
font->render(text, start,
- rect,
- color,
- LLFontGL::LEFT, mEditor.mVAlign,
- LLFontGL::NORMAL,
- mStyle->getShadowType(),
- length,
- &right_x,
- mEditor.getUseEllipses());
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
}
rect.mLeft = (S32)ceil(right_x);
@@ -2552,14 +2602,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
S32 length = end - start;
font->render(text, start,
- rect,
- mStyle->getSelectedColor().get(),
- LLFontGL::LEFT, mEditor.mVAlign,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- length,
- &right_x,
- mEditor.getUseEllipses());
+ rect,
+ mStyle->getSelectedColor().get(),
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
}
rect.mLeft = (S32)ceil(right_x);
if( selection_end < seg_end )
@@ -2569,14 +2619,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
S32 end = seg_end;
S32 length = end - start;
font->render(text, start,
- rect,
- color,
- LLFontGL::LEFT, mEditor.mVAlign,
- LLFontGL::NORMAL,
- mStyle->getShadowType(),
- length,
- &right_x,
- mEditor.getUseEllipses());
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
}
return right_x;
}
@@ -2914,11 +2964,18 @@ bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width
S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
{
LLUIImagePtr image = mStyle->getImage();
+
+ if (image.isNull())
+ {
+ return 1;
+ }
+
S32 image_width = image->getWidth();
if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD)
{
return 1;
}
+
return 0;
}
@@ -2928,18 +2985,21 @@ F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 select
{
LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
LLUIImagePtr image = mStyle->getImage();
- S32 style_image_height = image->getHeight();
- S32 style_image_width = image->getWidth();
- // Text is drawn from the top of the draw_rect downward
-
- S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
- // Align image to center of draw rect
- S32 image_bottom = text_center - (style_image_height / 2);
- image->draw(draw_rect.mLeft, image_bottom,
- style_image_width, style_image_height, color);
-
- const S32 IMAGE_HPAD = 3;
- return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+ if (image.notNull())
+ {
+ S32 style_image_height = image->getHeight();
+ S32 style_image_width = image->getWidth();
+ // Text is drawn from the top of the draw_rect downward
+
+ S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
+ // Align image to center of draw rect
+ S32 image_bottom = text_center - (style_image_height / 2);
+ image->draw(draw_rect.mLeft, image_bottom,
+ style_image_width, style_image_height, color);
+
+ const S32 IMAGE_HPAD = 3;
+ return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+ }
}
return 0.0;
}