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

游戏引擎

开发平台:

C++ Builder

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