00001
00032
00033
00034 #include "linden_common.h"
00035
00036 #include "lllineeditor.h"
00037
00038 #include "audioengine.h"
00039 #include "llmath.h"
00040 #include "llfontgl.h"
00041 #include "llgl.h"
00042 #include "sound_ids.h"
00043 #include "lltimer.h"
00044
00045
00046 #include "llcontrol.h"
00047 #include "llbutton.h"
00048 #include "llfocusmgr.h"
00049 #include "llkeyboard.h"
00050 #include "llrect.h"
00051 #include "llresmgr.h"
00052 #include "llstring.h"
00053 #include "llwindow.h"
00054 #include "llui.h"
00055 #include "lluictrlfactory.h"
00056 #include "llclipboard.h"
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 const S32 UI_LINEEDITOR_CURSOR_THICKNESS = 2;
00067 const S32 UI_LINEEDITOR_H_PAD = 2;
00068 const S32 UI_LINEEDITOR_V_PAD = 1;
00069 const F32 CURSOR_FLASH_DELAY = 1.0f;
00070 const S32 SCROLL_INCREMENT_ADD = 0;
00071 const S32 SCROLL_INCREMENT_DEL = 4;
00072 const F32 AUTO_SCROLL_TIME = 0.05f;
00073
00074 const F32 PREEDIT_MARKER_BRIGHTNESS = 0.4f;
00075 const S32 PREEDIT_MARKER_GAP = 1;
00076 const S32 PREEDIT_MARKER_POSITION = 2;
00077 const S32 PREEDIT_MARKER_THICKNESS = 1;
00078 const F32 PREEDIT_STANDOUT_BRIGHTNESS = 0.6f;
00079 const S32 PREEDIT_STANDOUT_GAP = 1;
00080 const S32 PREEDIT_STANDOUT_POSITION = 2;
00081 const S32 PREEDIT_STANDOUT_THICKNESS = 2;
00082
00083 static LLRegisterWidget<LLLineEditor> r1("line_editor");
00084
00085 LLPointer<LLUIImage> LLLineEditor::sImage;
00086
00087
00088
00089
00090
00091 LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect,
00092 const LLString& default_text, const LLFontGL* font,
00093 S32 max_length_bytes,
00094 void (*commit_callback)(LLUICtrl* caller, void* user_data ),
00095 void (*keystroke_callback)(LLLineEditor* caller, void* user_data ),
00096 void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data ),
00097 void* userdata,
00098 LLLinePrevalidateFunc prevalidate_func,
00099 LLViewBorder::EBevel border_bevel,
00100 LLViewBorder::EStyle border_style,
00101 S32 border_thickness)
00102 :
00103 LLUICtrl( name, rect, TRUE, commit_callback, userdata, FOLLOWS_TOP | FOLLOWS_LEFT ),
00104 mMaxLengthBytes(max_length_bytes),
00105 mCursorPos( 0 ),
00106 mScrollHPos( 0 ),
00107 mTextPadLeft(0),
00108 mTextPadRight(0),
00109 mCommitOnFocusLost( TRUE ),
00110 mRevertOnEsc( TRUE ),
00111 mKeystrokeCallback( keystroke_callback ),
00112 mIsSelecting( FALSE ),
00113 mSelectionStart( 0 ),
00114 mSelectionEnd( 0 ),
00115 mLastSelectionX(-1),
00116 mLastSelectionY(-1),
00117 mLastSelectionStart(-1),
00118 mLastSelectionEnd(-1),
00119 mPrevalidateFunc( prevalidate_func ),
00120 mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ),
00121 mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ),
00122 mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ),
00123 mTentativeFgColor( LLUI::sColorsGroup->getColor( "TextFgTentativeColor" ) ),
00124 mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ),
00125 mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ),
00126 mFocusBgColor( LLUI::sColorsGroup->getColor( "TextBgFocusColor" ) ),
00127 mBorderThickness( border_thickness ),
00128 mIgnoreArrowKeys( FALSE ),
00129 mIgnoreTab( TRUE ),
00130 mDrawAsterixes( FALSE ),
00131 mHandleEditKeysDirectly( FALSE ),
00132 mSelectAllonFocusReceived( FALSE ),
00133 mPassDelete(FALSE),
00134 mReadOnly(FALSE),
00135 mImage( sImage ),
00136 mReplaceNewlinesWithSpaces( TRUE )
00137 {
00138 llassert( max_length_bytes > 0 );
00139
00140
00141
00142 mLineHistory.insert( mLineHistory.end(), "" );
00143
00144 mHaveHistory = FALSE;
00145
00146 mCurrentHistoryLine = 0;
00147
00148 if (font)
00149 {
00150 mGLFont = font;
00151 }
00152 else
00153 {
00154 mGLFont = LLFontGL::sSansSerifSmall;
00155 }
00156
00157 setFocusLostCallback(focus_lost_callback);
00158
00159 setTextPadding(0, 0);
00160
00161 mScrollTimer.reset();
00162
00163 setText(default_text);
00164
00165 setCursor(mText.length());
00166
00167
00168
00169 LLRect border_rect(0, getRect().getHeight()-1, getRect().getWidth()-1, 0);
00170 mBorder = new LLViewBorder( "line ed border", border_rect, border_bevel, border_style, mBorderThickness );
00171 addChild( mBorder );
00172 mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM);
00173
00174 if( ! sImage)
00175 {
00176 sImage = LLUI::getUIImage("sm_rounded_corners_simple.tga");
00177 }
00178 mImage = sImage;
00179 }
00180
00181
00182 LLLineEditor::~LLLineEditor()
00183 {
00184 mCommitOnFocusLost = FALSE;
00185
00186 gFocusMgr.releaseFocusIfNeeded( this );
00187
00188 if( gEditMenuHandler == this )
00189 {
00190 gEditMenuHandler = NULL;
00191 }
00192 }
00193
00194
00195 void LLLineEditor::onFocusReceived()
00196 {
00197 LLUICtrl::onFocusReceived();
00198 updateAllowingLanguageInput();
00199 }
00200
00201 void LLLineEditor::onFocusLost()
00202 {
00203
00204
00205
00206
00207 updateAllowingLanguageInput();
00208
00209 if( mCommitOnFocusLost && mText.getString() != mPrevText)
00210 {
00211 onCommit();
00212 }
00213
00214 if( gEditMenuHandler == this )
00215 {
00216 gEditMenuHandler = NULL;
00217 }
00218
00219 getWindow()->showCursorFromMouseMove();
00220
00221 LLUICtrl::onFocusLost();
00222 }
00223
00224 void LLLineEditor::onCommit()
00225 {
00226
00227 updateHistory();
00228
00229 LLUICtrl::onCommit();
00230 selectAll();
00231 }
00232
00233
00234
00235 void LLLineEditor::updateHistory()
00236 {
00237
00238
00239
00240
00241 if( mHaveHistory && mText.length() && ( mLineHistory.empty() || getText() != mLineHistory.back() ) )
00242 {
00243
00244
00245 if( !mLineHistory.back().length() )
00246 {
00247 mLineHistory.pop_back();
00248 }
00249 mLineHistory.insert( mLineHistory.end(), getText() );
00250 mCurrentHistoryLine = mLineHistory.size() - 1;
00251 }
00252 }
00253
00254 void LLLineEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
00255 {
00256 LLUICtrl::reshape(width, height, called_from_parent);
00257 setTextPadding(mTextPadLeft, mTextPadRight);
00258 setCursor(mCursorPos);
00259 }
00260
00261 void LLLineEditor::setEnabled(BOOL enabled)
00262 {
00263 mReadOnly = !enabled;
00264 setTabStop(!mReadOnly);
00265 updateAllowingLanguageInput();
00266 }
00267
00268
00269 void LLLineEditor::setMaxTextLength(S32 max_text_length)
00270 {
00271 S32 max_len = llmax(0, max_text_length);
00272 mMaxLengthBytes = max_len;
00273 }
00274
00275 void LLLineEditor::setTextPadding(S32 left, S32 right)
00276 {
00277 mTextPadLeft = llclamp(left, 0, getRect().getWidth());
00278 mTextPadRight = llclamp(right, 0, getRect().getWidth());
00279 mMinHPixels = UI_LINEEDITOR_H_PAD + mTextPadLeft;
00280 mMaxHPixels = getRect().getWidth() - mMinHPixels - mTextPadRight;
00281 }
00282
00283
00284 void LLLineEditor::setText(const LLStringExplicit &new_text)
00285 {
00286
00287 if (mText.getString() == new_text)
00288 {
00289 return;
00290 }
00291
00292
00293 S32 len = mText.length();
00294 BOOL all_selected = (len > 0)
00295 && (( mSelectionStart == 0 && mSelectionEnd == len )
00296 || ( mSelectionStart == len && mSelectionEnd == 0 ));
00297
00298
00299
00300 all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived);
00301
00302 LLString truncated_utf8 = new_text;
00303 if (truncated_utf8.size() > (U32)mMaxLengthBytes)
00304 {
00305 truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);
00306 }
00307 mText.assign(truncated_utf8);
00308
00309 if (all_selected)
00310 {
00311
00312 selectAll();
00313 }
00314 else
00315 {
00316
00317 deselect();
00318 }
00319 setCursor(llmin((S32)mText.length(), getCursor()));
00320
00321
00322
00323 mLineHistory.insert( mLineHistory.end(), new_text );
00324
00325 mCurrentHistoryLine = mLineHistory.size() - 1;
00326
00327 mPrevText = mText;
00328 }
00329
00330
00331
00332 void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
00333 {
00334 const llwchar* wtext = mText.getWString().c_str();
00335 LLWString asterix_text;
00336 if (mDrawAsterixes)
00337 {
00338 for (S32 i = 0; i < mText.length(); i++)
00339 {
00340 asterix_text += '*';
00341 }
00342 wtext = asterix_text.c_str();
00343 }
00344
00345 S32 cursor_pos =
00346 mScrollHPos +
00347 mGLFont->charFromPixelOffset(
00348 wtext, mScrollHPos,
00349 (F32)(local_mouse_x - mMinHPixels),
00350 (F32)(mMaxHPixels - mMinHPixels + 1));
00351 setCursor(cursor_pos);
00352 }
00353
00354 void LLLineEditor::setCursor( S32 pos )
00355 {
00356 S32 old_cursor_pos = getCursor();
00357 mCursorPos = llclamp( pos, 0, mText.length());
00358
00359 S32 pixels_after_scroll = findPixelNearestPos();
00360 if( pixels_after_scroll > mMaxHPixels )
00361 {
00362 S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
00363 S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mMaxHPixels - mMinHPixels + width_chars_to_left)));
00364 S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mMaxHPixels - mMinHPixels), mText.length(), getCursor());
00365 if (old_cursor_pos == last_visible_char)
00366 {
00367 mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
00368 }
00369 else
00370 {
00371 mScrollHPos = min_scroll;
00372 }
00373 }
00374 else if (getCursor() < mScrollHPos)
00375 {
00376 if (old_cursor_pos == mScrollHPos)
00377 {
00378 mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL));
00379 }
00380 else
00381 {
00382 mScrollHPos = getCursor();
00383 }
00384 }
00385 }
00386
00387
00388 void LLLineEditor::setCursorToEnd()
00389 {
00390 setCursor(mText.length());
00391 deselect();
00392 }
00393
00394 BOOL LLLineEditor::canDeselect() const
00395 {
00396 return hasSelection();
00397 }
00398
00399 void LLLineEditor::deselect()
00400 {
00401 mSelectionStart = 0;
00402 mSelectionEnd = 0;
00403 mIsSelecting = FALSE;
00404 }
00405
00406
00407 void LLLineEditor::startSelection()
00408 {
00409 mIsSelecting = TRUE;
00410 mSelectionStart = getCursor();
00411 mSelectionEnd = getCursor();
00412 }
00413
00414 void LLLineEditor::endSelection()
00415 {
00416 if( mIsSelecting )
00417 {
00418 mIsSelecting = FALSE;
00419 mSelectionEnd = getCursor();
00420 }
00421 }
00422
00423 BOOL LLLineEditor::canSelectAll() const
00424 {
00425 return TRUE;
00426 }
00427
00428 void LLLineEditor::selectAll()
00429 {
00430 mSelectionStart = mText.length();
00431 mSelectionEnd = 0;
00432 setCursor(mSelectionEnd);
00433
00434 mIsSelecting = TRUE;
00435 }
00436
00437
00438 BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
00439 {
00440 setFocus( TRUE );
00441
00442 if (mSelectionEnd == 0 && mSelectionStart == mText.length())
00443 {
00444
00445 handleMouseDown(x, y, mask);
00446 }
00447 else
00448 {
00449 const LLWString& wtext = mText.getWString();
00450
00451 BOOL doSelectAll = TRUE;
00452
00453
00454 if( isPartOfWord( wtext[mCursorPos] ) )
00455 {
00456 S32 old_selection_start = mLastSelectionStart;
00457 S32 old_selection_end = mLastSelectionEnd;
00458
00459
00460 while ((mCursorPos > 0) && isPartOfWord( wtext[mCursorPos-1] ))
00461 {
00462 mCursorPos--;
00463 }
00464 startSelection();
00465
00466 while ((mCursorPos < (S32)wtext.length()) && isPartOfWord( wtext[mCursorPos] ) )
00467 {
00468 mCursorPos++;
00469 }
00470 mSelectionEnd = mCursorPos;
00471
00472
00473 doSelectAll = (old_selection_start == mSelectionStart) &&
00474 (old_selection_end == mSelectionEnd);
00475 }
00476
00477 if ( doSelectAll )
00478 {
00479 selectAll();
00480 }
00481 }
00482
00483
00484
00485
00486 mIsSelecting = FALSE;
00487
00488
00489 mKeystrokeTimer.reset();
00490
00491 return TRUE;
00492 }
00493
00494 BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
00495 {
00496
00497 if(childrenHandleMouseDown(x, y, mask) != NULL)
00498 {
00499 return TRUE;
00500 }
00501 if (mSelectAllonFocusReceived
00502 && gFocusMgr.getKeyboardFocus() != this)
00503 {
00504 setFocus( TRUE );
00505 }
00506 else
00507 {
00508 mLastSelectionStart = -1;
00509 mLastSelectionStart = -1;
00510
00511 setFocus( TRUE );
00512
00513 if (mask & MASK_SHIFT)
00514 {
00515
00516 S32 old_cursor_pos = getCursor();
00517 setCursorAtLocalPos(x);
00518
00519 if (hasSelection())
00520 {
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542 mSelectionEnd = getCursor();
00543 }
00544 else
00545 {
00546 mSelectionStart = old_cursor_pos;
00547 mSelectionEnd = getCursor();
00548 }
00549
00550 mIsSelecting = TRUE;
00551 }
00552 else
00553 {
00554
00555 mLastSelectionStart = mSelectionStart;
00556 mLastSelectionEnd = mSelectionEnd;
00557
00558
00559 setCursorAtLocalPos( x );
00560 deselect();
00561 startSelection();
00562 }
00563
00564 gFocusMgr.setMouseCapture( this );
00565 }
00566
00567
00568 mKeystrokeTimer.reset();
00569
00570 return TRUE;
00571 }
00572
00573
00574 BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
00575 {
00576 BOOL handled = FALSE;
00577
00578 if(!hasMouseCapture())
00579 {
00580 if(childrenHandleHover(x, y, mask) != NULL)
00581 {
00582 return TRUE;
00583 }
00584 }
00585
00586 if( (hasMouseCapture()) && mIsSelecting )
00587 {
00588 if (x != mLastSelectionX || y != mLastSelectionY)
00589 {
00590 mLastSelectionX = x;
00591 mLastSelectionY = y;
00592 }
00593
00594 if (mScrollTimer.hasExpired())
00595 {
00596 S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
00597 mScrollTimer.reset();
00598 mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
00599 if( (x < mMinHPixels) && (mScrollHPos > 0 ) )
00600 {
00601
00602 mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
00603 }
00604 else
00605 if( (x > mMaxHPixels) && (mCursorPos < (S32)mText.length()) )
00606 {
00607
00608 S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
00609 if( pixels_after_scrolling_one_char >= mMaxHPixels )
00610 {
00611
00612 mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
00613 }
00614 }
00615 }
00616
00617 setCursorAtLocalPos( x );
00618 mSelectionEnd = getCursor();
00619
00620
00621 mKeystrokeTimer.reset();
00622
00623 getWindow()->setCursor(UI_CURSOR_IBEAM);
00624 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
00625 handled = TRUE;
00626 }
00627
00628 if( !handled )
00629 {
00630 getWindow()->setCursor(UI_CURSOR_IBEAM);
00631 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
00632 handled = TRUE;
00633 }
00634
00635 return handled;
00636 }
00637
00638
00639 BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
00640 {
00641 BOOL handled = FALSE;
00642
00643 if( hasMouseCapture() )
00644 {
00645 gFocusMgr.setMouseCapture( NULL );
00646 handled = TRUE;
00647 }
00648
00649
00650 if(!handled && childrenHandleMouseUp(x, y, mask) != NULL)
00651 {
00652 return TRUE;
00653 }
00654
00655 if( mIsSelecting )
00656 {
00657 setCursorAtLocalPos( x );
00658 mSelectionEnd = getCursor();
00659
00660 handled = TRUE;
00661 }
00662
00663 if( handled )
00664 {
00665
00666 mKeystrokeTimer.reset();
00667 }
00668
00669 return handled;
00670 }
00671
00672
00673
00674 void LLLineEditor::removeChar()
00675 {
00676 if( getCursor() > 0 )
00677 {
00678 mText.erase(getCursor() - 1, 1);
00679
00680 setCursor(getCursor() - 1);
00681 }
00682 else
00683 {
00684 reportBadKeystroke();
00685 }
00686 }
00687
00688
00689 void LLLineEditor::addChar(const llwchar uni_char)
00690 {
00691 llwchar new_c = uni_char;
00692 if (hasSelection())
00693 {
00694 deleteSelection();
00695 }
00696 else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
00697 {
00698 mText.erase(getCursor(), 1);
00699 }
00700
00701 S32 cur_bytes = mText.getString().size();
00702 S32 new_bytes = wchar_utf8_length(new_c);
00703
00704 BOOL allow_char = TRUE;
00705
00706
00707 if ((new_bytes + cur_bytes) > mMaxLengthBytes)
00708 {
00709 allow_char = FALSE;
00710 }
00711
00712 if (allow_char)
00713 {
00714
00715 LLWString w_buf;
00716 w_buf.assign(1, new_c);
00717
00718 mText.insert(getCursor(), w_buf);
00719 setCursor(getCursor() + 1);
00720 }
00721 else
00722 {
00723 reportBadKeystroke();
00724 }
00725
00726 getWindow()->hideCursorUntilMouseMove();
00727 }
00728
00729
00730 void LLLineEditor::extendSelection( S32 new_cursor_pos )
00731 {
00732 if( !mIsSelecting )
00733 {
00734 startSelection();
00735 }
00736
00737 setCursor(new_cursor_pos);
00738 mSelectionEnd = getCursor();
00739 }
00740
00741
00742 void LLLineEditor::setSelection(S32 start, S32 end)
00743 {
00744 S32 len = mText.length();
00745
00746 mIsSelecting = TRUE;
00747
00748
00749
00750 mSelectionStart = llclamp(end, 0, len);
00751 mSelectionEnd = llclamp(start, 0, len);
00752 setCursor(start);
00753 }
00754
00755 void LLLineEditor::setDrawAsterixes(BOOL b)
00756 {
00757 mDrawAsterixes = b;
00758 updateAllowingLanguageInput();
00759 }
00760
00761 S32 LLLineEditor::prevWordPos(S32 cursorPos) const
00762 {
00763 const LLWString& wtext = mText.getWString();
00764 while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
00765 {
00766 cursorPos--;
00767 }
00768 while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) )
00769 {
00770 cursorPos--;
00771 }
00772 return cursorPos;
00773 }
00774
00775 S32 LLLineEditor::nextWordPos(S32 cursorPos) const
00776 {
00777 const LLWString& wtext = mText.getWString();
00778 while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos] ) )
00779 {
00780 cursorPos++;
00781 }
00782 while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
00783 {
00784 cursorPos++;
00785 }
00786 return cursorPos;
00787 }
00788
00789
00790 BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
00791 {
00792 BOOL handled = FALSE;
00793
00794 if( mask & MASK_SHIFT )
00795 {
00796 handled = TRUE;
00797
00798 switch( key )
00799 {
00800 case KEY_LEFT:
00801 if( 0 < getCursor() )
00802 {
00803 S32 cursorPos = getCursor() - 1;
00804 if( mask & MASK_CONTROL )
00805 {
00806 cursorPos = prevWordPos(cursorPos);
00807 }
00808 extendSelection( cursorPos );
00809 }
00810 else
00811 {
00812 reportBadKeystroke();
00813 }
00814 break;
00815
00816 case KEY_RIGHT:
00817 if( getCursor() < mText.length())
00818 {
00819 S32 cursorPos = getCursor() + 1;
00820 if( mask & MASK_CONTROL )
00821 {
00822 cursorPos = nextWordPos(cursorPos);
00823 }
00824 extendSelection( cursorPos );
00825 }
00826 else
00827 {
00828 reportBadKeystroke();
00829 }
00830 break;
00831
00832 case KEY_PAGE_UP:
00833 case KEY_HOME:
00834 extendSelection( 0 );
00835 break;
00836
00837 case KEY_PAGE_DOWN:
00838 case KEY_END:
00839 {
00840 S32 len = mText.length();
00841 if( len )
00842 {
00843 extendSelection( len );
00844 }
00845 break;
00846 }
00847
00848 default:
00849 handled = FALSE;
00850 break;
00851 }
00852 }
00853
00854 if (!handled && mHandleEditKeysDirectly)
00855 {
00856 if( (MASK_CONTROL & mask) && ('A' == key) )
00857 {
00858 if( canSelectAll() )
00859 {
00860 selectAll();
00861 }
00862 else
00863 {
00864 reportBadKeystroke();
00865 }
00866 handled = TRUE;
00867 }
00868 }
00869
00870
00871 return handled;
00872 }
00873
00874 void LLLineEditor::deleteSelection()
00875 {
00876 if( !mReadOnly && hasSelection() )
00877 {
00878 S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
00879 S32 selection_length = abs( mSelectionStart - mSelectionEnd );
00880
00881 mText.erase(left_pos, selection_length);
00882 deselect();
00883 setCursor(left_pos);
00884 }
00885 }
00886
00887 BOOL LLLineEditor::canCut() const
00888 {
00889 return !mReadOnly && !mDrawAsterixes && hasSelection();
00890 }
00891
00892
00893 void LLLineEditor::cut()
00894 {
00895 if( canCut() )
00896 {
00897
00898 LLLineEditorRollback rollback( this );
00899
00900
00901 S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
00902 S32 length = abs( mSelectionStart - mSelectionEnd );
00903 gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
00904 deleteSelection();
00905
00906
00907 BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
00908 if( need_to_rollback )
00909 {
00910 rollback.doRollback( this );
00911 reportBadKeystroke();
00912 }
00913 else
00914 if( mKeystrokeCallback )
00915 {
00916 mKeystrokeCallback( this, mCallbackUserData );
00917 }
00918 }
00919 }
00920
00921 BOOL LLLineEditor::canCopy() const
00922 {
00923 return !mDrawAsterixes && hasSelection();
00924 }
00925
00926
00927
00928 void LLLineEditor::copy()
00929 {
00930 if( canCopy() )
00931 {
00932 S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
00933 S32 length = abs( mSelectionStart - mSelectionEnd );
00934 gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
00935 }
00936 }
00937
00938 BOOL LLLineEditor::canPaste() const
00939 {
00940 return !mReadOnly && gClipboard.canPasteString();
00941 }
00942
00943
00944
00945 void LLLineEditor::paste()
00946 {
00947 if (canPaste())
00948 {
00949 LLWString paste = gClipboard.getPasteWString();
00950 if (!paste.empty())
00951 {
00952
00953 LLLineEditorRollback rollback(this);
00954
00955
00956 if (hasSelection())
00957 {
00958 deleteSelection();
00959 }
00960
00961
00962 LLWString clean_string(paste);
00963 LLWString::replaceTabsWithSpaces(clean_string, 1);
00964
00965 LLWString::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182);
00966
00967
00968
00969
00970 U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText);
00971
00972 if ( available_bytes < (U32) wstring_utf8_length(clean_string) )
00973 {
00974 llwchar current_symbol = clean_string[0];
00975 U32 wchars_that_fit = 0;
00976 U32 total_bytes = wchar_utf8_length(current_symbol);
00977
00978
00979
00980 while ( total_bytes <= available_bytes )
00981 {
00982
00983
00984
00985 current_symbol = clean_string[++wchars_that_fit];
00986 total_bytes += wchar_utf8_length(current_symbol);
00987 }
00988
00989 clean_string = clean_string.substr(0, wchars_that_fit);
00990 reportBadKeystroke();
00991 }
00992
00993 mText.insert(getCursor(), clean_string);
00994 setCursor( getCursor() + (S32)clean_string.length() );
00995 deselect();
00996
00997
00998 BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
00999 if( need_to_rollback )
01000 {
01001 rollback.doRollback( this );
01002 reportBadKeystroke();
01003 }
01004 else
01005 if( mKeystrokeCallback )
01006 {
01007 mKeystrokeCallback( this, mCallbackUserData );
01008 }
01009 }
01010 }
01011 }
01012
01013
01014 BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
01015 {
01016 BOOL handled = FALSE;
01017
01018 switch( key )
01019 {
01020 case KEY_INSERT:
01021 if (mask == MASK_NONE)
01022 {
01023 gKeyboard->toggleInsertMode();
01024 }
01025
01026 handled = TRUE;
01027 break;
01028
01029 case KEY_BACKSPACE:
01030 if (!mReadOnly)
01031 {
01032
01033 if( hasSelection() )
01034 {
01035 deleteSelection();
01036 }
01037 else
01038 if( 0 < getCursor() )
01039 {
01040 removeChar();
01041 }
01042 else
01043 {
01044 reportBadKeystroke();
01045 }
01046 }
01047 handled = TRUE;
01048 break;
01049
01050 case KEY_PAGE_UP:
01051 case KEY_HOME:
01052 if (!mIgnoreArrowKeys)
01053 {
01054 setCursor(0);
01055 handled = TRUE;
01056 }
01057 break;
01058
01059 case KEY_PAGE_DOWN:
01060 case KEY_END:
01061 if (!mIgnoreArrowKeys)
01062 {
01063 S32 len = mText.length();
01064 if( len )
01065 {
01066 setCursor(len);
01067 }
01068 handled = TRUE;
01069 }
01070 break;
01071
01072 case KEY_LEFT:
01073 if (mIgnoreArrowKeys && mask == MASK_NONE)
01074 break;
01075 if ((mask & MASK_ALT) == 0)
01076 {
01077 if( hasSelection() )
01078 {
01079 setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd ));
01080 }
01081 else
01082 if( 0 < getCursor() )
01083 {
01084 S32 cursorPos = getCursor() - 1;
01085 if( mask & MASK_CONTROL )
01086 {
01087 cursorPos = prevWordPos(cursorPos);
01088 }
01089 setCursor(cursorPos);
01090 }
01091 else
01092 {
01093 reportBadKeystroke();
01094 }
01095 handled = TRUE;
01096 }
01097 break;
01098
01099 case KEY_RIGHT:
01100 if (mIgnoreArrowKeys && mask == MASK_NONE)
01101 break;
01102 if ((mask & MASK_ALT) == 0)
01103 {
01104 if (hasSelection())
01105 {
01106 setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd));
01107 }
01108 else
01109 if (getCursor() < mText.length())
01110 {
01111 S32 cursorPos = getCursor() + 1;
01112 if( mask & MASK_CONTROL )
01113 {
01114 cursorPos = nextWordPos(cursorPos);
01115 }
01116 setCursor(cursorPos);
01117 }
01118 else
01119 {
01120 reportBadKeystroke();
01121 }
01122 handled = TRUE;
01123 }
01124 break;
01125
01126
01127 case KEY_UP:
01128 if( mHaveHistory && ( MASK_CONTROL == mask ) )
01129 {
01130 if( mCurrentHistoryLine > 0 )
01131 {
01132 mText.assign( mLineHistory[ --mCurrentHistoryLine ] );
01133 setCursor(llmin((S32)mText.length(), getCursor()));
01134 }
01135 else
01136 {
01137 reportBadKeystroke();
01138 }
01139 handled = TRUE;
01140 }
01141 break;
01142
01143
01144 case KEY_DOWN:
01145 if( mHaveHistory && ( MASK_CONTROL == mask ) )
01146 {
01147 if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.size() - 1 )
01148 {
01149 mText.assign( mLineHistory[ ++mCurrentHistoryLine ] );
01150 setCursor(llmin((S32)mText.length(), getCursor()));
01151 }
01152 else
01153 {
01154 reportBadKeystroke();
01155 }
01156 handled = TRUE;
01157 }
01158 break;
01159
01160 case KEY_RETURN:
01161
01162 updateHistory();
01163 break;
01164
01165 case KEY_ESCAPE:
01166 if (mRevertOnEsc && mText.getString() != mPrevText)
01167 {
01168 setText(mPrevText);
01169
01170 }
01171 break;
01172
01173 default:
01174 break;
01175 }
01176
01177 if( !handled && mHandleEditKeysDirectly )
01178 {
01179
01180 if( KEY_DELETE == key )
01181 {
01182 if( canDoDelete() )
01183 {
01184 doDelete();
01185 }
01186 else
01187 {
01188 reportBadKeystroke();
01189 }
01190 handled = TRUE;
01191 }
01192 else
01193 if( MASK_CONTROL & mask )
01194 {
01195 if( 'C' == key )
01196 {
01197 if( canCopy() )
01198 {
01199 copy();
01200 }
01201 else
01202 {
01203 reportBadKeystroke();
01204 }
01205 handled = TRUE;
01206 }
01207 else
01208 if( 'V' == key )
01209 {
01210 if( canPaste() )
01211 {
01212 paste();
01213 }
01214 else
01215 {
01216 reportBadKeystroke();
01217 }
01218 handled = TRUE;
01219 }
01220 else
01221 if( 'X' == key )
01222 {
01223 if( canCut() )
01224 {
01225 cut();
01226 }
01227 else
01228 {
01229 reportBadKeystroke();
01230 }
01231 handled = TRUE;
01232 }
01233 }
01234 }
01235 return handled;
01236 }
01237
01238
01239 BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
01240 {
01241 BOOL handled = FALSE;
01242 BOOL selection_modified = FALSE;
01243
01244 if ( gFocusMgr.getKeyboardFocus() == this )
01245 {
01246 LLLineEditorRollback rollback( this );
01247
01248 if( !handled )
01249 {
01250 handled = handleSelectionKey( key, mask );
01251 selection_modified = handled;
01252 }
01253
01254
01255 if ( !mReadOnly )
01256 {
01257 if( !handled )
01258 {
01259 handled = handleSpecialKey( key, mask );
01260 }
01261 }
01262
01263 if( handled )
01264 {
01265 mKeystrokeTimer.reset();
01266
01267
01268 if( !selection_modified &&
01269 KEY_SHIFT != key &&
01270 KEY_CONTROL != key &&
01271 KEY_ALT != key &&
01272 KEY_CAPSLOCK )
01273 {
01274 deselect();
01275 }
01276
01277 BOOL need_to_rollback = FALSE;
01278
01279
01280 need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
01281
01282
01283 need_to_rollback |= (mPrevalidateFunc && !mPrevalidateFunc(mText.getWString()));
01284
01285 if (need_to_rollback)
01286 {
01287 rollback.doRollback(this);
01288
01289 reportBadKeystroke();
01290 }
01291
01292
01293 if (!need_to_rollback && handled)
01294 {
01295 if (mKeystrokeCallback)
01296 {
01297 mKeystrokeCallback(this, mCallbackUserData);
01298 }
01299 }
01300 }
01301 }
01302
01303 return handled;
01304 }
01305
01306
01307 BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
01308 {
01309 if ((uni_char < 0x20) || (uni_char == 0x7F))
01310 {
01311 return FALSE;
01312 }
01313
01314 BOOL handled = FALSE;
01315
01316 if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly)
01317 {
01318 handled = TRUE;
01319
01320 LLLineEditorRollback rollback( this );
01321
01322 addChar(uni_char);
01323
01324 mKeystrokeTimer.reset();
01325
01326 deselect();
01327
01328 BOOL need_to_rollback = FALSE;
01329
01330
01331 need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
01332
01333 if( need_to_rollback )
01334 {
01335 rollback.doRollback( this );
01336
01337 reportBadKeystroke();
01338 }
01339
01340
01341 if( !need_to_rollback && handled )
01342 {
01343 if( mKeystrokeCallback )
01344 {
01345
01346
01347 mKeystrokeCallback( this, mCallbackUserData );
01348 }
01349 }
01350 }
01351 return handled;
01352 }
01353
01354
01355 BOOL LLLineEditor::canDoDelete() const
01356 {
01357 return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
01358 }
01359
01360 void LLLineEditor::doDelete()
01361 {
01362 if (canDoDelete())
01363 {
01364
01365 LLLineEditorRollback rollback( this );
01366
01367 if (hasSelection())
01368 {
01369 deleteSelection();
01370 }
01371 else if ( getCursor() < mText.length())
01372 {
01373 setCursor(getCursor() + 1);
01374 removeChar();
01375 }
01376
01377
01378 BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
01379 if( need_to_rollback )
01380 {
01381 rollback.doRollback( this );
01382 reportBadKeystroke();
01383 }
01384 else
01385 {
01386 if( mKeystrokeCallback )
01387 {
01388 mKeystrokeCallback( this, mCallbackUserData );
01389 }
01390 }
01391 }
01392 }
01393
01394
01395 void LLLineEditor::draw()
01396 {
01397 S32 text_len = mText.length();
01398
01399 LLString saved_text;
01400 if (mDrawAsterixes)
01401 {
01402 saved_text = mText.getString();
01403 LLString text;
01404 for (S32 i = 0; i < mText.length(); i++)
01405 {
01406 text += '*';
01407 }
01408 mText = text;
01409 }
01410
01411
01412 LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
01413 background.stretch( -mBorderThickness );
01414
01415 LLColor4 bg_color = mReadOnlyBgColor;
01416
01417 #if 0 // for when we're ready for image art.
01418 if( hasFocus())
01419 {
01420 mImage->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
01421 }
01422 mImage->draw(getLocalRect(), mReadOnly ? mReadOnlyBgColor : mWriteableBgColor );
01423 #else // the old programmer art.
01424
01425 {
01426 LLGLSNoTexture no_texture;
01427
01428 if( !mReadOnly )
01429 {
01430 if( gFocusMgr.getKeyboardFocus() == this )
01431 {
01432 bg_color = mFocusBgColor;
01433 }
01434 else
01435 {
01436 bg_color = mWriteableBgColor;
01437 }
01438 }
01439 gl_rect_2d(background, bg_color);
01440 }
01441 #endif
01442
01443
01444
01445 S32 cursor_bottom = background.mBottom + 1;
01446 S32 cursor_top = background.mTop - 1;
01447
01448 LLColor4 text_color;
01449 if (!mReadOnly)
01450 {
01451 if (!getTentative())
01452 {
01453 text_color = mFgColor;
01454 }
01455 else
01456 {
01457 text_color = mTentativeFgColor;
01458 }
01459 }
01460 else
01461 {
01462 text_color = mReadOnlyFgColor;
01463 }
01464 LLColor4 label_color = mTentativeFgColor;
01465
01466 if (hasPreeditString())
01467 {
01468
01469 for (U32 i = 0; i < mPreeditStandouts.size(); i++)
01470 {
01471 const S32 preedit_left = mPreeditPositions[i];
01472 const S32 preedit_right = mPreeditPositions[i + 1];
01473 if (preedit_right > mScrollHPos)
01474 {
01475 S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor());
01476 S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight);
01477 if (preedit_pixels_left >= background.mRight)
01478 {
01479 break;
01480 }
01481 if (mPreeditStandouts[i])
01482 {
01483 gl_rect_2d(preedit_pixels_left + PREEDIT_STANDOUT_GAP,
01484 background.mBottom + PREEDIT_STANDOUT_POSITION,
01485 preedit_pixels_right - PREEDIT_STANDOUT_GAP - 1,
01486 background.mBottom + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS,
01487 (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f));
01488 }
01489 else
01490 {
01491 gl_rect_2d(preedit_pixels_left + PREEDIT_MARKER_GAP,
01492 background.mBottom + PREEDIT_MARKER_POSITION,
01493 preedit_pixels_right - PREEDIT_MARKER_GAP - 1,
01494 background.mBottom + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS,
01495 (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f));
01496 }
01497 }
01498 }
01499 }
01500
01501 S32 rendered_text = 0;
01502 F32 rendered_pixels_right = (F32)mMinHPixels;
01503 F32 text_bottom = (F32)background.mBottom + (F32)UI_LINEEDITOR_V_PAD;
01504
01505 if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
01506 {
01507 S32 select_left;
01508 S32 select_right;
01509 if( mSelectionStart < getCursor() )
01510 {
01511 select_left = mSelectionStart;
01512 select_right = getCursor();
01513 }
01514 else
01515 {
01516 select_left = getCursor();
01517 select_right = mSelectionStart;
01518 }
01519
01520 if( select_left > mScrollHPos )
01521 {
01522
01523 rendered_text = mGLFont->render(
01524 mText, mScrollHPos,
01525 rendered_pixels_right, text_bottom,
01526 text_color,
01527 LLFontGL::LEFT, LLFontGL::BOTTOM,
01528 LLFontGL::NORMAL,
01529 select_left - mScrollHPos,
01530 mMaxHPixels - llround(rendered_pixels_right),
01531 &rendered_pixels_right);
01532 }
01533
01534 if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
01535 {
01536 LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f);
01537
01538 S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
01539 width = llmin(width, mMaxHPixels - llround(rendered_pixels_right));
01540 gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color);
01541
01542 rendered_text += mGLFont->render(
01543 mText, mScrollHPos + rendered_text,
01544 rendered_pixels_right, text_bottom,
01545 LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
01546 LLFontGL::LEFT, LLFontGL::BOTTOM,
01547 LLFontGL::NORMAL,
01548 select_right - mScrollHPos - rendered_text,
01549 mMaxHPixels - llround(rendered_pixels_right),
01550 &rendered_pixels_right);
01551 }
01552
01553 if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
01554 {
01555
01556 mGLFont->render(
01557 mText, mScrollHPos + rendered_text,
01558 rendered_pixels_right, text_bottom,
01559 text_color,
01560 LLFontGL::LEFT, LLFontGL::BOTTOM,
01561 LLFontGL::NORMAL,
01562 S32_MAX,
01563 mMaxHPixels - llround(rendered_pixels_right),
01564 &rendered_pixels_right);
01565 }
01566 }
01567 else
01568 {
01569 mGLFont->render(
01570 mText, mScrollHPos,
01571 rendered_pixels_right, text_bottom,
01572 text_color,
01573 LLFontGL::LEFT, LLFontGL::BOTTOM,
01574 LLFontGL::NORMAL,
01575 S32_MAX,
01576 mMaxHPixels - llround(rendered_pixels_right),
01577 &rendered_pixels_right);
01578 }
01579 #if 0 // for when we're ready for image art.
01580 mBorder->setVisible(FALSE);
01581 #endif
01582
01583
01584 if( gFocusMgr.getKeyboardFocus() == this)
01585 {
01586
01587
01588 if (gShowTextEditCursor && !mReadOnly)
01589 {
01590 F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
01591 if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
01592 {
01593 S32 cursor_left = findPixelNearestPos();
01594 cursor_left -= UI_LINEEDITOR_CURSOR_THICKNESS / 2;
01595 S32 cursor_right = cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS;
01596 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
01597 {
01598 const LLWString space(utf8str_to_wstring(LLString(" ")));
01599 S32 wswidth = mGLFont->getWidth(space.c_str());
01600 S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
01601 cursor_right = cursor_left + llmax(wswidth, width);
01602 }
01603
01604 gl_rect_2d(cursor_left, cursor_top,
01605 cursor_right, cursor_bottom, text_color);
01606 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
01607 {
01608 mGLFont->render(mText, getCursor(), (F32)(cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS / 2), text_bottom,
01609 LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
01610 LLFontGL::LEFT, LLFontGL::BOTTOM,
01611 LLFontGL::NORMAL,
01612 1);
01613 }
01614
01615
01616 S32 pixels_after_scroll = findPixelNearestPos();
01617 LLRect screen_pos = getScreenRect();
01618 LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - UI_LINEEDITOR_V_PAD );
01619
01620 ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
01621 ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
01622 getWindow()->setLanguageTextInput( ime_pos );
01623 }
01624 }
01625
01626
01627
01628 mBorder->setKeyboardFocusHighlight( TRUE );
01629 LLView::draw();
01630 mBorder->setKeyboardFocusHighlight( FALSE );
01631
01632 }
01633 else
01634 {
01635
01636 if (0 == mText.length())
01637 {
01638 mGLFont->render(mLabel.getWString(), 0,
01639 mMinHPixels, (F32)text_bottom,
01640 label_color,
01641 LLFontGL::LEFT, LLFontGL::BOTTOM,
01642 LLFontGL::NORMAL,
01643 S32_MAX,
01644 mMaxHPixels - llround(rendered_pixels_right),
01645 &rendered_pixels_right, FALSE);
01646 }
01647
01648 LLView::draw();
01649 }
01650
01651 if (mDrawAsterixes)
01652 {
01653 mText = saved_text;
01654 }
01655 }
01656
01657
01658
01659 S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
01660 {
01661 S32 dpos = getCursor() - mScrollHPos + cursor_offset;
01662 S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mMinHPixels;
01663 return result;
01664 }
01665
01666 void LLLineEditor::reportBadKeystroke()
01667 {
01668 make_ui_sound("UISndBadKeystroke");
01669 }
01670
01671
01672 void LLLineEditor::clear()
01673 {
01674 mText.clear();
01675 setCursor(0);
01676 }
01677
01678
01679 void LLLineEditor::onTabInto()
01680 {
01681 selectAll();
01682 }
01683
01684
01685 BOOL LLLineEditor::acceptsTextInput() const
01686 {
01687 return TRUE;
01688 }
01689
01690
01691 void LLLineEditor::setFocus( BOOL new_state )
01692 {
01693 BOOL old_state = hasFocus();
01694
01695 if (!new_state)
01696 {
01697 getWindow()->allowLanguageTextInput(this, FALSE);
01698 }
01699
01700
01701
01702 if (!old_state && new_state && mSelectAllonFocusReceived)
01703 {
01704 selectAll();
01705
01706
01707
01708 mIsSelecting = FALSE;
01709 }
01710
01711 if( new_state )
01712 {
01713 gEditMenuHandler = this;
01714
01715
01716 mKeystrokeTimer.reset();
01717 }
01718 else
01719 {
01720
01721
01722 if( gEditMenuHandler == this )
01723 {
01724 gEditMenuHandler = NULL;
01725 }
01726
01727 endSelection();
01728 }
01729
01730 LLUICtrl::setFocus( new_state );
01731
01732 if (new_state)
01733 {
01734
01735
01736
01737
01738
01739 getWindow()->allowLanguageTextInput(this, mPrevalidateFunc == NULL);
01740 }
01741 }
01742
01743
01744 void LLLineEditor::setRect(const LLRect& rect)
01745 {
01746 LLUICtrl::setRect(rect);
01747 if (mBorder)
01748 {
01749 LLRect border_rect = mBorder->getRect();
01750
01751
01752 border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom,
01753 rect.getWidth()-1, rect.getHeight()-1);
01754 mBorder->setRect(border_rect);
01755 }
01756 }
01757
01758 void LLLineEditor::setPrevalidate(BOOL (*func)(const LLWString &))
01759 {
01760 mPrevalidateFunc = func;
01761 updateAllowingLanguageInput();
01762 }
01763
01764
01765
01766
01767
01768
01769 BOOL LLLineEditor::prevalidateFloat(const LLWString &str)
01770 {
01771 LLLocale locale(LLLocale::USER_LOCALE);
01772
01773 BOOL success = TRUE;
01774 LLWString trimmed = str;
01775 LLWString::trim(trimmed);
01776 S32 len = trimmed.length();
01777 if( 0 < len )
01778 {
01779
01780 llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
01781
01782 S32 i = 0;
01783
01784
01785 if( '-' == trimmed[0] )
01786 {
01787 i++;
01788 }
01789
01790 for( ; i < len; i++ )
01791 {
01792 if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) )
01793 {
01794 success = FALSE;
01795 break;
01796 }
01797 }
01798 }
01799
01800 return success;
01801 }
01802
01803
01804 BOOL LLLineEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); }
01805
01806
01807 BOOL LLLineEditor::postvalidateFloat(const LLString &str)
01808 {
01809 LLLocale locale(LLLocale::USER_LOCALE);
01810
01811 BOOL success = TRUE;
01812 BOOL has_decimal = FALSE;
01813 BOOL has_digit = FALSE;
01814
01815 LLWString trimmed = utf8str_to_wstring(str);
01816 LLWString::trim(trimmed);
01817 S32 len = trimmed.length();
01818 if( 0 < len )
01819 {
01820 S32 i = 0;
01821
01822
01823 if( '-' == trimmed[0] )
01824 {
01825 i++;
01826 }
01827
01828
01829 llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
01830
01831 for( ; i < len; i++ )
01832 {
01833 if( decimal_point == trimmed[i] )
01834 {
01835 if( has_decimal )
01836 {
01837
01838 success = FALSE;
01839 break;
01840 }
01841 else
01842 {
01843 has_decimal = TRUE;
01844 }
01845 }
01846 else
01847 if( LLStringOps::isDigit( trimmed[i] ) )
01848 {
01849 has_digit = TRUE;
01850 }
01851 else
01852 {
01853 success = FALSE;
01854 break;
01855 }
01856 }
01857 }
01858
01859
01860 success = has_digit;
01861
01862 return success;
01863 }
01864
01865
01866
01867
01868
01869
01870 BOOL LLLineEditor::prevalidateInt(const LLWString &str)
01871 {
01872 LLLocale locale(LLLocale::USER_LOCALE);
01873
01874 BOOL success = TRUE;
01875 LLWString trimmed = str;
01876 LLWString::trim(trimmed);
01877 S32 len = trimmed.length();
01878 if( 0 < len )
01879 {
01880 S32 i = 0;
01881
01882
01883 if( '-' == trimmed[0] )
01884 {
01885 i++;
01886 }
01887
01888 for( ; i < len; i++ )
01889 {
01890 if( !LLStringOps::isDigit( trimmed[i] ) )
01891 {
01892 success = FALSE;
01893 break;
01894 }
01895 }
01896 }
01897
01898 return success;
01899 }
01900
01901
01902 BOOL LLLineEditor::prevalidatePositiveS32(const LLWString &str)
01903 {
01904 LLLocale locale(LLLocale::USER_LOCALE);
01905
01906 LLWString trimmed = str;
01907 LLWString::trim(trimmed);
01908 S32 len = trimmed.length();
01909 BOOL success = TRUE;
01910 if(0 < len)
01911 {
01912 if(('-' == trimmed[0]) || ('0' == trimmed[0]))
01913 {
01914 success = FALSE;
01915 }
01916 S32 i = 0;
01917 while(success && (i < len))
01918 {
01919 if(!LLStringOps::isDigit(trimmed[i++]))
01920 {
01921 success = FALSE;
01922 }
01923 }
01924 }
01925 if (success)
01926 {
01927 S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
01928 if (val <= 0)
01929 {
01930 success = FALSE;
01931 }
01932 }
01933 return success;
01934 }
01935
01936 BOOL LLLineEditor::prevalidateNonNegativeS32(const LLWString &str)
01937 {
01938 LLLocale locale(LLLocale::USER_LOCALE);
01939
01940 LLWString trimmed = str;
01941 LLWString::trim(trimmed);
01942 S32 len = trimmed.length();
01943 BOOL success = TRUE;
01944 if(0 < len)
01945 {
01946 if('-' == trimmed[0])
01947 {
01948 success = FALSE;
01949 }
01950 S32 i = 0;
01951 while(success && (i < len))
01952 {
01953 if(!LLStringOps::isDigit(trimmed[i++]))
01954 {
01955 success = FALSE;
01956 }
01957 }
01958 }
01959 if (success)
01960 {
01961 S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
01962 if (val < 0)
01963 {
01964 success = FALSE;
01965 }
01966 }
01967 return success;
01968 }
01969
01970 BOOL LLLineEditor::prevalidateAlphaNum(const LLWString &str)
01971 {
01972 LLLocale locale(LLLocale::USER_LOCALE);
01973
01974 BOOL rv = TRUE;
01975 S32 len = str.length();
01976 if(len == 0) return rv;
01977 while(len--)
01978 {
01979 if( !isalnum(str[len]) )
01980 {
01981 rv = FALSE;
01982 break;
01983 }
01984 }
01985 return rv;
01986 }
01987
01988
01989 BOOL LLLineEditor::prevalidateAlphaNumSpace(const LLWString &str)
01990 {
01991 LLLocale locale(LLLocale::USER_LOCALE);
01992
01993 BOOL rv = TRUE;
01994 S32 len = str.length();
01995 if(len == 0) return rv;
01996 while(len--)
01997 {
01998 if(!(isalnum(str[len]) || (' ' == str[len])))
01999 {
02000 rv = FALSE;
02001 break;
02002 }
02003 }
02004 return rv;
02005 }
02006
02007
02008 BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str)
02009 {
02010 BOOL rv = TRUE;
02011 S32 len = str.length();
02012 if(len == 0) return rv;
02013 while(len--)
02014 {
02015 if('|' == str[len])
02016 {
02017 rv = FALSE;
02018 break;
02019 }
02020 if(!((' ' == str[len]) || isalnum(str[len]) || ispunct(str[len])))
02021 {
02022 rv = FALSE;
02023 break;
02024 }
02025 }
02026 return rv;
02027 }
02028
02029
02030
02031 BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str)
02032 {
02033 BOOL rv = TRUE;
02034 S32 len = str.length();
02035 if(len == 0) return rv;
02036 while(len--)
02037 {
02038 if(iswspace(str[len]))
02039 {
02040 rv = FALSE;
02041 break;
02042 }
02043 if( !(isalnum(str[len]) || ispunct(str[len]) ) )
02044 {
02045 rv = FALSE;
02046 break;
02047 }
02048 }
02049 return rv;
02050 }
02051
02052
02053 BOOL LLLineEditor::prevalidateASCII(const LLWString &str)
02054 {
02055 BOOL rv = TRUE;
02056 S32 len = str.length();
02057 while(len--)
02058 {
02059 if (str[len] < 0x20 || str[len] > 0x7f)
02060 {
02061 rv = FALSE;
02062 break;
02063 }
02064 }
02065 return rv;
02066 }
02067
02068 void LLLineEditor::onMouseCaptureLost()
02069 {
02070 endSelection();
02071 }
02072
02073
02074 void LLLineEditor::setSelectAllonFocusReceived(BOOL b)
02075 {
02076 mSelectAllonFocusReceived = b;
02077 }
02078
02079
02080 void LLLineEditor::setKeystrokeCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data))
02081 {
02082 mKeystrokeCallback = keystroke_callback;
02083 }
02084
02085
02086 LLXMLNodePtr LLLineEditor::getXML(bool save_children) const
02087 {
02088 LLXMLNodePtr node = LLUICtrl::getXML();
02089
02090 node->createChild("max_length", TRUE)->setIntValue(mMaxLengthBytes);
02091
02092 node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont));
02093
02094 if (mBorder)
02095 {
02096 LLString bevel;
02097 switch(mBorder->getBevel())
02098 {
02099 default:
02100 case LLViewBorder::BEVEL_NONE: bevel = "none"; break;
02101 case LLViewBorder::BEVEL_IN: bevel = "in"; break;
02102 case LLViewBorder::BEVEL_OUT: bevel = "out"; break;
02103 case LLViewBorder::BEVEL_BRIGHT:bevel = "bright"; break;
02104 }
02105 node->createChild("bevel_style", TRUE)->setStringValue(bevel);
02106
02107 LLString style;
02108 switch(mBorder->getStyle())
02109 {
02110 default:
02111 case LLViewBorder::STYLE_LINE: style = "line"; break;
02112 case LLViewBorder::STYLE_TEXTURE: style = "texture"; break;
02113 }
02114 node->createChild("border_style", TRUE)->setStringValue(style);
02115
02116 node->createChild("border_thickness", TRUE)->setIntValue(mBorder->getBorderWidth());
02117 }
02118
02119 if (!mLabel.empty())
02120 {
02121 node->createChild("label", TRUE)->setStringValue(mLabel.getString());
02122 }
02123
02124 node->createChild("select_all_on_focus_received", TRUE)->setBoolValue(mSelectAllonFocusReceived);
02125
02126 node->createChild("handle_edit_keys_directly", TRUE)->setBoolValue(mHandleEditKeysDirectly );
02127
02128 addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor");
02129 addColorXML(node, mFgColor, "text_color", "TextFgColor");
02130 addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor");
02131 addColorXML(node, mTentativeFgColor, "text_tentative_color", "TextFgTentativeColor");
02132 addColorXML(node, mReadOnlyBgColor, "bg_readonly_color", "TextBgReadOnlyColor");
02133 addColorXML(node, mWriteableBgColor, "bg_writeable_color", "TextBgWriteableColor");
02134 addColorXML(node, mFocusBgColor, "bg_focus_color", "TextBgFocusColor");
02135
02136 node->createChild("select_on_focus", TRUE)->setBoolValue(mSelectAllonFocusReceived );
02137
02138 return node;
02139 }
02140
02141
02142 LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
02143 {
02144 LLString name("line_editor");
02145 node->getAttributeString("name", name);
02146
02147 LLRect rect;
02148 createRect(node, rect, parent, LLRect());
02149
02150 S32 max_text_length = 128;
02151 node->getAttributeS32("max_length", max_text_length);
02152
02153 LLFontGL* font = LLView::selectFont(node);
02154
02155 LLString text = node->getTextContents().substr(0, max_text_length - 1);
02156
02157 LLViewBorder::EBevel bevel_style = LLViewBorder::BEVEL_IN;
02158 LLViewBorder::getBevelFromAttribute(node, bevel_style);
02159
02160 LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE;
02161 LLString border_string;
02162 node->getAttributeString("border_style", border_string);
02163 LLString::toLower(border_string);
02164
02165 if (border_string == "texture")
02166 {
02167 border_style = LLViewBorder::STYLE_TEXTURE;
02168 }
02169
02170 S32 border_thickness = 1;
02171 node->getAttributeS32("border_thickness", border_thickness);
02172
02173 LLUICtrlCallback commit_callback = NULL;
02174
02175 LLLineEditor* line_editor = new LLLineEditor(name,
02176 rect,
02177 text,
02178 font,
02179 max_text_length,
02180 commit_callback,
02181 NULL,
02182 NULL,
02183 NULL,
02184 NULL,
02185 bevel_style,
02186 border_style,
02187 border_thickness);
02188
02189 LLString label;
02190 if(node->getAttributeString("label", label))
02191 {
02192 line_editor->setLabel(label);
02193 }
02194 BOOL select_all_on_focus_received = FALSE;
02195 if (node->getAttributeBOOL("select_all_on_focus_received", select_all_on_focus_received))
02196 {
02197 line_editor->setSelectAllonFocusReceived(select_all_on_focus_received);
02198 }
02199 BOOL handle_edit_keys_directly = FALSE;
02200 if (node->getAttributeBOOL("handle_edit_keys_directly", handle_edit_keys_directly))
02201 {
02202 line_editor->setHandleEditKeysDirectly(handle_edit_keys_directly);
02203 }
02204 BOOL commit_on_focus_lost = TRUE;
02205 if (node->getAttributeBOOL("commit_on_focus_lost", commit_on_focus_lost))
02206 {
02207 line_editor->setCommitOnFocusLost(commit_on_focus_lost);
02208 }
02209
02210 line_editor->setColorParameters(node);
02211
02212 if(node->hasAttribute("select_on_focus"))
02213 {
02214 BOOL selectall = FALSE;
02215 node->getAttributeBOOL("select_on_focus", selectall);
02216 line_editor->setSelectAllonFocusReceived(selectall);
02217 }
02218
02219 LLString prevalidate;
02220 if(node->getAttributeString("prevalidate", prevalidate))
02221 {
02222 LLString::toLower(prevalidate);
02223
02224 if ("ascii" == prevalidate)
02225 {
02226 line_editor->setPrevalidate( LLLineEditor::prevalidateASCII );
02227 }
02228 else if ("float" == prevalidate)
02229 {
02230 line_editor->setPrevalidate( LLLineEditor::prevalidateFloat );
02231 }
02232 else if ("int" == prevalidate)
02233 {
02234 line_editor->setPrevalidate( LLLineEditor::prevalidateInt );
02235 }
02236 else if ("positive_s32" == prevalidate)
02237 {
02238 line_editor->setPrevalidate( LLLineEditor::prevalidatePositiveS32 );
02239 }
02240 else if ("non_negative_s32" == prevalidate)
02241 {
02242 line_editor->setPrevalidate( LLLineEditor::prevalidateNonNegativeS32 );
02243 }
02244 else if ("alpha_num" == prevalidate)
02245 {
02246 line_editor->setPrevalidate( LLLineEditor::prevalidateAlphaNum );
02247 }
02248 else if ("alpha_num_space" == prevalidate)
02249 {
02250 line_editor->setPrevalidate( LLLineEditor::prevalidateAlphaNumSpace );
02251 }
02252 else if ("printable_not_pipe" == prevalidate)
02253 {
02254 line_editor->setPrevalidate( LLLineEditor::prevalidatePrintableNotPipe );
02255 }
02256 else if ("printable_no_space" == prevalidate)
02257 {
02258 line_editor->setPrevalidate( LLLineEditor::prevalidatePrintableNoSpace );
02259 }
02260 }
02261
02262 line_editor->initFromXML(node, parent);
02263
02264 return line_editor;
02265 }
02266
02267
02268 void LLLineEditor::cleanupClass()
02269 {
02270 sImage = NULL;
02271 }
02272
02273
02274 LLPointer<LLUIImage> LLLineEditor::parseImage(LLString name, LLXMLNodePtr from, LLPointer<LLUIImage> def)
02275 {
02276 LLString xml_name;
02277 if (from->hasAttribute(name)) from->getAttributeString(name, xml_name);
02278 if (xml_name == LLString::null) return def;
02279 LLPointer<LLUIImage> image = LLUI::getUIImage(xml_name);
02280 return image.isNull() ? def : image;
02281 }
02282
02283 void LLLineEditor::setColorParameters(LLXMLNodePtr node)
02284 {
02285
02286 mImage = parseImage("image", node, mImage);
02287
02288 LLColor4 color;
02289 if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color))
02290 {
02291 setCursorColor(color);
02292 }
02293 if(node->hasAttribute("text_color"))
02294 {
02295 LLUICtrlFactory::getAttributeColor(node,"text_color", color);
02296 setFgColor(color);
02297 }
02298 if(node->hasAttribute("text_readonly_color"))
02299 {
02300 LLUICtrlFactory::getAttributeColor(node,"text_readonly_color", color);
02301 setReadOnlyFgColor(color);
02302 }
02303 if (LLUICtrlFactory::getAttributeColor(node,"text_tentative_color", color))
02304 {
02305 setTentativeFgColor(color);
02306 }
02307 if(node->hasAttribute("bg_readonly_color"))
02308 {
02309 LLUICtrlFactory::getAttributeColor(node,"bg_readonly_color", color);
02310 setReadOnlyBgColor(color);
02311 }
02312 if(node->hasAttribute("bg_writeable_color"))
02313 {
02314 LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
02315 setWriteableBgColor(color);
02316 }
02317 }
02318
02319 BOOL LLLineEditor::setTextArg( const LLString& key, const LLStringExplicit& text )
02320 {
02321 mText.setArg(key, text);
02322 return TRUE;
02323 }
02324
02325 BOOL LLLineEditor::setLabelArg( const LLString& key, const LLStringExplicit& text )
02326 {
02327 mLabel.setArg(key, text);
02328 return TRUE;
02329 }
02330
02331
02332 void LLLineEditor::updateAllowingLanguageInput()
02333 {
02334
02335
02336
02337
02338
02339
02340 if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL)
02341 {
02342 getWindow()->allowLanguageTextInput(this, TRUE);
02343 }
02344 else
02345 {
02346 getWindow()->allowLanguageTextInput(this, FALSE);
02347 }
02348 }
02349
02350 BOOL LLLineEditor::hasPreeditString() const
02351 {
02352 return (mPreeditPositions.size() > 1);
02353 }
02354
02355 void LLLineEditor::resetPreedit()
02356 {
02357 if (hasPreeditString())
02358 {
02359 if (hasSelection())
02360 {
02361 llwarns << "Preedit and selection!" << llendl;
02362 deselect();
02363 }
02364
02365 const S32 preedit_pos = mPreeditPositions.front();
02366 mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
02367 mText.insert(preedit_pos, mPreeditOverwrittenWString);
02368 setCursor(preedit_pos);
02369
02370 mPreeditWString.clear();
02371 mPreeditOverwrittenWString.clear();
02372 mPreeditPositions.clear();
02373
02374
02375
02376
02377
02378 }
02379 }
02380
02381 void LLLineEditor::updatePreedit(const LLWString &preedit_string,
02382 const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
02383 {
02384
02385 if (mReadOnly)
02386 {
02387 return;
02388 }
02389
02390
02391
02392
02393 S32 insert_preedit_at = getCursor();
02394
02395 mPreeditWString = preedit_string;
02396 mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
02397 S32 position = insert_preedit_at;
02398 for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
02399 {
02400 mPreeditPositions[i] = position;
02401 position += preedit_segment_lengths[i];
02402 }
02403 mPreeditPositions.back() = position;
02404 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
02405 {
02406 mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) );
02407 mText.erase(insert_preedit_at, mPreeditWString.length());
02408 }
02409 else
02410 {
02411 mPreeditOverwrittenWString.clear();
02412 }
02413 mText.insert(insert_preedit_at, mPreeditWString);
02414
02415 mPreeditStandouts = preedit_standouts;
02416
02417 setCursor(position);
02418 setCursor(mPreeditPositions.front() + caret_position);
02419
02420
02421 mKeystrokeTimer.reset();
02422 if( mKeystrokeCallback )
02423 {
02424 mKeystrokeCallback( this, mCallbackUserData );
02425 }
02426 }
02427
02428 BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
02429 {
02430 if (control)
02431 {
02432 LLRect control_rect_screen;
02433 localRectToScreen(getRect(), &control_rect_screen);
02434 LLUI::screenRectToGL(control_rect_screen, control);
02435 }
02436
02437 S32 preedit_left_column, preedit_right_column;
02438 if (hasPreeditString())
02439 {
02440 preedit_left_column = mPreeditPositions.front();
02441 preedit_right_column = mPreeditPositions.back();
02442 }
02443 else
02444 {
02445 preedit_left_column = preedit_right_column = getCursor();
02446 }
02447 if (preedit_right_column < mScrollHPos)
02448 {
02449
02450 return FALSE;
02451 }
02452
02453 const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor());
02454 if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column)
02455 {
02456 return FALSE;
02457 }
02458
02459 if (coord)
02460 {
02461 S32 query_local = findPixelNearestPos(query - getCursor());
02462 S32 query_screen_x, query_screen_y;
02463 localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y);
02464 LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
02465 }
02466
02467 if (bounds)
02468 {
02469 S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor());
02470 S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness);
02471 if (preedit_left_local > preedit_right_local)
02472 {
02473
02474 preedit_right_local = preedit_left_local;
02475 }
02476
02477 LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0);
02478 LLRect preedit_rect_screen;
02479 localRectToScreen(preedit_rect_local, &preedit_rect_screen);
02480 LLUI::screenRectToGL(preedit_rect_screen, bounds);
02481 }
02482
02483 return TRUE;
02484 }
02485
02486 void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const
02487 {
02488 if (hasPreeditString())
02489 {
02490 *position = mPreeditPositions.front();
02491 *length = mPreeditPositions.back() - mPreeditPositions.front();
02492 }
02493 else
02494 {
02495 *position = mCursorPos;
02496 *length = 0;
02497 }
02498 }
02499
02500 void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const
02501 {
02502 if (hasSelection())
02503 {
02504 *position = llmin(mSelectionStart, mSelectionEnd);
02505 *length = llabs(mSelectionStart - mSelectionEnd);
02506 }
02507 else
02508 {
02509 *position = mCursorPos;
02510 *length = 0;
02511 }
02512 }
02513
02514 void LLLineEditor::markAsPreedit(S32 position, S32 length)
02515 {
02516 deselect();
02517 setCursor(position);
02518 if (hasPreeditString())
02519 {
02520 llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
02521 }
02522 mPreeditWString.assign( LLWString( mText.getWString(), position, length ) );
02523 if (length > 0)
02524 {
02525 mPreeditPositions.resize(2);
02526 mPreeditPositions[0] = position;
02527 mPreeditPositions[1] = position + length;
02528 mPreeditStandouts.resize(1);
02529 mPreeditStandouts[0] = FALSE;
02530 }
02531 else
02532 {
02533 mPreeditPositions.clear();
02534 mPreeditStandouts.clear();
02535 }
02536 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
02537 {
02538 mPreeditOverwrittenWString = mPreeditWString;
02539 }
02540 else
02541 {
02542 mPreeditOverwrittenWString.clear();
02543 }
02544 }
02545
02546 S32 LLLineEditor::getPreeditFontSize() const
02547 {
02548 return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
02549 }
02550
02551 void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace)
02552 {
02553 mReplaceNewlinesWithSpaces = replace;
02554 }
02555
02556 static LLRegisterWidget<LLSearchEditor> r2("search_editor");
02557
02558
02559 LLSearchEditor::LLSearchEditor(const LLString& name,
02560 const LLRect& rect,
02561 S32 max_length_bytes,
02562 void (*search_callback)(const LLString& search_string, void* user_data),
02563 void* userdata)
02564 :
02565 LLUICtrl(name, rect, TRUE, NULL, userdata),
02566 mSearchCallback(search_callback)
02567 {
02568 LLRect search_edit_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
02569 mSearchEdit = new LLLineEditor("search edit",
02570 search_edit_rect,
02571 LLString::null,
02572 NULL,
02573 max_length_bytes,
02574 NULL,
02575 onSearchEdit,
02576 NULL,
02577 this);
02578
02579 mSearchEdit->setFollowsAll();
02580 mSearchEdit->setSelectAllonFocusReceived(TRUE);
02581
02582 addChild(mSearchEdit);
02583
02584 S32 btn_width = rect.getHeight();
02585 LLRect clear_btn_rect(rect.getWidth() - btn_width, rect.getHeight(), rect.getWidth(), 0);
02586 mClearSearchButton = new LLButton("clear search",
02587 clear_btn_rect,
02588 "icn_clear_lineeditor.tga",
02589 "UIImgBtnCloseInactiveUUID",
02590 LLString::null,
02591 onClearSearch,
02592 this,
02593 NULL,
02594 LLString::null);
02595 mClearSearchButton->setFollowsRight();
02596 mClearSearchButton->setFollowsTop();
02597 mClearSearchButton->setImageColor(LLUI::sColorsGroup->getColor("TextFgTentativeColor"));
02598 mClearSearchButton->setTabStop(FALSE);
02599 mSearchEdit->addChild(mClearSearchButton);
02600
02601 mSearchEdit->setTextPadding(0, btn_width);
02602 }
02603
02604
02605
02606 void LLSearchEditor::setValue(const LLSD& value )
02607 {
02608 mSearchEdit->setValue(value);
02609 }
02610
02611
02612 LLSD LLSearchEditor::getValue() const
02613 {
02614 return mSearchEdit->getValue();
02615 }
02616
02617
02618 BOOL LLSearchEditor::setTextArg( const LLString& key, const LLStringExplicit& text )
02619 {
02620 return mSearchEdit->setTextArg(key, text);
02621 }
02622
02623
02624 BOOL LLSearchEditor::setLabelArg( const LLString& key, const LLStringExplicit& text )
02625 {
02626 return mSearchEdit->setLabelArg(key, text);
02627 }
02628
02629
02630 void LLSearchEditor::clear()
02631 {
02632 if (mSearchEdit)
02633 {
02634 mSearchEdit->clear();
02635 }
02636 }
02637
02638 void LLSearchEditor::draw()
02639 {
02640 mClearSearchButton->setVisible(!mSearchEdit->getWText().empty());
02641
02642 LLUICtrl::draw();
02643 }
02644
02645
02646
02647 void LLSearchEditor::onSearchEdit(LLLineEditor* caller, void* user_data )
02648 {
02649 LLSearchEditor* search_editor = (LLSearchEditor*)user_data;
02650 if (search_editor->mSearchCallback)
02651 {
02652 search_editor->mSearchCallback(caller->getText(), search_editor->mCallbackUserData);
02653 }
02654 }
02655
02656
02657 void LLSearchEditor::onClearSearch(void* user_data)
02658 {
02659 LLSearchEditor* search_editor = (LLSearchEditor*)user_data;
02660
02661 search_editor->setText(LLString::null);
02662 if (search_editor->mSearchCallback)
02663 {
02664 search_editor->mSearchCallback(LLString::null, search_editor->mCallbackUserData);
02665 }
02666 }
02667
02668
02669 LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
02670 {
02671 LLString name("search_editor");
02672 node->getAttributeString("name", name);
02673
02674 LLRect rect;
02675 createRect(node, rect, parent, LLRect());
02676
02677 S32 max_text_length = 128;
02678 node->getAttributeS32("max_length", max_text_length);
02679
02680 LLString text = node->getValue().substr(0, max_text_length - 1);
02681
02682 LLSearchEditor* search_editor = new LLSearchEditor(name,
02683 rect,
02684 max_text_length,
02685 NULL, NULL);
02686
02687 LLString label;
02688 if(node->getAttributeString("label", label))
02689 {
02690 search_editor->mSearchEdit->setLabel(label);
02691 }
02692
02693 search_editor->setText(text);
02694
02695 search_editor->initFromXML(node, parent);
02696
02697 return search_editor;
02698 }