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

/ExtLibs/wxWidgets/src/univ/textctrl.cpp

https://bitbucket.org/cafu/cafu
C++ | 5010 lines | 3186 code | 797 blank | 1027 comment | 711 complexity | 2c3a144faec1015d4bee44a84d1ea6cb MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0, BSD-3-Clause, LGPL-3.0, LGPL-2.1, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/univ/textctrl.cpp
  3. // Purpose: wxTextCtrl
  4. // Author: Vadim Zeitlin
  5. // Modified by:
  6. // Created: 15.09.00
  7. // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
  8. // Licence: wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10. /*
  11. TODO
  12. + 1. update vert scrollbar when any line length changes for WrapLines()
  13. + 2. cursor movement ("Hello,^" -> "^verse!" on Arrow Down)?
  14. -> maybe save the x position and use it instead of current in handling
  15. DOWN/UP actions (this would make up/down always return the cursor to
  16. the same location)?
  17. 3. split file into chunks
  18. +? 4. rewrite Replace() refresh logic to deal with wrapping lines
  19. +? 5. cache info found by GetPartOfWrappedLine() - performance must be horrible
  20. with lots of text
  21. 6. backspace refreshes too much (until end of line)
  22. */
  23. /*
  24. Optimisation hints from PureQuantify:
  25. +1. wxStringTokenize is the slowest part of Replace
  26. 2. GetDC/ReleaseDC are very slow, avoid calling them several times
  27. +3. GetCharHeight() should be cached too
  28. 4. wxClientDC construction/destruction in HitTestLine is horribly expensive
  29. For line wrapping controls HitTest2 takes 50% of program time. The results
  30. of GetRowsPerLine and GetPartOfWrappedLine *MUST* be cached.
  31. Search for "OPT!" for things which must be optimized.
  32. */
  33. /*
  34. Some terminology:
  35. Everywhere in this file LINE refers to a logical line of text, and ROW to a
  36. physical line of text on the display. They are the same unless WrapLines()
  37. is true in which case a single LINE may correspond to multiple ROWs.
  38. A text position is an unsigned int (which for reasons of compatibility is
  39. still a long as wxTextPos) from 0 to GetLastPosition() inclusive. The positions
  40. correspond to the gaps between the letters so the position 0 is just
  41. before the first character and the last position is the one beyond the last
  42. character. For an empty text control GetLastPosition() returns 0.
  43. Lines and columns returned/accepted by XYToPosition() and PositionToXY()
  44. start from 0. The y coordinate is a LINE, not a ROW. Columns correspond to
  45. the characters, the first column of a line is the first character in it,
  46. the last one is length(line text). For compatibility, again, lines and
  47. columns are also longs.
  48. When translating lines/column coordinates to/from positions, the line and
  49. column give the character after the given position. Thus, GetLastPosition()
  50. doesn't have any corresponding column.
  51. An example of positions and lines/columns for a control without wrapping
  52. containing the text "Hello, Universe!\nGoodbye"
  53. 1 1 1 1 1 1 1
  54. pos: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
  55. H e l l o , U n i v e r s e ! line 0
  56. col: 0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1
  57. 0 1 2 3 4 5
  58. pos: 1 1 1 2 2 2 2 2
  59. 7 8 9 0 1 2 3 4
  60. G o o d b y e line 1
  61. col: 0 1 2 3 4 5 6
  62. The same example for a control with line wrap assuming "Universe" is too
  63. long to fit on the same line with "Hello,":
  64. pos: 0 1 2 3 4 5
  65. H e l l o , line 0 (row 0)
  66. col: 0 1 2 3 4 5
  67. 1 1 1 1 1 1 1
  68. pos: 6 7 8 9 0 1 2 3 4 5 6
  69. U n i v e r s e ! line 0 (row 1)
  70. col: 6 7 8 9 1 1 1 1 1 1
  71. 0 1 2 3 4 5
  72. (line 1 == row 2 same as above)
  73. Note that there is still the same number of columns and positions and that
  74. there is no (logical) position at the end of the first ROW. This position
  75. is identified with the preceding one (which is not how Windows does it: it
  76. identifies it with the next one, i.e. the first position of the next line,
  77. but much more logical IMHO).
  78. */
  79. /*
  80. Search for "OPT" for possible optimizations
  81. A possible global optimization would be to always store the coords in the
  82. text in triplets (pos, col, line) and update them simultaneously instead of
  83. recalculating col and line from pos each time it is needed. Currently we
  84. only do it for the current position but we might also do it for the
  85. selection start and end.
  86. */
  87. // ============================================================================
  88. // declarations
  89. // ============================================================================
  90. // ----------------------------------------------------------------------------
  91. // headers
  92. // ----------------------------------------------------------------------------
  93. #include "wx/wxprec.h"
  94. #ifdef __BORLANDC__
  95. #pragma hdrstop
  96. #endif
  97. #if wxUSE_TEXTCTRL
  98. #include "wx/textctrl.h"
  99. #ifndef WX_PRECOMP
  100. #include "wx/log.h"
  101. #include "wx/dcclient.h"
  102. #include "wx/validate.h"
  103. #include "wx/dataobj.h"
  104. #endif
  105. #include <ctype.h>
  106. #include "wx/clipbrd.h"
  107. #include "wx/textfile.h"
  108. #include "wx/caret.h"
  109. #include "wx/univ/inphand.h"
  110. #include "wx/univ/renderer.h"
  111. #include "wx/univ/colschem.h"
  112. #include "wx/univ/theme.h"
  113. #include "wx/cmdproc.h"
  114. #if wxDEBUG_LEVEL >= 2
  115. // turn extra wxTextCtrl-specific debugging on/off
  116. #define WXDEBUG_TEXT
  117. // turn wxTextCtrl::Replace() debugging on (slows down code a *lot*!)
  118. #define WXDEBUG_TEXT_REPLACE
  119. #endif // wxDEBUG_LEVEL >= 2
  120. // wxStringTokenize only needed for debug checks
  121. #ifdef WXDEBUG_TEXT_REPLACE
  122. #include "wx/tokenzr.h"
  123. #endif // WXDEBUG_TEXT_REPLACE
  124. // ----------------------------------------------------------------------------
  125. // wxStdTextCtrlInputHandler: this control handles only the mouse/kbd actions
  126. // common to Win32 and GTK, platform-specific things are implemented elsewhere
  127. // ----------------------------------------------------------------------------
  128. class WXDLLEXPORT wxStdTextCtrlInputHandler : public wxStdInputHandler
  129. {
  130. public:
  131. wxStdTextCtrlInputHandler(wxInputHandler *inphand);
  132. virtual bool HandleKey(wxInputConsumer *consumer,
  133. const wxKeyEvent& event,
  134. bool pressed);
  135. virtual bool HandleMouse(wxInputConsumer *consumer,
  136. const wxMouseEvent& event);
  137. virtual bool HandleMouseMove(wxInputConsumer *consumer,
  138. const wxMouseEvent& event);
  139. virtual bool HandleFocus(wxInputConsumer *consumer, const wxFocusEvent& event);
  140. protected:
  141. // get the position of the mouse click
  142. static wxTextPos HitTest(const wxTextCtrl *text, const wxPoint& pos);
  143. // capture data
  144. wxTextCtrl *m_winCapture;
  145. };
  146. // ----------------------------------------------------------------------------
  147. // private functions
  148. // ----------------------------------------------------------------------------
  149. // exchange two positions so that from is always less than or equal to to
  150. static inline void OrderPositions(wxTextPos& from, wxTextPos& to)
  151. {
  152. if ( from > to )
  153. {
  154. wxTextPos tmp = from;
  155. from = to;
  156. to = tmp;
  157. }
  158. }
  159. // ----------------------------------------------------------------------------
  160. // constants
  161. // ----------------------------------------------------------------------------
  162. // names of text ctrl commands
  163. #define wxTEXT_COMMAND_INSERT wxT("insert")
  164. #define wxTEXT_COMMAND_REMOVE wxT("remove")
  165. // the value which is never used for text position, even not -1 which is
  166. // sometimes used for some special meaning
  167. static const wxTextPos INVALID_POS_VALUE = wxInvalidTextCoord;
  168. // overlap between pages (when using PageUp/Dn) in lines
  169. static const size_t PAGE_OVERLAP_IN_LINES = 1;
  170. // ----------------------------------------------------------------------------
  171. // private data of wxTextCtrl
  172. // ----------------------------------------------------------------------------
  173. // the data only used by single line text controls
  174. struct wxTextSingleLineData
  175. {
  176. // the position of the first visible pixel and the first visible column
  177. wxCoord m_ofsHorz;
  178. wxTextCoord m_colStart;
  179. // and the last ones (m_posLastVisible is the width but m_colLastVisible
  180. // is an absolute value)
  181. wxCoord m_posLastVisible;
  182. wxTextCoord m_colLastVisible;
  183. // def ctor
  184. wxTextSingleLineData()
  185. {
  186. m_colStart = 0;
  187. m_ofsHorz = 0;
  188. m_colLastVisible = -1;
  189. m_posLastVisible = -1;
  190. }
  191. };
  192. // the data only used by multi line text controls
  193. struct wxTextMultiLineData
  194. {
  195. // the lines of text
  196. wxArrayString m_lines;
  197. // the current ranges of the scrollbars
  198. int m_scrollRangeX,
  199. m_scrollRangeY;
  200. // should we adjust the horz/vert scrollbar?
  201. bool m_updateScrollbarX,
  202. m_updateScrollbarY;
  203. // the max line length in pixels
  204. wxCoord m_widthMax;
  205. // the index of the line which has the length of m_widthMax
  206. wxTextCoord m_lineLongest;
  207. // the rect in which text appears: it is even less than m_rectText because
  208. // only the last _complete_ line is shown, hence there is an unoccupied
  209. // horizontal band at the bottom of it
  210. wxRect m_rectTextReal;
  211. // the x-coordinate of the caret before we started moving it vertically:
  212. // this is used to ensure that moving the caret up and then down will
  213. // return it to the same position as if we always round it in one direction
  214. // we would shift it in that direction
  215. //
  216. // when m_xCaret == -1, we don't have any remembered position
  217. wxCoord m_xCaret;
  218. // the def ctor
  219. wxTextMultiLineData()
  220. {
  221. m_scrollRangeX =
  222. m_scrollRangeY = 0;
  223. m_updateScrollbarX =
  224. m_updateScrollbarY = false;
  225. m_widthMax = -1;
  226. m_lineLongest = 0;
  227. m_xCaret = -1;
  228. }
  229. };
  230. // the data only used by multi line text controls in line wrap mode
  231. class wxWrappedLineData
  232. {
  233. // these functions set all our values, so give them access to them
  234. friend void wxTextCtrl::LayoutLine(wxTextCoord line,
  235. wxWrappedLineData& lineData) const;
  236. friend void wxTextCtrl::LayoutLines(wxTextCoord) const;
  237. public:
  238. // def ctor
  239. wxWrappedLineData()
  240. {
  241. m_rowFirst = -1;
  242. }
  243. // get the start of any row (remember that accessing m_rowsStart doesn't work
  244. // for the first one)
  245. wxTextCoord GetRowStart(wxTextCoord row) const
  246. {
  247. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  248. return row ? m_rowsStart[row - 1] : 0;
  249. }
  250. // get the length of the row (using the total line length which we don't
  251. // have here but need to calculate the length of the last row, so it must
  252. // be given to us)
  253. wxTextCoord GetRowLength(wxTextCoord row, wxTextCoord lenLine) const
  254. {
  255. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  256. // note that m_rowsStart[row] is the same as GetRowStart(row + 1) (but
  257. // slightly more efficient) and lenLine is the same as the start of the
  258. // first row of the next line
  259. return ((size_t)row == m_rowsStart.GetCount() ? lenLine : m_rowsStart[row])
  260. - GetRowStart(row);
  261. }
  262. // return the width of the row in pixels
  263. wxCoord GetRowWidth(wxTextCoord row) const
  264. {
  265. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  266. return m_rowsWidth[row];
  267. }
  268. // return the number of rows
  269. size_t GetRowCount() const
  270. {
  271. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  272. return m_rowsStart.GetCount() + 1;
  273. }
  274. // return the number of additional (i.e. after the first one) rows
  275. size_t GetExtraRowCount() const
  276. {
  277. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  278. return m_rowsStart.GetCount();
  279. }
  280. // return the first row of this line
  281. wxTextCoord GetFirstRow() const
  282. {
  283. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  284. return m_rowFirst;
  285. }
  286. // return the first row of the next line
  287. wxTextCoord GetNextRow() const
  288. {
  289. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  290. return m_rowFirst + m_rowsStart.GetCount() + 1;
  291. }
  292. // this just provides direct access to m_rowsStart aerray for efficiency
  293. wxTextCoord GetExtraRowStart(wxTextCoord row) const
  294. {
  295. wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
  296. return m_rowsStart[row];
  297. }
  298. // this code is unused any longer
  299. #if 0
  300. // return true if the column is in the start of the last row (hence the row
  301. // it is in is not wrapped)
  302. bool IsLastRow(wxTextCoord colRowStart) const
  303. {
  304. return colRowStart == GetRowStart(m_rowsStart.GetCount());
  305. }
  306. // return true if the column is the last column of the row starting in
  307. // colRowStart
  308. bool IsLastColInRow(wxTextCoord colRowStart,
  309. wxTextCoord colRowEnd,
  310. wxTextCoord lenLine) const
  311. {
  312. // find the row which starts with colRowStart
  313. size_t nRows = GetRowCount();
  314. for ( size_t n = 0; n < nRows; n++ )
  315. {
  316. if ( GetRowStart(n) == colRowStart )
  317. {
  318. wxTextCoord colNextRowStart = n == nRows - 1
  319. ? lenLine
  320. : GetRowStart(n + 1);
  321. wxASSERT_MSG( colRowEnd < colNextRowStart,
  322. wxT("this column is not in this row at all!") );
  323. return colRowEnd == colNextRowStart - 1;
  324. }
  325. }
  326. // caller got it wrong
  327. wxFAIL_MSG( wxT("this column is not in the start of the row!") );
  328. return false;
  329. }
  330. #endif // 0
  331. // is this row the last one in its line?
  332. bool IsLastRow(wxTextCoord row) const
  333. {
  334. return (size_t)row == GetExtraRowCount();
  335. }
  336. // the line is valid if it had been laid out correctly: note that just
  337. // shiwting the line (because one of previous lines changed) doesn't make
  338. // it invalid
  339. bool IsValid() const { return !m_rowsWidth.IsEmpty(); }
  340. // invalidating line will relayout it
  341. void Invalidate() { m_rowsWidth.Empty(); }
  342. private:
  343. // for each line we remember the starting columns of all its rows after the
  344. // first one (which always starts at 0), i.e. if a line is wrapped twice
  345. // (== takes 3 rows) its m_rowsStart[0] may be 10 and m_rowsStart[1] == 15
  346. wxArrayLong m_rowsStart;
  347. // and the width of each row in pixels (this array starts from 0, as usual)
  348. wxArrayInt m_rowsWidth;
  349. // and also its starting row (0 for the first line, first lines'
  350. // m_rowsStart.GetCount() + 1 for the second &c): it is set to -1 initially
  351. // and this means that the struct hadn't yet been initialized
  352. wxTextCoord m_rowFirst;
  353. // the last modification "time"-stamp used by LayoutLines()
  354. size_t m_timestamp;
  355. };
  356. WX_DECLARE_OBJARRAY(wxWrappedLineData, wxArrayWrappedLinesData);
  357. #include "wx/arrimpl.cpp"
  358. WX_DEFINE_OBJARRAY(wxArrayWrappedLinesData);
  359. struct wxTextWrappedData : public wxTextMultiLineData
  360. {
  361. // the width of the column to the right of the text rect used for the
  362. // indicator mark display for the wrapped lines
  363. wxCoord m_widthMark;
  364. // the data for each line
  365. wxArrayWrappedLinesData m_linesData;
  366. // flag telling us to recalculate all starting rows starting from this line
  367. // (if it is -1, we don't have to recalculate anything) - it is set when
  368. // the number of the rows in the middle of the control changes
  369. wxTextCoord m_rowFirstInvalid;
  370. // the current timestamp used by LayoutLines()
  371. size_t m_timestamp;
  372. // invalidate starting rows of all lines (NOT rows!) after this one
  373. void InvalidateLinesBelow(wxTextCoord line)
  374. {
  375. if ( m_rowFirstInvalid == -1 || m_rowFirstInvalid > line )
  376. {
  377. m_rowFirstInvalid = line;
  378. }
  379. }
  380. // check if this line is valid: i.e. before the first invalid one
  381. bool IsValidLine(wxTextCoord line) const
  382. {
  383. return ((m_rowFirstInvalid == -1) || (line < m_rowFirstInvalid)) &&
  384. m_linesData[line].IsValid();
  385. }
  386. // def ctor
  387. wxTextWrappedData()
  388. {
  389. m_widthMark = 0;
  390. m_rowFirstInvalid = -1;
  391. m_timestamp = 0;
  392. }
  393. };
  394. // ----------------------------------------------------------------------------
  395. // private classes for undo/redo management
  396. // ----------------------------------------------------------------------------
  397. /*
  398. We use custom versions of wxWidgets command processor to implement undo/redo
  399. as we want to avoid storing the backpointer to wxTextCtrl in wxCommand
  400. itself: this is a waste of memory as all commands in the given command
  401. processor always have the same associated wxTextCtrl and so it makes sense
  402. to store the backpointer there.
  403. As for the rest of the implementation, it's fairly standard: we have 2
  404. command classes corresponding to adding and removing text.
  405. */
  406. // a command corresponding to a wxTextCtrl action
  407. class wxTextCtrlCommand : public wxCommand
  408. {
  409. public:
  410. wxTextCtrlCommand(const wxString& name) : wxCommand(true, name) { }
  411. // we don't use these methods as they don't make sense for us as we need a
  412. // wxTextCtrl to be applied
  413. virtual bool Do() { wxFAIL_MSG(wxT("shouldn't be called")); return false; }
  414. virtual bool Undo() { wxFAIL_MSG(wxT("shouldn't be called")); return false; }
  415. // instead, our command processor uses these methods
  416. virtual bool Do(wxTextCtrl *text) = 0;
  417. virtual bool Undo(wxTextCtrl *text) = 0;
  418. };
  419. // insert text command
  420. class wxTextCtrlInsertCommand : public wxTextCtrlCommand
  421. {
  422. public:
  423. wxTextCtrlInsertCommand(const wxString& textToInsert)
  424. : wxTextCtrlCommand(wxTEXT_COMMAND_INSERT), m_text(textToInsert)
  425. {
  426. m_from = -1;
  427. }
  428. // combine the 2 commands together
  429. void Append(wxTextCtrlInsertCommand *other);
  430. virtual bool CanUndo() const;
  431. virtual bool Do(wxTextCtrl *text);
  432. virtual bool Do() { return wxTextCtrlCommand::Do(); }
  433. virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
  434. virtual bool Undo(wxTextCtrl *text);
  435. private:
  436. // the text we insert
  437. wxString m_text;
  438. // the position where we inserted the text
  439. wxTextPos m_from;
  440. };
  441. // remove text command
  442. class wxTextCtrlRemoveCommand : public wxTextCtrlCommand
  443. {
  444. public:
  445. wxTextCtrlRemoveCommand(wxTextPos from, wxTextPos to)
  446. : wxTextCtrlCommand(wxTEXT_COMMAND_REMOVE)
  447. {
  448. m_from = from;
  449. m_to = to;
  450. }
  451. virtual bool CanUndo() const;
  452. virtual bool Do(wxTextCtrl *text);
  453. virtual bool Do() { return wxTextCtrlCommand::Do(); }
  454. virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
  455. virtual bool Undo(wxTextCtrl *text);
  456. private:
  457. // the range of text to delete
  458. wxTextPos m_from,
  459. m_to;
  460. // the text which was deleted when this command was Do()ne
  461. wxString m_textDeleted;
  462. };
  463. // a command processor for a wxTextCtrl
  464. class wxTextCtrlCommandProcessor : public wxCommandProcessor
  465. {
  466. public:
  467. wxTextCtrlCommandProcessor(wxTextCtrl *text)
  468. {
  469. m_compressInserts = false;
  470. m_text = text;
  471. }
  472. // override Store() to compress multiple wxTextCtrlInsertCommand into one
  473. virtual void Store(wxCommand *command);
  474. // stop compressing insert commands when this is called
  475. void StopCompressing() { m_compressInserts = false; }
  476. // accessors
  477. wxTextCtrl *GetTextCtrl() const { return m_text; }
  478. bool IsCompressing() const { return m_compressInserts; }
  479. protected:
  480. virtual bool DoCommand(wxCommand& cmd)
  481. { return ((wxTextCtrlCommand &)cmd).Do(m_text); }
  482. virtual bool UndoCommand(wxCommand& cmd)
  483. { return ((wxTextCtrlCommand &)cmd).Undo(m_text); }
  484. // check if this command is a wxTextCtrlInsertCommand and return it casted
  485. // to the right type if it is or NULL otherwise
  486. wxTextCtrlInsertCommand *IsInsertCommand(wxCommand *cmd);
  487. private:
  488. // the control we're associated with
  489. wxTextCtrl *m_text;
  490. // if the flag is true we're compressing subsequent insert commands into
  491. // one so that the entire typing could be undone in one call to Undo()
  492. bool m_compressInserts;
  493. };
  494. // ============================================================================
  495. // implementation
  496. // ============================================================================
  497. BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
  498. EVT_CHAR(wxTextCtrl::OnChar)
  499. EVT_SIZE(wxTextCtrl::OnSize)
  500. END_EVENT_TABLE()
  501. // ----------------------------------------------------------------------------
  502. // creation
  503. // ----------------------------------------------------------------------------
  504. void wxTextCtrl::Init()
  505. {
  506. m_selAnchor =
  507. m_selStart =
  508. m_selEnd = -1;
  509. m_isModified = false;
  510. m_isEditable = true;
  511. m_wrapLines = false;
  512. m_posLast =
  513. m_curPos =
  514. m_curCol =
  515. m_curRow = 0;
  516. m_heightLine =
  517. m_widthAvg = -1;
  518. // init the undo manager
  519. m_cmdProcessor = new wxTextCtrlCommandProcessor(this);
  520. // no data yet
  521. m_data.data = NULL;
  522. }
  523. bool wxTextCtrl::Create(wxWindow *parent,
  524. wxWindowID id,
  525. const wxString& value,
  526. const wxPoint& pos,
  527. const wxSize& size,
  528. long style,
  529. const wxValidator& validator,
  530. const wxString &name)
  531. {
  532. if ( style & wxTE_MULTILINE )
  533. {
  534. // for compatibility with wxMSW we create the controls with vertical
  535. // scrollbar always shown unless they have wxTE_RICH style (because
  536. // Windows text controls always has vert scrollbar but richedit one
  537. // doesn't)
  538. if ( !(style & wxTE_RICH) )
  539. {
  540. style |= wxALWAYS_SHOW_SB;
  541. }
  542. // wrapping style: wxTE_DONTWRAP == wxHSCROLL so if it's _not_ given,
  543. // we won't have horizontal scrollbar automatically, no need to do
  544. // anything
  545. // TODO: support wxTE_NO_VSCROLL (?)
  546. // create data object for normal multiline or for controls with line
  547. // wrap as needed
  548. if ( style & wxHSCROLL )
  549. {
  550. m_data.mdata = new wxTextMultiLineData;
  551. }
  552. else // we must wrap lines if we don't have horizontal scrollbar
  553. {
  554. // NB: we can't rely on HasFlag(wxHSCROLL) as the flags can change
  555. // later and even wxWindow::Create() itself temporarily resets
  556. // wxHSCROLL in wxUniv, so remember that we have a wrapped data
  557. // and not just a multi line data in a separate variable
  558. m_wrapLines = true;
  559. m_data.wdata = new wxTextWrappedData;
  560. }
  561. }
  562. else
  563. {
  564. // this doesn't make sense for single line controls
  565. style &= ~wxHSCROLL;
  566. // create data object for single line controls
  567. m_data.sdata = new wxTextSingleLineData;
  568. }
  569. #if wxUSE_TWO_WINDOWS
  570. if ((style & wxBORDER_MASK) == 0)
  571. style |= wxBORDER_SUNKEN;
  572. #endif
  573. if ( !wxControl::Create(parent, id, pos, size, style,
  574. validator, name) )
  575. {
  576. return false;
  577. }
  578. SetCursor(wxCURSOR_IBEAM);
  579. if ( style & wxTE_MULTILINE )
  580. {
  581. // we should always have at least one line in a multiline control
  582. MData().m_lines.Add(wxEmptyString);
  583. if ( !(style & wxHSCROLL) )
  584. {
  585. WData().m_linesData.Add(new wxWrappedLineData);
  586. WData().InvalidateLinesBelow(0);
  587. }
  588. // we might support it but it's quite useless and other ports don't
  589. // support it anyhow
  590. wxASSERT_MSG( !(style & wxTE_PASSWORD),
  591. wxT("wxTE_PASSWORD can't be used with multiline ctrls") );
  592. }
  593. RecalcFontMetrics();
  594. SetValue(value);
  595. SetInitialSize(size);
  596. m_isEditable = !(style & wxTE_READONLY);
  597. CreateCaret();
  598. InitInsertionPoint();
  599. // we can't show caret right now as we're not shown yet and so it would
  600. // result in garbage on the screen - we'll do it after first OnPaint()
  601. m_hasCaret = false;
  602. CreateInputHandler(wxINP_HANDLER_TEXTCTRL);
  603. wxSizeEvent sizeEvent(GetSize(), GetId());
  604. GetEventHandler()->ProcessEvent(sizeEvent);
  605. return true;
  606. }
  607. wxTextCtrl::~wxTextCtrl()
  608. {
  609. delete m_cmdProcessor;
  610. if ( m_data.data )
  611. {
  612. if ( IsSingleLine() )
  613. delete m_data.sdata;
  614. else if ( WrapLines() )
  615. delete m_data.wdata;
  616. else
  617. delete m_data.mdata;
  618. }
  619. }
  620. // ----------------------------------------------------------------------------
  621. // set/get the value
  622. // ----------------------------------------------------------------------------
  623. void wxTextCtrl::DoSetValue(const wxString& value, int flags)
  624. {
  625. if ( value != GetValue() )
  626. {
  627. EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
  628. Replace(0, GetLastPosition(), value);
  629. if ( IsSingleLine() )
  630. {
  631. SetInsertionPoint(0);
  632. }
  633. }
  634. else // nothing changed
  635. {
  636. // still send event for consistency
  637. if ( flags & SetValue_SendEvent )
  638. SendTextUpdatedEvent();
  639. }
  640. }
  641. const wxArrayString& wxTextCtrl::GetLines() const
  642. {
  643. return MData().m_lines;
  644. }
  645. size_t wxTextCtrl::GetLineCount() const
  646. {
  647. return MData().m_lines.GetCount();
  648. }
  649. wxString wxTextCtrl::DoGetValue() const
  650. {
  651. // for multiline controls we don't always store the total value but only
  652. // recompute it when asked - and to invalidate it we just empty it in
  653. // Replace()
  654. if ( !IsSingleLine() && m_value.empty() )
  655. {
  656. // recalculate: note that we always do it for empty multilien control,
  657. // but then it's so quick that it's not important
  658. // the first line is special as there is no \n before it, so it's
  659. // outside the loop
  660. const wxArrayString& lines = GetLines();
  661. wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
  662. self->m_value << lines[0u];
  663. size_t count = lines.GetCount();
  664. for ( size_t n = 1; n < count; n++ )
  665. {
  666. self->m_value << wxT('\n') << lines[n];
  667. }
  668. }
  669. return m_value;
  670. }
  671. void wxTextCtrl::Clear()
  672. {
  673. SetValue(wxEmptyString);
  674. }
  675. bool wxTextCtrl::ReplaceLine(wxTextCoord line,
  676. const wxString& text)
  677. {
  678. if ( WrapLines() )
  679. {
  680. // first, we have to relayout the line entirely
  681. //
  682. // OPT: we might try not to recalc the unchanged part of line
  683. wxWrappedLineData& lineData = WData().m_linesData[line];
  684. // if we had some number of rows before, use this number, otherwise
  685. // just make sure that the test below (rowsNew != rowsOld) will be true
  686. int rowsOld;
  687. if ( lineData.IsValid() )
  688. {
  689. rowsOld = lineData.GetExtraRowCount();
  690. }
  691. else // line wasn't laid out yet
  692. {
  693. // assume it changed entirely as we can't do anything better
  694. rowsOld = -1;
  695. }
  696. // now change the line
  697. MData().m_lines[line] = text;
  698. // OPT: we choose to lay it out immediately instead of delaying it
  699. // until it is needed because it allows us to avoid invalidating
  700. // lines further down if the number of rows didn't change, but
  701. // maybe we can improve this even further?
  702. LayoutLine(line, lineData);
  703. int rowsNew = lineData.GetExtraRowCount();
  704. if ( rowsNew != rowsOld )
  705. {
  706. // we have to update the line wrap marks as this is normally done
  707. // by LayoutLines() which we bypassed by calling LayoutLine()
  708. // directly
  709. wxTextCoord rowFirst = lineData.GetFirstRow(),
  710. rowCount = wxMax(rowsOld, rowsNew);
  711. RefreshLineWrapMarks(rowFirst, rowFirst + rowCount);
  712. // next, if this is not the last line, as the number of rows in it
  713. // changed, we need to shift all the lines below it
  714. if ( (size_t)line < WData().m_linesData.GetCount() )
  715. {
  716. // number of rows changed shifting all lines below
  717. WData().InvalidateLinesBelow(line + 1);
  718. }
  719. // the number of rows changed
  720. return true;
  721. }
  722. }
  723. else // no line wrap
  724. {
  725. MData().m_lines[line] = text;
  726. }
  727. // the number of rows didn't change
  728. return false;
  729. }
  730. void wxTextCtrl::RemoveLine(wxTextCoord line)
  731. {
  732. MData().m_lines.RemoveAt(line);
  733. if ( WrapLines() )
  734. {
  735. // we need to recalculate all the starting rows from this line, but we
  736. // can avoid doing it if this line was never calculated: this means
  737. // that we will recalculate all lines below it anyhow later if needed
  738. if ( WData().IsValidLine(line) )
  739. {
  740. WData().InvalidateLinesBelow(line);
  741. }
  742. WData().m_linesData.RemoveAt(line);
  743. }
  744. }
  745. void wxTextCtrl::InsertLine(wxTextCoord line, const wxString& text)
  746. {
  747. MData().m_lines.Insert(text, line);
  748. if ( WrapLines() )
  749. {
  750. WData().m_linesData.Insert(new wxWrappedLineData, line);
  751. // invalidate everything below it
  752. WData().InvalidateLinesBelow(line);
  753. }
  754. }
  755. void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
  756. {
  757. wxTextCoord colStart, colEnd,
  758. lineStart, lineEnd;
  759. if ( (from > to) ||
  760. !PositionToXY(from, &colStart, &lineStart) ||
  761. !PositionToXY(to, &colEnd, &lineEnd) )
  762. {
  763. wxFAIL_MSG(wxT("invalid range in wxTextCtrl::Replace"));
  764. return;
  765. }
  766. #ifdef WXDEBUG_TEXT_REPLACE
  767. // a straighforward (but very inefficient) way of calculating what the new
  768. // value should be
  769. wxString textTotal = GetValue();
  770. wxString textTotalNew(textTotal, (size_t)from);
  771. textTotalNew += text;
  772. if ( (size_t)to < textTotal.length() )
  773. textTotalNew += textTotal.c_str() + (size_t)to;
  774. #endif // WXDEBUG_TEXT_REPLACE
  775. // remember the old selection and reset it immediately: we must do it
  776. // before calling Refresh(anything) as, at least under GTK, this leads to
  777. // an _immediate_ repaint (under MSW it is delayed) and hence parts of
  778. // text would be redrawn as selected if we didn't reset the selection
  779. int selStartOld = m_selStart,
  780. selEndOld = m_selEnd;
  781. m_selStart =
  782. m_selEnd = -1;
  783. if ( IsSingleLine() )
  784. {
  785. // replace the part of the text with the new value
  786. wxString valueNew(m_value, (size_t)from);
  787. // remember it for later use
  788. wxCoord startNewText = GetTextWidth(valueNew);
  789. valueNew += text;
  790. if ( (size_t)to < m_value.length() )
  791. {
  792. valueNew += m_value.c_str() + (size_t)to;
  793. }
  794. // we usually refresh till the end of line except of the most common case
  795. // when some text is appended to the end of the string in which case we
  796. // refresh just it
  797. wxCoord widthNewText;
  798. if ( (size_t)from < m_value.length() )
  799. {
  800. // refresh till the end of line
  801. widthNewText = 0;
  802. }
  803. else // text appended, not replaced
  804. {
  805. // refresh only the new text
  806. widthNewText = GetTextWidth(text);
  807. }
  808. m_value = valueNew;
  809. // force SData().m_colLastVisible update
  810. SData().m_colLastVisible = -1;
  811. // repaint
  812. RefreshPixelRange(0, startNewText, widthNewText);
  813. }
  814. else // multiline
  815. {
  816. //OPT: special case for replacements inside single line?
  817. /*
  818. Join all the lines in the replacement range into one string, then
  819. replace a part of it with the new text and break it into lines again.
  820. */
  821. // (0) we want to know if this replacement changes the number of rows
  822. // as if it does we need to refresh everything below the changed
  823. // text (it will be shifted...) and we can avoid it if there is no
  824. // row relayout
  825. bool rowsNumberChanged = false;
  826. // (1) join lines
  827. const wxArrayString& linesOld = GetLines();
  828. wxString textOrig;
  829. wxTextCoord line;
  830. for ( line = lineStart; line <= lineEnd; line++ )
  831. {
  832. if ( line > lineStart )
  833. {
  834. // from the previous line
  835. textOrig += wxT('\n');
  836. }
  837. textOrig += linesOld[line];
  838. }
  839. // we need to append the '\n' for the last line unless there is no
  840. // following line
  841. size_t countOld = linesOld.GetCount();
  842. // (2) replace text in the combined string
  843. // (2a) leave the part before replaced area unchanged
  844. wxString textNew(textOrig, colStart);
  845. // these values will be used to refresh the changed area below
  846. wxCoord widthNewText,
  847. startNewText = GetTextWidth(textNew);
  848. if ( (size_t)colStart == linesOld[lineStart].length() )
  849. {
  850. // text appended, refresh just enough to show the new text
  851. widthNewText = GetTextWidth(text.BeforeFirst(wxT('\n')));
  852. }
  853. else // text inserted, refresh till the end of line
  854. {
  855. widthNewText = 0;
  856. }
  857. // (2b) insert new text
  858. textNew += text;
  859. // (2c) and append the end of the old text
  860. // adjust for index shift: to is relative to colStart, not 0
  861. size_t toRel = (size_t)((to - from) + colStart);
  862. if ( toRel < textOrig.length() )
  863. {
  864. textNew += textOrig.c_str() + toRel;
  865. }
  866. // (3) break it into lines
  867. wxArrayString lines;
  868. const wxChar *curLineStart = textNew.c_str();
  869. for ( const wxChar *p = textNew.c_str(); ; p++ )
  870. {
  871. // end of line/text?
  872. if ( !*p || *p == wxT('\n') )
  873. {
  874. lines.Add(wxString(curLineStart, p));
  875. if ( !*p )
  876. break;
  877. curLineStart = p + 1;
  878. }
  879. }
  880. #ifdef WXDEBUG_TEXT_REPLACE
  881. // (3a) all empty tokens should be counted as replacing with "foo" and
  882. // with "foo\n" should have different effects
  883. wxArrayString lines2 = wxStringTokenize(textNew, wxT("\n"),
  884. wxTOKEN_RET_EMPTY_ALL);
  885. if ( lines2.IsEmpty() )
  886. {
  887. lines2.Add(wxEmptyString);
  888. }
  889. wxASSERT_MSG( lines.GetCount() == lines2.GetCount(),
  890. wxT("Replace() broken") );
  891. for ( size_t n = 0; n < lines.GetCount(); n++ )
  892. {
  893. wxASSERT_MSG( lines[n] == lines2[n], wxT("Replace() broken") );
  894. }
  895. #endif // WXDEBUG_TEXT_REPLACE
  896. // (3b) special case: if we replace everything till the end we need to
  897. // keep an empty line or the lines would disappear completely
  898. // (this also takes care of never leaving m_lines empty)
  899. if ( ((size_t)lineEnd == countOld - 1) && lines.IsEmpty() )
  900. {
  901. lines.Add(wxEmptyString);
  902. }
  903. size_t nReplaceCount = lines.GetCount(),
  904. nReplaceLine = 0;
  905. // (4) merge into the array
  906. // (4a) replace
  907. for ( line = lineStart; line <= lineEnd; line++, nReplaceLine++ )
  908. {
  909. if ( nReplaceLine < nReplaceCount )
  910. {
  911. // we have the replacement line for this one
  912. if ( ReplaceLine(line, lines[nReplaceLine]) )
  913. {
  914. rowsNumberChanged = true;
  915. }
  916. UpdateMaxWidth(line);
  917. }
  918. else // no more replacement lines
  919. {
  920. // (4b) delete all extra lines (note that we need to delete
  921. // them backwards because indices shift while we do it)
  922. bool deletedLongestLine = false;
  923. for ( wxTextCoord lineDel = lineEnd; lineDel >= line; lineDel-- )
  924. {
  925. if ( lineDel == MData().m_lineLongest )
  926. {
  927. // we will need to recalc the max line width
  928. deletedLongestLine = true;
  929. }
  930. RemoveLine(lineDel);
  931. }
  932. if ( deletedLongestLine )
  933. {
  934. RecalcMaxWidth();
  935. }
  936. // even the line number changed
  937. rowsNumberChanged = true;
  938. // update line to exit the loop
  939. line = lineEnd + 1;
  940. }
  941. }
  942. // (4c) insert the new lines
  943. if ( nReplaceLine < nReplaceCount )
  944. {
  945. // even the line number changed
  946. rowsNumberChanged = true;
  947. do
  948. {
  949. InsertLine(++lineEnd, lines[nReplaceLine++]);
  950. UpdateMaxWidth(lineEnd);
  951. }
  952. while ( nReplaceLine < nReplaceCount );
  953. }
  954. // (5) now refresh the changed area
  955. // update the (cached) last position first as refresh functions use it
  956. m_posLast += text.length() - to + from;
  957. // we may optimize refresh if the number of rows didn't change - but if
  958. // it did we have to refresh everything below the part we chanegd as
  959. // well as it might have moved
  960. if ( !rowsNumberChanged )
  961. {
  962. // refresh the line we changed
  963. if ( !WrapLines() )
  964. {
  965. RefreshPixelRange(lineStart++, startNewText, widthNewText);
  966. }
  967. else
  968. {
  969. //OPT: we shouldn't refresh the unchanged part of the line in
  970. // this case, but instead just refresh the tail of it - the
  971. // trouble is that we don't know here where does this tail
  972. // start
  973. }
  974. // number of rows didn't change, refresh the updated rows and the
  975. // last one
  976. if ( lineStart <= lineEnd )
  977. RefreshLineRange(lineStart, lineEnd);
  978. }
  979. else // rows number did change
  980. {
  981. if ( !WrapLines() )
  982. {
  983. // refresh only part of the first line
  984. RefreshPixelRange(lineStart++, startNewText, widthNewText);
  985. }
  986. //else: we have to refresh everything as some part of the text
  987. // could be in the previous row before but moved to the next
  988. // one now (due to word wrap)
  989. wxTextCoord lineEnd = GetLines().GetCount() - 1;
  990. if ( lineStart <= lineEnd )
  991. RefreshLineRange(lineStart, lineEnd);
  992. // refresh text rect left below
  993. RefreshLineRange(lineEnd + 1, 0);
  994. // the vert scrollbar might [dis]appear
  995. MData().m_updateScrollbarY = true;
  996. }
  997. // must recalculate it - will do later
  998. m_value.clear();
  999. }
  1000. #ifdef WXDEBUG_TEXT_REPLACE
  1001. // optimized code above should give the same result as straightforward
  1002. // computation in the beginning
  1003. wxASSERT_MSG( GetValue() == textTotalNew, wxT("error in Replace()") );
  1004. #endif // WXDEBUG_TEXT_REPLACE
  1005. // update the current position: note that we always put the cursor at the
  1006. // end of the replacement text
  1007. DoSetInsertionPoint(from + text.length());
  1008. // and the selection: this is complicated by the fact that selection coords
  1009. // must be first updated to reflect change in text coords, i.e. if we had
  1010. // selection from 17 to 19 and we just removed this range, we don't have to
  1011. // refresh anything, so we can't just use ClearSelection() here
  1012. if ( selStartOld != -1 )
  1013. {
  1014. // refresh the parst of the selection outside the changed text (which
  1015. // we already refreshed)
  1016. if ( selStartOld < from )
  1017. RefreshTextRange(selStartOld, from);
  1018. if ( to < selEndOld )
  1019. RefreshTextRange(to, selEndOld);
  1020. }
  1021. // now call it to do the rest (not related to refreshing)
  1022. ClearSelection();
  1023. SendTextUpdatedEventIfAllowed();
  1024. }
  1025. void wxTextCtrl::Remove(wxTextPos from, wxTextPos to)
  1026. {
  1027. // Replace() only works with correctly ordered arguments, so exchange them
  1028. // if necessary
  1029. OrderPositions(from, to);
  1030. Replace(from, to, wxEmptyString);
  1031. }
  1032. void wxTextCtrl::WriteText(const wxString& text)
  1033. {
  1034. // replace the selection with the new text
  1035. RemoveSelection();
  1036. Replace(m_curPos, m_curPos, text);
  1037. }
  1038. void wxTextCtrl::AppendText(const wxString& text)
  1039. {
  1040. SetInsertionPointEnd();
  1041. WriteText(text);
  1042. }
  1043. // ----------------------------------------------------------------------------
  1044. // current position
  1045. // ----------------------------------------------------------------------------
  1046. void wxTextCtrl::SetInsertionPoint(wxTextPos pos)
  1047. {
  1048. wxCHECK_RET( pos >= 0 && pos <= GetLastPosition(),
  1049. wxT("insertion point position out of range") );
  1050. // don't do anything if it didn't change
  1051. if ( pos != m_curPos )
  1052. {
  1053. DoSetInsertionPoint(pos);
  1054. }
  1055. if ( !IsSingleLine() )
  1056. {
  1057. // moving cursor should reset the stored abscissa (even if the cursor
  1058. // position didn't actually change!)
  1059. MData().m_xCaret = -1;
  1060. }
  1061. ClearSelection();
  1062. }
  1063. void wxTextCtrl::InitInsertionPoint()
  1064. {
  1065. // so far always put it in the beginning
  1066. DoSetInsertionPoint(0);
  1067. // this will also set the selection anchor correctly
  1068. ClearSelection();
  1069. }
  1070. void wxTextCtrl::MoveInsertionPoint(wxTextPos pos)
  1071. {
  1072. wxASSERT_MSG( pos >= 0 && pos <= GetLastPosition(),
  1073. wxT("DoSetInsertionPoint() can only be called with valid pos") );
  1074. m_curPos = pos;
  1075. PositionToXY(m_curPos, &m_curCol, &m_curRow);
  1076. }
  1077. void wxTextCtrl::DoSetInsertionPoint(wxTextPos pos)
  1078. {
  1079. MoveInsertionPoint(pos);
  1080. ShowPosition(pos);
  1081. }
  1082. void wxTextCtrl::SetInsertionPointEnd()
  1083. {
  1084. SetInsertionPoint(GetLastPosition());
  1085. }
  1086. wxTextPos wxTextCtrl::GetInsertionPoint() const
  1087. {
  1088. return m_curPos;
  1089. }
  1090. wxTextPos wxTextCtrl::GetLastPosition() const
  1091. {
  1092. wxTextPos pos;
  1093. if ( IsSingleLine() )
  1094. {
  1095. pos = m_value.length();
  1096. }
  1097. else // multiline
  1098. {
  1099. #ifdef WXDEBUG_TEXT
  1100. pos = 0;
  1101. size_t nLineCount = GetLineCount();
  1102. for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
  1103. {
  1104. // +1 is because the positions at the end of this line and of the
  1105. // start of the next one are different
  1106. pos += GetLines()[nLine].length() + 1;
  1107. }
  1108. if ( pos > 0 )
  1109. {
  1110. // the last position is at the end of the last line, not in the
  1111. // beginning of the next line after it
  1112. pos--;
  1113. }
  1114. // more probable reason of this would be to forget to update m_posLast
  1115. wxASSERT_MSG( pos == m_posLast, wxT("bug in GetLastPosition()") );
  1116. #endif // WXDEBUG_TEXT
  1117. pos = m_posLast;
  1118. }
  1119. return pos;
  1120. }
  1121. // ----------------------------------------------------------------------------
  1122. // selection
  1123. // ----------------------------------------------------------------------------
  1124. void wxTextCtrl::GetSelection(wxTextPos* from, wxTextPos* to) const
  1125. {
  1126. if ( from )
  1127. *from = m_selStart;
  1128. if ( to )
  1129. *to = m_selEnd;
  1130. }
  1131. wxString wxTextCtrl::GetSelectionText() const
  1132. {
  1133. wxString sel;
  1134. if ( HasSelection() )
  1135. {
  1136. if ( IsSingleLine() )
  1137. {
  1138. sel = m_value.Mid(m_selStart, m_selEnd - m_selStart);
  1139. }
  1140. else // multiline
  1141. {
  1142. wxTextCoord colStart, lineStart,
  1143. colEnd, lineEnd;
  1144. PositionToXY(m_selStart, &colStart, &lineStart);
  1145. PositionToXY(m_selEnd, &colEnd, &lineEnd);
  1146. // as always, we need to check for the special case when the start
  1147. // and end line are the same
  1148. if ( lineEnd == lineStart )
  1149. {
  1150. sel = GetLines()[lineStart].Mid(colStart, colEnd - colStart);
  1151. }
  1152. else // sel on multiple lines
  1153. {
  1154. // take the end of the first line
  1155. sel = GetLines()[lineStart].c_str() + colStart;
  1156. sel += wxT('\n');
  1157. // all intermediate ones
  1158. for ( wxTextCoord line = lineStart + 1; line < lineEnd; line++ )
  1159. {
  1160. sel << GetLines()[line] << wxT('\n');
  1161. }
  1162. // and the start of the last one
  1163. sel += GetLines()[lineEnd].Left(colEnd);
  1164. }
  1165. }
  1166. }
  1167. return sel;
  1168. }
  1169. void wxTextCtrl::SetSelection(wxTextPos from, wxTextPos to)
  1170. {
  1171. // selecting till -1 is the same as selecting to the end
  1172. if ( to == -1 )
  1173. {
  1174. // and selecting (-1, -1) range is the same as selecting everything, by
  1175. // convention
  1176. if ( from == -1 )
  1177. from = 0;
  1178. to = GetLastPosition();
  1179. }
  1180. if ( from == -1 || to == from )
  1181. {
  1182. ClearSelection();
  1183. }
  1184. else // valid sel range
  1185. {
  1186. // remember the 'to' position as the current position, used to move the
  1187. // caret there later
  1188. wxTextPos toOrig = to;
  1189. OrderPositions(from, to);
  1190. wxCHECK_RET( to <= GetLastPosition(),
  1191. wxT("invalid range in wxTextCtrl::SetSelection") );
  1192. if ( from != m_selStart || to != m_selEnd )
  1193. {
  1194. // we need to use temp vars as RefreshTextRange() may call DoDraw()
  1195. // directly and so m_selStart/End must be reset by then
  1196. wxTextPos selStartOld = m_selStart,
  1197. selEndOld = m_selEnd;
  1198. m_selStart = from;
  1199. m_selEnd = to;
  1200. wxLogTrace(wxT("text"), wxT("Selection range is %ld-%ld"),
  1201. m_selStart, m_selEnd);
  1202. // refresh only the part of text which became (un)selected if
  1203. // possible
  1204. if ( selStartOld == m_selStart )
  1205. {
  1206. RefreshTextRange(selEndOld, m_selEnd);
  1207. }
  1208. else if ( selEndOld == m_selEnd )
  1209. {
  1210. RefreshTextRange(m_selStart, selStartOld);
  1211. }
  1212. else
  1213. {
  1214. // OPT: could check for other cases too but it is probably not
  1215. // worth it as the two above are the most common ones
  1216. if ( selStartOld != -1 )
  1217. RefreshTextRange(selStartOld, selEndOld);
  1218. if ( m_selStart != -1 )
  1219. RefreshTextRange(m_selStart, m_selEnd);
  1220. }
  1221. // we need to fully repaint the invalidated areas of the window
  1222. // before scrolling it (from DoSetInsertionPoint which is typically
  1223. // called after SetSelection()), otherwise they may stay unpainted
  1224. m_targetWindow->Update();
  1225. }
  1226. //else: nothing to do
  1227. // the insertion point is put at the location where the caret was moved
  1228. DoSetInsertionPoint(toOrig);
  1229. }
  1230. }
  1231. void wxTextCtrl::ClearSelection()
  1232. {
  1233. if ( HasSelection() )
  1234. {
  1235. // we need to use temp vars as RefreshTextRange() may call DoDraw()
  1236. // directly (see above as well)
  1237. wxTextPos selStart = m_selStart,
  1238. selEnd = m_selEnd;
  1239. // no selection any more
  1240. m_selStart =
  1241. m_selEnd = -1;
  1242. // refresh the old selection
  1243. RefreshTextRange(selStart, selEnd);
  1244. }
  1245. // the anchor should be moved even if there was no selection previously
  1246. m_selAnchor = m_curPos;
  1247. }
  1248. void wxTextCtrl::RemoveSelection()
  1249. {
  1250. if ( !HasSelection() )
  1251. return;
  1252. Remove(m_selStart, m_selEnd);
  1253. }
  1254. bool wxTextCtrl::GetSelectedPartOfLine(wxTextCoord line,
  1255. wxTextPos *start, wxTextPos *end) const
  1256. {
  1257. if ( start )
  1258. *start = -1;
  1259. if ( end )
  1260. *end = -1;
  1261. if ( !HasSelection() )
  1262. {
  1263. // no selection at all, hence no selection in this line
  1264. return false;
  1265. }
  1266. wxTextCoord lineStart, colStart;
  1267. PositionToXY(m_selStart, &colStart, &lineStart);
  1268. if ( lineStart > line )
  1269. {
  1270. // this line is entirely above the selection
  1271. return false;
  1272. }
  1273. wxTextCoord lineEnd, colEnd;
  1274. PositionToXY(m_selEnd, &colEnd, &lineEnd);
  1275. if ( lineEnd < line )
  1276. {
  1277. // this line is entirely below the selection
  1278. return false;
  1279. }
  1280. if ( line == lineStart )
  1281. {
  1282. if ( start )
  1283. *start = colStart;
  1284. if ( end )
  1285. *end = lineEnd == lineStart ? colEnd : GetLineLength(line);
  1286. }
  1287. else if ( line == lineEnd )
  1288. {
  1289. if ( start )
  1290. *start = lineEnd == lineStart ? colStart : 0;
  1291. if ( end )
  1292. *end = colEnd;
  1293. }
  1294. else // the line is entirely inside the selection
  1295. {
  1296. if ( start )
  1297. *start = 0;
  1298. if ( end )
  1299. *end = GetLineLength(line);
  1300. }
  1301. return true;
  1302. }
  1303. // ----------------------------------------------------------------------------
  1304. // flags
  1305. // ----------------------------------------------------------------------------
  1306. bool wxTextCtrl::IsModified() const
  1307. {
  1308. return m_isModified;
  1309. }
  1310. bool wxTextCtrl::IsEditable() const
  1311. {
  1312. // disabled control can never be edited
  1313. return m_isEditable && IsEnabled();
  1314. }
  1315. void wxTextCtrl::MarkDirty()
  1316. {
  1317. m_isModified = true;
  1318. }
  1319. void wxTextCtrl::DiscardEdits()
  1320. {
  1321. m_isModified = false;
  1322. }
  1323. void wxTextCtrl::SetEditable(bool editable)
  1324. {
  1325. if ( editable != m_isEditable )
  1326. {
  1327. m_isEditable = editable;
  1328. // the caret (dis)appears
  1329. CreateCaret();
  1330. // the appearance of the control might have changed
  1331. Refresh();
  1332. }
  1333. }
  1334. // ----------------------------------------------------------------------------
  1335. // col/lines <-> positiā€¦

Large files files are truncated, but you can click here to view the full file