PageRenderTime 205ms CodeModel.GetById 65ms app.highlight 126ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llui/lllineeditor.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2411 lines | 1844 code | 332 blank | 235 comment | 329 complexity | e94dd9a9004889715e8763428b77bcf7 MD5 | raw file
   1/** 
   2 * @file lllineeditor.cpp
   3 * @brief LLLineEditor base class
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27// Text editor widget to let users enter a single line.
  28
  29#include "linden_common.h"
  30 
  31#define LLLINEEDITOR_CPP
  32#include "lllineeditor.h"
  33
  34#include "lltexteditor.h"
  35#include "llmath.h"
  36#include "llfontgl.h"
  37#include "llgl.h"
  38#include "lltimer.h"
  39
  40#include "llcalc.h"
  41//#include "llclipboard.h"
  42#include "llcontrol.h"
  43#include "llbutton.h"
  44#include "llfocusmgr.h"
  45#include "llkeyboard.h"
  46#include "llrect.h"
  47#include "llresmgr.h"
  48#include "llstring.h"
  49#include "llwindow.h"
  50#include "llui.h"
  51#include "lluictrlfactory.h"
  52#include "llclipboard.h"
  53#include "llmenugl.h"
  54
  55//
  56// Imported globals
  57//
  58
  59//
  60// Constants
  61//
  62
  63const F32	CURSOR_FLASH_DELAY = 1.0f;  // in seconds
  64const S32	SCROLL_INCREMENT_ADD = 0;	// make space for typing
  65const S32   SCROLL_INCREMENT_DEL = 4;	// make space for baskspacing
  66const F32   AUTO_SCROLL_TIME = 0.05f;
  67const F32	TRIPLE_CLICK_INTERVAL = 0.3f;	// delay between double and triple click. *TODO: make this equal to the double click interval?
  68
  69const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET
  70
  71static LLDefaultChildRegistry::Register<LLLineEditor> r1("line_editor");
  72
  73// Compiler optimization, generate extern template
  74template class LLLineEditor* LLView::getChild<class LLLineEditor>(
  75	const std::string& name, BOOL recurse) const;
  76
  77//
  78// Member functions
  79//
  80
  81LLLineEditor::Params::Params()
  82:	max_length(""),
  83    keystroke_callback("keystroke_callback"),
  84	prevalidate_callback("prevalidate_callback"),
  85	prevalidate_input_callback("prevalidate_input_callback"),
  86	background_image("background_image"),
  87	background_image_disabled("background_image_disabled"),
  88	background_image_focused("background_image_focused"),
  89	select_on_focus("select_on_focus", false),
  90	revert_on_esc("revert_on_esc", true),
  91	commit_on_focus_lost("commit_on_focus_lost", true),
  92	ignore_tab("ignore_tab", true),
  93	is_password("is_password", false),
  94	cursor_color("cursor_color"),
  95	text_color("text_color"),
  96	text_readonly_color("text_readonly_color"),
  97	text_tentative_color("text_tentative_color"),
  98	highlight_color("highlight_color"),
  99	preedit_bg_color("preedit_bg_color"),
 100	border(""),
 101	bg_visible("bg_visible"),
 102	text_pad_left("text_pad_left"),
 103	text_pad_right("text_pad_right"),
 104	default_text("default_text")
 105{
 106	changeDefault(mouse_opaque, true);
 107	addSynonym(select_on_focus, "select_all_on_focus_received");
 108	addSynonym(border, "border");
 109	addSynonym(label, "watermark_text");
 110	addSynonym(max_length.chars, "max_length");
 111}
 112
 113LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 114:	LLUICtrl(p),
 115	mMaxLengthBytes(p.max_length.bytes),
 116	mMaxLengthChars(p.max_length.chars),
 117	mCursorPos( 0 ),
 118	mScrollHPos( 0 ),
 119	mTextPadLeft(p.text_pad_left),
 120	mTextPadRight(p.text_pad_right),
 121	mTextLeftEdge(0),		// computed in updateTextPadding() below
 122	mTextRightEdge(0),		// computed in updateTextPadding() below
 123	mCommitOnFocusLost( p.commit_on_focus_lost ),
 124	mRevertOnEsc( p.revert_on_esc ),
 125	mKeystrokeCallback( p.keystroke_callback() ),
 126	mIsSelecting( FALSE ),
 127	mSelectionStart( 0 ),
 128	mSelectionEnd( 0 ),
 129	mLastSelectionX(-1),
 130	mLastSelectionY(-1),
 131	mLastSelectionStart(-1),
 132	mLastSelectionEnd(-1),
 133	mBorderThickness( 0 ),
 134	mIgnoreArrowKeys( FALSE ),
 135	mIgnoreTab( p.ignore_tab ),
 136	mDrawAsterixes( p.is_password ),
 137	mSelectAllonFocusReceived( p.select_on_focus ),
 138	mSelectAllonCommit( TRUE ),
 139	mPassDelete(FALSE),
 140	mReadOnly(FALSE),
 141	mBgImage( p.background_image ),
 142	mBgImageDisabled( p.background_image_disabled ),
 143	mBgImageFocused( p.background_image_focused ),
 144	mHaveHistory(FALSE),
 145	mReplaceNewlinesWithSpaces( TRUE ),
 146	mLabel(p.label),
 147	mCursorColor(p.cursor_color()),
 148	mFgColor(p.text_color()),
 149	mReadOnlyFgColor(p.text_readonly_color()),
 150	mTentativeFgColor(p.text_tentative_color()),
 151	mHighlightColor(p.highlight_color()),
 152	mPreeditBgColor(p.preedit_bg_color()),
 153	mGLFont(p.font),
 154	mContextMenuHandle()
 155{
 156	llassert( mMaxLengthBytes > 0 );
 157
 158	mScrollTimer.reset();
 159	mTripleClickTimer.reset();
 160	setText(p.default_text());
 161
 162	// Initialize current history line iterator
 163	mCurrentHistoryLine = mLineHistory.begin();
 164
 165	LLRect border_rect(getLocalRect());
 166	// adjust for gl line drawing glitch
 167	border_rect.mTop -= 1;
 168	border_rect.mRight -=1;
 169	LLViewBorder::Params border_p(p.border);
 170	border_p.rect = border_rect;
 171	border_p.follows.flags = FOLLOWS_ALL;
 172	border_p.bevel_style = LLViewBorder::BEVEL_IN;
 173	mBorder = LLUICtrlFactory::create<LLViewBorder>(border_p);
 174	addChild( mBorder );
 175
 176	// clamp text padding to current editor size
 177	updateTextPadding();
 178	setCursor(mText.length());
 179
 180	setPrevalidateInput(p.prevalidate_input_callback());
 181	setPrevalidate(p.prevalidate_callback());
 182
 183	LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
 184		("menu_text_editor.xml",
 185		 LLMenuGL::sMenuContainer,
 186		 LLMenuHolderGL::child_registry_t::instance());
 187	setContextMenu(menu);
 188}
 189 
 190LLLineEditor::~LLLineEditor()
 191{
 192	mCommitOnFocusLost = FALSE;
 193
 194	// calls onCommit() while LLLineEditor still valid
 195	gFocusMgr.releaseFocusIfNeeded( this );
 196}
 197
 198
 199void LLLineEditor::onFocusReceived()
 200{
 201	gEditMenuHandler = this;
 202	LLUICtrl::onFocusReceived();
 203	updateAllowingLanguageInput();
 204}
 205
 206void LLLineEditor::onFocusLost()
 207{
 208	// The call to updateAllowLanguageInput()
 209	// when loosing the keyboard focus *may*
 210	// indirectly invoke handleUnicodeCharHere(), 
 211	// so it must be called before onCommit.
 212	updateAllowingLanguageInput();
 213
 214	if( mCommitOnFocusLost && mText.getString() != mPrevText) 
 215	{
 216		onCommit();
 217	}
 218
 219	if( gEditMenuHandler == this )
 220	{
 221		gEditMenuHandler = NULL;
 222	}
 223
 224	getWindow()->showCursorFromMouseMove();
 225
 226	LLUICtrl::onFocusLost();
 227}
 228
 229// virtual
 230void LLLineEditor::onCommit()
 231{
 232	// put current line into the line history
 233	updateHistory();
 234
 235	setControlValue(getValue());
 236	LLUICtrl::onCommit();
 237
 238	// Selection on commit needs to be turned off when evaluating maths
 239	// expressions, to allow indication of the error position
 240	if (mSelectAllonCommit) selectAll();
 241}
 242
 243// Returns TRUE if user changed value at all
 244// virtual
 245BOOL LLLineEditor::isDirty() const
 246{
 247	return mText.getString() != mPrevText;
 248}
 249
 250// Clear dirty state
 251// virtual
 252void LLLineEditor::resetDirty()
 253{
 254	mPrevText = mText.getString();
 255}		
 256
 257// assumes UTF8 text
 258// virtual
 259void LLLineEditor::setValue(const LLSD& value )
 260{
 261	setText(value.asString());
 262}
 263
 264//virtual
 265LLSD LLLineEditor::getValue() const
 266{
 267	return LLSD(getText());
 268}
 269
 270
 271// line history support
 272void LLLineEditor::updateHistory()
 273{
 274	// On history enabled line editors, remember committed line and
 275	// reset current history line number.
 276	// Be sure only to remember lines that are not empty and that are
 277	// different from the last on the list.
 278	if( mHaveHistory && getLength() )
 279	{
 280		if( !mLineHistory.empty() )
 281		{
 282			// When not empty, last line of history should always be blank.
 283			if( mLineHistory.back().empty() )
 284			{
 285				// discard the empty line
 286				mLineHistory.pop_back();
 287			}
 288			else
 289			{
 290				LL_WARNS("") << "Last line of history was not blank." << LL_ENDL;
 291			}
 292		}
 293
 294		// Add text to history, ignoring duplicates
 295		if( mLineHistory.empty() || getText() != mLineHistory.back() )
 296		{
 297			mLineHistory.push_back( getText() );
 298		}
 299
 300		// Restore the blank line and set mCurrentHistoryLine to point at it
 301		mLineHistory.push_back( "" );
 302		mCurrentHistoryLine = mLineHistory.end() - 1;
 303	}
 304}
 305
 306void LLLineEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
 307{
 308	LLUICtrl::reshape(width, height, called_from_parent);
 309	updateTextPadding(); // For clamping side-effect.
 310	setCursor(mCursorPos); // For clamping side-effect.
 311}
 312
 313void LLLineEditor::setEnabled(BOOL enabled)
 314{
 315	mReadOnly = !enabled;
 316	setTabStop(!mReadOnly);
 317	updateAllowingLanguageInput();
 318}
 319
 320
 321void LLLineEditor::setMaxTextLength(S32 max_text_length)
 322{
 323	S32 max_len = llmax(0, max_text_length);
 324	mMaxLengthBytes = max_len;
 325} 
 326
 327void LLLineEditor::setMaxTextChars(S32 max_text_chars)
 328{
 329	S32 max_chars = llmax(0, max_text_chars);
 330	mMaxLengthChars = max_chars;
 331} 
 332
 333void LLLineEditor::getTextPadding(S32 *left, S32 *right)
 334{
 335	*left = mTextPadLeft;
 336	*right = mTextPadRight;
 337}
 338
 339void LLLineEditor::setTextPadding(S32 left, S32 right)
 340{
 341	mTextPadLeft = left;
 342	mTextPadRight = right;
 343	updateTextPadding();
 344}
 345
 346void LLLineEditor::updateTextPadding()
 347{
 348	mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth());
 349	mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth());
 350}
 351
 352
 353void LLLineEditor::setText(const LLStringExplicit &new_text)
 354{
 355	// If new text is identical, don't copy and don't move insertion point
 356	if (mText.getString() == new_text)
 357	{
 358		return;
 359	}
 360
 361	// Check to see if entire field is selected.
 362	S32 len = mText.length();
 363	BOOL all_selected = (len > 0)
 364		&& (( mSelectionStart == 0 && mSelectionEnd == len ) 
 365			|| ( mSelectionStart == len && mSelectionEnd == 0 ));
 366
 367	// Do safe truncation so we don't split multi-byte characters
 368	// also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor
 369	all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived);
 370
 371	std::string truncated_utf8 = new_text;
 372	if (truncated_utf8.size() > (U32)mMaxLengthBytes)
 373	{	
 374		truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);
 375	}
 376	mText.assign(truncated_utf8);
 377
 378	if (mMaxLengthChars)
 379	{
 380		LLWString truncated_wstring = utf8str_to_wstring(truncated_utf8);
 381		if (truncated_wstring.size() > (U32)mMaxLengthChars)
 382		{
 383			truncated_wstring = truncated_wstring.substr(0, mMaxLengthChars);
 384		}
 385		mText.assign(wstring_to_utf8str(truncated_wstring));
 386	}
 387
 388	if (all_selected)
 389	{
 390		// ...keep whole thing selected
 391		selectAll();
 392	}
 393	else
 394	{
 395		// try to preserve insertion point, but deselect text
 396		deselect();
 397	}
 398	setCursor(llmin((S32)mText.length(), getCursor()));
 399
 400	// Set current history line to end of history.
 401	if (mLineHistory.empty())
 402	{
 403		mCurrentHistoryLine = mLineHistory.end();
 404	}
 405	else
 406	{
 407		mCurrentHistoryLine = mLineHistory.end() - 1;
 408	}
 409
 410	mPrevText = mText;
 411}
 412
 413
 414// Picks a new cursor position based on the actual screen size of text being drawn.
 415void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
 416{
 417	S32 cursor_pos = calcCursorPos(local_mouse_x);
 418
 419	S32 left_pos = llmin( mSelectionStart, cursor_pos );
 420	S32 length = llabs( mSelectionStart - cursor_pos );
 421	const LLWString& substr = mText.getWString().substr(left_pos, length);
 422
 423	if (mIsSelecting && !prevalidateInput(substr))
 424		return;
 425
 426	setCursor(cursor_pos);
 427}
 428
 429void LLLineEditor::setCursor( S32 pos )
 430{
 431	S32 old_cursor_pos = getCursor();
 432	mCursorPos = llclamp( pos, 0, mText.length());
 433
 434	// position of end of next character after cursor
 435	S32 pixels_after_scroll = findPixelNearestPos();
 436	if( pixels_after_scroll > mTextRightEdge )
 437	{
 438		S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
 439		S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left))); 
 440		// character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters)
 441		// or first character if cursor is at beginning
 442		S32 new_last_visible_char = llmax(0, getCursor() - 1);
 443		S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char);
 444		if (old_cursor_pos == last_visible_char)
 445		{
 446			mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
 447		}
 448		else
 449		{
 450			mScrollHPos = min_scroll;
 451		}
 452	}
 453	else if (getCursor() < mScrollHPos)
 454	{
 455		if (old_cursor_pos == mScrollHPos)
 456		{
 457			mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL));
 458		}
 459		else
 460		{
 461			mScrollHPos = getCursor();
 462		}
 463	}
 464}
 465
 466
 467void LLLineEditor::setCursorToEnd()
 468{
 469	setCursor(mText.length());
 470	deselect();
 471}
 472
 473BOOL LLLineEditor::canDeselect() const
 474{
 475	return hasSelection();
 476}
 477
 478void LLLineEditor::deselect()
 479{
 480	mSelectionStart = 0;
 481	mSelectionEnd = 0;
 482	mIsSelecting = FALSE;
 483}
 484
 485
 486void LLLineEditor::startSelection()
 487{
 488	mIsSelecting = TRUE;
 489	mSelectionStart = getCursor();
 490	mSelectionEnd = getCursor();
 491}
 492
 493void LLLineEditor::endSelection()
 494{
 495	if( mIsSelecting )
 496	{
 497		mIsSelecting = FALSE;
 498		mSelectionEnd = getCursor();
 499	}
 500}
 501
 502BOOL LLLineEditor::canSelectAll() const
 503{
 504	return TRUE;
 505}
 506
 507void LLLineEditor::selectAll()
 508{
 509	if (!prevalidateInput(mText.getWString()))
 510	{
 511		return;
 512	}
 513
 514	mSelectionStart = mText.length();
 515	mSelectionEnd = 0;
 516	setCursor(mSelectionEnd);
 517	//mScrollHPos = 0;
 518	mIsSelecting = TRUE;
 519	updatePrimary();
 520}
 521
 522
 523BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
 524{
 525	setFocus( TRUE );
 526	mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL);
 527
 528	if (mSelectionEnd == 0 && mSelectionStart == mText.length())
 529	{
 530		// if everything is selected, handle this as a normal click to change insertion point
 531		handleMouseDown(x, y, mask);
 532	}
 533	else
 534	{
 535		const LLWString& wtext = mText.getWString();
 536
 537		BOOL doSelectAll = TRUE;
 538
 539		// Select the word we're on
 540		if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
 541		{
 542			S32 old_selection_start = mLastSelectionStart;
 543			S32 old_selection_end = mLastSelectionEnd;
 544
 545			// Select word the cursor is over
 546			while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] ))
 547			{	// Find the start of the word
 548				mCursorPos--;
 549			}
 550			startSelection();	
 551
 552			while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
 553			{	// Find the end of the word
 554				mCursorPos++;
 555			}
 556			mSelectionEnd = mCursorPos;
 557
 558			// If nothing changed, then the word was already selected.  Select the whole line.
 559			doSelectAll = (old_selection_start == mSelectionStart) &&  
 560						  (old_selection_end   == mSelectionEnd);
 561		}
 562		
 563		if ( doSelectAll )
 564		{	// Select everything
 565			selectAll();
 566		}
 567	}
 568
 569	// We don't want handleMouseUp() to "finish" the selection (and thereby
 570	// set mSelectionEnd to where the mouse is), so we finish the selection 
 571	// here.
 572	mIsSelecting = FALSE;  
 573
 574	// delay cursor flashing
 575	mKeystrokeTimer.reset();
 576
 577	// take selection to 'primary' clipboard
 578	updatePrimary();
 579
 580	return TRUE;
 581}
 582
 583BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
 584{
 585	// Check first whether the "clear search" button wants to deal with this.
 586	if(childrenHandleMouseDown(x, y, mask) != NULL) 
 587	{
 588		return TRUE;
 589	}
 590	
 591	if (!mSelectAllonFocusReceived
 592		|| gFocusMgr.getKeyboardFocus() == this)
 593	{
 594		mLastSelectionStart = -1;
 595		mLastSelectionStart = -1;
 596
 597		if (mask & MASK_SHIFT)
 598		{
 599			// assume we're starting a drag select
 600			mIsSelecting = TRUE;
 601
 602			// Handle selection extension
 603			S32 old_cursor_pos = getCursor();
 604			setCursorAtLocalPos(x);
 605
 606			if (hasSelection())
 607			{
 608				/* Mac-like behavior - extend selection towards the cursor
 609				if (getCursor() < mSelectionStart
 610					&& getCursor() < mSelectionEnd)
 611				{
 612					// ...left of selection
 613					mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
 614					mSelectionEnd = getCursor();
 615				}
 616				else if (getCursor() > mSelectionStart
 617					&& getCursor() > mSelectionEnd)
 618				{
 619					// ...right of selection
 620					mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
 621					mSelectionEnd = getCursor();
 622				}
 623				else
 624				{
 625					mSelectionEnd = getCursor();
 626				}
 627				*/
 628				// Windows behavior
 629				mSelectionEnd = getCursor();
 630			}
 631			else
 632			{
 633				mSelectionStart = old_cursor_pos;
 634				mSelectionEnd = getCursor();
 635			}
 636		}
 637		else
 638		{
 639			if (mTripleClickTimer.hasExpired())
 640			{
 641				// Save selection for word/line selecting on double-click
 642				mLastSelectionStart = mSelectionStart;
 643				mLastSelectionEnd = mSelectionEnd;
 644
 645				// Move cursor and deselect for regular click
 646				setCursorAtLocalPos( x );
 647				deselect();
 648				startSelection();
 649			}
 650			else // handle triple click
 651			{
 652				selectAll();
 653				// We don't want handleMouseUp() to "finish" the selection (and thereby
 654				// set mSelectionEnd to where the mouse is), so we finish the selection 
 655				// here.
 656				mIsSelecting = FALSE;
 657			}
 658		}
 659
 660		gFocusMgr.setMouseCapture( this );
 661	}
 662
 663	setFocus(TRUE);
 664
 665	// delay cursor flashing
 666	mKeystrokeTimer.reset();
 667	
 668	if (mMouseDownSignal)
 669		(*mMouseDownSignal)(this,x,y,mask);
 670
 671	return TRUE;
 672}
 673
 674BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
 675{
 676        // llinfos << "MiddleMouseDown" << llendl;
 677	setFocus( TRUE );
 678	if( canPastePrimary() )
 679	{
 680		setCursorAtLocalPos(x);
 681		pastePrimary();
 682	}
 683	return TRUE;
 684}
 685
 686BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
 687{
 688	setFocus(TRUE);
 689	if (!LLUICtrl::handleRightMouseDown(x, y, mask))
 690	{
 691		showContextMenu(x, y);
 692	}
 693	return TRUE;
 694}
 695
 696BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
 697{
 698	BOOL handled = FALSE;
 699	// Check first whether the "clear search" button wants to deal with this.
 700	if(!hasMouseCapture())
 701	{
 702		if(childrenHandleHover(x, y, mask) != NULL) 
 703		{
 704			return TRUE;
 705		}
 706	}
 707
 708	if( (hasMouseCapture()) && mIsSelecting )
 709	{
 710		if (x != mLastSelectionX || y != mLastSelectionY)
 711		{
 712			mLastSelectionX = x;
 713			mLastSelectionY = y;
 714		}
 715		// Scroll if mouse cursor outside of bounds
 716		if (mScrollTimer.hasExpired())
 717		{
 718			S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
 719			mScrollTimer.reset();
 720			mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
 721			if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) )
 722			{
 723				// Scroll to the left
 724				mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
 725			}
 726			else
 727			if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) )
 728			{
 729				// If scrolling one pixel would make a difference...
 730				S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
 731				if( pixels_after_scrolling_one_char >= mTextRightEdge )
 732				{
 733					// ...scroll to the right
 734					mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
 735				}
 736			}
 737		}
 738
 739		setCursorAtLocalPos( x );
 740		mSelectionEnd = getCursor();
 741
 742		// delay cursor flashing
 743		mKeystrokeTimer.reset();
 744
 745		getWindow()->setCursor(UI_CURSOR_IBEAM);
 746		lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;		
 747		handled = TRUE;
 748	}
 749
 750	if( !handled  )
 751	{
 752		getWindow()->setCursor(UI_CURSOR_IBEAM);
 753		lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;		
 754		handled = TRUE;
 755	}
 756
 757	return handled;
 758}
 759
 760
 761BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
 762{
 763	BOOL	handled = FALSE;
 764
 765	if( hasMouseCapture() )
 766	{
 767		gFocusMgr.setMouseCapture( NULL );
 768		handled = TRUE;
 769	}
 770
 771	// Check first whether the "clear search" button wants to deal with this.
 772	if(!handled && childrenHandleMouseUp(x, y, mask) != NULL) 
 773	{
 774		return TRUE;
 775	}
 776
 777	if( mIsSelecting )
 778	{
 779		setCursorAtLocalPos( x );
 780		mSelectionEnd = getCursor();
 781
 782		handled = TRUE;
 783	}
 784
 785	if( handled )
 786	{
 787		// delay cursor flashing
 788		mKeystrokeTimer.reset();
 789
 790		// take selection to 'primary' clipboard
 791		updatePrimary();
 792	}
 793	
 794	// We won't call LLUICtrl::handleMouseUp to avoid double calls of  childrenHandleMouseUp().Just invoke the signal manually.
 795	if (mMouseUpSignal)
 796		(*mMouseUpSignal)(this,x,y, mask);
 797	return handled;
 798}
 799
 800
 801// Remove a single character from the text
 802void LLLineEditor::removeChar()
 803{
 804	if( getCursor() > 0 )
 805	{
 806		if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1)))
 807			return;
 808
 809		mText.erase(getCursor() - 1, 1);
 810
 811		setCursor(getCursor() - 1);
 812	}
 813	else
 814	{
 815		LLUI::reportBadKeystroke();
 816	}
 817}
 818
 819
 820void LLLineEditor::addChar(const llwchar uni_char)
 821{
 822	llwchar new_c = uni_char;
 823	if (hasSelection())
 824	{
 825		deleteSelection();
 826	}
 827	else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
 828	{
 829		if (!prevalidateInput(mText.getWString().substr(getCursor(), 1)))
 830			return;
 831
 832		mText.erase(getCursor(), 1);
 833	}
 834
 835	S32 cur_bytes = mText.getString().size();
 836
 837	S32 new_bytes = wchar_utf8_length(new_c);
 838
 839	BOOL allow_char = TRUE;
 840
 841	// Check byte length limit
 842	if ((new_bytes + cur_bytes) > mMaxLengthBytes)
 843	{
 844		allow_char = FALSE;
 845	}
 846	else if (mMaxLengthChars)
 847	{
 848		S32 wide_chars = mText.getWString().size();
 849		if ((wide_chars + 1) > mMaxLengthChars)
 850		{
 851			allow_char = FALSE;
 852		}
 853	}
 854
 855	if (allow_char)
 856	{
 857		// Will we need to scroll?
 858		LLWString w_buf;
 859		w_buf.assign(1, new_c);
 860
 861		mText.insert(getCursor(), w_buf);
 862		setCursor(getCursor() + 1);
 863	}
 864	else
 865	{
 866		LLUI::reportBadKeystroke();
 867	}
 868
 869	getWindow()->hideCursorUntilMouseMove();
 870}
 871
 872// Extends the selection box to the new cursor position
 873void LLLineEditor::extendSelection( S32 new_cursor_pos )
 874{
 875	if( !mIsSelecting )
 876	{
 877		startSelection();
 878	}
 879	
 880	S32 left_pos = llmin( mSelectionStart, new_cursor_pos );
 881	S32 selection_length = llabs( mSelectionStart - new_cursor_pos );
 882	const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
 883
 884	if (!prevalidateInput(selection))
 885		return;
 886
 887	setCursor(new_cursor_pos);
 888	mSelectionEnd = getCursor();
 889}
 890
 891
 892void LLLineEditor::setSelection(S32 start, S32 end)
 893{
 894	S32 len = mText.length();
 895
 896	mIsSelecting = TRUE;
 897
 898	// JC, yes, this seems odd, but I think you have to presume a 
 899	// selection dragged from the end towards the start.
 900	mSelectionStart = llclamp(end, 0, len);
 901	mSelectionEnd = llclamp(start, 0, len);
 902	setCursor(start);
 903}
 904
 905void LLLineEditor::setDrawAsterixes(BOOL b)
 906{
 907	mDrawAsterixes = b;
 908	updateAllowingLanguageInput();
 909}
 910
 911S32 LLLineEditor::prevWordPos(S32 cursorPos) const
 912{
 913	const LLWString& wtext = mText.getWString();
 914	while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
 915	{
 916		cursorPos--;
 917	}
 918	while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
 919	{
 920		cursorPos--;
 921	}
 922	return cursorPos;
 923}
 924
 925S32 LLLineEditor::nextWordPos(S32 cursorPos) const
 926{
 927	const LLWString& wtext = mText.getWString();
 928	while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
 929	{
 930		cursorPos++;
 931	} 
 932	while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
 933	{
 934		cursorPos++;
 935	}
 936	return cursorPos;
 937}
 938
 939
 940BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
 941{
 942	BOOL handled = FALSE;
 943
 944	if( mask & MASK_SHIFT )
 945	{
 946		handled = TRUE;
 947
 948		switch( key )
 949		{
 950		case KEY_LEFT:
 951			if( 0 < getCursor() )
 952			{
 953				S32 cursorPos = getCursor() - 1;
 954				if( mask & MASK_CONTROL )
 955				{
 956					cursorPos = prevWordPos(cursorPos);
 957				}
 958				extendSelection( cursorPos );
 959			}
 960			else
 961			{
 962				LLUI::reportBadKeystroke();
 963			}
 964			break;
 965
 966		case KEY_RIGHT:
 967			if( getCursor() < mText.length())
 968			{
 969				S32 cursorPos = getCursor() + 1;
 970				if( mask & MASK_CONTROL )
 971				{
 972					cursorPos = nextWordPos(cursorPos);
 973				}
 974				extendSelection( cursorPos );
 975			}
 976			else
 977			{
 978				LLUI::reportBadKeystroke();
 979			}
 980			break;
 981
 982		case KEY_PAGE_UP:
 983		case KEY_HOME:
 984			extendSelection( 0 );
 985			break;
 986		
 987		case KEY_PAGE_DOWN:
 988		case KEY_END:
 989			{
 990				S32 len = mText.length();
 991				if( len )
 992				{
 993					extendSelection( len );
 994				}
 995				break;
 996			}
 997
 998		default:
 999			handled = FALSE;
1000			break;
1001		}
1002	}
1003
1004	if(handled)
1005	{
1006		// take selection to 'primary' clipboard
1007		updatePrimary();
1008	}
1009 
1010	return handled;
1011}
1012
1013void LLLineEditor::deleteSelection()
1014{
1015	if( !mReadOnly && hasSelection() )
1016	{
1017		S32 left_pos, selection_length;
1018		getSelectionRange(&left_pos, &selection_length);
1019		const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
1020
1021		if (!prevalidateInput(selection))
1022			return;
1023
1024		mText.erase(left_pos, selection_length);
1025		deselect();
1026		setCursor(left_pos);
1027	}
1028}
1029
1030BOOL LLLineEditor::canCut() const
1031{
1032	return !mReadOnly && !mDrawAsterixes && hasSelection();
1033}
1034
1035// cut selection to clipboard
1036void LLLineEditor::cut()
1037{
1038	if( canCut() )
1039	{
1040		S32 left_pos, length;
1041		getSelectionRange(&left_pos, &length);
1042		const LLWString& selection = mText.getWString().substr(left_pos, length);
1043
1044		if (!prevalidateInput(selection))
1045			return;
1046
1047		// Prepare for possible rollback
1048		LLLineEditorRollback rollback( this );
1049
1050		gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
1051		deleteSelection();
1052
1053		// Validate new string and rollback the if needed.
1054		BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
1055		if( need_to_rollback )
1056		{
1057			rollback.doRollback( this );
1058			LLUI::reportBadKeystroke();
1059		}
1060		else
1061		if( mKeystrokeCallback )
1062		{
1063			mKeystrokeCallback( this );
1064		}
1065	}
1066}
1067
1068BOOL LLLineEditor::canCopy() const
1069{
1070	return !mDrawAsterixes && hasSelection();
1071}
1072
1073
1074// copy selection to clipboard
1075void LLLineEditor::copy()
1076{
1077	if( canCopy() )
1078	{
1079		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
1080		S32 length = llabs( mSelectionStart - mSelectionEnd );
1081		gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
1082	}
1083}
1084
1085BOOL LLLineEditor::canPaste() const
1086{
1087	return !mReadOnly && gClipboard.canPasteString(); 
1088}
1089
1090void LLLineEditor::paste()
1091{
1092	bool is_primary = false;
1093	pasteHelper(is_primary);
1094}
1095
1096void LLLineEditor::pastePrimary()
1097{
1098	bool is_primary = true;
1099	pasteHelper(is_primary);
1100}
1101
1102// paste from primary (is_primary==true) or clipboard (is_primary==false)
1103void LLLineEditor::pasteHelper(bool is_primary)
1104{
1105	bool can_paste_it;
1106	if (is_primary)
1107	{
1108		can_paste_it = canPastePrimary();
1109	}
1110	else
1111	{
1112		can_paste_it = canPaste();
1113	}
1114
1115	if (can_paste_it)
1116	{
1117		LLWString paste;
1118		if (is_primary)
1119		{
1120			paste = gClipboard.getPastePrimaryWString();
1121		}
1122		else 
1123		{
1124			paste = gClipboard.getPasteWString();
1125		}
1126
1127		if (!paste.empty())
1128		{
1129			if (!prevalidateInput(paste))
1130				return;
1131
1132			// Prepare for possible rollback
1133			LLLineEditorRollback rollback(this);
1134			
1135			// Delete any selected characters
1136			if ((!is_primary) && hasSelection())
1137			{
1138				deleteSelection();
1139			}
1140
1141			// Clean up string (replace tabs and returns and remove characters that our fonts don't support.)
1142			LLWString clean_string(paste);
1143			LLWStringUtil::replaceTabsWithSpaces(clean_string, 1);
1144			//clean_string = wstring_detabify(paste, 1);
1145			LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character
1146
1147			// Insert the string
1148
1149			// Check to see that the size isn't going to be larger than the max number of bytes
1150			U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText);
1151
1152			if ( available_bytes < (U32) wstring_utf8_length(clean_string) )
1153			{	// Doesn't all fit
1154				llwchar current_symbol = clean_string[0];
1155				U32 wchars_that_fit = 0;
1156				U32 total_bytes = wchar_utf8_length(current_symbol);
1157
1158				//loop over the "wide" characters (symbols)
1159				//and check to see how large (in bytes) each symbol is.
1160				while ( total_bytes <= available_bytes )
1161				{
1162					//while we still have available bytes
1163					//"accept" the current symbol and check the size
1164					//of the next one
1165					current_symbol = clean_string[++wchars_that_fit];
1166					total_bytes += wchar_utf8_length(current_symbol);
1167				}
1168				// Truncate the clean string at the limit of what will fit
1169				clean_string = clean_string.substr(0, wchars_that_fit);
1170				LLUI::reportBadKeystroke();
1171			}
1172
1173			if (mMaxLengthChars)
1174			{
1175				U32 available_chars = mMaxLengthChars - mText.getWString().size();
1176		
1177				if (available_chars < clean_string.size())
1178				{
1179					clean_string = clean_string.substr(0, available_chars);
1180				}
1181
1182				LLUI::reportBadKeystroke();
1183			}
1184
1185			mText.insert(getCursor(), clean_string);
1186			setCursor( getCursor() + (S32)clean_string.length() );
1187			deselect();
1188
1189			// Validate new string and rollback the if needed.
1190			BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
1191			if( need_to_rollback )
1192			{
1193				rollback.doRollback( this );
1194				LLUI::reportBadKeystroke();
1195			}
1196			else
1197			if( mKeystrokeCallback )
1198			{
1199				mKeystrokeCallback( this );
1200			}
1201		}
1202	}
1203}
1204
1205// copy selection to primary
1206void LLLineEditor::copyPrimary()
1207{
1208	if( canCopy() )
1209	{
1210		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
1211		S32 length = llabs( mSelectionStart - mSelectionEnd );
1212		gClipboard.copyFromPrimarySubstring( mText.getWString(), left_pos, length );
1213	}
1214}
1215
1216BOOL LLLineEditor::canPastePrimary() const
1217{
1218	return !mReadOnly && gClipboard.canPastePrimaryString(); 
1219}
1220
1221void LLLineEditor::updatePrimary()
1222{
1223	if(canCopy() )
1224	{
1225		copyPrimary();
1226	}
1227}
1228
1229BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)	
1230{
1231	BOOL handled = FALSE;
1232
1233	switch( key )
1234	{
1235	case KEY_INSERT:
1236		if (mask == MASK_NONE)
1237		{
1238			gKeyboard->toggleInsertMode();
1239		}
1240
1241		handled = TRUE;
1242		break;
1243
1244	case KEY_BACKSPACE:
1245		if (!mReadOnly)
1246		{
1247			//llinfos << "Handling backspace" << llendl;
1248			if( hasSelection() )
1249			{
1250				deleteSelection();
1251			}
1252			else
1253			if( 0 < getCursor() )
1254			{
1255				removeChar();
1256			}
1257			else
1258			{
1259				LLUI::reportBadKeystroke();
1260			}
1261		}
1262		handled = TRUE;
1263		break;
1264
1265	case KEY_PAGE_UP:
1266	case KEY_HOME:
1267		if (!mIgnoreArrowKeys)
1268		{
1269			setCursor(0);
1270			handled = TRUE;
1271		}
1272		break;
1273
1274	case KEY_PAGE_DOWN:
1275	case KEY_END:
1276		if (!mIgnoreArrowKeys)
1277		{
1278			S32 len = mText.length();
1279			if( len )
1280			{
1281				setCursor(len);
1282			}
1283			handled = TRUE;
1284		}
1285		break;
1286
1287	case KEY_LEFT:
1288		if (mIgnoreArrowKeys && mask == MASK_NONE)
1289			break;
1290		if ((mask & MASK_ALT) == 0)
1291		{
1292			if( hasSelection() )
1293			{
1294				setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd ));
1295			}
1296			else
1297			if( 0 < getCursor() )
1298			{
1299				S32 cursorPos = getCursor() - 1;
1300				if( mask & MASK_CONTROL )
1301				{
1302					cursorPos = prevWordPos(cursorPos);
1303				}
1304				setCursor(cursorPos);
1305			}
1306			else
1307			{
1308				LLUI::reportBadKeystroke();
1309			}
1310			handled = TRUE;
1311		}
1312		break;
1313
1314	case KEY_RIGHT:
1315		if (mIgnoreArrowKeys && mask == MASK_NONE)
1316			break;
1317		if ((mask & MASK_ALT) == 0)
1318		{
1319			if (hasSelection())
1320			{
1321				setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd));
1322			}
1323			else
1324			if (getCursor() < mText.length())
1325			{
1326				S32 cursorPos = getCursor() + 1;
1327				if( mask & MASK_CONTROL )
1328				{
1329					cursorPos = nextWordPos(cursorPos);
1330				}
1331				setCursor(cursorPos);
1332			}
1333			else
1334			{
1335				LLUI::reportBadKeystroke();
1336			}
1337			handled = TRUE;
1338		}
1339		break;
1340
1341	// handle ctrl-uparrow if we have a history enabled line editor.
1342	case KEY_UP:
1343		if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) )
1344		{
1345			if( mCurrentHistoryLine > mLineHistory.begin() )
1346			{
1347				mText.assign( *(--mCurrentHistoryLine) );
1348				setCursorToEnd();
1349			}
1350			else
1351			{
1352				LLUI::reportBadKeystroke();
1353			}
1354			handled = TRUE;
1355		}
1356		break;
1357
1358	// handle [ctrl]-downarrow if we have a history enabled line editor
1359	case KEY_DOWN:
1360		if( mHaveHistory  && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) )
1361		{
1362			if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 )
1363			{
1364				mText.assign( *(++mCurrentHistoryLine) );
1365				setCursorToEnd();
1366			}
1367			else
1368			{
1369				LLUI::reportBadKeystroke();
1370			}
1371			handled = TRUE;
1372		}
1373		break;
1374
1375	case KEY_RETURN:
1376		// store sent line in history
1377		updateHistory();
1378		break;
1379
1380	case KEY_ESCAPE:
1381	    if (mRevertOnEsc && mText.getString() != mPrevText)
1382		{
1383			setText(mPrevText);
1384			// Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged)
1385		}
1386		break;
1387		
1388	default:
1389		break;
1390	}
1391
1392	return handled;
1393}
1394
1395
1396BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
1397{
1398	BOOL	handled = FALSE;
1399	BOOL	selection_modified = FALSE;
1400
1401	if ( gFocusMgr.getKeyboardFocus() == this )
1402	{
1403		LLLineEditorRollback rollback( this );
1404
1405		if( !handled )
1406		{
1407			handled = handleSelectionKey( key, mask );
1408			selection_modified = handled;
1409		}
1410		
1411		// Handle most keys only if the text editor is writeable.
1412		if ( !mReadOnly )
1413		{
1414			if( !handled )
1415			{
1416				handled = handleSpecialKey( key, mask );
1417			}
1418		}
1419
1420		if( handled )
1421		{
1422			mKeystrokeTimer.reset();
1423
1424			// Most keystrokes will make the selection box go away, but not all will.
1425			if( !selection_modified &&
1426				KEY_SHIFT != key &&
1427				KEY_CONTROL != key &&
1428				KEY_ALT != key &&
1429				KEY_CAPSLOCK )
1430			{
1431				deselect();
1432			}
1433
1434			BOOL need_to_rollback = FALSE;
1435
1436			// If read-only, don't allow changes
1437			need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
1438
1439			// Validate new string and rollback the keystroke if needed.
1440			need_to_rollback |= (mPrevalidateFunc && !mPrevalidateFunc(mText.getWString()));
1441
1442			if (need_to_rollback)
1443			{
1444				rollback.doRollback(this);
1445
1446				LLUI::reportBadKeystroke();
1447			}
1448
1449			// Notify owner if requested
1450			if (!need_to_rollback && handled)
1451			{
1452				if (mKeystrokeCallback)
1453				{
1454					mKeystrokeCallback(this);
1455				}
1456			}
1457		}
1458	}
1459
1460	return handled;
1461}
1462
1463
1464BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
1465{
1466	if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
1467	{
1468		return FALSE;
1469	}
1470
1471	BOOL	handled = FALSE;
1472
1473	if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly)
1474	{
1475		handled = TRUE;
1476
1477		LLLineEditorRollback rollback( this );
1478
1479		{
1480			LLWString u_char;
1481			u_char.assign(1, uni_char);
1482			if (!prevalidateInput(u_char))
1483				return handled;
1484		}
1485
1486		addChar(uni_char);
1487
1488		mKeystrokeTimer.reset();
1489
1490		deselect();
1491
1492		BOOL need_to_rollback = FALSE;
1493
1494		// Validate new string and rollback the keystroke if needed.
1495		need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
1496
1497		if( need_to_rollback )
1498		{
1499			rollback.doRollback( this );
1500
1501			LLUI::reportBadKeystroke();
1502		}
1503
1504		// Notify owner if requested
1505		if( !need_to_rollback && handled )
1506		{
1507			if( mKeystrokeCallback )
1508			{
1509				// HACK! The only usage of this callback doesn't do anything with the character.
1510				// We'll have to do something about this if something ever changes! - Doug
1511				mKeystrokeCallback( this );
1512			}
1513		}
1514	}
1515	return handled;
1516}
1517
1518
1519BOOL LLLineEditor::canDoDelete() const
1520{
1521	return ( !mReadOnly && mText.length() > 0 && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
1522}
1523
1524void LLLineEditor::doDelete()
1525{
1526	if (canDoDelete())
1527	{
1528		// Prepare for possible rollback
1529		LLLineEditorRollback rollback( this );
1530
1531		if (hasSelection())
1532		{
1533			deleteSelection();
1534		}
1535		else if ( getCursor() < mText.length())
1536		{	
1537			const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1);
1538
1539			if (!prevalidateInput(text_to_delete))
1540			{
1541				if( mKeystrokeCallback )
1542					mKeystrokeCallback( this );
1543
1544				return;
1545			}
1546			setCursor(getCursor() + 1);
1547			removeChar();
1548		}
1549
1550		// Validate new string and rollback the if needed.
1551		BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
1552		if( need_to_rollback )
1553		{
1554			rollback.doRollback( this );
1555			LLUI::reportBadKeystroke();
1556		}
1557		else
1558		{
1559			if( mKeystrokeCallback )
1560			{
1561				mKeystrokeCallback( this );
1562			}
1563		}
1564	}
1565}
1566
1567
1568void LLLineEditor::drawBackground()
1569{
1570	bool has_focus = hasFocus();
1571	LLUIImage* image;
1572	if ( mReadOnly )
1573	{
1574		image = mBgImageDisabled;
1575	}
1576	else if ( has_focus )
1577	{
1578		image = mBgImageFocused;
1579	}
1580	else
1581	{
1582		image = mBgImage;
1583	}
1584
1585	if (!image) return;
1586	
1587	F32 alpha = getCurrentTransparency();
1588
1589	// optionally draw programmatic border
1590	if (has_focus)
1591	{
1592		LLColor4 tmp_color = gFocusMgr.getFocusColor();
1593		tmp_color.setAlpha(alpha);
1594		image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
1595						  tmp_color,
1596						  gFocusMgr.getFocusFlashWidth());
1597	}
1598	LLColor4 tmp_color = UI_VERTEX_COLOR;
1599	tmp_color.setAlpha(alpha);
1600	image->draw(getLocalRect(), tmp_color);
1601}
1602
1603void LLLineEditor::draw()
1604{
1605	F32 alpha = getDrawContext().mAlpha;
1606	S32 text_len = mText.length();
1607	static LLUICachedControl<S32> lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0);
1608	static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
1609	static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
1610	static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
1611	static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
1612	static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
1613	static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
1614	static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
1615	static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
1616
1617	std::string saved_text;
1618	if (mDrawAsterixes)
1619	{
1620		saved_text = mText.getString();
1621		std::string text;
1622		for (S32 i = 0; i < mText.length(); i++)
1623		{
1624			text += PASSWORD_ASTERISK;
1625		}
1626		mText = text;
1627	}
1628
1629	// draw rectangle for the background
1630	LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
1631	background.stretch( -mBorderThickness );
1632
1633	S32 lineeditor_v_pad = llround((background.getHeight() - mGLFont->getLineHeight())/2);
1634
1635	drawBackground();
1636	
1637	// draw text
1638
1639	// With viewer-2 art files, input region is 2 pixels up
1640	S32 cursor_bottom = background.mBottom + 2;
1641	S32 cursor_top = background.mTop - 1;
1642
1643	LLColor4 text_color;
1644	if (!mReadOnly)
1645	{
1646		if (!getTentative())
1647		{
1648			text_color = mFgColor.get();
1649		}
1650		else
1651		{
1652			text_color = mTentativeFgColor.get();
1653		}
1654	}
1655	else
1656	{
1657		text_color = mReadOnlyFgColor.get();
1658	}
1659	text_color.setAlpha(alpha);
1660	LLColor4 label_color = mTentativeFgColor.get();
1661	label_color.setAlpha(alpha);
1662	
1663	if (hasPreeditString())
1664	{
1665		// Draw preedit markers.  This needs to be before drawing letters.
1666		for (U32 i = 0; i < mPreeditStandouts.size(); i++)
1667		{
1668			const S32 preedit_left = mPreeditPositions[i];
1669			const S32 preedit_right = mPreeditPositions[i + 1];
1670			if (preedit_right > mScrollHPos)
1671			{
1672				S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor());
1673				S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight);
1674				if (preedit_pixels_left >= background.mRight)
1675				{
1676					break;
1677				}
1678				if (mPreeditStandouts[i])
1679				{
1680					gl_rect_2d(preedit_pixels_left + preedit_standout_gap,
1681						background.mBottom + preedit_standout_position,
1682						preedit_pixels_right - preedit_standout_gap - 1,
1683						background.mBottom + preedit_standout_position - preedit_standout_thickness,
1684						(text_color * preedit_standout_brightness 
1685						 + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/));
1686				}
1687				else
1688				{
1689					gl_rect_2d(preedit_pixels_left + preedit_marker_gap,
1690						background.mBottom + preedit_marker_position,
1691						preedit_pixels_right - preedit_marker_gap - 1,
1692						background.mBottom + preedit_marker_position - preedit_marker_thickness,
1693						(text_color * preedit_marker_brightness
1694						 + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/));
1695				}
1696			}
1697		}
1698	}
1699
1700	S32 rendered_text = 0;
1701	F32 rendered_pixels_right = (F32)mTextLeftEdge;
1702	F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad;
1703
1704	if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
1705	{
1706		S32 select_left;
1707		S32 select_right;
1708		if( mSelectionStart < getCursor() )
1709		{
1710			select_left = mSelectionStart;
1711			select_right = getCursor();
1712		}
1713		else
1714		{
1715			select_left = getCursor();
1716			select_right = mSelectionStart;
1717		}
1718		
1719		if( select_left > mScrollHPos )
1720		{
1721			// unselected, left side
1722			rendered_text = mGLFont->render( 
1723				mText, mScrollHPos,
1724				rendered_pixels_right, text_bottom,
1725				text_color,
1726				LLFontGL::LEFT, LLFontGL::BOTTOM,
1727				0,
1728				LLFontGL::NO_SHADOW,
1729				select_left - mScrollHPos,
1730				mTextRightEdge - llround(rendered_pixels_right),
1731				&rendered_pixels_right);
1732		}
1733		
1734		if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
1735		{
1736			LLColor4 color = mHighlightColor;
1737			color.setAlpha(alpha);
1738			// selected middle
1739			S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
1740			width = llmin(width, mTextRightEdge - llround(rendered_pixels_right));
1741			gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color);
1742
1743			LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
1744			rendered_text += mGLFont->render( 
1745				mText, mScrollHPos + rendered_text,
1746				rendered_pixels_right, text_bottom,
1747				tmp_color,
1748				LLFontGL::LEFT, LLFontGL::BOTTOM,
1749				0,
1750				LLFontGL::NO_SHADOW,
1751				select_right - mScrollHPos - rendered_text,
1752				mTextRightEdge - llround(rendered_pixels_right),
1753				&rendered_pixels_right);
1754		}
1755
1756		if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
1757		{
1758			// unselected, right side
1759			mGLFont->render( 
1760				mText, mScrollHPos + rendered_text,
1761				rendered_pixels_right, text_bottom,
1762				text_color,
1763				LLFontGL::LEFT, LLFontGL::BOTTOM,
1764				0,
1765				LLFontGL::NO_SHADOW,
1766				S32_MAX,
1767				mTextRightEdge - llround(rendered_pixels_right),
1768				&rendered_pixels_right);
1769		}
1770	}
1771	else
1772	{
1773		mGLFont->render( 
1774			mText, mScrollHPos, 
1775			rendered_pixels_right, text_bottom,
1776			text_color,
1777			LLFontGL::LEFT, LLFontGL::BOTTOM,
1778			0,
1779			LLFontGL::NO_SHADOW,
1780			S32_MAX,
1781			mTextRightEdge - llround(rendered_pixels_right),
1782			&rendered_pixels_right);
1783	}
1784#if 1 // for when we're ready for image art.
1785	mBorder->setVisible(FALSE); // no more programmatic art.
1786#endif
1787
1788	// If we're editing...
1789	if( hasFocus())
1790	{
1791		//mBorder->setVisible(TRUE); // ok, programmer art just this once.
1792		// (Flash the cursor every half second)
1793		if (!mReadOnly && gFocusMgr.getAppHasFocus())
1794		{
1795			F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
1796			if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
1797			{
1798				S32 cursor_left = findPixelNearestPos();
1799				cursor_left -= lineeditor_cursor_thickness / 2;
1800				S32 cursor_right = cursor_left + lineeditor_cursor_thickness;
1801				if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
1802				{
1803					const LLWString space(utf8str_to_wstring(std::string(" ")));
1804					S32 wswidth = mGLFont->getWidth(space.c_str());
1805					S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
1806					cursor_right = cursor_left + llmax(wswidth, width);
1807				}
1808				// Use same color as text for the Cursor
1809				gl_rect_2d(cursor_left, cursor_top,
1810					cursor_right, cursor_bottom, text_color);
1811				if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
1812				{
1813					LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
1814					mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom, 
1815						tmp_color,
1816						LLFontGL::LEFT, LLFontGL::BOTTOM,
1817						0,
1818						LLFontGL::NO_SHADOW,
1819						1);
1820				}
1821
1822				// Make sure the IME is in the right place
1823				S32 pixels_after_scroll = findPixelNearestPos();	// RCalculcate for IME position
1824				LLRect screen_pos = calcScreenRect();
1825				LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad );
1826
1827				ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
1828				ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
1829				getWindow()->setLanguageTextInput( ime_pos );
1830			}
1831		}
1832
1833		//draw label if no text is provided
1834		//but we should draw it in a different color
1835		//to give indication that it is not text you typed in
1836		if (0 == mText.length() && mReadOnly)
1837		{
1838			mGLFont->render(mLabel.getWString(), 0,
1839							mTextLeftEdge, (F32)text_bottom,
1840							label_color,
1841							LLFontGL::LEFT,
1842							LLFontGL::BOTTOM,
1843							0,
1844							LLFontGL::NO_SHADOW,
1845							S32_MAX,
1846							mTextRightEdge - llround(rendered_pixels_right),
1847							&rendered_pixels_right, FALSE);
1848		}
1849
1850
1851		// Draw children (border)
1852		//mBorder->setVisible(TRUE);
1853		mBorder->setKeyboardFocusHighlight( TRUE );
1854		LLView::draw();
1855		mBorder->setKeyboardFocusHighlight( FALSE );
1856		//mBorder->setVisible(FALSE);
1857	}
1858	else // does not have keyboard input
1859	{
1860		// draw label if no text provided
1861		if (0 == mText.length())
1862		{
1863			mGLFont->render(mLabel.getWString(), 0,
1864							mTextLeftEdge, (F32)text_bottom,
1865							label_color,
1866							LLFontGL::LEFT,
1867							LLFontGL::BOTTOM,
1868							0,
1869							LLFontGL::NO_SHADOW,
1870							S32_MAX,
1871							mTextRightEdge - llround(rendered_pixels_right),
1872							&rendered_pixels_right, FALSE);
1873		}
1874		// Draw children (border)
1875		LLView::draw();
1876	}
1877
1878	if (mDrawAsterixes)
1879	{
1880		mText = saved_text;
1881	}
1882}
1883
1884
1885// Returns the local screen space X coordinate associated with the text cursor position.
1886S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
1887{
1888	S32 dpos = getCursor() - mScrollHPos + cursor_offset;
1889	S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge;
1890	return result;
1891}
1892
1893S32 LLLineEditor::calcCursorPos(S32 mouse_x)
1894{
1895	const llwchar* wtext = mText.getWString().c_str();
1896	LLWString asterix_text;
1897	if (mDrawAsterixes)
1898	{
1899		for (S32 i = 0; i < mText.length(); i++)
1900		{
1901			asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
1902		}
1903		wtext = asterix_text.c_str();
1904	}
1905
1906	S32 cur_pos = mScrollHPos +
1907			mGLFont->charFromPixelOffset(
1908				wtext, mScrollHPos,
1909				(F32)(mouse_x - mTextLeftEdge),
1910				(F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
1911
1912	return cur_pos;
1913}
1914//virtual
1915void LLLineEditor::clear()
1916{
1917	mText.clear();
1918	setCursor(0);
1919}
1920
1921//virtual
1922void LLLineEditor::onTabInto()
1923{
1924	selectAll();
1925}
1926
1927//virtual
1928BOOL LLLineEditor::acceptsTextInput() const
1929{
1930	return TRUE;
1931}
1932
1933// Start or stop the editor from accepting text-editing keystrokes
1934void LLLineEditor::setFocus( BOOL new_state )
1935{
1936	BOOL old_state = hasFocus();
1937
1938	if (!new_state)
1939	{
1940		getWindow()->allowLanguageTextInput(this, FALSE);
1941	}
1942
1943
1944	// getting focus when we didn't have it before, and we want to select all
1945	if (!old_state && new_state && mSelectAllonFocusReceived)
1946	{
1947		selectAll();
1948		// We don't want handleMouseUp() to "finish" the selection (and thereby
1949		// set mSelectionEnd to where the mouse is), so we finish the selection 
1950		// here.
1951		mIsSelecting = FALSE;
1952	}
1953
1954	if( new_state )
1955	{
1956		gEditMenuHandler = this;
1957
1958		// Don't start the cursor flashing right away
1959		mKeystrokeTimer.reset();
1960	}
1961	else
1962	{
1963		// Not really needed, since loss of keyboard focus should take care of this,
1964		// but limited paranoia is ok.
1965		if( gEditMenuHandler == this )
1966		{
1967			gEditMenuHandler = NULL;
1968		}
1969
1970		endSelection();
1971	}
1972
1973	LLUICtrl::setFocus( new_state );
1974
1975	if (new_state)
1976	{
1977		// Allow Language Text Input only when this LineEditor has
1978		// no prevalidate function attached.  This criterion works
1979		// fine on 1.15.0.2, since all prevalidate func reject any
1980		// non-ASCII characters.  I'm not sure on future versions,
1981		// however.
1982		getWindow()->allowLanguageTextInput(this, mPrevalidateFunc == NULL);
1983	}
1984}
1985
1986//virtual 
1987void LLLineEditor::setRect(const LLRect& rect)
1988{
1989	LLUICtrl::setRect(rect);
1990	if (mBorder)
1991	{
1992		LLRect border_rect = mBorder->getRect();
1993		// Scalable UI somehow made these rectangles off-by-one.
1994		// I don't know why. JC
1995		border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom, 
1996				rect.getWidth()-1, rect.getHeight()-1);
1997		mBorder->setRect(border_rect);
1998	}
1999}
2000
2001void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
2002{
2003	mPrevalidateFunc = func;
2004	updateAllowingLanguageInput();
2005}
2006
2007void LLLineEditor::setPrevalidateInput(LLTextValidate::validate_func_t func)
2008{
2009	mPrevalidateInputFunc = func;
2010	updateAllowingLanguageInput();
2011}
2012
2013bool LLLineEditor::prevalidateInput(const LLWString& wstr)
2014{
2015	if (mPrevalidateInputFunc && !mPrevalidateInputFunc(wstr))
2016	{
2017		return false;
2018	}
2019
2020	return true;
2021}
2022
2023// static
2024BOOL LLLineEditor::postvalidateFloat(const std::string &str)
2025{
2026	LLLocale locale(LLLocale::USER_LOCALE);
2027
2028	BOOL success = TRUE;
2029	BOOL has_decimal = FALSE;
2030	BOOL has_digit = FALSE;
2031
2032	LLWString trimmed = utf8str_to_wstring(str);
2033	LLWStringUtil::trim(trimmed);
2034	S32 len = trimmed.length();
2035	if( 0 < len )
2036	{
2037		S32 i = 0;
2038
2039		// First character can be a negative sign
2040		if( '-' == trimmed[0] )
2041		{
2042			i++;
2043		}
2044
2045		// May be a comma or period, depending on the locale
2046		llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
2047
2048		for( ; i < len; i++ )
2049		{
2050			if( decimal_point == trimmed[i] )
2051			{
2052				if( has_decimal )
2053				{
2054					// can't have two
2055					success = FALSE;
2056					break;
2057				}
2058				else
2059				{
2060					has_decimal = TRUE;
2061				}
2062			}
2063			else
2064			if( LLStringOps::isDigit( trimmed[i] ) )
2065			{
2066				has_digit = TRUE;
2067			}
2068			else
2069			{
2070				success = FALSE;
2071				break;
2072			}
2073		}
2074	}		
2075
2076	// Gotta have at least one
2077	success = has_digit;
2078
2079	return success;
2080}
2081
2082BOOL LLLineEditor::evaluateFloat()
2083{
2084	bool success;
2085	F32 result = 0.f;
2086	std::string expr = getText();
2087	LLStringUtil::toUpper(expr);
2088
2089	success = LLCalc::getInstance()->evalString(expr, result);
2090
2091	if (!success)
2092	{
2093		// Move the cursor to near the error on failure
2094		setCursor(LLCalc::getInstance()->getLastErrorPos());
2095		// *TODO: Translated error message indicating the type of error? Select error text?
2096	}
2097	else
2098	{
2099		// Replace the expression with the result
2100		std::string result_str = llformat("%f",result);
2101		setText(result_str);
2102		selectAll();
2103	}
2104
2105	return success;
2106}
2107
2108void LLLineEditor::onMouseCaptureLost()
2109{
2110	endSelection();
2111}
2112
2113
2114void LLLineEditor::setSelectAllonFocusReceived(BOOL b)
2115{
2116	mSelectAllonFocusReceived = b;
2117}
2118
2119
2120void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data)
2121{
2122	mKeystrokeCallback = boost::bind(callback, _1, user_data);
2123}
2124
2125
2126BOOL LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
2127{
2128	mText.setArg(key, text);
2129	return TRUE;
2130}
2131
2132BOOL LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
2133{
2134	mLabel.setArg(key, text);
2135	return TRUE;
2136}
2137
2138
2139void LLLineEditor::updateAllowingLanguageInput()
2140{
2141	// Allow Language Text Input only when this LineEditor has
2142	// no prevalidate function attached (as long as other criteria
2143	// common to LLTextEditor).  This criterion works
2144	// fine on 1.15.0.2, since all prevalidate func reject any
2145	// non-ASCII characters.  I'm not sure on future versions,
2146	// however...
2147	LLWindow* window = getWindow();
2148	if (!window)
2149	{
2150		// test app, no window available
2151		return;	
2152	}
2153	if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL)
2154	{
2155		window->allowLanguageTextInput(this, TRUE);
2156	}
2157	else
2158	{
2159		window->allowLanguageTextInput(this, FALSE);
2160	}
2161}
2162
2163BOOL LLLineEditor::hasPreeditString() const
2164{
2165	return (mPreeditPositions.size() > 1);
2166}
2167
2168void LLLineEditor::resetPreedit()
2169{
2170	if (hasSelection())
2171	{
2172		if (hasPreeditString())
2173		{
2174			llwarns << "Preedit and selection!" << llendl;
2175			deselect();
2176		}
2177		else
2178		{
2179			deleteSelection();
2180		}
2181	}
2182	if (hasPreeditString())
2183	{
2184		const S32 preedit_pos = mPreeditPositions.front();
2185		mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
2186		mText.insert(preedit_pos, mPreeditOverwrittenWString);
2187		setCursor(preedit_pos);
2188		
2189		mPreeditWString.clear();
2190		mPreeditOverwrittenWString.clear();
2191		mPreeditPositions.clear();
2192
2193		// Don't reset key stroke timer nor invoke keystroke callback,
2194		// because a call to updatePreedit should be follow soon in 
2195		// normal course of operation, and timer and callback will be 
2196		// maintained there.  Doing so here made an odd sound.  (VWR-3410) 
2197	}
2198}
2199
2200void LLLineEditor::updatePreedit(const LLWString &preedit_string,
2201		const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
2202{
2203	// Just in case.
2204	if (mReadOnly)
2205	{
2206		return;
2207	}
2208
2209	// Note that call to updatePreedit is always preceeded by resetPreedit,
2210	// so we have no existing selection/preedit.
2211
2212	S32 insert_preedit_at = getCursor();
2213
2214	mPreeditWString = preedit_string;
2215	mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
2216	S32 position = insert_preedit_at;
2217	for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
2218	{
2219		mPreeditPositions[i] = position;
2220		position += preedit_segment_lengths[i];
2221	}
2222	mPreeditPositions.back() = position;
2223	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
2224	{
2225		mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) );
2226		mText.erase(insert_preedit_at, mPreeditWString.length());
2227	}
2228	else
2229	{
2230		mPreeditOverwrittenWString.clear();
2231	}
2232	mText.insert(insert_preedit_at, mPreeditWString);
2233
2234	mPreeditStandouts = preedit_standouts;
2235
2236	setCursor(position);
2237	setCursor(mPreeditPositions.front() + caret_position);
2238
2239	// Update of the preedit should be caused by some key strokes.
2240	mKeystrokeTimer.reset();
2241	if( mKeystrokeCallback )
2242	{
2243		mKeystrokeCallback( this );
2244	}
2245}
2246
2247BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
2248{
2249	if (control)
2250	{
2251		LLRect control_rect_screen;
2252		localRectToScreen(getRect(), &control_rect_screen);
2253		LLUI::screenRectToGL(control_rect_screen, control);
2254	}
2255
2256	S32 preedit_left_column, preedit_right_column;
2257	if (hasPreeditString())
2258	{
2259		preedit_left_column = mPreeditPositions.front();
2260		preedit_right_column = mPreeditPositions.back();
2261	}
2262	else
2263	{
2264		preedit_left_column = preedit_right_column = getCursor();
2265	}
2266	if (preedit_right_column < mScrollHPos)
2267	{
2268		// This should not occure...
2269		return FALSE;
2270	}
2271
2272	const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor());
2273	if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column)
2274	{
2275		return FALSE;
2276	}
2277
2278	if (coord)
2279	{
2280		S32 query_local = findPixelNearestPos(query - getCursor());
2281		S32 query_screen_x, query_screen_y;
2282		localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y);
2283		LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
2284	}
2285
2286	if (bounds)
2287	{
2288		S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor());
2289		S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness);
2290		if (preedit_left_local > preedit_right_local)
2291		{
2292			// Is this condition possible?
2293			preedit_right_local = preedit_left_local;
2294		}
2295
2296		LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0);
2297		LLRect preedit_rect_screen;
2298		localRectToScreen(preedit_rect_local, &preedit_rect_screen);
2299		LLUI::screenRectToGL(preedit_rect_screen, bounds);
2300	}
2301
2302	return TRUE;
2303}
2304
2305void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const
2306{
2307	if (hasPreeditString())
2308	{
2309		*position = mPreeditPositions.front();
2310		*length = mPreeditPositions.back() - mPreeditPositions.front();
2311	}
2312	else
2313	{
2314		*position = mCursorPos;
2315		*length = 0;
2316	}
2317}
2318
2319void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const
2320{
2321	if (hasSelection())
2322	{
2323		*position = llmin(mSelectionStart, mSelectionEnd);
2324		*length = llabs(mSelectionStart - mSelectionEnd);
2325	}
2326	else
2327	{
2328		*position = mCursorPos;
2329		*length = 0;
2330	}
2331}
2332
2333void LLLineEditor::markAsPreedit(S32 position, S32 length)
2334{
2335	deselect();
2336	setCursor(position);
2337	if (hasPreeditString())
2338	{
2339		llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
2340	}
2341	mPreeditWString.assign( LLWString( mText.getWString(), position, length ) );
2342	if (length > 0)
2343	{
2344		mPreeditPositions.resize(2);
2345		mPreeditPositions[0] = position;
2346		mPreeditPositions[1] = position + length;
2347		mPreeditStandouts.resize(1);
2348		mPreeditStandouts[0] = FALSE;
2349	}
2350	else
2351	{
2352		mPreeditPositions.clear();
2353		mPreeditStandouts.clear();
2354	}
2355	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
2356	{
2357		mPreeditOverwrittenWString = mPreeditWString;
2358	}
2359	else
2360	{
2361		mPreeditOverwrittenWString.clear();
2362	}
2363}
2364
2365S32 LLLineEditor::getPreeditFontSize() const
2366{
2367	return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
2368}
2369
2370void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace)
2371{
2372	mReplaceNewlinesWithSpaces = replace;
2373}
2374
2375LLWString LLLineEditor::getConvertedText() const
2376{
2377	LLWString text = getWText();
2378	LLWStringUtil::trim(text);
2379	if (!mReplaceNewlinesWithSpaces)
2380	{
2381		LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
2382	}
2383	return text;
2384}
2385
2386void LLLineEditor::showContextMenu(S32 x, S32 y)
2387{
2388	LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
2389
2390	if (menu)
2391	{
2392		gEditMenuHandler = this;
2393
2394		S32 screen_x, screen_y;
2395		localPointToScreen(x, y, &screen_x, &screen_y);
2396		menu->show(screen_x, screen_y);
2397	}
2398}
2399
2400void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu)
2401{
2402	if (new_context_menu)
2403		mContextMenuHandle = new_context_menu->getHandle();
2404	else
2405		mContextMenuHandle.markDead();
2406}
2407
2408void LLLineEditor::setFont(const LLFontGL* font)
2409{
2410	mGLFont = font;
2411}