diff options
author | James Cook <james@lindenlab.com> | 2009-10-03 23:40:28 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2009-10-03 23:40:28 +0000 |
commit | ada0f4fa221f2c7070fb02a2b7ff903bdde11c45 (patch) | |
tree | 0ede83511c304110138c01d16da2fff55162ef31 /indra/llui/lltexteditor.cpp | |
parent | b1a280841e1823a19658923a8eefeb67d1d70735 (diff) |
Merge inspectors UI project, gooey-4, into viewer-2 trunk. Added new tooltips to 3D avatars, 2D avatar names, and 3D objects. Refactors tooltips and text boxes, line editors, and text editors. Breaks LLExpandableTextBox, but a fix is coming.
Resolved conflicts in lltexteditor.cpp, llchatitemscontainerctrl.cpp, llchatmsgbox.cpp, llfloaterbuycurrency.cpp, llnearbychat.cpp, floater_buy_currency.xml, and ru/strings.xml
Merging revisions 134925-135157 of svn+ssh://svn.lindenlab.com/svn/linden/branches/gooey/gooey-4 into C:\source\viewer-2.0.0-3, respecting ancestry
Diffstat (limited to 'indra/llui/lltexteditor.cpp')
-rw-r--r-- | indra/llui/lltexteditor.cpp | 1981 |
1 files changed, 103 insertions, 1878 deletions
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 39f09b297f..997c5b8fa8 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -77,106 +77,31 @@ static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor"); // const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32; const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4; -const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds -const S32 CURSOR_THICKNESS = 2; const S32 SPACES_PER_TAB = 4; - -// helper functors -struct LLTextEditor::compare_bottom -{ - bool operator()(const S32& a, const LLTextEditor::line_info& b) const - { - return a > b.mBottom; // bottom of a is higher than bottom of b - } - - bool operator()(const LLTextEditor::line_info& a, const S32& b) const - { - return a.mBottom > b; // bottom of a is higher than bottom of b - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return a.mBottom > b.mBottom; // bottom of a is higher than bottom of b - } - -}; - -// helper functors -struct LLTextEditor::compare_top -{ - bool operator()(const S32& a, const LLTextEditor::line_info& b) const - { - return a > b.mTop; // top of a is higher than top of b - } - - bool operator()(const LLTextEditor::line_info& a, const S32& b) const - { - return a.mTop > b; // top of a is higher than top of b - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return a.mTop > b.mTop; // top of a is higher than top of b - } -}; - -struct LLTextEditor::line_end_compare -{ - bool operator()(const S32& pos, const LLTextEditor::line_info& info) const - { - return (pos < info.mDocIndexEnd); - } - - bool operator()(const LLTextEditor::line_info& info, const S32& pos) const - { - return (info.mDocIndexEnd < pos); - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return (a.mDocIndexEnd < b.mDocIndexEnd); - } - -}; - -// -// DocumentPanel -// - -class DocumentPanel : public LLPanel -{ -public: - DocumentPanel(const Params&); -}; - -DocumentPanel::DocumentPanel(const Params& p) -: LLPanel(p) -{} - /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd { public: - LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) - : LLTextCmd(pos, group_with_next, segment), mWString(ws) + TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(ws) { } - virtual ~LLTextCmdInsert() {} - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual ~TextCmdInsert() {} + virtual BOOL execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString ); LLWStringUtil::truncate(mWString, *delta); //mWString = wstring_truncate(mWString, *delta); return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { remove(editor, getPosition(), mWString.length() ); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { insert(editor, getPosition(), mWString ); return getPosition() + mWString.length(); @@ -187,11 +112,11 @@ private: }; /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd { public: - LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) - : LLTextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) + TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) { } virtual void blockExtensions() @@ -205,14 +130,14 @@ public: return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); } - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString); LLWStringUtil::truncate(mWString, *delta); //mWString = wstring_truncate(mWString, *delta); return (*delta != 0); } - virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta ) + virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) { LLWString ws; ws += wc; @@ -224,12 +149,12 @@ public: } return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { remove(editor, getPosition(), mWString.length() ); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { insert(editor, getPosition(), mWString ); return getPosition() + mWString.length(); @@ -243,25 +168,25 @@ private: /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdOverwriteChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd { public: - LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) - : LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} + TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) + : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { - mOldChar = editor->getWChar(getPosition()); + mOldChar = editor->getWText()[getPosition()]; overwrite(editor, getPosition(), mChar); *delta = 0; return TRUE; } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { overwrite(editor, getPosition(), mOldChar); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { overwrite(editor, getPosition(), mChar); return getPosition()+1; @@ -274,26 +199,26 @@ private: /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd { public: - LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : - LLTextCmd(pos, group_with_next), mLen(len) + TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : + TextCmd(pos, group_with_next), mLen(len) { std::swap(mSegments, segments); } - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { - mWString = editor->getWSubString(getPosition(), mLen); + mWString = editor->getWText().substr(getPosition(), mLen); *delta = remove(editor, getPosition(), mLen ); return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { insert(editor, getPosition(), mWString); return getPosition() + mWString.length(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { remove(editor, getPosition(), mLen ); return getPosition(); @@ -307,138 +232,61 @@ private: /////////////////////////////////////////////////////////////////// LLTextEditor::Params::Params() : default_text("default_text"), - max_text_length("max_length", 255), - read_only("read_only", false), embedded_items("embedded_items", false), - hide_scrollbar("hide_scrollbar"), - hide_border("hide_border", false), - word_wrap("word_wrap", false), ignore_tab("ignore_tab", true), - track_bottom("track_bottom", false), handle_edit_keys_directly("handle_edit_keys_directly", false), show_line_numbers("show_line_numbers", false), - cursor_color("cursor_color"), default_color("default_color"), - text_color("text_color"), - text_readonly_color("text_readonly_color"), - bg_readonly_color("bg_readonly_color"), - bg_writeable_color("bg_writeable_color"), - bg_focus_color("bg_focus_color"), - link_color("link_color"), - commit_on_focus_lost("commit_on_focus_lost", false), - length("length"), // ignored - type("type"), // ignored - is_unicode("is_unicode")// ignored + commit_on_focus_lost("commit_on_focus_lost", false) {} LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : - LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), LLTextBase(p), - mMaxTextByteLength( p.max_text_length ), mBaseDocIsPristine(TRUE), mPristineCmd( NULL ), mLastCmd( NULL ), - mCursorPos( 0 ), - mIsSelecting( FALSE ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mOnScrollEndData( NULL ), - mCursorColor( p.cursor_color() ), - mFgColor( p.text_color() ), mDefaultColor( p.default_color() ), - mReadOnlyFgColor( p.text_readonly_color() ), - mWriteableBgColor( p.bg_writeable_color() ), - mReadOnlyBgColor( p.bg_readonly_color() ), - mFocusBgColor( p.bg_focus_color() ), - mLinkColor( p.link_color() ), - mReadOnly(p.read_only), mShowLineNumbers ( p.show_line_numbers ), mCommitOnFocusLost( p.commit_on_focus_lost), - mTrackBottom( p.track_bottom ), mAllowEmbeddedItems( p.embedded_items ), mHandleEditKeysDirectly( p.handle_edit_keys_directly ), mMouseDownX(0), mMouseDownY(0), - mLastSelectionX(-1), - mReflowNeeded(FALSE), - mScrollNeeded(FALSE), - mLastSelectionY(-1), - mParseHighlights(FALSE), - mTabsToNextField(p.ignore_tab), - mScrollIndex(-1) + mTabsToNextField(p.ignore_tab) { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - - mWordWrap = p.word_wrap; mDefaultFont = p.font; - mParseHTML = FALSE; mSourceID.generate(); - // reset desired x cursor position - mDesiredXPixel = -1; - - LLScrollContainer::Params scroll_params; - scroll_params.name = "text scroller"; - scroll_params.rect = getLocalRect(); - scroll_params.follows.flags = FOLLOWS_ALL; - scroll_params.is_opaque = false; - scroll_params.mouse_opaque = false; - scroll_params.min_auto_scroll_rate = 200; - scroll_params.max_auto_scroll_rate = 800; - mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params); - addChild(mScroller); - - LLPanel::Params panel_params; - panel_params.name = "text_contents"; - panel_params.rect = LLRect(0, 500, 500, 0); - panel_params.background_visible = true; - panel_params.background_opaque = true; - panel_params.mouse_opaque = false; - - mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params); - mScroller->addChild(mDocumentPanel); - - updateTextRect(); - - static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0); + //FIXME: use image? LLViewBorder::Params params; params.name = "text ed border"; params.rect = getLocalRect(); params.bevel_style = LLViewBorder::BEVEL_IN; - params.border_thickness = text_editor_border; + params.border_thickness = 1; + params.visible = p.border_visible; mBorder = LLUICtrlFactory::create<LLViewBorder> (params); addChild( mBorder ); - mBorder->setVisible(!p.hide_border); - - createDefaultSegment(); - appendText(p.default_text, FALSE, FALSE); + setText(p.default_text()); + if (mShowLineNumbers) + { + mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; + updateTextRect(); + } } void LLTextEditor::initFromParams( const LLTextEditor::Params& p) { - resetDirty(); // Update saved text state - LLUICtrl::initFromParams(p); - // HACK: work around enabled == readonly design bug -- RN - // setEnabled will modify our read only status, so do this after - // LLUICtrl::initFromParams - if (p.read_only.isProvided()) - { - mReadOnly = p.read_only; - } - + LLTextBase::initFromParams(p); + if (p.commit_on_focus_lost.isProvided()) { mCommitOnFocusLost = p.commit_on_focus_lost; } - updateSegments(); updateAllowingLanguageInput(); - - // HACK: text editors always need to be enabled so that we can scroll - LLView::setEnabled(true); } LLTextEditor::~LLTextEditor() @@ -455,282 +303,18 @@ LLTextEditor::~LLTextEditor() std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); } -LLTextViewModel* LLTextEditor::getViewModel() const -{ - return (LLTextViewModel*)mViewModel.get(); -} - -static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow"); -void LLTextEditor::reflow(S32 start_index) -{ - if (!mReflowNeeded) return; - - LLFastTimer ft(FTM_TEXT_REFLOW); - static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0); - - updateSegments(); - - while(mReflowNeeded) - { - bool scrolled_to_bottom = mScroller->isAtBottom(); - mReflowNeeded = FALSE; - - LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos); - bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible - 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); - //first_char_rect.intersectWith(mTextRect); - - S32 cur_top = -texteditor_vpad_top; - - if (getLength()) - { - segment_set_t::iterator seg_iter = mSegments.begin(); - S32 seg_offset = 0; - S32 line_start_index = 0; - S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin - S32 remaining_pixels = text_width; - LLWString text(getWText()); - S32 line_count = 0; - - // find and erase line info structs starting at start_index and going to end of document - if (!mLineInfoList.empty()) - { - // find first element whose end comes after start_index - line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); - line_start_index = iter->mDocIndexStart; - line_count = iter->mLineNum; - getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); - mLineInfoList.erase(iter, mLineInfoList.end()); - } - - // reserve enough space for line numbers - S32 line_height = mShowLineNumbers ? (S32)(LLFontGL::getFontMonospace()->getLineHeight()) : 0; - - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segment = *seg_iter; - - // track maximum height of any segment on this line - line_height = llmax(line_height, segment->getMaxHeight()); - S32 cur_index = segment->getStart() + seg_offset; - // find run of text from this segment that we can display on one line - S32 end_index = cur_index; - while(end_index < segment->getEnd() && text[end_index] != '\n') - { - ++end_index; - } - - // ask segment how many character fit in remaining space - S32 max_characters = end_index - cur_index; - S32 character_count = segment->getNumChars(llmax(0, remaining_pixels), seg_offset, cur_index - line_start_index, max_characters); - - seg_offset += character_count; - - S32 last_segment_char_on_line = segment->getStart() + seg_offset; - - // if we didn't finish the current segment... - if (last_segment_char_on_line < segment->getEnd()) - { - // set up index for next line - // ...skip newline, we don't want to draw - S32 next_line_count = line_count; - if (text[last_segment_char_on_line] == '\n') - { - seg_offset++; - last_segment_char_on_line++; - next_line_count++; - } - - // add line info and keep going - mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); - - line_start_index = segment->getStart() + seg_offset; - cur_top -= line_height; - remaining_pixels = text_width; - line_height = 0; - line_count = next_line_count; - } - // ...just consumed last segment.. - else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) - { - mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); - cur_top -= line_height; - break; - } - // finished a segment and there are segments remaining on this line - else - { - // subtract pixels used and increment segment - remaining_pixels -= segment->getWidth(seg_offset, character_count); - ++seg_iter; - seg_offset = 0; - } - } - } - - // change mDocumentPanel document size to accomodate reflowed text - LLRect document_rect; - document_rect.setOriginAndSize(1, 1, - mScroller->getContentWindowRect().getWidth(), - llmax(mScroller->getContentWindowRect().getHeight(), -cur_top)); - mDocumentPanel->setShape(document_rect); - - // after making document big enough to hold all the text, move the text to fit in the document - if (!mLineInfoList.empty()) - { - S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mTop - texteditor_vpad_top; - // move line segments to fit new document rect - for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) - { - it->mTop += delta_pos; - it->mBottom += delta_pos; - } - } - - // calculate visible region for diplaying text - updateTextRect(); - - for (segment_set_t::iterator segment_it = mSegments.begin(); - segment_it != mSegments.end(); - ++segment_it) - { - LLTextSegmentPtr segmentp = *segment_it; - segmentp->updateLayout(*this); - - } - - // apply scroll constraints after reflowing text - if (!hasMouseCapture()) - { - LLRect visible_content_rect = mScroller->getVisibleContentRect(); - if (scrolled_to_bottom && mTrackBottom) - { - // 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 = getLocalRectFromDocIndex(mCursorPos); - new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); - mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); - //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom); - } - else - { - // keep first line of text visible - LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex); - new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); - mScroller->scrollToShowRect(new_first_char_rect, first_char_rect); - //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom); - } - } - } - - // reset desired x cursor position - updateCursorXPos(); -} - //////////////////////////////////////////////////////////// // LLTextEditor // Public methods -BOOL LLTextEditor::truncate() -{ - BOOL did_truncate = FALSE; - - // First rough check - if we're less than 1/4th the size, we're OK - if (getLength() >= S32(mMaxTextByteLength / 4)) - { - // Have to check actual byte size - LLWString text(getWText()); - S32 utf8_byte_size = wstring_utf8_length(text); - if ( utf8_byte_size > mMaxTextByteLength ) - { - // Truncate safely in UTF-8 - std::string temp_utf8_text = wstring_to_utf8str(text); - temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); - getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text )); - did_truncate = TRUE; - } - } - - return did_truncate; -} - void LLTextEditor::setText(const LLStringExplicit &utf8str) { - // clear out the existing text and segments - clearSegments(); - - getViewModel()->setValue(""); - - truncate(); - blockUndo(); - - createDefaultSegment(); - - startOfDoc(); - deselect(); - - // append the new text (supports Url linking) - std::string text(utf8str); - LLStringUtil::removeCRLF(text); - appendStyledText(text, false, false, LLStyle::Params()); - - needsReflow(); - - resetDirty(); - - onValueChange(0, getLength()); -} - -void LLTextEditor::setWText(const LLWString &wtext) -{ - // clear out the existing text and segments - clearSegments(); - - getViewModel()->setDisplay(LLWString()); - - truncate(); blockUndo(); - - createDefaultSegment(); - - startOfDoc(); deselect(); - // append the new text (supports Url linking) - appendStyledText(wstring_to_utf8str(wtext), false, false, LLStyle::Params()); - - needsReflow(); + LLTextBase::setText(utf8str); resetDirty(); - - onValueChange(0, getLength()); -} - -// virtual -void LLTextEditor::setValue(const LLSD& value) -{ - setText(value.asString()); -} - -std::string LLTextEditor::getText() const -{ - if (mAllowEmbeddedItems) - { - llwarns << "getText() called on text with embedded items (not supported)" << llendl; - } - return getViewModel()->getValue().asString(); } void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap) @@ -828,12 +412,6 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str } } -// Picks a new cursor position based on the screen size of text being drawn. -void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) -{ - setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); -} - S32 LLTextEditor::prevWordPos(S32 cursorPos) const { LLWString wtext(getWText()); @@ -862,60 +440,6 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const return cursorPos; } -S32 LLTextEditor::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; -} - -S32 LLTextEditor::getLineHeight( S32 line ) const -{ - S32 num_lines = getLineCount(); - if (num_lines == 0) - { - return 0; - } - - line = llclamp(line, 0, num_lines-1); - return mLineInfoList[line].mTop - mLineInfoList[line].mBottom; -} - -// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. -void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp, bool include_wordwrap) const -{ - if (mLineInfoList.empty()) - { - *linep = 0; - *offsetp = startpos; - } - else - { - line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); - if (include_wordwrap) - { - *linep = iter - mLineInfoList.begin(); - } - else - { - if (iter == mLineInfoList.end()) - { - *linep = mLineInfoList.back().mLineNum; - } - else - { - *linep = iter->mLineNum; - } - } - *offsetp = startpos - iter->mDocIndexStart; - } -} - const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const { // find segment index at character to left of cursor (or rightmost edge of selection) @@ -957,201 +481,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, } } -// If 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. - -S32 LLTextEditor::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const -{ - // Figure out which line we're nearest to. - LLRect visible_region = mScroller->getVisibleContentRect(); - - // 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 - mTextRect.mBottom + visible_region.mBottom, compare_bottom()); - - if (line_iter == mLineInfoList.end()) - { - return getLength(); // past the end - } - - S32 pos = getLength(); - S32 start_x = mTextRect.mLeft; - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - line_seg_iter != mSegments.end(); - ++line_seg_iter, line_seg_offset = 0) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - S32 segment_line_start = segmentp->getStart() + line_seg_offset; - S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd - 1) - segment_line_start; - S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length); - if (local_x < start_x + text_width // cursor to left of right edge of text - || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line - { - // Figure out which character we're nearest to. - S32 offset; - if (!segmentp->canEdit()) - { - S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart()); - if (round && local_x - start_x > segment_width / 2) - { - offset = segment_line_length; - } - else - { - offset = 0; - } - } - else - { - offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); - } - pos = segment_line_start + offset; - break; - } - start_x += text_width; - } - - return pos; -} - -LLRect LLTextEditor::getLocalRectFromDocIndex(S32 pos) const -{ - LLRect local_rect(mTextRect); - local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight()); - if (mLineInfoList.empty()) - { - return local_rect; - } - - // clamp pos to valid values - pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); - - - // find line that contains cursor - line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); - - LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); - local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft; - local_rect.mBottom = mTextRect.mBottom + (line_iter->mBottom - scrolled_view_rect.mBottom); - local_rect.mTop = mTextRect.mBottom + (line_iter->mTop - scrolled_view_rect.mBottom); - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - segment_set_t::iterator cursor_seg_iter; - S32 cursor_seg_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); - - while(line_seg_iter != mSegments.end()) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - if (line_seg_iter == cursor_seg_iter) - { - // cursor advanced to right based on difference in offset of cursor to start of line - local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset); - - break; - } - else - { - // add remainder of current text segment to cursor position - local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset); - // offset will be 0 for all segments after the first - line_seg_offset = 0; - // go to next text segment on this line - ++line_seg_iter; - } - } - - local_rect.mRight = local_rect.mLeft; - - return local_rect; -} - -void LLTextEditor::addDocumentChild(LLView* view) -{ - mDocumentPanel->addChild(view); -} - -void LLTextEditor::removeDocumentChild(LLView* view) -{ - mDocumentPanel->removeChild(view); -} - -bool LLTextEditor::setCursor(S32 row, S32 column) -{ - if (0 <= row && row < (S32)mLineInfoList.size()) - { - S32 doc_pos = mLineInfoList[row].mDocIndexStart; - column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); - doc_pos += column; - updateCursorXPos(); - - return setCursorPos(doc_pos); - } - return false; -} - -bool LLTextEditor::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) -{ - S32 new_cursor_pos = cursor_pos; - if (new_cursor_pos != mCursorPos) - { - new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); - } - - mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); - needsScroll(); - if (!keep_cursor_offset) - updateCursorXPos(); - // did we get requested position? - return new_cursor_pos == cursor_pos; -} - -void LLTextEditor::updateCursorXPos() -{ - // reset desired x cursor position - mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; -} - -// constraint cursor to editable segments of document -// NOTE: index must be within document range -S32 LLTextEditor::getEditableIndex(S32 index, bool increasing_direction) -{ - segment_set_t::iterator segment_iter; - S32 offset; - getSegmentAndOffset(index, &segment_iter, &offset); - - LLTextSegmentPtr segmentp = *segment_iter; - - if (segmentp->canEdit()) - { - return segmentp->getStart() + offset; - } - else if (segmentp->getStart() < index && index < segmentp->getEnd()) - { - // bias towards document end - if (increasing_direction) - { - return segmentp->getEnd(); - } - // bias towards document start - else - { - return segmentp->getStart(); - } - } - else - { - return index; - } -} - // virtual BOOL LLTextEditor::canDeselect() const { @@ -1167,25 +496,6 @@ void LLTextEditor::deselect() } -void LLTextEditor::startSelection() -{ - if( !mIsSelecting ) - { - mIsSelecting = TRUE; - mSelectionStart = mCursorPos; - mSelectionEnd = mCursorPos; - } -} - -void LLTextEditor::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = FALSE; - mSelectionEnd = mCursorPos; - } -} - BOOL LLTextEditor::selectionContainsLineBreaks() { if (hasSelection()) @@ -1334,23 +644,12 @@ void LLTextEditor::selectAll() setCursorPos(mSelectionEnd); } - -BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) -{ - if (childrenHandleToolTip(x, y, msg, sticky_rect_screen)) - { - return TRUE; - } - - return handleToolTipForUrl(this, x, y, msg, sticky_rect_screen); -} - BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; // Let scrollbar have first dibs - handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + handled = LLTextBase::handleMouseDown(x, y, mask); if( !handled ) { @@ -1398,7 +697,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); return handled; } @@ -1407,7 +706,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; + handled = LLTextBase::handleMouseDown(x, y, mask); if (!handled) { @@ -1424,19 +723,12 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); BOOL handled = FALSE; if(hasMouseCapture() ) { if( mIsSelecting ) { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } - mScroller->autoScroll(x, y); S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); @@ -1453,32 +745,19 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) if( !handled ) { // Pass to children - handled = LLView::childrenHandleHover(x, y, mask) != NULL; + handled = LLTextBase::handleHover(x, y, mask); } if( handled ) { // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); } - // Opaque if( !handled ) { - // Check to see if we're over an HTML-style link - handled = handleHoverOverUrl(x, y); - if( handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - } - - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = TRUE; - } + getWindow()->setCursor(UI_CURSOR_IBEAM); + handled = TRUE; } return handled; @@ -1489,8 +768,12 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - // let scrollbar have first dibs - handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL; + // if I'm not currently selecting text + if (!(hasSelection() && hasMouseCapture())) + { + // let text segments handle mouse event + handled = LLTextBase::handleMouseUp(x, y, mask); + } if( !handled ) { @@ -1503,11 +786,6 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) endSelection(); } - if( !hasSelection() && hasMouseCapture() ) - { - handleMouseUpOverUrl(x, y); - } - // take selection to 'primary' clipboard updatePrimary(); @@ -1515,7 +793,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) } // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); if( hasMouseCapture() ) { @@ -1532,8 +810,8 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - // let scrollbar have first dibs - handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; + // let scrollbar and text segments have first dibs + handled = LLTextBase::handleDoubleClick(x, y, mask); if( !handled ) { @@ -1571,7 +849,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) mIsSelecting = FALSE; // delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); // take selection to 'primary' clipboard updatePrimary(); @@ -1583,35 +861,18 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) } -// Allow calling cards to be dropped onto text fields. Append the name and -// a carriage return. -// virtual -BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, void *cargo_data, - EAcceptance *accept, - std::string& tooltip_msg) -{ - *accept = ACCEPT_NO; - - return TRUE; -} - //---------------------------------------------------------------------------- // Returns change in number of characters in mText -S32 LLTextEditor::execute( LLTextCmd* cmd ) +S32 LLTextEditor::execute( TextCmd* cmd ) { S32 delta = 0; if( cmd->execute(this, &delta) ) { // Delete top of undo stack undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); - if (enditer != mUndoStack.begin()) - { - --enditer; - std::for_each(mUndoStack.begin(), enditer, DeletePointer()); - mUndoStack.erase(mUndoStack.begin(), enditer); - } + std::for_each(mUndoStack.begin(), enditer, DeletePointer()); + mUndoStack.erase(mUndoStack.begin(), enditer); // Push the new command is now on the top (front) of the undo stack. mUndoStack.push_front(cmd); mLastCmd = cmd; @@ -1627,7 +888,7 @@ S32 LLTextEditor::execute( LLTextCmd* cmd ) S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) { - return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr, segment ) ); + return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) ); } S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) @@ -1638,12 +899,7 @@ S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) // store text segments getSegmentsInRange(segments_to_remove, pos, pos + length, false); - return execute( new LLTextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); -} - -S32 LLTextEditor::append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) -{ - return insert(getLength(), wstr, group_with_next_op, segment); + return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); } S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) @@ -1654,7 +910,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) } else { - return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc)); + return execute(new TextCmdOverwriteChar(pos, FALSE, wc)); } } @@ -1674,8 +930,7 @@ void LLTextEditor::removeCharOrTab() if (text[mCursorPos - 1] == ' ') { // Try to remove a "tab" - S32 line, offset; - getLineAndOffset(mCursorPos, &line, &offset); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); if (offset > 0) { chars_to_remove = offset % SPACES_PER_TAB; @@ -1749,7 +1004,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc) } else { - return execute(new LLTextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); + return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); } } @@ -2349,8 +1604,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return deleteSelection(FALSE); } - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB); for( S32 i=0; i < spaces_needed; i++ ) @@ -2481,7 +1735,7 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) if( handled ) { - resetKeystrokeTimer(); + resetCursorBlink(); // Most keystrokes will make the selection box go away, but not all will. if( !selection_modified && @@ -2534,7 +1788,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) if( handled ) { - resetKeystrokeTimer(); + resetCursorBlink(); // Most keystrokes will make the selection box go away, but not all will. deselect(); @@ -2573,8 +1827,7 @@ void LLTextEditor::doDelete() if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) ) { // Try to remove a full tab's worth of spaces - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB); if( chars_to_remove == 0 ) { @@ -2694,7 +1947,7 @@ void LLTextEditor::redo() void LLTextEditor::onFocusReceived() { - LLUICtrl::onFocusReceived(); + LLTextBase::onFocusReceived(); updateAllowingLanguageInput(); } @@ -2717,19 +1970,19 @@ void LLTextEditor::onFocusLost() // Make sure cursor is shown again getWindow()->showCursorFromMouseMove(); - LLUICtrl::onFocusLost(); + LLTextBase::onFocusLost(); } void LLTextEditor::onCommit() { setControlValue(getValue()); - LLUICtrl::onCommit(); + LLTextBase::onCommit(); } void LLTextEditor::setEnabled(BOOL enabled) { // just treat enabled as read-only flag - BOOL read_only = !enabled; + bool read_only = !enabled; if (read_only != mReadOnly) { mReadOnly = read_only; @@ -2738,195 +1991,6 @@ void LLTextEditor::setEnabled(BOOL enabled) } } -void LLTextEditor::drawBackground() -{ - S32 left = 0; - S32 top = getRect().getHeight(); - S32 bottom = 0; - - LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get() - : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - if( mShowLineNumbers ) { - gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only - gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator - } -} - -// Draws the black box behind the selected text -void LLTextEditor::drawSelectionBackground() -{ - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection() && !mLineInfoList.empty()) - { - LLWString text = getWText(); - std::vector<LLRect> selection_rects; - - S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); - LLRect selection_rect = mTextRect; - - // Skip through the lines we aren't drawing. - LLRect content_display_rect = mScroller->getVisibleContentRect(); - - // 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()); - - bool done = false; - - // Find the coordinates of the selected area - for (;line_iter != end_iter && !done; ++line_iter) - { - // is selection visible on this line? - if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) - { - segment_set_t::iterator segment_iter; - S32 segment_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - - LLRect selection_rect; - selection_rect.mLeft = 0; - selection_rect.mRight = 0; - selection_rect.mBottom = line_iter->mBottom; - selection_rect.mTop = line_iter->mTop; - - for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) - { - LLTextSegmentPtr segmentp = *segment_iter; - - S32 segment_line_start = segmentp->getStart() + segment_offset; - S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - - // if selection after beginning of segment - if(selection_left >= segment_line_start) - { - S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; - selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars); - } - - // if selection spans end of current segment... - if (selection_right > segment_line_end) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start); - } - // else if selection ends on current segment... - else - { - S32 num_chars = selection_right - segment_line_start; - selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars); - - break; - } - } - selection_rects.push_back(selection_rect); - } - } - - // Draw the selection box (we're using a box instead of reversing the colors on the selected text). - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); - F32 alpha = hasFocus() ? 0.7f : 0.3f; - gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); - - for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); - rect_it != selection_rects.end(); - ++rect_it) - { - LLRect selection_rect = *rect_it; - selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom); - gl_rect_2d(selection_rect); - } - } -} - -void LLTextEditor::drawCursor() -{ - if( hasFocus() - && gFocusMgr.getAppHasFocus() - && !mReadOnly) - { - LLWString wtext = getWText(); - const llwchar* text = wtext.c_str(); - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - cursor_rect.translate(-1, 0); - segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); - - // take style from last segment - LLTextSegmentPtr segmentp; - - if (seg_it != mSegments.end()) - { - segmentp = *seg_it; - } - else - { - //segmentp = mSegments.back(); - return; - } - - // Draw the cursor - // (Flash the cursor every half second starting a fixed time after the last keystroke) - F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) - { - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1)); - cursor_rect.mRight = cursor_rect.mLeft + width; - } - else - { - cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.color4fv( mCursorColor.get().mV ); - - gl_rect_2d(cursor_rect); - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') - { - LLColor4 text_color; - const LLFontGL* fontp; - if (segmentp) - { - text_color = segmentp->getColor(); - fontp = segmentp->getStyle()->getFont(); - } - else if (mReadOnly) - { - text_color = mReadOnlyFgColor.get(); - fontp = mDefaultFont; - } - else - { - text_color = mFgColor.get(); - fontp = mDefaultFont; - } - fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mBottom, - LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - 1); - } - - // Make sure the IME is in the right place - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); - - ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } - } -} - void LLTextEditor::drawPreeditMarker() { static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); @@ -3032,96 +2096,6 @@ void LLTextEditor::drawPreeditMarker() } -void LLTextEditor::drawText() -{ - LLWString text = getWText(); - const S32 text_len = getLength(); - if( text_len <= 0 ) - { - return; - } - S32 selection_left = -1; - S32 selection_right = -1; - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection()) - { - selection_left = llmin( mSelectionStart, mSelectionEnd ); - selection_right = llmax( mSelectionStart, mSelectionEnd ); - } - - LLGLSUIDefault gls_ui; - LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); - LLRect content_rect = mScroller->getContentWindowRect(); - S32 first_line = getFirstVisibleLine(); - S32 num_lines = getLineCount(); - if (first_line >= num_lines) - { - return; - } - - S32 line_start = getLineStart(first_line); - // find first text segment that spans top of visible portion of text buffer - segment_set_t::iterator seg_iter = getSegIterContaining(line_start); - if (seg_iter == mSegments.end()) - { - return; - } - - LLTextSegmentPtr cur_segment = *seg_iter; - - for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) - { - line_info& line = mLineInfoList[cur_line]; - - if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) - { - break; - } - - 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; - } - - LLRect text_rect(mTextRect.mLeft - scrolled_view_rect.mLeft, - line.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom, - mTextRect.getWidth() - scrolled_view_rect.mLeft, - line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom); - - // draw a single line of text - S32 seg_start = line_start; - while( seg_start < line_end ) - { - while( cur_segment->getEnd() <= seg_start ) - { - seg_iter++; - if (seg_iter == mSegments.end()) - { - llwarns << "Ran off the segmentation end!" << llendl; - - return; - } - cur_segment = *seg_iter; - } - - S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart(); - text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect)); - - seg_start = clipped_end + cur_segment->getStart(); - } - - line_start = next_start; - } -} - void LLTextEditor::drawLineNumbers() { LLGLSUIDefault gls_ui; @@ -3136,24 +2110,31 @@ void LLTextEditor::drawLineNumbers() return; } - S32 cursor_line = getCurrentLine(); + S32 cursor_line = getLineNumFromDocIndex(mCursorPos); if (mShowLineNumbers) { + S32 left = 0; + S32 top = getRect().getHeight(); + S32 bottom = 0; + + gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only + gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator + S32 last_line_num = -1; for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) { line_info& line = mLineInfoList[cur_line]; - if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) { break; } - S32 line_bottom = line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; + S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; // draw the line numbers - if(line.mLineNum != last_line_num && line.mTop <= scrolled_view_rect.mTop) + if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop) { const LLFontGL *num_font = LLFontGL::getFontMonospace(); const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum )); @@ -3180,58 +2161,23 @@ void LLTextEditor::drawLineNumbers() void LLTextEditor::draw() { - // reflow if needed, on demand - reflow(); - - // then update scroll position, as cursor may have moved - updateScrollFromCursor(); - - LLColor4 bg_color = mReadOnly - ? mReadOnlyBgColor.get() - : hasFocus() - ? mFocusBgColor.get() - : mWriteableBgColor.get(); - - mDocumentPanel->setBackgroundColor(bg_color); - - LLView::draw(); - drawBackground(); //overlays scrolling panel bg - drawLineNumbers(); - { // pad clipping rectangle so that cursor can draw at full width // when at left edge of mTextRect LLRect clip_rect(mTextRect); clip_rect.stretch(1); LLLocalClipRect clip(clip_rect); - drawSelectionBackground(); drawPreeditMarker(); - drawText(); - drawCursor(); } + LLTextBase::draw(); + drawLineNumbers(); + //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret // when in readonly mode mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly); } - -S32 LLTextEditor::getFirstVisibleLine() const -{ - LLRect visible_region = mScroller->getVisibleContentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); - - return iter - mLineInfoList.begin(); -} - -// virtual -void LLTextEditor::clear() -{ - setText(LLStringUtil::null); -} - // Start or stop the editor from accepting text-editing keystrokes // see also LLLineEditor void LLTextEditor::setFocus( BOOL new_state ) @@ -3247,7 +2193,7 @@ void LLTextEditor::setFocus( BOOL new_state ) getWindow()->allowLanguageTextInput(this, FALSE); } - LLUICtrl::setFocus( new_state ); + LLTextBase::setFocus( new_state ); if( new_state ) { @@ -3255,7 +2201,7 @@ void LLTextEditor::setFocus( BOOL new_state ) gEditMenuHandler = this; // Don't start the cursor flashing right away - resetKeystrokeTimer(); + resetCursorBlink(); } else { @@ -3269,96 +2215,6 @@ void LLTextEditor::setFocus( BOOL new_state ) } } -// virtual -BOOL LLTextEditor::acceptsTextInput() const -{ - return !mReadOnly; -} - -// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text. -S32 LLTextEditor::getPos( S32 line, S32 offset ) -{ - S32 line_start = getLineStart(line); - S32 next_start = getLineStart(line+1); - if (next_start == line_start) - { - next_start = getLength() + 1; - } - S32 line_length = next_start - line_start - 1; - line_length = llmax(line_length, 0); - return line_start + llmin( offset, line_length ); -} - - -void LLTextEditor::changePage( S32 delta ) -{ - const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; - if (delta == 0) return; - - //RN: use pixel heights - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - - if( delta == -1 ) - { - mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - else - if( delta == 1 ) - { - mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - - if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) - { - // cursor didn't change apparent position, so move to top or bottom of document, respectively - if (delta < 0) - { - startOfDoc(); - } - else - { - endOfDoc(); - } - } - else - { - setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); - } -} - -void LLTextEditor::changeLine( S32 delta ) -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - S32 new_line = line; - if( (delta < 0) && (line > 0 ) ) - { - new_line = line - 1; - } - else if( (delta > 0) && (line < (getLineCount() - 1)) ) - { - new_line = line + 1; - } - - LLRect visible_region = mScroller->getVisibleContentRect(); - - S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE); - setCursorPos(new_cursor_pos, true); -} - - -void LLTextEditor::startOfLine() -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - setCursorPos(mCursorPos - offset); -} - - // public void LLTextEditor::setCursorAndScrollToEnd() { @@ -3366,92 +2222,16 @@ void LLTextEditor::setCursorAndScrollToEnd() endOfDoc(); } -void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) -{ - getLineAndOffset( mCursorPos, line, col, include_wordwrap ); -} - void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ) { - getLineAndColumnForPosition(mCursorPos, line, col, include_wordwrap); -} - -S32 LLTextEditor::getCurrentLine() -{ - return getLineForPosition(mCursorPos); -} - -S32 LLTextEditor::getLineForPosition(S32 position) -{ - S32 line, col; - getLineAndColumnForPosition(position, &line, &col, FALSE); - return line; -} - - -void LLTextEditor::endOfLine() -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - S32 num_lines = getLineCount(); - if (line + 1 >= num_lines) - { - setCursorPos(getLength()); - } - else - { - setCursorPos( getLineStart(line + 1) - 1 ); - } -} - -void LLTextEditor::startOfDoc() -{ - setCursorPos(0); -} - -void LLTextEditor::endOfDoc() -{ - setCursorPos(getLength()); -} - -// Sets the scrollbar from the cursor position -void LLTextEditor::updateScrollFromCursor() -{ - // Update scroll position even in read-only mode (when there's no cursor displayed) - // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736. - - if (!mScrollNeeded) - { - return; - } - mScrollNeeded = FALSE; - - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - // scroll so that the cursor is at the top of the page - LLRect scroller_doc_window = mScroller->getVisibleContentRect(); - LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); - cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom); - mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); -} - -void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLView::reshape( width, height, called_from_parent ); - - // do this first after reshape, because other things depend on - // up-to-date mTextRect - updateTextRect(); - - needsReflow(); + *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); + *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); } void LLTextEditor::autoIndent() { // Count the number of spaces in the current line - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 line = getLineNumFromDocIndex(mCursorPos); S32 line_start = getLineStart(line); S32 space_count = 0; S32 i; @@ -3496,221 +2276,6 @@ void LLTextEditor::insertText(const std::string &new_text) setEnabled( enabled ); } - -void LLTextEditor::appendColoredText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - const LLColor4 &color, - const std::string& font_name) -{ - LLColor4 lcolor=color; - if (mParseHighlights) - { - LLTextParser* highlight = LLTextParser::getInstance(); - highlight->parseFullLineHighlights(new_text, &lcolor); - } - - LLStyle::Params style_params; - style_params.color = lcolor; - if (font_name.empty()) - { - style_params.font = mDefaultFont; - } - else - { - style_params.font.name = font_name; - } - appendStyledText(new_text, allow_undo, prepend_newline, style_params); -} - -void LLTextEditor::appendStyledText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - const LLStyle::Params& style_params) -{ - S32 part = (S32)LLTextParser::WHOLE; - if(mParseHTML) - { - - S32 start=0,end=0; - LLUrlMatch match; - std::string text = new_text; - while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextEditor::onUrlLabelUpdated, this, _1, _2)) ) - { - start = match.getStart(); - end = match.getEnd()+1; - - LLStyle::Params link_params = style_params; - link_params.color = mLinkColor; - link_params.font.style = "UNDERLINE"; - link_params.link_href = match.getUrl(); - - // output the text before the Url - if (start > 0) - { - if (part == (S32)LLTextParser::WHOLE || - part == (S32)LLTextParser::START) - { - part = (S32)LLTextParser::START; - } - else - { - part = (S32)LLTextParser::MIDDLE; - } - std::string subtext=text.substr(0,start); - appendHighlightedText(subtext,allow_undo, prepend_newline, part, style_params); - prepend_newline = false; - } - - // output the styled Url - appendText(match.getLabel(),allow_undo, prepend_newline, link_params); - prepend_newline = false; - - // set the tooltip for the Url label - if (! match.getTooltip().empty()) - { - segment_set_t::iterator it = getSegIterContaining(getLength()-1); - if (it != mSegments.end()) - { - LLTextSegmentPtr segment = *it; - segment->setToolTip(match.getTooltip()); - } - } - - // output an optional icon after the Url - if (! match.getIcon().empty()) - { - LLUIImagePtr image = LLUI::getUIImage(match.getIcon()); - if (image) - { - LLStyle::Params icon; - icon.image = image; - // TODO: fix spacing of images and remove the fixed char spacing - appendText(" ", allow_undo, prepend_newline, icon); - } - } - - // move on to the rest of the text after the Url - if (end < (S32)text.length()) - { - text = text.substr(end,text.length() - end); - end=0; - part=(S32)LLTextParser::END; - } - else - { - break; - } - } - if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; - if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, style_params); - } - else - { - appendHighlightedText(new_text, allow_undo, prepend_newline, part, style_params); - } -} - -void LLTextEditor::appendHighlightedText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - S32 highlight_part, - const LLStyle::Params& style_params) -{ - if (mParseHighlights) - { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && !style_params.isDefault()) - { - LLStyle::Params highlight_params = style_params; - - LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), highlight_part); - bool lprepend=prepend_newline; - for (S32 i=0;i<pieces.size();i++) - { - LLSD color_llsd = pieces[i]["color"]; - LLColor4 lcolor; - lcolor.setValue(color_llsd); - highlight_params.color = lcolor; - if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params); - } - return; - } - } - appendText(new_text, allow_undo, prepend_newline, style_params); -} - -// Appends new text to end of document -void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, - const LLStyle::Params& stylep) -{ - if (new_text.empty()) return; - - // Save old state - S32 selection_start = mSelectionStart; - S32 selection_end = mSelectionEnd; - BOOL was_selecting = mIsSelecting; - S32 cursor_pos = mCursorPos; - S32 old_length = getLength(); - BOOL cursor_was_at_end = (mCursorPos == old_length); - - deselect(); - - setCursorPos(old_length); - - LLWString wide_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); - } - - LLTextSegmentPtr segmentp; - if (!stylep.isDefault()) - { - S32 segment_start = old_length; - S32 segment_end = old_length + wide_text.size(); - segmentp = new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ); - } - - append(wide_text, TRUE, segmentp); - - needsReflow(); - - // Set the cursor and scroll position - if( selection_start != selection_end ) - { - mSelectionStart = selection_start; - mSelectionEnd = selection_end; - - mIsSelecting = was_selecting; - setCursorPos(cursor_pos); - } - else if( cursor_was_at_end ) - { - setCursorPos(getLength()); - } - else - { - setCursorPos(cursor_pos); - } - - if( !allow_undo ) - { - blockUndo(); - } -} - - void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline) { // Save old state @@ -3739,7 +2304,7 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, } LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size()); - append(widget_wide_text, FALSE, segment); + insert(getLength(), widget_wide_text, FALSE, segment); needsReflow(); @@ -3767,12 +2332,6 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, } } -void LLTextEditor::onUrlLabelUpdated(const std::string &url, - const std::string &label) -{ - // LLUrlRegistry has given us a new label for one of our Urls - replaceUrlLabel(url, label); -} void LLTextEditor::replaceUrlLabel(const std::string &url, const std::string &label) @@ -3830,164 +2389,10 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) mSelectionStart = llclamp(mSelectionStart, 0, len); mSelectionEnd = llclamp(mSelectionEnd, 0, len); - reflow(); + needsReflow(); needsScroll(); } -/////////////////////////////////////////////////////////////////// -// Returns change in number of characters in mWText - -S32 LLTextEditor::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextEditor::segment_vec_t* segments ) -{ - LLWString text(getWText()); - S32 old_len = text.length(); // length() returns character length - S32 insert_len = wstr.length(); - - pos = getEditableIndex(pos, true); - - segment_set_t::iterator seg_iter = getSegIterContaining(pos); - - LLTextSegmentPtr default_segment; - - LLTextSegmentPtr segmentp; - if (seg_iter != mSegments.end()) - { - segmentp = *seg_iter; - } - else - { - //segmentp = mSegments.back(); - return pos; - } - - if (segmentp->canEdit()) - { - segmentp->setEnd(segmentp->getEnd() + insert_len); - if (seg_iter != mSegments.end()) - { - ++seg_iter; - } - } - else - { - // create default editable segment to hold new text - default_segment = new LLNormalTextSegment( getDefaultStyle(), pos, pos + insert_len, *this); - } - - // shift remaining segments to right - for(;seg_iter != mSegments.end(); ++seg_iter) - { - LLTextSegmentPtr segmentp = *seg_iter; - segmentp->setStart(segmentp->getStart() + insert_len); - segmentp->setEnd(segmentp->getEnd() + insert_len); - } - - // insert new segments - if (segments) - { - if (default_segment.notNull()) - { - // potentially overwritten by segments passed in - insertSegment(default_segment); - } - for (segment_vec_t::iterator seg_iter = segments->begin(); - seg_iter != segments->end(); - ++seg_iter) - { - LLTextSegment* segmentp = *seg_iter; - insertSegment(segmentp); - } - } - - text.insert(pos, wstr); - getViewModel()->setDisplay(text); - - if ( truncate() ) - { - // The user's not getting everything he's hoping for - make_ui_sound("UISndBadKeystroke"); - insert_len = getLength() - old_len; - } - - onValueChange(pos, pos + insert_len); - - return insert_len; -} - -S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) -{ - LLWString text(getWText()); - segment_set_t::iterator seg_iter = getSegIterContaining(pos); - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segmentp = *seg_iter; - S32 end = pos + length; - if (segmentp->getStart() < pos) - { - // deleting from middle of segment - if (segmentp->getEnd() > end) - { - segmentp->setEnd(segmentp->getEnd() - length); - } - // truncating segment - else - { - segmentp->setEnd(pos); - } - } - else if (segmentp->getStart() < end) - { - // deleting entire segment - if (segmentp->getEnd() <= end) - { - // remove segment - segmentp->unlinkFromDocument(this); - segment_set_t::iterator seg_to_erase(seg_iter++); - mSegments.erase(seg_to_erase); - continue; - } - // deleting head of segment - else - { - segmentp->setStart(pos); - segmentp->setEnd(segmentp->getEnd() - length); - } - } - else - { - // shifting segments backward to fill deleted portion - segmentp->setStart(segmentp->getStart() - length); - segmentp->setEnd(segmentp->getEnd() - length); - } - ++seg_iter; - } - - text.erase(pos, length); - getViewModel()->setDisplay(text); - - // recreate default segment in case we erased everything - createDefaultSegment(); - - onValueChange(pos, pos); - - return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length -} - -S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc) -{ - if (pos > (S32)getLength()) - { - return 0; - } - LLWString text(getWText()); - text[pos] = wc; - getViewModel()->setDisplay(text); - - onValueChange(pos, pos + 1); - - return 1; -} - //---------------------------------------------------------------------------- void LLTextEditor::makePristine() @@ -4051,29 +2456,13 @@ BOOL LLTextEditor::tryToRevertToPristineState() } -void LLTextEditor::updateTextRect() -{ - static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0); - static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0); - - LLRect old_text_rect = mTextRect; - mTextRect = mScroller->getContentWindowRect(); - mTextRect.stretch(texteditor_border * -1); - mTextRect.mLeft += texteditor_h_pad; - mTextRect.mLeft += mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; - if (mTextRect != old_text_rect) - { - needsReflow(); - } -} - -LLFastTimer::DeclareTimer FTM_TEXT_EDITOR_LOAD_KEYWORD("Text Editor Load Keywords"); +static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting"); void LLTextEditor::loadKeywords(const std::string& filename, const std::vector<std::string>& funcs, const std::vector<std::string>& tooltips, const LLColor3& color) { - LLFastTimer ft(FTM_TEXT_EDITOR_LOAD_KEYWORD); + LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); if(mKeywords.loadFromFile(filename)) { S32 count = llmin(funcs.size(), tooltips.size()); @@ -4094,27 +2483,9 @@ void LLTextEditor::loadKeywords(const std::string& filename, } } -void LLTextEditor::createDefaultSegment() -{ - // ensures that there is always at least one segment - if (mSegments.empty()) - { - LLTextSegmentPtr default_segment = new LLNormalTextSegment( getDefaultStyle(), 0, getLength() + 1, *this); - mSegments.insert(default_segment); - default_segment->linkToDocument(this); - } -} - -LLStyleSP LLTextEditor::getDefaultStyle() -{ - LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); - return LLStyleSP(new LLStyle(LLStyle::Params().color(text_color).font(mDefaultFont))); -} - -LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); void LLTextEditor::updateSegments() { - LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS); + LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); if (mKeywords.isLoaded()) { // HACK: No non-ascii keywords for now @@ -4125,11 +2496,11 @@ void LLTextEditor::updateSegments() segment_set_t::iterator insert_it = mSegments.begin(); for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) { - insert_it = mSegments.insert(insert_it, *list_it); + insertSegment(*list_it); } } - createDefaultSegment(); + LLTextBase::updateSegments(); } void LLTextEditor::updateLinkSegments() @@ -4155,66 +2526,7 @@ void LLTextEditor::updateLinkSegments() } } -void LLTextEditor::insertSegment(LLTextSegmentPtr segment_to_insert) -{ - if (segment_to_insert.isNull()) - { - return; - } - - segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); - if (cur_seg_iter == mSegments.end()) - { - mSegments.insert(segment_to_insert); - segment_to_insert->linkToDocument(this); - } - else - { - LLTextSegmentPtr cur_segmentp = *cur_seg_iter; - if (cur_segmentp->getStart() < segment_to_insert->getStart()) - { - S32 old_segment_end = cur_segmentp->getEnd(); - // split old at start point for new segment - cur_segmentp->setEnd(segment_to_insert->getStart()); - // advance to next segment - ++cur_seg_iter; - // insert remainder of old segment - LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this); - cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment); - remainder_segment->linkToDocument(this); - // insert new segment before remainder of old segment - cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); - - segment_to_insert->linkToDocument(this); - // move to "remanider" segment and start truncation there - ++cur_seg_iter; - } - else - { - cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); - ++cur_seg_iter; - segment_to_insert->linkToDocument(this); - } - - // now delete/truncate remaining segments as necessary - while(cur_seg_iter != mSegments.end()) - { - cur_segmentp = *cur_seg_iter; - if (cur_segmentp->getEnd() <= segment_to_insert->getEnd()) - { - cur_segmentp->unlinkFromDocument(this); - segment_set_t::iterator seg_to_erase(cur_seg_iter++); - mSegments.erase(seg_to_erase); - } - else - { - cur_segmentp->setStart(segment_to_insert->getEnd()); - break; - } - } - } -} void LLTextEditor::onMouseCaptureLost() { @@ -4400,7 +2712,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { - mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length()); + mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length()); removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); } else @@ -4415,7 +2727,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, setCursorPos(insert_preedit_at + caret_position); // Update of the preedit should be caused by some key strokes. - mKeystrokeTimer.reset(); + resetCursorBlink(); onKeyStroke(); } @@ -4578,93 +2890,6 @@ S32 LLTextEditor::getPreeditFontSize() const return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); } -LLWString LLTextEditor::getWText() const -{ - return getViewModel()->getDisplay(); -} - -void LLTextEditor::onValueChange(S32 start, S32 end) -{ -} - -// -// LLInlineViewSegment -// - -LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end) -: LLTextSegment(start, end), - mView(view) -{ -} - -LLInlineViewSegment::~LLInlineViewSegment() -{ - mView->die(); -} - -S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const -{ - if (first_char == 0 && num_chars == 0) - { - return 0; - } - else - { - return mView->getRect().getWidth(); - } -} - -S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const -{ - if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) - { - return 0; - } - else - { - return mEnd - mStart; - } -} - -void LLInlineViewSegment::updateLayout(const LLTextBase& editor) -{ - const LLTextEditor *ed = dynamic_cast<const LLTextEditor *>(&editor); - if (ed) - { - LLRect start_rect = ed->getLocalRectFromDocIndex(mStart); - LLRect doc_rect = ed->getDocumentPanel()->getRect(); - mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom); - } -} - -F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) -{ - return (F32)(draw_rect.mLeft + mView->getRect().getWidth()); -} - -S32 LLInlineViewSegment::getMaxHeight() const -{ - return mView->getRect().getHeight(); -} - -void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) -{ - LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor); - if (ed) - { - ed->removeDocumentChild(mView); - } -} - -void LLInlineViewSegment::linkToDocument(LLTextBase* editor) -{ - LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor); - if (ed) - { - ed->addDocumentChild(mView); - } -} - BOOL LLTextEditor::isDirty() const { if(mReadOnly) |