From c285f59ce2a05703e3a1232fcaf3ee3aea714b3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 18 Feb 2024 12:52:19 +0100 Subject: Replace BOOL with bool in llwindow and dependent classes --- indra/llui/lllineeditor.cpp | 70 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 60dbfd68c6..a1b9b5696c 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -660,9 +660,9 @@ void LLLineEditor::onSpellCheckSettingsChange() mSpellCheckStart = mSpellCheckEnd = -1; } -BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { - setFocus( TRUE ); + setFocus( true ); mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); if (mSelectionEnd == 0 && mSelectionStart == mText.length()) @@ -674,7 +674,7 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { const LLWString& wtext = mText.getWString(); - BOOL doSelectAll = TRUE; + bool doSelectAll = true; // Select the word we're on if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) @@ -709,7 +709,7 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; // delay cursor flashing mKeystrokeTimer.reset(); @@ -717,15 +717,15 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // take selection to 'primary' clipboard updatePrimary(); - return TRUE; + return true; } -BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) { // Check first whether the "clear search" button wants to deal with this. if(childrenHandleMouseDown(x, y, mask) != NULL) { - return TRUE; + return true; } if (!mSelectAllonFocusReceived @@ -737,7 +737,7 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mask & MASK_SHIFT) { // assume we're starting a drag select - mIsSelecting = TRUE; + mIsSelecting = true; // Handle selection extension S32 old_cursor_pos = getCursor(); @@ -793,14 +793,14 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; } } gFocusMgr.setMouseCapture( this ); } - setFocus(TRUE); + setFocus(true); // delay cursor flashing mKeystrokeTimer.reset(); @@ -808,40 +808,40 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mMouseDownSignal) (*mMouseDownSignal)(this,x,y,mask); - return TRUE; + return true; } -BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; - setFocus( TRUE ); + setFocus( true ); if( canPastePrimary() ) { setCursorAtLocalPos(x); pastePrimary(); } - return TRUE; + return true; } -BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { - setFocus(TRUE); + setFocus(true); if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) { showContextMenu(x, y); } - return TRUE; + return true; } -BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; // Check first whether the "clear search" button wants to deal with this. if(!hasMouseCapture()) { if(childrenHandleHover(x, y, mask) != NULL) { - return TRUE; + return true; } } @@ -884,34 +884,34 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) getWindow()->setCursor(UI_CURSOR_IBEAM); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = TRUE; + handled = true; } if( !handled ) { getWindow()->setCursor(UI_CURSOR_IBEAM); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = TRUE; + handled = true; } return handled; } -BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( hasMouseCapture() ) { gFocusMgr.setMouseCapture( NULL ); - handled = TRUE; + handled = true; } // Check first whether the "clear search" button wants to deal with this. if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) { - return TRUE; + return true; } if( mIsSelecting ) @@ -919,7 +919,7 @@ BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) setCursorAtLocalPos( x ); mSelectionEnd = getCursor(); - handled = TRUE; + handled = true; } if( handled ) @@ -1597,18 +1597,18 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) } -BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) +bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL { - return FALSE; + return false; } - BOOL handled = FALSE; + bool handled = false; if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) { - handled = TRUE; + handled = true; LLLineEditorRollback rollback( this ); @@ -2489,7 +2489,7 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string, mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); } -BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const { if (control) { @@ -2511,13 +2511,13 @@ BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect if (preedit_right_column < mScrollHPos) { // This should not occure... - return FALSE; + return false; } const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) { - return FALSE; + return false; } if (coord) @@ -2544,7 +2544,7 @@ BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); } - return TRUE; + return true; } void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const -- cgit v1.2.3 From 5486d87b566aabb43fa585de3ee86e5bc77391c9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 19 Feb 2024 15:44:40 +0100 Subject: Change LLPreeditor::standouts_t to std::deque since std::vector since it's a specialization that does not necessarily behave like standard STL containers --- indra/llui/lllineeditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index a1b9b5696c..9b320fdf97 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2590,7 +2590,7 @@ void LLLineEditor::markAsPreedit(S32 position, S32 length) mPreeditPositions[0] = position; mPreeditPositions[1] = position + length; mPreeditStandouts.resize(1); - mPreeditStandouts[0] = FALSE; + mPreeditStandouts[0] = false; } else { -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/llui/lllineeditor.cpp | 178 ++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 89 deletions(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 9b320fdf97..6664117bcd 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -74,7 +74,7 @@ static LLDefaultChildRegistry::Register r1("line_editor"); // Compiler optimization, generate extern template template class LLLineEditor* LLView::getChild( - const std::string& name, BOOL recurse) const; + const std::string& name, bool recurse) const; // // Member functions @@ -127,10 +127,10 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mTextLeftEdge(0), // computed in updateTextPadding() below mTextRightEdge(0), // computed in updateTextPadding() below mCommitOnFocusLost( p.commit_on_focus_lost ), - mKeystrokeOnEsc(FALSE), + mKeystrokeOnEsc(false), mRevertOnEsc( p.revert_on_esc ), mKeystrokeCallback( p.keystroke_callback() ), - mIsSelecting( FALSE ), + mIsSelecting( false ), mSelectionStart( 0 ), mSelectionEnd( 0 ), mLastSelectionX(-1), @@ -138,23 +138,23 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mLastSelectionStart(-1), mLastSelectionEnd(-1), mBorderThickness( 0 ), - mIgnoreArrowKeys( FALSE ), + mIgnoreArrowKeys( false ), mIgnoreTab( p.ignore_tab ), mDrawAsterixes( p.is_password ), mSpellCheck( p.spellcheck ), mSpellCheckStart(-1), mSpellCheckEnd(-1), mSelectAllonFocusReceived( p.select_on_focus ), - mSelectAllonCommit( TRUE ), - mPassDelete(FALSE), - mReadOnly(FALSE), + mSelectAllonCommit( true ), + mPassDelete(false), + mReadOnly(false), mBgImage( p.background_image ), mBgImageDisabled( p.background_image_disabled ), mBgImageFocused( p.background_image_focused ), mShowImageFocused( p.bg_image_always_focused ), mUseBgColor(p.use_bg_color), - mHaveHistory(FALSE), - mReplaceNewlinesWithSpaces( TRUE ), + mHaveHistory(false), + mReplaceNewlinesWithSpaces( true ), mLabel(p.label), mCursorColor(p.cursor_color()), mBgColor(p.bg_color()), @@ -169,7 +169,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) { llassert( mMaxLengthBytes > 0 ); - LLUICtrl::setEnabled(TRUE); + LLUICtrl::setEnabled(true); setEnabled(p.enabled); mScrollTimer.reset(); @@ -214,7 +214,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) LLLineEditor::~LLLineEditor() { - mCommitOnFocusLost = FALSE; + mCommitOnFocusLost = false; // Make sure no context menu linger around once the widget is deleted LLContextMenu* menu = static_cast(mContextMenuHandle.get()); @@ -231,7 +231,7 @@ LLLineEditor::~LLLineEditor() void LLLineEditor::initFromParams(const LLLineEditor::Params& params) { LLUICtrl::initFromParams(params); - LLUICtrl::setEnabled(TRUE); + LLUICtrl::setEnabled(true); setEnabled(params.enabled); } @@ -280,9 +280,9 @@ void LLLineEditor::onCommit() if (mSelectAllonCommit) selectAll(); } -// Returns TRUE if user changed value at all +// Returns true if user changed value at all // virtual -BOOL LLLineEditor::isDirty() const +bool LLLineEditor::isDirty() const { return mText.getString() != mPrevText; } @@ -343,14 +343,14 @@ void LLLineEditor::updateHistory() } } -void LLLineEditor::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); updateTextPadding(); // For clamping side-effect. setCursor(mCursorPos); // For clamping side-effect. } -void LLLineEditor::setEnabled(BOOL enabled) +void LLLineEditor::setEnabled(bool enabled) { mReadOnly = !enabled; setTabStop(!mReadOnly); @@ -405,7 +405,7 @@ void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit // Check to see if entire field is selected. S32 len = mText.length(); - BOOL all_selected = (len > 0) + bool all_selected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len ) || ( mSelectionStart == len && mSelectionEnd == 0 )); @@ -517,7 +517,7 @@ void LLLineEditor::resetScrollPosition() setCursor(getCursor()); } -BOOL LLLineEditor::canDeselect() const +bool LLLineEditor::canDeselect() const { return hasSelection(); } @@ -526,13 +526,13 @@ void LLLineEditor::deselect() { mSelectionStart = 0; mSelectionEnd = 0; - mIsSelecting = FALSE; + mIsSelecting = false; } void LLLineEditor::startSelection() { - mIsSelecting = TRUE; + mIsSelecting = true; mSelectionStart = getCursor(); mSelectionEnd = getCursor(); } @@ -541,14 +541,14 @@ void LLLineEditor::endSelection() { if( mIsSelecting ) { - mIsSelecting = FALSE; + mIsSelecting = false; mSelectionEnd = getCursor(); } } -BOOL LLLineEditor::canSelectAll() const +bool LLLineEditor::canSelectAll() const { - return TRUE; + return true; } void LLLineEditor::selectAll() @@ -562,7 +562,7 @@ void LLLineEditor::selectAll() mSelectionEnd = 0; setCursor(mSelectionEnd); //mScrollHPos = 0; - mIsSelecting = TRUE; + mIsSelecting = true; updatePrimary(); } @@ -976,19 +976,19 @@ void LLLineEditor::addChar(const llwchar uni_char) S32 new_bytes = wchar_utf8_length(new_c); - BOOL allow_char = TRUE; + bool allow_char = true; // Check byte length limit if ((new_bytes + cur_bytes) > mMaxLengthBytes) { - allow_char = FALSE; + allow_char = false; } else if (mMaxLengthChars) { S32 wide_chars = mText.getWString().size(); if ((wide_chars + 1) > mMaxLengthChars) { - allow_char = FALSE; + allow_char = false; } } @@ -1033,7 +1033,7 @@ void LLLineEditor::setSelection(S32 start, S32 end) { S32 len = mText.length(); - mIsSelecting = TRUE; + mIsSelecting = true; // JC, yes, this seems odd, but I think you have to presume a // selection dragged from the end towards the start. @@ -1042,7 +1042,7 @@ void LLLineEditor::setSelection(S32 start, S32 end) setCursor(start); } -void LLLineEditor::setDrawAsterixes(BOOL b) +void LLLineEditor::setDrawAsterixes(bool b) { mDrawAsterixes = b; updateAllowingLanguageInput(); @@ -1077,13 +1077,13 @@ S32 LLLineEditor::nextWordPos(S32 cursorPos) const } -BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask) +bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; if( mask & MASK_SHIFT ) { - handled = TRUE; + handled = true; switch( key ) { @@ -1136,7 +1136,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask) } default: - handled = FALSE; + handled = false; break; } } @@ -1167,7 +1167,7 @@ void LLLineEditor::deleteSelection() } } -BOOL LLLineEditor::canCut() const +bool LLLineEditor::canCut() const { return !mReadOnly && !mDrawAsterixes && hasSelection(); } @@ -1191,7 +1191,7 @@ void LLLineEditor::cut() deleteSelection(); // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1204,7 +1204,7 @@ void LLLineEditor::cut() } } -BOOL LLLineEditor::canCopy() const +bool LLLineEditor::canCopy() const { return !mDrawAsterixes && hasSelection(); } @@ -1221,7 +1221,7 @@ void LLLineEditor::copy() } } -BOOL LLLineEditor::canPaste() const +bool LLLineEditor::canPaste() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(); } @@ -1319,7 +1319,7 @@ void LLLineEditor::pasteHelper(bool is_primary) deselect(); // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1344,7 +1344,7 @@ void LLLineEditor::copyPrimary() } } -BOOL LLLineEditor::canPastePrimary() const +bool LLLineEditor::canPastePrimary() const { return !mReadOnly && LLClipboard::instance().isTextAvailable(true); } @@ -1357,9 +1357,9 @@ void LLLineEditor::updatePrimary() } } -BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) +bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; switch( key ) { @@ -1369,7 +1369,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) gKeyboard->toggleInsertMode(); } - handled = TRUE; + handled = true; break; case KEY_BACKSPACE: @@ -1390,7 +1390,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) LLUI::getInstance()->reportBadKeystroke(); } } - handled = TRUE; + handled = true; break; case KEY_PAGE_UP: @@ -1398,7 +1398,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) if (!mIgnoreArrowKeys) { setCursor(0); - handled = TRUE; + handled = true; } break; @@ -1411,7 +1411,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { setCursor(len); } - handled = TRUE; + handled = true; } break; @@ -1438,7 +1438,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1465,7 +1465,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1482,7 +1482,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1499,7 +1499,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) { LLUI::getInstance()->reportBadKeystroke(); } - handled = TRUE; + handled = true; } break; @@ -1528,10 +1528,10 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) } -BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) +bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) { - BOOL handled = FALSE; - BOOL selection_modified = FALSE; + bool handled = false; + bool selection_modified = false; if ( gFocusMgr.getKeyboardFocus() == this ) { @@ -1566,7 +1566,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask ) deselect(); } - BOOL need_to_rollback = FALSE; + bool need_to_rollback = false; // If read-only, don't allow changes need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText())); @@ -1625,7 +1625,7 @@ bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) deselect(); - BOOL need_to_rollback = FALSE; + bool need_to_rollback = false; // Validate new string and rollback the keystroke if needed. need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); @@ -1651,7 +1651,7 @@ bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) } -BOOL LLLineEditor::canDoDelete() const +bool LLLineEditor::canDoDelete() const { return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); } @@ -1681,7 +1681,7 @@ void LLLineEditor::doDelete() } // Validate new string and rollback the if needed. - BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); + bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) ); if( need_to_rollback ) { rollback.doRollback( this ); @@ -1702,7 +1702,7 @@ void LLLineEditor::drawBackground() F32 alpha = getCurrentTransparency(); if (mUseBgColor) { - gl_rect_2d(getLocalRect(), mBgColor % alpha, TRUE); + gl_rect_2d(getLocalRect(), mBgColor % alpha, true); } else { @@ -1923,7 +1923,7 @@ void LLLineEditor::draw() &rendered_pixels_right); } #if 1 // for when we're ready for image art. - mBorder->setVisible(FALSE); // no more programmatic art. + mBorder->setVisible(false); // no more programmatic art. #endif if ( (getSpellCheck()) && (mText.length() > 2) ) @@ -2024,7 +2024,7 @@ void LLLineEditor::draw() // If we're editing... if( hasFocus()) { - //mBorder->setVisible(TRUE); // ok, programmer art just this once. + //mBorder->setVisible(true); // ok, programmer art just this once. // (Flash the cursor every half second) if (!mReadOnly && gFocusMgr.getAppHasFocus()) { @@ -2080,16 +2080,16 @@ void LLLineEditor::draw() LLFontGL::NO_SHADOW, S32_MAX, mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, FALSE); + &rendered_pixels_right, false); } // Draw children (border) - //mBorder->setVisible(TRUE); - mBorder->setKeyboardFocusHighlight( TRUE ); + //mBorder->setVisible(true); + mBorder->setKeyboardFocusHighlight( true ); LLView::draw(); - mBorder->setKeyboardFocusHighlight( FALSE ); - //mBorder->setVisible(FALSE); + mBorder->setKeyboardFocusHighlight( false ); + //mBorder->setVisible(false); } else // does not have keyboard input { @@ -2105,7 +2105,7 @@ void LLLineEditor::draw() LLFontGL::NO_SHADOW, S32_MAX, mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, FALSE); + &rendered_pixels_right, false); } // Draw children (border) LLView::draw(); @@ -2162,19 +2162,19 @@ void LLLineEditor::onTabInto() } //virtual -BOOL LLLineEditor::acceptsTextInput() const +bool LLLineEditor::acceptsTextInput() const { - return TRUE; + return true; } // Start or stop the editor from accepting text-editing keystrokes -void LLLineEditor::setFocus( BOOL new_state ) +void LLLineEditor::setFocus( bool new_state ) { - BOOL old_state = hasFocus(); + bool old_state = hasFocus(); if (!new_state) { - getWindow()->allowLanguageTextInput(this, FALSE); + getWindow()->allowLanguageTextInput(this, false); } @@ -2185,7 +2185,7 @@ void LLLineEditor::setFocus( BOOL new_state ) // We don't want handleMouseUp() to "finish" the selection (and thereby // set mSelectionEnd to where the mouse is), so we finish the selection // here. - mIsSelecting = FALSE; + mIsSelecting = false; } if( new_state ) @@ -2258,13 +2258,13 @@ bool LLLineEditor::prevalidateInput(const LLWString& wstr) } // static -BOOL LLLineEditor::postvalidateFloat(const std::string &str) +bool LLLineEditor::postvalidateFloat(const std::string &str) { LLLocale locale(LLLocale::USER_LOCALE); - BOOL success = TRUE; - BOOL has_decimal = FALSE; - BOOL has_digit = FALSE; + bool success = true; + bool has_decimal = false; + bool has_digit = false; LLWString trimmed = utf8str_to_wstring(str); LLWStringUtil::trim(trimmed); @@ -2289,22 +2289,22 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) if( has_decimal ) { // can't have two - success = FALSE; + success = false; break; } else { - has_decimal = TRUE; + has_decimal = true; } } else if( LLStringOps::isDigit( trimmed[i] ) ) { - has_digit = TRUE; + has_digit = true; } else { - success = FALSE; + success = false; break; } } @@ -2316,7 +2316,7 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) return success; } -BOOL LLLineEditor::evaluateFloat() +bool LLLineEditor::evaluateFloat() { bool success; F32 result = 0.f; @@ -2348,7 +2348,7 @@ void LLLineEditor::onMouseCaptureLost() } -void LLLineEditor::setSelectAllonFocusReceived(BOOL b) +void LLLineEditor::setSelectAllonFocusReceived(bool b) { mSelectAllonFocusReceived = b; } @@ -2369,16 +2369,16 @@ void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) } -BOOL LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); - return TRUE; + return true; } -BOOL LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) { mLabel.setArg(key, text); - return TRUE; + return true; } @@ -2398,15 +2398,15 @@ void LLLineEditor::updateAllowingLanguageInput() } if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL) { - window->allowLanguageTextInput(this, TRUE); + window->allowLanguageTextInput(this, true); } else { - window->allowLanguageTextInput(this, FALSE); + window->allowLanguageTextInput(this, false); } } -BOOL LLLineEditor::hasPreeditString() const +bool LLLineEditor::hasPreeditString() const { return (mPreeditPositions.size() > 1); } @@ -2612,7 +2612,7 @@ S32 LLLineEditor::getPreeditFontSize() const return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } -void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace) +void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) { mReplaceNewlinesWithSpaces = replace; } -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/llui/lllineeditor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 505216d0ff..8a04342af0 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1473,11 +1473,11 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) // handle ctrl-uparrow if we have a history enabled line editor. case KEY_UP: - if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) { - if( mCurrentHistoryLine > mLineHistory.begin() ) + if (mCurrentHistoryLine > mLineHistory.begin()) { - mText.assign( *(--mCurrentHistoryLine) ); + mText.assign(*(--mCurrentHistoryLine)); setCursorToEnd(); } else @@ -1490,9 +1490,9 @@ bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) // handle [ctrl]-downarrow if we have a history enabled line editor case KEY_DOWN: - if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) { - if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 ) + if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) { mText.assign( *(++mCurrentHistoryLine) ); setCursorToEnd(); @@ -2684,7 +2684,7 @@ void LLLineEditor::showContextMenu(S32 x, S32 y) // If the cursor is on a misspelled word, retrieve suggestions for it std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty()) == true) + if ((is_misspelled = !misspelled_word.empty())) { LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); } -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llui/lllineeditor.cpp | 5470 +++++++++++++++++++++---------------------- 1 file changed, 2735 insertions(+), 2735 deletions(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 8c5e69fbb2..5ec4c34f57 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1,2735 +1,2735 @@ -/** - * @file lllineeditor.cpp - * @brief LLLineEditor base class - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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. - * - * 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. - * - * 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// Text editor widget to let users enter a single line. - -#include "linden_common.h" - -#define LLLINEEDITOR_CPP -#include "lllineeditor.h" - -#include "lltexteditor.h" -#include "llmath.h" -#include "llfontgl.h" -#include "llgl.h" -#include "lltimer.h" - -#include "llcalc.h" -//#include "llclipboard.h" -#include "llcontrol.h" -#include "llbutton.h" -#include "llfocusmgr.h" -#include "llkeyboard.h" -#include "llrect.h" -#include "llresmgr.h" -#include "llspellcheck.h" -#include "llstring.h" -#include "llwindow.h" -#include "llui.h" -#include "lluictrlfactory.h" -#include "llclipboard.h" -#include "llmenugl.h" - -// -// Imported globals -// - -// -// Constants -// - -const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds -const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing -const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing -const F32 AUTO_SCROLL_TIME = 0.05f; -const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval? -const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on - -const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET - -static LLDefaultChildRegistry::Register r1("line_editor"); - -// Compiler optimization, generate extern template -template class LLLineEditor* LLView::getChild( - const std::string& name, bool recurse) const; - -// -// Member functions -// - -LLLineEditor::Params::Params() -: max_length(""), - keystroke_callback("keystroke_callback"), - prevalidator("prevalidator"), - input_prevalidator("input_prevalidator"), - background_image("background_image"), - background_image_disabled("background_image_disabled"), - background_image_focused("background_image_focused"), - bg_image_always_focused("bg_image_always_focused", false), - show_label_focused("show_label_focused", false), - select_on_focus("select_on_focus", false), - revert_on_esc("revert_on_esc", true), - spellcheck("spellcheck", false), - commit_on_focus_lost("commit_on_focus_lost", true), - ignore_tab("ignore_tab", true), - is_password("is_password", false), - allow_emoji("allow_emoji", true), - cursor_color("cursor_color"), - use_bg_color("use_bg_color", false), - bg_color("bg_color"), - text_color("text_color"), - text_readonly_color("text_readonly_color"), - text_tentative_color("text_tentative_color"), - highlight_color("highlight_color"), - preedit_bg_color("preedit_bg_color"), - border(""), - bg_visible("bg_visible"), - text_pad_left("text_pad_left"), - text_pad_right("text_pad_right"), - default_text("default_text") -{ - changeDefault(mouse_opaque, true); - addSynonym(prevalidator, "prevalidate_callback"); - addSynonym(input_prevalidator, "prevalidate_input_callback"); - addSynonym(select_on_focus, "select_all_on_focus_received"); - addSynonym(border, "border"); - addSynonym(label, "watermark_text"); - addSynonym(max_length.chars, "max_length"); -} - -LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) -: LLUICtrl(p), - mMaxLengthBytes(p.max_length.bytes), - mMaxLengthChars(p.max_length.chars), - mCursorPos( 0 ), - mScrollHPos( 0 ), - mTextPadLeft(p.text_pad_left), - mTextPadRight(p.text_pad_right), - mTextLeftEdge(0), // computed in updateTextPadding() below - mTextRightEdge(0), // computed in updateTextPadding() below - mCommitOnFocusLost( p.commit_on_focus_lost ), - mKeystrokeOnEsc(false), - mRevertOnEsc( p.revert_on_esc ), - mKeystrokeCallback( p.keystroke_callback() ), - mIsSelecting( false ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mLastSelectionX(-1), - mLastSelectionY(-1), - mLastSelectionStart(-1), - mLastSelectionEnd(-1), - mBorderThickness( 0 ), - mIgnoreArrowKeys( false ), - mIgnoreTab( p.ignore_tab ), - mDrawAsterixes( p.is_password ), - mAllowEmoji( p.allow_emoji ), - mSpellCheck( p.spellcheck ), - mSpellCheckStart(-1), - mSpellCheckEnd(-1), - mSelectAllonFocusReceived( p.select_on_focus ), - mSelectAllonCommit( true ), - mPassDelete(false), - mReadOnly(false), - mBgImage( p.background_image ), - mBgImageDisabled( p.background_image_disabled ), - mBgImageFocused( p.background_image_focused ), - mShowImageFocused( p.bg_image_always_focused ), - mShowLabelFocused( p.show_label_focused ), - mUseBgColor(p.use_bg_color), - mHaveHistory(false), - mReplaceNewlinesWithSpaces( true ), - mPrevalidator(p.prevalidator()), - mInputPrevalidator(p.input_prevalidator()), - mLabel(p.label), - mCursorColor(p.cursor_color()), - mBgColor(p.bg_color()), - mFgColor(p.text_color()), - mReadOnlyFgColor(p.text_readonly_color()), - mTentativeFgColor(p.text_tentative_color()), - mHighlightColor(p.highlight_color()), - mPreeditBgColor(p.preedit_bg_color()), - mGLFont(p.font), - mContextMenuHandle(), - mShowContextMenu(true) -{ - llassert( mMaxLengthBytes > 0 ); - - LLUICtrl::setEnabled(true); - setEnabled(p.enabled); - - mScrollTimer.reset(); - mTripleClickTimer.reset(); - setText(p.default_text()); - - if (p.initial_value.isProvided() - && !p.control_name.isProvided()) - { - // Initial value often is descriptive, like "Type some ID here" - // and can be longer than size limitation, ignore size - setText(p.initial_value.getValue().asString(), false); - } - - // Initialize current history line iterator - mCurrentHistoryLine = mLineHistory.begin(); - - LLRect border_rect(getLocalRect()); - // adjust for gl line drawing glitch - border_rect.mTop -= 1; - border_rect.mRight -=1; - LLViewBorder::Params border_p(p.border); - border_p.rect = border_rect; - border_p.follows.flags = FOLLOWS_ALL; - border_p.bevel_style = LLViewBorder::BEVEL_IN; - mBorder = LLUICtrlFactory::create(border_p); - addChild( mBorder ); - - // clamp text padding to current editor size - updateTextPadding(); - setCursor(mText.length()); - - if (mSpellCheck) - { - LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this)); - } - mSpellCheckTimer.reset(); - - updateAllowingLanguageInput(); -} - -LLLineEditor::~LLLineEditor() -{ - mCommitOnFocusLost = false; - - // Make sure no context menu linger around once the widget is deleted - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (menu) - { - menu->hide(); - } - setContextMenu(NULL); - - // calls onCommit() while LLLineEditor still valid - gFocusMgr.releaseFocusIfNeeded( this ); -} - -void LLLineEditor::initFromParams(const LLLineEditor::Params& params) -{ - LLUICtrl::initFromParams(params); - LLUICtrl::setEnabled(true); - setEnabled(params.enabled); -} - -void LLLineEditor::onFocusReceived() -{ - gEditMenuHandler = this; - LLUICtrl::onFocusReceived(); - updateAllowingLanguageInput(); -} - -void LLLineEditor::onFocusLost() -{ - // The call to updateAllowLanguageInput() - // when loosing the keyboard focus *may* - // indirectly invoke handleUnicodeCharHere(), - // so it must be called before onCommit. - updateAllowingLanguageInput(); - - if( mCommitOnFocusLost && mText.getString() != mPrevText) - { - onCommit(); - } - - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - getWindow()->showCursorFromMouseMove(); - - LLUICtrl::onFocusLost(); -} - -// virtual -void LLLineEditor::onCommit() -{ - // put current line into the line history - updateHistory(); - - setControlValue(getValue()); - LLUICtrl::onCommit(); - resetDirty(); - - // Selection on commit needs to be turned off when evaluating maths - // expressions, to allow indication of the error position - if (mSelectAllonCommit) selectAll(); -} - -// Returns true if user changed value at all -// virtual -bool LLLineEditor::isDirty() const -{ - return mText.getString() != mPrevText; -} - -// Clear dirty state -// virtual -void LLLineEditor::resetDirty() -{ - mPrevText = mText.getString(); -} - -// assumes UTF8 text -// virtual -void LLLineEditor::setValue(const LLSD& value ) -{ - setText(value.asString()); -} - -//virtual -LLSD LLLineEditor::getValue() const -{ - return LLSD(getText()); -} - - -// line history support -void LLLineEditor::updateHistory() -{ - // On history enabled line editors, remember committed line and - // reset current history line number. - // Be sure only to remember lines that are not empty and that are - // different from the last on the list. - if( mHaveHistory && getLength() ) - { - if( !mLineHistory.empty() ) - { - // When not empty, last line of history should always be blank. - if( mLineHistory.back().empty() ) - { - // discard the empty line - mLineHistory.pop_back(); - } - else - { - LL_WARNS("") << "Last line of history was not blank." << LL_ENDL; - } - } - - // Add text to history, ignoring duplicates - if( mLineHistory.empty() || getText() != mLineHistory.back() ) - { - mLineHistory.push_back( getText() ); - } - - // Restore the blank line and set mCurrentHistoryLine to point at it - mLineHistory.push_back( "" ); - mCurrentHistoryLine = mLineHistory.end() - 1; - } -} - -void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLUICtrl::reshape(width, height, called_from_parent); - updateTextPadding(); // For clamping side-effect. - setCursor(mCursorPos); // For clamping side-effect. -} - -void LLLineEditor::setEnabled(bool enabled) -{ - mReadOnly = !enabled; - setTabStop(!mReadOnly); - updateAllowingLanguageInput(); -} - - -void LLLineEditor::setMaxTextLength(S32 max_text_length) -{ - S32 max_len = llmax(0, max_text_length); - mMaxLengthBytes = max_len; -} - -void LLLineEditor::setMaxTextChars(S32 max_text_chars) -{ - S32 max_chars = llmax(0, max_text_chars); - mMaxLengthChars = max_chars; -} - -void LLLineEditor::getTextPadding(S32 *left, S32 *right) -{ - *left = mTextPadLeft; - *right = mTextPadRight; -} - -void LLLineEditor::setTextPadding(S32 left, S32 right) -{ - mTextPadLeft = left; - mTextPadRight = right; - updateTextPadding(); -} - -void LLLineEditor::updateTextPadding() -{ - mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth()); - mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth()); -} - - -void LLLineEditor::setText(const LLStringExplicit &new_text) -{ - setText(new_text, true); -} - -void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit) -{ - // If new text is identical, don't copy and don't move insertion point - if (mText.getString() == new_text) - { - return; - } - - // Check to see if entire field is selected. - S32 len = mText.length(); - bool all_selected = (len > 0) - && (( mSelectionStart == 0 && mSelectionEnd == len ) - || ( mSelectionStart == len && mSelectionEnd == 0 )); - - // Do safe truncation so we don't split multi-byte characters - // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor - all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); - - std::string truncated_utf8 = new_text; - if (!mAllowEmoji) - { - // Cut emoji symbols if exist - utf8str_remove_emojis(truncated_utf8); - } - if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes) - { - truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes); - } - mText.assign(truncated_utf8); - - if (use_size_limit && mMaxLengthChars) - { - mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars)); - } - - if (all_selected) - { - // ...keep whole thing selected - selectAll(); - } - else - { - // try to preserve insertion point, but deselect text - deselect(); - } - setCursor(llmin((S32)mText.length(), getCursor())); - - // Set current history line to end of history. - if (mLineHistory.empty()) - { - mCurrentHistoryLine = mLineHistory.end(); - } - else - { - mCurrentHistoryLine = mLineHistory.end() - 1; - } - - mPrevText = mText; -} - - -// Picks a new cursor position based on the actual screen size of text being drawn. -void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) -{ - S32 cursor_pos = calcCursorPos(local_mouse_x); - - S32 left_pos = llmin( mSelectionStart, cursor_pos ); - S32 length = llabs( mSelectionStart - cursor_pos ); - const LLWString& substr = mText.getWString().substr(left_pos, length); - - if (mIsSelecting && !prevalidateInput(substr)) - return; - - setCursor(cursor_pos); -} - -void LLLineEditor::setCursor( S32 pos ) -{ - S32 old_cursor_pos = getCursor(); - mCursorPos = llclamp( pos, 0, mText.length()); - - // position of end of next character after cursor - S32 pixels_after_scroll = findPixelNearestPos(); - if( pixels_after_scroll > mTextRightEdge ) - { - S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos); - S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left))); - // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters) - // or first character if cursor is at beginning - S32 new_last_visible_char = llmax(0, getCursor() - 1); - S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char); - if (old_cursor_pos == last_visible_char) - { - mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD)); - } - else - { - mScrollHPos = min_scroll; - } - } - else if (getCursor() < mScrollHPos) - { - if (old_cursor_pos == mScrollHPos) - { - mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL)); - } - else - { - mScrollHPos = getCursor(); - } - } -} - - -void LLLineEditor::setCursorToEnd() -{ - setCursor(mText.length()); - deselect(); -} - -void LLLineEditor::resetScrollPosition() -{ - mScrollHPos = 0; - // make sure cursor says in visible range - setCursor(getCursor()); -} - -bool LLLineEditor::canDeselect() const -{ - return hasSelection(); -} - -void LLLineEditor::deselect() -{ - mSelectionStart = 0; - mSelectionEnd = 0; - mIsSelecting = false; -} - - -void LLLineEditor::startSelection() -{ - mIsSelecting = true; - mSelectionStart = getCursor(); - mSelectionEnd = getCursor(); -} - -void LLLineEditor::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = false; - mSelectionEnd = getCursor(); - } -} - -bool LLLineEditor::canSelectAll() const -{ - return true; -} - -void LLLineEditor::selectAll() -{ - if (!prevalidateInput(mText.getWString())) - { - return; - } - - mSelectionStart = mText.length(); - mSelectionEnd = 0; - setCursor(mSelectionEnd); - //mScrollHPos = 0; - mIsSelecting = true; - updatePrimary(); -} - -bool LLLineEditor::getSpellCheck() const -{ - return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); -} - -const std::string& LLLineEditor::getSuggestion(U32 index) const -{ - return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; -} - -U32 LLLineEditor::getSuggestionCount() const -{ - return mSuggestionList.size(); -} - -void LLLineEditor::replaceWithSuggestion(U32 index) -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) - { - LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); - if (!mAllowEmoji) - { - // Cut emoji symbols if exist - wstring_remove_emojis(suggestion); - } - if (suggestion.empty()) - return; - - deselect(); - - // Delete the misspelled word - mText.erase(it->first, it->second - it->first); - - // Insert the suggestion in its place - mText.insert(it->first, suggestion); - setCursor(it->first + (S32)suggestion.length()); - - break; - } - } - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLLineEditor::addToDictionary() -{ - if (canAddToDictionary()) - { - LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); - } -} - -bool LLLineEditor::canAddToDictionary() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -void LLLineEditor::addToIgnore() -{ - if (canAddToIgnore()) - { - LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); - } -} - -bool LLLineEditor::canAddToIgnore() const -{ - return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); -} - -std::string LLLineEditor::getMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first)); - } - } - return LLStringUtil::null; -} - -bool LLLineEditor::isMisspelledWord(U32 pos) const -{ - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - if ( (it->first <= pos) && (it->second >= pos) ) - { - return true; - } - } - return false; -} - -void LLLineEditor::onSpellCheckSettingsChange() -{ - // Recheck the spelling on every change - mMisspellRanges.clear(); - mSpellCheckStart = mSpellCheckEnd = -1; -} - -bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - setFocus( true ); - mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); - - if (mSelectionEnd == 0 && mSelectionStart == mText.length()) - { - // if everything is selected, handle this as a normal click to change insertion point - handleMouseDown(x, y, mask); - } - else - { - const LLWString& wtext = mText.getWString(); - - bool doSelectAll = true; - - // Select the word we're on - if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) - { - S32 old_selection_start = mLastSelectionStart; - S32 old_selection_end = mLastSelectionEnd; - - // Select word the cursor is over - while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] )) - { // Find the start of the word - mCursorPos--; - } - startSelection(); - - while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) - { // Find the end of the word - mCursorPos++; - } - mSelectionEnd = mCursorPos; - - // If nothing changed, then the word was already selected. Select the whole line. - doSelectAll = (old_selection_start == mSelectionStart) && - (old_selection_end == mSelectionEnd); - } - - if ( doSelectAll ) - { // Select everything - selectAll(); - } - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - - // delay cursor flashing - mKeystrokeTimer.reset(); - - // take selection to 'primary' clipboard - updatePrimary(); - - return true; -} - -bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Check first whether the "clear search" button wants to deal with this. - if(childrenHandleMouseDown(x, y, mask) != NULL) - { - return true; - } - - if (!mSelectAllonFocusReceived - || gFocusMgr.getKeyboardFocus() == this) - { - mLastSelectionStart = -1; - mLastSelectionStart = -1; - - if (mask & MASK_SHIFT) - { - // assume we're starting a drag select - mIsSelecting = true; - - // Handle selection extension - S32 old_cursor_pos = getCursor(); - setCursorAtLocalPos(x); - - if (hasSelection()) - { - /* Mac-like behavior - extend selection towards the cursor - if (getCursor() < mSelectionStart - && getCursor() < mSelectionEnd) - { - // ...left of selection - mSelectionStart = llmax(mSelectionStart, mSelectionEnd); - mSelectionEnd = getCursor(); - } - else if (getCursor() > mSelectionStart - && getCursor() > mSelectionEnd) - { - // ...right of selection - mSelectionStart = llmin(mSelectionStart, mSelectionEnd); - mSelectionEnd = getCursor(); - } - else - { - mSelectionEnd = getCursor(); - } - */ - // Windows behavior - mSelectionEnd = getCursor(); - } - else - { - mSelectionStart = old_cursor_pos; - mSelectionEnd = getCursor(); - } - } - else - { - if (mTripleClickTimer.hasExpired()) - { - // Save selection for word/line selecting on double-click - mLastSelectionStart = mSelectionStart; - mLastSelectionEnd = mSelectionEnd; - - // Move cursor and deselect for regular click - setCursorAtLocalPos( x ); - deselect(); - startSelection(); - } - else // handle triple click - { - selectAll(); - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - } - } - - gFocusMgr.setMouseCapture( this ); - } - - setFocus(true); - - // delay cursor flashing - mKeystrokeTimer.reset(); - - if (mMouseDownSignal) - (*mMouseDownSignal)(this,x,y,mask); - - return true; -} - -bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; - setFocus( true ); - if( canPastePrimary() ) - { - setCursorAtLocalPos(x); - pastePrimary(); - } - return true; -} - -bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - setFocus(true); - if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) - { - showContextMenu(x, y); - } - return true; -} - -bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) -{ - bool handled = false; - // Check first whether the "clear search" button wants to deal with this. - if(!hasMouseCapture()) - { - if(childrenHandleHover(x, y, mask) != NULL) - { - return true; - } - } - - if( (hasMouseCapture()) && mIsSelecting ) - { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } - // Scroll if mouse cursor outside of bounds - if (mScrollTimer.hasExpired()) - { - S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); - mScrollTimer.reset(); - mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME); - if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) ) - { - // Scroll to the left - mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length()); - } - else - if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) ) - { - // If scrolling one pixel would make a difference... - S32 pixels_after_scrolling_one_char = findPixelNearestPos(1); - if( pixels_after_scrolling_one_char >= mTextRightEdge ) - { - // ...scroll to the right - mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length()); - } - } - } - - setCursorAtLocalPos( x ); - mSelectionEnd = getCursor(); - - // delay cursor flashing - mKeystrokeTimer.reset(); - - getWindow()->setCursor(UI_CURSOR_IBEAM); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; - handled = true; - } - - if( !handled ) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - handled = true; - } - - return handled; -} - - -bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - if( hasMouseCapture() ) - { - gFocusMgr.setMouseCapture( NULL ); - handled = true; - } - - // Check first whether the "clear search" button wants to deal with this. - if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) - { - return true; - } - - if( mIsSelecting ) - { - setCursorAtLocalPos( x ); - mSelectionEnd = getCursor(); - - handled = true; - } - - if( handled ) - { - // delay cursor flashing - mKeystrokeTimer.reset(); - - // take selection to 'primary' clipboard - updatePrimary(); - } - - // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually. - if (mMouseUpSignal) - (*mMouseUpSignal)(this,x,y, mask); - return handled; -} - - -// Remove a single character from the text -void LLLineEditor::removeChar() -{ - if( getCursor() > 0 ) - { - if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) - return; - - mText.erase(getCursor() - 1, 1); - - setCursor(getCursor() - 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } -} - -void LLLineEditor::addChar(const llwchar uni_char) -{ - if (!mAllowEmoji && LLStringOps::isEmoji(uni_char)) - return; - - llwchar new_c = uni_char; - if (hasSelection()) - { - deleteSelection(); - } - else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) - return; - - mText.erase(getCursor(), 1); - } - - S32 cur_bytes = mText.getString().size(); - - S32 new_bytes = wchar_utf8_length(new_c); - - bool allow_char = true; - - // Check byte length limit - if ((new_bytes + cur_bytes) > mMaxLengthBytes) - { - allow_char = false; - } - else if (mMaxLengthChars) - { - S32 wide_chars = mText.getWString().size(); - if ((wide_chars + 1) > mMaxLengthChars) - { - allow_char = false; - } - } - - if (allow_char) - { - // Will we need to scroll? - LLWString w_buf; - w_buf.assign(1, new_c); - - mText.insert(getCursor(), w_buf); - setCursor(getCursor() + 1); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - - getWindow()->hideCursorUntilMouseMove(); -} - -// Extends the selection box to the new cursor position -void LLLineEditor::extendSelection( S32 new_cursor_pos ) -{ - if( !mIsSelecting ) - { - startSelection(); - } - - S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); - S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); - const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - - if (!prevalidateInput(selection)) - return; - - setCursor(new_cursor_pos); - mSelectionEnd = getCursor(); -} - - -void LLLineEditor::setSelection(S32 start, S32 end) -{ - S32 len = mText.length(); - - mIsSelecting = true; - - // JC, yes, this seems odd, but I think you have to presume a - // selection dragged from the end towards the start. - mSelectionStart = llclamp(end, 0, len); - mSelectionEnd = llclamp(start, 0, len); - setCursor(start); -} - -void LLLineEditor::setDrawAsterixes(bool b) -{ - mDrawAsterixes = b; - updateAllowingLanguageInput(); -} - -S32 LLLineEditor::prevWordPos(S32 cursorPos) const -{ - const LLWString& wtext = mText.getWString(); - while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) - { - cursorPos--; - } - while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) - { - cursorPos--; - } - return cursorPos; -} - -S32 LLLineEditor::nextWordPos(S32 cursorPos) const -{ - const LLWString& wtext = mText.getWString(); - while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) - { - cursorPos++; - } - while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) - { - cursorPos++; - } - return cursorPos; -} - - -bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) -{ - bool handled = false; - - if( mask & MASK_SHIFT ) - { - handled = true; - - switch( key ) - { - case KEY_LEFT: - if( 0 < getCursor() ) - { - S32 cursorPos = getCursor() - 1; - if( mask & MASK_CONTROL ) - { - cursorPos = prevWordPos(cursorPos); - } - extendSelection( cursorPos ); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - break; - - case KEY_RIGHT: - if( getCursor() < mText.length()) - { - S32 cursorPos = getCursor() + 1; - if( mask & MASK_CONTROL ) - { - cursorPos = nextWordPos(cursorPos); - } - extendSelection( cursorPos ); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - break; - - case KEY_PAGE_UP: - case KEY_HOME: - extendSelection( 0 ); - break; - - case KEY_PAGE_DOWN: - case KEY_END: - { - S32 len = mText.length(); - if( len ) - { - extendSelection( len ); - } - break; - } - - default: - handled = false; - break; - } - } - - if(handled) - { - // take selection to 'primary' clipboard - updatePrimary(); - } - - return handled; -} - -void LLLineEditor::deleteSelection() -{ - if( !mReadOnly && hasSelection() ) - { - S32 left_pos, selection_length; - getSelectionRange(&left_pos, &selection_length); - const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - - if (!prevalidateInput(selection)) - return; - - mText.erase(left_pos, selection_length); - deselect(); - setCursor(left_pos); - } -} - -bool LLLineEditor::canCut() const -{ - return !mReadOnly && !mDrawAsterixes && hasSelection(); -} - -// cut selection to clipboard -void LLLineEditor::cut() -{ - if( canCut() ) - { - S32 left_pos, length; - getSelectionRange(&left_pos, &length); - const LLWString& selection = mText.getWString().substr(left_pos, length); - - if (!prevalidateInput(selection)) - return; - - // Prepare for possible rollback - LLLineEditorRollback rollback( this ); - - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); - deleteSelection(); - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - } - } -} - -bool LLLineEditor::canCopy() const -{ - return !mDrawAsterixes && hasSelection(); -} - - -// copy selection to clipboard -void LLLineEditor::copy() -{ - if( canCopy() ) - { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); - } -} - -bool LLLineEditor::canPaste() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(); -} - -void LLLineEditor::paste() -{ - bool is_primary = false; - pasteHelper(is_primary); -} - -void LLLineEditor::pastePrimary() -{ - bool is_primary = true; - pasteHelper(is_primary); -} - -// paste from primary (is_primary==true) or clipboard (is_primary==false) -void LLLineEditor::pasteHelper(bool is_primary) -{ - bool can_paste_it; - if (is_primary) - { - can_paste_it = canPastePrimary(); - } - else - { - can_paste_it = canPaste(); - } - - if (can_paste_it) - { - LLWString paste; - LLClipboard::instance().pasteFromClipboard(paste, is_primary); - - if (!paste.empty()) - { - if (!mAllowEmoji) - { - wstring_remove_emojis(paste); - } - - if (!prevalidateInput(paste)) - return; - - // Prepare for possible rollback - LLLineEditorRollback rollback(this); - - // Delete any selected characters - if ((!is_primary) && hasSelection()) - { - deleteSelection(); - } - - // Clean up string (replace tabs and returns and remove characters that our fonts don't support.) - LLWString clean_string(paste); - LLWStringUtil::replaceTabsWithSpaces(clean_string, 1); - //clean_string = wstring_detabify(paste, 1); - LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character - - // Insert the string - - // Check to see that the size isn't going to be larger than the max number of bytes - U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText); - - if ( available_bytes < (U32) wstring_utf8_length(clean_string) ) - { // Doesn't all fit - llwchar current_symbol = clean_string[0]; - U32 wchars_that_fit = 0; - U32 total_bytes = wchar_utf8_length(current_symbol); - - //loop over the "wide" characters (symbols) - //and check to see how large (in bytes) each symbol is. - while ( total_bytes <= available_bytes ) - { - //while we still have available bytes - //"accept" the current symbol and check the size - //of the next one - current_symbol = clean_string[++wchars_that_fit]; - total_bytes += wchar_utf8_length(current_symbol); - } - // Truncate the clean string at the limit of what will fit - clean_string = clean_string.substr(0, wchars_that_fit); - LLUI::getInstance()->reportBadKeystroke(); - } - - if (mMaxLengthChars) - { - U32 available_chars = mMaxLengthChars - mText.getWString().size(); - - if (available_chars < clean_string.size()) - { - clean_string = clean_string.substr(0, available_chars); - } - - LLUI::getInstance()->reportBadKeystroke(); - } - - mText.insert(getCursor(), clean_string); - setCursor( getCursor() + (S32)clean_string.length() ); - deselect(); - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - } - } - } -} - -// copy selection to primary -void LLLineEditor::copyPrimary() -{ - if( canCopy() ) - { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true); - } -} - -bool LLLineEditor::canPastePrimary() const -{ - return !mReadOnly && LLClipboard::instance().isTextAvailable(true); -} - -void LLLineEditor::updatePrimary() -{ - if(canCopy() ) - { - copyPrimary(); - } -} - -bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) -{ - bool handled = false; - - switch( key ) - { - case KEY_INSERT: - if (mask == MASK_NONE) - { - gKeyboard->toggleInsertMode(); - } - - handled = true; - break; - - case KEY_BACKSPACE: - if (!mReadOnly) - { - //LL_INFOS() << "Handling backspace" << LL_ENDL; - if( hasSelection() ) - { - deleteSelection(); - } - else - if( 0 < getCursor() ) - { - removeChar(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - } - handled = true; - break; - - case KEY_PAGE_UP: - case KEY_HOME: - if (!mIgnoreArrowKeys) - { - setCursor(0); - handled = true; - } - break; - - case KEY_PAGE_DOWN: - case KEY_END: - if (!mIgnoreArrowKeys) - { - S32 len = mText.length(); - if( len ) - { - setCursor(len); - } - handled = true; - } - break; - - case KEY_LEFT: - if (mIgnoreArrowKeys && mask == MASK_NONE) - break; - if ((mask & MASK_ALT) == 0) - { - if( hasSelection() ) - { - setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd )); - } - else - if( 0 < getCursor() ) - { - S32 cursorPos = getCursor() - 1; - if( mask & MASK_CONTROL ) - { - cursorPos = prevWordPos(cursorPos); - } - setCursor(cursorPos); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - case KEY_RIGHT: - if (mIgnoreArrowKeys && mask == MASK_NONE) - break; - if ((mask & MASK_ALT) == 0) - { - if (hasSelection()) - { - setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd)); - } - else - if (getCursor() < mText.length()) - { - S32 cursorPos = getCursor() + 1; - if( mask & MASK_CONTROL ) - { - cursorPos = nextWordPos(cursorPos); - } - setCursor(cursorPos); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - // handle ctrl-uparrow if we have a history enabled line editor. - case KEY_UP: - if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) - { - if (mCurrentHistoryLine > mLineHistory.begin()) - { - mText.assign(*(--mCurrentHistoryLine)); - setCursorToEnd(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - // handle [ctrl]-downarrow if we have a history enabled line editor - case KEY_DOWN: - if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) - { - if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) - { - mText.assign( *(++mCurrentHistoryLine) ); - setCursorToEnd(); - } - else - { - LLUI::getInstance()->reportBadKeystroke(); - } - handled = true; - } - break; - - case KEY_RETURN: - // store sent line in history - updateHistory(); - break; - - case KEY_ESCAPE: - if (mRevertOnEsc && mText.getString() != mPrevText) - { - setText(mPrevText); - // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged) - if (mKeystrokeOnEsc) - { - onKeystroke(); - } - } - break; - - default: - break; - } - - return handled; -} - - -bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) -{ - bool handled = false; - bool selection_modified = false; - - if ( gFocusMgr.getKeyboardFocus() == this ) - { - LLLineEditorRollback rollback( this ); - - if( !handled ) - { - handled = handleSelectionKey( key, mask ); - selection_modified = handled; - } - - // Handle most keys only if the text editor is writeable. - if ( !mReadOnly ) - { - if( !handled ) - { - handled = handleSpecialKey( key, mask ); - } - } - - if( handled ) - { - mKeystrokeTimer.reset(); - - // Most keystrokes will make the selection box go away, but not all will. - if( !selection_modified && - KEY_SHIFT != key && - KEY_CONTROL != key && - KEY_ALT != key && - KEY_CAPSLOCK != key) - { - deselect(); - } - - bool prevalidator_failed = false; - - // If read-only, don't allow changes - bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText()); - - // Validate new string and rollback the keystroke if needed. - if (!need_to_rollback && mPrevalidator) - { - prevalidator_failed = !mPrevalidator.validate(mText.getWString()); - need_to_rollback |= prevalidator_failed; - } - - if (need_to_rollback) - { - rollback.doRollback(this); - - LLUI::getInstance()->reportBadKeystroke(); - if (prevalidator_failed) - { - mPrevalidator.showLastErrorUsingTimeout(); - } - } - - // Notify owner if requested - if (!need_to_rollback && handled) - { - onKeystroke(); - if ( (!selection_modified) && (KEY_BACKSPACE == key) ) - { - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } - } - } - - return handled; -} - - -bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return false; - } - - bool handled = false; - - if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) - { - handled = true; - - LLLineEditorRollback rollback( this ); - - { - LLWString u_char; - u_char.assign(1, uni_char); - if (!prevalidateInput(u_char)) - return handled; - } - - addChar(uni_char); - - mKeystrokeTimer.reset(); - - deselect(); - - // Validate new string and rollback the keystroke if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback( this ); - - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - - // Notify owner if requested - if (!need_to_rollback && handled) - { - // HACK! The only usage of this callback doesn't do anything with the character. - // We'll have to do something about this if something ever changes! - Doug - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } - return handled; -} - - -bool LLLineEditor::canDoDelete() const -{ - return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); -} - -void LLLineEditor::doDelete() -{ - if (canDoDelete() && mText.length() > 0) - { - // Prepare for possible rollback - LLLineEditorRollback rollback( this ); - - if (hasSelection()) - { - deleteSelection(); - } - else if ( getCursor() < mText.length()) - { - const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); - - if (!prevalidateInput(text_to_delete)) - { - onKeystroke(); - return; - } - setCursor(getCursor() + 1); - removeChar(); - } - - // Validate new string and rollback the if needed. - bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); - if (need_to_rollback) - { - rollback.doRollback(this); - LLUI::getInstance()->reportBadKeystroke(); - mPrevalidator.showLastErrorUsingTimeout(); - } - else - { - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); - } - } -} - - -void LLLineEditor::drawBackground() -{ - F32 alpha = getCurrentTransparency(); - if (mUseBgColor) - { - gl_rect_2d(getLocalRect(), mBgColor % alpha, true); - } - else - { - bool has_focus = hasFocus(); - LLUIImage* image; - if (mReadOnly) - { - image = mBgImageDisabled; - } - else if (has_focus || mShowImageFocused) - { - image = mBgImageFocused; - } - else - { - image = mBgImage; - } - - if (!image) return; - // optionally draw programmatic border - if (has_focus) - { - LLColor4 tmp_color = gFocusMgr.getFocusColor(); - tmp_color.setAlpha(alpha); - image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), - tmp_color, - gFocusMgr.getFocusFlashWidth()); - } - LLColor4 tmp_color = UI_VERTEX_COLOR; - tmp_color.setAlpha(alpha); - image->draw(getLocalRect(), tmp_color); - } -} - -//virtual -void LLLineEditor::draw() -{ - F32 alpha = getDrawContext().mAlpha; - S32 text_len = mText.length(); - static LLUICachedControl lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); - static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); - static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); - static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); - static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); - static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); - static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); - static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); - static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); - - std::string saved_text; - if (mDrawAsterixes) - { - saved_text = mText.getString(); - std::string text; - for (S32 i = 0; i < mText.length(); i++) - { - text += PASSWORD_ASTERISK; - } - mText = text; - } - - // draw rectangle for the background - LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - background.stretch( -mBorderThickness ); - - S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2; - if (mSpellCheck) - { - lineeditor_v_pad += 1; - } - - drawBackground(); - - // draw text - - // With viewer-2 art files, input region is 2 pixels up - S32 cursor_bottom = background.mBottom + 2; - S32 cursor_top = background.mTop - 1; - - LLColor4 text_color; - if (!mReadOnly) - { - if (!getTentative()) - { - text_color = mFgColor.get(); - } - else - { - text_color = mTentativeFgColor.get(); - } - } - else - { - text_color = mReadOnlyFgColor.get(); - } - text_color.setAlpha(alpha); - LLColor4 label_color = mTentativeFgColor.get(); - label_color.setAlpha(alpha); - - if (hasPreeditString()) - { - // Draw preedit markers. This needs to be before drawing letters. - for (U32 i = 0; i < mPreeditStandouts.size(); i++) - { - const S32 preedit_left = mPreeditPositions[i]; - const S32 preedit_right = mPreeditPositions[i + 1]; - if (preedit_right > mScrollHPos) - { - S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor()); - S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight); - if (preedit_pixels_left >= background.mRight) - { - break; - } - if (mPreeditStandouts[i]) - { - gl_rect_2d(preedit_pixels_left + preedit_standout_gap, - background.mBottom + preedit_standout_position, - preedit_pixels_right - preedit_standout_gap - 1, - background.mBottom + preedit_standout_position - preedit_standout_thickness, - (text_color * preedit_standout_brightness - + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/)); - } - else - { - gl_rect_2d(preedit_pixels_left + preedit_marker_gap, - background.mBottom + preedit_marker_position, - preedit_pixels_right - preedit_marker_gap - 1, - background.mBottom + preedit_marker_position - preedit_marker_thickness, - (text_color * preedit_marker_brightness - + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/)); - } - } - } - } - - S32 rendered_text = 0; - F32 rendered_pixels_right = (F32)mTextLeftEdge; - F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad; - - if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() ) - { - S32 select_left; - S32 select_right; - if (mSelectionStart < mSelectionEnd) - { - select_left = mSelectionStart; - select_right = mSelectionEnd; - } - else - { - select_left = mSelectionEnd; - select_right = mSelectionStart; - } - - if( select_left > mScrollHPos ) - { - // unselected, left side - rendered_text = mGLFont->render( - mText, mScrollHPos, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - select_left - mScrollHPos, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - - if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) - { - LLColor4 color = mHighlightColor; - color.setAlpha(alpha); - // selected middle - S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); - width = llmin(width, mTextRightEdge - ll_round(rendered_pixels_right)); - gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color); - - LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); - rendered_text += mGLFont->render( - mText, mScrollHPos + rendered_text, - rendered_pixels_right, text_bottom, - tmp_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - select_right - mScrollHPos - rendered_text, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - - if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) - { - // unselected, right side - rendered_text += mGLFont->render( - mText, mScrollHPos + rendered_text, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - } - else - { - rendered_text = mGLFont->render( - mText, mScrollHPos, - rendered_pixels_right, text_bottom, - text_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } -#if 1 // for when we're ready for image art. - mBorder->setVisible(false); // no more programmatic art. -#endif - - if ( (getSpellCheck()) && (mText.length() > 2) ) - { - // Calculate start and end indices for the first and last visible word - U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text); - - if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) - { - const LLWString& text = mText.getWString().substr(start, end); - - // Find the start of the first word - U32 word_start = 0, word_end = 0; - while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) ) - { - word_start++; - } - - // Iterate over all words in the text block and check them one by one - mMisspellRanges.clear(); - while (word_start < text.length()) - { - // Find the end of the current word (special case handling for "'" when it's used as a contraction) - word_end = word_start + 1; - while ( (word_end < text.length()) && - ((LLWStringUtil::isPartOfWord(text[word_end])) || - ((L'\'' == text[word_end]) && (word_end + 1 < text.length()) && - (LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) ) - { - word_end++; - } - if (word_end > text.length()) - { - break; - } - - // Don't process words shorter than 3 characters - std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start)); - if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) - { - mMisspellRanges.push_back(std::pair(start + word_start, start + word_end)); - } - - // Find the start of the next word - word_start = word_end + 1; - while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) ) - { - word_start++; - } - } - - mSpellCheckStart = start; - mSpellCheckEnd = end; - } - - // Draw squiggly lines under any (visible) misspelled words - for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) - { - // Skip over words that aren't (partially) visible - if ( ((it->first < start) && (it->second < start)) || (it->first > end) ) - { - continue; - } - - // Skip the current word if the user is still busy editing it - if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) - { - continue; - } - - S32 pxWidth = getRect().getWidth(); - S32 pxStart = findPixelNearestPos(it->first - getCursor()); - if (pxStart > pxWidth) - { - continue; - } - S32 pxEnd = findPixelNearestPos(it->second - getCursor()); - if (pxEnd > pxWidth) - { - pxEnd = pxWidth; - } - - S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight()); - - gGL.color4ub(255, 0, 0, 200); - while (pxStart + 1 < pxEnd) - { - gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2); - if (pxStart + 3 < pxEnd) - { - gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1); - } - pxStart += 4; - } - } - } - - // If we're editing... - if( hasFocus()) - { - //mBorder->setVisible(true); // ok, programmer art just this once. - // (Flash the cursor every half second) - if (!mReadOnly && gFocusMgr.getAppHasFocus()) - { - F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) - { - S32 cursor_left = findPixelNearestPos(); - cursor_left -= lineeditor_cursor_thickness / 2; - S32 cursor_right = cursor_left + lineeditor_cursor_thickness; - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - const LLWString space(utf8str_to_wstring(std::string(" "))); - S32 wswidth = mGLFont->getWidth(space.c_str()); - S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1; - cursor_right = cursor_left + llmax(wswidth, width); - } - // Use same color as text for the Cursor - gl_rect_2d(cursor_left, cursor_top, - cursor_right, cursor_bottom, text_color); - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); - mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, - tmp_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - 1); - } - - // Make sure the IME is in the right place - S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad ); - - ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } - } - - //draw label if no text is provided - //but we should draw it in a different color - //to give indication that it is not text you typed in - if (0 == mText.length() && (mReadOnly || mShowLabelFocused)) - { - mGLFont->render(mLabel.getWString(), 0, - mTextLeftEdge, (F32)text_bottom, - label_color, - LLFontGL::LEFT, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right, false); - } - - - // Draw children (border) - //mBorder->setVisible(true); - mBorder->setKeyboardFocusHighlight( true ); - LLView::draw(); - mBorder->setKeyboardFocusHighlight( false ); - //mBorder->setVisible(false); - } - else // does not have keyboard input - { - // draw label if no text provided - if (0 == mText.length()) - { - mGLFont->render(mLabel.getWString(), 0, - mTextLeftEdge, (F32)text_bottom, - label_color, - LLFontGL::LEFT, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - S32_MAX, - mTextRightEdge - ll_round(rendered_pixels_right), - &rendered_pixels_right); - } - // Draw children (border) - LLView::draw(); - } - - if (mDrawAsterixes) - { - mText = saved_text; - } -} - - -// Returns the local screen space X coordinate associated with the text cursor position. -S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const -{ - S32 dpos = getCursor() - mScrollHPos + cursor_offset; - S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge; - return result; -} - -S32 LLLineEditor::calcCursorPos(S32 mouse_x) -{ - const llwchar* wtext = mText.getWString().c_str(); - LLWString asterix_text; - if (mDrawAsterixes) - { - for (S32 i = 0; i < mText.length(); i++) - { - asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); - } - wtext = asterix_text.c_str(); - } - - S32 cur_pos = mScrollHPos + - mGLFont->charFromPixelOffset( - wtext, mScrollHPos, - (F32)(mouse_x - mTextLeftEdge), - (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive - - return cur_pos; -} -//virtual -void LLLineEditor::clear() -{ - mText.clear(); - setCursor(0); -} - -//virtual -void LLLineEditor::onTabInto() -{ - selectAll(); - LLUICtrl::onTabInto(); -} - -//virtual -bool LLLineEditor::acceptsTextInput() const -{ - return true; -} - -// Start or stop the editor from accepting text-editing keystrokes -void LLLineEditor::setFocus( bool new_state ) -{ - bool old_state = hasFocus(); - - if (!new_state) - { - getWindow()->allowLanguageTextInput(this, false); - } - - - // getting focus when we didn't have it before, and we want to select all - if (!old_state && new_state && mSelectAllonFocusReceived) - { - selectAll(); - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection - // here. - mIsSelecting = false; - } - - if( new_state ) - { - gEditMenuHandler = this; - - // Don't start the cursor flashing right away - mKeystrokeTimer.reset(); - } - else - { - // Not really needed, since loss of keyboard focus should take care of this, - // but limited paranoia is ok. - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - endSelection(); - } - - LLUICtrl::setFocus( new_state ); - - if (new_state) - { - // Allow Language Text Input only when this LineEditor has - // no prevalidate function attached. This criterion works - // fine on 1.15.0.2, since all prevalidate func reject any - // non-ASCII characters. I'm not sure on future versions, - // however. - getWindow()->allowLanguageTextInput(this, !mPrevalidator); - } -} - -//virtual -void LLLineEditor::setRect(const LLRect& rect) -{ - LLUICtrl::setRect(rect); - if (mBorder) - { - LLRect border_rect = mBorder->getRect(); - // Scalable UI somehow made these rectangles off-by-one. - // I don't know why. JC - border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom, - rect.getWidth()-1, rect.getHeight()-1); - mBorder->setRect(border_rect); - } -} - -void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator) -{ - mPrevalidator = validator; - updateAllowingLanguageInput(); -} - -void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator) -{ - mInputPrevalidator = validator; - updateAllowingLanguageInput(); -} - -bool LLLineEditor::prevalidateInput(const LLWString& wstr) -{ - return mInputPrevalidator.validate(wstr); -} - -// static -bool LLLineEditor::postvalidateFloat(const std::string &str) -{ - LLLocale locale(LLLocale::USER_LOCALE); - - bool success = true; - bool has_decimal = false; - bool has_digit = false; - - LLWString trimmed = utf8str_to_wstring(str); - LLWStringUtil::trim(trimmed); - S32 len = trimmed.length(); - if( 0 < len ) - { - S32 i = 0; - - // First character can be a negative sign - if( '-' == trimmed[0] ) - { - i++; - } - - // May be a comma or period, depending on the locale - llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint(); - - for( ; i < len; i++ ) - { - if( decimal_point == trimmed[i] ) - { - if( has_decimal ) - { - // can't have two - success = false; - break; - } - else - { - has_decimal = true; - } - } - else - if( LLStringOps::isDigit( trimmed[i] ) ) - { - has_digit = true; - } - else - { - success = false; - break; - } - } - } - - // Gotta have at least one - success = has_digit; - - return success; -} - -bool LLLineEditor::evaluateFloat() -{ - bool success; - F32 result = 0.f; - std::string expr = getText(); - LLStringUtil::toUpper(expr); - - success = LLCalc::getInstance()->evalString(expr, result); - - if (!success) - { - // Move the cursor to near the error on failure - setCursor(LLCalc::getInstance()->getLastErrorPos()); - // *TODO: Translated error message indicating the type of error? Select error text? - } - else - { - // Replace the expression with the result - std::string result_str = llformat("%f",result); - setText(result_str); - selectAll(); - } - - return success; -} - -void LLLineEditor::onMouseCaptureLost() -{ - endSelection(); -} - - -void LLLineEditor::setSelectAllonFocusReceived(bool b) -{ - mSelectAllonFocusReceived = b; -} - -void LLLineEditor::onKeystroke() -{ - if (mKeystrokeCallback) - { - mKeystrokeCallback(this); - } - - mSpellCheckStart = mSpellCheckEnd = -1; -} - -void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) -{ - mKeystrokeCallback = boost::bind(callback, _1, user_data); -} - - -bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) -{ - mText.setArg(key, text); - return true; -} - -bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - mLabel.setArg(key, text); - return true; -} - - -void LLLineEditor::updateAllowingLanguageInput() -{ - // Allow Language Text Input only when this LineEditor has - // no prevalidate function attached (as long as other criteria - // common to LLTextEditor). This criterion works - // fine on 1.15.0.2, since all prevalidate func reject any - // non-ASCII characters. I'm not sure on future versions, - // however... - LLWindow* window = getWindow(); - if (!window) - { - // test app, no window available - return; - } - if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator) - { - window->allowLanguageTextInput(this, true); - } - else - { - window->allowLanguageTextInput(this, false); - } -} - -bool LLLineEditor::hasPreeditString() const -{ - return (mPreeditPositions.size() > 1); -} - -void LLLineEditor::resetPreedit() -{ - if (hasSelection()) - { - if (hasPreeditString()) - { - LL_WARNS() << "Preedit and selection!" << LL_ENDL; - deselect(); - } - else - { - deleteSelection(); - } - } - if (hasPreeditString()) - { - const S32 preedit_pos = mPreeditPositions.front(); - mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); - mText.insert(preedit_pos, mPreeditOverwrittenWString); - setCursor(preedit_pos); - - mPreeditWString.clear(); - mPreeditOverwrittenWString.clear(); - mPreeditPositions.clear(); - - // Don't reset key stroke timer nor invoke keystroke callback, - // because a call to updatePreedit should be follow soon in - // normal course of operation, and timer and callback will be - // maintained there. Doing so here made an odd sound. (VWR-3410) - } -} - -void LLLineEditor::updatePreedit(const LLWString &preedit_string, - const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) -{ - // Just in case. - if (mReadOnly) - { - return; - } - - // Note that call to updatePreedit is always preceeded by resetPreedit, - // so we have no existing selection/preedit. - - S32 insert_preedit_at = getCursor(); - - mPreeditWString = preedit_string; - mPreeditPositions.resize(preedit_segment_lengths.size() + 1); - S32 position = insert_preedit_at; - for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) - { - mPreeditPositions[i] = position; - position += preedit_segment_lengths[i]; - } - mPreeditPositions.back() = position; - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); - mText.erase(insert_preedit_at, mPreeditWString.length()); - } - else - { - mPreeditOverwrittenWString.clear(); - } - mText.insert(insert_preedit_at, mPreeditWString); - - mPreeditStandouts = preedit_standouts; - - setCursor(position); - setCursor(mPreeditPositions.front() + caret_position); - - // Update of the preedit should be caused by some key strokes. - mKeystrokeTimer.reset(); - onKeystroke(); - - mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); -} - -bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const -{ - if (control) - { - LLRect control_rect_screen; - localRectToScreen(getRect(), &control_rect_screen); - LLUI::getInstance()->screenRectToGL(control_rect_screen, control); - } - - S32 preedit_left_column, preedit_right_column; - if (hasPreeditString()) - { - preedit_left_column = mPreeditPositions.front(); - preedit_right_column = mPreeditPositions.back(); - } - else - { - preedit_left_column = preedit_right_column = getCursor(); - } - if (preedit_right_column < mScrollHPos) - { - // This should not occure... - return false; - } - - const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); - if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) - { - return false; - } - - if (coord) - { - S32 query_local = findPixelNearestPos(query - getCursor()); - S32 query_screen_x, query_screen_y; - localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y); - LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); - } - - if (bounds) - { - S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor()); - S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness); - if (preedit_left_local > preedit_right_local) - { - // Is this condition possible? - preedit_right_local = preedit_left_local; - } - - LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0); - LLRect preedit_rect_screen; - localRectToScreen(preedit_rect_local, &preedit_rect_screen); - LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); - } - - return true; -} - -void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const -{ - if (hasPreeditString()) - { - *position = mPreeditPositions.front(); - *length = mPreeditPositions.back() - mPreeditPositions.front(); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const -{ - if (hasSelection()) - { - *position = llmin(mSelectionStart, mSelectionEnd); - *length = llabs(mSelectionStart - mSelectionEnd); - } - else - { - *position = mCursorPos; - *length = 0; - } -} - -void LLLineEditor::markAsPreedit(S32 position, S32 length) -{ - deselect(); - setCursor(position); - if (hasPreeditString()) - { - LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; - } - mPreeditWString.assign( LLWString( mText.getWString(), position, length ) ); - if (length > 0) - { - mPreeditPositions.resize(2); - mPreeditPositions[0] = position; - mPreeditPositions[1] = position + length; - mPreeditStandouts.resize(1); - mPreeditStandouts[0] = false; - } - else - { - mPreeditPositions.clear(); - mPreeditStandouts.clear(); - } - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) - { - mPreeditOverwrittenWString = mPreeditWString; - } - else - { - mPreeditOverwrittenWString.clear(); - } -} - -S32 LLLineEditor::getPreeditFontSize() const -{ - return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); -} - -void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) -{ - mReplaceNewlinesWithSpaces = replace; -} - -LLWString LLLineEditor::getConvertedText() const -{ - LLWString text = getWText(); - LLWStringUtil::trim(text); - if (!mReplaceNewlinesWithSpaces) - { - LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. - } - return text; -} - -void LLLineEditor::showContextMenu(S32 x, S32 y) -{ - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (!menu) - { - llassert(LLMenuGL::sMenuContainer != NULL); - menu = LLUICtrlFactory::createFromFile - ("menu_text_editor.xml", - LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); - setContextMenu(menu); - } - - if (menu) - { - gEditMenuHandler = this; - - S32 screen_x, screen_y; - localPointToScreen(x, y, &screen_x, &screen_y); - - setCursorAtLocalPos(x); - if (hasSelection()) - { - if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) - { - deselect(); - } - else - { - setCursor(llmax(mSelectionStart, mSelectionEnd)); - } - } - - bool use_spellcheck = getSpellCheck(), is_misspelled = false; - if (use_spellcheck) - { - mSuggestionList.clear(); - - // If the cursor is on a misspelled word, retrieve suggestions for it - std::string misspelled_word = getMisspelledWord(mCursorPos); - if ((is_misspelled = !misspelled_word.empty())) - { - LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); - } - } - - menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); - menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); - menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); - menu->show(screen_x, screen_y, this); - } -} - -void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) -{ - LLContextMenu* menu = static_cast(mContextMenuHandle.get()); - if (menu) - { - menu->die(); - mContextMenuHandle.markDead(); - } - - if (new_context_menu) - { - mContextMenuHandle = new_context_menu->getHandle(); - } -} - -void LLLineEditor::setFont(const LLFontGL* font) -{ - mGLFont = font; -} +/** + * @file lllineeditor.cpp + * @brief LLLineEditor base class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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. + * + * 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. + * + * 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Text editor widget to let users enter a single line. + +#include "linden_common.h" + +#define LLLINEEDITOR_CPP +#include "lllineeditor.h" + +#include "lltexteditor.h" +#include "llmath.h" +#include "llfontgl.h" +#include "llgl.h" +#include "lltimer.h" + +#include "llcalc.h" +//#include "llclipboard.h" +#include "llcontrol.h" +#include "llbutton.h" +#include "llfocusmgr.h" +#include "llkeyboard.h" +#include "llrect.h" +#include "llresmgr.h" +#include "llspellcheck.h" +#include "llstring.h" +#include "llwindow.h" +#include "llui.h" +#include "lluictrlfactory.h" +#include "llclipboard.h" +#include "llmenugl.h" + +// +// Imported globals +// + +// +// Constants +// + +const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds +const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing +const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing +const F32 AUTO_SCROLL_TIME = 0.05f; +const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval? +const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on + +const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET + +static LLDefaultChildRegistry::Register r1("line_editor"); + +// Compiler optimization, generate extern template +template class LLLineEditor* LLView::getChild( + const std::string& name, bool recurse) const; + +// +// Member functions +// + +LLLineEditor::Params::Params() +: max_length(""), + keystroke_callback("keystroke_callback"), + prevalidator("prevalidator"), + input_prevalidator("input_prevalidator"), + background_image("background_image"), + background_image_disabled("background_image_disabled"), + background_image_focused("background_image_focused"), + bg_image_always_focused("bg_image_always_focused", false), + show_label_focused("show_label_focused", false), + select_on_focus("select_on_focus", false), + revert_on_esc("revert_on_esc", true), + spellcheck("spellcheck", false), + commit_on_focus_lost("commit_on_focus_lost", true), + ignore_tab("ignore_tab", true), + is_password("is_password", false), + allow_emoji("allow_emoji", true), + cursor_color("cursor_color"), + use_bg_color("use_bg_color", false), + bg_color("bg_color"), + text_color("text_color"), + text_readonly_color("text_readonly_color"), + text_tentative_color("text_tentative_color"), + highlight_color("highlight_color"), + preedit_bg_color("preedit_bg_color"), + border(""), + bg_visible("bg_visible"), + text_pad_left("text_pad_left"), + text_pad_right("text_pad_right"), + default_text("default_text") +{ + changeDefault(mouse_opaque, true); + addSynonym(prevalidator, "prevalidate_callback"); + addSynonym(input_prevalidator, "prevalidate_input_callback"); + addSynonym(select_on_focus, "select_all_on_focus_received"); + addSynonym(border, "border"); + addSynonym(label, "watermark_text"); + addSynonym(max_length.chars, "max_length"); +} + +LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) +: LLUICtrl(p), + mMaxLengthBytes(p.max_length.bytes), + mMaxLengthChars(p.max_length.chars), + mCursorPos( 0 ), + mScrollHPos( 0 ), + mTextPadLeft(p.text_pad_left), + mTextPadRight(p.text_pad_right), + mTextLeftEdge(0), // computed in updateTextPadding() below + mTextRightEdge(0), // computed in updateTextPadding() below + mCommitOnFocusLost( p.commit_on_focus_lost ), + mKeystrokeOnEsc(false), + mRevertOnEsc( p.revert_on_esc ), + mKeystrokeCallback( p.keystroke_callback() ), + mIsSelecting( false ), + mSelectionStart( 0 ), + mSelectionEnd( 0 ), + mLastSelectionX(-1), + mLastSelectionY(-1), + mLastSelectionStart(-1), + mLastSelectionEnd(-1), + mBorderThickness( 0 ), + mIgnoreArrowKeys( false ), + mIgnoreTab( p.ignore_tab ), + mDrawAsterixes( p.is_password ), + mAllowEmoji( p.allow_emoji ), + mSpellCheck( p.spellcheck ), + mSpellCheckStart(-1), + mSpellCheckEnd(-1), + mSelectAllonFocusReceived( p.select_on_focus ), + mSelectAllonCommit( true ), + mPassDelete(false), + mReadOnly(false), + mBgImage( p.background_image ), + mBgImageDisabled( p.background_image_disabled ), + mBgImageFocused( p.background_image_focused ), + mShowImageFocused( p.bg_image_always_focused ), + mShowLabelFocused( p.show_label_focused ), + mUseBgColor(p.use_bg_color), + mHaveHistory(false), + mReplaceNewlinesWithSpaces( true ), + mPrevalidator(p.prevalidator()), + mInputPrevalidator(p.input_prevalidator()), + mLabel(p.label), + mCursorColor(p.cursor_color()), + mBgColor(p.bg_color()), + mFgColor(p.text_color()), + mReadOnlyFgColor(p.text_readonly_color()), + mTentativeFgColor(p.text_tentative_color()), + mHighlightColor(p.highlight_color()), + mPreeditBgColor(p.preedit_bg_color()), + mGLFont(p.font), + mContextMenuHandle(), + mShowContextMenu(true) +{ + llassert( mMaxLengthBytes > 0 ); + + LLUICtrl::setEnabled(true); + setEnabled(p.enabled); + + mScrollTimer.reset(); + mTripleClickTimer.reset(); + setText(p.default_text()); + + if (p.initial_value.isProvided() + && !p.control_name.isProvided()) + { + // Initial value often is descriptive, like "Type some ID here" + // and can be longer than size limitation, ignore size + setText(p.initial_value.getValue().asString(), false); + } + + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + LLRect border_rect(getLocalRect()); + // adjust for gl line drawing glitch + border_rect.mTop -= 1; + border_rect.mRight -=1; + LLViewBorder::Params border_p(p.border); + border_p.rect = border_rect; + border_p.follows.flags = FOLLOWS_ALL; + border_p.bevel_style = LLViewBorder::BEVEL_IN; + mBorder = LLUICtrlFactory::create(border_p); + addChild( mBorder ); + + // clamp text padding to current editor size + updateTextPadding(); + setCursor(mText.length()); + + if (mSpellCheck) + { + LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this)); + } + mSpellCheckTimer.reset(); + + updateAllowingLanguageInput(); +} + +LLLineEditor::~LLLineEditor() +{ + mCommitOnFocusLost = false; + + // Make sure no context menu linger around once the widget is deleted + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (menu) + { + menu->hide(); + } + setContextMenu(NULL); + + // calls onCommit() while LLLineEditor still valid + gFocusMgr.releaseFocusIfNeeded( this ); +} + +void LLLineEditor::initFromParams(const LLLineEditor::Params& params) +{ + LLUICtrl::initFromParams(params); + LLUICtrl::setEnabled(true); + setEnabled(params.enabled); +} + +void LLLineEditor::onFocusReceived() +{ + gEditMenuHandler = this; + LLUICtrl::onFocusReceived(); + updateAllowingLanguageInput(); +} + +void LLLineEditor::onFocusLost() +{ + // The call to updateAllowLanguageInput() + // when loosing the keyboard focus *may* + // indirectly invoke handleUnicodeCharHere(), + // so it must be called before onCommit. + updateAllowingLanguageInput(); + + if( mCommitOnFocusLost && mText.getString() != mPrevText) + { + onCommit(); + } + + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + getWindow()->showCursorFromMouseMove(); + + LLUICtrl::onFocusLost(); +} + +// virtual +void LLLineEditor::onCommit() +{ + // put current line into the line history + updateHistory(); + + setControlValue(getValue()); + LLUICtrl::onCommit(); + resetDirty(); + + // Selection on commit needs to be turned off when evaluating maths + // expressions, to allow indication of the error position + if (mSelectAllonCommit) selectAll(); +} + +// Returns true if user changed value at all +// virtual +bool LLLineEditor::isDirty() const +{ + return mText.getString() != mPrevText; +} + +// Clear dirty state +// virtual +void LLLineEditor::resetDirty() +{ + mPrevText = mText.getString(); +} + +// assumes UTF8 text +// virtual +void LLLineEditor::setValue(const LLSD& value ) +{ + setText(value.asString()); +} + +//virtual +LLSD LLLineEditor::getValue() const +{ + return LLSD(getText()); +} + + +// line history support +void LLLineEditor::updateHistory() +{ + // On history enabled line editors, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if( mHaveHistory && getLength() ) + { + if( !mLineHistory.empty() ) + { + // When not empty, last line of history should always be blank. + if( mLineHistory.back().empty() ) + { + // discard the empty line + mLineHistory.pop_back(); + } + else + { + LL_WARNS("") << "Last line of history was not blank." << LL_ENDL; + } + } + + // Add text to history, ignoring duplicates + if( mLineHistory.empty() || getText() != mLineHistory.back() ) + { + mLineHistory.push_back( getText() ); + } + + // Restore the blank line and set mCurrentHistoryLine to point at it + mLineHistory.push_back( "" ); + mCurrentHistoryLine = mLineHistory.end() - 1; + } +} + +void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateTextPadding(); // For clamping side-effect. + setCursor(mCursorPos); // For clamping side-effect. +} + +void LLLineEditor::setEnabled(bool enabled) +{ + mReadOnly = !enabled; + setTabStop(!mReadOnly); + updateAllowingLanguageInput(); +} + + +void LLLineEditor::setMaxTextLength(S32 max_text_length) +{ + S32 max_len = llmax(0, max_text_length); + mMaxLengthBytes = max_len; +} + +void LLLineEditor::setMaxTextChars(S32 max_text_chars) +{ + S32 max_chars = llmax(0, max_text_chars); + mMaxLengthChars = max_chars; +} + +void LLLineEditor::getTextPadding(S32 *left, S32 *right) +{ + *left = mTextPadLeft; + *right = mTextPadRight; +} + +void LLLineEditor::setTextPadding(S32 left, S32 right) +{ + mTextPadLeft = left; + mTextPadRight = right; + updateTextPadding(); +} + +void LLLineEditor::updateTextPadding() +{ + mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth()); + mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth()); +} + + +void LLLineEditor::setText(const LLStringExplicit &new_text) +{ + setText(new_text, true); +} + +void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit) +{ + // If new text is identical, don't copy and don't move insertion point + if (mText.getString() == new_text) + { + return; + } + + // Check to see if entire field is selected. + S32 len = mText.length(); + bool all_selected = (len > 0) + && (( mSelectionStart == 0 && mSelectionEnd == len ) + || ( mSelectionStart == len && mSelectionEnd == 0 )); + + // Do safe truncation so we don't split multi-byte characters + // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor + all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); + + std::string truncated_utf8 = new_text; + if (!mAllowEmoji) + { + // Cut emoji symbols if exist + utf8str_remove_emojis(truncated_utf8); + } + if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes) + { + truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes); + } + mText.assign(truncated_utf8); + + if (use_size_limit && mMaxLengthChars) + { + mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars)); + } + + if (all_selected) + { + // ...keep whole thing selected + selectAll(); + } + else + { + // try to preserve insertion point, but deselect text + deselect(); + } + setCursor(llmin((S32)mText.length(), getCursor())); + + // Set current history line to end of history. + if (mLineHistory.empty()) + { + mCurrentHistoryLine = mLineHistory.end(); + } + else + { + mCurrentHistoryLine = mLineHistory.end() - 1; + } + + mPrevText = mText; +} + + +// Picks a new cursor position based on the actual screen size of text being drawn. +void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) +{ + S32 cursor_pos = calcCursorPos(local_mouse_x); + + S32 left_pos = llmin( mSelectionStart, cursor_pos ); + S32 length = llabs( mSelectionStart - cursor_pos ); + const LLWString& substr = mText.getWString().substr(left_pos, length); + + if (mIsSelecting && !prevalidateInput(substr)) + return; + + setCursor(cursor_pos); +} + +void LLLineEditor::setCursor( S32 pos ) +{ + S32 old_cursor_pos = getCursor(); + mCursorPos = llclamp( pos, 0, mText.length()); + + // position of end of next character after cursor + S32 pixels_after_scroll = findPixelNearestPos(); + if( pixels_after_scroll > mTextRightEdge ) + { + S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos); + S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left))); + // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters) + // or first character if cursor is at beginning + S32 new_last_visible_char = llmax(0, getCursor() - 1); + S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char); + if (old_cursor_pos == last_visible_char) + { + mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD)); + } + else + { + mScrollHPos = min_scroll; + } + } + else if (getCursor() < mScrollHPos) + { + if (old_cursor_pos == mScrollHPos) + { + mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL)); + } + else + { + mScrollHPos = getCursor(); + } + } +} + + +void LLLineEditor::setCursorToEnd() +{ + setCursor(mText.length()); + deselect(); +} + +void LLLineEditor::resetScrollPosition() +{ + mScrollHPos = 0; + // make sure cursor says in visible range + setCursor(getCursor()); +} + +bool LLLineEditor::canDeselect() const +{ + return hasSelection(); +} + +void LLLineEditor::deselect() +{ + mSelectionStart = 0; + mSelectionEnd = 0; + mIsSelecting = false; +} + + +void LLLineEditor::startSelection() +{ + mIsSelecting = true; + mSelectionStart = getCursor(); + mSelectionEnd = getCursor(); +} + +void LLLineEditor::endSelection() +{ + if( mIsSelecting ) + { + mIsSelecting = false; + mSelectionEnd = getCursor(); + } +} + +bool LLLineEditor::canSelectAll() const +{ + return true; +} + +void LLLineEditor::selectAll() +{ + if (!prevalidateInput(mText.getWString())) + { + return; + } + + mSelectionStart = mText.length(); + mSelectionEnd = 0; + setCursor(mSelectionEnd); + //mScrollHPos = 0; + mIsSelecting = true; + updatePrimary(); +} + +bool LLLineEditor::getSpellCheck() const +{ + return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); +} + +const std::string& LLLineEditor::getSuggestion(U32 index) const +{ + return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null; +} + +U32 LLLineEditor::getSuggestionCount() const +{ + return mSuggestionList.size(); +} + +void LLLineEditor::replaceWithSuggestion(U32 index) +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) + { + LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]); + if (!mAllowEmoji) + { + // Cut emoji symbols if exist + wstring_remove_emojis(suggestion); + } + if (suggestion.empty()) + return; + + deselect(); + + // Delete the misspelled word + mText.erase(it->first, it->second - it->first); + + // Insert the suggestion in its place + mText.insert(it->first, suggestion); + setCursor(it->first + (S32)suggestion.length()); + + break; + } + } + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLLineEditor::addToDictionary() +{ + if (canAddToDictionary()) + { + LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos)); + } +} + +bool LLLineEditor::canAddToDictionary() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +void LLLineEditor::addToIgnore() +{ + if (canAddToIgnore()) + { + LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos)); + } +} + +bool LLLineEditor::canAddToIgnore() const +{ + return (getSpellCheck()) && (isMisspelledWord(mCursorPos)); +} + +std::string LLLineEditor::getMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first)); + } + } + return LLStringUtil::null; +} + +bool LLLineEditor::isMisspelledWord(U32 pos) const +{ + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + if ( (it->first <= pos) && (it->second >= pos) ) + { + return true; + } + } + return false; +} + +void LLLineEditor::onSpellCheckSettingsChange() +{ + // Recheck the spelling on every change + mMisspellRanges.clear(); + mSpellCheckStart = mSpellCheckEnd = -1; +} + +bool LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + setFocus( true ); + mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); + + if (mSelectionEnd == 0 && mSelectionStart == mText.length()) + { + // if everything is selected, handle this as a normal click to change insertion point + handleMouseDown(x, y, mask); + } + else + { + const LLWString& wtext = mText.getWString(); + + bool doSelectAll = true; + + // Select the word we're on + if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) + { + S32 old_selection_start = mLastSelectionStart; + S32 old_selection_end = mLastSelectionEnd; + + // Select word the cursor is over + while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] )) + { // Find the start of the word + mCursorPos--; + } + startSelection(); + + while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) + { // Find the end of the word + mCursorPos++; + } + mSelectionEnd = mCursorPos; + + // If nothing changed, then the word was already selected. Select the whole line. + doSelectAll = (old_selection_start == mSelectionStart) && + (old_selection_end == mSelectionEnd); + } + + if ( doSelectAll ) + { // Select everything + selectAll(); + } + } + + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + + // delay cursor flashing + mKeystrokeTimer.reset(); + + // take selection to 'primary' clipboard + updatePrimary(); + + return true; +} + +bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Check first whether the "clear search" button wants to deal with this. + if(childrenHandleMouseDown(x, y, mask) != NULL) + { + return true; + } + + if (!mSelectAllonFocusReceived + || gFocusMgr.getKeyboardFocus() == this) + { + mLastSelectionStart = -1; + mLastSelectionStart = -1; + + if (mask & MASK_SHIFT) + { + // assume we're starting a drag select + mIsSelecting = true; + + // Handle selection extension + S32 old_cursor_pos = getCursor(); + setCursorAtLocalPos(x); + + if (hasSelection()) + { + /* Mac-like behavior - extend selection towards the cursor + if (getCursor() < mSelectionStart + && getCursor() < mSelectionEnd) + { + // ...left of selection + mSelectionStart = llmax(mSelectionStart, mSelectionEnd); + mSelectionEnd = getCursor(); + } + else if (getCursor() > mSelectionStart + && getCursor() > mSelectionEnd) + { + // ...right of selection + mSelectionStart = llmin(mSelectionStart, mSelectionEnd); + mSelectionEnd = getCursor(); + } + else + { + mSelectionEnd = getCursor(); + } + */ + // Windows behavior + mSelectionEnd = getCursor(); + } + else + { + mSelectionStart = old_cursor_pos; + mSelectionEnd = getCursor(); + } + } + else + { + if (mTripleClickTimer.hasExpired()) + { + // Save selection for word/line selecting on double-click + mLastSelectionStart = mSelectionStart; + mLastSelectionEnd = mSelectionEnd; + + // Move cursor and deselect for regular click + setCursorAtLocalPos( x ); + deselect(); + startSelection(); + } + else // handle triple click + { + selectAll(); + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + } + } + + gFocusMgr.setMouseCapture( this ); + } + + setFocus(true); + + // delay cursor flashing + mKeystrokeTimer.reset(); + + if (mMouseDownSignal) + (*mMouseDownSignal)(this,x,y,mask); + + return true; +} + +bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + // LL_INFOS() << "MiddleMouseDown" << LL_ENDL; + setFocus( true ); + if( canPastePrimary() ) + { + setCursorAtLocalPos(x); + pastePrimary(); + } + return true; +} + +bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + setFocus(true); + if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu()) + { + showContextMenu(x, y); + } + return true; +} + +bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = false; + // Check first whether the "clear search" button wants to deal with this. + if(!hasMouseCapture()) + { + if(childrenHandleHover(x, y, mask) != NULL) + { + return true; + } + } + + if( (hasMouseCapture()) && mIsSelecting ) + { + if (x != mLastSelectionX || y != mLastSelectionY) + { + mLastSelectionX = x; + mLastSelectionY = y; + } + // Scroll if mouse cursor outside of bounds + if (mScrollTimer.hasExpired()) + { + S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); + mScrollTimer.reset(); + mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME); + if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) ) + { + // Scroll to the left + mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length()); + } + else + if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) ) + { + // If scrolling one pixel would make a difference... + S32 pixels_after_scrolling_one_char = findPixelNearestPos(1); + if( pixels_after_scrolling_one_char >= mTextRightEdge ) + { + // ...scroll to the right + mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length()); + } + } + } + + setCursorAtLocalPos( x ); + mSelectionEnd = getCursor(); + + // delay cursor flashing + mKeystrokeTimer.reset(); + + getWindow()->setCursor(UI_CURSOR_IBEAM); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; + handled = true; + } + + if( !handled ) + { + getWindow()->setCursor(UI_CURSOR_IBEAM); + LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; + handled = true; + } + + return handled; +} + + +bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + gFocusMgr.setMouseCapture( NULL ); + handled = true; + } + + // Check first whether the "clear search" button wants to deal with this. + if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) + { + return true; + } + + if( mIsSelecting ) + { + setCursorAtLocalPos( x ); + mSelectionEnd = getCursor(); + + handled = true; + } + + if( handled ) + { + // delay cursor flashing + mKeystrokeTimer.reset(); + + // take selection to 'primary' clipboard + updatePrimary(); + } + + // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually. + if (mMouseUpSignal) + (*mMouseUpSignal)(this,x,y, mask); + return handled; +} + + +// Remove a single character from the text +void LLLineEditor::removeChar() +{ + if( getCursor() > 0 ) + { + if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) + return; + + mText.erase(getCursor() - 1, 1); + + setCursor(getCursor() - 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } +} + +void LLLineEditor::addChar(const llwchar uni_char) +{ + if (!mAllowEmoji && LLStringOps::isEmoji(uni_char)) + return; + + llwchar new_c = uni_char; + if (hasSelection()) + { + deleteSelection(); + } + else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) + return; + + mText.erase(getCursor(), 1); + } + + S32 cur_bytes = mText.getString().size(); + + S32 new_bytes = wchar_utf8_length(new_c); + + bool allow_char = true; + + // Check byte length limit + if ((new_bytes + cur_bytes) > mMaxLengthBytes) + { + allow_char = false; + } + else if (mMaxLengthChars) + { + S32 wide_chars = mText.getWString().size(); + if ((wide_chars + 1) > mMaxLengthChars) + { + allow_char = false; + } + } + + if (allow_char) + { + // Will we need to scroll? + LLWString w_buf; + w_buf.assign(1, new_c); + + mText.insert(getCursor(), w_buf); + setCursor(getCursor() + 1); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + + getWindow()->hideCursorUntilMouseMove(); +} + +// Extends the selection box to the new cursor position +void LLLineEditor::extendSelection( S32 new_cursor_pos ) +{ + if( !mIsSelecting ) + { + startSelection(); + } + + S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); + S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; + + setCursor(new_cursor_pos); + mSelectionEnd = getCursor(); +} + + +void LLLineEditor::setSelection(S32 start, S32 end) +{ + S32 len = mText.length(); + + mIsSelecting = true; + + // JC, yes, this seems odd, but I think you have to presume a + // selection dragged from the end towards the start. + mSelectionStart = llclamp(end, 0, len); + mSelectionEnd = llclamp(start, 0, len); + setCursor(start); +} + +void LLLineEditor::setDrawAsterixes(bool b) +{ + mDrawAsterixes = b; + updateAllowingLanguageInput(); +} + +S32 LLLineEditor::prevWordPos(S32 cursorPos) const +{ + const LLWString& wtext = mText.getWString(); + while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') ) + { + cursorPos--; + } + while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) + { + cursorPos--; + } + return cursorPos; +} + +S32 LLLineEditor::nextWordPos(S32 cursorPos) const +{ + const LLWString& wtext = mText.getWString(); + while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) + { + cursorPos++; + } + while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') ) + { + cursorPos++; + } + return cursorPos; +} + + +bool LLLineEditor::handleSelectionKey(KEY key, MASK mask) +{ + bool handled = false; + + if( mask & MASK_SHIFT ) + { + handled = true; + + switch( key ) + { + case KEY_LEFT: + if( 0 < getCursor() ) + { + S32 cursorPos = getCursor() - 1; + if( mask & MASK_CONTROL ) + { + cursorPos = prevWordPos(cursorPos); + } + extendSelection( cursorPos ); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + break; + + case KEY_RIGHT: + if( getCursor() < mText.length()) + { + S32 cursorPos = getCursor() + 1; + if( mask & MASK_CONTROL ) + { + cursorPos = nextWordPos(cursorPos); + } + extendSelection( cursorPos ); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + break; + + case KEY_PAGE_UP: + case KEY_HOME: + extendSelection( 0 ); + break; + + case KEY_PAGE_DOWN: + case KEY_END: + { + S32 len = mText.length(); + if( len ) + { + extendSelection( len ); + } + break; + } + + default: + handled = false; + break; + } + } + + if(handled) + { + // take selection to 'primary' clipboard + updatePrimary(); + } + + return handled; +} + +void LLLineEditor::deleteSelection() +{ + if( !mReadOnly && hasSelection() ) + { + S32 left_pos, selection_length; + getSelectionRange(&left_pos, &selection_length); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; + + mText.erase(left_pos, selection_length); + deselect(); + setCursor(left_pos); + } +} + +bool LLLineEditor::canCut() const +{ + return !mReadOnly && !mDrawAsterixes && hasSelection(); +} + +// cut selection to clipboard +void LLLineEditor::cut() +{ + if( canCut() ) + { + S32 left_pos, length; + getSelectionRange(&left_pos, &length); + const LLWString& selection = mText.getWString().substr(left_pos, length); + + if (!prevalidateInput(selection)) + return; + + // Prepare for possible rollback + LLLineEditorRollback rollback( this ); + + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); + deleteSelection(); + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + } + } +} + +bool LLLineEditor::canCopy() const +{ + return !mDrawAsterixes && hasSelection(); +} + + +// copy selection to clipboard +void LLLineEditor::copy() +{ + if( canCopy() ) + { + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); + } +} + +bool LLLineEditor::canPaste() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(); +} + +void LLLineEditor::paste() +{ + bool is_primary = false; + pasteHelper(is_primary); +} + +void LLLineEditor::pastePrimary() +{ + bool is_primary = true; + pasteHelper(is_primary); +} + +// paste from primary (is_primary==true) or clipboard (is_primary==false) +void LLLineEditor::pasteHelper(bool is_primary) +{ + bool can_paste_it; + if (is_primary) + { + can_paste_it = canPastePrimary(); + } + else + { + can_paste_it = canPaste(); + } + + if (can_paste_it) + { + LLWString paste; + LLClipboard::instance().pasteFromClipboard(paste, is_primary); + + if (!paste.empty()) + { + if (!mAllowEmoji) + { + wstring_remove_emojis(paste); + } + + if (!prevalidateInput(paste)) + return; + + // Prepare for possible rollback + LLLineEditorRollback rollback(this); + + // Delete any selected characters + if ((!is_primary) && hasSelection()) + { + deleteSelection(); + } + + // Clean up string (replace tabs and returns and remove characters that our fonts don't support.) + LLWString clean_string(paste); + LLWStringUtil::replaceTabsWithSpaces(clean_string, 1); + //clean_string = wstring_detabify(paste, 1); + LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character + + // Insert the string + + // Check to see that the size isn't going to be larger than the max number of bytes + U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText); + + if ( available_bytes < (U32) wstring_utf8_length(clean_string) ) + { // Doesn't all fit + llwchar current_symbol = clean_string[0]; + U32 wchars_that_fit = 0; + U32 total_bytes = wchar_utf8_length(current_symbol); + + //loop over the "wide" characters (symbols) + //and check to see how large (in bytes) each symbol is. + while ( total_bytes <= available_bytes ) + { + //while we still have available bytes + //"accept" the current symbol and check the size + //of the next one + current_symbol = clean_string[++wchars_that_fit]; + total_bytes += wchar_utf8_length(current_symbol); + } + // Truncate the clean string at the limit of what will fit + clean_string = clean_string.substr(0, wchars_that_fit); + LLUI::getInstance()->reportBadKeystroke(); + } + + if (mMaxLengthChars) + { + U32 available_chars = mMaxLengthChars - mText.getWString().size(); + + if (available_chars < clean_string.size()) + { + clean_string = clean_string.substr(0, available_chars); + } + + LLUI::getInstance()->reportBadKeystroke(); + } + + mText.insert(getCursor(), clean_string); + setCursor( getCursor() + (S32)clean_string.length() ); + deselect(); + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + } + } + } +} + +// copy selection to primary +void LLLineEditor::copyPrimary() +{ + if( canCopy() ) + { + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true); + } +} + +bool LLLineEditor::canPastePrimary() const +{ + return !mReadOnly && LLClipboard::instance().isTextAvailable(true); +} + +void LLLineEditor::updatePrimary() +{ + if(canCopy() ) + { + copyPrimary(); + } +} + +bool LLLineEditor::handleSpecialKey(KEY key, MASK mask) +{ + bool handled = false; + + switch( key ) + { + case KEY_INSERT: + if (mask == MASK_NONE) + { + gKeyboard->toggleInsertMode(); + } + + handled = true; + break; + + case KEY_BACKSPACE: + if (!mReadOnly) + { + //LL_INFOS() << "Handling backspace" << LL_ENDL; + if( hasSelection() ) + { + deleteSelection(); + } + else + if( 0 < getCursor() ) + { + removeChar(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + } + handled = true; + break; + + case KEY_PAGE_UP: + case KEY_HOME: + if (!mIgnoreArrowKeys) + { + setCursor(0); + handled = true; + } + break; + + case KEY_PAGE_DOWN: + case KEY_END: + if (!mIgnoreArrowKeys) + { + S32 len = mText.length(); + if( len ) + { + setCursor(len); + } + handled = true; + } + break; + + case KEY_LEFT: + if (mIgnoreArrowKeys && mask == MASK_NONE) + break; + if ((mask & MASK_ALT) == 0) + { + if( hasSelection() ) + { + setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd )); + } + else + if( 0 < getCursor() ) + { + S32 cursorPos = getCursor() - 1; + if( mask & MASK_CONTROL ) + { + cursorPos = prevWordPos(cursorPos); + } + setCursor(cursorPos); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + case KEY_RIGHT: + if (mIgnoreArrowKeys && mask == MASK_NONE) + break; + if ((mask & MASK_ALT) == 0) + { + if (hasSelection()) + { + setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd)); + } + else + if (getCursor() < mText.length()) + { + S32 cursorPos = getCursor() + 1; + if( mask & MASK_CONTROL ) + { + cursorPos = nextWordPos(cursorPos); + } + setCursor(cursorPos); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + // handle ctrl-uparrow if we have a history enabled line editor. + case KEY_UP: + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) + { + if (mCurrentHistoryLine > mLineHistory.begin()) + { + mText.assign(*(--mCurrentHistoryLine)); + setCursorToEnd(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + // handle [ctrl]-downarrow if we have a history enabled line editor + case KEY_DOWN: + if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask))) + { + if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1) + { + mText.assign( *(++mCurrentHistoryLine) ); + setCursorToEnd(); + } + else + { + LLUI::getInstance()->reportBadKeystroke(); + } + handled = true; + } + break; + + case KEY_RETURN: + // store sent line in history + updateHistory(); + break; + + case KEY_ESCAPE: + if (mRevertOnEsc && mText.getString() != mPrevText) + { + setText(mPrevText); + // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged) + if (mKeystrokeOnEsc) + { + onKeystroke(); + } + } + break; + + default: + break; + } + + return handled; +} + + +bool LLLineEditor::handleKeyHere(KEY key, MASK mask ) +{ + bool handled = false; + bool selection_modified = false; + + if ( gFocusMgr.getKeyboardFocus() == this ) + { + LLLineEditorRollback rollback( this ); + + if( !handled ) + { + handled = handleSelectionKey( key, mask ); + selection_modified = handled; + } + + // Handle most keys only if the text editor is writeable. + if ( !mReadOnly ) + { + if( !handled ) + { + handled = handleSpecialKey( key, mask ); + } + } + + if( handled ) + { + mKeystrokeTimer.reset(); + + // Most keystrokes will make the selection box go away, but not all will. + if( !selection_modified && + KEY_SHIFT != key && + KEY_CONTROL != key && + KEY_ALT != key && + KEY_CAPSLOCK != key) + { + deselect(); + } + + bool prevalidator_failed = false; + + // If read-only, don't allow changes + bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText()); + + // Validate new string and rollback the keystroke if needed. + if (!need_to_rollback && mPrevalidator) + { + prevalidator_failed = !mPrevalidator.validate(mText.getWString()); + need_to_rollback |= prevalidator_failed; + } + + if (need_to_rollback) + { + rollback.doRollback(this); + + LLUI::getInstance()->reportBadKeystroke(); + if (prevalidator_failed) + { + mPrevalidator.showLastErrorUsingTimeout(); + } + } + + // Notify owner if requested + if (!need_to_rollback && handled) + { + onKeystroke(); + if ( (!selection_modified) && (KEY_BACKSPACE == key) ) + { + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } + } + } + + return handled; +} + + +bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char) +{ + if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL + { + return false; + } + + bool handled = false; + + if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly) + { + handled = true; + + LLLineEditorRollback rollback( this ); + + { + LLWString u_char; + u_char.assign(1, uni_char); + if (!prevalidateInput(u_char)) + return handled; + } + + addChar(uni_char); + + mKeystrokeTimer.reset(); + + deselect(); + + // Validate new string and rollback the keystroke if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback( this ); + + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + + // Notify owner if requested + if (!need_to_rollback && handled) + { + // HACK! The only usage of this callback doesn't do anything with the character. + // We'll have to do something about this if something ever changes! - Doug + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } + return handled; +} + + +bool LLLineEditor::canDoDelete() const +{ + return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) ); +} + +void LLLineEditor::doDelete() +{ + if (canDoDelete() && mText.length() > 0) + { + // Prepare for possible rollback + LLLineEditorRollback rollback( this ); + + if (hasSelection()) + { + deleteSelection(); + } + else if ( getCursor() < mText.length()) + { + const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); + + if (!prevalidateInput(text_to_delete)) + { + onKeystroke(); + return; + } + setCursor(getCursor() + 1); + removeChar(); + } + + // Validate new string and rollback the if needed. + bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString()); + if (need_to_rollback) + { + rollback.doRollback(this); + LLUI::getInstance()->reportBadKeystroke(); + mPrevalidator.showLastErrorUsingTimeout(); + } + else + { + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); + } + } +} + + +void LLLineEditor::drawBackground() +{ + F32 alpha = getCurrentTransparency(); + if (mUseBgColor) + { + gl_rect_2d(getLocalRect(), mBgColor % alpha, true); + } + else + { + bool has_focus = hasFocus(); + LLUIImage* image; + if (mReadOnly) + { + image = mBgImageDisabled; + } + else if (has_focus || mShowImageFocused) + { + image = mBgImageFocused; + } + else + { + image = mBgImage; + } + + if (!image) return; + // optionally draw programmatic border + if (has_focus) + { + LLColor4 tmp_color = gFocusMgr.getFocusColor(); + tmp_color.setAlpha(alpha); + image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), + tmp_color, + gFocusMgr.getFocusFlashWidth()); + } + LLColor4 tmp_color = UI_VERTEX_COLOR; + tmp_color.setAlpha(alpha); + image->draw(getLocalRect(), tmp_color); + } +} + +//virtual +void LLLineEditor::draw() +{ + F32 alpha = getDrawContext().mAlpha; + S32 text_len = mText.length(); + static LLUICachedControl lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); + static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); + static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); + static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); + static LLUICachedControl preedit_marker_thickness ("UIPreeditMarkerThickness", 0); + static LLUICachedControl preedit_standout_brightness ("UIPreeditStandoutBrightness", 0); + static LLUICachedControl preedit_standout_gap ("UIPreeditStandoutGap", 0); + static LLUICachedControl preedit_standout_position ("UIPreeditStandoutPosition", 0); + static LLUICachedControl preedit_standout_thickness ("UIPreeditStandoutThickness", 0); + + std::string saved_text; + if (mDrawAsterixes) + { + saved_text = mText.getString(); + std::string text; + for (S32 i = 0; i < mText.length(); i++) + { + text += PASSWORD_ASTERISK; + } + mText = text; + } + + // draw rectangle for the background + LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + background.stretch( -mBorderThickness ); + + S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2; + if (mSpellCheck) + { + lineeditor_v_pad += 1; + } + + drawBackground(); + + // draw text + + // With viewer-2 art files, input region is 2 pixels up + S32 cursor_bottom = background.mBottom + 2; + S32 cursor_top = background.mTop - 1; + + LLColor4 text_color; + if (!mReadOnly) + { + if (!getTentative()) + { + text_color = mFgColor.get(); + } + else + { + text_color = mTentativeFgColor.get(); + } + } + else + { + text_color = mReadOnlyFgColor.get(); + } + text_color.setAlpha(alpha); + LLColor4 label_color = mTentativeFgColor.get(); + label_color.setAlpha(alpha); + + if (hasPreeditString()) + { + // Draw preedit markers. This needs to be before drawing letters. + for (U32 i = 0; i < mPreeditStandouts.size(); i++) + { + const S32 preedit_left = mPreeditPositions[i]; + const S32 preedit_right = mPreeditPositions[i + 1]; + if (preedit_right > mScrollHPos) + { + S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor()); + S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight); + if (preedit_pixels_left >= background.mRight) + { + break; + } + if (mPreeditStandouts[i]) + { + gl_rect_2d(preedit_pixels_left + preedit_standout_gap, + background.mBottom + preedit_standout_position, + preedit_pixels_right - preedit_standout_gap - 1, + background.mBottom + preedit_standout_position - preedit_standout_thickness, + (text_color * preedit_standout_brightness + + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/)); + } + else + { + gl_rect_2d(preedit_pixels_left + preedit_marker_gap, + background.mBottom + preedit_marker_position, + preedit_pixels_right - preedit_marker_gap - 1, + background.mBottom + preedit_marker_position - preedit_marker_thickness, + (text_color * preedit_marker_brightness + + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/)); + } + } + } + } + + S32 rendered_text = 0; + F32 rendered_pixels_right = (F32)mTextLeftEdge; + F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad; + + if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() ) + { + S32 select_left; + S32 select_right; + if (mSelectionStart < mSelectionEnd) + { + select_left = mSelectionStart; + select_right = mSelectionEnd; + } + else + { + select_left = mSelectionEnd; + select_right = mSelectionStart; + } + + if( select_left > mScrollHPos ) + { + // unselected, left side + rendered_text = mGLFont->render( + mText, mScrollHPos, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + select_left - mScrollHPos, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + + if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) + { + LLColor4 color = mHighlightColor; + color.setAlpha(alpha); + // selected middle + S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); + width = llmin(width, mTextRightEdge - ll_round(rendered_pixels_right)); + gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color); + + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); + rendered_text += mGLFont->render( + mText, mScrollHPos + rendered_text, + rendered_pixels_right, text_bottom, + tmp_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + select_right - mScrollHPos - rendered_text, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + + if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) ) + { + // unselected, right side + rendered_text += mGLFont->render( + mText, mScrollHPos + rendered_text, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + } + else + { + rendered_text = mGLFont->render( + mText, mScrollHPos, + rendered_pixels_right, text_bottom, + text_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } +#if 1 // for when we're ready for image art. + mBorder->setVisible(false); // no more programmatic art. +#endif + + if ( (getSpellCheck()) && (mText.length() > 2) ) + { + // Calculate start and end indices for the first and last visible word + U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text); + + if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) ) + { + const LLWString& text = mText.getWString().substr(start, end); + + // Find the start of the first word + U32 word_start = 0, word_end = 0; + while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) ) + { + word_start++; + } + + // Iterate over all words in the text block and check them one by one + mMisspellRanges.clear(); + while (word_start < text.length()) + { + // Find the end of the current word (special case handling for "'" when it's used as a contraction) + word_end = word_start + 1; + while ( (word_end < text.length()) && + ((LLWStringUtil::isPartOfWord(text[word_end])) || + ((L'\'' == text[word_end]) && (word_end + 1 < text.length()) && + (LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) ) + { + word_end++; + } + if (word_end > text.length()) + { + break; + } + + // Don't process words shorter than 3 characters + std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start)); + if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) + { + mMisspellRanges.push_back(std::pair(start + word_start, start + word_end)); + } + + // Find the start of the next word + word_start = word_end + 1; + while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) ) + { + word_start++; + } + } + + mSpellCheckStart = start; + mSpellCheckEnd = end; + } + + // Draw squiggly lines under any (visible) misspelled words + for (std::list >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it) + { + // Skip over words that aren't (partially) visible + if ( ((it->first < start) && (it->second < start)) || (it->first > end) ) + { + continue; + } + + // Skip the current word if the user is still busy editing it + if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) ) + { + continue; + } + + S32 pxWidth = getRect().getWidth(); + S32 pxStart = findPixelNearestPos(it->first - getCursor()); + if (pxStart > pxWidth) + { + continue; + } + S32 pxEnd = findPixelNearestPos(it->second - getCursor()); + if (pxEnd > pxWidth) + { + pxEnd = pxWidth; + } + + S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight()); + + gGL.color4ub(255, 0, 0, 200); + while (pxStart + 1 < pxEnd) + { + gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2); + if (pxStart + 3 < pxEnd) + { + gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1); + } + pxStart += 4; + } + } + } + + // If we're editing... + if( hasFocus()) + { + //mBorder->setVisible(true); // ok, programmer art just this once. + // (Flash the cursor every half second) + if (!mReadOnly && gFocusMgr.getAppHasFocus()) + { + F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); + if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) + { + S32 cursor_left = findPixelNearestPos(); + cursor_left -= lineeditor_cursor_thickness / 2; + S32 cursor_right = cursor_left + lineeditor_cursor_thickness; + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + const LLWString space(utf8str_to_wstring(std::string(" "))); + S32 wswidth = mGLFont->getWidth(space.c_str()); + S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1; + cursor_right = cursor_left + llmax(wswidth, width); + } + // Use same color as text for the Cursor + gl_rect_2d(cursor_left, cursor_top, + cursor_right, cursor_bottom, text_color); + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); + mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, + tmp_color, + LLFontGL::LEFT, LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + 1); + } + + // Make sure the IME is in the right place + S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position + LLRect screen_pos = calcScreenRect(); + LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad ); + + ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]); + ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]); + getWindow()->setLanguageTextInput( ime_pos ); + } + } + + //draw label if no text is provided + //but we should draw it in a different color + //to give indication that it is not text you typed in + if (0 == mText.length() && (mReadOnly || mShowLabelFocused)) + { + mGLFont->render(mLabel.getWString(), 0, + mTextLeftEdge, (F32)text_bottom, + label_color, + LLFontGL::LEFT, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right, false); + } + + + // Draw children (border) + //mBorder->setVisible(true); + mBorder->setKeyboardFocusHighlight( true ); + LLView::draw(); + mBorder->setKeyboardFocusHighlight( false ); + //mBorder->setVisible(false); + } + else // does not have keyboard input + { + // draw label if no text provided + if (0 == mText.length()) + { + mGLFont->render(mLabel.getWString(), 0, + mTextLeftEdge, (F32)text_bottom, + label_color, + LLFontGL::LEFT, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + S32_MAX, + mTextRightEdge - ll_round(rendered_pixels_right), + &rendered_pixels_right); + } + // Draw children (border) + LLView::draw(); + } + + if (mDrawAsterixes) + { + mText = saved_text; + } +} + + +// Returns the local screen space X coordinate associated with the text cursor position. +S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const +{ + S32 dpos = getCursor() - mScrollHPos + cursor_offset; + S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge; + return result; +} + +S32 LLLineEditor::calcCursorPos(S32 mouse_x) +{ + const llwchar* wtext = mText.getWString().c_str(); + LLWString asterix_text; + if (mDrawAsterixes) + { + for (S32 i = 0; i < mText.length(); i++) + { + asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); + } + wtext = asterix_text.c_str(); + } + + S32 cur_pos = mScrollHPos + + mGLFont->charFromPixelOffset( + wtext, mScrollHPos, + (F32)(mouse_x - mTextLeftEdge), + (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive + + return cur_pos; +} +//virtual +void LLLineEditor::clear() +{ + mText.clear(); + setCursor(0); +} + +//virtual +void LLLineEditor::onTabInto() +{ + selectAll(); + LLUICtrl::onTabInto(); +} + +//virtual +bool LLLineEditor::acceptsTextInput() const +{ + return true; +} + +// Start or stop the editor from accepting text-editing keystrokes +void LLLineEditor::setFocus( bool new_state ) +{ + bool old_state = hasFocus(); + + if (!new_state) + { + getWindow()->allowLanguageTextInput(this, false); + } + + + // getting focus when we didn't have it before, and we want to select all + if (!old_state && new_state && mSelectAllonFocusReceived) + { + selectAll(); + // We don't want handleMouseUp() to "finish" the selection (and thereby + // set mSelectionEnd to where the mouse is), so we finish the selection + // here. + mIsSelecting = false; + } + + if( new_state ) + { + gEditMenuHandler = this; + + // Don't start the cursor flashing right away + mKeystrokeTimer.reset(); + } + else + { + // Not really needed, since loss of keyboard focus should take care of this, + // but limited paranoia is ok. + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } + + endSelection(); + } + + LLUICtrl::setFocus( new_state ); + + if (new_state) + { + // Allow Language Text Input only when this LineEditor has + // no prevalidate function attached. This criterion works + // fine on 1.15.0.2, since all prevalidate func reject any + // non-ASCII characters. I'm not sure on future versions, + // however. + getWindow()->allowLanguageTextInput(this, !mPrevalidator); + } +} + +//virtual +void LLLineEditor::setRect(const LLRect& rect) +{ + LLUICtrl::setRect(rect); + if (mBorder) + { + LLRect border_rect = mBorder->getRect(); + // Scalable UI somehow made these rectangles off-by-one. + // I don't know why. JC + border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom, + rect.getWidth()-1, rect.getHeight()-1); + mBorder->setRect(border_rect); + } +} + +void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator) +{ + mPrevalidator = validator; + updateAllowingLanguageInput(); +} + +void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator) +{ + mInputPrevalidator = validator; + updateAllowingLanguageInput(); +} + +bool LLLineEditor::prevalidateInput(const LLWString& wstr) +{ + return mInputPrevalidator.validate(wstr); +} + +// static +bool LLLineEditor::postvalidateFloat(const std::string &str) +{ + LLLocale locale(LLLocale::USER_LOCALE); + + bool success = true; + bool has_decimal = false; + bool has_digit = false; + + LLWString trimmed = utf8str_to_wstring(str); + LLWStringUtil::trim(trimmed); + S32 len = trimmed.length(); + if( 0 < len ) + { + S32 i = 0; + + // First character can be a negative sign + if( '-' == trimmed[0] ) + { + i++; + } + + // May be a comma or period, depending on the locale + llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint(); + + for( ; i < len; i++ ) + { + if( decimal_point == trimmed[i] ) + { + if( has_decimal ) + { + // can't have two + success = false; + break; + } + else + { + has_decimal = true; + } + } + else + if( LLStringOps::isDigit( trimmed[i] ) ) + { + has_digit = true; + } + else + { + success = false; + break; + } + } + } + + // Gotta have at least one + success = has_digit; + + return success; +} + +bool LLLineEditor::evaluateFloat() +{ + bool success; + F32 result = 0.f; + std::string expr = getText(); + LLStringUtil::toUpper(expr); + + success = LLCalc::getInstance()->evalString(expr, result); + + if (!success) + { + // Move the cursor to near the error on failure + setCursor(LLCalc::getInstance()->getLastErrorPos()); + // *TODO: Translated error message indicating the type of error? Select error text? + } + else + { + // Replace the expression with the result + std::string result_str = llformat("%f",result); + setText(result_str); + selectAll(); + } + + return success; +} + +void LLLineEditor::onMouseCaptureLost() +{ + endSelection(); +} + + +void LLLineEditor::setSelectAllonFocusReceived(bool b) +{ + mSelectAllonFocusReceived = b; +} + +void LLLineEditor::onKeystroke() +{ + if (mKeystrokeCallback) + { + mKeystrokeCallback(this); + } + + mSpellCheckStart = mSpellCheckEnd = -1; +} + +void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data) +{ + mKeystrokeCallback = boost::bind(callback, _1, user_data); +} + + +bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text ) +{ + mText.setArg(key, text); + return true; +} + +bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return true; +} + + +void LLLineEditor::updateAllowingLanguageInput() +{ + // Allow Language Text Input only when this LineEditor has + // no prevalidate function attached (as long as other criteria + // common to LLTextEditor). This criterion works + // fine on 1.15.0.2, since all prevalidate func reject any + // non-ASCII characters. I'm not sure on future versions, + // however... + LLWindow* window = getWindow(); + if (!window) + { + // test app, no window available + return; + } + if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator) + { + window->allowLanguageTextInput(this, true); + } + else + { + window->allowLanguageTextInput(this, false); + } +} + +bool LLLineEditor::hasPreeditString() const +{ + return (mPreeditPositions.size() > 1); +} + +void LLLineEditor::resetPreedit() +{ + if (hasSelection()) + { + if (hasPreeditString()) + { + LL_WARNS() << "Preedit and selection!" << LL_ENDL; + deselect(); + } + else + { + deleteSelection(); + } + } + if (hasPreeditString()) + { + const S32 preedit_pos = mPreeditPositions.front(); + mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); + mText.insert(preedit_pos, mPreeditOverwrittenWString); + setCursor(preedit_pos); + + mPreeditWString.clear(); + mPreeditOverwrittenWString.clear(); + mPreeditPositions.clear(); + + // Don't reset key stroke timer nor invoke keystroke callback, + // because a call to updatePreedit should be follow soon in + // normal course of operation, and timer and callback will be + // maintained there. Doing so here made an odd sound. (VWR-3410) + } +} + +void LLLineEditor::updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) +{ + // Just in case. + if (mReadOnly) + { + return; + } + + // Note that call to updatePreedit is always preceeded by resetPreedit, + // so we have no existing selection/preedit. + + S32 insert_preedit_at = getCursor(); + + mPreeditWString = preedit_string; + mPreeditPositions.resize(preedit_segment_lengths.size() + 1); + S32 position = insert_preedit_at; + for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++) + { + mPreeditPositions[i] = position; + position += preedit_segment_lengths[i]; + } + mPreeditPositions.back() = position; + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); + mText.erase(insert_preedit_at, mPreeditWString.length()); + } + else + { + mPreeditOverwrittenWString.clear(); + } + mText.insert(insert_preedit_at, mPreeditWString); + + mPreeditStandouts = preedit_standouts; + + setCursor(position); + setCursor(mPreeditPositions.front() + caret_position); + + // Update of the preedit should be caused by some key strokes. + mKeystrokeTimer.reset(); + onKeystroke(); + + mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY); +} + +bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const +{ + if (control) + { + LLRect control_rect_screen; + localRectToScreen(getRect(), &control_rect_screen); + LLUI::getInstance()->screenRectToGL(control_rect_screen, control); + } + + S32 preedit_left_column, preedit_right_column; + if (hasPreeditString()) + { + preedit_left_column = mPreeditPositions.front(); + preedit_right_column = mPreeditPositions.back(); + } + else + { + preedit_left_column = preedit_right_column = getCursor(); + } + if (preedit_right_column < mScrollHPos) + { + // This should not occure... + return false; + } + + const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor()); + if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column) + { + return false; + } + + if (coord) + { + S32 query_local = findPixelNearestPos(query - getCursor()); + S32 query_screen_x, query_screen_y; + localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y); + LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); + } + + if (bounds) + { + S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor()); + S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness); + if (preedit_left_local > preedit_right_local) + { + // Is this condition possible? + preedit_right_local = preedit_left_local; + } + + LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0); + LLRect preedit_rect_screen; + localRectToScreen(preedit_rect_local, &preedit_rect_screen); + LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds); + } + + return true; +} + +void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const +{ + if (hasPreeditString()) + { + *position = mPreeditPositions.front(); + *length = mPreeditPositions.back() - mPreeditPositions.front(); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const +{ + if (hasSelection()) + { + *position = llmin(mSelectionStart, mSelectionEnd); + *length = llabs(mSelectionStart - mSelectionEnd); + } + else + { + *position = mCursorPos; + *length = 0; + } +} + +void LLLineEditor::markAsPreedit(S32 position, S32 length) +{ + deselect(); + setCursor(position); + if (hasPreeditString()) + { + LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL; + } + mPreeditWString.assign( LLWString( mText.getWString(), position, length ) ); + if (length > 0) + { + mPreeditPositions.resize(2); + mPreeditPositions[0] = position; + mPreeditPositions[1] = position + length; + mPreeditStandouts.resize(1); + mPreeditStandouts[0] = false; + } + else + { + mPreeditPositions.clear(); + mPreeditStandouts.clear(); + } + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) + { + mPreeditOverwrittenWString = mPreeditWString; + } + else + { + mPreeditOverwrittenWString.clear(); + } +} + +S32 LLLineEditor::getPreeditFontSize() const +{ + return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); +} + +void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace) +{ + mReplaceNewlinesWithSpaces = replace; +} + +LLWString LLLineEditor::getConvertedText() const +{ + LLWString text = getWText(); + LLWStringUtil::trim(text); + if (!mReplaceNewlinesWithSpaces) + { + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + } + return text; +} + +void LLLineEditor::showContextMenu(S32 x, S32 y) +{ + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (!menu) + { + llassert(LLMenuGL::sMenuContainer != NULL); + menu = LLUICtrlFactory::createFromFile + ("menu_text_editor.xml", + LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + setContextMenu(menu); + } + + if (menu) + { + gEditMenuHandler = this; + + S32 screen_x, screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + setCursorAtLocalPos(x); + if (hasSelection()) + { + if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) ) + { + deselect(); + } + else + { + setCursor(llmax(mSelectionStart, mSelectionEnd)); + } + } + + bool use_spellcheck = getSpellCheck(), is_misspelled = false; + if (use_spellcheck) + { + mSuggestionList.clear(); + + // If the cursor is on a misspelled word, retrieve suggestions for it + std::string misspelled_word = getMisspelledWord(mCursorPos); + if ((is_misspelled = !misspelled_word.empty())) + { + LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList); + } + } + + menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty())); + menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled)); + menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled)); + menu->show(screen_x, screen_y, this); + } +} + +void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) +{ + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (menu) + { + menu->die(); + mContextMenuHandle.markDead(); + } + + if (new_context_menu) + { + mContextMenuHandle = new_context_menu->getHandle(); + } +} + +void LLLineEditor::setFont(const LLFontGL* font) +{ + mGLFont = font; +} -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/llui/lllineeditor.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'indra/llui/lllineeditor.cpp') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 5ec4c34f57..50bf3f5877 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -590,7 +590,7 @@ const std::string& LLLineEditor::getSuggestion(U32 index) const U32 LLLineEditor::getSuggestionCount() const { - return mSuggestionList.size(); + return static_cast(mSuggestionList.size()); } void LLLineEditor::replaceWithSuggestion(U32 index) @@ -994,7 +994,7 @@ void LLLineEditor::addChar(const llwchar uni_char) mText.erase(getCursor(), 1); } - S32 cur_bytes = mText.getString().size(); + S32 cur_bytes = static_cast(mText.getString().size()); S32 new_bytes = wchar_utf8_length(new_c); @@ -1007,7 +1007,7 @@ void LLLineEditor::addChar(const llwchar uni_char) } else if (mMaxLengthChars) { - S32 wide_chars = mText.getWString().size(); + auto wide_chars = mText.getWString().size(); if ((wide_chars + 1) > mMaxLengthChars) { allow_char = false; @@ -1332,7 +1332,7 @@ void LLLineEditor::pasteHelper(bool is_primary) if (mMaxLengthChars) { - U32 available_chars = mMaxLengthChars - mText.getWString().size(); + auto available_chars = mMaxLengthChars - mText.getWString().size(); if (available_chars < clean_string.size()) { @@ -2300,10 +2300,10 @@ bool LLLineEditor::postvalidateFloat(const std::string &str) LLWString trimmed = utf8str_to_wstring(str); LLWStringUtil::trim(trimmed); - S32 len = trimmed.length(); + auto len = trimmed.length(); if( 0 < len ) { - S32 i = 0; + size_t i = 0; // First character can be a negative sign if( '-' == trimmed[0] ) @@ -2360,7 +2360,7 @@ bool LLLineEditor::evaluateFloat() if (!success) { // Move the cursor to near the error on failure - setCursor(LLCalc::getInstance()->getLastErrorPos()); + setCursor(static_cast(LLCalc::getInstance()->getLastErrorPos())); // *TODO: Translated error message indicating the type of error? Select error text? } else @@ -2501,7 +2501,7 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string, if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) ); - mText.erase(insert_preedit_at, mPreeditWString.length()); + mText.erase(insert_preedit_at, static_cast(mPreeditWString.length())); } else { -- cgit v1.2.3