aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/lltexteditor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llui/lltexteditor.cpp')
-rw-r--r--linden/indra/llui/lltexteditor.cpp539
1 files changed, 465 insertions, 74 deletions
diff --git a/linden/indra/llui/lltexteditor.cpp b/linden/indra/llui/lltexteditor.cpp
index b4d693b..ca9ea0a 100644
--- a/linden/indra/llui/lltexteditor.cpp
+++ b/linden/indra/llui/lltexteditor.cpp
@@ -12,12 +12,12 @@
12 * ("GPL"), unless you have obtained a separate licensing agreement 12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of 13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or 14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2 15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 * 16 *
17 * There are special exceptions to the terms and conditions of the GPL as 17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception 18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or 19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at http://secondlife.com/developers/opensource/flossexception 20 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
21 * 21 *
22 * By copying, modifying or distributing this software, you acknowledge 22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above, 23 * that you have read and understood your obligations described above,
@@ -79,6 +79,15 @@ const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
79const S32 CURSOR_THICKNESS = 2; 79const S32 CURSOR_THICKNESS = 2;
80const S32 SPACES_PER_TAB = 4; 80const S32 SPACES_PER_TAB = 4;
81 81
82const F32 PREEDIT_MARKER_BRIGHTNESS = 0.4f;
83const S32 PREEDIT_MARKER_GAP = 1;
84const S32 PREEDIT_MARKER_POSITION = 2;
85const S32 PREEDIT_MARKER_THICKNESS = 1;
86const F32 PREEDIT_STANDOUT_BRIGHTNESS = 0.6f;
87const S32 PREEDIT_STANDOUT_GAP = 1;
88const S32 PREEDIT_STANDOUT_POSITION = 2;
89const S32 PREEDIT_STANDOUT_THICKNESS = 2;
90
82LLColor4 LLTextEditor::mLinkColor = LLColor4::blue; 91LLColor4 LLTextEditor::mLinkColor = LLColor4::blue;
83void (* LLTextEditor::mURLcallback)(const char*) = NULL; 92void (* LLTextEditor::mURLcallback)(const char*) = NULL;
84bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL; 93bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL;
@@ -274,14 +283,14 @@ private:
274LLTextEditor::LLTextEditor( 283LLTextEditor::LLTextEditor(
275 const LLString& name, 284 const LLString& name,
276 const LLRect& rect, 285 const LLRect& rect,
277 S32 max_length, 286 S32 max_length, // In bytes
278 const LLString &default_text, 287 const LLString &default_text,
279 const LLFontGL* font, 288 const LLFontGL* font,
280 BOOL allow_embedded_items) 289 BOOL allow_embedded_items)
281 : 290 :
282 LLUICtrl( name, rect, TRUE, NULL, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ), 291 LLUICtrl( name, rect, TRUE, NULL, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ),
283 mTextIsUpToDate(TRUE), 292 mTextIsUpToDate(TRUE),
284 mMaxTextLength( max_length ), 293 mMaxTextByteLength( max_length ),
285 mBaseDocIsPristine(TRUE), 294 mBaseDocIsPristine(TRUE),
286 mPristineCmd( NULL ), 295 mPristineCmd( NULL ),
287 mLastCmd( NULL ), 296 mLastCmd( NULL ),
@@ -293,6 +302,7 @@ LLTextEditor::LLTextEditor(
293 mOnScrollEndData( NULL ), 302 mOnScrollEndData( NULL ),
294 mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ), 303 mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ),
295 mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ), 304 mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ),
305 mDefaultColor( LLUI::sColorsGroup->getColor( "TextDefaultColor" ) ),
296 mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ), 306 mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ),
297 mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ), 307 mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ),
298 mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ), 308 mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ),
@@ -301,9 +311,9 @@ LLTextEditor::LLTextEditor(
301 mWordWrap( FALSE ), 311 mWordWrap( FALSE ),
302 mTabToNextField( TRUE ), 312 mTabToNextField( TRUE ),
303 mCommitOnFocusLost( FALSE ), 313 mCommitOnFocusLost( FALSE ),
304 mTakesFocus( TRUE ),
305 mHideScrollbarForShortDocs( FALSE ), 314 mHideScrollbarForShortDocs( FALSE ),
306 mTakesNonScrollClicks( TRUE ), 315 mTakesNonScrollClicks( TRUE ),
316 mTrackBottom( TRUE ),
307 mAllowEmbeddedItems( allow_embedded_items ), 317 mAllowEmbeddedItems( allow_embedded_items ),
308 mAcceptCallingCardNames(FALSE), 318 mAcceptCallingCardNames(FALSE),
309 mHandleEditKeysDirectly( FALSE ), 319 mHandleEditKeysDirectly( FALSE ),
@@ -510,13 +520,27 @@ BOOL LLTextEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); }
510 520
511 521
512 522
513void LLTextEditor::truncate() 523BOOL LLTextEditor::truncate()
514{ 524{
515 if (mWText.size() > (size_t)mMaxTextLength) 525 BOOL did_truncate = FALSE;
516 { 526
517 LLWString::truncate(mWText, mMaxTextLength); 527 // First rough check - if we're less than 1/4th the size, we're OK
518 mTextIsUpToDate = FALSE; 528 if (mWText.size() >= (size_t) (mMaxTextByteLength / 4))
529 {
530 // Have to check actual byte size
531 S32 utf8_byte_size = wstring_utf8_length( mWText );
532 if ( utf8_byte_size > mMaxTextByteLength )
533 {
534 // Truncate safely in UTF-8
535 std::string temp_utf8_text = wstring_to_utf8str( mWText );
536 temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
537 mWText = utf8str_to_wstring( temp_utf8_text );
538 mTextIsUpToDate = FALSE;
539 did_truncate = TRUE;
540 }
519 } 541 }
542
543 return did_truncate;
520} 544}
521 545
522void LLTextEditor::setText(const LLStringExplicit &utf8str) 546void LLTextEditor::setText(const LLStringExplicit &utf8str)
@@ -750,12 +774,12 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const
750 return cursorPos; 774 return cursorPos;
751} 775}
752 776
753S32 LLTextEditor::getLineCount() 777S32 LLTextEditor::getLineCount() const
754{ 778{
755 return mLineStartList.size(); 779 return mLineStartList.size();
756} 780}
757 781
758S32 LLTextEditor::getLineStart( S32 line ) 782S32 LLTextEditor::getLineStart( S32 line ) const
759{ 783{
760 S32 num_lines = getLineCount(); 784 S32 num_lines = getLineCount();
761 if (num_lines == 0) 785 if (num_lines == 0)
@@ -1118,46 +1142,42 @@ void LLTextEditor::selectAll()
1118 1142
1119BOOL LLTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) 1143BOOL LLTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
1120{ 1144{
1121 if (pointInView(x, y) && getVisible()) 1145 for ( child_list_const_iter_t child_it = getChildList()->begin();
1146 child_it != getChildList()->end(); ++child_it)
1122 { 1147 {
1123 for ( child_list_const_iter_t child_it = getChildList()->begin(); 1148 LLView* viewp = *child_it;
1124 child_it != getChildList()->end(); ++child_it) 1149 S32 local_x = x - viewp->getRect().mLeft;
1125 { 1150 S32 local_y = y - viewp->getRect().mBottom;
1126 LLView* viewp = *child_it; 1151 if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
1127 S32 local_x = x - viewp->getRect().mLeft;
1128 S32 local_y = y - viewp->getRect().mBottom;
1129 if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
1130 {
1131 return TRUE;
1132 }
1133 }
1134
1135 if( mSegments.empty() )
1136 { 1152 {
1137 return TRUE; 1153 return TRUE;
1138 } 1154 }
1155 }
1139 1156
1140 LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); 1157 if( mSegments.empty() )
1141 if( cur_segment ) 1158 {
1142 { 1159 return TRUE;
1143 BOOL has_tool_tip = FALSE; 1160 }
1144 has_tool_tip = cur_segment->getToolTip( msg );
1145 1161
1146 if( has_tool_tip ) 1162 LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
1147 { 1163 if( cur_segment )
1148 // Just use a slop area around the cursor 1164 {
1149 // Convert rect local to screen coordinates 1165 BOOL has_tool_tip = FALSE;
1150 S32 SLOP = 8; 1166 has_tool_tip = cur_segment->getToolTip( msg );
1151 localPointToScreen( 1167
1152 x - SLOP, y - SLOP, 1168 if( has_tool_tip )
1153 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); 1169 {
1154 sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; 1170 // Just use a slop area around the cursor
1155 sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; 1171 // Convert rect local to screen coordinates
1156 } 1172 S32 SLOP = 8;
1173 localPointToScreen(
1174 x - SLOP, y - SLOP,
1175 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
1176 sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
1177 sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
1157 } 1178 }
1158 return TRUE;
1159 } 1179 }
1160 return FALSE; 1180 return TRUE;
1161} 1181}
1162 1182
1163BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) 1183BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
@@ -1240,7 +1260,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
1240 handled = TRUE; 1260 handled = TRUE;
1241 } 1261 }
1242 1262
1243 if (mTakesFocus) 1263 if (hasTabStop())
1244 { 1264 {
1245 setFocus( TRUE ); 1265 setFocus( TRUE );
1246 handled = TRUE; 1266 handled = TRUE;
@@ -1413,11 +1433,6 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
1413 1433
1414 if( !handled && mTakesNonScrollClicks) 1434 if( !handled && mTakesNonScrollClicks)
1415 { 1435 {
1416 if (mTakesFocus)
1417 {
1418 setFocus( TRUE );
1419 }
1420
1421 setCursorAtLocalPos( x, y, FALSE ); 1436 setCursorAtLocalPos( x, y, FALSE );
1422 deselect(); 1437 deselect();
1423 1438
@@ -1604,7 +1619,7 @@ void LLTextEditor::removeChar()
1604// Add a single character to the text 1619// Add a single character to the text
1605S32 LLTextEditor::addChar(S32 pos, llwchar wc) 1620S32 LLTextEditor::addChar(S32 pos, llwchar wc)
1606{ 1621{
1607 if ((S32)mWText.length() == mMaxTextLength) 1622 if ( (wstring_utf8_length( mWText ) + wchar_utf8_length( wc )) >= mMaxTextByteLength)
1608 { 1623 {
1609 make_ui_sound("UISndBadKeystroke"); 1624 make_ui_sound("UISndBadKeystroke");
1610 return 0; 1625 return 0;
@@ -2490,11 +2505,16 @@ void LLTextEditor::redo()
2490 } 2505 }
2491} 2506}
2492 2507
2508void LLTextEditor::onFocusReceived()
2509{
2510 LLUICtrl::onFocusReceived();
2511 updateAllowingLanguageInput();
2512}
2493 2513
2494// virtual, from LLView 2514// virtual, from LLView
2495void LLTextEditor::onFocusLost() 2515void LLTextEditor::onFocusLost()
2496{ 2516{
2497 getWindow()->allowLanguageTextInput(FALSE); 2517 updateAllowingLanguageInput();
2498 2518
2499 // Route menu back to the default 2519 // Route menu back to the default
2500 if( gEditMenuHandler == this ) 2520 if( gEditMenuHandler == this )
@@ -2521,6 +2541,7 @@ void LLTextEditor::setEnabled(BOOL enabled)
2521 { 2541 {
2522 mReadOnly = read_only; 2542 mReadOnly = read_only;
2523 updateSegments(); 2543 updateSegments();
2544 updateAllowingLanguageInput();
2524 } 2545 }
2525} 2546}
2526 2547
@@ -2825,6 +2846,100 @@ void LLTextEditor::drawCursor()
2825 } 2846 }
2826} 2847}
2827 2848
2849void LLTextEditor::drawPreeditMarker()
2850{
2851 if (!hasPreeditString())
2852 {
2853 return;
2854 }
2855
2856 const llwchar *text = mWText.c_str();
2857 const S32 text_len = getLength();
2858 const S32 num_lines = getLineCount();
2859
2860 S32 cur_line = mScrollbar->getDocPos();
2861 if (cur_line >= num_lines)
2862 {
2863 return;
2864 }
2865
2866 const S32 line_height = llround( mGLFont->getLineHeight() );
2867
2868 S32 line_start = getLineStart(cur_line);
2869 S32 line_y = mTextRect.mTop - line_height;
2870 while((mTextRect.mBottom <= line_y) && (num_lines > cur_line))
2871 {
2872 S32 next_start = -1;
2873 S32 line_end = text_len;
2874
2875 if ((cur_line + 1) < num_lines)
2876 {
2877 next_start = getLineStart(cur_line + 1);
2878 line_end = next_start;
2879 }
2880 if ( text[line_end-1] == '\n' )
2881 {
2882 --line_end;
2883 }
2884
2885 // Does this line contain preedits?
2886 if (line_start >= mPreeditPositions.back())
2887 {
2888 // We have passed the preedits.
2889 break;
2890 }
2891 if (line_end > mPreeditPositions.front())
2892 {
2893 for (U32 i = 0; i < mPreeditStandouts.size(); i++)
2894 {
2895 S32 left = mPreeditPositions[i];
2896 S32 right = mPreeditPositions[i + 1];
2897 if (right <= line_start || left >= line_end)
2898 {
2899 continue;
2900 }
2901
2902 S32 preedit_left = mTextRect.mLeft;
2903 if (left > line_start)
2904 {
2905 preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems);
2906 }
2907 S32 preedit_right = mTextRect.mLeft;
2908 if (right < line_end)
2909 {
2910 preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems);
2911 }
2912 else
2913 {
2914 preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems);
2915 }
2916
2917 if (mPreeditStandouts[i])
2918 {
2919 gl_rect_2d(preedit_left + PREEDIT_STANDOUT_GAP,
2920 line_y + PREEDIT_STANDOUT_POSITION,
2921 preedit_right - PREEDIT_STANDOUT_GAP - 1,
2922 line_y + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS,
2923 (mCursorColor * PREEDIT_STANDOUT_BRIGHTNESS + mWriteableBgColor * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f));
2924 }
2925 else
2926 {
2927 gl_rect_2d(preedit_left + PREEDIT_MARKER_GAP,
2928 line_y + PREEDIT_MARKER_POSITION,
2929 preedit_right - PREEDIT_MARKER_GAP - 1,
2930 line_y + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS,
2931 (mCursorColor * PREEDIT_MARKER_BRIGHTNESS + mWriteableBgColor * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f));
2932 }
2933 }
2934 }
2935
2936 // move down one line
2937 line_y -= line_height;
2938 line_start = next_start;
2939 cur_line++;
2940 }
2941}
2942
2828 2943
2829void LLTextEditor::drawText() 2944void LLTextEditor::drawText()
2830{ 2945{
@@ -3025,6 +3140,7 @@ void LLTextEditor::draw()
3025 3140
3026 drawBackground(); 3141 drawBackground();
3027 drawSelectionBackground(); 3142 drawSelectionBackground();
3143 drawPreeditMarker();
3028 drawText(); 3144 drawText();
3029 drawCursor(); 3145 drawCursor();
3030 3146
@@ -3036,6 +3152,9 @@ void LLTextEditor::draw()
3036 } 3152 }
3037 LLView::draw(); // Draw children (scrollbar and border) 3153 LLView::draw(); // Draw children (scrollbar and border)
3038 } 3154 }
3155
3156 // remember if we are supposed to be at the bottom of the buffer
3157 mScrolledToBottom = isScrolledToBottom();
3039} 3158}
3040 3159
3041void LLTextEditor::reportBadKeystroke() 3160void LLTextEditor::reportBadKeystroke()
@@ -3067,10 +3186,10 @@ void LLTextEditor::setFocus( BOOL new_state )
3067 // Don't change anything if the focus state didn't change 3186 // Don't change anything if the focus state didn't change
3068 if (new_state == old_state) return; 3187 if (new_state == old_state) return;
3069 3188
3070 // Notify early if we are loosing focus. 3189 // Notify early if we are losing focus.
3071 if (!new_state) 3190 if (!new_state)
3072 { 3191 {
3073 getWindow()->allowLanguageTextInput(FALSE); 3192 getWindow()->allowLanguageTextInput(this, FALSE);
3074 } 3193 }
3075 3194
3076 LLUICtrl::setFocus( new_state ); 3195 LLUICtrl::setFocus( new_state );
@@ -3093,12 +3212,6 @@ void LLTextEditor::setFocus( BOOL new_state )
3093 3212
3094 endSelection(); 3213 endSelection();
3095 } 3214 }
3096
3097 // Notify late if we are gaining focus.
3098 if (new_state && !mReadOnly)
3099 {
3100 getWindow()->allowLanguageTextInput(TRUE);
3101 }
3102} 3215}
3103 3216
3104BOOL LLTextEditor::acceptsTextInput() const 3217BOOL LLTextEditor::acceptsTextInput() const
@@ -3210,6 +3323,17 @@ void LLTextEditor::changeLine( S32 delta )
3210 unbindEmbeddedChars( mGLFont ); 3323 unbindEmbeddedChars( mGLFont );
3211} 3324}
3212 3325
3326BOOL LLTextEditor::isScrolledToTop()
3327{
3328 return mScrollbar->isAtBeginning();
3329}
3330
3331BOOL LLTextEditor::isScrolledToBottom()
3332{
3333 return mScrollbar->isAtEnd();
3334}
3335
3336
3213void LLTextEditor::startOfLine() 3337void LLTextEditor::startOfLine()
3214{ 3338{
3215 S32 line, offset; 3339 S32 line, offset;
@@ -3330,6 +3454,13 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
3330{ 3454{
3331 LLView::reshape( width, height, called_from_parent ); 3455 LLView::reshape( width, height, called_from_parent );
3332 3456
3457 // if scrolled to bottom, stay at bottom
3458 // unless user is editing text
3459 if (mScrolledToBottom && mTrackBottom && !hasFocus())
3460 {
3461 endOfDoc();
3462 }
3463
3333 updateTextRect(); 3464 updateTextRect();
3334 3465
3335 S32 line_height = llround( mGLFont->getLineHeight() ); 3466 S32 line_height = llround( mGLFont->getLineHeight() );
@@ -3540,22 +3671,20 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
3540 3671
3541S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) 3672S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
3542{ 3673{
3543 S32 len = mWText.length(); 3674 S32 old_len = mWText.length(); // length() returns character length
3544 S32 s_len = wstr.length(); 3675 S32 insert_len = wstr.length();
3545 S32 new_len = len + s_len; 3676
3546 if( new_len > mMaxTextLength ) 3677 mWText.insert(pos, wstr);
3547 { 3678 mTextIsUpToDate = FALSE;
3548 new_len = mMaxTextLength;
3549 3679
3680 if ( truncate() )
3681 {
3550 // The user's not getting everything he's hoping for 3682 // The user's not getting everything he's hoping for
3551 make_ui_sound("UISndBadKeystroke"); 3683 make_ui_sound("UISndBadKeystroke");
3684 insert_len = mWText.length() - old_len;
3552 } 3685 }
3553 3686
3554 mWText.insert(pos, wstr); 3687 return insert_len;
3555 mTextIsUpToDate = FALSE;
3556 truncate();
3557
3558 return new_len - len;
3559} 3688}
3560 3689
3561S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) 3690S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length)
@@ -3671,7 +3800,7 @@ void LLTextEditor::loadKeywords(const LLString& filename,
3671 mKeywords.addToken(LLKeywordToken::WORD, name.c_str(), color, tooltips.get(i) ); 3800 mKeywords.addToken(LLKeywordToken::WORD, name.c_str(), color, tooltips.get(i) );
3672 } 3801 }
3673 3802
3674 mKeywords.findSegments( &mSegments, mWText ); 3803 mKeywords.findSegments( &mSegments, mWText, mDefaultColor );
3675 3804
3676 llassert( mSegments.front()->getStart() == 0 ); 3805 llassert( mSegments.front()->getStart() == 0 );
3677 llassert( mSegments.back()->getEnd() == getLength() ); 3806 llassert( mSegments.back()->getEnd() == getLength() );
@@ -3683,7 +3812,7 @@ void LLTextEditor::updateSegments()
3683 if (mKeywords.isLoaded()) 3812 if (mKeywords.isLoaded())
3684 { 3813 {
3685 // HACK: No non-ascii keywords for now 3814 // HACK: No non-ascii keywords for now
3686 mKeywords.findSegments(&mSegments, mWText); 3815 mKeywords.findSegments(&mSegments, mWText, mDefaultColor);
3687 } 3816 }
3688 else if (mAllowEmbeddedItems) 3817 else if (mAllowEmbeddedItems)
3689 { 3818 {
@@ -3920,7 +4049,7 @@ BOOL LLTextEditor::importBuffer(const LLString& buffer )
3920 return FALSE; 4049 return FALSE;
3921 } 4050 }
3922 4051
3923 if( text_len > mMaxTextLength ) 4052 if( text_len > mMaxTextByteLength )
3924 { 4053 {
3925 llwarns << "Invalid Linden text length: " << text_len << llendl; 4054 llwarns << "Invalid Linden text length: " << text_len << llendl;
3926 return FALSE; 4055 return FALSE;
@@ -4064,6 +4193,7 @@ LLXMLNodePtr LLTextEditor::getXML(bool save_children) const
4064 4193
4065 addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor"); 4194 addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor");
4066 addColorXML(node, mFgColor, "text_color", "TextFgColor"); 4195 addColorXML(node, mFgColor, "text_color", "TextFgColor");
4196 addColorXML(node, mDefaultColor, "text_default_color", "TextDefaultColor");
4067 addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor"); 4197 addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor");
4068 addColorXML(node, mReadOnlyBgColor, "bg_readonly_color", "TextBgReadOnlyColor"); 4198 addColorXML(node, mReadOnlyBgColor, "bg_readonly_color", "TextBgReadOnlyColor");
4069 addColorXML(node, mWriteableBgColor, "bg_writeable_color", "TextBgWriteableColor"); 4199 addColorXML(node, mWriteableBgColor, "bg_writeable_color", "TextBgWriteableColor");
@@ -4118,6 +4248,8 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node)
4118 node->getAttributeBOOL("word_wrap", word_wrap); 4248 node->getAttributeBOOL("word_wrap", word_wrap);
4119 setWordWrap(word_wrap); 4249 setWordWrap(word_wrap);
4120 4250
4251 node->getAttributeBOOL("track_bottom", mTrackBottom);
4252
4121 LLColor4 color; 4253 LLColor4 color;
4122 if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color)) 4254 if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color))
4123 { 4255 {
@@ -4281,3 +4413,262 @@ BOOL LLTextEditor::findHTML(const LLString &line, S32 *begin, S32 *end)
4281 } 4413 }
4282 return matched; 4414 return matched;
4283} 4415}
4416
4417
4418
4419void LLTextEditor::updateAllowingLanguageInput()
4420{
4421 if (hasFocus() && !mReadOnly)
4422 {
4423 getWindow()->allowLanguageTextInput(this, TRUE);
4424 }
4425 else
4426 {
4427 getWindow()->allowLanguageTextInput(this, FALSE);
4428 }
4429}
4430
4431// Preedit is managed off the undo/redo command stack.
4432
4433BOOL LLTextEditor::hasPreeditString() const
4434{
4435 return (mPreeditPositions.size() > 1);
4436}
4437
4438void LLTextEditor::resetPreedit()
4439{
4440 if (hasPreeditString())
4441 {
4442 mCursorPos = mPreeditPositions.front();
4443 removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
4444 insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
4445
4446 mPreeditWString.clear();
4447 mPreeditOverwrittenWString.clear();
4448 mPreeditPositions.clear();
4449
4450 updateLineStartList();
4451 setCursorPos(mCursorPos);
4452 // updateScrollFromCursor();
4453 }
4454}
4455
4456void LLTextEditor::updatePreedit(const LLWString &preedit_string,
4457 const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
4458{
4459 // Just in case.
4460 if (mReadOnly)
4461 {
4462 return;
4463 }
4464
4465 if (hasSelection())
4466 {
4467 if (hasPreeditString())
4468 {
4469 llwarns << "Preedit and selection!" << llendl;
4470 deselect();
4471 }
4472 else
4473 {
4474 deleteSelection(TRUE);
4475 }
4476 }
4477
4478 getWindow()->hideCursorUntilMouseMove();
4479
4480 S32 insert_preedit_at = mCursorPos;
4481 if (hasPreeditString())
4482 {
4483 insert_preedit_at = mPreeditPositions.front();
4484 removeStringNoUndo(insert_preedit_at, mPreeditPositions.back() - insert_preedit_at);
4485 insertStringNoUndo(insert_preedit_at, mPreeditOverwrittenWString);
4486 }
4487
4488 mPreeditWString = preedit_string;
4489 mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
4490 S32 position = insert_preedit_at;
4491 for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
4492 {
4493 mPreeditPositions[i] = position;
4494 position += preedit_segment_lengths[i];
4495 }
4496 mPreeditPositions.back() = position;
4497
4498 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
4499 {
4500 mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length());
4501 removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
4502 }
4503 else
4504 {
4505 mPreeditOverwrittenWString.clear();
4506 }
4507 insertStringNoUndo(insert_preedit_at, mPreeditWString);
4508
4509 mPreeditStandouts = preedit_standouts;
4510
4511 updateLineStartList();
4512 setCursorPos(insert_preedit_at + caret_position);
4513 // updateScrollFromCursor();
4514
4515 // Update of the preedit should be caused by some key strokes.
4516 mKeystrokeTimer.reset();
4517}
4518
4519BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
4520{
4521 if (control)
4522 {
4523 LLRect control_rect_screen;
4524 localRectToScreen(mTextRect, &control_rect_screen);
4525 LLUI::screenRectToGL(control_rect_screen, control);
4526 }
4527
4528 S32 preedit_left_position, preedit_right_position;
4529 if (hasPreeditString())
4530 {
4531 preedit_left_position = mPreeditPositions.front();
4532 preedit_right_position = mPreeditPositions.back();
4533 }
4534 else
4535 {
4536 preedit_left_position = preedit_right_position = mCursorPos;
4537 }
4538
4539 const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos);
4540 if (query < preedit_left_position || query > preedit_right_position)
4541 {
4542 return FALSE;
4543 }
4544
4545 const S32 first_visible_line = mScrollbar->getDocPos();
4546 if (query < getLineStart(first_visible_line))
4547 {
4548 return FALSE;
4549 }
4550
4551 S32 current_line = first_visible_line;
4552 S32 current_line_start, current_line_end;
4553 for (;;)
4554 {
4555 current_line_start = getLineStart(current_line);
4556 current_line_end = getLineStart(current_line + 1);
4557 if (query >= current_line_start && query < current_line_end)
4558 {
4559 break;
4560 }
4561 if (current_line_start == current_line_end)
4562 {
4563 // We have reached on the last line. The query position must be here.
4564 break;
4565 }
4566 current_line++;
4567 }
4568
4569 const llwchar * const text = mWText.c_str();
4570 const S32 line_height = llround(mGLFont->getLineHeight());
4571
4572 if (coord)
4573 {
4574 const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems);
4575 const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
4576 S32 query_screen_x, query_screen_y;
4577 localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y);
4578 LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
4579 }
4580
4581 if (bounds)
4582 {
4583 S32 preedit_left = mTextRect.mLeft;
4584 if (preedit_left_position > current_line_start)
4585 {
4586 preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems);
4587 }
4588
4589 S32 preedit_right = mTextRect.mLeft;
4590 if (preedit_right_position < current_line_end)
4591 {
4592 preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems);
4593 }
4594 else
4595 {
4596 preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems);
4597 }
4598
4599 const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height;
4600 const S32 preedit_bottom = preedit_top - line_height;
4601
4602 const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom);
4603 LLRect preedit_rect_screen;
4604 localRectToScreen(preedit_rect_local, &preedit_rect_screen);
4605 LLUI::screenRectToGL(preedit_rect_screen, bounds);
4606 }
4607
4608 return TRUE;
4609}
4610
4611void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const
4612{
4613 if (hasSelection())
4614 {
4615 *position = llmin(mSelectionStart, mSelectionEnd);
4616 *length = llabs(mSelectionStart - mSelectionEnd);
4617 }
4618 else
4619 {
4620 *position = mCursorPos;
4621 *length = 0;
4622 }
4623}
4624
4625void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const
4626{
4627 if (hasPreeditString())
4628 {
4629 *position = mPreeditPositions.front();
4630 *length = mPreeditPositions.back() - mPreeditPositions.front();
4631 }
4632 else
4633 {
4634 *position = mCursorPos;
4635 *length = 0;
4636 }
4637}
4638
4639void LLTextEditor::markAsPreedit(S32 position, S32 length)
4640{
4641 deselect();
4642 setCursorPos(position);
4643 if (hasPreeditString())
4644 {
4645 llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
4646 }
4647 mPreeditWString = LLWString( mWText, position, length );
4648 if (length > 0)
4649 {
4650 mPreeditPositions.resize(2);
4651 mPreeditPositions[0] = position;
4652 mPreeditPositions[1] = position + length;
4653 mPreeditStandouts.resize(1);
4654 mPreeditStandouts[0] = FALSE;
4655 }
4656 else
4657 {
4658 mPreeditPositions.clear();
4659 mPreeditStandouts.clear();
4660 }
4661 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
4662 {
4663 mPreeditOverwrittenWString = mPreeditWString;
4664 }
4665 else
4666 {
4667 mPreeditOverwrittenWString.clear();
4668 }
4669}
4670
4671S32 LLTextEditor::getPreeditFontSize() const
4672{
4673 return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
4674}