lllineeditor.cpp

Go to the documentation of this file.
00001 
00032 // Text editor widget to let users enter a single line.
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 //#include "llclipboard.h"
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 // Imported globals
00060 //
00061 
00062 //
00063 // Constants
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;  // in seconds
00070 const S32       SCROLL_INCREMENT_ADD = 0;       // make space for typing
00071 const S32   SCROLL_INCREMENT_DEL = 4;   // make space for baskspacing
00072 const F32   AUTO_SCROLL_TIME = 0.05f;
00073 const F32       LABEL_HPAD = 5.f;
00074 
00075 // This is a friend class of and is only used by LLLineEditor
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 // Member functions
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         // line history support:
00167         // - initialize line history list
00168         mLineHistory.insert( mLineHistory.end(), "" );
00169         // - disable line history by default
00170         mHaveHistory = FALSE;
00171         // - reset current history line pointer
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         // Scalable UI somehow made these rectangles off-by-one.
00195         // I don't know why. JC
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 //virtual
00216 EWidgetType LLLineEditor::getWidgetType() const
00217 {
00218         return WIDGET_TYPE_LINE_EDITOR;
00219 }
00220 
00221 //virtual
00222 LLString LLLineEditor::getWidgetTag() const
00223 {
00224         return LL_LINE_EDITOR_TAG;
00225 }
00226 
00227 void LLLineEditor::onFocusLost()
00228 {
00229         // Need to notify early when loosing focus.
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         // put current line into the line history
00250         updateHistory();
00251 
00252         LLUICtrl::onCommit();
00253         selectAll();
00254 }
00255 
00256 // virtual 
00257 BOOL    LLLineEditor::isDirty() const   
00258 { 
00259         return ( mText.getString() != mPrevText );      
00260 }
00261 
00262 // virtual 
00263 void    LLLineEditor::resetDirty()      
00264 { 
00265         mPrevText = mText.getString();  
00266 }
00267 
00268 
00269 // line history support
00270 void LLLineEditor::updateHistory()
00271 {
00272         // On history enabled line editors, remember committed line and
00273         // reset current history line number.
00274         // Be sure only to remember lines that are not empty and that are
00275         // different from the last on the list.
00276         if( mHaveHistory && mText.length() && ( mLineHistory.empty() || getText() != mLineHistory.back() ) )
00277         {
00278                 // discard possible empty line at the end of the history
00279                 // inserted by setText()
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         // If new text is identical, don't copy and don't move insertion point
00331         if (mText.getString() == new_text)
00332         {
00333                 return;
00334         }
00335 
00336         // Check to see if entire field is selected.
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                 // ...keep whole thing selected
00352                 selectAll();
00353         }
00354         else
00355         {
00356                 // try to preserve insertion point, but deselect text
00357                 deselect();
00358         }
00359         setCursor(llmin((S32)mText.length(), getCursor()));
00360 
00361         // Newly set text goes always in the last line of history.
00362         // Possible empty strings (as with chat line) will be deleted later.
00363         mLineHistory.insert( mLineHistory.end(), new_text );
00364         // Set current history line to end of history.
00365         mCurrentHistoryLine = mLineHistory.size() - 1;
00366 
00367         mPrevText = mText;
00368 }
00369 
00370 
00371 // Picks a new cursor position based on the actual screen size of text being drawn.
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)); // min-max range is inclusive
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         //mScrollHPos = 0;
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                 // if everything is selected, handle this as a normal click to change insertion point
00486                 handleMouseDown(x, y, mask);
00487         }
00488         else
00489         {
00490                 const LLWString& wtext = mText.getWString();
00491 
00492                 BOOL doSelectAll = TRUE;
00493 
00494                 // Select the word we're on
00495                 if( isPartOfWord( wtext[mCursorPos] ) )
00496                 {
00497                         S32 old_selection_start = mLastSelectionStart;
00498                         S32 old_selection_end = mLastSelectionEnd;
00499 
00500                         // Select word the cursor is over
00501                         while ((mCursorPos > 0) && isPartOfWord( wtext[mCursorPos-1] ))
00502                         {       // Find the start of the word
00503                                 mCursorPos--;
00504                         }
00505                         startSelection();       
00506 
00507                         while ((mCursorPos < (S32)wtext.length()) && isPartOfWord( wtext[mCursorPos] ) )
00508                         {       // Find the end of the word
00509                                 mCursorPos++;
00510                         }
00511                         mSelectionEnd = mCursorPos;
00512 
00513                         // If nothing changed, then the word was already selected.  Select the whole line.
00514                         doSelectAll = (old_selection_start == mSelectionStart) &&  
00515                                                   (old_selection_end   == mSelectionEnd);
00516                 }
00517                 
00518                 if ( doSelectAll )
00519                 {       // Select everything
00520                         selectAll();
00521                 }
00522         }
00523 
00524         // We don't want handleMouseUp() to "finish" the selection (and thereby
00525         // set mSelectionEnd to where the mouse is), so we finish the selection 
00526         // here.
00527         mIsSelecting = FALSE;  
00528 
00529         // delay cursor flashing
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                         // Handle selection extension
00556                         S32 old_cursor_pos = getCursor();
00557                         setCursorAtLocalPos(x);
00558 
00559                         if (hasSelection())
00560                         {
00561                                 /* Mac-like behavior - extend selection towards the cursor
00562                                 if (getCursor() < mSelectionStart
00563                                         && getCursor() < mSelectionEnd)
00564                                 {
00565                                         // ...left of selection
00566                                         mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
00567                                         mSelectionEnd = getCursor();
00568                                 }
00569                                 else if (getCursor() > mSelectionStart
00570                                         && getCursor() > mSelectionEnd)
00571                                 {
00572                                         // ...right of selection
00573                                         mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
00574                                         mSelectionEnd = getCursor();
00575                                 }
00576                                 else
00577                                 {
00578                                         mSelectionEnd = getCursor();
00579                                 }
00580                                 */
00581                                 // Windows behavior
00582                                 mSelectionEnd = getCursor();
00583                         }
00584                         else
00585                         {
00586                                 mSelectionStart = old_cursor_pos;
00587                                 mSelectionEnd = getCursor();
00588                         }
00589                         // assume we're starting a drag select
00590                         mIsSelecting = TRUE;
00591                 }
00592                 else
00593                 {
00594                         // Save selection for word/line selecting on double-click
00595                         mLastSelectionStart = mSelectionStart;
00596                         mLastSelectionEnd = mSelectionEnd;
00597 
00598                         // Move cursor and deselect for regular click
00599                         setCursorAtLocalPos( x );
00600                         deselect();
00601                         startSelection();
00602                 }
00603 
00604                 gFocusMgr.setMouseCapture( this );
00605         }
00606 
00607         // delay cursor flashing
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                         // Scroll if mouse cursor outside of bounds
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                                         // Scroll to the left
00640                                         mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
00641                                 }
00642                                 else
00643                                 if( (x > mMaxHPixels) && (mCursorPos < (S32)mText.length()) )
00644                                 {
00645                                         // If scrolling one pixel would make a difference...
00646                                         S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
00647                                         if( pixels_after_scrolling_one_char >= mMaxHPixels )
00648                                         {
00649                                                 // ...scroll to the right
00650                                                 mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
00651                                         }
00652                                 }
00653                         }
00654 
00655                         setCursorAtLocalPos( x );
00656                         mSelectionEnd = getCursor();
00657 
00658                         // delay cursor flashing
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                 // delay cursor flashing
00704                 mKeystrokeTimer.reset();
00705         }
00706 
00707         return handled;
00708 }
00709 
00710 
00711 // Remove a single character from the text
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         // Inserting character
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                 // Will we need to scroll?
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 // Extends the selection box to the new cursor position
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         // JC, yes, this seems odd, but I think you have to presume a 
00792         // selection dragged from the end towards the start.
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 // cut selection to clipboard
00950 void LLLineEditor::cut()
00951 {
00952         if( canCut() )
00953         {
00954                 // Prepare for possible rollback
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                 // Validate new string and rollback the if needed.
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 // copy selection to clipboard
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 // paste from clipboard
01002 void LLLineEditor::paste()
01003 {
01004         if (canPaste())
01005         {
01006                 LLWString paste = gClipboard.getPasteWString();
01007                 if (!paste.empty())
01008                 {
01009                         // Prepare for possible rollback
01010                         LLLineEditorRollback rollback(this);
01011                         
01012                         // Delete any selected characters
01013                         if (hasSelection())
01014                         {
01015                                 deleteSelection();
01016                         }
01017 
01018                         // Clean up string (replace tabs and returns and remove characters that our fonts don't support.)
01019                         LLWString clean_string(paste);
01020                         LLWString::replaceTabsWithSpaces(clean_string, 1);
01021                         //clean_string = wstring_detabify(paste, 1);
01022                         LLWString::replaceChar(clean_string, '\n', ' ');
01023 
01024                         // Insert the string
01025 
01026                         //check to see that the size isn't going to be larger than the
01027                         //max number of characters or bytes
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                                 //loop over the "wide" characters (symbols)
01038                                 //and check to see how large (in bytes) each symbol is.
01039                                 while ( total_bytes <= available_bytes )
01040                                 {
01041                                         //while we still have available bytes
01042                                         //"accept" the current symbol and check the size
01043                                         //of the next one
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                                 // We can't insert all the characters.  Insert as many as possible
01054                                 // but make a noise to alert the user. JC
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                         // Validate new string and rollback the if needed.
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                         //llinfos << "Handling backspace" << llendl;
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         // handle ctrl-uparrow if we have a history enabled line editor.
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         // handle ctrl-downarrow if we have a history enabled line editor
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                 // store sent line in history
01226                 updateHistory();
01227                 break;
01228 
01229         case KEY_ESCAPE:
01230             if (mRevertOnEsc && mText.getString() != mPrevText)
01231                 {
01232                         setText(mPrevText);
01233                         // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged)
01234                 }
01235                 break;
01236                 
01237         default:
01238                 break;
01239         }
01240 
01241         if( !handled && mHandleEditKeysDirectly )
01242         {
01243                 // Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system.
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                 // Handle most keys only if the text editor is writeable.
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                         // Most keystrokes will make the selection box go away, but not all will.
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                         // If read-only, don't allow changes
01344                         need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
01345 
01346                         // Validate new string and rollback the keystroke if needed.
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                         // Notify owner if requested
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)) // Control character or DEL
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                 // Validate new string and rollback the keystroke if needed.
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                 // Notify owner if requested
01405                 if( !need_to_rollback && handled )
01406                 {
01407                         if( mKeystrokeCallback )
01408                         {
01409                                 // HACK! The only usage of this callback doesn't do anything with the character.
01410                                 // We'll have to do something about this if something ever changes! - Doug
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                 // Prepare for possible rollback
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                 // Validate new string and rollback the if needed.
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         // draw rectangle for the background
01481         LLRect background( 0, mRect.getHeight(), mRect.getWidth(), 0 );
01482         background.stretch( -mBorderThickness );
01483 
01484         LLColor4 bg_color = mReadOnlyBgColor;
01485 
01486         // drawing solids requires texturing be disabled
01487         {
01488                 LLGLSNoTexture no_texture;
01489                 // draw background for text
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         // draw text
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                         // unselected, left side
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                         // selected middle
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                         // unselected, right side
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         // If we're editing...
01607         if( gFocusMgr.getKeyboardFocus() == this)
01608         {
01609                 // (Flash the cursor every half second)
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                                 // Use same color as text for the Cursor
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                                 // Make sure the IME is in the right place
01638                                 S32 pixels_after_scroll = findPixelNearestPos();        // RCalculcate for IME position
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                 // Draw children (border)
01654                 //mBorder->setVisible(TRUE);
01655                 mBorder->setKeyboardFocusHighlight( TRUE );
01656                 LLView::draw();
01657                 mBorder->setKeyboardFocusHighlight( FALSE );
01658                 //mBorder->setVisible(FALSE);
01659         }
01660         else // does not have keyboard input
01661         {
01662                 // draw label if no text provided
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                 // Draw children (border)
01675                 LLView::draw();
01676         }
01677 
01678         if (mDrawAsterixes)
01679         {
01680                 mText = saved_text;
01681         }
01682 }
01683 
01684 
01685 // Returns the local screen space X coordinate associated with the text cursor position.
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 //virtual
01699 void LLLineEditor::clear()
01700 {
01701         mText.clear();
01702         setCursor(0);
01703 }
01704 
01705 //virtual
01706 void LLLineEditor::onTabInto()
01707 {
01708         selectAll();
01709 }
01710 
01711 //virtual
01712 BOOL LLLineEditor::acceptsTextInput() const
01713 {
01714         return TRUE;
01715 }
01716 
01717 // Start or stop the editor from accepting text-editing keystrokes
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         // getting focus when we didn't have it before, and we want to select all
01729         if (!old_state && new_state && mSelectAllonFocusReceived)
01730         {
01731                 selectAll();
01732                 // We don't want handleMouseUp() to "finish" the selection (and thereby
01733                 // set mSelectionEnd to where the mouse is), so we finish the selection 
01734                 // here.
01735                 mIsSelecting = FALSE;
01736         }
01737 
01738         if( new_state )
01739         {
01740                 gEditMenuHandler = this;
01741 
01742                 // Don't start the cursor flashing right away
01743                 mKeystrokeTimer.reset();
01744         }
01745         else
01746         {
01747                 // Not really needed, since loss of keyboard focus should take care of this,
01748                 // but limited paranoia is ok.
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                 // Allow Language Text Input only when this LineEditor has
01762                 // no prevalidate function attached.  This criterion works
01763                 // fine on 1.15.0.2, since all prevalidate func reject any
01764                 // non-ASCII characters.  I'm not sure on future versions,
01765                 // however.
01766                 getWindow()->allowLanguageTextInput(mPrevalidateFunc == NULL);
01767         }
01768 }
01769 
01770 //virtual 
01771 void LLLineEditor::setRect(const LLRect& rect)
01772 {
01773         LLUICtrl::setRect(rect);
01774         if (mBorder)
01775         {
01776                 LLRect border_rect = mBorder->getRect();
01777                 // Scalable UI somehow made these rectangles off-by-one.
01778                 // I don't know why. JC
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 // Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
01786 // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
01787 // the simple reasons that intermediate states may be invalid even if the final result is valid.
01788 // 
01789 // static
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                 // May be a comma or period, depending on the locale
01801                 char decimal_point = gResMgr->getDecimalPoint();
01802 
01803                 S32 i = 0;
01804 
01805                 // First character can be a negative sign
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 //static
01825 BOOL LLLineEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); }
01826 
01827 // static
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                 // First character can be a negative sign
01844                 if( '-' == trimmed[0] )
01845                 {
01846                         i++;
01847                 }
01848 
01849                 // May be a comma or period, depending on the locale
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                                         // can't have two
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         // Gotta have at least one
01881         success = has_digit;
01882 
01883         return success;
01884 }
01885 
01886 // Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
01887 // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
01888 // the simple reasons that intermediate states may be invalid even if the final result is valid.
01889 //
01890 // static
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                 // First character can be a negative sign
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 // static
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 // static
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 // static
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 // static
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 // static
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 // virtual
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 // static
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(); // button is square, and as tall as search editor
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 //virtual
02395 EWidgetType LLSearchEditor::getWidgetType() const
02396 {
02397         return WIDGET_TYPE_SEARCH_EDITOR;
02398 }
02399 
02400 //virtual
02401 LLString LLSearchEditor::getWidgetTag() const
02402 {
02403         return LL_SEARCH_EDITOR_TAG;
02404 }
02405 
02406 //virtual
02407 void LLSearchEditor::setValue(const LLSD& value )
02408 {
02409         mSearchEdit->setValue(value);
02410 }
02411 
02412 //virtual
02413 LLSD LLSearchEditor::getValue() const
02414 {
02415         return mSearchEdit->getValue();
02416 }
02417 
02418 //virtual
02419 BOOL LLSearchEditor::setTextArg( const LLString& key, const LLStringExplicit& text )
02420 {
02421         return mSearchEdit->setTextArg(key, text);
02422 }
02423 
02424 //virtual
02425 BOOL LLSearchEditor::setLabelArg( const LLString& key, const LLStringExplicit& text )
02426 {
02427         return mSearchEdit->setLabelArg(key, text);
02428 }
02429 
02430 //virtual
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 //static
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 //static
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 // static
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 }

Generated on Thu Jul 1 06:08:48 2010 for Second Life Viewer by  doxygen 1.4.7