lllineeditor.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:53k
源码类别:

游戏引擎

开发平台:

C++ Builder

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