diff options
Diffstat (limited to 'indra/llui/lltextbase.cpp')
-rw-r--r-- | indra/llui/lltextbase.cpp | 581 |
1 files changed, 222 insertions, 359 deletions
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index b95596fa70..e08026eaf4 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -3,25 +3,31 @@ * @author Martin Reddy * @brief The base class of text box/editor, providing Url handling support * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. + * 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 * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * 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 * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * 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. * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * 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$ */ @@ -34,7 +40,6 @@ #include "llscrollcontainer.h" #include "llstl.h" #include "lltextparser.h" -#include "lltextutil.h" #include "lltooltip.h" #include "lluictrl.h" #include "llurlaction.h" @@ -60,10 +65,7 @@ bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, cons { return a->getStart() < b->getStart(); } - else - { - return a->getEnd() < b->getEnd(); - } + return a->getEnd() < b->getEnd(); } @@ -150,7 +152,6 @@ LLTextBase::Params::Params() bg_writeable_color("bg_writeable_color"), bg_focus_color("bg_focus_color"), allow_scroll("allow_scroll", true), - plain_text("plain_text",false), track_end("track_end", false), read_only("read_only", false), v_pad("v_pad", 0), @@ -171,7 +172,7 @@ LLTextBase::Params::Params() LLTextBase::LLTextBase(const LLTextBase::Params &p) : LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), - mURLClickSignal(NULL), + mURLClickSignal(), mMaxTextByteLength( p.max_text_length ), mDefaultFont(p.font), mFontShadow(p.font_shadow), @@ -191,7 +192,6 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mHPad(p.h_pad), mVPad(p.v_pad), mHAlign(p.font_halign), - mVAlign(p.font_valign), mLineSpacingMult(p.line_spacing.multiple), mLineSpacingPixels(p.line_spacing.pixels), mClipPartial(p.clip_partial && !p.allow_scroll), @@ -200,14 +200,12 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mSelectionStart( 0 ), mSelectionEnd( 0 ), mIsSelecting( FALSE ), - mPlainText ( p.plain_text ), mWordWrap(p.wrap), mUseEllipses( p.use_ellipses ), mParseHTML(p.allow_html), mParseHighlights(p.parse_highlights), mBGVisible(p.bg_visible), - mScroller(NULL), - mStyleDirty(true) + mScroller(NULL) { if(p.allow_scroll) { @@ -246,8 +244,9 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) LLTextBase::~LLTextBase() { + // Menu, like any other LLUICtrl, is deleted by its parent - gMenuHolder + mSegments.clear(); - delete mURLClickSignal; } void LLTextBase::initFromParams(const LLTextBase::Params& p) @@ -293,18 +292,13 @@ bool LLTextBase::truncate() return did_truncate; } -const LLStyle::Params& LLTextBase::getDefaultStyleParams() +LLStyle::Params LLTextBase::getDefaultStyleParams() { - if (mStyleDirty) - { - mDefaultStyle - .color(LLUIColor(&mFgColor)) - .readonly_color(LLUIColor(&mReadOnlyFgColor)) - .font(mDefaultFont) - .drop_shadow(mFontShadow); - mStyleDirty = false; - } - return mDefaultStyle; + return LLStyle::Params() + .color(LLUIColor(&mFgColor)) + .readonly_color(LLUIColor(&mReadOnlyFgColor)) + .font(mDefaultFont) + .drop_shadow(mFontShadow); } void LLTextBase::onValueChange(S32 start, S32 end) @@ -486,9 +480,9 @@ void LLTextBase::drawCursor() text_color = mFgColor.get(); fontp = mDefaultFont; } - fontp->render(text, mCursorPos, cursor_rect, + fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mTop, LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), - LLFontGL::LEFT, mVAlign, + LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, 1); @@ -863,12 +857,11 @@ BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) if (cur_segment && cur_segment->handleMouseUp(x, y, mask)) { // Did we just click on a link? - if (mURLClickSignal - && cur_segment->getStyle() + if (cur_segment->getStyle() && cur_segment->getStyle()->isLink()) { // *TODO: send URL here? - (*mURLClickSignal)(this, LLSD() ); + mURLClickSignal(this, LLSD() ); } return TRUE; } @@ -969,19 +962,18 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) { if (width != getRect().getWidth() || height != getRect().getHeight()) { - bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; - + //EXT-4288 + //to keep consistance scrolling behaviour + //when scrolling from top and from bottom... + bool is_scrolled_to_end = (mScroller!=NULL) && scrolledToEnd(); + LLUICtrl::reshape( width, height, called_from_parent ); - - if (mScroller && scrolled_to_bottom && mTrackEnd) + + if (is_scrolled_to_end) { - // keep bottom of text buffer visible - // do this here as well as in reflow to handle case - // where shrinking from top, which causes buffer to temporarily - // not be scrolled to the bottom, since the scroll index - // specified the _top_ of the visible document region - mScroller->goToBottom(); - } + deselect(); + endOfDoc(); + } // do this first after reshape, because other things depend on // up-to-date mVisibleTextRect @@ -1042,14 +1034,12 @@ void LLTextBase::draw() void LLTextBase::setColor( const LLColor4& c ) { mFgColor = c; - mStyleDirty = true; } //virtual void LLTextBase::setReadOnlyColor(const LLColor4 &c) { mReadOnlyFgColor = c; - mStyleDirty = true; } //virtual @@ -1110,7 +1100,7 @@ S32 LLTextBase::getLeftOffset(S32 width) case LLFontGL::LEFT: return mHPad; case LLFontGL::HCENTER: - return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2); + return mHPad + (mVisibleTextRect.getWidth() - width - mHPad) / 2; case LLFontGL::RIGHT: return mVisibleTextRect.getWidth() - width; default: @@ -1126,48 +1116,8 @@ void LLTextBase::reflow() updateSegments(); - if (mReflowIndex == S32_MAX) - { - return; - } - - bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible - - // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing - cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; - cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; - - S32 first_line = getFirstVisibleLine(); - - // if scroll anchor not on first line, update it to first character of first line - if (!mLineInfoList.empty() - && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart - || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) - { - mScrollIndex = mLineInfoList[first_line].mDocIndexStart; - } - LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); - // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing - first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; - first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; - - S32 reflow_count = 0; while(mReflowIndex < S32_MAX) { - // we can get into an infinite loop if the document height does not monotonically increase - // with decreasing width (embedded ui elements with alternate layouts). In that case, - // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case - // of introducing a vertical scrollbar causing a reflow with less width. We should also always - // use an even number of iterations to avoid user visible oscillation of the layout - if(++reflow_count > 2) - { - lldebugs << "Breaking out of reflow due to possible infinite loop in " << getName() << llendl; - break; - } - S32 start_index = mReflowIndex; mReflowIndex = S32_MAX; @@ -1175,6 +1125,25 @@ void LLTextBase::reflow() // to force inlined widgets with follows set to shrink mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight()); + bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; + + LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos); + bool follow_selection = mVisibleTextRect.overlaps(old_cursor_rect); // cursor is visible + old_cursor_rect.translate(-mVisibleTextRect.mLeft, -mVisibleTextRect.mBottom); + + S32 first_line = getFirstVisibleLine(); + + // if scroll anchor not on first line, update it to first character of first line + if (!mLineInfoList.empty() + && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart + || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) + { + mScrollIndex = mLineInfoList[first_line].mDocIndexStart; + } + LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + // subtract off effect of horizontal scrollbar from local position of first char + first_char_rect.translate(-mVisibleTextRect.mLeft, -mVisibleTextRect.mBottom); + S32 cur_top = 0; segment_set_t::iterator seg_iter = mSegments.begin(); @@ -1216,6 +1185,11 @@ void LLTextBase::reflow() // grow line height as necessary based on reported height of this segment line_height = llmax(line_height, segment_height); remaining_pixels -= segment_width; + if (remaining_pixels < 0) + { + // getNumChars() and getDimensions() should return consistent results + remaining_pixels = 0; + } seg_offset += character_count; @@ -1290,42 +1264,32 @@ void LLTextBase::reflow() segmentp->updateLayout(*this); } - } - // apply scroll constraints after reflowing text - if (!hasMouseCapture() && mScroller) - { - if (scrolled_to_bottom && mTrackEnd) - { - // keep bottom of text buffer visible - endOfDoc(); - } - else if (hasSelection() && follow_selection) + // apply scroll constraints after reflowing text + if (!hasMouseCapture() && mScroller) { - // keep cursor in same vertical position on screen when selecting text - LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); - LLRect old_cursor_rect = cursor_rect; - old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; - old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; - - mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + if (scrolled_to_bottom && mTrackEnd) + { + // keep bottom of text buffer visible + endOfDoc(); + } + else if (hasSelection() && follow_selection) + { + // keep cursor in same vertical position on screen when selecting text + LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); + mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + } + else + { + // keep first line of text visible + LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); + mScroller->scrollToShowRect(new_first_char_rect, first_char_rect); + } } - else - { - // keep first line of text visible - LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); - // pass in desired rect in the coordinate frame of the document viewport - LLRect old_first_char_rect = first_char_rect; - old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop; - old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; - - mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect); - } + // reset desired x cursor position + updateCursorXPos(); } - - // reset desired x cursor position - updateCursorXPos(); } LLRect LLTextBase::getTextBoundingRect() @@ -1493,22 +1457,12 @@ void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) { - if (index > getLength()) { return mSegments.end(); } - - // when there are no segments, we return the end iterator, which must be checked by caller - if (mSegments.size() <= 1) { return mSegments.begin(); } - segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index)); return it; } LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const { - if (index > getLength()) { return mSegments.end(); } - - // when there are no segments, we return the end iterator, which must be checked by caller - if (mSegments.size() <= 1) { return mSegments.begin(); } - LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index)); return it; } @@ -1553,7 +1507,6 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url)); registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url)); registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); - registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url)); registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); @@ -1597,7 +1550,7 @@ std::string LLTextBase::getText() const return getViewModel()->getValue().asString(); } -void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) +void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); style_params.fillFrom(getDefaultStyleParams()); @@ -1633,20 +1586,34 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para part = (S32)LLTextParser::MIDDLE; } std::string subtext=text.substr(0,start); - appendAndHighlightText(subtext, part, style_params); + appendAndHighlightText(subtext, prepend_newline, part, style_params); + prepend_newline = false; } - // inserts an avatar icon preceding the Url if appropriate - LLTextUtil::processUrlMatch(&match,this); + // output an optional icon before the Url + if (! match.getIcon().empty()) + { + LLUIImagePtr image = LLUI::getUIImage(match.getIcon()); + if (image) + { + LLStyle::Params icon; + icon.image = image; + // Text will be replaced during rendering with the icon, + // but string cannot be empty or the segment won't be + // added (or drawn). + appendAndHighlightText(" ", prepend_newline, part, icon); + prepend_newline = false; + } + } // output the styled Url (unless we've been asked to suppress hyperlinking) if (match.isLinkDisabled()) { - appendAndHighlightText(match.getLabel(), part, style_params); + appendAndHighlightText(match.getLabel(), prepend_newline, part, style_params); } else { - appendAndHighlightText(match.getLabel(), part, link_params); + appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params); // set the tooltip for the Url label if (! match.getTooltip().empty()) @@ -1659,6 +1626,8 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para } } } + prepend_newline = false; + // move on to the rest of the text after the Url if (end < (S32)text.length()) { @@ -1671,66 +1640,25 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para break; } } - if (part != (S32)LLTextParser::WHOLE) - part=(S32)LLTextParser::END; - if (end < (S32)text.length()) - appendAndHighlightText(text, part, style_params); + if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; + if (end < (S32)text.length()) appendAndHighlightText(text, prepend_newline, part, style_params); } else { - appendAndHighlightText(new_text, part, style_params); + appendAndHighlightText(new_text, prepend_newline, part, style_params); } } -void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) -{ - if (new_text.empty()) - return; - - if(prepend_newline) - appendLineBreakSegment(input_params); - appendTextImpl(new_text,input_params); -} - void LLTextBase::needsReflow(S32 index) { lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl; mReflowIndex = llmin(mReflowIndex, index); } -void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) +void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params) { - segment_vec_t segments; - LLStyleConstSP sp(new LLStyle(style_params)); - segments.push_back(new LLLineBreakTextSegment(sp, getLength())); + if (new_text.empty()) return; - insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments); -} - -void LLTextBase::appendImageSegment(const LLStyle::Params& style_params) -{ - if(getPlainText()) - { - return; - } - segment_vec_t segments; - LLStyleConstSP sp(new LLStyle(style_params)); - segments.push_back(new LLImageTextSegment(sp, getLength(),*this)); - - insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments); -} - -void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo) -{ - segment_vec_t segments; - LLWString widget_wide_text = utf8str_to_wstring(text); - segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size())); - - insertStringNoUndo(getLength(), widget_wide_text, &segments); -} - -void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params) -{ // Save old state S32 selection_start = mSelectionStart; S32 selection_end = mSelectionEnd; @@ -1743,11 +1671,13 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig setCursorPos(old_length); - if (mParseHighlights) + LLTextParser* highlight = LLTextParser::getInstance(); + + if (mParseHighlights && highlight) { LLStyle::Params highlight_params(style_params); - LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part); + LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part); for (S32 i = 0; i < pieces.size(); i++) { LLSD color_llsd = pieces[i]["color"]; @@ -1756,8 +1686,14 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig highlight_params.color = lcolor; LLWString wide_text; - wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); - + if (prepend_newline && (i == 0 || pieces.size() <= 1 )) + { + wide_text = utf8str_to_wstring(std::string("\n") + pieces[i]["text"].asString()); + } + else + { + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + } S32 cur_length = getLength(); LLStyleConstSP sp(new LLStyle(highlight_params)); LLTextSegmentPtr segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this); @@ -1769,7 +1705,17 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig else { LLWString wide_text; - wide_text = utf8str_to_wstring(new_text); + + // Add carriage return if not first line + if (getLength() != 0 + && prepend_newline) + { + wide_text = utf8str_to_wstring(std::string("\n") + new_text); + } + else + { + wide_text = utf8str_to_wstring(new_text); + } segment_vec_t segments; S32 segment_start = old_length; @@ -1797,29 +1743,11 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig { setCursorPos(cursor_pos); } -} - -void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params) -{ - 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, style_params); - } - appendLineBreakSegment(style_params); - 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, style_params); + //if( !allow_undo ) + //{ + // blockUndo(); + //} } @@ -1887,7 +1815,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, { // Figure out which line we're nearest to. LLRect visible_region = getVisibleDocumentRect(); - + // 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()); @@ -1897,7 +1825,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 = mVisibleTextRect.mLeft + line_iter->mRect.mLeft; segment_set_t::iterator line_seg_iter; S32 line_seg_offset; @@ -1912,19 +1840,14 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, S32 text_width, text_height; bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height); - if(newline) - { - pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); - break; - } - // 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) { pos = segment_line_start; break; } - if (local_x < start_x + text_width) // cursor to left of right edge of text + if (local_x < start_x + text_width // cursor to left of right edge of text + || newline) // or this line ends with a newline, set doc pos to newline char { // Figure out which character we're nearest to. S32 offset; @@ -1948,13 +1871,13 @@ 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 - 1) { - // 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); + // segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) + pos = llmin(getLength(), line_iter->mDocIndexEnd); break; } + start_x += text_width; } @@ -2023,18 +1946,11 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const { - LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); - if (mBorderVisible) - { - content_window_rect.stretch(-1); - } - LLRect local_rect; - if (mLineInfoList.empty()) { // return default height rect in upper left - local_rect = content_window_rect; + local_rect = mVisibleTextRect; local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight()); return local_rect; } @@ -2045,8 +1961,8 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const // compensate for scrolled, inset view of doc LLRect scrolled_view_rect = getVisibleDocumentRect(); local_rect = doc_rect; - local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft, - content_window_rect.mBottom - scrolled_view_rect.mBottom); + local_rect.translate(mVisibleTextRect.mLeft - scrolled_view_rect.mLeft, + mVisibleTextRect.mBottom - scrolled_view_rect.mBottom); return local_rect; } @@ -2277,12 +2193,6 @@ void LLTextBase::updateRects() ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight) : mVisibleTextRect.getWidth(); - if (!mScroller) - { - // push doc rect to top of text widget - doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop); - } - mDocumentView->setShape(doc_rect); //update mVisibleTextRect *after* mDocumentView has been resized @@ -2346,15 +2256,6 @@ LLRect LLTextBase::getVisibleDocumentRect() const } } -boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb) -{ - if (!mURLClickSignal) - { - mURLClickSignal = new commit_signal_t(); - } - return mURLClickSignal->connect(cb); -} - // // LLTextSegment // @@ -2433,6 +2334,25 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec { if( end - start > 0 ) { + if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart)) + { + // ...for images, only render the image, not the underlying text, + // which is only a placeholder space + 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 - (mFontHeight / 2); + // Align image to center of text + 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 drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect); } return draw_rect.mLeft; @@ -2445,6 +2365,11 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele const LLWString &text = mEditor.getWText(); + if ( text[seg_end-1] == '\n' ) + { + --seg_end; + } + F32 right_x = rect.mLeft; if (!mStyle->isVisible()) { @@ -2462,12 +2387,12 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 end = llmin( selection_start, seg_end ); S32 length = end - start; font->render(text, start, - rect, + rect.mLeft, rect.mTop, color, - LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, mStyle->getShadowType(), - length, + length, rect.getWidth(), &right_x, mEditor.getUseEllipses()); } @@ -2481,12 +2406,12 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 length = end - start; font->render(text, start, - rect, + rect.mLeft, rect.mTop, LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ), - LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - length, + length, rect.getWidth(), &right_x, mEditor.getUseEllipses()); } @@ -2498,12 +2423,12 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 end = seg_end; S32 length = end - start; font->render(text, start, - rect, + rect.mLeft, rect.mTop, color, - LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, mStyle->getShadowType(), - length, + length, rect.getWidth(), &right_x, mEditor.getUseEllipses()); } @@ -2603,14 +2528,33 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt { height = 0; width = 0; + bool force_newline = false; if (num_chars > 0) { height = mFontHeight; const LLWString &text = mEditor.getWText(); // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); + llwchar last_char = text[mStart + first_char + num_chars - 1]; + if (last_char == '\n') + { + force_newline = true; + // don't count newline in font width + width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars - 1); + } + else + { + width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); + } } - return false; + + LLUIImagePtr image = mStyle->getImage(); + if( image.notNull()) + { + width += image->getWidth(); + height = llmax(height, image->getHeight()); + } + + return force_newline; } S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const @@ -2633,7 +2577,15 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin num_pixels = llmax(0, num_pixels - image->getWidth()); } - S32 last_char = mEnd; + // search for newline and if found, truncate there + S32 last_char = mStart + segment_offset; + for (; last_char != mEnd; ++last_char) + { + if (text[last_char] == '\n') + { + break; + } + } // set max characters to length of segment, or to first newline max_chars = llmin(max_chars, last_char - (mStart + segment_offset)); @@ -2661,7 +2613,8 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin S32 last_char_in_run = mStart + segment_offset + num_chars; // check length first to avoid indexing off end of string if (last_char_in_run < mEnd - && (last_char_in_run >= mEditor.getLength() )) + && (last_char_in_run >= mEditor.getLength() + || text[last_char_in_run] == '\n')) { num_chars++; } @@ -2756,93 +2709,3 @@ void LLInlineViewSegment::linkToDocument(LLTextBase* editor) { editor->addDocumentChild(mView); } - -LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1) -{ - LLStyleSP s( new LLStyle(LLStyle::Params().visible(true))); - - mFontHeight = llceil(s->getFont()->getLineHeight()); -} -LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1) -{ - mFontHeight = llceil(style->getFont()->getLineHeight()); -} -LLLineBreakTextSegment::~LLLineBreakTextSegment() -{ -} -bool LLLineBreakTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const -{ - width = 0; - height = mFontHeight; - - return true; -} -S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const -{ - return 1; -} -F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) -{ - return draw_rect.mLeft; -} - -LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor) -: LLTextSegment(pos,pos+1), - mStyle( style ), - mEditor(editor) -{ -} - -LLImageTextSegment::~LLImageTextSegment() -{ -} - -static const S32 IMAGE_HPAD = 3; - -bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const -{ - width = 0; - height = llceil(mStyle->getFont()->getLineHeight());; - - LLUIImagePtr image = mStyle->getImage(); - if( num_chars>0 && image.notNull()) - { - width += image->getWidth() + IMAGE_HPAD; - height = llmax(height, image->getHeight() + IMAGE_HPAD ); - } - return false; -} - -S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const -{ - LLUIImagePtr image = mStyle->getImage(); - S32 image_width = image->getWidth(); - if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD) - { - return 1; - } - return 0; -} - -F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) -{ - if ( (start >= 0) && (end <= mEnd - mStart)) - { - 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; - } - return 0.0; -} - |