diff options
Diffstat (limited to 'indra/llui/lltexteditor.cpp')
-rw-r--r-- | indra/llui/lltexteditor.cpp | 266 |
1 files changed, 143 insertions, 123 deletions
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index f0238dba49..7d230f7d42 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1,6 +1,5 @@ /** * @file lltexteditor.cpp - * @brief LLTextEditor base class * * $LicenseInfo:firstyear=2001&license=viewergpl$ * @@ -34,6 +33,7 @@ #include "linden_common.h" +#define LLTEXTEDITOR_CPP #include "lltexteditor.h" #include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR @@ -73,6 +73,10 @@ // static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor"); +// Compiler optimization, generate extern template +template class LLTextEditor* LLView::getChild<class LLTextEditor>( + const std::string& name, BOOL recurse) const; + // // Constants // @@ -233,13 +237,17 @@ private: /////////////////////////////////////////////////////////////////// LLTextEditor::Params::Params() : default_text("default_text"), + prevalidate_callback("prevalidate_callback"), embedded_items("embedded_items", false), ignore_tab("ignore_tab", true), handle_edit_keys_directly("handle_edit_keys_directly", false), show_line_numbers("show_line_numbers", false), default_color("default_color"), - commit_on_focus_lost("commit_on_focus_lost", false) -{} + commit_on_focus_lost("commit_on_focus_lost", false), + show_context_menu("show_context_menu") +{ + addSynonym(prevalidate_callback, "text_type"); +} LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : LLTextBase(p), @@ -254,7 +262,9 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mMouseDownX(0), mMouseDownY(0), mTabsToNextField(p.ignore_tab), - mContextMenu(NULL) + mPrevalidateFunc(p.prevalidate_callback()), + mContextMenu(NULL), + mShowContextMenu(p.show_context_menu) { mDefaultFont = p.font; @@ -304,19 +314,31 @@ LLTextEditor::~LLTextEditor() // Scrollbar is deleted by LLView std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - delete mContextMenu; + // context menu is owned by menu holder, not us + //delete mContextMenu; } //////////////////////////////////////////////////////////// // LLTextEditor // Public methods -void LLTextEditor::setText(const LLStringExplicit &utf8str) +void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) { + // validate incoming text if necessary + if (mPrevalidateFunc) + { + LLWString test_text = utf8str_to_wstring(utf8str); + if (!mPrevalidateFunc(test_text)) + { + // not valid text, nothing to do + return; + } + } + blockUndo(); deselect(); - LLTextBase::setText(utf8str); + LLTextBase::setText(utf8str, input_params); resetDirty(); } @@ -652,6 +674,13 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; + // set focus first, in case click callbacks want to change it + // RN: do we really need to have a tab stop? + if (hasTabStop()) + { + setFocus( TRUE ); + } + // Let scrollbar have first dibs handled = LLTextBase::handleMouseDown(x, y, mask); @@ -694,12 +723,6 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) handled = TRUE; } - if (hasTabStop()) - { - setFocus( TRUE ); - handled = TRUE; - } - // Delay cursor flashing resetCursorBlink(); @@ -708,29 +731,35 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLTextBase::handleRightMouseDown(x, y, mask); - if (!handled && hasTabStop()) + if (hasTabStop()) { - setFocus( TRUE ); - showContextMenu(x, y); - handled = TRUE; + setFocus(TRUE); } - return handled; + if (!LLTextBase::handleRightMouseDown(x, y, mask)) + { + if(getShowContextMenu()) + { + showContextMenu(x, y); + } + } + return TRUE; } BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; - handled = LLTextBase::handleMouseDown(x, y, mask); + if (hasTabStop()) + { + setFocus(TRUE); + } - if (!handled) + if (!LLTextBase::handleMouseDown(x, y, mask)) { - setFocus( TRUE ); if( canPastePrimary() ) { setCursorAtLocalPos( x, y, true ); + // does not rely on focus being set pastePrimary(); } } @@ -746,10 +775,12 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { if( mIsSelecting ) { - mScroller->autoScroll(x, y); - - S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); - S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop); + if(mScroller) + { + mScroller->autoScroll(x, y); + } + S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); + S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); setCursorAtLocalPos( clamped_x, clamped_y, true ); mSelectionEnd = mCursorPos; } @@ -795,9 +826,12 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { if( mIsSelecting ) { - mScroller->autoScroll(x, y); - S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); - S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop); + if(mScroller) + { + mScroller->autoScroll(x, y); + } + S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); + S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); setCursorAtLocalPos( clamped_x, clamped_y, true ); endSelection(); } @@ -892,6 +926,21 @@ S32 LLTextEditor::execute( TextCmd* cmd ) // Push the new command is now on the top (front) of the undo stack. mUndoStack.push_front(cmd); mLastCmd = cmd; + + bool need_to_rollback = mPrevalidateFunc + && !mPrevalidateFunc(getViewModel()->getDisplay()); + if (need_to_rollback) + { + // get rid of this last command and clean up undo stack + undo(); + + // remove any evidence of this command from redo history + mUndoStack.pop_front(); + delete cmd; + + // failure, nothing changed + delta = 0; + } } else { @@ -1015,7 +1064,21 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc) if (mLastCmd && mLastCmd->canExtend(pos)) { S32 delta = 0; + if (mPrevalidateFunc) + { + // get a copy of current text contents + LLWString test_string(getViewModel()->getDisplay()); + + // modify text contents as if this addChar succeeded + llassert(pos <= (S32)test_string.size()); + test_string.insert(pos, 1, wc); + if (!mPrevalidateFunc( test_string)) + { + return 0; + } + } mLastCmd->extendAndExecute(this, pos, wc, &delta); + return delta; } else @@ -1271,8 +1334,6 @@ void LLTextEditor::cut() gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID ); deleteSelection( FALSE ); - needsReflow(); - onKeyStroke(); } @@ -1377,8 +1438,6 @@ void LLTextEditor::pasteHelper(bool is_primary) setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr())); deselect(); - needsReflow(); - onKeyStroke(); } @@ -1692,7 +1751,15 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) */ if (mReadOnly) { - handled = mScroller->handleKeyHere( key, mask ); + if(mScroller) + { + handled = mScroller->handleKeyHere( key, mask ); + } + else + { + handled = handleNavigationKey( key, mask ); + } + } else { @@ -1765,8 +1832,6 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) if(text_may_have_changed) { - needsReflow(); - onKeyStroke(); } needsScroll(); @@ -1809,8 +1874,6 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // Most keystrokes will make the selection box go away, but not all will. deselect(); - needsReflow(); - onKeyStroke(); } @@ -1866,10 +1929,9 @@ void LLTextEditor::doDelete() removeChar(); } - onKeyStroke(); } - needsReflow(); + onKeyStroke(); } //---------------------------------------------------------------------------- @@ -1912,8 +1974,6 @@ void LLTextEditor::undo() setCursorPos(pos); - needsReflow(); - onKeyStroke(); } @@ -1956,8 +2016,6 @@ void LLTextEditor::redo() setCursorPos(pos); - needsReflow(); - onKeyStroke(); } @@ -2001,7 +2059,8 @@ void LLTextEditor::setEnabled(BOOL enabled) bool read_only = !enabled; if (read_only != mReadOnly) { - mReadOnly = read_only; + //mReadOnly = read_only; + LLTextBase::setReadOnly(read_only); updateSegments(); updateAllowingLanguageInput(); } @@ -2016,6 +2075,20 @@ void LLTextEditor::showContextMenu(S32 x, S32 y) LLMenuHolderGL::child_registry_t::instance()); } + // Route menu to this class + // previously this was done in ::handleRightMoseDown: + //if(hasTabStop()) + // setFocus(TRUE) - why? weird... + // and then inside setFocus + // .... + // gEditMenuHandler = this; + // .... + // but this didn't work in all cases and just weird... + //why not here? + // (all this was done for EXT-4443) + + gEditMenuHandler = this; + S32 screen_x, screen_y; localPointToScreen(x, y, &screen_x, &screen_y); mContextMenu->show(screen_x, screen_y); @@ -2052,8 +2125,8 @@ void LLTextEditor::drawPreeditMarker() const S32 line_height = llround( mDefaultFont->getLineHeight() ); S32 line_start = getLineStart(cur_line); - S32 line_y = mTextRect.mTop - line_height; - while((mTextRect.mBottom <= line_y) && (num_lines > cur_line)) + S32 line_y = mVisibleTextRect.mTop - line_height; + while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line)) { S32 next_start = -1; S32 line_end = text_len; @@ -2085,12 +2158,12 @@ void LLTextEditor::drawPreeditMarker() continue; } - S32 preedit_left = mTextRect.mLeft; + S32 preedit_left = mVisibleTextRect.mLeft; if (left > line_start) { preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start); } - S32 preedit_right = mTextRect.mLeft; + S32 preedit_right = mVisibleTextRect.mLeft; if (right < line_end) { preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start); @@ -2130,9 +2203,8 @@ void LLTextEditor::drawPreeditMarker() void LLTextEditor::drawLineNumbers() { LLGLSUIDefault gls_ui; - - LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); - LLRect content_rect = mScroller->getContentWindowRect(); + LLRect scrolled_view_rect = getVisibleDocumentRect(); + LLRect content_rect = getVisibleTextRect(); LLLocalClipRect clip(content_rect); S32 first_line = getFirstVisibleLine(); S32 num_lines = getLineCount(); @@ -2141,7 +2213,7 @@ void LLTextEditor::drawLineNumbers() return; } - S32 cursor_line = getLineNumFromDocIndex(mCursorPos); + S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum; if (mShowLineNumbers) { @@ -2158,12 +2230,12 @@ void LLTextEditor::drawLineNumbers() { line_info& line = mLineInfoList[cur_line]; - if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom) { break; } - S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; + S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom; // draw the line numbers if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop) { @@ -2194,8 +2266,8 @@ void LLTextEditor::draw() { { // pad clipping rectangle so that cursor can draw at full width - // when at left edge of mTextRect - LLRect clip_rect(mTextRect); + // when at left edge of mVisibleTextRect + LLRect clip_rect(mVisibleTextRect); clip_rect.stretch(1); LLLocalClipRect clip(clip_rect); drawPreeditMarker(); @@ -2302,8 +2374,6 @@ void LLTextEditor::insertText(const std::string &new_text) setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); - needsReflow(); - setEnabled( enabled ); } @@ -2326,8 +2396,6 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); insert(getLength(), widget_wide_text, FALSE, segment); - needsReflow(); - // Set the cursor and scroll position if( selection_start != selection_end ) { @@ -2352,52 +2420,6 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const } } - -void LLTextEditor::replaceUrlLabel(const std::string &url, - const std::string &label) -{ - // get the full (wide) text for the editor so we can change it - LLWString text = getWText(); - LLWString wlabel = utf8str_to_wstring(label); - bool modified = false; - S32 seg_start = 0; - - // iterate through each segment looking for ones styled as links - segment_set_t::iterator it; - for (it = mSegments.begin(); it != mSegments.end(); ++it) - { - LLTextSegment *seg = *it; - const LLStyleSP style = seg->getStyle(); - - // update segment start/end length in case we replaced text earlier - S32 seg_length = seg->getEnd() - seg->getStart(); - seg->setStart(seg_start); - seg->setEnd(seg_start + seg_length); - - // if we find a link with our Url, then replace the label - if (style->isLink() && style->getLinkHREF() == url) - { - S32 start = seg->getStart(); - S32 end = seg->getEnd(); - text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); - seg->setEnd(start + wlabel.size()); - modified = true; - } - - // work out the character offset for the next segment - seg_start = seg->getEnd(); - } - - // update the editor with the new (wide) text string - if (modified) - { - getViewModel()->setDisplay(text); - deselect(); - setCursorPos(mCursorPos); - needsReflow(); - } -} - void LLTextEditor::removeTextFromEnd(S32 num_chars) { if (num_chars <= 0) return; @@ -2409,7 +2431,6 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) mSelectionStart = llclamp(mSelectionStart, 0, len); mSelectionEnd = llclamp(mSelectionEnd, 0, len); - needsReflow(); needsScroll(); } @@ -2468,8 +2489,6 @@ BOOL LLTextEditor::tryToRevertToPristineState() i--; } } - - needsReflow(); } return isPristine(); // TRUE => success @@ -2505,9 +2524,9 @@ void LLTextEditor::loadKeywords(const std::string& filename, void LLTextEditor::updateSegments() { - LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); - if (mKeywords.isLoaded()) + if (mReflowIndex < S32_MAX && mKeywords.isLoaded()) { + LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); // HACK: No non-ascii keywords for now segment_vec_t segment_list; mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this); @@ -2536,13 +2555,16 @@ void LLTextEditor::updateLinkSegments() // if the link's label (what the user can edit) is a valid Url, // then update the link's HREF to be the same as the label text. // This lets users edit Urls in-place. - LLStyleSP style = static_cast<LLStyleSP>(segment->getStyle()); + LLStyleConstSP style = segment->getStyle(); + LLStyleSP new_style(new LLStyle(*style)); LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart()); if (LLUrlRegistry::instance().hasUrl(url_label)) { std::string new_url = wstring_to_utf8str(url_label); LLStringUtil::trim(new_url); - style->setLinkHREF(new_url); + new_style->setLinkHREF(new_url); + LLStyleConstSP sp(new_style); + segment->setStyle(sp); } } } @@ -2641,7 +2663,6 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) startOfDoc(); deselect(); - needsReflow(); return success; } @@ -2745,7 +2766,6 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, mPreeditStandouts = preedit_standouts; - needsReflow(); setCursorPos(insert_preedit_at + caret_position); // Update of the preedit should be caused by some key strokes. @@ -2759,7 +2779,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect if (control) { LLRect control_rect_screen; - localRectToScreen(mTextRect, &control_rect_screen); + localRectToScreen(mVisibleTextRect, &control_rect_screen); LLUI::screenRectToGL(control_rect_screen, control); } @@ -2810,8 +2830,8 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect if (coord) { - const S32 query_x = mTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); - const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; + const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); + const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; S32 query_screen_x, query_screen_y; localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); @@ -2819,13 +2839,13 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect if (bounds) { - S32 preedit_left = mTextRect.mLeft; + S32 preedit_left = mVisibleTextRect.mLeft; if (preedit_left_position > current_line_start) { preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); } - S32 preedit_right = mTextRect.mLeft; + S32 preedit_right = mVisibleTextRect.mLeft; if (preedit_right_position < current_line_end) { preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); @@ -2835,7 +2855,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start); } - const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height; + const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; const S32 preedit_bottom = preedit_top - line_height; const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom); |