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