PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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