PageRenderTime 178ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/lltexteditor.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2866 lines | 2229 code | 428 blank | 209 comment | 373 complexity | d8694d653f94433c71ffb5f50ad37a5c MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lltexteditor.cpp
  3. *
  4. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  5. * Second Life Viewer Source Code
  6. * Copyright (C) 2010, Linden Research, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation;
  11. * version 2.1 of the License only.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  23. * $/LicenseInfo$
  24. */
  25. // Text editor widget to let users enter a a multi-line ASCII document.
  26. #include "linden_common.h"
  27. #define LLTEXTEDITOR_CPP
  28. #include "lltexteditor.h"
  29. #include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR
  30. #include "llfontgl.h"
  31. #include "llgl.h" // LLGLSUIDefault()
  32. #include "lllocalcliprect.h"
  33. #include "llrender.h"
  34. #include "llui.h"
  35. #include "lluictrlfactory.h"
  36. #include "llrect.h"
  37. #include "llfocusmgr.h"
  38. #include "lltimer.h"
  39. #include "llmath.h"
  40. #include "llclipboard.h"
  41. #include "llscrollbar.h"
  42. #include "llstl.h"
  43. #include "llstring.h"
  44. #include "llkeyboard.h"
  45. #include "llkeywords.h"
  46. #include "llundo.h"
  47. #include "llviewborder.h"
  48. #include "llcontrol.h"
  49. #include "llwindow.h"
  50. #include "lltextparser.h"
  51. #include "llscrollcontainer.h"
  52. #include "llpanel.h"
  53. #include "llurlregistry.h"
  54. #include "lltooltip.h"
  55. #include "llmenugl.h"
  56. #include <queue>
  57. #include "llcombobox.h"
  58. //
  59. // Globals
  60. //
  61. static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor");
  62. // Compiler optimization, generate extern template
  63. template class LLTextEditor* LLView::getChild<class LLTextEditor>(
  64. const std::string& name, BOOL recurse) const;
  65. //
  66. // Constants
  67. //
  68. const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
  69. const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
  70. const S32 SPACES_PER_TAB = 4;
  71. ///////////////////////////////////////////////////////////////////
  72. class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd
  73. {
  74. public:
  75. TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
  76. : TextCmd(pos, group_with_next, segment), mWString(ws)
  77. {
  78. }
  79. virtual ~TextCmdInsert() {}
  80. virtual BOOL execute( LLTextBase* editor, S32* delta )
  81. {
  82. *delta = insert(editor, getPosition(), mWString );
  83. LLWStringUtil::truncate(mWString, *delta);
  84. //mWString = wstring_truncate(mWString, *delta);
  85. return (*delta != 0);
  86. }
  87. virtual S32 undo( LLTextBase* editor )
  88. {
  89. remove(editor, getPosition(), mWString.length() );
  90. return getPosition();
  91. }
  92. virtual S32 redo( LLTextBase* editor )
  93. {
  94. insert(editor, getPosition(), mWString );
  95. return getPosition() + mWString.length();
  96. }
  97. private:
  98. LLWString mWString;
  99. };
  100. ///////////////////////////////////////////////////////////////////
  101. class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd
  102. {
  103. public:
  104. TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment)
  105. : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)
  106. {
  107. }
  108. virtual void blockExtensions()
  109. {
  110. mBlockExtensions = TRUE;
  111. }
  112. virtual BOOL canExtend(S32 pos) const
  113. {
  114. // cannot extend text with custom segments
  115. if (!mSegments.empty()) return FALSE;
  116. return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
  117. }
  118. virtual BOOL execute( LLTextBase* editor, S32* delta )
  119. {
  120. *delta = insert(editor, getPosition(), mWString);
  121. LLWStringUtil::truncate(mWString, *delta);
  122. //mWString = wstring_truncate(mWString, *delta);
  123. return (*delta != 0);
  124. }
  125. virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )
  126. {
  127. LLWString ws;
  128. ws += wc;
  129. *delta = insert(editor, pos, ws);
  130. if( *delta > 0 )
  131. {
  132. mWString += wc;
  133. }
  134. return (*delta != 0);
  135. }
  136. virtual S32 undo( LLTextBase* editor )
  137. {
  138. remove(editor, getPosition(), mWString.length() );
  139. return getPosition();
  140. }
  141. virtual S32 redo( LLTextBase* editor )
  142. {
  143. insert(editor, getPosition(), mWString );
  144. return getPosition() + mWString.length();
  145. }
  146. private:
  147. LLWString mWString;
  148. BOOL mBlockExtensions;
  149. };
  150. ///////////////////////////////////////////////////////////////////
  151. class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd
  152. {
  153. public:
  154. TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
  155. : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
  156. virtual BOOL execute( LLTextBase* editor, S32* delta )
  157. {
  158. mOldChar = editor->getWText()[getPosition()];
  159. overwrite(editor, getPosition(), mChar);
  160. *delta = 0;
  161. return TRUE;
  162. }
  163. virtual S32 undo( LLTextBase* editor )
  164. {
  165. overwrite(editor, getPosition(), mOldChar);
  166. return getPosition();
  167. }
  168. virtual S32 redo( LLTextBase* editor )
  169. {
  170. overwrite(editor, getPosition(), mChar);
  171. return getPosition()+1;
  172. }
  173. private:
  174. llwchar mChar;
  175. llwchar mOldChar;
  176. };
  177. ///////////////////////////////////////////////////////////////////
  178. class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd
  179. {
  180. public:
  181. TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) :
  182. TextCmd(pos, group_with_next), mLen(len)
  183. {
  184. std::swap(mSegments, segments);
  185. }
  186. virtual BOOL execute( LLTextBase* editor, S32* delta )
  187. {
  188. mWString = editor->getWText().substr(getPosition(), mLen);
  189. *delta = remove(editor, getPosition(), mLen );
  190. return (*delta != 0);
  191. }
  192. virtual S32 undo( LLTextBase* editor )
  193. {
  194. insert(editor, getPosition(), mWString);
  195. return getPosition() + mWString.length();
  196. }
  197. virtual S32 redo( LLTextBase* editor )
  198. {
  199. remove(editor, getPosition(), mLen );
  200. return getPosition();
  201. }
  202. private:
  203. LLWString mWString;
  204. S32 mLen;
  205. };
  206. ///////////////////////////////////////////////////////////////////
  207. LLTextEditor::Params::Params()
  208. : default_text("default_text"),
  209. prevalidate_callback("prevalidate_callback"),
  210. embedded_items("embedded_items", false),
  211. ignore_tab("ignore_tab", true),
  212. show_line_numbers("show_line_numbers", false),
  213. default_color("default_color"),
  214. commit_on_focus_lost("commit_on_focus_lost", false),
  215. show_context_menu("show_context_menu")
  216. {
  217. addSynonym(prevalidate_callback, "text_type");
  218. }
  219. LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
  220. LLTextBase(p),
  221. mBaseDocIsPristine(TRUE),
  222. mPristineCmd( NULL ),
  223. mLastCmd( NULL ),
  224. mDefaultColor( p.default_color() ),
  225. mShowLineNumbers ( p.show_line_numbers ),
  226. mCommitOnFocusLost( p.commit_on_focus_lost),
  227. mAllowEmbeddedItems( p.embedded_items ),
  228. mMouseDownX(0),
  229. mMouseDownY(0),
  230. mTabsToNextField(p.ignore_tab),
  231. mPrevalidateFunc(p.prevalidate_callback()),
  232. mContextMenu(NULL),
  233. mShowContextMenu(p.show_context_menu)
  234. {
  235. mSourceID.generate();
  236. //FIXME: use image?
  237. LLViewBorder::Params params;
  238. params.name = "text ed border";
  239. params.rect = getLocalRect();
  240. params.bevel_style = LLViewBorder::BEVEL_IN;
  241. params.border_thickness = 1;
  242. params.visible = p.border_visible;
  243. mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
  244. addChild( mBorder );
  245. setText(p.default_text());
  246. if (mShowLineNumbers)
  247. {
  248. mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
  249. updateRects();
  250. }
  251. mParseOnTheFly = TRUE;
  252. }
  253. void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
  254. {
  255. LLTextBase::initFromParams(p);
  256. // HACK: text editors always need to be enabled so that we can scroll
  257. LLView::setEnabled(true);
  258. if (p.commit_on_focus_lost.isProvided())
  259. {
  260. mCommitOnFocusLost = p.commit_on_focus_lost;
  261. }
  262. updateAllowingLanguageInput();
  263. }
  264. LLTextEditor::~LLTextEditor()
  265. {
  266. gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
  267. // Scrollbar is deleted by LLView
  268. std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
  269. // context menu is owned by menu holder, not us
  270. //delete mContextMenu;
  271. }
  272. ////////////////////////////////////////////////////////////
  273. // LLTextEditor
  274. // Public methods
  275. void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
  276. {
  277. // validate incoming text if necessary
  278. if (mPrevalidateFunc)
  279. {
  280. LLWString test_text = utf8str_to_wstring(utf8str);
  281. if (!mPrevalidateFunc(test_text))
  282. {
  283. // not valid text, nothing to do
  284. return;
  285. }
  286. }
  287. blockUndo();
  288. deselect();
  289. mParseOnTheFly = FALSE;
  290. LLTextBase::setText(utf8str, input_params);
  291. mParseOnTheFly = TRUE;
  292. resetDirty();
  293. }
  294. void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap)
  295. {
  296. if (search_text_in.empty())
  297. {
  298. return;
  299. }
  300. LLWString text = getWText();
  301. LLWString search_text = utf8str_to_wstring(search_text_in);
  302. if (case_insensitive)
  303. {
  304. LLWStringUtil::toLower(text);
  305. LLWStringUtil::toLower(search_text);
  306. }
  307. if (mIsSelecting)
  308. {
  309. LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
  310. if (selected_text == search_text)
  311. {
  312. // We already have this word selected, we are searching for the next.
  313. setCursorPos(mCursorPos + search_text.size());
  314. }
  315. }
  316. S32 loc = text.find(search_text,mCursorPos);
  317. // If Maybe we wrapped, search again
  318. if (wrap && (-1 == loc))
  319. {
  320. loc = text.find(search_text);
  321. }
  322. // If still -1, then search_text just isn't found.
  323. if (-1 == loc)
  324. {
  325. mIsSelecting = FALSE;
  326. mSelectionEnd = 0;
  327. mSelectionStart = 0;
  328. return;
  329. }
  330. setCursorPos(loc);
  331. mIsSelecting = TRUE;
  332. mSelectionEnd = mCursorPos;
  333. mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
  334. }
  335. BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text,
  336. BOOL case_insensitive, BOOL wrap)
  337. {
  338. BOOL replaced = FALSE;
  339. if (search_text_in.empty())
  340. {
  341. return replaced;
  342. }
  343. LLWString search_text = utf8str_to_wstring(search_text_in);
  344. if (mIsSelecting)
  345. {
  346. LLWString text = getWText();
  347. LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
  348. if (case_insensitive)
  349. {
  350. LLWStringUtil::toLower(selected_text);
  351. LLWStringUtil::toLower(search_text);
  352. }
  353. if (selected_text == search_text)
  354. {
  355. insertText(replace_text);
  356. replaced = TRUE;
  357. }
  358. }
  359. selectNext(search_text_in, case_insensitive, wrap);
  360. return replaced;
  361. }
  362. void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive)
  363. {
  364. startOfDoc();
  365. selectNext(search_text, case_insensitive, FALSE);
  366. BOOL replaced = TRUE;
  367. while ( replaced )
  368. {
  369. replaced = replaceText(search_text,replace_text, case_insensitive, FALSE);
  370. }
  371. }
  372. S32 LLTextEditor::prevWordPos(S32 cursorPos) const
  373. {
  374. LLWString wtext(getWText());
  375. while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
  376. {
  377. cursorPos--;
  378. }
  379. while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
  380. {
  381. cursorPos--;
  382. }
  383. return cursorPos;
  384. }
  385. S32 LLTextEditor::nextWordPos(S32 cursorPos) const
  386. {
  387. LLWString wtext(getWText());
  388. while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
  389. {
  390. cursorPos++;
  391. }
  392. while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
  393. {
  394. cursorPos++;
  395. }
  396. return cursorPos;
  397. }
  398. const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
  399. {
  400. static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment;
  401. index_segment->setStart(mCursorPos);
  402. index_segment->setEnd(mCursorPos);
  403. // find segment index at character to left of cursor (or rightmost edge of selection)
  404. segment_set_t::const_iterator it = mSegments.lower_bound(index_segment);
  405. if (it != mSegments.end())
  406. {
  407. return *it;
  408. }
  409. else
  410. {
  411. return LLTextSegmentPtr();
  412. }
  413. }
  414. void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const
  415. {
  416. S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;
  417. S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos;
  418. return getSegmentsInRange(segments, left, right, true);
  419. }
  420. void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const
  421. {
  422. segment_set_t::const_iterator first_it = getSegIterContaining(start);
  423. segment_set_t::const_iterator end_it = getSegIterContaining(end - 1);
  424. if (end_it != mSegments.end()) ++end_it;
  425. for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)
  426. {
  427. LLTextSegmentPtr segment = *it;
  428. if (include_partial
  429. || (segment->getStart() >= start
  430. && segment->getEnd() <= end))
  431. {
  432. segments_out.push_back(segment);
  433. }
  434. }
  435. }
  436. BOOL LLTextEditor::selectionContainsLineBreaks()
  437. {
  438. if (hasSelection())
  439. {
  440. S32 left = llmin(mSelectionStart, mSelectionEnd);
  441. S32 right = left + llabs(mSelectionStart - mSelectionEnd);
  442. LLWString wtext = getWText();
  443. for( S32 i = left; i < right; i++ )
  444. {
  445. if (wtext[i] == '\n')
  446. {
  447. return TRUE;
  448. }
  449. }
  450. }
  451. return FALSE;
  452. }
  453. S32 LLTextEditor::indentLine( S32 pos, S32 spaces )
  454. {
  455. // Assumes that pos is at the start of the line
  456. // spaces may be positive (indent) or negative (unindent).
  457. // Returns the actual number of characters added or removed.
  458. llassert(pos >= 0);
  459. llassert(pos <= getLength() );
  460. S32 delta_spaces = 0;
  461. if (spaces >= 0)
  462. {
  463. // Indent
  464. for(S32 i=0; i < spaces; i++)
  465. {
  466. delta_spaces += addChar(pos, ' ');
  467. }
  468. }
  469. else
  470. {
  471. // Unindent
  472. for(S32 i=0; i < -spaces; i++)
  473. {
  474. LLWString wtext = getWText();
  475. if (wtext[pos] == ' ')
  476. {
  477. delta_spaces += remove( pos, 1, FALSE );
  478. }
  479. }
  480. }
  481. return delta_spaces;
  482. }
  483. void LLTextEditor::indentSelectedLines( S32 spaces )
  484. {
  485. if( hasSelection() )
  486. {
  487. LLWString text = getWText();
  488. S32 left = llmin( mSelectionStart, mSelectionEnd );
  489. S32 right = left + llabs( mSelectionStart - mSelectionEnd );
  490. BOOL cursor_on_right = (mSelectionEnd > mSelectionStart);
  491. S32 cur = left;
  492. // Expand left to start of line
  493. while( (cur > 0) && (text[cur] != '\n') )
  494. {
  495. cur--;
  496. }
  497. left = cur;
  498. if( cur > 0 )
  499. {
  500. left++;
  501. }
  502. // Expand right to end of line
  503. if( text[right - 1] == '\n' )
  504. {
  505. right--;
  506. }
  507. else
  508. {
  509. while( (text[right] != '\n') && (right <= getLength() ) )
  510. {
  511. right++;
  512. }
  513. }
  514. // Disabling parsing on the fly to avoid updating text segments
  515. // until all indentation commands are executed.
  516. mParseOnTheFly = FALSE;
  517. // Find each start-of-line and indent it
  518. do
  519. {
  520. if( text[cur] == '\n' )
  521. {
  522. cur++;
  523. }
  524. S32 delta_spaces = indentLine( cur, spaces );
  525. if( delta_spaces > 0 )
  526. {
  527. cur += delta_spaces;
  528. }
  529. right += delta_spaces;
  530. text = getWText();
  531. // Find the next new line
  532. while( (cur < right) && (text[cur] != '\n') )
  533. {
  534. cur++;
  535. }
  536. }
  537. while( cur < right );
  538. mParseOnTheFly = TRUE;
  539. if( (right < getLength()) && (text[right] == '\n') )
  540. {
  541. right++;
  542. }
  543. // Set the selection and cursor
  544. if( cursor_on_right )
  545. {
  546. mSelectionStart = left;
  547. mSelectionEnd = right;
  548. }
  549. else
  550. {
  551. mSelectionStart = right;
  552. mSelectionEnd = left;
  553. }
  554. setCursorPos(mSelectionEnd);
  555. }
  556. }
  557. //virtual
  558. BOOL LLTextEditor::canSelectAll() const
  559. {
  560. return TRUE;
  561. }
  562. // virtual
  563. void LLTextEditor::selectAll()
  564. {
  565. mSelectionStart = getLength();
  566. mSelectionEnd = 0;
  567. setCursorPos(mSelectionEnd);
  568. updatePrimary();
  569. }
  570. BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
  571. {
  572. BOOL handled = FALSE;
  573. // set focus first, in case click callbacks want to change it
  574. // RN: do we really need to have a tab stop?
  575. if (hasTabStop())
  576. {
  577. setFocus( TRUE );
  578. }
  579. // Let scrollbar have first dibs
  580. handled = LLTextBase::handleMouseDown(x, y, mask);
  581. if( !handled )
  582. {
  583. if (!(mask & MASK_SHIFT))
  584. {
  585. deselect();
  586. }
  587. BOOL start_select = TRUE;
  588. if( start_select )
  589. {
  590. // If we're not scrolling (handled by child), then we're selecting
  591. if (mask & MASK_SHIFT)
  592. {
  593. S32 old_cursor_pos = mCursorPos;
  594. setCursorAtLocalPos( x, y, true );
  595. if (hasSelection())
  596. {
  597. mSelectionEnd = mCursorPos;
  598. }
  599. else
  600. {
  601. mSelectionStart = old_cursor_pos;
  602. mSelectionEnd = mCursorPos;
  603. }
  604. // assume we're starting a drag select
  605. mIsSelecting = TRUE;
  606. }
  607. else
  608. {
  609. setCursorAtLocalPos( x, y, true );
  610. startSelection();
  611. }
  612. gFocusMgr.setMouseCapture( this );
  613. }
  614. handled = TRUE;
  615. }
  616. // Delay cursor flashing
  617. resetCursorBlink();
  618. return handled;
  619. }
  620. BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
  621. {
  622. if (hasTabStop())
  623. {
  624. setFocus(TRUE);
  625. }
  626. // Prefer editor menu if it has selection. See EXT-6806.
  627. if (hasSelection() || !LLTextBase::handleRightMouseDown(x, y, mask))
  628. {
  629. if(getShowContextMenu())
  630. {
  631. showContextMenu(x, y);
  632. }
  633. }
  634. return TRUE;
  635. }
  636. BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
  637. {
  638. if (hasTabStop())
  639. {
  640. setFocus(TRUE);
  641. }
  642. if (!LLTextBase::handleMouseDown(x, y, mask))
  643. {
  644. if( canPastePrimary() )
  645. {
  646. setCursorAtLocalPos( x, y, true );
  647. // does not rely on focus being set
  648. pastePrimary();
  649. }
  650. }
  651. return TRUE;
  652. }
  653. BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
  654. {
  655. BOOL handled = FALSE;
  656. if(hasMouseCapture() )
  657. {
  658. if( mIsSelecting )
  659. {
  660. if(mScroller)
  661. {
  662. mScroller->autoScroll(x, y);
  663. }
  664. S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
  665. S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
  666. setCursorAtLocalPos( clamped_x, clamped_y, true );
  667. mSelectionEnd = mCursorPos;
  668. }
  669. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
  670. getWindow()->setCursor(UI_CURSOR_IBEAM);
  671. handled = TRUE;
  672. }
  673. if( !handled )
  674. {
  675. // Pass to children
  676. handled = LLTextBase::handleHover(x, y, mask);
  677. }
  678. if( handled )
  679. {
  680. // Delay cursor flashing
  681. resetCursorBlink();
  682. }
  683. if( !handled )
  684. {
  685. getWindow()->setCursor(UI_CURSOR_IBEAM);
  686. handled = TRUE;
  687. }
  688. return handled;
  689. }
  690. BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
  691. {
  692. BOOL handled = FALSE;
  693. // if I'm not currently selecting text
  694. if (!(hasSelection() && hasMouseCapture()))
  695. {
  696. // let text segments handle mouse event
  697. handled = LLTextBase::handleMouseUp(x, y, mask);
  698. }
  699. if( !handled )
  700. {
  701. if( mIsSelecting )
  702. {
  703. if(mScroller)
  704. {
  705. mScroller->autoScroll(x, y);
  706. }
  707. S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
  708. S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
  709. setCursorAtLocalPos( clamped_x, clamped_y, true );
  710. endSelection();
  711. }
  712. // take selection to 'primary' clipboard
  713. updatePrimary();
  714. handled = TRUE;
  715. }
  716. // Delay cursor flashing
  717. resetCursorBlink();
  718. if( hasMouseCapture() )
  719. {
  720. gFocusMgr.setMouseCapture( NULL );
  721. handled = TRUE;
  722. }
  723. return handled;
  724. }
  725. BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
  726. {
  727. BOOL handled = FALSE;
  728. // let scrollbar and text segments have first dibs
  729. handled = LLTextBase::handleDoubleClick(x, y, mask);
  730. if( !handled )
  731. {
  732. setCursorAtLocalPos( x, y, false );
  733. deselect();
  734. LLWString text = getWText();
  735. if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
  736. {
  737. // Select word the cursor is over
  738. while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))
  739. {
  740. if (!setCursorPos(mCursorPos - 1)) break;
  741. }
  742. startSelection();
  743. while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
  744. {
  745. if (!setCursorPos(mCursorPos + 1)) break;
  746. }
  747. mSelectionEnd = mCursorPos;
  748. }
  749. else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
  750. {
  751. // Select the character the cursor is over
  752. startSelection();
  753. setCursorPos(mCursorPos + 1);
  754. mSelectionEnd = mCursorPos;
  755. }
  756. // We don't want handleMouseUp() to "finish" the selection (and thereby
  757. // set mSelectionEnd to where the mouse is), so we finish the selection here.
  758. mIsSelecting = FALSE;
  759. // delay cursor flashing
  760. resetCursorBlink();
  761. // take selection to 'primary' clipboard
  762. updatePrimary();
  763. handled = TRUE;
  764. }
  765. return handled;
  766. }
  767. //----------------------------------------------------------------------------
  768. // Returns change in number of characters in mText
  769. S32 LLTextEditor::execute( TextCmd* cmd )
  770. {
  771. S32 delta = 0;
  772. if( cmd->execute(this, &delta) )
  773. {
  774. // Delete top of undo stack
  775. undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
  776. std::for_each(mUndoStack.begin(), enditer, DeletePointer());
  777. mUndoStack.erase(mUndoStack.begin(), enditer);
  778. // Push the new command is now on the top (front) of the undo stack.
  779. mUndoStack.push_front(cmd);
  780. mLastCmd = cmd;
  781. bool need_to_rollback = mPrevalidateFunc
  782. && !mPrevalidateFunc(getViewModel()->getDisplay());
  783. if (need_to_rollback)
  784. {
  785. // get rid of this last command and clean up undo stack
  786. undo();
  787. // remove any evidence of this command from redo history
  788. mUndoStack.pop_front();
  789. delete cmd;
  790. // failure, nothing changed
  791. delta = 0;
  792. }
  793. }
  794. else
  795. {
  796. // Operation failed, so don't put it on the undo stack.
  797. delete cmd;
  798. }
  799. return delta;
  800. }
  801. S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
  802. {
  803. return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );
  804. }
  805. S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
  806. {
  807. S32 end_pos = getEditableIndex(pos + length, true);
  808. segment_vec_t segments_to_remove;
  809. // store text segments
  810. getSegmentsInRange(segments_to_remove, pos, pos + length, false);
  811. return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
  812. }
  813. S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
  814. {
  815. if ((S32)getLength() == pos)
  816. {
  817. return addChar(pos, wc);
  818. }
  819. else
  820. {
  821. return execute(new TextCmdOverwriteChar(pos, FALSE, wc));
  822. }
  823. }
  824. // Remove a single character from the text. Tries to remove
  825. // a pseudo-tab (up to for spaces in a row)
  826. void LLTextEditor::removeCharOrTab()
  827. {
  828. if( !getEnabled() )
  829. {
  830. return;
  831. }
  832. if( mCursorPos > 0 )
  833. {
  834. S32 chars_to_remove = 1;
  835. LLWString text = getWText();
  836. if (text[mCursorPos - 1] == ' ')
  837. {
  838. // Try to remove a "tab"
  839. S32 offset = getLineOffsetFromDocIndex(mCursorPos);
  840. if (offset > 0)
  841. {
  842. chars_to_remove = offset % SPACES_PER_TAB;
  843. if( chars_to_remove == 0 )
  844. {
  845. chars_to_remove = SPACES_PER_TAB;
  846. }
  847. for( S32 i = 0; i < chars_to_remove; i++ )
  848. {
  849. if (text[ mCursorPos - i - 1] != ' ')
  850. {
  851. // Fewer than a full tab's worth of spaces, so
  852. // just delete a single character.
  853. chars_to_remove = 1;
  854. break;
  855. }
  856. }
  857. }
  858. }
  859. for (S32 i = 0; i < chars_to_remove; i++)
  860. {
  861. setCursorPos(mCursorPos - 1);
  862. remove( mCursorPos, 1, FALSE );
  863. }
  864. }
  865. else
  866. {
  867. LLUI::reportBadKeystroke();
  868. }
  869. }
  870. // Remove a single character from the text
  871. S32 LLTextEditor::removeChar(S32 pos)
  872. {
  873. return remove( pos, 1, FALSE );
  874. }
  875. void LLTextEditor::removeChar()
  876. {
  877. if (!getEnabled())
  878. {
  879. return;
  880. }
  881. if (mCursorPos > 0)
  882. {
  883. setCursorPos(mCursorPos - 1);
  884. removeChar(mCursorPos);
  885. }
  886. else
  887. {
  888. LLUI::reportBadKeystroke();
  889. }
  890. }
  891. // Add a single character to the text
  892. S32 LLTextEditor::addChar(S32 pos, llwchar wc)
  893. {
  894. if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc )) > mMaxTextByteLength)
  895. {
  896. make_ui_sound("UISndBadKeystroke");
  897. return 0;
  898. }
  899. if (mLastCmd && mLastCmd->canExtend(pos))
  900. {
  901. S32 delta = 0;
  902. if (mPrevalidateFunc)
  903. {
  904. // get a copy of current text contents
  905. LLWString test_string(getViewModel()->getDisplay());
  906. // modify text contents as if this addChar succeeded
  907. llassert(pos <= (S32)test_string.size());
  908. test_string.insert(pos, 1, wc);
  909. if (!mPrevalidateFunc( test_string))
  910. {
  911. return 0;
  912. }
  913. }
  914. mLastCmd->extendAndExecute(this, pos, wc, &delta);
  915. return delta;
  916. }
  917. else
  918. {
  919. return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
  920. }
  921. }
  922. void LLTextEditor::addChar(llwchar wc)
  923. {
  924. if( !getEnabled() )
  925. {
  926. return;
  927. }
  928. if( hasSelection() )
  929. {
  930. deleteSelection(TRUE);
  931. }
  932. else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
  933. {
  934. removeChar(mCursorPos);
  935. }
  936. setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
  937. }
  938. void LLTextEditor::addLineBreakChar()
  939. {
  940. if( !getEnabled() )
  941. {
  942. return;
  943. }
  944. if( hasSelection() )
  945. {
  946. deleteSelection(TRUE);
  947. }
  948. else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
  949. {
  950. removeChar(mCursorPos);
  951. }
  952. LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
  953. LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
  954. S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment));
  955. setCursorPos(mCursorPos + pos);
  956. }
  957. BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
  958. {
  959. BOOL handled = FALSE;
  960. if( mask & MASK_SHIFT )
  961. {
  962. handled = TRUE;
  963. switch( key )
  964. {
  965. case KEY_LEFT:
  966. if( 0 < mCursorPos )
  967. {
  968. startSelection();
  969. setCursorPos(mCursorPos - 1);
  970. if( mask & MASK_CONTROL )
  971. {
  972. setCursorPos(prevWordPos(mCursorPos));
  973. }
  974. mSelectionEnd = mCursorPos;
  975. }
  976. break;
  977. case KEY_RIGHT:
  978. if( mCursorPos < getLength() )
  979. {
  980. startSelection();
  981. setCursorPos(mCursorPos + 1);
  982. if( mask & MASK_CONTROL )
  983. {
  984. setCursorPos(nextWordPos(mCursorPos));
  985. }
  986. mSelectionEnd = mCursorPos;
  987. }
  988. break;
  989. case KEY_UP:
  990. startSelection();
  991. changeLine( -1 );
  992. mSelectionEnd = mCursorPos;
  993. break;
  994. case KEY_PAGE_UP:
  995. startSelection();
  996. changePage( -1 );
  997. mSelectionEnd = mCursorPos;
  998. break;
  999. case KEY_HOME:
  1000. startSelection();
  1001. if( mask & MASK_CONTROL )
  1002. {
  1003. setCursorPos(0);
  1004. }
  1005. else
  1006. {
  1007. startOfLine();
  1008. }
  1009. mSelectionEnd = mCursorPos;
  1010. break;
  1011. case KEY_DOWN:
  1012. startSelection();
  1013. changeLine( 1 );
  1014. mSelectionEnd = mCursorPos;
  1015. break;
  1016. case KEY_PAGE_DOWN:
  1017. startSelection();
  1018. changePage( 1 );
  1019. mSelectionEnd = mCursorPos;
  1020. break;
  1021. case KEY_END:
  1022. startSelection();
  1023. if( mask & MASK_CONTROL )
  1024. {
  1025. setCursorPos(getLength());
  1026. }
  1027. else
  1028. {
  1029. endOfLine();
  1030. }
  1031. mSelectionEnd = mCursorPos;
  1032. break;
  1033. default:
  1034. handled = FALSE;
  1035. break;
  1036. }
  1037. }
  1038. if( handled )
  1039. {
  1040. // take selection to 'primary' clipboard
  1041. updatePrimary();
  1042. }
  1043. return handled;
  1044. }
  1045. BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
  1046. {
  1047. BOOL handled = FALSE;
  1048. // Ignore capslock key
  1049. if( MASK_NONE == mask )
  1050. {
  1051. handled = TRUE;
  1052. switch( key )
  1053. {
  1054. case KEY_UP:
  1055. changeLine( -1 );
  1056. break;
  1057. case KEY_PAGE_UP:
  1058. changePage( -1 );
  1059. break;
  1060. case KEY_HOME:
  1061. startOfLine();
  1062. break;
  1063. case KEY_DOWN:
  1064. changeLine( 1 );
  1065. deselect();
  1066. break;
  1067. case KEY_PAGE_DOWN:
  1068. changePage( 1 );
  1069. break;
  1070. case KEY_END:
  1071. endOfLine();
  1072. break;
  1073. case KEY_LEFT:
  1074. if( hasSelection() )
  1075. {
  1076. setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
  1077. }
  1078. else
  1079. {
  1080. if( 0 < mCursorPos )
  1081. {
  1082. setCursorPos(mCursorPos - 1);
  1083. }
  1084. else
  1085. {
  1086. LLUI::reportBadKeystroke();
  1087. }
  1088. }
  1089. break;
  1090. case KEY_RIGHT:
  1091. if( hasSelection() )
  1092. {
  1093. setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
  1094. }
  1095. else
  1096. {
  1097. if( mCursorPos < getLength() )
  1098. {
  1099. setCursorPos(mCursorPos + 1);
  1100. }
  1101. else
  1102. {
  1103. LLUI::reportBadKeystroke();
  1104. }
  1105. }
  1106. break;
  1107. default:
  1108. handled = FALSE;
  1109. break;
  1110. }
  1111. }
  1112. if (handled)
  1113. {
  1114. deselect();
  1115. }
  1116. return handled;
  1117. }
  1118. void LLTextEditor::deleteSelection(BOOL group_with_next_op )
  1119. {
  1120. if( getEnabled() && hasSelection() )
  1121. {
  1122. S32 pos = llmin( mSelectionStart, mSelectionEnd );
  1123. S32 length = llabs( mSelectionStart - mSelectionEnd );
  1124. remove( pos, length, group_with_next_op );
  1125. deselect();
  1126. setCursorPos(pos);
  1127. }
  1128. }
  1129. // virtual
  1130. BOOL LLTextEditor::canCut() const
  1131. {
  1132. return !mReadOnly && hasSelection();
  1133. }
  1134. // cut selection to clipboard
  1135. void LLTextEditor::cut()
  1136. {
  1137. if( !canCut() )
  1138. {
  1139. return;
  1140. }
  1141. S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
  1142. S32 length = llabs( mSelectionStart - mSelectionEnd );
  1143. gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
  1144. deleteSelection( FALSE );
  1145. onKeyStroke();
  1146. }
  1147. BOOL LLTextEditor::canCopy() const
  1148. {
  1149. return hasSelection();
  1150. }
  1151. // copy selection to clipboard
  1152. void LLTextEditor::copy()
  1153. {
  1154. if( !canCopy() )
  1155. {
  1156. return;
  1157. }
  1158. S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
  1159. S32 length = llabs( mSelectionStart - mSelectionEnd );
  1160. gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID);
  1161. }
  1162. BOOL LLTextEditor::canPaste() const
  1163. {
  1164. return !mReadOnly && gClipboard.canPasteString();
  1165. }
  1166. // paste from clipboard
  1167. void LLTextEditor::paste()
  1168. {
  1169. bool is_primary = false;
  1170. pasteHelper(is_primary);
  1171. }
  1172. // paste from primary
  1173. void LLTextEditor::pastePrimary()
  1174. {
  1175. bool is_primary = true;
  1176. pasteHelper(is_primary);
  1177. }
  1178. // paste from primary (itsprimary==true) or clipboard (itsprimary==false)
  1179. void LLTextEditor::pasteHelper(bool is_primary)
  1180. {
  1181. mParseOnTheFly = FALSE;
  1182. bool can_paste_it;
  1183. if (is_primary)
  1184. {
  1185. can_paste_it = canPastePrimary();
  1186. }
  1187. else
  1188. {
  1189. can_paste_it = canPaste();
  1190. }
  1191. if (!can_paste_it)
  1192. {
  1193. return;
  1194. }
  1195. LLUUID source_id;
  1196. LLWString paste;
  1197. if (is_primary)
  1198. {
  1199. paste = gClipboard.getPastePrimaryWString(&source_id);
  1200. }
  1201. else
  1202. {
  1203. paste = gClipboard.getPasteWString(&source_id);
  1204. }
  1205. if (paste.empty())
  1206. {
  1207. return;
  1208. }
  1209. // Delete any selected characters (the paste replaces them)
  1210. if( (!is_primary) && hasSelection() )
  1211. {
  1212. deleteSelection(TRUE);
  1213. }
  1214. // Clean up string (replace tabs and remove characters that our fonts don't support).
  1215. LLWString clean_string(paste);
  1216. LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
  1217. if( mAllowEmbeddedItems )
  1218. {
  1219. const llwchar LF = 10;
  1220. S32 len = clean_string.length();
  1221. for( S32 i = 0; i < len; i++ )
  1222. {
  1223. llwchar wc = clean_string[i];
  1224. if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) )
  1225. {
  1226. clean_string[i] = LL_UNKNOWN_CHAR;
  1227. }
  1228. else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR)
  1229. {
  1230. clean_string[i] = pasteEmbeddedItem(wc);
  1231. }
  1232. }
  1233. }
  1234. // Insert the new text into the existing text.
  1235. //paste text with linebreaks.
  1236. std::basic_string<llwchar>::size_type start = 0;
  1237. std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
  1238. while(pos!=-1)
  1239. {
  1240. if(pos!=start)
  1241. {
  1242. std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start);
  1243. setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
  1244. }
  1245. addLineBreakChar();
  1246. start = pos+1;
  1247. pos = clean_string.find('\n',start);
  1248. }
  1249. std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
  1250. setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
  1251. deselect();
  1252. onKeyStroke();
  1253. mParseOnTheFly = TRUE;
  1254. }
  1255. // copy selection to primary
  1256. void LLTextEditor::copyPrimary()
  1257. {
  1258. if( !canCopy() )
  1259. {
  1260. return;
  1261. }
  1262. S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
  1263. S32 length = llabs( mSelectionStart - mSelectionEnd );
  1264. gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID);
  1265. }
  1266. BOOL LLTextEditor::canPastePrimary() const
  1267. {
  1268. return !mReadOnly && gClipboard.canPastePrimaryString();
  1269. }
  1270. void LLTextEditor::updatePrimary()
  1271. {
  1272. if (canCopy())
  1273. {
  1274. copyPrimary();
  1275. }
  1276. }
  1277. BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
  1278. {
  1279. BOOL handled = FALSE;
  1280. if( mask & MASK_CONTROL )
  1281. {
  1282. handled = TRUE;
  1283. switch( key )
  1284. {
  1285. case KEY_HOME:
  1286. if( mask & MASK_SHIFT )
  1287. {
  1288. startSelection();
  1289. setCursorPos(0);
  1290. mSelectionEnd = mCursorPos;
  1291. }
  1292. else
  1293. {
  1294. // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
  1295. // all move the cursor as if clicking, so should deselect.
  1296. deselect();
  1297. startOfDoc();
  1298. }
  1299. break;
  1300. case KEY_END:
  1301. {
  1302. if( mask & MASK_SHIFT )
  1303. {
  1304. startSelection();
  1305. }
  1306. else
  1307. {
  1308. // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
  1309. // all move the cursor as if clicking, so should deselect.
  1310. deselect();
  1311. }
  1312. endOfDoc();
  1313. if( mask & MASK_SHIFT )
  1314. {
  1315. mSelectionEnd = mCursorPos;
  1316. }
  1317. break;
  1318. }
  1319. case KEY_RIGHT:
  1320. if( mCursorPos < getLength() )
  1321. {
  1322. // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
  1323. // all move the cursor as if clicking, so should deselect.
  1324. deselect();
  1325. setCursorPos(nextWordPos(mCursorPos + 1));
  1326. }
  1327. break;
  1328. case KEY_LEFT:
  1329. if( mCursorPos > 0 )
  1330. {
  1331. // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
  1332. // all move the cursor as if clicking, so should deselect.
  1333. deselect();
  1334. setCursorPos(prevWordPos(mCursorPos - 1));
  1335. }
  1336. break;
  1337. default:
  1338. handled = FALSE;
  1339. break;
  1340. }
  1341. }
  1342. if (handled)
  1343. {
  1344. updatePrimary();
  1345. }
  1346. return handled;
  1347. }
  1348. BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
  1349. {
  1350. BOOL handled = TRUE;
  1351. if (mReadOnly) return FALSE;
  1352. switch( key )
  1353. {
  1354. case KEY_INSERT:
  1355. if (mask == MASK_NONE)
  1356. {
  1357. gKeyboard->toggleInsertMode();
  1358. }
  1359. break;
  1360. case KEY_BACKSPACE:
  1361. if( hasSelection() )
  1362. {
  1363. deleteSelection(FALSE);
  1364. }
  1365. else
  1366. if( 0 < mCursorPos )
  1367. {
  1368. removeCharOrTab();
  1369. }
  1370. else
  1371. {
  1372. LLUI::reportBadKeystroke();
  1373. }
  1374. break;
  1375. case KEY_RETURN:
  1376. if (mask == MASK_NONE)
  1377. {
  1378. if( hasSelection() )
  1379. {
  1380. deleteSelection(FALSE);
  1381. }
  1382. autoIndent(); // TODO: make this optional
  1383. }
  1384. else
  1385. {
  1386. handled = FALSE;
  1387. break;
  1388. }
  1389. break;
  1390. case KEY_TAB:
  1391. if (mask & MASK_CONTROL)
  1392. {
  1393. handled = FALSE;
  1394. break;
  1395. }
  1396. if( hasSelection() && selectionContainsLineBreaks() )
  1397. {
  1398. indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB );
  1399. }
  1400. else
  1401. {
  1402. if( hasSelection() )
  1403. {
  1404. deleteSelection(FALSE);
  1405. }
  1406. S32 offset = getLineOffsetFromDocIndex(mCursorPos);
  1407. S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
  1408. for( S32 i=0; i < spaces_needed; i++ )
  1409. {
  1410. addChar( ' ' );
  1411. }
  1412. }
  1413. break;
  1414. default:
  1415. handled = FALSE;
  1416. break;
  1417. }
  1418. if (handled)
  1419. {
  1420. onKeyStroke();
  1421. }
  1422. return handled;
  1423. }
  1424. void LLTextEditor::unindentLineBeforeCloseBrace()
  1425. {
  1426. if( mCursorPos >= 1 )
  1427. {
  1428. LLWString text = getWText();
  1429. if( ' ' == text[ mCursorPos - 1 ] )
  1430. {
  1431. removeCharOrTab();
  1432. }
  1433. }
  1434. }
  1435. BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
  1436. {
  1437. BOOL handled = FALSE;
  1438. // Special case for TAB. If want to move to next field, report
  1439. // not handled and let the parent take care of field movement.
  1440. if (KEY_TAB == key && mTabsToNextField)
  1441. {
  1442. return FALSE;
  1443. }
  1444. if (mReadOnly && mScroller)
  1445. {
  1446. handled = (mScroller && mScroller->handleKeyHere( key, mask ))
  1447. || handleSelectionKey(key, mask)
  1448. || handleControlKey(key, mask);
  1449. }
  1450. else
  1451. {
  1452. handled = handleNavigationKey( key, mask )
  1453. || handleSelectionKey(key, mask)
  1454. || handleControlKey(key, mask)
  1455. || handleSpecialKey(key, mask);
  1456. }
  1457. if( handled )
  1458. {
  1459. resetCursorBlink();
  1460. needsScroll();
  1461. }
  1462. return handled;
  1463. }
  1464. BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
  1465. {
  1466. if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
  1467. {
  1468. return FALSE;
  1469. }
  1470. BOOL handled = FALSE;
  1471. // Handle most keys only if the text editor is writeable.
  1472. if( !mReadOnly )
  1473. {
  1474. if( '}' == uni_char )
  1475. {
  1476. unindentLineBeforeCloseBrace();
  1477. }
  1478. // TODO: KLW Add auto show of tool tip on (
  1479. addChar( uni_char );
  1480. // Keys that add characters temporarily hide the cursor
  1481. getWindow()->hideCursorUntilMouseMove();
  1482. handled = TRUE;
  1483. }
  1484. if( handled )
  1485. {
  1486. resetCursorBlink();
  1487. // Most keystrokes will make the selection box go away, but not all will.
  1488. deselect();
  1489. onKeyStroke();
  1490. }
  1491. return handled;
  1492. }
  1493. // virtual
  1494. BOOL LLTextEditor::canDoDelete() const
  1495. {
  1496. return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
  1497. }
  1498. void LLTextEditor::doDelete()
  1499. {
  1500. if( !canDoDelete() )
  1501. {
  1502. return;
  1503. }
  1504. if( hasSelection() )
  1505. {
  1506. deleteSelection(FALSE);
  1507. }
  1508. else
  1509. if( mCursorPos < getLength() )
  1510. {
  1511. S32 i;
  1512. S32 chars_to_remove = 1;
  1513. LLWString text = getWText();
  1514. if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
  1515. {
  1516. // Try to remove a full tab's worth of spaces
  1517. S32 offset = getLineOffsetFromDocIndex(mCursorPos);
  1518. chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
  1519. if( chars_to_remove == 0 )
  1520. {
  1521. chars_to_remove = SPACES_PER_TAB;
  1522. }
  1523. for( i = 0; i < chars_to_remove; i++ )
  1524. {
  1525. if( text[mCursorPos + i] != ' ' )
  1526. {
  1527. chars_to_remove = 1;
  1528. break;
  1529. }
  1530. }
  1531. }
  1532. for( i = 0; i < chars_to_remove; i++ )
  1533. {
  1534. setCursorPos(mCursorPos + 1);
  1535. removeChar();
  1536. }
  1537. }
  1538. onKeyStroke();
  1539. }
  1540. //----------------------------------------------------------------------------
  1541. void LLTextEditor::blockUndo()
  1542. {
  1543. mBaseDocIsPristine = FALSE;
  1544. mLastCmd = NULL;
  1545. std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
  1546. mUndoStack.clear();
  1547. }
  1548. // virtual
  1549. BOOL LLTextEditor::canUndo() const
  1550. {
  1551. return !mReadOnly && mLastCmd != NULL;
  1552. }
  1553. void LLTextEditor::undo()
  1554. {
  1555. if( !canUndo() )
  1556. {
  1557. return;
  1558. }
  1559. deselect();
  1560. S32 pos = 0;
  1561. do
  1562. {
  1563. pos = mLastCmd->undo(this);
  1564. undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
  1565. if (iter != mUndoStack.end())
  1566. ++iter;
  1567. if (iter != mUndoStack.end())
  1568. mLastCmd = *iter;
  1569. else
  1570. mLastCmd = NULL;
  1571. } while( mLastCmd && mLastCmd->groupWithNext() );
  1572. setCursorPos(pos);
  1573. onKeyStroke();
  1574. }
  1575. BOOL LLTextEditor::canRedo() const
  1576. {
  1577. return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front());
  1578. }
  1579. void LLTextEditor::redo()
  1580. {
  1581. if( !canRedo() )
  1582. {
  1583. return;
  1584. }
  1585. deselect();
  1586. S32 pos = 0;
  1587. do
  1588. {
  1589. if( !mLastCmd )
  1590. {
  1591. mLastCmd = mUndoStack.back();
  1592. }
  1593. else
  1594. {
  1595. undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
  1596. if (iter != mUndoStack.begin())
  1597. mLastCmd = *(--iter);
  1598. else
  1599. mLastCmd = NULL;
  1600. }
  1601. if( mLastCmd )
  1602. {
  1603. pos = mLastCmd->redo(this);
  1604. }
  1605. } while(
  1606. mLastCmd &&
  1607. mLastCmd->groupWithNext() &&
  1608. (mLastCmd != mUndoStack.front()) );
  1609. setCursorPos(pos);
  1610. onKeyStroke();
  1611. }
  1612. void LLTextEditor::onFocusReceived()
  1613. {
  1614. LLTextBase::onFocusReceived();
  1615. updateAllowingLanguageInput();
  1616. }
  1617. // virtual, from LLView
  1618. void LLTextEditor::onFocusLost()
  1619. {
  1620. updateAllowingLanguageInput();
  1621. // Route menu back to the default
  1622. if( gEditMenuHandler == this )
  1623. {
  1624. gEditMenuHandler = NULL;
  1625. }
  1626. if (mCommitOnFocusLost)
  1627. {
  1628. onCommit();
  1629. }
  1630. // Make sure cursor is shown again
  1631. getWindow()->showCursorFromMouseMove();
  1632. LLTextBase::onFocusLost();
  1633. }
  1634. void LLTextEditor::onCommit()
  1635. {
  1636. setControlValue(getValue());
  1637. LLTextBase::onCommit();
  1638. }
  1639. void LLTextEditor::setEnabled(BOOL enabled)
  1640. {
  1641. // just treat enabled as read-only flag
  1642. bool read_only = !enabled;
  1643. if (read_only != mReadOnly)
  1644. {
  1645. //mReadOnly = read_only;
  1646. LLTextBase::setReadOnly(read_only);
  1647. updateSegments();
  1648. updateAllowingLanguageInput();
  1649. }
  1650. }
  1651. void LLTextEditor::showContextMenu(S32 x, S32 y)
  1652. {
  1653. if (!mContextMenu)
  1654. {
  1655. mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",
  1656. LLMenuGL::sMenuContainer,
  1657. LLMenuHolderGL::child_registry_t::instance());
  1658. }
  1659. // Route menu to this class
  1660. // previously this was done in ::handleRightMoseDown:
  1661. //if(hasTabStop())
  1662. // setFocus(TRUE) - why? weird...
  1663. // and then inside setFocus
  1664. // ....
  1665. // gEditMenuHandler = this;
  1666. // ....
  1667. // but this didn't work in all cases and just weird...
  1668. //why not here?
  1669. // (all this was done for EXT-4443)
  1670. gEditMenuHandler = this;
  1671. S32 screen_x, screen_y;
  1672. localPointToScreen(x, y, &screen_x, &screen_y);
  1673. mContextMenu->show(screen_x, screen_y);
  1674. }
  1675. void LLTextEditor::drawPreeditMarker()
  1676. {
  1677. static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
  1678. static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
  1679. static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
  1680. static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
  1681. static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
  1682. static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
  1683. static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
  1684. static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
  1685. if (!hasPreeditString())
  1686. {
  1687. return;
  1688. }
  1689. const LLWString textString(getWText());
  1690. const llwchar *text = textString.c_str();
  1691. const S32 text_len = getLength();
  1692. const S32 num_lines = getLineCount();
  1693. S32 cur_line = getFirstVisibleLine();
  1694. if (cur_line >= num_lines)
  1695. {
  1696. return;
  1697. }
  1698. const S32 line_height = llround( mDefaultFont->getLineHeight() );
  1699. S32 line_start = getLineStart(cur_line);
  1700. S32 line_y = mVisibleTextRect.mTop - line_height;
  1701. while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line))
  1702. {
  1703. S32 next_start = -1;
  1704. S32 line_end = text_len;
  1705. if ((cur_line + 1) < num_lines)
  1706. {
  1707. next_start = getLineStart(cur_line + 1);
  1708. line_end = next_start;
  1709. }
  1710. if ( text[line_end-1] == '\n' )
  1711. {
  1712. --line_end;
  1713. }
  1714. // Does this line contain preedits?
  1715. if (line_start >= mPreeditPositions.back())
  1716. {
  1717. // We have passed the preedits.
  1718. break;
  1719. }
  1720. if (line_end > mPreeditPositions.front())
  1721. {
  1722. for (U32 i = 0; i < mPreeditStandouts.size(); i++)
  1723. {
  1724. S32 left = mPreeditPositions[i];
  1725. S32 right = mPreeditPositions[i + 1];
  1726. if (right <= line_start || left >= line_end)
  1727. {
  1728. continue;
  1729. }
  1730. S32 preedit_left = mVisibleTextRect.mLeft;
  1731. if (left > line_start)
  1732. {
  1733. preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
  1734. }
  1735. S32 preedit_right = mVisibleTextRect.mLeft;
  1736. if (right < line_end)
  1737. {
  1738. preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
  1739. }
  1740. else
  1741. {
  1742. preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
  1743. }
  1744. if (mPreeditStandouts[i])
  1745. {
  1746. gl_rect_2d(preedit_left + preedit_standout_gap,
  1747. line_y + preedit_standout_position,
  1748. preedit_right - preedit_standout_gap - 1,
  1749. line_y + preedit_standout_position - preedit_standout_thickness,
  1750. (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
  1751. }
  1752. else
  1753. {
  1754. gl_rect_2d(preedit_left + preedit_marker_gap,
  1755. line_y + preedit_marker_position,
  1756. preedit_right - preedit_marker_gap - 1,
  1757. line_y + preedit_marker_position - preedit_marker_thickness,
  1758. (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
  1759. }
  1760. }
  1761. }
  1762. // move down one line
  1763. line_y -= line_height;
  1764. line_start = next_start;
  1765. cur_line++;
  1766. }
  1767. }
  1768. void LLTextEditor::drawLineNumbers()
  1769. {
  1770. LLGLSUIDefault gls_ui;
  1771. LLRect scrolled_view_rect = getVisibleDocumentRect();
  1772. LLRect content_rect = getVisibleTextRect();
  1773. LLLocalClipRect clip(content_rect);
  1774. S32 first_line = getFirstVisibleLine();
  1775. S32 num_lines = getLineCount();
  1776. if (first_line >= num_lines)
  1777. {
  1778. return;
  1779. }
  1780. S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum;
  1781. if (mShowLineNumbers)
  1782. {
  1783. S32 left = 0;
  1784. S32 top = getRect().getHeight();
  1785. S32 bottom = 0;
  1786. gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
  1787. gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
  1788. S32 last_line_num = -1;
  1789. for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
  1790. {
  1791. line_info& line = mLineInfoList[cur_line];
  1792. if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom)
  1793. {
  1794. break;
  1795. }
  1796. S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom;
  1797. // draw the line numbers
  1798. if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop)
  1799. {
  1800. const LLFontGL *num_font = LLFontGL::getFontMonospace();
  1801. const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum ));
  1802. BOOL is_cur_line = cursor_line == line.mLineNum;
  1803. const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL;
  1804. const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor;
  1805. num_font->render(
  1806. ltext, // string to draw
  1807. 0, // begin offset
  1808. UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x
  1809. line_bottom, // y
  1810. fg_color,
  1811. LLFontGL::RIGHT, // horizontal alignment
  1812. LLFontGL::BOTTOM, // vertical alignment
  1813. style,
  1814. LLFontGL::NO_SHADOW,
  1815. S32_MAX, // max chars
  1816. UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels
  1817. last_line_num = line.mLineNum;
  1818. }
  1819. }
  1820. }
  1821. }
  1822. void LLTextEditor::draw()
  1823. {
  1824. {
  1825. // pad clipping rectangle so that cursor can draw at full width
  1826. // when at left edge of mVisibleTextRect
  1827. LLRect clip_rect(mVisibleTextRect);
  1828. clip_rect.stretch(1);
  1829. LLLocalClipRect clip(clip_rect);
  1830. drawPreeditMarker();
  1831. }
  1832. LLTextBase::draw();
  1833. drawLineNumbers();
  1834. //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
  1835. // when in readonly mode
  1836. mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly);
  1837. }
  1838. // Start or stop the editor from accepting text-editing keystrokes
  1839. // see also LLLineEditor
  1840. void LLTextEditor::setFocus( BOOL new_state )
  1841. {
  1842. BOOL old_state = hasFocus();
  1843. // Don't change anything if the focus state didn't change
  1844. if (new_state == old_state) return;
  1845. // Notify early if we are losing focus.
  1846. if (!new_state)
  1847. {
  1848. getWindow()->allowLanguageTextInput(this, FALSE);
  1849. }
  1850. LLTextBase::setFocus( new_state );
  1851. if( new_state )
  1852. {
  1853. // Route menu to this class
  1854. gEditMenuHandler = this;
  1855. // Don't start the cursor flashing right away
  1856. resetCursorBlink();
  1857. }
  1858. else
  1859. {
  1860. // Route menu back to the default
  1861. if( gEditMenuHandler == this )
  1862. {
  1863. gEditMenuHandler = NULL;
  1864. }
  1865. endSelection();
  1866. }
  1867. }
  1868. // public
  1869. void LLTextEditor::setCursorAndScrollToEnd()
  1870. {
  1871. deselect();
  1872. endOfDoc();
  1873. }
  1874. void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )
  1875. {
  1876. *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap);
  1877. *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);
  1878. }
  1879. void LLTextEditor::autoIndent()
  1880. {
  1881. // Count the number of spaces in the current line
  1882. S32 line = getLineNumFromDocIndex(mCursorPos, false);
  1883. S32 line_start = getLineStart(line);
  1884. S32 space_count = 0;
  1885. S32 i;
  1886. LLWString text = getWText();
  1887. while( ' ' == text[line_start] )
  1888. {
  1889. space_count++;
  1890. line_start++;
  1891. }
  1892. // If we're starting a braced section, indent one level.
  1893. if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') )
  1894. {
  1895. space_count += SPACES_PER_TAB;
  1896. }
  1897. // Insert that number of spaces on the new line
  1898. //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' );
  1899. addLineBreakChar();
  1900. for( i = 0; i < space_count; i++ )
  1901. {
  1902. addChar( ' ' );
  1903. }
  1904. }
  1905. // Inserts new text at the cursor position
  1906. void LLTextEditor::insertText(const std::string &new_text)
  1907. {
  1908. BOOL enabled = getEnabled();
  1909. setEnabled( TRUE );
  1910. // Delete any selected characters (the insertion replaces them)
  1911. if( hasSelection() )
  1912. {
  1913. deleteSelection(TRUE);
  1914. }
  1915. setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() ));
  1916. setEnabled( enabled );
  1917. }
  1918. void LLTextEditor::insertText(LLWString &new_text)
  1919. {
  1920. BOOL enabled = getEnabled();
  1921. setEnabled( TRUE );
  1922. // Delete any selected characters (the insertion replaces them)
  1923. if( hasSelection() )
  1924. {
  1925. deleteSelection(TRUE);
  1926. }
  1927. setCursorPos(mCursorPos + insert( mCursorPos, new_text, FALSE, LLTextSegmentPtr() ));
  1928. setEnabled( enabled );
  1929. }
  1930. void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
  1931. {
  1932. // Save old state
  1933. S32 selection_start = mSelectionStart;
  1934. S32 selection_end = mSelectionEnd;
  1935. BOOL was_selecting = mIsSelecting;
  1936. S32 cursor_pos = mCursorPos;
  1937. S32 old_length = getLength();
  1938. BOOL cursor_was_at_end = (mCursorPos == old_length);
  1939. deselect();
  1940. setCursorPos(old_length);
  1941. LLWString widget_wide_text = utf8str_to_wstring(text);
  1942. LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
  1943. insert(getLength(), widget_wide_text, FALSE, segment);
  1944. // Set the cursor and scroll position
  1945. if( selection_start != selection_end )
  1946. {
  1947. mSelectionStart = selection_start;
  1948. mSelectionEnd = selection_end;
  1949. mIsSelecting = was_selecting;
  1950. setCursorPos(cursor_pos);
  1951. }
  1952. else if( cursor_was_at_end )
  1953. {
  1954. setCursorPos(getLength());
  1955. }
  1956. else
  1957. {
  1958. setCursorPos(cursor_pos);
  1959. }
  1960. if (!allow_undo)
  1961. {
  1962. blockUndo();
  1963. }
  1964. }
  1965. void LLTextEditor::removeTextFromEnd(S32 num_chars)
  1966. {
  1967. if (num_chars <= 0) return;
  1968. remove(getLength() - num_chars, num_chars, FALSE);
  1969. S32 len = getLength();
  1970. setCursorPos (llclamp(mCursorPos, 0, len));
  1971. mSelectionStart = llclamp(mSelectionStart, 0, len);
  1972. mSelectionEnd = llclamp(mSelectionEnd, 0, len);
  1973. needsScroll();
  1974. }
  1975. //----------------------------------------------------------------------------
  1976. void LLTextEditor::makePristine()
  1977. {
  1978. mPristineCmd = mLastCmd;
  1979. mBaseDocIsPristine = !mLastCmd;
  1980. // Create a clean partition in the undo stack. We don't want a single command to extend from
  1981. // the "pre-pristine" state to the "post-pristine" state.
  1982. if( mLastCmd )
  1983. {
  1984. mLastCmd->blockExtensions();
  1985. }
  1986. }
  1987. BOOL LLTextEditor::isPristine() const
  1988. {
  1989. if( mPristineCmd )
  1990. {
  1991. return (mPristineCmd == mLastCmd);
  1992. }
  1993. else
  1994. {
  1995. // No undo stack, so check if the version before and commands were done was the original version
  1996. return !mLastCmd && mBaseDocIsPristine;
  1997. }
  1998. }
  1999. BOOL LLTextEditor::tryToRevertToPristineState()
  2000. {
  2001. if( !isPristine() )
  2002. {
  2003. deselect();
  2004. S32 i = 0;
  2005. while( !isPristine() && canUndo() )
  2006. {
  2007. undo();
  2008. i--;
  2009. }
  2010. while( !isPristine() && canRedo() )
  2011. {
  2012. redo();
  2013. i++;
  2014. }
  2015. if( !isPristine() )
  2016. {
  2017. // failed, so go back to where we started
  2018. while( i > 0 )
  2019. {
  2020. undo();
  2021. i--;
  2022. }
  2023. }
  2024. }
  2025. return isPristine(); // TRUE => success
  2026. }
  2027. static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting");
  2028. void LLTextEditor::loadKeywords(const std::string& filename,
  2029. const std::vector<std::string>& funcs,
  2030. const std::vector<std::string>& tooltips,
  2031. const LLColor3& color)
  2032. {
  2033. LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
  2034. if(mKeywords.loadFromFile(filename))
  2035. {
  2036. S32 count = llmin(funcs.size(), tooltips.size());
  2037. for(S32 i = 0; i < count; i++)
  2038. {
  2039. std::string name = utf8str_trim(funcs[i]);
  2040. mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] );
  2041. }
  2042. segment_vec_t segment_list;
  2043. mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
  2044. mSegments.clear();
  2045. segment_set_t::iterator insert_it = mSegments.begin();
  2046. for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
  2047. {
  2048. insert_it = mSegments.insert(insert_it, *list_it);
  2049. }
  2050. }
  2051. }
  2052. void LLTextEditor::updateSegments()
  2053. {
  2054. if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)
  2055. {
  2056. LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
  2057. // HACK: No non-ascii keywords for now
  2058. segment_vec_t segment_list;
  2059. mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
  2060. clearSegments();
  2061. segment_set_t::iterator insert_it = mSegments.begin();
  2062. for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
  2063. {
  2064. insertSegment(*list_it);
  2065. }
  2066. }
  2067. LLTextBase::updateSegments();
  2068. }
  2069. void LLTextEditor::updateLinkSegments()
  2070. {
  2071. LLWString wtext = getWText();
  2072. // update any segments that contain a link
  2073. for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it)
  2074. {
  2075. LLTextSegment *segment = *it;
  2076. if (segment && segment->getStyle() && segment->getStyle()->isLink())
  2077. {
  2078. // if the link's label (what the user can edit) is a valid Url,
  2079. // then update the link's HREF to be the same as the label text.
  2080. // This lets users edit Urls in-place.
  2081. LLStyleConstSP style = segment->getStyle();
  2082. LLStyleSP new_style(new LLStyle(*style));
  2083. LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart());
  2084. if (LLUrlRegistry::instance().hasUrl(url_label))
  2085. {
  2086. std::string new_url = wstring_to_utf8str(url_label);
  2087. LLStringUtil::trim(new_url);
  2088. new_style->setLinkHREF(new_url);
  2089. LLStyleConstSP sp(new_style);
  2090. segment->setStyle(sp);
  2091. }
  2092. }
  2093. }
  2094. }
  2095. void LLTextEditor::onMouseCaptureLost()
  2096. {
  2097. endSelection();
  2098. }
  2099. ///////////////////////////////////////////////////////////////////
  2100. // Hack for Notecards
  2101. BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
  2102. {
  2103. std::istringstream instream(buffer);
  2104. // Version 1 format:
  2105. // Linden text version 1\n
  2106. // {\n
  2107. // <EmbeddedItemList chunk>
  2108. // Text length <bytes without \0>\n
  2109. // <text without \0> (text may contain ext_char_values)
  2110. // }\n
  2111. char tbuf[MAX_STRING]; /* Flawfinder: ignore */
  2112. S32 version = 0;
  2113. instream.getline(tbuf, MAX_STRING);
  2114. if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
  2115. {
  2116. llwarns << "Invalid Linden text file header " << llendl;
  2117. return FALSE;
  2118. }
  2119. if( 1 != version )
  2120. {
  2121. llwarns << "Invalid Linden text file version: " << version << llendl;
  2122. return FALSE;
  2123. }
  2124. instream.getline(tbuf, MAX_STRING);
  2125. if( 0 != sscanf(tbuf, "{") )
  2126. {
  2127. llwarns << "Invalid Linden text file format" << llendl;
  2128. return FALSE;
  2129. }
  2130. S32 text_len = 0;
  2131. instream.getline(tbuf, MAX_STRING);
  2132. if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
  2133. {
  2134. llwarns << "Invalid Linden text length field" << llendl;
  2135. return FALSE;
  2136. }
  2137. if( text_len > mMaxTextByteLength )
  2138. {
  2139. llwarns << "Invalid Linden text length: " << text_len << llendl;
  2140. return FALSE;
  2141. }
  2142. BOOL success = TRUE;
  2143. char* text = new char[ text_len + 1];
  2144. if (text == NULL)
  2145. {
  2146. llerrs << "Memory allocation failure." << llendl;
  2147. return FALSE;
  2148. }
  2149. instream.get(text, text_len + 1, '\0');
  2150. text[text_len] = '\0';
  2151. if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */
  2152. {
  2153. llwarns << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << llendl;/* Flawfinder: ignore */
  2154. success = FALSE;
  2155. }
  2156. instream.getline(tbuf, MAX_STRING);
  2157. if( success && (0 != sscanf(tbuf, "}")) )
  2158. {
  2159. llwarns << "Invalid Linden text file format: missing terminal }" << llendl;
  2160. success = FALSE;
  2161. }
  2162. if( success )
  2163. {
  2164. // Actually set the text
  2165. setText( LLStringExplicit(text) );
  2166. }
  2167. delete[] text;
  2168. startOfDoc();
  2169. deselect();
  2170. return success;
  2171. }
  2172. BOOL LLTextEditor::exportBuffer(std::string &buffer )
  2173. {
  2174. std::ostringstream outstream(buffer);
  2175. outstream << "Linden text version 1\n";
  2176. outstream << "{\n";
  2177. outstream << llformat("Text length %d\n", getLength() );
  2178. outstream << getText();
  2179. outstream << "}\n";
  2180. return TRUE;
  2181. }
  2182. void LLTextEditor::updateAllowingLanguageInput()
  2183. {
  2184. LLWindow* window = getWindow();
  2185. if (!window)
  2186. {
  2187. // test app, no window available
  2188. return;
  2189. }
  2190. if (hasFocus() && !mReadOnly)
  2191. {
  2192. window->allowLanguageTextInput(this, TRUE);
  2193. }
  2194. else
  2195. {
  2196. window->allowLanguageTextInput(this, FALSE);
  2197. }
  2198. }
  2199. // Preedit is managed off the undo/redo command stack.
  2200. BOOL LLTextEditor::hasPreeditString() const
  2201. {
  2202. return (mPreeditPositions.size() > 1);
  2203. }
  2204. void LLTextEditor::resetPreedit()
  2205. {
  2206. if (hasPreeditString())
  2207. {
  2208. if (hasSelection())
  2209. {
  2210. llwarns << "Preedit and selection!" << llendl;
  2211. deselect();
  2212. }
  2213. setCursorPos(mPreeditPositions.front());
  2214. removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
  2215. insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
  2216. mPreeditWString.clear();
  2217. mPreeditOverwrittenWString.clear();
  2218. mPreeditPositions.clear();
  2219. // A call to updatePreedit should soon follow under a
  2220. // normal course of operation, so we don't need to
  2221. // maintain internal variables such as line start
  2222. // positions now.
  2223. }
  2224. }
  2225. void LLTextEditor::updatePreedit(const LLWString &preedit_string,
  2226. const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
  2227. {
  2228. // Just in case.
  2229. if (mReadOnly)
  2230. {
  2231. return;
  2232. }
  2233. getWindow()->hideCursorUntilMouseMove();
  2234. S32 insert_preedit_at = mCursorPos;
  2235. mPreeditWString = preedit_string;
  2236. mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
  2237. S32 position = insert_preedit_at;
  2238. for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
  2239. {
  2240. mPreeditPositions[i] = position;
  2241. position += preedit_segment_lengths[i];
  2242. }
  2243. mPreeditPositions.back() = position;
  2244. if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
  2245. {
  2246. mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());
  2247. removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
  2248. }
  2249. else
  2250. {
  2251. mPreeditOverwrittenWString.clear();
  2252. }
  2253. insertStringNoUndo(insert_preedit_at, mPreeditWString);
  2254. mPreeditStandouts = preedit_standouts;
  2255. setCursorPos(insert_preedit_at + caret_position);
  2256. // Update of the preedit should be caused by some key strokes.
  2257. resetCursorBlink();
  2258. onKeyStroke();
  2259. }
  2260. BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
  2261. {
  2262. if (control)
  2263. {
  2264. LLRect control_rect_screen;
  2265. localRectToScreen(mVisibleTextRect, &control_rect_screen);
  2266. LLUI::screenRectToGL(control_rect_screen, control);
  2267. }
  2268. S32 preedit_left_position, preedit_right_position;
  2269. if (hasPreeditString())
  2270. {
  2271. preedit_left_position = mPreeditPositions.front();
  2272. preedit_right_position = mPreeditPositions.back();
  2273. }
  2274. else
  2275. {
  2276. preedit_left_position = preedit_right_position = mCursorPos;
  2277. }
  2278. const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos);
  2279. if (query < preedit_left_position || query > preedit_right_position)
  2280. {
  2281. return FALSE;
  2282. }
  2283. const S32 first_visible_line = getFirstVisibleLine();
  2284. if (query < getLineStart(first_visible_line))
  2285. {
  2286. return FALSE;
  2287. }
  2288. S32 current_line = first_visible_line;
  2289. S32 current_line_start, current_line_end;
  2290. for (;;)
  2291. {
  2292. current_line_start = getLineStart(current_line);
  2293. current_line_end = getLineStart(current_line + 1);
  2294. if (query >= current_line_start && query < current_line_end)
  2295. {
  2296. break;
  2297. }
  2298. if (current_line_start == current_line_end)
  2299. {
  2300. // We have reached on the last line. The query position must be here.
  2301. break;
  2302. }
  2303. current_line++;
  2304. }
  2305. const LLWString textString(getWText());
  2306. const llwchar * const text = textString.c_str();
  2307. const S32 line_height = llround(mDefaultFont->getLineHeight());
  2308. if (coord)
  2309. {
  2310. const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
  2311. const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
  2312. S32 query_screen_x, query_screen_y;
  2313. localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y);
  2314. LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
  2315. }
  2316. if (bounds)
  2317. {
  2318. S32 preedit_left = mVisibleTextRect.mLeft;
  2319. if (preedit_left_position > current_line_start)
  2320. {
  2321. preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
  2322. }
  2323. S32 preedit_right = mVisibleTextRect.mLeft;
  2324. if (preedit_right_position < current_line_end)
  2325. {
  2326. preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
  2327. }
  2328. else
  2329. {
  2330. preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
  2331. }
  2332. const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height;
  2333. const S32 preedit_bottom = preedit_top - line_height;
  2334. const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom);
  2335. LLRect preedit_rect_screen;
  2336. localRectToScreen(preedit_rect_local, &preedit_rect_screen);
  2337. LLUI::screenRectToGL(preedit_rect_screen, bounds);
  2338. }
  2339. return TRUE;
  2340. }
  2341. void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const
  2342. {
  2343. if (hasSelection())
  2344. {
  2345. *position = llmin(mSelectionStart, mSelectionEnd);
  2346. *length = llabs(mSelectionStart - mSelectionEnd);
  2347. }
  2348. else
  2349. {
  2350. *position = mCursorPos;
  2351. *length = 0;
  2352. }
  2353. }
  2354. void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const
  2355. {
  2356. if (hasPreeditString())
  2357. {
  2358. *position = mPreeditPositions.front();
  2359. *length = mPreeditPositions.back() - mPreeditPositions.front();
  2360. }
  2361. else
  2362. {
  2363. *position = mCursorPos;
  2364. *length = 0;
  2365. }
  2366. }
  2367. void LLTextEditor::markAsPreedit(S32 position, S32 length)
  2368. {
  2369. deselect();
  2370. setCursorPos(position);
  2371. if (hasPreeditString())
  2372. {
  2373. llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
  2374. }
  2375. mPreeditWString = LLWString( getWText(), position, length );
  2376. if (length > 0)
  2377. {
  2378. mPreeditPositions.resize(2);
  2379. mPreeditPositions[0] = position;
  2380. mPreeditPositions[1] = position + length;
  2381. mPreeditStandouts.resize(1);
  2382. mPreeditStandouts[0] = FALSE;
  2383. }
  2384. else
  2385. {
  2386. mPreeditPositions.clear();
  2387. mPreeditStandouts.clear();
  2388. }
  2389. if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
  2390. {
  2391. mPreeditOverwrittenWString = mPreeditWString;
  2392. }
  2393. else
  2394. {
  2395. mPreeditOverwrittenWString.clear();
  2396. }
  2397. }
  2398. S32 LLTextEditor::getPreeditFontSize() const
  2399. {
  2400. return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
  2401. }
  2402. BOOL LLTextEditor::isDirty() const
  2403. {
  2404. if(mReadOnly)
  2405. {
  2406. return FALSE;
  2407. }
  2408. if( mPristineCmd )
  2409. {
  2410. return ( mPristineCmd == mLastCmd );
  2411. }
  2412. else
  2413. {
  2414. return ( NULL != mLastCmd );
  2415. }
  2416. }
  2417. void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback)
  2418. {
  2419. mKeystrokeSignal.connect(callback);
  2420. }
  2421. void LLTextEditor::onKeyStroke()
  2422. {
  2423. mKeystrokeSignal(this);
  2424. }
  2425. //virtual
  2426. void LLTextEditor::clear()
  2427. {
  2428. getViewModel()->setDisplay(LLWStringUtil::null);
  2429. clearSegments();
  2430. }
  2431. bool LLTextEditor::canLoadOrSaveToFile()
  2432. {
  2433. return !mReadOnly;
  2434. }
  2435. S32 LLTextEditor::spacesPerTab()
  2436. {
  2437. return SPACES_PER_TAB;
  2438. }