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

Generated on Fri May 16 08:32:53 2008 for SecondLife by  doxygen 1.5.5