]> git.saurik.com Git - wxWidgets.git/blob - src/univ/textctrl.cpp
Added work around in wxPropertyGrid for a wxTextCtrl beep on enter press
[wxWidgets.git] / src / univ / textctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/univ/textctrl.cpp
3 // Purpose:     wxTextCtrl
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     15.09.00
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13    TODO
14
15 +  1. update vert scrollbar when any line length changes for WrapLines()
16 +  2. cursor movement ("Hello,^" -> "^verse!" on Arrow Down)?
17      -> maybe save the x position and use it instead of current in handling
18         DOWN/UP actions (this would make up/down always return the cursor to
19         the same location)?
20    3. split file into chunks
21 +? 4. rewrite Replace() refresh logic to deal with wrapping lines
22 +? 5. cache info found by GetPartOfWrappedLine() - performance must be horrible
23       with lots of text
24
25    6. backspace refreshes too much (until end of line)
26  */
27
28 /*
29    Optimisation hints from PureQuantify:
30
31   +1. wxStringTokenize is the slowest part of Replace
32    2. GetDC/ReleaseDC are very slow, avoid calling them several times
33   +3. GetCharHeight() should be cached too
34    4. wxClientDC construction/destruction in HitTestLine is horribly expensive
35
36    For line wrapping controls HitTest2 takes 50% of program time. The results
37    of GetRowsPerLine and GetPartOfWrappedLine *MUST* be cached.
38
39    Search for "OPT!" for things which must be optimized.
40  */
41
42 /*
43    Some terminology:
44
45    Everywhere in this file LINE refers to a logical line of text, and ROW to a
46    physical line of text on the display. They are the same unless WrapLines()
47    is true in which case a single LINE may correspond to multiple ROWs.
48
49    A text position is an unsigned int (which for reasons of compatibility is
50    still a long as wxTextPos) from 0 to GetLastPosition() inclusive. The positions
51    correspond to the gaps between the letters so the position 0 is just
52    before the first character and the last position is the one beyond the last
53    character. For an empty text control GetLastPosition() returns 0.
54
55    Lines and columns returned/accepted by XYToPosition() and PositionToXY()
56    start from 0. The y coordinate is a LINE, not a ROW. Columns correspond to
57    the characters, the first column of a line is the first character in it,
58    the last one is length(line text). For compatibility, again, lines and
59    columns are also longs.
60
61    When translating lines/column coordinates to/from positions, the line and
62    column give the character after the given position. Thus, GetLastPosition()
63    doesn't have any corresponding column.
64
65    An example of positions and lines/columns for a control without wrapping
66    containing the text "Hello, Universe!\nGoodbye"
67
68                                1 1 1 1 1 1 1
69    pos:    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
70             H e l l o ,   U n i v e r s e !         line 0
71    col:     0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1
72                                 0 1 2 3 4 5
73
74    pos:    1 1 1 2 2 2 2 2
75            7 8 9 0 1 2 3 4
76             G o o d b y e                           line 1
77    col:     0 1 2 3 4 5 6
78
79
80    The same example for a control with line wrap assuming "Universe" is too
81    long to fit on the same line with "Hello,":
82
83    pos:    0 1 2 3 4 5
84             H e l l o ,                             line 0 (row 0)
85    col:     0 1 2 3 4 5
86
87                    1 1 1 1 1 1 1
88    pos:    6 7 8 9 0 1 2 3 4 5 6
89               U n i v e r s e !                     line 0 (row 1)
90    col:     6 7 8 9 1 1 1 1 1 1
91                     0 1 2 3 4 5
92
93    (line 1 == row 2 same as above)
94
95    Note that there is still the same number of columns and positions and that
96    there is no (logical) position at the end of the first ROW. This position
97    is identified with the preceding one (which is not how Windows does it: it
98    identifies it with the next one, i.e. the first position of the next line,
99    but much more logical IMHO).
100  */
101
102 /*
103    Search for "OPT" for possible optimizations
104
105    A possible global optimization would be to always store the coords in the
106    text in triplets (pos, col, line) and update them simultaneously instead of
107    recalculating col and line from pos each time it is needed. Currently we
108    only do it for the current position but we might also do it for the
109    selection start and end.
110  */
111
112 // ============================================================================
113 // declarations
114 // ============================================================================
115
116 // ----------------------------------------------------------------------------
117 // headers
118 // ----------------------------------------------------------------------------
119
120 #include "wx/wxprec.h"
121
122 #ifdef __BORLANDC__
123     #pragma hdrstop
124 #endif
125
126 #if wxUSE_TEXTCTRL
127
128 #include "wx/textctrl.h"
129
130 #ifndef WX_PRECOMP
131     #include "wx/log.h"
132     #include "wx/dcclient.h"
133     #include "wx/validate.h"
134     #include "wx/dataobj.h"
135 #endif
136
137 #include <ctype.h>
138
139 #include "wx/clipbrd.h"
140
141 #include "wx/textfile.h"
142
143 #include "wx/caret.h"
144
145 #include "wx/univ/inphand.h"
146 #include "wx/univ/renderer.h"
147 #include "wx/univ/colschem.h"
148 #include "wx/univ/theme.h"
149
150 #include "wx/cmdproc.h"
151
152 #if wxDEBUG_LEVEL >= 2
153     // turn extra wxTextCtrl-specific debugging on/off
154     #define WXDEBUG_TEXT
155
156     // turn wxTextCtrl::Replace() debugging on (slows down code a *lot*!)
157     #define WXDEBUG_TEXT_REPLACE
158 #endif // wxDEBUG_LEVEL >= 2
159
160 // wxStringTokenize only needed for debug checks
161 #ifdef WXDEBUG_TEXT_REPLACE
162     #include "wx/tokenzr.h"
163 #endif // WXDEBUG_TEXT_REPLACE
164
165 // ----------------------------------------------------------------------------
166 // wxStdTextCtrlInputHandler: this control handles only the mouse/kbd actions
167 // common to Win32 and GTK, platform-specific things are implemented elsewhere
168 // ----------------------------------------------------------------------------
169
170 class WXDLLEXPORT wxStdTextCtrlInputHandler : public wxStdInputHandler
171 {
172 public:
173     wxStdTextCtrlInputHandler(wxInputHandler *inphand);
174
175     virtual bool HandleKey(wxInputConsumer *consumer,
176                            const wxKeyEvent& event,
177                            bool pressed);
178     virtual bool HandleMouse(wxInputConsumer *consumer,
179                              const wxMouseEvent& event);
180     virtual bool HandleMouseMove(wxInputConsumer *consumer,
181                                  const wxMouseEvent& event);
182     virtual bool HandleFocus(wxInputConsumer *consumer, const wxFocusEvent& event);
183
184 protected:
185     // get the position of the mouse click
186     static wxTextPos HitTest(const wxTextCtrl *text, const wxPoint& pos);
187
188     // capture data
189     wxTextCtrl *m_winCapture;
190 };
191
192 // ----------------------------------------------------------------------------
193 // private functions
194 // ----------------------------------------------------------------------------
195
196 // exchange two positions so that from is always less than or equal to to
197 static inline void OrderPositions(wxTextPos& from, wxTextPos& to)
198 {
199     if ( from > to )
200     {
201         wxTextPos tmp = from;
202         from = to;
203         to = tmp;
204     }
205 }
206
207 // ----------------------------------------------------------------------------
208 // constants
209 // ----------------------------------------------------------------------------
210
211 // names of text ctrl commands
212 #define wxTEXT_COMMAND_INSERT wxT("insert")
213 #define wxTEXT_COMMAND_REMOVE wxT("remove")
214
215 // the value which is never used for text position, even not -1 which is
216 // sometimes used for some special meaning
217 static const wxTextPos INVALID_POS_VALUE = wxInvalidTextCoord;
218
219 // overlap between pages (when using PageUp/Dn) in lines
220 static const size_t PAGE_OVERLAP_IN_LINES = 1;
221
222 // ----------------------------------------------------------------------------
223 // private data of wxTextCtrl
224 // ----------------------------------------------------------------------------
225
226 // the data only used by single line text controls
227 struct wxTextSingleLineData
228 {
229     // the position of the first visible pixel and the first visible column
230     wxCoord m_ofsHorz;
231     wxTextCoord m_colStart;
232
233     // and the last ones (m_posLastVisible is the width but m_colLastVisible
234     // is an absolute value)
235     wxCoord m_posLastVisible;
236     wxTextCoord m_colLastVisible;
237
238     // def ctor
239     wxTextSingleLineData()
240     {
241         m_colStart = 0;
242         m_ofsHorz = 0;
243
244         m_colLastVisible = -1;
245         m_posLastVisible = -1;
246     }
247
248 };
249
250 // the data only used by multi line text controls
251 struct wxTextMultiLineData
252 {
253     // the lines of text
254     wxArrayString m_lines;
255
256     // the current ranges of the scrollbars
257     int m_scrollRangeX,
258         m_scrollRangeY;
259
260     // should we adjust the horz/vert scrollbar?
261     bool m_updateScrollbarX,
262          m_updateScrollbarY;
263
264     // the max line length in pixels
265     wxCoord m_widthMax;
266
267     // the index of the line which has the length of m_widthMax
268     wxTextCoord m_lineLongest;
269
270     // the rect in which text appears: it is even less than m_rectText because
271     // only the last _complete_ line is shown, hence there is an unoccupied
272     // horizontal band at the bottom of it
273     wxRect m_rectTextReal;
274
275     // the x-coordinate of the caret before we started moving it vertically:
276     // this is used to ensure that moving the caret up and then down will
277     // return it to the same position as if we always round it in one direction
278     // we would shift it in that direction
279     //
280     // when m_xCaret == -1, we don't have any remembered position
281     wxCoord m_xCaret;
282
283     // the def ctor
284     wxTextMultiLineData()
285     {
286         m_scrollRangeX =
287         m_scrollRangeY = 0;
288
289         m_updateScrollbarX =
290         m_updateScrollbarY = false;
291
292         m_widthMax = -1;
293         m_lineLongest = 0;
294
295         m_xCaret = -1;
296     }
297 };
298
299 // the data only used by multi line text controls in line wrap mode
300 class wxWrappedLineData
301 {
302     // these functions set all our values, so give them access to them
303 friend void wxTextCtrl::LayoutLine(wxTextCoord line,
304                                     wxWrappedLineData& lineData) const;
305 friend void wxTextCtrl::LayoutLines(wxTextCoord) const;
306
307 public:
308     // def ctor
309     wxWrappedLineData()
310     {
311         m_rowFirst = -1;
312     }
313
314     // get the start of any row (remember that accessing m_rowsStart doesn't work
315     // for the first one)
316     wxTextCoord GetRowStart(wxTextCoord row) const
317     {
318         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
319
320         return row ? m_rowsStart[row - 1] : 0;
321     }
322
323     // get the length of the row (using the total line length which we don't
324     // have here but need to calculate the length of the last row, so it must
325     // be given to us)
326     wxTextCoord GetRowLength(wxTextCoord row, wxTextCoord lenLine) const
327     {
328         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
329
330         // note that m_rowsStart[row] is the same as GetRowStart(row + 1) (but
331         // slightly more efficient) and lenLine is the same as the start of the
332         // first row of the next line
333         return ((size_t)row == m_rowsStart.GetCount() ? lenLine : m_rowsStart[row])
334                     - GetRowStart(row);
335     }
336
337     // return the width of the row in pixels
338     wxCoord GetRowWidth(wxTextCoord row) const
339     {
340         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
341
342         return m_rowsWidth[row];
343     }
344
345     // return the number of rows
346     size_t GetRowCount() const
347     {
348         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
349
350         return m_rowsStart.GetCount() + 1;
351     }
352
353     // return the number of additional (i.e. after the first one) rows
354     size_t GetExtraRowCount() const
355     {
356         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
357
358         return m_rowsStart.GetCount();
359     }
360
361     // return the first row of this line
362     wxTextCoord GetFirstRow() const
363     {
364         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
365
366         return m_rowFirst;
367     }
368
369     // return the first row of the next line
370     wxTextCoord GetNextRow() const
371     {
372         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
373
374         return m_rowFirst + m_rowsStart.GetCount() + 1;
375     }
376
377     // this just provides direct access to m_rowsStart aerray for efficiency
378     wxTextCoord GetExtraRowStart(wxTextCoord row) const
379     {
380         wxASSERT_MSG( IsValid(), wxT("this line hadn't been laid out") );
381
382         return m_rowsStart[row];
383     }
384
385     // this code is unused any longer
386 #if 0
387     // return true if the column is in the start of the last row (hence the row
388     // it is in is not wrapped)
389     bool IsLastRow(wxTextCoord colRowStart) const
390     {
391         return colRowStart == GetRowStart(m_rowsStart.GetCount());
392     }
393
394     // return true if the column is the last column of the row starting in
395     // colRowStart
396     bool IsLastColInRow(wxTextCoord colRowStart,
397                         wxTextCoord colRowEnd,
398                         wxTextCoord lenLine) const
399     {
400         // find the row which starts with colRowStart
401         size_t nRows = GetRowCount();
402         for ( size_t n = 0; n < nRows; n++ )
403         {
404             if ( GetRowStart(n) == colRowStart )
405             {
406                 wxTextCoord colNextRowStart = n == nRows - 1
407                                                 ? lenLine
408                                                 : GetRowStart(n + 1);
409
410                 wxASSERT_MSG( colRowEnd < colNextRowStart,
411                               wxT("this column is not in this row at all!") );
412
413                 return colRowEnd == colNextRowStart - 1;
414             }
415         }
416
417         // caller got it wrong
418         wxFAIL_MSG( wxT("this column is not in the start of the row!") );
419
420         return false;
421     }
422 #endif // 0
423
424     // is this row the last one in its line?
425     bool IsLastRow(wxTextCoord row) const
426     {
427         return (size_t)row == GetExtraRowCount();
428     }
429
430     // the line is valid if it had been laid out correctly: note that just
431     // shiwting the line (because one of previous lines changed) doesn't make
432     // it invalid
433     bool IsValid() const { return !m_rowsWidth.IsEmpty(); }
434
435     // invalidating line will relayout it
436     void Invalidate() { m_rowsWidth.Empty(); }
437
438 private:
439     // for each line we remember the starting columns of all its rows after the
440     // first one (which always starts at 0), i.e. if a line is wrapped twice
441     // (== takes 3 rows) its m_rowsStart[0] may be 10 and m_rowsStart[1] == 15
442     wxArrayLong m_rowsStart;
443
444     // and the width of each row in pixels (this array starts from 0, as usual)
445     wxArrayInt m_rowsWidth;
446
447     // and also its starting row (0 for the first line, first lines'
448     // m_rowsStart.GetCount() + 1 for the second &c): it is set to -1 initially
449     // and this means that the struct hadn't yet been initialized
450     wxTextCoord m_rowFirst;
451
452     // the last modification "time"-stamp used by LayoutLines()
453     size_t m_timestamp;
454 };
455
456 WX_DECLARE_OBJARRAY(wxWrappedLineData, wxArrayWrappedLinesData);
457 #include "wx/arrimpl.cpp"
458 WX_DEFINE_OBJARRAY(wxArrayWrappedLinesData);
459
460 struct wxTextWrappedData : public wxTextMultiLineData
461 {
462     // the width of the column to the right of the text rect used for the
463     // indicator mark display for the wrapped lines
464     wxCoord m_widthMark;
465
466     // the data for each line
467     wxArrayWrappedLinesData m_linesData;
468
469     // flag telling us to recalculate all starting rows starting from this line
470     // (if it is -1, we don't have to recalculate anything) - it is set when
471     // the number of the rows in the middle of the control changes
472     wxTextCoord m_rowFirstInvalid;
473
474     // the current timestamp used by LayoutLines()
475     size_t m_timestamp;
476
477     // invalidate starting rows of all lines (NOT rows!) after this one
478     void InvalidateLinesBelow(wxTextCoord line)
479     {
480         if ( m_rowFirstInvalid == -1 || m_rowFirstInvalid > line )
481         {
482             m_rowFirstInvalid = line;
483         }
484     }
485
486     // check if this line is valid: i.e. before the first invalid one
487     bool IsValidLine(wxTextCoord line) const
488     {
489         return ((m_rowFirstInvalid == -1) || (line < m_rowFirstInvalid)) &&
490                     m_linesData[line].IsValid();
491     }
492
493     // def ctor
494     wxTextWrappedData()
495     {
496         m_widthMark = 0;
497         m_rowFirstInvalid = -1;
498         m_timestamp = 0;
499     }
500 };
501
502 // ----------------------------------------------------------------------------
503 // private classes for undo/redo management
504 // ----------------------------------------------------------------------------
505
506 /*
507    We use custom versions of wxWidgets command processor to implement undo/redo
508    as we want to avoid storing the backpointer to wxTextCtrl in wxCommand
509    itself: this is a waste of memory as all commands in the given command
510    processor always have the same associated wxTextCtrl and so it makes sense
511    to store the backpointer there.
512
513    As for the rest of the implementation, it's fairly standard: we have 2
514    command classes corresponding to adding and removing text.
515  */
516
517 // a command corresponding to a wxTextCtrl action
518 class wxTextCtrlCommand : public wxCommand
519 {
520 public:
521     wxTextCtrlCommand(const wxString& name) : wxCommand(true, name) { }
522
523     // we don't use these methods as they don't make sense for us as we need a
524     // wxTextCtrl to be applied
525     virtual bool Do() { wxFAIL_MSG(wxT("shouldn't be called")); return false; }
526     virtual bool Undo() { wxFAIL_MSG(wxT("shouldn't be called")); return false; }
527
528     // instead, our command processor uses these methods
529     virtual bool Do(wxTextCtrl *text) = 0;
530     virtual bool Undo(wxTextCtrl *text) = 0;
531 };
532
533 // insert text command
534 class wxTextCtrlInsertCommand : public wxTextCtrlCommand
535 {
536 public:
537     wxTextCtrlInsertCommand(const wxString& textToInsert)
538         : wxTextCtrlCommand(wxTEXT_COMMAND_INSERT), m_text(textToInsert)
539     {
540         m_from = -1;
541     }
542
543     // combine the 2 commands together
544     void Append(wxTextCtrlInsertCommand *other);
545
546     virtual bool CanUndo() const;
547     virtual bool Do(wxTextCtrl *text);
548     virtual bool Do() { return wxTextCtrlCommand::Do(); }
549     virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
550     virtual bool Undo(wxTextCtrl *text);
551
552 private:
553     // the text we insert
554     wxString m_text;
555
556     // the position where we inserted the text
557     wxTextPos m_from;
558 };
559
560 // remove text command
561 class wxTextCtrlRemoveCommand : public wxTextCtrlCommand
562 {
563 public:
564     wxTextCtrlRemoveCommand(wxTextPos from, wxTextPos to)
565         : wxTextCtrlCommand(wxTEXT_COMMAND_REMOVE)
566     {
567         m_from = from;
568         m_to = to;
569     }
570
571     virtual bool CanUndo() const;
572     virtual bool Do(wxTextCtrl *text);
573     virtual bool Do() { return wxTextCtrlCommand::Do(); }
574     virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
575     virtual bool Undo(wxTextCtrl *text);
576
577 private:
578     // the range of text to delete
579     wxTextPos m_from,
580          m_to;
581
582     // the text which was deleted when this command was Do()ne
583     wxString m_textDeleted;
584 };
585
586 // a command processor for a wxTextCtrl
587 class wxTextCtrlCommandProcessor : public wxCommandProcessor
588 {
589 public:
590     wxTextCtrlCommandProcessor(wxTextCtrl *text)
591     {
592         m_compressInserts = false;
593
594         m_text = text;
595     }
596
597     // override Store() to compress multiple wxTextCtrlInsertCommand into one
598     virtual void Store(wxCommand *command);
599
600     // stop compressing insert commands when this is called
601     void StopCompressing() { m_compressInserts = false; }
602
603     // accessors
604     wxTextCtrl *GetTextCtrl() const { return m_text; }
605     bool IsCompressing() const { return m_compressInserts; }
606
607 protected:
608     virtual bool DoCommand(wxCommand& cmd)
609         { return ((wxTextCtrlCommand &)cmd).Do(m_text); }
610     virtual bool UndoCommand(wxCommand& cmd)
611         { return ((wxTextCtrlCommand &)cmd).Undo(m_text); }
612
613     // check if this command is a wxTextCtrlInsertCommand and return it casted
614     // to the right type if it is or NULL otherwise
615     wxTextCtrlInsertCommand *IsInsertCommand(wxCommand *cmd);
616
617 private:
618     // the control we're associated with
619     wxTextCtrl *m_text;
620
621     // if the flag is true we're compressing subsequent insert commands into
622     // one so that the entire typing could be undone in one call to Undo()
623     bool m_compressInserts;
624 };
625
626 // ============================================================================
627 // implementation
628 // ============================================================================
629
630 BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
631     EVT_CHAR(wxTextCtrl::OnChar)
632
633     EVT_SIZE(wxTextCtrl::OnSize)
634 END_EVENT_TABLE()
635
636 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
637
638 // ----------------------------------------------------------------------------
639 // creation
640 // ----------------------------------------------------------------------------
641
642 void wxTextCtrl::Init()
643 {
644     m_selAnchor =
645     m_selStart =
646     m_selEnd = -1;
647
648     m_isModified = false;
649     m_isEditable = true;
650     m_wrapLines = false;
651
652     m_posLast =
653     m_curPos =
654     m_curCol =
655     m_curRow = 0;
656
657     m_heightLine =
658     m_widthAvg = -1;
659
660     // init the undo manager
661     m_cmdProcessor = new wxTextCtrlCommandProcessor(this);
662
663     // no data yet
664     m_data.data = NULL;
665 }
666
667 bool wxTextCtrl::Create(wxWindow *parent,
668                         wxWindowID id,
669                         const wxString& value,
670                         const wxPoint& pos,
671                         const wxSize& size,
672                         long style,
673                         const wxValidator& validator,
674                         const wxString &name)
675 {
676     if ( style & wxTE_MULTILINE )
677     {
678         // for compatibility with wxMSW we create the controls with vertical
679         // scrollbar always shown unless they have wxTE_RICH style (because
680         // Windows text controls always has vert scrollbar but richedit one
681         // doesn't)
682         if ( !(style & wxTE_RICH) )
683         {
684             style |= wxALWAYS_SHOW_SB;
685         }
686
687         // wrapping style: wxTE_DONTWRAP == wxHSCROLL so if it's _not_ given,
688         // we won't have horizontal scrollbar automatically, no need to do
689         // anything
690
691         // TODO: support wxTE_NO_VSCROLL (?)
692
693         // create data object for normal multiline or for controls with line
694         // wrap as needed
695         if ( style & wxHSCROLL )
696         {
697             m_data.mdata = new wxTextMultiLineData;
698         }
699         else // we must wrap lines if we don't have horizontal scrollbar
700         {
701             // NB: we can't rely on HasFlag(wxHSCROLL) as the flags can change
702             //     later and even wxWindow::Create() itself temporarily resets
703             //     wxHSCROLL in wxUniv, so remember that we have a wrapped data
704             //     and not just a multi line data in a separate variable
705             m_wrapLines = true;
706             m_data.wdata = new wxTextWrappedData;
707         }
708     }
709     else
710     {
711         // this doesn't make sense for single line controls
712         style &= ~wxHSCROLL;
713
714         // create data object for single line controls
715         m_data.sdata = new wxTextSingleLineData;
716     }
717
718 #if wxUSE_TWO_WINDOWS
719     if ((style & wxBORDER_MASK) == 0)
720         style |= wxBORDER_SUNKEN;
721 #endif
722
723     if ( !wxControl::Create(parent, id, pos, size, style,
724                             validator, name) )
725     {
726         return false;
727     }
728
729     SetCursor(wxCURSOR_IBEAM);
730
731     if ( style & wxTE_MULTILINE )
732     {
733         // we should always have at least one line in a multiline control
734         MData().m_lines.Add(wxEmptyString);
735
736         if ( !(style & wxHSCROLL) )
737         {
738             WData().m_linesData.Add(new wxWrappedLineData);
739             WData().InvalidateLinesBelow(0);
740         }
741
742         // we might support it but it's quite useless and other ports don't
743         // support it anyhow
744         wxASSERT_MSG( !(style & wxTE_PASSWORD),
745                       wxT("wxTE_PASSWORD can't be used with multiline ctrls") );
746     }
747
748     RecalcFontMetrics();
749     SetValue(value);
750     SetInitialSize(size);
751
752     m_isEditable = !(style & wxTE_READONLY);
753
754     CreateCaret();
755     InitInsertionPoint();
756
757     // we can't show caret right now as we're not shown yet and so it would
758     // result in garbage on the screen - we'll do it after first OnPaint()
759     m_hasCaret = false;
760
761     CreateInputHandler(wxINP_HANDLER_TEXTCTRL);
762
763     wxSizeEvent sizeEvent(GetSize(), GetId());
764     GetEventHandler()->ProcessEvent(sizeEvent);
765
766     return true;
767 }
768
769 wxTextCtrl::~wxTextCtrl()
770 {
771     delete m_cmdProcessor;
772
773     if ( m_data.data )
774     {
775         if ( IsSingleLine() )
776             delete m_data.sdata;
777         else if ( WrapLines() )
778             delete m_data.wdata;
779         else
780             delete m_data.mdata;
781     }
782 }
783
784 // ----------------------------------------------------------------------------
785 // set/get the value
786 // ----------------------------------------------------------------------------
787
788 void wxTextCtrl::DoSetValue(const wxString& value, int flags)
789 {
790     if ( value != GetValue() )
791     {
792         EventsSuppressor noeventsIf(this, !(flags & SetValue_SendEvent));
793
794         Replace(0, GetLastPosition(), value);
795
796         if ( IsSingleLine() )
797         {
798             SetInsertionPoint(0);
799         }
800     }
801     else // nothing changed
802     {
803         // still send event for consistency
804         if ( flags & SetValue_SendEvent )
805             SendTextUpdatedEvent();
806     }
807 }
808
809 const wxArrayString& wxTextCtrl::GetLines() const
810 {
811     return MData().m_lines;
812 }
813
814 size_t wxTextCtrl::GetLineCount() const
815 {
816     return MData().m_lines.GetCount();
817 }
818
819 wxString wxTextCtrl::DoGetValue() const
820 {
821     // for multiline controls we don't always store the total value but only
822     // recompute it when asked - and to invalidate it we just empty it in
823     // Replace()
824     if ( !IsSingleLine() && m_value.empty() )
825     {
826         // recalculate: note that we always do it for empty multilien control,
827         // but then it's so quick that it's not important
828
829         // the first line is special as there is no \n before it, so it's
830         // outside the loop
831         const wxArrayString& lines = GetLines();
832         wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
833         self->m_value << lines[0u];
834         size_t count = lines.GetCount();
835         for ( size_t n = 1; n < count; n++ )
836         {
837             self->m_value << wxT('\n') << lines[n];
838         }
839     }
840
841     return m_value;
842 }
843
844 void wxTextCtrl::Clear()
845 {
846     SetValue(wxEmptyString);
847 }
848
849 bool wxTextCtrl::ReplaceLine(wxTextCoord line,
850                              const wxString& text)
851 {
852     if ( WrapLines() )
853     {
854         // first, we have to relayout the line entirely
855         //
856         // OPT: we might try not to recalc the unchanged part of line
857
858         wxWrappedLineData& lineData = WData().m_linesData[line];
859
860         // if we had some number of rows before, use this number, otherwise
861         // just make sure that the test below (rowsNew != rowsOld) will be true
862         int rowsOld;
863         if ( lineData.IsValid() )
864         {
865             rowsOld = lineData.GetExtraRowCount();
866         }
867         else // line wasn't laid out yet
868         {
869             // assume it changed entirely as we can't do anything better
870             rowsOld = -1;
871         }
872
873         // now change the line
874         MData().m_lines[line] = text;
875
876         // OPT: we choose to lay it our immediately instead of delaying it
877         //      until it is needed because it allows us to avoid invalidating
878         //      lines further down if the number of rows didn't chnage, but
879         //      maybe we can imporve this even further?
880         LayoutLine(line, lineData);
881
882         int rowsNew = lineData.GetExtraRowCount();
883
884         if ( rowsNew != rowsOld )
885         {
886             // we have to update the line wrap marks as this is normally done
887             // by LayoutLines() which we bypassed by calling LayoutLine()
888             // directly
889             wxTextCoord rowFirst = lineData.GetFirstRow(),
890                         rowCount = wxMax(rowsOld, rowsNew);
891             RefreshLineWrapMarks(rowFirst, rowFirst + rowCount);
892
893             // next, if this is not the last line, as the number of rows in it
894             // changed, we need to shift all the lines below it
895             if ( (size_t)line < WData().m_linesData.GetCount() )
896             {
897                 // number of rows changed shifting all lines below
898                 WData().InvalidateLinesBelow(line + 1);
899             }
900
901             // the number of rows changed
902             return true;
903         }
904     }
905     else // no line wrap
906     {
907         MData().m_lines[line] = text;
908     }
909
910     // the number of rows didn't change
911     return false;
912 }
913
914 void wxTextCtrl::RemoveLine(wxTextCoord line)
915 {
916     MData().m_lines.RemoveAt(line);
917     if ( WrapLines() )
918     {
919         // we need to recalculate all the starting rows from this line, but we
920         // can avoid doing it if this line was never calculated: this means
921         // that we will recalculate all lines below it anyhow later if needed
922         if ( WData().IsValidLine(line) )
923         {
924             WData().InvalidateLinesBelow(line);
925         }
926
927         WData().m_linesData.RemoveAt(line);
928     }
929 }
930
931 void wxTextCtrl::InsertLine(wxTextCoord line, const wxString& text)
932 {
933     MData().m_lines.Insert(text, line);
934     if ( WrapLines() )
935     {
936         WData().m_linesData.Insert(new wxWrappedLineData, line);
937
938         // invalidate everything below it
939         WData().InvalidateLinesBelow(line);
940     }
941 }
942
943 void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
944 {
945     wxTextCoord colStart, colEnd,
946                 lineStart, lineEnd;
947
948     if ( (from > to) ||
949          !PositionToXY(from, &colStart, &lineStart) ||
950          !PositionToXY(to, &colEnd, &lineEnd) )
951     {
952         wxFAIL_MSG(wxT("invalid range in wxTextCtrl::Replace"));
953
954         return;
955     }
956
957 #ifdef WXDEBUG_TEXT_REPLACE
958     // a straighforward (but very inefficient) way of calculating what the new
959     // value should be
960     wxString textTotal = GetValue();
961     wxString textTotalNew(textTotal, (size_t)from);
962     textTotalNew += text;
963     if ( (size_t)to < textTotal.length() )
964         textTotalNew += textTotal.c_str() + (size_t)to;
965 #endif // WXDEBUG_TEXT_REPLACE
966
967     // remember the old selection and reset it immediately: we must do it
968     // before calling Refresh(anything) as, at least under GTK, this leads to
969     // an _immediate_ repaint (under MSW it is delayed) and hence parts of
970     // text would be redrawn as selected if we didn't reset the selection
971     int selStartOld = m_selStart,
972         selEndOld = m_selEnd;
973
974     m_selStart =
975     m_selEnd = -1;
976
977     if ( IsSingleLine() )
978     {
979         // replace the part of the text with the new value
980         wxString valueNew(m_value, (size_t)from);
981
982         // remember it for later use
983         wxCoord startNewText = GetTextWidth(valueNew);
984
985         valueNew += text;
986         if ( (size_t)to < m_value.length() )
987         {
988             valueNew += m_value.c_str() + (size_t)to;
989         }
990
991         // we usually refresh till the end of line except of the most common case
992         // when some text is appended to the end of the string in which case we
993         // refresh just it
994         wxCoord widthNewText;
995
996         if ( (size_t)from < m_value.length() )
997         {
998             // refresh till the end of line
999             widthNewText = 0;
1000         }
1001         else // text appended, not replaced
1002         {
1003             // refresh only the new text
1004             widthNewText = GetTextWidth(text);
1005         }
1006
1007         m_value = valueNew;
1008
1009         // force SData().m_colLastVisible update
1010         SData().m_colLastVisible = -1;
1011
1012         // repaint
1013         RefreshPixelRange(0, startNewText, widthNewText);
1014     }
1015     else // multiline
1016     {
1017         //OPT: special case for replacements inside single line?
1018
1019         /*
1020            Join all the lines in the replacement range into one string, then
1021            replace a part of it with the new text and break it into lines again.
1022         */
1023
1024         // (0) we want to know if this replacement changes the number of rows
1025         //     as if it does we need to refresh everything below the changed
1026         //     text (it will be shifted...) and we can avoid it if there is no
1027         //     row relayout
1028         bool rowsNumberChanged = false;
1029
1030         // (1) join lines
1031         const wxArrayString& linesOld = GetLines();
1032         wxString textOrig;
1033         wxTextCoord line;
1034         for ( line = lineStart; line <= lineEnd; line++ )
1035         {
1036             if ( line > lineStart )
1037             {
1038                 // from the previous line
1039                 textOrig += wxT('\n');
1040             }
1041
1042             textOrig += linesOld[line];
1043         }
1044
1045         // we need to append the '\n' for the last line unless there is no
1046         // following line
1047         size_t countOld = linesOld.GetCount();
1048
1049         // (2) replace text in the combined string
1050
1051         // (2a) leave the part before replaced area unchanged
1052         wxString textNew(textOrig, colStart);
1053
1054         // these values will be used to refresh the changed area below
1055         wxCoord widthNewText,
1056                 startNewText = GetTextWidth(textNew);
1057         if ( (size_t)colStart == linesOld[lineStart].length() )
1058         {
1059             // text appended, refresh just enough to show the new text
1060             widthNewText = GetTextWidth(text.BeforeFirst(wxT('\n')));
1061         }
1062         else // text inserted, refresh till the end of line
1063         {
1064             widthNewText = 0;
1065         }
1066
1067         // (2b) insert new text
1068         textNew += text;
1069
1070         // (2c) and append the end of the old text
1071
1072         // adjust for index shift: to is relative to colStart, not 0
1073         size_t toRel = (size_t)((to - from) + colStart);
1074         if ( toRel < textOrig.length() )
1075         {
1076             textNew += textOrig.c_str() + toRel;
1077         }
1078
1079         // (3) break it into lines
1080
1081         wxArrayString lines;
1082         const wxChar *curLineStart = textNew.c_str();
1083         for ( const wxChar *p = textNew.c_str(); ; p++ )
1084         {
1085             // end of line/text?
1086             if ( !*p || *p == wxT('\n') )
1087             {
1088                 lines.Add(wxString(curLineStart, p));
1089                 if ( !*p )
1090                     break;
1091
1092                 curLineStart = p + 1;
1093             }
1094         }
1095
1096 #ifdef WXDEBUG_TEXT_REPLACE
1097         // (3a) all empty tokens should be counted as replacing with "foo" and
1098         //      with "foo\n" should have different effects
1099         wxArrayString lines2 = wxStringTokenize(textNew, wxT("\n"),
1100                                                 wxTOKEN_RET_EMPTY_ALL);
1101
1102         if ( lines2.IsEmpty() )
1103         {
1104             lines2.Add(wxEmptyString);
1105         }
1106
1107         wxASSERT_MSG( lines.GetCount() == lines2.GetCount(),
1108                       wxT("Replace() broken") );
1109         for ( size_t n = 0; n < lines.GetCount(); n++ )
1110         {
1111             wxASSERT_MSG( lines[n] == lines2[n], wxT("Replace() broken") );
1112         }
1113 #endif // WXDEBUG_TEXT_REPLACE
1114
1115         // (3b) special case: if we replace everything till the end we need to
1116         //      keep an empty line or the lines would disappear completely
1117         //      (this also takes care of never leaving m_lines empty)
1118         if ( ((size_t)lineEnd == countOld - 1) && lines.IsEmpty() )
1119         {
1120             lines.Add(wxEmptyString);
1121         }
1122
1123         size_t nReplaceCount = lines.GetCount(),
1124                nReplaceLine = 0;
1125
1126         // (4) merge into the array
1127
1128         // (4a) replace
1129         for ( line = lineStart; line <= lineEnd; line++, nReplaceLine++ )
1130         {
1131             if ( nReplaceLine < nReplaceCount )
1132             {
1133                 // we have the replacement line for this one
1134                 if ( ReplaceLine(line, lines[nReplaceLine]) )
1135                 {
1136                     rowsNumberChanged = true;
1137                 }
1138
1139                 UpdateMaxWidth(line);
1140             }
1141             else // no more replacement lines
1142             {
1143                 // (4b) delete all extra lines (note that we need to delete
1144                 //      them backwards because indices shift while we do it)
1145                 bool deletedLongestLine = false;
1146                 for ( wxTextCoord lineDel = lineEnd; lineDel >= line; lineDel-- )
1147                 {
1148                     if ( lineDel == MData().m_lineLongest )
1149                     {
1150                         // we will need to recalc the max line width
1151                         deletedLongestLine = true;
1152                     }
1153
1154                     RemoveLine(lineDel);
1155                 }
1156
1157                 if ( deletedLongestLine )
1158                 {
1159                     RecalcMaxWidth();
1160                 }
1161
1162                 // even the line number changed
1163                 rowsNumberChanged = true;
1164
1165                 // update line to exit the loop
1166                 line = lineEnd + 1;
1167             }
1168         }
1169
1170         // (4c) insert the new lines
1171         if ( nReplaceLine < nReplaceCount )
1172         {
1173             // even the line number changed
1174             rowsNumberChanged = true;
1175
1176             do
1177             {
1178                 InsertLine(++lineEnd, lines[nReplaceLine++]);
1179
1180                 UpdateMaxWidth(lineEnd);
1181             }
1182             while ( nReplaceLine < nReplaceCount );
1183         }
1184
1185         // (5) now refresh the changed area
1186
1187         // update the (cached) last position first as refresh functions use it
1188         m_posLast += text.length() - to + from;
1189
1190         // we may optimize refresh if the number of rows didn't change - but if
1191         // it did we have to refresh everything below the part we chanegd as
1192         // well as it might have moved
1193         if ( !rowsNumberChanged )
1194         {
1195             // refresh the line we changed
1196             if ( !WrapLines() )
1197             {
1198                 RefreshPixelRange(lineStart++, startNewText, widthNewText);
1199             }
1200             else
1201             {
1202                 //OPT: we shouldn't refresh the unchanged part of the line in
1203                 //     this case, but instead just refresh the tail of it - the
1204                 //     trouble is that we don't know here where does this tail
1205                 //     start
1206             }
1207
1208             // number of rows didn't change, refresh the updated rows and the
1209             // last one
1210             if ( lineStart <= lineEnd )
1211                 RefreshLineRange(lineStart, lineEnd);
1212         }
1213         else // rows number did change
1214         {
1215             if ( !WrapLines() )
1216             {
1217                 // refresh only part of the first line
1218                 RefreshPixelRange(lineStart++, startNewText, widthNewText);
1219             }
1220             //else: we have to refresh everything as some part of the text
1221             //      could be in the previous row before but moved to the next
1222             //      one now (due to word wrap)
1223
1224             wxTextCoord lineEnd = GetLines().GetCount() - 1;
1225             if ( lineStart <= lineEnd )
1226                 RefreshLineRange(lineStart, lineEnd);
1227
1228             // refresh text rect left below
1229             RefreshLineRange(lineEnd + 1, 0);
1230
1231             // the vert scrollbar might [dis]appear
1232             MData().m_updateScrollbarY = true;
1233         }
1234
1235         // must recalculate it - will do later
1236         m_value.clear();
1237     }
1238
1239 #ifdef WXDEBUG_TEXT_REPLACE
1240     // optimized code above should give the same result as straightforward
1241     // computation in the beginning
1242     wxASSERT_MSG( GetValue() == textTotalNew, wxT("error in Replace()") );
1243 #endif // WXDEBUG_TEXT_REPLACE
1244
1245     // update the current position: note that we always put the cursor at the
1246     // end of the replacement text
1247     DoSetInsertionPoint(from + text.length());
1248
1249     // and the selection: this is complicated by the fact that selection coords
1250     // must be first updated to reflect change in text coords, i.e. if we had
1251     // selection from 17 to 19 and we just removed this range, we don't have to
1252     // refresh anything, so we can't just use ClearSelection() here
1253     if ( selStartOld != -1 )
1254     {
1255         // refresh the parst of the selection outside the changed text (which
1256         // we already refreshed)
1257         if ( selStartOld < from )
1258             RefreshTextRange(selStartOld, from);
1259         if ( to < selEndOld )
1260             RefreshTextRange(to, selEndOld);
1261
1262     }
1263
1264     // now call it to do the rest (not related to refreshing)
1265     ClearSelection();
1266
1267     if ( EventsAllowed() )
1268         SendTextUpdatedEvent();
1269 }
1270
1271 void wxTextCtrl::Remove(wxTextPos from, wxTextPos to)
1272 {
1273     // Replace() only works with correctly ordered arguments, so exchange them
1274     // if necessary
1275     OrderPositions(from, to);
1276
1277     Replace(from, to, wxEmptyString);
1278 }
1279
1280 void wxTextCtrl::WriteText(const wxString& text)
1281 {
1282     // replace the selection with the new text
1283     RemoveSelection();
1284
1285     Replace(m_curPos, m_curPos, text);
1286 }
1287
1288 void wxTextCtrl::AppendText(const wxString& text)
1289 {
1290     SetInsertionPointEnd();
1291     WriteText(text);
1292 }
1293
1294 // ----------------------------------------------------------------------------
1295 // current position
1296 // ----------------------------------------------------------------------------
1297
1298 void wxTextCtrl::SetInsertionPoint(wxTextPos pos)
1299 {
1300     wxCHECK_RET( pos >= 0 && pos <= GetLastPosition(),
1301                  wxT("insertion point position out of range") );
1302
1303     // don't do anything if it didn't change
1304     if ( pos != m_curPos )
1305     {
1306         DoSetInsertionPoint(pos);
1307     }
1308
1309     if ( !IsSingleLine() )
1310     {
1311         // moving cursor should reset the stored abscissa (even if the cursor
1312         // position didn't actually change!)
1313         MData().m_xCaret = -1;
1314     }
1315
1316     ClearSelection();
1317 }
1318
1319 void wxTextCtrl::InitInsertionPoint()
1320 {
1321     // so far always put it in the beginning
1322     DoSetInsertionPoint(0);
1323
1324     // this will also set the selection anchor correctly
1325     ClearSelection();
1326 }
1327
1328 void wxTextCtrl::MoveInsertionPoint(wxTextPos pos)
1329 {
1330     wxASSERT_MSG( pos >= 0 && pos <= GetLastPosition(),
1331                  wxT("DoSetInsertionPoint() can only be called with valid pos") );
1332
1333     m_curPos = pos;
1334     PositionToXY(m_curPos, &m_curCol, &m_curRow);
1335 }
1336
1337 void wxTextCtrl::DoSetInsertionPoint(wxTextPos pos)
1338 {
1339     MoveInsertionPoint(pos);
1340
1341     ShowPosition(pos);
1342 }
1343
1344 void wxTextCtrl::SetInsertionPointEnd()
1345 {
1346     SetInsertionPoint(GetLastPosition());
1347 }
1348
1349 wxTextPos wxTextCtrl::GetInsertionPoint() const
1350 {
1351     return m_curPos;
1352 }
1353
1354 wxTextPos wxTextCtrl::GetLastPosition() const
1355 {
1356     wxTextPos pos;
1357     if ( IsSingleLine() )
1358     {
1359         pos = m_value.length();
1360     }
1361     else // multiline
1362     {
1363 #ifdef WXDEBUG_TEXT
1364         pos = 0;
1365         size_t nLineCount = GetLineCount();
1366         for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
1367         {
1368             // +1 is because the positions at the end of this line and of the
1369             // start of the next one are different
1370             pos += GetLines()[nLine].length() + 1;
1371         }
1372
1373         if ( pos > 0 )
1374         {
1375             // the last position is at the end of the last line, not in the
1376             // beginning of the next line after it
1377             pos--;
1378         }
1379
1380         // more probable reason of this would be to forget to update m_posLast
1381         wxASSERT_MSG( pos == m_posLast, wxT("bug in GetLastPosition()") );
1382 #endif // WXDEBUG_TEXT
1383
1384         pos = m_posLast;
1385     }
1386
1387     return pos;
1388 }
1389
1390 // ----------------------------------------------------------------------------
1391 // selection
1392 // ----------------------------------------------------------------------------
1393
1394 void wxTextCtrl::GetSelection(wxTextPos* from, wxTextPos* to) const
1395 {
1396     if ( from )
1397         *from = m_selStart;
1398     if ( to )
1399         *to = m_selEnd;
1400 }
1401
1402 wxString wxTextCtrl::GetSelectionText() const
1403 {
1404     wxString sel;
1405
1406     if ( HasSelection() )
1407     {
1408         if ( IsSingleLine() )
1409         {
1410             sel = m_value.Mid(m_selStart, m_selEnd - m_selStart);
1411         }
1412         else // multiline
1413         {
1414             wxTextCoord colStart, lineStart,
1415                         colEnd, lineEnd;
1416             PositionToXY(m_selStart, &colStart, &lineStart);
1417             PositionToXY(m_selEnd, &colEnd, &lineEnd);
1418
1419             // as always, we need to check for the special case when the start
1420             // and end line are the same
1421             if ( lineEnd == lineStart )
1422             {
1423                 sel = GetLines()[lineStart].Mid(colStart, colEnd - colStart);
1424             }
1425             else // sel on multiple lines
1426             {
1427                 // take the end of the first line
1428                 sel = GetLines()[lineStart].c_str() + colStart;
1429                 sel += wxT('\n');
1430
1431                 // all intermediate ones
1432                 for ( wxTextCoord line = lineStart + 1; line < lineEnd; line++ )
1433                 {
1434                     sel << GetLines()[line] << wxT('\n');
1435                 }
1436
1437                 // and the start of the last one
1438                 sel += GetLines()[lineEnd].Left(colEnd);
1439             }
1440         }
1441     }
1442
1443     return sel;
1444 }
1445
1446 void wxTextCtrl::SetSelection(wxTextPos from, wxTextPos to)
1447 {
1448     // selecting till -1 is the same as selecting to the end
1449     if ( to == -1 )
1450     {
1451         // and selecting (-1, -1) range is the same as selecting everything, by
1452         // convention
1453         if ( from == -1 )
1454             from = 0;
1455         to = GetLastPosition();
1456     }
1457
1458     if ( from == -1 || to == from )
1459     {
1460         ClearSelection();
1461     }
1462     else // valid sel range
1463     {
1464         // remember the 'to' position as the current position, used to move the
1465         // caret there later
1466         wxTextPos toOrig = to;
1467
1468         OrderPositions(from, to);
1469
1470         wxCHECK_RET( to <= GetLastPosition(),
1471                      wxT("invalid range in wxTextCtrl::SetSelection") );
1472
1473         if ( from != m_selStart || to != m_selEnd )
1474         {
1475             // we need to use temp vars as RefreshTextRange() may call DoDraw()
1476             // directly and so m_selStart/End must be reset by then
1477             wxTextPos selStartOld = m_selStart,
1478                       selEndOld = m_selEnd;
1479
1480             m_selStart = from;
1481             m_selEnd = to;
1482
1483             wxLogTrace(wxT("text"), wxT("Selection range is %ld-%ld"),
1484                        m_selStart, m_selEnd);
1485
1486             // refresh only the part of text which became (un)selected if
1487             // possible
1488             if ( selStartOld == m_selStart )
1489             {
1490                 RefreshTextRange(selEndOld, m_selEnd);
1491             }
1492             else if ( selEndOld == m_selEnd )
1493             {
1494                 RefreshTextRange(m_selStart, selStartOld);
1495             }
1496             else
1497             {
1498                 // OPT: could check for other cases too but it is probably not
1499                 //      worth it as the two above are the most common ones
1500                 if ( selStartOld != -1 )
1501                     RefreshTextRange(selStartOld, selEndOld);
1502                 if ( m_selStart != -1 )
1503                     RefreshTextRange(m_selStart, m_selEnd);
1504             }
1505
1506             // we need to fully repaint the invalidated areas of the window
1507             // before scrolling it (from DoSetInsertionPoint which is typically
1508             // called after SetSelection()), otherwise they may stay unpainted
1509             m_targetWindow->Update();
1510         }
1511         //else: nothing to do
1512
1513         // the insertion point is put at the location where the caret was moved
1514         DoSetInsertionPoint(toOrig);
1515     }
1516 }
1517
1518 void wxTextCtrl::ClearSelection()
1519 {
1520     if ( HasSelection() )
1521     {
1522         // we need to use temp vars as RefreshTextRange() may call DoDraw()
1523         // directly (see above as well)
1524         wxTextPos selStart = m_selStart,
1525                   selEnd = m_selEnd;
1526
1527         // no selection any more
1528         m_selStart =
1529         m_selEnd = -1;
1530
1531         // refresh the old selection
1532         RefreshTextRange(selStart, selEnd);
1533     }
1534
1535     // the anchor should be moved even if there was no selection previously
1536     m_selAnchor = m_curPos;
1537 }
1538
1539 void wxTextCtrl::RemoveSelection()
1540 {
1541     if ( !HasSelection() )
1542         return;
1543
1544     Remove(m_selStart, m_selEnd);
1545 }
1546
1547 bool wxTextCtrl::GetSelectedPartOfLine(wxTextCoord line,
1548                                        wxTextPos *start, wxTextPos *end) const
1549 {
1550     if ( start )
1551         *start = -1;
1552     if ( end )
1553         *end = -1;
1554
1555     if ( !HasSelection() )
1556     {
1557         // no selection at all, hence no selection in this line
1558         return false;
1559     }
1560
1561     wxTextCoord lineStart, colStart;
1562     PositionToXY(m_selStart, &colStart, &lineStart);
1563     if ( lineStart > line )
1564     {
1565         // this line is entirely above the selection
1566         return false;
1567     }
1568
1569     wxTextCoord lineEnd, colEnd;
1570     PositionToXY(m_selEnd, &colEnd, &lineEnd);
1571     if ( lineEnd < line )
1572     {
1573         // this line is entirely below the selection
1574         return false;
1575     }
1576
1577     if ( line == lineStart )
1578     {
1579         if ( start )
1580             *start = colStart;
1581         if ( end )
1582             *end = lineEnd == lineStart ? colEnd : GetLineLength(line);
1583     }
1584     else if ( line == lineEnd )
1585     {
1586         if ( start )
1587             *start = lineEnd == lineStart ? colStart : 0;
1588         if ( end )
1589             *end = colEnd;
1590     }
1591     else // the line is entirely inside the selection
1592     {
1593         if ( start )
1594             *start = 0;
1595         if ( end )
1596             *end = GetLineLength(line);
1597     }
1598
1599     return true;
1600 }
1601
1602 // ----------------------------------------------------------------------------
1603 // flags
1604 // ----------------------------------------------------------------------------
1605
1606 bool wxTextCtrl::IsModified() const
1607 {
1608     return m_isModified;
1609 }
1610
1611 bool wxTextCtrl::IsEditable() const
1612 {
1613     // disabled control can never be edited
1614     return m_isEditable && IsEnabled();
1615 }
1616
1617 void wxTextCtrl::MarkDirty()
1618 {
1619     m_isModified = true;
1620 }
1621
1622 void wxTextCtrl::DiscardEdits()
1623 {
1624     m_isModified = false;
1625 }
1626
1627 void wxTextCtrl::SetEditable(bool editable)
1628 {
1629     if ( editable != m_isEditable )
1630     {
1631         m_isEditable = editable;
1632
1633         // the caret (dis)appears
1634         CreateCaret();
1635
1636         // the appearance of the control might have changed
1637         Refresh();
1638     }
1639 }
1640
1641 // ----------------------------------------------------------------------------
1642 // col/lines <-> position correspondence
1643 // ----------------------------------------------------------------------------
1644
1645 /*
1646     A few remarks about this stuff:
1647
1648     o   The numbering of the text control columns/rows starts from 0.
1649     o   Start of first line is position 0, its last position is line.length()
1650     o   Start of the next line is the last position of the previous line + 1
1651  */
1652
1653 int wxTextCtrl::GetLineLength(wxTextCoord line) const
1654 {
1655     if ( IsSingleLine() )
1656     {
1657         wxASSERT_MSG( line == 0, wxT("invalid GetLineLength() parameter") );
1658
1659         return m_value.length();
1660     }
1661     else // multiline
1662     {
1663         wxCHECK_MSG( (size_t)line < GetLineCount(), -1,
1664                      wxT("line index out of range") );
1665
1666         return GetLines()[line].length();
1667     }
1668 }
1669
1670 wxString wxTextCtrl::GetLineText(wxTextCoord line) const
1671 {
1672     if ( IsSingleLine() )
1673     {
1674         wxASSERT_MSG( line == 0, wxT("invalid GetLineLength() parameter") );
1675
1676         return m_value;
1677     }
1678     else // multiline
1679     {
1680         //this is called during DoGetBestSize
1681         if (line == 0 && GetLineCount() == 0) return wxEmptyString ;
1682
1683         wxCHECK_MSG( (size_t)line < GetLineCount(), wxEmptyString,
1684                      wxT("line index out of range") );
1685
1686         return GetLines()[line];
1687     }
1688 }
1689
1690 int wxTextCtrl::GetNumberOfLines() const
1691 {
1692     // there is always 1 line, even if the text is empty
1693     return IsSingleLine() ? 1 : GetLineCount();
1694 }
1695
1696 wxTextPos wxTextCtrl::XYToPosition(wxTextCoord x, wxTextCoord y) const
1697 {
1698     // note that this method should accept any values of x and y and return -1
1699     // if they are out of range
1700     if ( IsSingleLine() )
1701     {
1702         return ( x > GetLastPosition() || y > 0 ) ? wxOutOfRangeTextCoord : x;
1703     }
1704     else // multiline
1705     {
1706         if ( (size_t)y >= GetLineCount() )
1707         {
1708             // this position is below the text
1709             return GetLastPosition();
1710         }
1711
1712         wxTextPos pos = 0;
1713         for ( size_t nLine = 0; nLine < (size_t)y; nLine++ )
1714         {
1715             // +1 is because the positions at the end of this line and of the
1716             // start of the next one are different
1717             pos += GetLines()[nLine].length() + 1;
1718         }
1719
1720         // take into account also the position in line
1721         if ( (size_t)x > GetLines()[y].length() )
1722         {
1723             // don't return position in the next line
1724             x = GetLines()[y].length();
1725         }
1726
1727         return pos + x;
1728     }
1729 }
1730
1731 bool wxTextCtrl::PositionToXY(wxTextPos pos,
1732                               wxTextCoord *x, wxTextCoord *y) const
1733 {
1734     if ( IsSingleLine() )
1735     {
1736         if ( (size_t)pos > m_value.length() )
1737             return false;
1738
1739         if ( x )
1740             *x = pos;
1741         if ( y )
1742             *y = 0;
1743
1744         return true;
1745     }
1746     else // multiline
1747     {
1748         wxTextPos posCur = 0;
1749         size_t nLineCount = GetLineCount();
1750         for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
1751         {
1752             // +1 is because the start the start of the next line is one
1753             // position after the end of this one
1754             wxTextPos posNew = posCur + GetLines()[nLine].length() + 1;
1755             if ( posNew > pos )
1756             {
1757                 // we've found the line, now just calc the column
1758                 if ( x )
1759                     *x = pos - posCur;
1760
1761                 if ( y )
1762                     *y = nLine;
1763
1764 #ifdef WXDEBUG_TEXT
1765                 wxASSERT_MSG( XYToPosition(pos - posCur, nLine) == pos,
1766                               wxT("XYToPosition() or PositionToXY() broken") );
1767 #endif // WXDEBUG_TEXT
1768
1769                 return true;
1770             }
1771             else // go further down
1772             {
1773                 posCur = posNew;
1774             }
1775         }
1776
1777         // beyond the last line
1778         return false;
1779     }
1780 }
1781
1782 wxTextCoord wxTextCtrl::GetRowsPerLine(wxTextCoord line) const
1783 {
1784     // a normal line has one row
1785     wxTextCoord numRows = 1;
1786
1787     if ( WrapLines() )
1788     {
1789         // add the number of additional rows
1790         numRows += WData().m_linesData[line].GetExtraRowCount();
1791     }
1792
1793     return numRows;
1794 }
1795
1796 wxTextCoord wxTextCtrl::GetRowCount() const
1797 {
1798     wxTextCoord count = GetLineCount();
1799     if (count == 0)
1800         return 0;
1801     if ( WrapLines() )
1802     {
1803         count = GetFirstRowOfLine(count - 1) +
1804                     WData().m_linesData[count - 1].GetRowCount();
1805     }
1806
1807     return count;
1808 }
1809
1810 wxTextCoord wxTextCtrl::GetRowAfterLine(wxTextCoord line) const
1811 {
1812     if ( !WrapLines() )
1813         return line + 1;
1814
1815     if ( !WData().IsValidLine(line) )
1816     {
1817         LayoutLines(line);
1818     }
1819
1820     return WData().m_linesData[line].GetNextRow();
1821 }
1822
1823 wxTextCoord wxTextCtrl::GetFirstRowOfLine(wxTextCoord line) const
1824 {
1825     if ( !WrapLines() )
1826         return line;
1827
1828     if ( !WData().IsValidLine(line) )
1829     {
1830         LayoutLines(line);
1831     }
1832
1833     return WData().m_linesData[line].GetFirstRow();
1834 }
1835
1836 bool wxTextCtrl::PositionToLogicalXY(wxTextPos pos,
1837                                      wxCoord *xOut,
1838                                      wxCoord *yOut) const
1839 {
1840     wxTextCoord col, line;
1841
1842     // optimization for special (but common) case when we already have the col
1843     // and line
1844     if ( pos == m_curPos )
1845     {
1846         col = m_curCol;
1847         line = m_curRow;
1848     }
1849     else // must really calculate col/line from pos
1850     {
1851         if ( !PositionToXY(pos, &col, &line) )
1852             return false;
1853     }
1854
1855     int hLine = GetLineHeight();
1856     wxCoord x, y;
1857     wxString textLine = GetLineText(line);
1858     if ( IsSingleLine() || !WrapLines() )
1859     {
1860         x = GetTextWidth(textLine.Left(col));
1861         y = line*hLine;
1862     }
1863     else // difficult case: multline control with line wrap
1864     {
1865         y = GetFirstRowOfLine(line);
1866
1867         wxTextCoord colRowStart;
1868         y += GetRowInLine(line, col, &colRowStart);
1869
1870         y *= hLine;
1871
1872         // x is the width of the text before this position in this row
1873         x = GetTextWidth(textLine.Mid(colRowStart, col - colRowStart));
1874     }
1875
1876     if ( xOut )
1877         *xOut = x;
1878     if ( yOut )
1879         *yOut = y;
1880
1881     return true;
1882 }
1883
1884 bool wxTextCtrl::PositionToDeviceXY(wxTextPos pos,
1885                                     wxCoord *xOut,
1886                                     wxCoord *yOut) const
1887 {
1888     wxCoord x, y;
1889     if ( !PositionToLogicalXY(pos, &x, &y) )
1890         return false;
1891
1892     // finally translate the logical text rect coords into physical client
1893     // coords
1894     CalcScrolledPosition(m_rectText.x + x, m_rectText.y + y, xOut, yOut);
1895
1896     return true;
1897 }
1898
1899 wxPoint wxTextCtrl::GetCaretPosition() const
1900 {
1901     wxCoord xCaret, yCaret;
1902     if ( !PositionToDeviceXY(m_curPos, &xCaret, &yCaret) )
1903     {
1904         wxFAIL_MSG( wxT("Caret can't be beyond the text!") );
1905     }
1906
1907     return wxPoint(xCaret, yCaret);
1908 }
1909
1910 // pos may be -1 to show the current position
1911 void wxTextCtrl::ShowPosition(wxTextPos pos)
1912 {
1913     bool showCaret = GetCaret() && GetCaret()->IsVisible();
1914     if (showCaret)
1915         HideCaret();
1916
1917     if ( IsSingleLine() )
1918     {
1919         ShowHorzPosition(GetTextWidth(m_value.Left(pos)));
1920     }
1921     else if ( MData().m_scrollRangeX || MData().m_scrollRangeY ) // multiline with scrollbars
1922     {
1923         int xStart, yStart;
1924         GetViewStart(&xStart, &yStart);
1925
1926         if ( pos == -1 )
1927             pos = m_curPos;
1928
1929         wxCoord x, y;
1930         PositionToLogicalXY(pos, &x, &y);
1931
1932         wxRect rectText = GetRealTextArea();
1933
1934         // scroll the position vertically into view: if it is currently above
1935         // it, make it the first one, otherwise the last one
1936         if ( MData().m_scrollRangeY )
1937         {
1938             y /= GetLineHeight();
1939
1940             if ( y < yStart )
1941             {
1942                 Scroll(0, y);
1943             }
1944             else // we are currently in or below the view area
1945             {
1946                 // find the last row currently shown
1947                 wxTextCoord yEnd;
1948
1949                 if ( WrapLines() )
1950                 {
1951                     // to find the last row we need to use the generic HitTest
1952                     wxTextCoord col;
1953
1954                     // OPT this is a bit silly: we undo this in HitTest(), so
1955                     //     it would be better to factor out the common
1956                     //     functionality into a separate function (OTOH it
1957                     //     won't probably save us that much)
1958                     wxPoint pt(0, rectText.height - 1);
1959                     pt += GetClientAreaOrigin();
1960                     pt += m_rectText.GetPosition();
1961                     HitTest(pt, &col, &yEnd);
1962
1963                     // find the row inside the line
1964                     yEnd = GetFirstRowOfLine(yEnd) + GetRowInLine(yEnd, col);
1965                 }
1966                 else
1967                 {
1968                     // finding the last line is easy if each line has exactly
1969                     // one row
1970                     yEnd = yStart + rectText.height / GetLineHeight();
1971                 }
1972
1973                 if ( yEnd < y )
1974                 {
1975                     // scroll down: the current item should appear at the
1976                     // bottom of the view
1977                     Scroll(0, y - (yEnd - yStart));
1978                 }
1979             }
1980         }
1981
1982         // scroll the position horizontally into view
1983         //
1984         // we follow what I believe to be Windows behaviour here, that is if
1985         // the position is already entirely in the view we do nothing, but if
1986         // we do have to scroll the window to bring it into view, we scroll it
1987         // not just enough to show the position but slightly more so that this
1988         // position is at 1/3 of the window width from the closest border to it
1989         // (I'm not sure that Windows does exactly this but it looks like this)
1990         if ( MData().m_scrollRangeX )
1991         {
1992             // unlike for the rows, xStart doesn't correspond to the starting
1993             // column as they all have different widths, so we need to
1994             // translate everything to pixels
1995
1996             // we want the text between x and x2 be entirely inside the view
1997             // (i.e. the current character)
1998
1999             // make xStart the first visible pixel (and not position)
2000             int wChar = GetAverageWidth();
2001             xStart *= wChar;
2002
2003             if ( x < xStart )
2004             {
2005                 // we want the position of this column be 1/3 to the right of
2006                 // the left edge
2007                 x -= rectText.width / 3;
2008                 if ( x < 0 )
2009                     x = 0;
2010                 Scroll(x / wChar, y);
2011             }
2012             else // maybe we're beyond the right border of the view?
2013             {
2014                 wxTextCoord col, row;
2015                 if ( PositionToXY(pos, &col, &row) )
2016                 {
2017                     wxString lineText = GetLineText(row);
2018                     wxCoord x2 = x + GetTextWidth(lineText[(size_t)col]);
2019                     if ( x2 > xStart + rectText.width )
2020                     {
2021                         // we want the position of this column be 1/3 to the
2022                         // left of the right edge, i.e. 2/3 right of the left
2023                         // one
2024                         x2 -= (2*rectText.width)/3;
2025                         if ( x2 < 0 )
2026                             x2 = 0;
2027                         Scroll(x2 / wChar, row);
2028                     }
2029                 }
2030             }
2031         }
2032     }
2033     //else: multiline but no scrollbars, hence nothing to do
2034
2035     if (showCaret)
2036         ShowCaret();
2037 }
2038
2039 // ----------------------------------------------------------------------------
2040 // word stuff
2041 // ----------------------------------------------------------------------------
2042
2043 /*
2044     TODO: we could have (easy to do) vi-like options for word movement, i.e.
2045           distinguish between inlusive/exclusive words and between words and
2046           WORDS (in vim sense) and also, finally, make the set of characters
2047           which make up a word configurable - currently we use the exclusive
2048           WORDS only (coincidentally, this is what Windows edit control does)
2049
2050           For future references, here is what vim help says:
2051
2052           A word consists of a sequence of letters, digits and underscores, or
2053           a sequence of other non-blank characters, separated with white space
2054           (spaces, tabs, <EOL>).  This can be changed with the 'iskeyword'
2055           option.
2056
2057           A WORD consists of a sequence of non-blank characters, separated with
2058           white space.  An empty line is also considered to be a word and a
2059           WORD.
2060  */
2061
2062 static inline bool IsWordChar(wxChar ch)
2063 {
2064     return !wxIsspace(ch);
2065 }
2066
2067 wxTextPos wxTextCtrl::GetWordStart() const
2068 {
2069     if ( m_curPos == -1 || m_curPos == 0 )
2070         return 0;
2071
2072     if ( m_curCol == 0 )
2073     {
2074         // go to the end of the previous line
2075         return m_curPos - 1;
2076     }
2077
2078     // it shouldn't be possible to learn where the word starts in the password
2079     // text entry zone
2080     if ( IsPassword() )
2081         return 0;
2082
2083     // start at the previous position
2084     const wxChar *p0 = GetLineText(m_curRow).c_str();
2085     const wxChar *p = p0 + m_curCol - 1;
2086
2087     // find the end of the previous word
2088     while ( (p > p0) && !IsWordChar(*p) )
2089         p--;
2090
2091     // now find the beginning of this word
2092     while ( (p > p0) && IsWordChar(*p) )
2093         p--;
2094
2095     // we might have gone too far
2096     if ( !IsWordChar(*p) )
2097         p++;
2098
2099     return (m_curPos - m_curCol) + p - p0;
2100 }
2101
2102 wxTextPos wxTextCtrl::GetWordEnd() const
2103 {
2104     if ( m_curPos == -1 )
2105         return 0;
2106
2107     wxString line = GetLineText(m_curRow);
2108     if ( (size_t)m_curCol == line.length() )
2109     {
2110         // if we're on the last position in the line, go to the next one - if
2111         // it exists
2112         wxTextPos pos = m_curPos;
2113         if ( pos < GetLastPosition() )
2114             pos++;
2115
2116         return pos;
2117     }
2118
2119     // it shouldn't be possible to learn where the word ends in the password
2120     // text entry zone
2121     if ( IsPassword() )
2122         return GetLastPosition();
2123
2124     // start at the current position
2125     const wxChar *p0 = line.c_str();
2126     const wxChar *p = p0 + m_curCol;
2127
2128     // find the start of the next word
2129     while ( *p && !IsWordChar(*p) )
2130         p++;
2131
2132     // now find the end of it
2133     while ( *p && IsWordChar(*p) )
2134         p++;
2135
2136     // and find the start of the next word
2137     while ( *p && !IsWordChar(*p) )
2138         p++;
2139
2140     return (m_curPos - m_curCol) + p - p0;
2141 }
2142
2143 // ----------------------------------------------------------------------------
2144 // clipboard stuff
2145 // ----------------------------------------------------------------------------
2146
2147 void wxTextCtrl::Copy()
2148 {
2149 #if wxUSE_CLIPBOARD
2150     if ( HasSelection() )
2151     {
2152         wxClipboardLocker clipLock;
2153
2154         // wxTextFile::Translate() is needed to transform all '\n' into "\r\n"
2155         wxString text = wxTextFile::Translate(GetTextToShow(GetSelectionText()));
2156         wxTextDataObject *data = new wxTextDataObject(text);
2157         wxTheClipboard->SetData(data);
2158     }
2159 #endif // wxUSE_CLIPBOARD
2160 }
2161
2162 void wxTextCtrl::Cut()
2163 {
2164     (void)DoCut();
2165 }
2166
2167 bool wxTextCtrl::DoCut()
2168 {
2169     if ( !HasSelection() )
2170         return false;
2171
2172     Copy();
2173
2174     RemoveSelection();
2175
2176     return true;
2177 }
2178
2179 void wxTextCtrl::Paste()
2180 {
2181     (void)DoPaste();
2182 }
2183
2184 bool wxTextCtrl::DoPaste()
2185 {
2186 #if wxUSE_CLIPBOARD
2187     wxClipboardLocker clipLock;
2188
2189     wxTextDataObject data;
2190     if ( wxTheClipboard->IsSupported(data.GetFormat())
2191             && wxTheClipboard->GetData(data) )
2192     {
2193         // reverse transformation: '\r\n\" -> '\n'
2194         wxString text = wxTextFile::Translate(data.GetText(),
2195                                               wxTextFileType_Unix);
2196         if ( !text.empty() )
2197         {
2198             WriteText(text);
2199
2200             return true;
2201         }
2202     }
2203 #endif // wxUSE_CLIPBOARD
2204
2205     return false;
2206 }
2207
2208 // ----------------------------------------------------------------------------
2209 // Undo and redo
2210 // ----------------------------------------------------------------------------
2211
2212 wxTextCtrlInsertCommand *
2213 wxTextCtrlCommandProcessor::IsInsertCommand(wxCommand *command)
2214 {
2215     return (wxTextCtrlInsertCommand *)
2216             (command && (command->GetName() == wxTEXT_COMMAND_INSERT)
2217                 ? command : NULL);
2218 }
2219
2220 void wxTextCtrlCommandProcessor::Store(wxCommand *command)
2221 {
2222     wxTextCtrlInsertCommand *cmdIns = IsInsertCommand(command);
2223     if ( cmdIns )
2224     {
2225         if ( IsCompressing() )
2226         {
2227             wxTextCtrlInsertCommand *
2228                 cmdInsLast = IsInsertCommand(GetCurrentCommand());
2229
2230             // it is possible that we don't have any last command at all if,
2231             // for example, it was undone since the last Store(), so deal with
2232             // this case too
2233             if ( cmdInsLast )
2234             {
2235                 cmdInsLast->Append(cmdIns);
2236
2237                 delete cmdIns;
2238
2239                 // don't need to call the base class version
2240                 return;
2241             }
2242         }
2243
2244         // append the following insert commands to this one
2245         m_compressInserts = true;
2246
2247         // let the base class version will do the job normally
2248     }
2249     else // not an insert command
2250     {
2251         // stop compressing insert commands - this won't work with the last
2252         // command not being an insert one anyhow
2253         StopCompressing();
2254
2255         // let the base class version will do the job normally
2256     }
2257
2258     wxCommandProcessor::Store(command);
2259 }
2260
2261 void wxTextCtrlInsertCommand::Append(wxTextCtrlInsertCommand *other)
2262 {
2263     m_text += other->m_text;
2264 }
2265
2266 bool wxTextCtrlInsertCommand::CanUndo() const
2267 {
2268     return m_from != -1;
2269 }
2270
2271 bool wxTextCtrlInsertCommand::Do(wxTextCtrl *text)
2272 {
2273     // the text is going to be inserted at the current position, remember where
2274     // exactly it is
2275     m_from = text->GetInsertionPoint();
2276
2277     // and now do insert it
2278     text->WriteText(m_text);
2279
2280     return true;
2281 }
2282
2283 bool wxTextCtrlInsertCommand::Undo(wxTextCtrl *text)
2284 {
2285     wxCHECK_MSG( CanUndo(), false, wxT("impossible to undo insert cmd") );
2286
2287     // remove the text from where we inserted it
2288     text->Remove(m_from, m_from + m_text.length());
2289
2290     return true;
2291 }
2292
2293 bool wxTextCtrlRemoveCommand::CanUndo() const
2294 {
2295     // if we were executed, we should have the text we removed
2296     return !m_textDeleted.empty();
2297 }
2298
2299 bool wxTextCtrlRemoveCommand::Do(wxTextCtrl *text)
2300 {
2301     text->SetSelection(m_from, m_to);
2302     m_textDeleted = text->GetSelectionText();
2303     text->RemoveSelection();
2304
2305     return true;
2306 }
2307
2308 bool wxTextCtrlRemoveCommand::Undo(wxTextCtrl *text)
2309 {
2310     // it is possible that the text was deleted and that we can't restore text
2311     // at the same position we removed it any more
2312     wxTextPos posLast = text->GetLastPosition();
2313     text->SetInsertionPoint(m_from > posLast ? posLast : m_from);
2314     text->WriteText(m_textDeleted);
2315
2316     return true;
2317 }
2318
2319 void wxTextCtrl::Undo()
2320 {
2321     // the caller must check it
2322     wxASSERT_MSG( CanUndo(), wxT("can't call Undo() if !CanUndo()") );
2323
2324     m_cmdProcessor->Undo();
2325 }
2326
2327 void wxTextCtrl::Redo()
2328 {
2329     // the caller must check it
2330     wxASSERT_MSG( CanRedo(), wxT("can't call Undo() if !CanUndo()") );
2331
2332     m_cmdProcessor->Redo();
2333 }
2334
2335 bool wxTextCtrl::CanUndo() const
2336 {
2337     return IsEditable() && m_cmdProcessor->CanUndo();
2338 }
2339
2340 bool wxTextCtrl::CanRedo() const
2341 {
2342     return IsEditable() && m_cmdProcessor->CanRedo();
2343 }
2344
2345 // ----------------------------------------------------------------------------
2346 // geometry
2347 // ----------------------------------------------------------------------------
2348
2349 wxSize wxTextCtrl::DoGetBestClientSize() const
2350 {
2351     // when we're called for the very first time from Create() we must
2352     // calculate the font metrics here because we can't do it before calling
2353     // Create() (there is no window yet and wxGTK crashes) but we need them
2354     // here
2355     if ( m_heightLine == -1 )
2356     {
2357         wxConstCast(this, wxTextCtrl)->RecalcFontMetrics();
2358     }
2359
2360     wxCoord w, h;
2361     GetTextExtent(GetTextToShow(GetLineText(0)), &w, &h);
2362
2363     int wChar = GetAverageWidth(),
2364         hChar = GetLineHeight();
2365
2366     int widthMin = wxMax(10*wChar, 100);
2367     if ( w < widthMin )
2368         w = widthMin;
2369     if ( h < hChar )
2370         h = hChar;
2371
2372     if ( !IsSingleLine() )
2373     {
2374         // let the control have a reasonable number of lines
2375         int lines = GetNumberOfLines();
2376         if ( lines < 5 )
2377             lines = 5;
2378         else if ( lines > 10 )
2379             lines = 10;
2380         h *= lines;
2381     }
2382
2383     wxRect rectText;
2384     rectText.width = w;
2385     rectText.height = h;
2386     wxRect rectTotal = GetRenderer()->GetTextTotalArea(this, rectText);
2387     return wxSize(rectTotal.width, rectTotal.height);
2388 }
2389
2390 void wxTextCtrl::UpdateTextRect()
2391 {
2392     wxRect rectTotal(GetClientSize());
2393     wxCoord *extraSpace = WrapLines() ? &WData().m_widthMark : NULL;
2394     m_rectText = GetRenderer()->GetTextClientArea(this, rectTotal, extraSpace);
2395
2396     // code elsewhere is confused by negative rect size
2397     if ( m_rectText.width <= 0 )
2398         m_rectText.width = 1;
2399     if ( m_rectText.height <= 0 )
2400         m_rectText.height = 1;
2401
2402     if ( !IsSingleLine() )
2403     {
2404         // invalidate it so that GetRealTextArea() will recalc it
2405         MData().m_rectTextReal.width = 0;
2406
2407         // only scroll this rect when the window is scrolled: note that we have
2408         // to scroll not only the text but the line wrap marks too if we show
2409         // them
2410         wxRect rectText = GetRealTextArea();
2411         if ( extraSpace && *extraSpace )
2412         {
2413             rectText.width += *extraSpace;
2414         }
2415         SetTargetRect(rectText);
2416
2417         // relayout all lines
2418         if ( WrapLines() )
2419         {
2420             WData().m_rowFirstInvalid = 0;
2421
2422             // increase timestamp: this means that the lines which had been
2423             // laid out before will be relaid out the next time LayoutLines()
2424             // is called because their timestamp will be smaller than the
2425             // current one
2426             WData().m_timestamp++;
2427         }
2428     }
2429
2430     UpdateLastVisible();
2431 }
2432
2433 void wxTextCtrl::UpdateLastVisible()
2434 {
2435     // this method is only used for horizontal "scrollbarless" scrolling which
2436     // is used only with single line controls
2437     if ( !IsSingleLine() )
2438         return;
2439
2440     // use (efficient) HitTestLine to find the last visible character
2441     wxString text = m_value.Mid((size_t)SData().m_colStart /* to the end */);
2442     wxTextCoord col;
2443     switch ( HitTestLine(text, m_rectText.width, &col) )
2444     {
2445         case wxTE_HT_BEYOND:
2446             // everything is visible
2447             SData().m_ofsHorz = 0;
2448
2449             SData().m_colStart = 0;
2450             SData().m_colLastVisible = text.length();
2451
2452             // calculate it below
2453             SData().m_posLastVisible = -1;
2454             break;
2455
2456            /*
2457         case wxTE_HT_BEFORE:
2458         case wxTE_HT_BELOW:
2459             */
2460         default:
2461             wxFAIL_MSG(wxT("unexpected HitTestLine() return value"));
2462             // fall through
2463
2464         case wxTE_HT_ON_TEXT:
2465             if ( col > 0 )
2466             {
2467                 // the last entirely seen character is the previous one because
2468                 // this one is only partly visible - unless the width of the
2469                 // string is exactly the max width
2470                 SData().m_posLastVisible = GetTextWidth(text.Truncate(col + 1));
2471                 if ( SData().m_posLastVisible > m_rectText.width )
2472                 {
2473                     // this character is not entirely visible, take the
2474                     // previous one
2475                     col--;
2476
2477                     // recalc it
2478                     SData().m_posLastVisible = -1;
2479                 }
2480                 //else: we can just see it
2481
2482                 SData().m_colLastVisible = col;
2483             }
2484             break;
2485     }
2486
2487     // calculate the width of the text really shown
2488     if ( SData().m_posLastVisible == -1 )
2489     {
2490         SData().m_posLastVisible = GetTextWidth(text.Truncate(SData().m_colLastVisible + 1));
2491     }
2492
2493     // current value is relative the start of the string text which starts at
2494     // SData().m_colStart, we need an absolute offset into string
2495     SData().m_colLastVisible += SData().m_colStart;
2496
2497     wxLogTrace(wxT("text"), wxT("Last visible column/position is %d/%ld"),
2498                (int) SData().m_colLastVisible, (long) SData().m_posLastVisible);
2499 }
2500
2501 void wxTextCtrl::OnSize(wxSizeEvent& event)
2502 {
2503     UpdateTextRect();
2504
2505     if ( !IsSingleLine() )
2506     {
2507 #if 0
2508         // update them immediately because if we are called for the first time,
2509         // we need to create them in order for the base class version to
2510         // position the scrollbars correctly - if we don't do it now, it won't
2511         // happen at all if we don't get more size events
2512         UpdateScrollbars();
2513 #endif // 0
2514
2515         MData().m_updateScrollbarX =
2516         MData().m_updateScrollbarY = true;
2517     }
2518
2519     event.Skip();
2520 }
2521
2522 wxCoord wxTextCtrl::GetTotalWidth() const
2523 {
2524     wxCoord w;
2525     CalcUnscrolledPosition(m_rectText.width, 0, &w, NULL);
2526     return w;
2527 }
2528
2529 wxCoord wxTextCtrl::GetTextWidth(const wxString& text) const
2530 {
2531     wxCoord w;
2532     GetTextExtent(GetTextToShow(text), &w, NULL);
2533     return w;
2534 }
2535
2536 wxRect wxTextCtrl::GetRealTextArea() const
2537 {
2538     // for single line text control it's just the same as text rect
2539     if ( IsSingleLine() )
2540         return m_rectText;
2541
2542     // the real text area always holds an entire number of lines, so the only
2543     // difference with the text area is a narrow strip along the bottom border
2544     wxRect rectText = MData().m_rectTextReal;
2545     if ( !rectText.width )
2546     {
2547         // recalculate it
2548         rectText = m_rectText;
2549
2550         // when we're called for the very first time, the line height might not
2551         // had been calculated yet, so do get it now
2552         wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
2553         self->RecalcFontMetrics();
2554
2555         int hLine = GetLineHeight();
2556         rectText.height = (m_rectText.height / hLine) * hLine;
2557
2558         // cache the result
2559         self->MData().m_rectTextReal = rectText;
2560     }
2561
2562     return rectText;
2563 }
2564
2565 wxTextCoord wxTextCtrl::GetRowInLine(wxTextCoord line,
2566                                      wxTextCoord col,
2567                                      wxTextCoord *colRowStart) const
2568 {
2569     wxASSERT_MSG( WrapLines(), wxT("shouldn't be called") );
2570
2571     const wxWrappedLineData& lineData = WData().m_linesData[line];
2572
2573     if ( !WData().IsValidLine(line) )
2574         LayoutLines(line);
2575
2576     // row is here counted a bit specially: 0 is the 2nd row of the line (1st
2577     // extra row)
2578     size_t row = 0,
2579            rowMax = lineData.GetExtraRowCount();
2580     if ( rowMax )
2581     {
2582         row = 0;
2583         while ( (row < rowMax) && (col >= lineData.GetExtraRowStart(row)) )
2584             row++;
2585
2586         // it's ok here that row is 1 greater than needed: like this, it is
2587         // counted as a normal (and not extra) row
2588     }
2589     //else: only one row anyhow
2590
2591     if ( colRowStart )
2592     {
2593         // +1 because we need a real row number, not the extra row one
2594         *colRowStart = lineData.GetRowStart(row);
2595
2596         // this can't happen, of course
2597         wxASSERT_MSG( *colRowStart <= col, wxT("GetRowInLine() is broken") );
2598     }
2599
2600     return row;
2601 }
2602
2603 void wxTextCtrl::LayoutLine(wxTextCoord line, wxWrappedLineData& lineData) const
2604 {
2605     // FIXME: this uses old GetPartOfWrappedLine() which is not used anywhere
2606     //        else now and has rather awkward interface for our needs here
2607
2608     lineData.m_rowsStart.Empty();
2609     lineData.m_rowsWidth.Empty();
2610
2611     const wxString& text = GetLineText(line);
2612     wxCoord widthRow;
2613     size_t colRowStart = 0;
2614     do
2615     {
2616         size_t lenRow = GetPartOfWrappedLine
2617                         (
2618                             text.c_str() + colRowStart,
2619                             &widthRow
2620                         );
2621
2622         // remember the start of this row (not for the first one as
2623         // it's always 0) and its width
2624         if ( colRowStart )
2625             lineData.m_rowsStart.Add(colRowStart);
2626         lineData.m_rowsWidth.Add(widthRow);
2627
2628         colRowStart += lenRow;
2629     }
2630     while ( colRowStart < text.length() );
2631
2632     // put the current timestamp on it
2633     lineData.m_timestamp = WData().m_timestamp;
2634 }
2635
2636 void wxTextCtrl::LayoutLines(wxTextCoord lineLast) const
2637 {
2638     wxASSERT_MSG( WrapLines(), wxT("should only be used for line wrapping") );
2639
2640     // if we were called, some line was dirty and if it was dirty we must have
2641     // had m_rowFirstInvalid set to something too
2642     wxTextCoord lineFirst = WData().m_rowFirstInvalid;
2643     wxASSERT_MSG( lineFirst != -1, wxT("nothing to layout?") );
2644
2645     wxTextCoord rowFirst, rowCur;
2646     if ( lineFirst )
2647     {
2648         // start after the last known valid line
2649         const wxWrappedLineData& lineData = WData().m_linesData[lineFirst - 1];
2650         rowFirst = lineData.GetFirstRow() + lineData.GetRowCount();
2651     }
2652     else // no valid lines, start at row 0
2653     {
2654         rowFirst = 0;
2655     }
2656
2657     rowCur = rowFirst;
2658     for ( wxTextCoord line = lineFirst; line <= lineLast; line++ )
2659     {
2660         // set the starting row for this line
2661         wxWrappedLineData& lineData = WData().m_linesData[line];
2662         lineData.m_rowFirst = rowCur;
2663
2664         // had the line been already broken into rows?
2665         //
2666         // if so, compare its timestamp with the current one: if nothing has
2667         // been changed, don't relayout it
2668         if ( !lineData.IsValid() ||
2669                 (lineData.m_timestamp < WData().m_timestamp) )
2670         {
2671             // now do break it in rows
2672             LayoutLine(line, lineData);
2673         }
2674
2675         rowCur += lineData.GetRowCount();
2676     }
2677
2678     // we are now valid at least up to this line, but if it is the last one we
2679     // just don't have any more invalid rows at all
2680     if ( (size_t)lineLast == WData().m_linesData.GetCount() -1 )
2681     {
2682         lineLast = -1;
2683     }
2684
2685     wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
2686     self->WData().m_rowFirstInvalid = lineLast;
2687
2688     // also refresh the line end indicators (FIXME shouldn't do it always!)
2689     self->RefreshLineWrapMarks(rowFirst, rowCur);
2690 }
2691
2692 size_t wxTextCtrl::GetPartOfWrappedLine(const wxChar* text,
2693                                         wxCoord *widthReal) const
2694 {
2695     // this function is slow, it shouldn't be called unless really needed
2696     wxASSERT_MSG( WrapLines(), wxT("shouldn't be called") );
2697
2698     wxString s(text);
2699     wxTextCoord col;
2700     wxCoord wReal = wxDefaultCoord;
2701     switch ( HitTestLine(s, m_rectText.width, &col) )
2702     {
2703             /*
2704         case wxTE_HT_BEFORE:
2705         case wxTE_HT_BELOW:
2706             */
2707         default:
2708             wxFAIL_MSG(wxT("unexpected HitTestLine() return value"));
2709             // fall through
2710
2711         case wxTE_HT_ON_TEXT:
2712             if ( col > 0 )
2713             {
2714                 // the last entirely seen character is the previous one because
2715                 // this one is only partly visible - unless the width of the
2716                 // string is exactly the max width
2717                 wReal = GetTextWidth(s.Truncate(col + 1));
2718                 if ( wReal > m_rectText.width )
2719                 {
2720                     // this character is not entirely visible, take the
2721                     // previous one
2722                     col--;
2723
2724                     // recalc the width
2725                     wReal = wxDefaultCoord;
2726                 }
2727                 //else: we can just see it
2728
2729                 // wrap at any character or only at words boundaries?
2730                 if ( !(GetWindowStyle() & wxTE_CHARWRAP) )
2731                 {
2732                     // find the (last) not word char before this word
2733                     wxTextCoord colWordStart;
2734                     for ( colWordStart = col;
2735                           colWordStart && IsWordChar(s[(size_t)colWordStart]);
2736                           colWordStart-- )
2737                         ;
2738
2739                     if ( colWordStart > 0 )
2740                     {
2741                         if ( colWordStart != col )
2742                         {
2743                             // will have to recalc the real width
2744                             wReal = wxDefaultCoord;
2745
2746                             col = colWordStart;
2747                         }
2748                     }
2749                     //else: only a single word, have to wrap it here
2750                 }
2751             }
2752             break;
2753
2754         case wxTE_HT_BEYOND:
2755             break;
2756     }
2757
2758     // we return the number of characters, not the index of the last one
2759     if ( (size_t)col < s.length() )
2760     {
2761         // but don't return more than this (empty) string has
2762         col++;
2763     }
2764
2765     if ( widthReal )
2766     {
2767         if ( wReal == wxDefaultCoord )
2768         {
2769             // calc it if not done yet
2770             wReal = GetTextWidth(s.Truncate(col));
2771         }
2772
2773         *widthReal = wReal;
2774     }
2775
2776     // VZ: old, horribly inefficient code which can still be used for checking
2777     //     the result (in line, not word, wrap mode only) - to be removed later
2778 #if 0
2779     wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
2780     wxClientDC dc(self);
2781     dc.SetFont(GetFont());
2782     self->DoPrepareDC(dc);
2783
2784     wxCoord widthMax = m_rectText.width;
2785
2786     // the text which we can keep in this ROW
2787     wxString str;
2788     wxCoord w, wOld;
2789     for ( wOld = w = 0; *text && (w <= widthMax); )
2790     {
2791         wOld = w;
2792         str += *text++;
2793         dc.GetTextExtent(str, &w, NULL);
2794     }
2795
2796     if ( w > widthMax )
2797     {
2798         // if we wrapped, the last letter was one too much
2799         if ( str.length() > 1 )
2800         {
2801             // remove it
2802             str.erase(str.length() - 1, 1);
2803         }
2804         else // but always keep at least one letter in each row
2805         {
2806             // the real width then is the last value of w and not teh one
2807             // before last
2808             wOld = w;
2809         }
2810     }
2811     else // we didn't wrap
2812     {
2813         wOld = w;
2814     }
2815
2816     wxASSERT( col == str.length() );
2817
2818     if ( widthReal )
2819     {
2820         wxASSERT( *widthReal == wOld );
2821
2822         *widthReal = wOld;
2823     }
2824
2825     //return str.length();
2826 #endif
2827
2828     return col;
2829 }
2830
2831 // OPT: this function is called a lot - would be nice to optimize it but I
2832 //      don't really know how yet
2833 wxTextCtrlHitTestResult wxTextCtrl::HitTestLine(const wxString& line,
2834                                                 wxCoord x,
2835                                                 wxTextCoord *colOut) const
2836 {
2837     wxTextCtrlHitTestResult res = wxTE_HT_ON_TEXT;
2838
2839     int col;
2840     wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
2841     wxClientDC dc(self);
2842     dc.SetFont(GetFont());
2843     self->DoPrepareDC(dc);
2844
2845     wxCoord width;
2846     dc.GetTextExtent(line, &width, NULL);
2847     if ( x >= width )
2848     {
2849         // clicking beyond the end of line is equivalent to clicking at
2850         // the end of it, so return the last line column
2851         col = line.length();
2852         if ( col )
2853         {
2854             // unless the line is empty and so doesn't have any column at all -
2855             // in this case return 0, what else can we do?
2856             col--;
2857         }
2858
2859         res = wxTE_HT_BEYOND;
2860     }
2861     else if ( x < 0 )
2862     {
2863         col = 0;
2864
2865         res = wxTE_HT_BEFORE;
2866     }
2867     else // we're inside the line
2868     {
2869         // now calculate the column: first, approximate it with fixed-width
2870         // value and then calculate the correct value iteratively: note that
2871         // we use the first character of the line instead of (average)
2872         // GetCharWidth(): it is common to have lines of dashes, for example,
2873         // and this should give us much better approximation in such case
2874         //
2875         // OPT: maybe using (cache) m_widthAvg would be still faster? profile!
2876         dc.GetTextExtent(line[0], &width, NULL);
2877
2878         col = x / width;
2879         if ( col < 0 )
2880         {
2881             col = 0;
2882         }
2883         else if ( (size_t)col > line.length() )
2884         {
2885             col = line.length();
2886         }
2887
2888         // matchDir is the direction in which we should move to reach the
2889         // character containing the given position
2890         enum
2891         {
2892             Match_Left  = -1,
2893             Match_None  = 0,
2894             Match_Right = 1
2895         } matchDir = Match_None;
2896         for ( ;; )
2897         {
2898             // check that we didn't go beyond the line boundary
2899             if ( col < 0 )
2900             {
2901                 col = 0;
2902                 break;
2903             }
2904             if ( (size_t)col > line.length() )
2905             {
2906                 col = line.length();
2907                 break;
2908             }
2909
2910             wxString strBefore(line, (size_t)col);
2911             dc.GetTextExtent(strBefore, &width, NULL);
2912             if ( width > x )
2913             {
2914                 if ( matchDir == Match_Right )
2915                 {
2916                     // we were going to the right and, finally, moved beyond
2917                     // the original position - stop on the previous one
2918                     col--;
2919
2920                     break;
2921                 }
2922
2923                 if ( matchDir == Match_None )
2924                 {
2925                     // we just started iterating, now we know that we should
2926                     // move to the left
2927                     matchDir = Match_Left;
2928                 }
2929                 //else: we are still to the right of the target, continue
2930             }
2931             else // width < x
2932             {
2933                 // invert the logic above
2934                 if ( matchDir == Match_Left )
2935                 {
2936                     // with the exception that we don't need to backtrack here
2937                     break;
2938                 }
2939
2940                 if ( matchDir == Match_None )
2941                 {
2942                     // go to the right
2943                     matchDir = Match_Right;
2944                 }
2945             }
2946
2947             // this is not supposed to happen
2948             wxASSERT_MSG( matchDir, wxT("logic error in wxTextCtrl::HitTest") );
2949
2950             if ( matchDir == Match_Right )
2951                 col++;
2952             else
2953                 col--;
2954         }
2955     }
2956
2957     // check that we calculated it correctly
2958 #ifdef WXDEBUG_TEXT
2959     if ( res == wxTE_HT_ON_TEXT )
2960     {
2961         wxCoord width1;
2962         wxString text = line.Left(col);
2963         dc.GetTextExtent(text, &width1, NULL);
2964         if ( (size_t)col < line.length() )
2965         {
2966             wxCoord width2;
2967
2968             text += line[col];
2969             dc.GetTextExtent(text, &width2, NULL);
2970
2971             wxASSERT_MSG( (width1 <= x) && (x < width2),
2972                           wxT("incorrect HitTestLine() result") );
2973         }
2974         else // we return last char
2975         {
2976             wxASSERT_MSG( x >= width1, wxT("incorrect HitTestLine() result") );
2977         }
2978     }
2979 #endif // WXDEBUG_TEXT
2980
2981     if ( colOut )
2982         *colOut = col;
2983
2984     return res;
2985 }
2986
2987 wxTextCtrlHitTestResult wxTextCtrl::HitTest(const wxPoint& pt, long *pos) const
2988 {
2989     wxTextCoord x, y;
2990     wxTextCtrlHitTestResult rc = HitTest(pt, &x, &y);
2991     if ( rc != wxTE_HT_UNKNOWN && pos )
2992     {
2993         *pos = XYToPosition(x, y);
2994     }
2995
2996     return rc;
2997 }
2998
2999 wxTextCtrlHitTestResult wxTextCtrl::HitTest(const wxPoint& pos,
3000                                             wxTextCoord *colOut,
3001                                             wxTextCoord *rowOut) const
3002 {
3003     return HitTest2(pos.y, pos.x, 0, rowOut, colOut, NULL, NULL);
3004 }
3005
3006 wxTextCtrlHitTestResult wxTextCtrl::HitTestLogical(const wxPoint& pos,
3007                                                    wxTextCoord *colOut,
3008                                                    wxTextCoord *rowOut) const
3009 {
3010     return HitTest2(pos.y, pos.x, 0, rowOut, colOut, NULL, NULL, false);
3011 }
3012
3013 wxTextCtrlHitTestResult wxTextCtrl::HitTest2(wxCoord y0,
3014                                              wxCoord x10,
3015                                              wxCoord x20,
3016                                              wxTextCoord *rowOut,
3017                                              wxTextCoord *colStart,
3018                                              wxTextCoord *colEnd,
3019                                              wxTextCoord *colRowStartOut,
3020                                              bool deviceCoords) const
3021 {
3022     // is the point in the text area or to the right or below it?
3023     wxTextCtrlHitTestResult res = wxTE_HT_ON_TEXT;
3024
3025     // translate the window coords x0 and y0 into the client coords in the text
3026     // area by adjusting for both the client and text area offsets (unless this
3027     // was already done)
3028     int x1, y;
3029     if ( deviceCoords )
3030     {
3031         wxPoint pt = GetClientAreaOrigin() + m_rectText.GetPosition();
3032         CalcUnscrolledPosition(x10 - pt.x, y0 - pt.y, &x1, &y);
3033     }
3034     else
3035     {
3036         y = y0;
3037         x1 = x10;
3038     }
3039
3040     // calculate the row (it is really a LINE, not a ROW)
3041     wxTextCoord row;
3042
3043     // these vars are used only for WrapLines() case
3044     wxTextCoord colRowStart = 0;
3045     size_t rowLen = 0;
3046
3047     if ( colRowStartOut )
3048         *colRowStartOut = 0;
3049
3050     int hLine = GetLineHeight();
3051     if ( y < 0 )
3052     {
3053         // and clicking before it is the same as clicking on the first one
3054         row = 0;
3055
3056         res = wxTE_HT_BEFORE;
3057     }
3058     else // y >= 0
3059     {
3060         wxTextCoord rowLast = GetNumberOfLines() - 1;
3061         row = y / hLine;
3062         if ( IsSingleLine() || !WrapLines() )
3063         {
3064             // in this case row calculation is simple as all lines have the
3065             // same height and so row is the same as line
3066             if ( row > rowLast )
3067             {
3068                 // clicking below the text is the same as clicking on the last
3069                 // line
3070                 row = rowLast;
3071
3072                 res = wxTE_HT_BELOW;
3073             }
3074         }
3075         else // multline control with line wrap
3076         {
3077             // use binary search to find the line containing this row
3078             const wxArrayWrappedLinesData& linesData = WData().m_linesData;
3079             size_t lo = 0,
3080                    hi = linesData.GetCount(),
3081                    cur;
3082             while ( lo < hi )
3083             {
3084                 cur = (lo + hi)/2;
3085                 const wxWrappedLineData& lineData = linesData[cur];
3086                 if ( !WData().IsValidLine(cur) )
3087                     LayoutLines(cur);
3088                 wxTextCoord rowFirst = lineData.GetFirstRow();
3089
3090                 if ( row < rowFirst )
3091                 {
3092                     hi = cur;
3093                 }
3094                 else
3095                 {
3096                     // our row is after the first row of the cur line:
3097                     // obviously, if cur is the last line, it contains this
3098                     // row, otherwise we have to test that it is before the
3099                     // first row of the next line
3100                     bool found = cur == linesData.GetCount() - 1;
3101                     if ( found )
3102                     {
3103                         // if the row is beyond the end of text, adjust it to
3104                         // be the last one and set res accordingly
3105                         if ( (size_t)(row - rowFirst) >= lineData.GetRowCount() )
3106                         {
3107                             res = wxTE_HT_BELOW;
3108
3109                             row = lineData.GetRowCount() + rowFirst - 1;
3110                         }
3111                     }
3112                     else // not the last row
3113                     {
3114                         const wxWrappedLineData&
3115                                 lineNextData = linesData[cur + 1];
3116                         if ( !WData().IsValidLine(cur + 1) )
3117                             LayoutLines(cur + 1);
3118                         found = row < lineNextData.GetFirstRow();
3119                     }
3120
3121                     if ( found )
3122                     {
3123                         colRowStart = lineData.GetRowStart(row - rowFirst);
3124                         rowLen = lineData.GetRowLength(row - rowFirst,
3125                                                        GetLines()[cur].length());
3126                         row = cur;
3127
3128                         break;
3129                     }
3130                     else
3131                     {
3132                         lo = cur;
3133                     }
3134                 }
3135             }
3136         }
3137     }
3138
3139     if ( res == wxTE_HT_ON_TEXT )
3140     {
3141         // now find the position in the line
3142         wxString lineText = GetLineText(row),
3143                  rowText;
3144
3145         if ( colRowStart || rowLen )
3146         {
3147             // look in this row only, not in whole line
3148             rowText = lineText.Mid(colRowStart, rowLen);
3149         }
3150         else
3151         {
3152             // just take the whole string
3153             rowText = lineText;
3154         }
3155
3156         if ( colStart )
3157         {
3158             res = HitTestLine(GetTextToShow(rowText), x1, colStart);
3159
3160             if ( colRowStart )
3161             {
3162                 if ( colRowStartOut )
3163                 {
3164                     // give them the column offset in this ROW in pixels
3165                     *colRowStartOut = colRowStart;
3166                 }
3167
3168                 // take into account that the ROW doesn't start in the
3169                 // beginning of the LINE
3170                 *colStart += colRowStart;
3171             }
3172
3173             if ( colEnd )
3174             {
3175                 // the hit test result we return is for x1, so throw out
3176                 // the result for x2 here
3177                 int x2 = x1 + x20 - x10;
3178                 (void)HitTestLine(GetTextToShow(rowText), x2, colEnd);
3179
3180                 *colEnd += colRowStart;
3181             }
3182         }
3183     }
3184     else // before/after vertical text span
3185     {
3186         if ( colStart )
3187         {
3188             // fill the column with the first/last position in the
3189             // corresponding line
3190             if ( res == wxTE_HT_BEFORE )
3191                 *colStart = 0;
3192             else // res == wxTE_HT_BELOW
3193                 *colStart = GetLineText(GetNumberOfLines() - 1).length();
3194         }
3195     }
3196
3197     if ( rowOut )
3198     {
3199         // give them the row in text coords (as is)
3200         *rowOut = row;
3201     }
3202
3203     return res;
3204 }
3205
3206 bool wxTextCtrl::GetLineAndRow(wxTextCoord row,
3207                                wxTextCoord *lineOut,
3208                                wxTextCoord *rowInLineOut) const
3209 {
3210     wxTextCoord line,
3211                 rowInLine = 0;
3212
3213     if ( row < 0 )
3214         return false;
3215
3216     int nLines = GetNumberOfLines();
3217     if ( WrapLines() )
3218     {
3219         const wxArrayWrappedLinesData& linesData = WData().m_linesData;
3220         for ( line = 0; line < nLines; line++ )
3221         {
3222             if ( !WData().IsValidLine(line) )
3223                 LayoutLines(line);
3224
3225             if ( row < linesData[line].GetNextRow() )
3226             {
3227                 // we found the right line
3228                 rowInLine = row - linesData[line].GetFirstRow();
3229
3230                 break;
3231             }
3232         }
3233
3234         if ( line == nLines )
3235         {
3236             // the row is out of range
3237             return false;
3238         }
3239     }
3240     else // no line wrapping, everything is easy
3241     {
3242         if ( row >= nLines )
3243             return false;
3244
3245         line = row;
3246     }
3247
3248     if ( lineOut )
3249         *lineOut = line;
3250     if ( rowInLineOut )
3251         *rowInLineOut = rowInLine;
3252
3253     return true;
3254 }
3255
3256 // ----------------------------------------------------------------------------
3257 // scrolling
3258 // ----------------------------------------------------------------------------
3259
3260 /*
3261    wxTextCtrl has not one but two scrolling mechanisms: one is semi-automatic
3262    scrolling in both horizontal and vertical direction implemented using
3263    wxScrollHelper and the second one is manual scrolling implemented using
3264    SData().m_ofsHorz and used by the single line controls without scroll bar.
3265
3266    The first version (the standard one) always scrolls by fixed amount which is
3267    fine for vertical scrolling as all lines have the same height but is rather
3268    ugly for horizontal scrolling if proportional font is used. This is why we
3269    manually update and use SData().m_ofsHorz which contains the length of the string
3270    which is hidden beyond the left border. An important property of text
3271    controls using this kind of scrolling is that an entire number of characters
3272    is always shown and that parts of characters never appear on display -
3273    neither in the leftmost nor rightmost positions.
3274
3275    Once again, for multi line controls SData().m_ofsHorz is always 0 and scrolling is
3276    done as usual for wxScrollWindow.
3277  */
3278
3279 void wxTextCtrl::ShowHorzPosition(wxCoord pos)
3280 {
3281     wxASSERT_MSG( IsSingleLine(), wxT("doesn't work for multiline") );
3282
3283     // pos is the logical position to show
3284
3285     // SData().m_ofsHorz is the first logical position shown
3286     if ( pos < SData().m_ofsHorz )
3287     {
3288         // scroll backwards
3289         wxTextCoord col;
3290         HitTestLine(m_value, pos, &col);
3291         ScrollText(col);
3292     }
3293     else
3294     {
3295         wxCoord width = m_rectText.width;
3296         if ( !width )
3297         {
3298             // if we are called from the ctor, m_rectText is not initialized
3299             // yet, so do it now
3300             UpdateTextRect();
3301             width = m_rectText.width;
3302         }
3303
3304         // SData().m_ofsHorz + width is the last logical position shown
3305         if ( pos > SData().m_ofsHorz + width)
3306         {
3307             // scroll forward
3308             wxTextCoord col;
3309             HitTestLine(m_value, pos - width, &col);
3310             ScrollText(col + 1);
3311         }
3312     }
3313 }
3314
3315 // scroll the window horizontally so that the first visible character becomes
3316 // the one at this position
3317 void wxTextCtrl::ScrollText(wxTextCoord col)
3318 {
3319     wxASSERT_MSG( IsSingleLine(),
3320                   wxT("ScrollText() is for single line controls only") );
3321
3322     // never scroll beyond the left border
3323     if ( col < 0 )
3324         col = 0;
3325
3326     // OPT: could only get the extent of the part of the string between col
3327     //      and SData().m_colStart
3328     wxCoord ofsHorz = GetTextWidth(GetLineText(0).Left(col));
3329
3330     if ( ofsHorz != SData().m_ofsHorz )
3331     {
3332         // remember the last currently used pixel
3333         int posLastVisible = SData().m_posLastVisible;
3334         if ( posLastVisible == -1 )
3335         {
3336             // this may happen when we're called very early, during the
3337             // controls construction
3338             UpdateLastVisible();
3339
3340             posLastVisible = SData().m_posLastVisible;
3341         }
3342
3343         // NB1: to scroll to the right, offset must be negative, hence the
3344         //      order of operands
3345         int dx = SData().m_ofsHorz - ofsHorz;
3346
3347         // NB2: we call Refresh() below which results in a call to
3348         //      DoDraw(), so we must update SData().m_ofsHorz before calling it
3349         SData().m_ofsHorz = ofsHorz;
3350         SData().m_colStart = col;
3351
3352         // after changing m_colStart, recalc the last visible position: we need
3353         // to recalc the last visible position beore scrolling in order to make
3354         // it appear exactly at the right edge of the text area after scrolling
3355         UpdateLastVisible();
3356
3357 #if 0 // do we?
3358         if ( dx < 0 )
3359         {
3360             // we want to force the update of it after scrolling
3361             SData().m_colLastVisible = -1;
3362         }
3363 #endif
3364
3365         // scroll only the rectangle inside which there is the text
3366         wxRect rect = m_rectText;
3367         rect.width = posLastVisible;
3368
3369         rect = ScrollNoRefresh(dx, 0, &rect);
3370
3371         /*
3372            we need to manually refresh the part which ScrollWindow() doesn't
3373            refresh (with new API this means the part outside the rect returned
3374            by ScrollNoRefresh): indeed, if we had this:
3375
3376                                    ********o
3377
3378            where '*' is text and 'o' is blank area at the end (too small to
3379            hold the next char) then after scrolling by 2 positions to the left
3380            we're going to have
3381
3382                                    ******RRo
3383
3384            where 'R' is the area refreshed by ScrollWindow() - but we still
3385            need to refresh the 'o' at the end as it may be now big enough to
3386            hold the new character shifted into view.
3387
3388            when we are scrolling to the right, we need to update this rect as
3389            well because it might have contained something before but doesn't
3390            contain anything any more
3391          */
3392
3393         // we can combine both rectangles into one when scrolling to the left,
3394         // but we need two separate Refreshes() otherwise
3395         if ( dx > 0 )
3396         {
3397             // refresh the uncovered part on the left
3398             Refresh(true, &rect);
3399
3400             // and now the area on the right
3401             rect.x = m_rectText.x + posLastVisible;
3402             rect.width = m_rectText.width - posLastVisible;
3403         }
3404         else // scrolling to the left
3405         {
3406             // just extend the rect covering the uncovered area to the edge of
3407             // the text rect
3408             rect.width += m_rectText.width - posLastVisible;
3409         }
3410
3411         Refresh(true, &rect);
3412
3413         // I don't know exactly why is this needed here but without it we may
3414         // scroll the window again (from the same method) before the previously
3415         // invalidated area is repainted when typing *very* quickly - and this
3416         // may lead to the display corruption
3417         Update();
3418     }
3419 }
3420
3421 void wxTextCtrl::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
3422 {
3423     if ( IsSingleLine() )
3424     {
3425         // we don't use wxScrollHelper
3426         if ( xx )
3427             *xx = x + SData().m_ofsHorz;
3428         if ( yy )
3429             *yy = y;
3430     }
3431     else
3432     {
3433         // let the base class do it
3434         wxScrollHelper::CalcUnscrolledPosition(x, y, xx, yy);
3435     }
3436 }
3437
3438 void wxTextCtrl::CalcScrolledPosition(int x, int y, int *xx, int *yy) const
3439 {
3440     if ( IsSingleLine() )
3441     {
3442         // we don't use wxScrollHelper
3443         if ( xx )
3444             *xx = x - SData().m_ofsHorz;
3445         if ( yy )
3446             *yy = y;
3447     }
3448     else
3449     {
3450         // let the base class do it
3451         wxScrollHelper::CalcScrolledPosition(x, y, xx, yy);
3452     }
3453 }
3454
3455 void wxTextCtrl::DoPrepareDC(wxDC& dc)
3456 {
3457     // for single line controls we only have to deal with SData().m_ofsHorz and it's
3458     // useless to call base class version as they don't use normal scrolling
3459     if ( IsSingleLine() && SData().m_ofsHorz )
3460     {
3461         // adjust the DC origin if the text is shifted
3462         wxPoint pt = dc.GetDeviceOrigin();
3463         dc.SetDeviceOrigin(pt.x - SData().m_ofsHorz, pt.y);
3464     }
3465     else
3466     {
3467         wxScrollHelper::DoPrepareDC(dc);
3468     }
3469 }
3470
3471 void wxTextCtrl::UpdateMaxWidth(wxTextCoord line)
3472 {
3473     // OPT!
3474
3475     // check if the max width changes after this line was modified
3476     wxCoord widthMaxOld = MData().m_widthMax,
3477             width;
3478     GetTextExtent(GetLineText(line), &width, NULL);
3479
3480     if ( line == MData().m_lineLongest )
3481     {
3482         // this line was the longest one, is it still?
3483         if ( width > MData().m_widthMax )
3484         {
3485             MData().m_widthMax = width;
3486         }
3487         else if ( width < MData().m_widthMax )
3488         {
3489             // we need to find the new longest line
3490             RecalcMaxWidth();
3491         }
3492         //else: its length didn't change, nothing to do
3493     }
3494     else // it wasn't the longest line, but maybe it became it?
3495     {
3496         // GetMaxWidth() and not MData().m_widthMax as it might be not calculated yet
3497         if ( width > GetMaxWidth() )
3498         {
3499             MData().m_widthMax = width;
3500             MData().m_lineLongest = line;
3501         }
3502     }
3503
3504     MData().m_updateScrollbarX = MData().m_widthMax != widthMaxOld;
3505 }
3506
3507 void wxTextCtrl::RecalcFontMetrics()
3508 {
3509     m_heightLine = GetCharHeight();
3510     m_widthAvg = GetCharWidth();
3511 }
3512
3513 void wxTextCtrl::RecalcMaxWidth()
3514 {
3515     wxASSERT_MSG( !IsSingleLine(), wxT("only used for multiline") );
3516
3517     MData().m_widthMax = -1;
3518     (void)GetMaxWidth();
3519 }
3520
3521 wxCoord wxTextCtrl::GetMaxWidth() const
3522 {
3523     if ( MData().m_widthMax == -1 )
3524     {
3525         // recalculate it
3526
3527         // OPT: should we remember the widths of all the lines?
3528
3529         wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
3530         wxClientDC dc(self);
3531         dc.SetFont(GetFont());
3532
3533         self->MData().m_widthMax = 0;
3534
3535         size_t count = GetLineCount();
3536         for ( size_t n = 0; n < count; n++ )
3537         {
3538             wxCoord width;
3539             dc.GetTextExtent(GetLines()[n], &width, NULL);
3540             if ( width > MData().m_widthMax )
3541             {
3542                 // remember the width and the line which has it
3543                 self->MData().m_widthMax = width;
3544                 self->MData().m_lineLongest = n;
3545             }
3546         }
3547     }
3548
3549     wxASSERT_MSG( MData().m_widthMax != -1, wxT("should have at least 1 line") );
3550
3551     return MData().m_widthMax;
3552 }
3553
3554 void wxTextCtrl::UpdateScrollbars()
3555 {
3556     wxASSERT_MSG( !IsSingleLine(), wxT("only used for multiline") );
3557
3558     wxSize size = GetRealTextArea().GetSize();
3559
3560     // is our height enough to show all items?
3561     wxTextCoord nRows = GetRowCount();
3562     wxCoord lineHeight = GetLineHeight();
3563     bool showScrollbarY = nRows*lineHeight > size.y;
3564
3565     // is our width enough to show the longest line?
3566     wxCoord charWidth, maxWidth;
3567     bool showScrollbarX;
3568     if ( !WrapLines() )
3569     {
3570         charWidth = GetAverageWidth();
3571         maxWidth = GetMaxWidth();
3572         showScrollbarX = maxWidth > size.x;
3573     }
3574     else // never show the horz scrollbar
3575     {
3576         // just to suppress compiler warnings about using uninit vars below
3577         charWidth = maxWidth = 0;
3578
3579         showScrollbarX = false;
3580     }
3581
3582     // calc the scrollbars ranges
3583     int scrollRangeX = showScrollbarX
3584                         ? (maxWidth + 2*charWidth - 1) / charWidth
3585                         : 0;
3586     int scrollRangeY = showScrollbarY ? nRows : 0;
3587
3588     int scrollRangeXOld = MData().m_scrollRangeX,
3589         scrollRangeYOld = MData().m_scrollRangeY;
3590     if ( (scrollRangeY != scrollRangeYOld) || (scrollRangeX != scrollRangeXOld) )
3591     {
3592         int x, y;
3593         GetViewStart(&x, &y);
3594
3595 #if 0
3596         // we want to leave the scrollbars at the same position which means
3597         // that x and y have to be adjusted as the number of positions may have
3598         // changed
3599         //
3600         // the number of positions is calculated from knowing that last
3601         // position = range - thumbSize and thumbSize == pageSize which is
3602         // equal to the window width / pixelsPerLine
3603         if ( scrollRangeXOld )
3604         {
3605             x *= scrollRangeX - m_rectText.width / charWidth;
3606             x /= scrollRangeXOld - m_rectText.width / charWidth;
3607         }
3608
3609         if ( scrollRangeYOld )
3610             y *= scrollRangeY / scrollRangeYOld;
3611 #endif // 0
3612
3613         SetScrollbars(charWidth, lineHeight,
3614                       scrollRangeX, scrollRangeY,
3615                       x, y,
3616                       true /* no refresh */);
3617
3618         if ( scrollRangeXOld )
3619         {
3620             const int w = m_rectText.width / charWidth;
3621             if ( w != scrollRangeXOld )
3622             {
3623                 x *= scrollRangeX - w;
3624                 x /= scrollRangeXOld - w;
3625             }
3626             Scroll(x, y);
3627         }
3628
3629         MData().m_scrollRangeX = scrollRangeX;
3630         MData().m_scrollRangeY = scrollRangeY;
3631
3632         // bring the current position in view
3633         ShowPosition(-1);
3634     }
3635
3636     MData().m_updateScrollbarX =
3637     MData().m_updateScrollbarY = false;
3638 }
3639
3640 void wxTextCtrl::OnInternalIdle()
3641 {
3642     // notice that single line text control never has scrollbars
3643     if ( !IsSingleLine() &&
3644             (MData().m_updateScrollbarX || MData().m_updateScrollbarY) )
3645     {
3646         UpdateScrollbars();
3647     }
3648     wxControl::OnInternalIdle();
3649 }
3650
3651 bool wxTextCtrl::SendAutoScrollEvents(wxScrollWinEvent& event) const
3652 {
3653     bool forward = event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN;
3654     if ( event.GetOrientation() == wxHORIZONTAL )
3655     {
3656         return forward ? m_curCol <= GetLineLength(m_curRow) : m_curCol > 0;
3657     }
3658     else // wxVERTICAL
3659     {
3660         return forward ? m_curRow < GetNumberOfLines() : m_curRow > 0;
3661     }
3662 }
3663
3664 // ----------------------------------------------------------------------------
3665 // refresh
3666 // ----------------------------------------------------------------------------
3667
3668 void wxTextCtrl::RefreshSelection()
3669 {
3670     if ( HasSelection() )
3671     {
3672         RefreshTextRange(m_selStart, m_selEnd);
3673     }
3674 }
3675
3676 void wxTextCtrl::RefreshLineRange(wxTextCoord lineFirst, wxTextCoord lineLast)
3677 {
3678     wxASSERT_MSG( lineFirst <= lineLast || !lineLast,
3679                   wxT("no lines to refresh") );
3680
3681     wxRect rect;
3682     // rect.x is already 0
3683     rect.width = m_rectText.width;
3684     wxCoord h = GetLineHeight();
3685
3686     wxTextCoord rowFirst;
3687     if ( lineFirst < GetNumberOfLines() )
3688     {
3689         rowFirst = GetFirstRowOfLine(lineFirst);
3690     }
3691     else // lineFirst == GetNumberOfLines()
3692     {
3693         // lineFirst may be beyond the last line only if we refresh till
3694         // the end, otherwise it's illegal
3695         wxASSERT_MSG( lineFirst == GetNumberOfLines() && !lineLast,
3696                       wxT("invalid line range") );
3697
3698         rowFirst = GetRowAfterLine(lineFirst - 1);
3699     }
3700
3701     rect.y = rowFirst*h;
3702
3703     if ( lineLast )
3704     {
3705         // refresh till this line (inclusive)
3706         wxTextCoord rowLast = GetRowAfterLine(lineLast);
3707
3708         rect.height = (rowLast - rowFirst + 1)*h;
3709     }
3710     else // lineLast == 0 means to refresh till the end
3711     {
3712         // FIXME: calc it exactly
3713         rect.height = 32000;
3714     }
3715
3716     RefreshTextRect(rect);
3717 }
3718
3719 void wxTextCtrl::RefreshTextRange(wxTextPos start, wxTextPos end)
3720 {
3721     wxCHECK_RET( start != -1 && end != -1,
3722                  wxT("invalid RefreshTextRange() arguments") );
3723
3724     // accept arguments in any order as it is more conenient for the caller
3725     OrderPositions(start, end);
3726
3727     // this is acceptable but we don't do anything in this case
3728     if ( start == end )
3729         return;
3730
3731     wxTextPos colStart, lineStart;
3732     if ( !PositionToXY(start, &colStart, &lineStart) )
3733     {
3734         // the range is entirely beyond the end of the text, nothing to do
3735         return;
3736     }
3737
3738     wxTextCoord colEnd, lineEnd;
3739     if ( !PositionToXY(end, &colEnd, &lineEnd) )
3740     {
3741         // the range spans beyond the end of text, refresh to the end
3742         colEnd = -1;
3743         lineEnd = GetNumberOfLines() - 1;
3744     }
3745
3746     // refresh all lines one by one
3747     for ( wxTextCoord line = lineStart; line <= lineEnd; line++ )
3748     {
3749         // refresh the first line from the start of the range to the end, the
3750         // intermediate ones entirely and the last one from the beginning to
3751         // the end of the range
3752         wxTextPos posStart = line == lineStart ? colStart : 0;
3753         size_t posCount;
3754         if ( (line != lineEnd) || (colEnd == -1) )
3755         {
3756             // intermediate line or the last one but we need to refresh it
3757             // until the end anyhow - do it
3758             posCount = wxString::npos;
3759         }
3760         else // last line
3761         {
3762             // refresh just the positions in between the start and the end one
3763             posCount = colEnd - posStart;
3764         }
3765
3766         if ( posCount )
3767             RefreshColRange(line, posStart, posCount);
3768     }
3769 }
3770
3771 void wxTextCtrl::RefreshColRange(wxTextCoord line,
3772                                  wxTextPos start,
3773                                  size_t count)
3774 {
3775     wxString text = GetLineText(line);
3776
3777     wxASSERT_MSG( (size_t)start <= text.length() && count,
3778                   wxT("invalid RefreshColRange() parameter") );
3779
3780     RefreshPixelRange(line,
3781                       GetTextWidth(text.Left((size_t)start)),
3782                       GetTextWidth(text.Mid((size_t)start, (size_t)count)));
3783 }
3784
3785 // this method accepts "logical" coords in the sense that they are coordinates
3786 // in a logical line but it can span several rows if we wrap lines and
3787 // RefreshPixelRange() will then refresh several rows
3788 void wxTextCtrl::RefreshPixelRange(wxTextCoord line,
3789                                    wxCoord start,
3790                                    wxCoord width)
3791 {
3792     // we will use line text only in line wrap case
3793     wxString text;
3794     if ( WrapLines() )
3795     {
3796         text = GetLineText(line);
3797     }
3798
3799     // special case: width == 0 means to refresh till the end of line
3800     if ( width == 0 )
3801     {
3802         // refresh till the end of visible line
3803         width = GetTotalWidth();
3804
3805         if ( WrapLines() )
3806         {
3807             // refresh till the end of text
3808             wxCoord widthAll = GetTextWidth(text);
3809
3810             // extend width to the end of ROW
3811             width = widthAll - widthAll % width + width;
3812         }
3813
3814         // no need to refresh beyond the end of line
3815         width -= start;
3816     }
3817     //else: just refresh the specified part
3818
3819     wxCoord h = GetLineHeight();
3820     wxRect rect;
3821     rect.x = start;
3822     rect.y = GetFirstRowOfLine(line)*h;
3823     rect.height = h;
3824
3825     if ( WrapLines() )
3826     {
3827         // (1) skip all rows which we don't touch at all
3828         const wxWrappedLineData& lineData = WData().m_linesData[line];
3829         if ( !WData().IsValidLine(line) )
3830             LayoutLines(line);
3831
3832         wxCoord wLine = 0; // suppress compiler warning about uninit var
3833         size_t rowLast = lineData.GetRowCount(),
3834                row = 0;
3835         while ( (row < rowLast) &&
3836                 (rect.x > (wLine = lineData.GetRowWidth(row++))) )
3837         {
3838             rect.x -= wLine;
3839             rect.y += h;
3840         }
3841
3842         // (2) now refresh all lines except the last one: note that the first
3843         //     line is refreshed from the given start to the end, all the next
3844         //     ones - entirely
3845         while ( (row < rowLast) && (width > wLine - rect.x) )
3846         {
3847             rect.width = GetTotalWidth() - rect.x;
3848             RefreshTextRect(rect);
3849
3850             width -= wLine - rect.x;
3851             rect.x = 0;
3852             rect.y += h;
3853
3854             wLine = lineData.GetRowWidth(row++);
3855         }
3856
3857         // (3) the code below will refresh the last line
3858     }
3859
3860     rect.width = width;
3861
3862     RefreshTextRect(rect);
3863 }
3864
3865 void wxTextCtrl::RefreshTextRect(const wxRect& rectClient, bool textOnly)
3866 {
3867     wxRect rect;
3868     CalcScrolledPosition(rectClient.x, rectClient.y, &rect.x, &rect.y);
3869     rect.width = rectClient.width;
3870     rect.height = rectClient.height;
3871
3872     // account for the text area offset
3873     rect.Offset(m_rectText.GetPosition());
3874
3875     // don't refresh beyond the text area unless we're refreshing the line wrap
3876     // marks in which case textOnly is false
3877     if ( textOnly )
3878     {
3879         if ( rect.GetRight() > m_rectText.GetRight() )
3880         {
3881             rect.SetRight(m_rectText.GetRight());
3882
3883             if ( rect.width <= 0 )
3884             {
3885                 // nothing to refresh
3886                 return;
3887             }
3888         }
3889     }
3890
3891     // check the bottom boundary always, even for the line wrap marks
3892     if ( rect.GetBottom() > m_rectText.GetBottom() )
3893     {
3894         rect.SetBottom(m_rectText.GetBottom());
3895
3896         if ( rect.height <= 0 )
3897         {
3898             // nothing to refresh
3899             return;
3900         }
3901     }
3902
3903     // never refresh before the visible rect
3904     if ( rect.x < m_rectText.x )
3905         rect.x = m_rectText.x;
3906
3907     if ( rect.y < m_rectText.y )
3908         rect.y = m_rectText.y;
3909
3910     wxLogTrace(wxT("text"), wxT("Refreshing (%d, %d)-(%d, %d)"),
3911                rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
3912
3913     Refresh(true, &rect);
3914 }
3915
3916 void wxTextCtrl::RefreshLineWrapMarks(wxTextCoord rowFirst,
3917                                       wxTextCoord rowLast)
3918 {
3919     if ( WData().m_widthMark )
3920     {
3921         wxRect rectMarks;
3922         rectMarks.x = m_rectText.width;
3923         rectMarks.width = WData().m_widthMark;
3924         rectMarks.y = rowFirst*GetLineHeight();
3925         rectMarks.height = (rowLast - rowFirst)*GetLineHeight();
3926
3927         RefreshTextRect(rectMarks, false /* don't limit to text area */);
3928     }
3929 }
3930
3931 // ----------------------------------------------------------------------------
3932 // border drawing
3933 // ----------------------------------------------------------------------------
3934
3935 void wxTextCtrl::DoDrawBorder(wxDC& dc, const wxRect& rect)
3936 {
3937     m_renderer->DrawTextBorder(dc, GetBorder(), rect, GetStateFlags());
3938 }
3939
3940 // ----------------------------------------------------------------------------
3941 // client area drawing
3942 // ----------------------------------------------------------------------------
3943
3944 /*
3945    Several remarks about wxTextCtrl redraw logic:
3946
3947    1. only the regions which must be updated are redrawn, this means that we
3948       never Refresh() the entire window but use RefreshPixelRange() and
3949       ScrollWindow() which only refresh small parts of it and iterate over the
3950       update region in our DoDraw()
3951
3952    2. the text displayed on the screen is obtained using GetTextToShow(): it
3953       should be used for all drawing/measuring
3954  */
3955
3956 wxString wxTextCtrl::GetTextToShow(const wxString& text) const
3957 {
3958     wxString textShown;
3959     if ( IsPassword() )
3960         textShown = wxString(wxT('*'), text.length());
3961     else
3962         textShown = text;
3963
3964     return textShown;
3965 }
3966
3967 void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate)
3968 {
3969     // debugging trick to see the update rect visually
3970 #ifdef WXDEBUG_TEXT
3971     static int s_countUpdates = -1;
3972     if ( s_countUpdates != -1 )
3973     {
3974         wxWindowDC dc(this);
3975         dc.SetBrush(*(++s_countUpdates % 2 ? wxRED_BRUSH : wxGREEN_BRUSH));
3976         dc.SetPen(*wxTRANSPARENT_PEN);
3977         dc.DrawRectangle(rectUpdate);
3978     }
3979 #endif // WXDEBUG_TEXT
3980
3981     // calculate the range lineStart..lineEnd of lines to redraw
3982     wxTextCoord lineStart, lineEnd;
3983     if ( IsSingleLine() )
3984     {
3985         lineStart =
3986         lineEnd = 0;
3987     }
3988     else // multiline
3989     {
3990         wxPoint pt = rectUpdate.GetPosition();
3991         (void)HitTest(pt, NULL, &lineStart);
3992
3993         pt.y += rectUpdate.height;
3994         (void)HitTest(pt, NULL, &lineEnd);
3995     }
3996
3997     // prepare for drawing
3998     wxCoord hLine = GetLineHeight();
3999
4000     // these vars will be used for hit testing of the current row
4001     wxCoord y = rectUpdate.y;
4002     const wxCoord x1 = rectUpdate.x;
4003     const wxCoord x2 = rectUpdate.x + rectUpdate.width;
4004
4005     wxRect rectText;
4006     rectText.height = hLine;
4007     wxCoord yClient = y - GetClientAreaOrigin().y;
4008
4009     // we want to always start at the top of the line, otherwise if we redraw a
4010     // rect whose top is in the middle of a line, we'd draw this line shifted
4011     yClient -= (yClient - m_rectText.y) % hLine;
4012
4013     if ( IsSingleLine() )
4014     {
4015         rectText.y = yClient;
4016     }
4017     else // multiline, adjust for scrolling
4018     {
4019         CalcUnscrolledPosition(0, yClient, NULL, &rectText.y);
4020     }
4021
4022     wxRenderer *renderer = GetRenderer();
4023
4024     // do draw the invalidated parts of each line: note that we iterate here
4025     // over ROWs, not over LINEs
4026     for ( wxTextCoord line = lineStart;
4027           y < rectUpdate.y + rectUpdate.height;
4028           y += hLine,
4029           rectText.y += hLine )
4030     {
4031         // calculate the update rect in text positions for this line
4032         wxTextCoord colStart, colEnd, colRowStart;
4033         wxTextCtrlHitTestResult ht = HitTest2(y, x1, x2,
4034                                               &line, &colStart, &colEnd,
4035                                               &colRowStart);
4036
4037         if ( (ht == wxTE_HT_BEYOND) || (ht == wxTE_HT_BELOW) )
4038         {
4039             wxASSERT_MSG( line <= lineEnd, wxT("how did we get that far?") );
4040
4041             if ( line == lineEnd )
4042             {
4043                 // we redrew everything
4044                 break;
4045             }
4046
4047             // the update rect is beyond the end of line, no need to redraw
4048             // anything on this line - but continue with the remaining ones
4049             continue;
4050         }
4051
4052         // for single line controls we may additionally cut off everything
4053         // which is to the right of the last visible position
4054         if ( IsSingleLine() )
4055         {
4056             // don't show the columns which are scrolled out to the left
4057             if ( colStart < SData().m_colStart )
4058                 colStart = SData().m_colStart;
4059
4060             // colEnd may be less than colStart if colStart was changed by the
4061             // assignment above
4062             if ( colEnd < colStart )
4063                 colEnd = colStart;
4064
4065             // don't draw the chars beyond the rightmost one
4066             if ( SData().m_colLastVisible == -1 )
4067             {
4068                 // recalculate this rightmost column
4069                 UpdateLastVisible();
4070             }
4071
4072             if ( colStart > SData().m_colLastVisible )
4073             {
4074                 // don't bother redrawing something that is beyond the last
4075                 // visible position
4076                 continue;
4077             }
4078
4079             if ( colEnd > SData().m_colLastVisible )
4080             {
4081                 colEnd = SData().m_colLastVisible;
4082             }
4083         }
4084
4085         // extract the part of line we need to redraw
4086         wxString textLine = GetTextToShow(GetLineText(line));
4087         wxString text = textLine.Mid(colStart, colEnd - colStart + 1);
4088
4089         // now deal with the selection: only do something if at least part of
4090         // the line is selected
4091         wxTextPos selStart, selEnd;
4092         if ( GetSelectedPartOfLine(line, &selStart, &selEnd) )
4093         {
4094             // and if this part is (at least partly) in the current row
4095             if ( (selStart <= colEnd) &&
4096                     (selEnd >= wxMax(colStart, colRowStart)) )
4097             {
4098                 // these values are relative to the start of the line while the
4099                 // string passed to DrawTextLine() is only part of it, so
4100                 // adjust the selection range accordingly
4101                 selStart -= colStart;
4102                 selEnd -= colStart;
4103
4104                 if ( selStart < 0 )
4105                     selStart = 0;
4106
4107                 if ( (size_t)selEnd >= text.length() )
4108                     selEnd = text.length();
4109             }
4110             else
4111             {
4112                 // reset selStart and selEnd to avoid passing them to
4113                 // DrawTextLine() below
4114                 selStart =
4115                 selEnd = -1;
4116             }
4117         }
4118
4119         // calculate the text coords on screen
4120         wxASSERT_MSG( colStart >= colRowStart, wxT("invalid string part") );
4121         wxCoord ofsStart = GetTextWidth(
4122                                     textLine.Mid(colRowStart,
4123                                                  colStart - colRowStart));
4124         rectText.x = m_rectText.x + ofsStart;
4125         rectText.width = GetTextWidth(text);
4126
4127         // do draw the text
4128         renderer->DrawTextLine(dc, text, rectText, selStart, selEnd,
4129                                GetStateFlags());
4130         wxLogTrace(wxT("text"), wxT("Line %ld: positions %ld-%ld redrawn."),
4131                    line, colStart, colEnd);
4132     }
4133 }
4134
4135 void wxTextCtrl::DoDrawLineWrapMarks(wxDC& dc, const wxRect& rectUpdate)
4136 {
4137     wxASSERT_MSG( WrapLines() && WData().m_widthMark,
4138                   wxT("shouldn't be called at all") );
4139
4140     wxRenderer *renderer = GetRenderer();
4141
4142     wxRect rectMark;
4143     rectMark.x = rectUpdate.x;
4144     rectMark.width = rectUpdate.width;
4145     wxCoord yTop = GetClientAreaOrigin().y;
4146     CalcUnscrolledPosition(0, rectUpdate.y - yTop, NULL, &rectMark.y);
4147     wxCoord hLine = GetLineHeight();
4148     rectMark.height = hLine;
4149
4150     wxTextCoord line, rowInLine;
4151
4152     wxCoord yBottom;
4153     CalcUnscrolledPosition(0, rectUpdate.GetBottom() - yTop, NULL, &yBottom);
4154     for ( ; rectMark.y < yBottom; rectMark.y += hLine )
4155     {
4156         if ( !GetLineAndRow(rectMark.y / hLine, &line, &rowInLine) )
4157         {
4158             // we went beyond the end of text
4159             break;
4160         }
4161
4162         // is this row continued on the next one?
4163         if ( !WData().m_linesData[line].IsLastRow(rowInLine) )
4164         {
4165             renderer->DrawLineWrapMark(dc, rectMark);
4166         }
4167     }
4168 }
4169
4170 void wxTextCtrl::DoDraw(wxControlRenderer *renderer)
4171 {
4172     // hide the caret while we're redrawing the window and show it after we are
4173     // done with it
4174     wxCaretSuspend cs(this);
4175
4176     // prepare the DC
4177     wxDC& dc = renderer->GetDC();
4178     dc.SetFont(GetFont());
4179     dc.SetTextForeground(GetForegroundColour());
4180
4181     // get the intersection of the update region with the text area: note that
4182     // the update region is in window coords and text area is in the client
4183     // ones, so it must be shifted before computing intersection
4184     wxRegion rgnUpdate = GetUpdateRegion();
4185
4186     wxRect rectTextArea = GetRealTextArea();
4187     wxPoint pt = GetClientAreaOrigin();
4188     wxRect rectTextAreaAdjusted = rectTextArea;
4189     rectTextAreaAdjusted.x += pt.x;
4190     rectTextAreaAdjusted.y += pt.y;
4191     rgnUpdate.Intersect(rectTextAreaAdjusted);
4192
4193     // even though the drawing is already clipped to the update region, we must
4194     // explicitly clip it to the rect we will use as otherwise parts of letters
4195     // might be drawn outside of it (if even a small part of a charater is
4196     // inside, HitTest() will return its column and DrawText() can't draw only
4197     // the part of the character, of course)
4198 #ifdef __WXMSW__
4199     // FIXME: is this really a bug in wxMSW?
4200     rectTextArea.width--;
4201 #endif // __WXMSW__
4202     dc.DestroyClippingRegion();
4203     dc.SetClippingRegion(rectTextArea);
4204
4205     // adjust for scrolling
4206     DoPrepareDC(dc);
4207
4208     // and now refresh the invalidated parts of the window
4209     wxRegionIterator iter(rgnUpdate);
4210     for ( ; iter.HaveRects(); iter++ )
4211     {
4212         wxRect r = iter.GetRect();
4213
4214         // this is a workaround for wxGTK::wxRegion bug
4215 #ifdef __WXGTK__
4216         if ( !r.width || !r.height )
4217         {
4218             // ignore invalid rect
4219             continue;
4220         }
4221 #endif // __WXGTK__
4222
4223         DoDrawTextInRect(dc, r);
4224     }
4225
4226     // now redraw the line wrap marks (if we draw them)
4227     if ( WrapLines() && WData().m_widthMark )
4228     {
4229         // this is the rect inside which line wrap marks are drawn
4230         wxRect rectMarks;
4231         rectMarks.x = rectTextAreaAdjusted.GetRight() + 1;
4232         rectMarks.y = rectTextAreaAdjusted.y;
4233         rectMarks.width = WData().m_widthMark;
4234         rectMarks.height = rectTextAreaAdjusted.height;
4235
4236         rgnUpdate = GetUpdateRegion();
4237         rgnUpdate.Intersect(rectMarks);
4238
4239         wxRect rectUpdate = rgnUpdate.GetBox();
4240         if ( rectUpdate.width && rectUpdate.height )
4241         {
4242             // the marks are outside previously set clipping region
4243             dc.DestroyClippingRegion();
4244
4245             DoDrawLineWrapMarks(dc, rectUpdate);
4246         }
4247     }
4248
4249     // show caret first time only: we must show it after drawing the text or
4250     // the display can be corrupted when it's hidden
4251     if ( !m_hasCaret && GetCaret() && (FindFocus() == this) )
4252     {
4253         ShowCaret();
4254
4255         m_hasCaret = true;
4256     }
4257 }
4258
4259 // ----------------------------------------------------------------------------
4260 // caret
4261 // ----------------------------------------------------------------------------
4262
4263 bool wxTextCtrl::SetFont(const wxFont& font)
4264 {
4265     if ( !wxControl::SetFont(font) )
4266         return false;
4267
4268     // and refresh everything, of course
4269     InitInsertionPoint();
4270     ClearSelection();
4271
4272     // update geometry parameters
4273     UpdateTextRect();
4274     RecalcFontMetrics();
4275     if ( !IsSingleLine() )
4276     {
4277         UpdateScrollbars();
4278         RecalcMaxWidth();
4279     }
4280
4281     // recreate it, in fact
4282     CreateCaret();
4283
4284     Refresh();
4285
4286     return true;
4287 }
4288
4289 bool wxTextCtrl::Enable(bool enable)
4290 {
4291     if ( !wxTextCtrlBase::Enable(enable) )
4292         return false;
4293
4294     if (FindFocus() == this && GetCaret() &&
4295         ((enable && !GetCaret()->IsVisible()) ||
4296          (!enable && GetCaret()->IsVisible())))
4297         ShowCaret(enable);
4298
4299     return true;
4300 }
4301
4302 void wxTextCtrl::CreateCaret()
4303 {
4304     wxCaret *caret;
4305
4306     if ( IsEditable() )
4307     {
4308         // FIXME use renderer
4309         caret = new wxCaret(this, 1, GetLineHeight());
4310     }
4311     else
4312     {
4313         // read only controls don't have the caret
4314         caret = NULL;
4315     }
4316
4317     // SetCaret() will delete the old caret if any
4318     SetCaret(caret);
4319 }
4320
4321 void wxTextCtrl::ShowCaret(bool show)
4322 {
4323     wxCaret *caret = GetCaret();
4324     if ( caret )
4325     {
4326         // (re)position caret correctly
4327         caret->Move(GetCaretPosition());
4328
4329         // and show it there
4330         if ((show && !caret->IsVisible()) ||
4331             (!show && caret->IsVisible()))
4332             caret->Show(show);
4333     }
4334 }
4335
4336 // ----------------------------------------------------------------------------
4337 // vertical scrolling (multiline only)
4338 // ----------------------------------------------------------------------------
4339
4340 size_t wxTextCtrl::GetLinesPerPage() const
4341 {
4342     if ( IsSingleLine() )
4343         return 1;
4344
4345     return GetRealTextArea().height / GetLineHeight();
4346 }
4347
4348 wxTextPos wxTextCtrl::GetPositionAbove()
4349 {
4350     wxCHECK_MSG( !IsSingleLine(), INVALID_POS_VALUE,
4351                  wxT("can't move cursor vertically in a single line control") );
4352
4353     // move the cursor up by one ROW not by one LINE: this means that
4354     // we should really use HitTest() and not just go to the same
4355     // position in the previous line
4356     wxPoint pt = GetCaretPosition() - m_rectText.GetPosition();
4357     if ( MData().m_xCaret == -1 )
4358     {
4359         // remember the initial cursor abscissa
4360         MData().m_xCaret = pt.x;
4361     }
4362     else
4363     {
4364         // use the remembered abscissa
4365         pt.x = MData().m_xCaret;
4366     }
4367
4368     CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
4369     pt.y -= GetLineHeight();
4370
4371     wxTextCoord col, row;
4372     if ( HitTestLogical(pt, &col, &row) == wxTE_HT_BEFORE )
4373     {
4374         // can't move further
4375         return INVALID_POS_VALUE;
4376     }
4377
4378     return XYToPosition(col, row);
4379 }
4380
4381 wxTextPos wxTextCtrl::GetPositionBelow()
4382 {
4383     wxCHECK_MSG( !IsSingleLine(), INVALID_POS_VALUE,
4384                  wxT("can't move cursor vertically in a single line control") );
4385
4386     // see comments for wxACTION_TEXT_UP
4387     wxPoint pt = GetCaretPosition() - m_rectText.GetPosition();
4388     if ( MData().m_xCaret == -1 )
4389     {
4390         // remember the initial cursor abscissa
4391         MData().m_xCaret = pt.x;
4392     }
4393     else
4394     {
4395         // use the remembered abscissa
4396         pt.x = MData().m_xCaret;
4397     }
4398
4399     CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
4400     pt.y += GetLineHeight();
4401
4402     wxTextCoord col, row;
4403     if ( HitTestLogical(pt, &col, &row) == wxTE_HT_BELOW )
4404     {
4405         // can't go further down
4406         return INVALID_POS_VALUE;
4407     }
4408
4409     // note that wxTE_HT_BEYOND is ok: it happens when we go down
4410     // from a longer line to a shorter one, for example (OTOH
4411     // wxTE_HT_BEFORE can never happen)
4412     return XYToPosition(col, row);
4413 }
4414
4415 // ----------------------------------------------------------------------------
4416 // input
4417 // ----------------------------------------------------------------------------
4418
4419 bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
4420                                long numArg,
4421                                const wxString& strArg)
4422 {
4423     // has the text changed as result of this action?
4424     bool textChanged = false;
4425
4426     // the remembered cursor abscissa for multiline text controls is usually
4427     // reset after each user action but for ones which do use it (UP and DOWN
4428     // for example) we shouldn't do it - as indicated by this flag
4429     bool rememberAbscissa = false;
4430
4431     // the command this action corresponds to or NULL if this action doesn't
4432     // change text at all or can't be undone
4433     wxTextCtrlCommand *command = NULL;
4434
4435     wxString action;
4436     bool del = false,
4437          sel = false;
4438     if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_DEL, &action) )
4439     {
4440         if ( IsEditable() )
4441             del = true;
4442     }
4443     else if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_SEL, &action) )
4444     {
4445         sel = true;
4446     }
4447     else // not selection nor delete action
4448     {
4449         action = actionOrig;
4450     }
4451
4452     // set newPos to -2 as it can't become equal to it in the assignments below
4453     // (but it can become -1)
4454     wxTextPos newPos = INVALID_POS_VALUE;
4455
4456     if ( action == wxACTION_TEXT_HOME )
4457     {
4458         newPos = m_curPos - m_curCol;
4459     }
4460     else if ( action == wxACTION_TEXT_END )
4461     {
4462         newPos = m_curPos + GetLineLength(m_curRow) - m_curCol;
4463     }
4464     else if ( (action == wxACTION_TEXT_GOTO) ||
4465               (action == wxACTION_TEXT_FIRST) ||
4466               (action == wxACTION_TEXT_LAST) )
4467     {
4468         if ( action == wxACTION_TEXT_FIRST )
4469             numArg = 0;
4470         else if ( action == wxACTION_TEXT_LAST )
4471             numArg = GetLastPosition();
4472         //else: numArg already contains the position
4473
4474         newPos = numArg;
4475     }
4476     else if ( action == wxACTION_TEXT_UP )
4477     {
4478         if ( !IsSingleLine() )
4479         {
4480             newPos = GetPositionAbove();
4481
4482             if ( newPos != INVALID_POS_VALUE )
4483             {
4484                 // remember where the cursor original had been
4485                 rememberAbscissa = true;
4486             }
4487         }
4488     }
4489     else if ( action == wxACTION_TEXT_DOWN )
4490     {
4491         if ( !IsSingleLine() )
4492         {
4493             newPos = GetPositionBelow();
4494
4495             if ( newPos != INVALID_POS_VALUE )
4496             {
4497                 // remember where the cursor original had been
4498                 rememberAbscissa = true;
4499             }
4500         }
4501     }
4502     else if ( action == wxACTION_TEXT_LEFT )
4503     {
4504         newPos = m_curPos - 1;
4505     }
4506     else if ( action == wxACTION_TEXT_WORD_LEFT )
4507     {
4508         newPos = GetWordStart();
4509     }
4510     else if ( action == wxACTION_TEXT_RIGHT )
4511     {
4512         newPos = m_curPos + 1;
4513     }
4514     else if ( action == wxACTION_TEXT_WORD_RIGHT )
4515     {
4516         newPos = GetWordEnd();
4517     }
4518     else if ( action == wxACTION_TEXT_INSERT )
4519     {
4520         if ( IsEditable() && !strArg.empty() )
4521         {
4522             // inserting text can be undone
4523             command = new wxTextCtrlInsertCommand(strArg);
4524
4525             textChanged = true;
4526         }
4527     }
4528     else if ( (action == wxACTION_TEXT_PAGE_UP) ||
4529               (action == wxACTION_TEXT_PAGE_DOWN) )
4530     {
4531         if ( !IsSingleLine() )
4532         {
4533             size_t count = GetLinesPerPage();
4534             if ( count > PAGE_OVERLAP_IN_LINES )
4535             {
4536                 // pages should overlap slightly to allow the reader to keep
4537                 // orientation in the text
4538                 count -= PAGE_OVERLAP_IN_LINES;
4539             }
4540
4541             // remember where the cursor original had been
4542             rememberAbscissa = true;
4543
4544             bool goUp = action == wxACTION_TEXT_PAGE_UP;
4545             for ( size_t line = 0; line < count; line++ )
4546             {
4547                 wxTextPos pos = goUp ? GetPositionAbove() : GetPositionBelow();
4548                 if ( pos == INVALID_POS_VALUE )
4549                 {
4550                     // can't move further
4551                     break;
4552                 }
4553
4554                 MoveInsertionPoint(pos);
4555                 newPos = pos;
4556             }
4557
4558             // we implement the Unix scrolling model here: cursor will always
4559             // be on the first line after Page Down and on the last one after
4560             // Page Up
4561             //
4562             // Windows programs usually keep the cursor line offset constant
4563             // but do we really need it?
4564             wxCoord y;
4565             if ( goUp )
4566             {
4567                 // find the line such that when it is the first one, the
4568                 // current position is in the last line
4569                 wxTextPos pos = 0;
4570                 for ( size_t line = 0; line < count; line++ )
4571                 {
4572                     pos = GetPositionAbove();
4573                     if ( pos == INVALID_POS_VALUE )
4574                         break;
4575
4576                     MoveInsertionPoint(pos);
4577                 }
4578
4579                 MoveInsertionPoint(newPos);
4580
4581                 PositionToLogicalXY(pos, NULL, &y);
4582             }
4583             else // scrolled down
4584             {
4585                 PositionToLogicalXY(newPos, NULL, &y);
4586             }
4587
4588             // scroll vertically only
4589             Scroll(wxDefaultCoord, y);
4590         }
4591     }
4592     else if ( action == wxACTION_TEXT_SEL_WORD )
4593     {
4594         SetSelection(GetWordStart(), GetWordEnd());
4595     }
4596     else if ( action == wxACTION_TEXT_ANCHOR_SEL )
4597     {
4598         newPos = numArg;
4599     }
4600     else if ( action == wxACTION_TEXT_EXTEND_SEL )
4601     {
4602         SetSelection(m_selAnchor, numArg);
4603     }
4604     else if ( action == wxACTION_TEXT_COPY )
4605     {
4606         Copy();
4607     }
4608     else if ( action == wxACTION_TEXT_CUT )
4609     {
4610         if ( IsEditable() )
4611             Cut();
4612     }
4613     else if ( action == wxACTION_TEXT_PASTE )
4614     {
4615         if ( IsEditable() )
4616             Paste();
4617     }
4618     else if ( action == wxACTION_TEXT_UNDO )
4619     {
4620         if ( CanUndo() )
4621             Undo();
4622     }
4623     else if ( action == wxACTION_TEXT_REDO )
4624     {
4625         if ( CanRedo() )
4626             Redo();
4627     }
4628     else
4629     {
4630         return wxControl::PerformAction(action, numArg, strArg);
4631     }
4632
4633     if ( newPos != INVALID_POS_VALUE )
4634     {
4635         // bring the new position into the range
4636         if ( newPos < 0 )
4637             newPos = 0;
4638
4639         wxTextPos posLast = GetLastPosition();
4640         if ( newPos > posLast )
4641             newPos = posLast;
4642
4643         if ( del )
4644         {
4645             // if we have the selection, remove just it
4646             wxTextPos from, to;
4647             if ( HasSelection() )
4648             {
4649                 from = m_selStart;
4650                 to = m_selEnd;
4651             }
4652             else
4653             {
4654                 // otherwise delete everything between current position and
4655                 // the new one
4656                 if ( m_curPos != newPos )
4657                 {
4658                     from = m_curPos;
4659                     to = newPos;
4660                 }
4661                 else // nothing to delete
4662                 {
4663                     // prevent test below from working
4664                     from = INVALID_POS_VALUE;
4665
4666                     // and this is just to silent the compiler warning
4667                     to = 0;
4668                 }
4669             }
4670
4671             if ( from != INVALID_POS_VALUE )
4672             {
4673                 command = new wxTextCtrlRemoveCommand(from, to);
4674             }
4675         }
4676         else // cursor movement command
4677         {
4678             // just go there
4679             DoSetInsertionPoint(newPos);
4680
4681             if ( sel )
4682             {
4683                 SetSelection(m_selAnchor, m_curPos);
4684             }
4685             else // simple movement
4686             {
4687                 // clear the existing selection
4688                 ClearSelection();
4689             }
4690         }
4691
4692         if ( !rememberAbscissa && !IsSingleLine() )
4693         {
4694             MData().m_xCaret = -1;
4695         }
4696     }
4697
4698     if ( command )
4699     {
4700         // execute and remember it to be able to undo it later
4701         m_cmdProcessor->Submit(command);
4702
4703         // undoable commands always change text
4704         textChanged = true;
4705     }
4706     else // no undoable command
4707     {
4708         // m_cmdProcessor->StopCompressing()
4709     }
4710
4711     if ( textChanged )
4712     {
4713         wxASSERT_MSG( IsEditable(), wxT("non editable control changed?") );
4714
4715         wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
4716         InitCommandEvent(event);
4717         GetEventHandler()->ProcessEvent(event);
4718
4719         // as the text changed...
4720         m_isModified = true;
4721     }
4722
4723     return true;
4724 }
4725
4726 void wxTextCtrl::OnChar(wxKeyEvent& event)
4727 {
4728     // only process the key events from "simple keys" here
4729     if ( !event.HasModifiers() )
4730     {
4731         int keycode = event.GetKeyCode();
4732 #if wxUSE_UNICODE
4733         wxChar unicode = event.GetUnicodeKey();
4734 #endif
4735         if ( keycode == WXK_RETURN )
4736         {
4737             if ( IsSingleLine() || (GetWindowStyle() & wxTE_PROCESS_ENTER) )
4738             {
4739                 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, GetId());
4740                 InitCommandEvent(event);
4741                 event.SetString(GetValue());
4742                 GetEventHandler()->ProcessEvent(event);
4743             }
4744             else // interpret <Enter> normally: insert new line
4745             {
4746                 PerformAction(wxACTION_TEXT_INSERT, -1, wxT('\n'));
4747             }
4748         }
4749         else if ( keycode < 255 && isprint(keycode) )
4750         {
4751             PerformAction(wxACTION_TEXT_INSERT, -1, (wxChar)keycode);
4752
4753             // skip event.Skip() below
4754             return;
4755         }
4756 #if wxUSE_UNICODE
4757         else if (unicode > 0)
4758         {
4759             PerformAction(wxACTION_TEXT_INSERT, -1, unicode);
4760
4761             return;
4762         }
4763 #endif
4764     }
4765 #if wxDEBUG_LEVEL >= 2
4766     // Ctrl-R refreshes the control in debug mode
4767     else if ( event.ControlDown() && event.GetKeyCode() == 'r' )
4768         Refresh();
4769 #endif // wxDEBUG_LEVEL >= 2
4770
4771     event.Skip();
4772 }
4773
4774 /* static */
4775 wxInputHandler *wxTextCtrl::GetStdInputHandler(wxInputHandler *handlerDef)
4776 {
4777     static wxStdTextCtrlInputHandler s_handler(handlerDef);
4778
4779     return &s_handler;
4780 }
4781
4782 // ----------------------------------------------------------------------------
4783 // wxStdTextCtrlInputHandler
4784 // ----------------------------------------------------------------------------
4785
4786 wxStdTextCtrlInputHandler::wxStdTextCtrlInputHandler(wxInputHandler *inphand)
4787                          : wxStdInputHandler(inphand)
4788 {
4789     m_winCapture = NULL;
4790 }
4791
4792 /* static */
4793 wxTextPos wxStdTextCtrlInputHandler::HitTest(const wxTextCtrl *text,
4794                                              const wxPoint& pt)
4795 {
4796     wxTextCoord col, row;
4797     wxTextCtrlHitTestResult ht = text->HitTest(pt, &col, &row);
4798
4799     wxTextPos pos = text->XYToPosition(col, row);
4800
4801     // if the point is after the last column we must adjust the position to be
4802     // the last position in the line (unless it is already the last)
4803     if ( (ht == wxTE_HT_BEYOND) && (pos < text->GetLastPosition()) )
4804     {
4805         pos++;
4806     }
4807
4808     return pos;
4809 }
4810
4811 bool wxStdTextCtrlInputHandler::HandleKey(wxInputConsumer *consumer,
4812                                           const wxKeyEvent& event,
4813                                           bool pressed)
4814 {
4815     // we're only interested in key presses
4816     if ( !pressed )
4817         return false;
4818
4819     int keycode = event.GetKeyCode();
4820
4821     wxControlAction action;
4822     wxString str;
4823     bool ctrlDown = event.ControlDown(),
4824          shiftDown = event.ShiftDown();
4825     if ( shiftDown )
4826     {
4827         action = wxACTION_TEXT_PREFIX_SEL;
4828     }
4829
4830     // the only key combination with Alt we recognize is Alt-Bksp for undo, so
4831     // treat it first separately
4832     if ( event.AltDown() )
4833     {
4834         if ( keycode == WXK_BACK && !ctrlDown && !shiftDown )
4835             action = wxACTION_TEXT_UNDO;
4836     }
4837     else switch ( keycode )
4838     {
4839         // cursor movement
4840         case WXK_HOME:
4841             action << (ctrlDown ? wxACTION_TEXT_FIRST
4842                                 : wxACTION_TEXT_HOME);
4843             break;
4844
4845         case WXK_END:
4846             action << (ctrlDown ? wxACTION_TEXT_LAST
4847                                 : wxACTION_TEXT_END);
4848             break;
4849
4850         case WXK_UP:
4851             if ( !ctrlDown )
4852                 action << wxACTION_TEXT_UP;
4853             break;
4854
4855         case WXK_DOWN:
4856             if ( !ctrlDown )
4857                 action << wxACTION_TEXT_DOWN;
4858             break;
4859
4860         case WXK_LEFT:
4861             action << (ctrlDown ? wxACTION_TEXT_WORD_LEFT
4862                                 : wxACTION_TEXT_LEFT);
4863             break;
4864
4865         case WXK_RIGHT:
4866             action << (ctrlDown ? wxACTION_TEXT_WORD_RIGHT
4867                                 : wxACTION_TEXT_RIGHT);
4868             break;
4869
4870         case WXK_PAGEDOWN:
4871             // we don't map Ctrl-PgUp/Dn to anything special - what should it
4872             // to? for now, it's the same as without control
4873             action << wxACTION_TEXT_PAGE_DOWN;
4874             break;
4875
4876         case WXK_PAGEUP:
4877             action << wxACTION_TEXT_PAGE_UP;
4878             break;
4879
4880         // delete
4881         case WXK_DELETE:
4882             if ( !ctrlDown )
4883                 action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_RIGHT;
4884             break;
4885
4886         case WXK_BACK:
4887             if ( !ctrlDown )
4888                 action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_LEFT;
4889             break;
4890
4891         // something else
4892         default:
4893             // reset the action as it could be already set to one of the
4894             // prefixes
4895             action = wxACTION_NONE;
4896
4897             if ( ctrlDown )
4898             {
4899                 switch ( keycode )
4900                 {
4901                     case 'A':
4902                         action = wxACTION_TEXT_REDO;
4903                         break;
4904
4905                     case 'C':
4906                         action = wxACTION_TEXT_COPY;
4907                         break;
4908
4909                     case 'V':
4910                         action = wxACTION_TEXT_PASTE;
4911                         break;
4912
4913                     case 'X':
4914                         action = wxACTION_TEXT_CUT;
4915                         break;
4916
4917                     case 'Z':
4918                         action = wxACTION_TEXT_UNDO;
4919                         break;
4920                 }
4921             }
4922     }
4923
4924     if ( (action != wxACTION_NONE) && (action != wxACTION_TEXT_PREFIX_SEL) )
4925     {
4926         consumer->PerformAction(action, -1, str);
4927
4928         return true;
4929     }
4930
4931     return wxStdInputHandler::HandleKey(consumer, event, pressed);
4932 }
4933
4934 bool wxStdTextCtrlInputHandler::HandleMouse(wxInputConsumer *consumer,
4935                                             const wxMouseEvent& event)
4936 {
4937     if ( event.LeftDown() )
4938     {
4939         wxASSERT_MSG( !m_winCapture, wxT("left button going down twice?") );
4940
4941         wxTextCtrl *text = wxStaticCast(consumer->GetInputWindow(), wxTextCtrl);
4942
4943         m_winCapture = text;
4944         m_winCapture->CaptureMouse();
4945
4946         text->HideCaret();
4947
4948         wxTextPos pos = HitTest(text, event.GetPosition());
4949         if ( pos != -1 )
4950         {
4951             text->PerformAction(wxACTION_TEXT_ANCHOR_SEL, pos);
4952         }
4953     }
4954     else if ( event.LeftDClick() )
4955     {
4956         // select the word the cursor is on
4957         consumer->PerformAction(wxACTION_TEXT_SEL_WORD);
4958     }
4959     else if ( event.LeftUp() )
4960     {
4961         if ( m_winCapture )
4962         {
4963             m_winCapture->ShowCaret();
4964
4965             m_winCapture->ReleaseMouse();
4966             m_winCapture = NULL;
4967         }
4968     }
4969
4970     return wxStdInputHandler::HandleMouse(consumer, event);
4971 }
4972
4973 bool wxStdTextCtrlInputHandler::HandleMouseMove(wxInputConsumer *consumer,
4974                                                 const wxMouseEvent& event)
4975 {
4976     if ( m_winCapture )
4977     {
4978         // track it
4979         wxTextCtrl *text = wxStaticCast(m_winCapture, wxTextCtrl);
4980         wxTextPos pos = HitTest(text, event.GetPosition());
4981         if ( pos != -1 )
4982         {
4983             text->PerformAction(wxACTION_TEXT_EXTEND_SEL, pos);
4984         }
4985     }
4986
4987     return wxStdInputHandler::HandleMouseMove(consumer, event);
4988 }
4989
4990 bool
4991 wxStdTextCtrlInputHandler::HandleFocus(wxInputConsumer *consumer,
4992                                        const wxFocusEvent& event)
4993 {
4994     wxTextCtrl *text = wxStaticCast(consumer->GetInputWindow(), wxTextCtrl);
4995
4996     // the selection appearance changes depending on whether we have the focus
4997     text->RefreshSelection();
4998
4999     if (event.GetEventType() == wxEVT_SET_FOCUS)
5000     {
5001         if (text->GetCaret() && !text->GetCaret()->IsVisible())
5002             text->ShowCaret();
5003     }
5004     else
5005     {
5006         if (text->GetCaret() && text->GetCaret()->IsVisible())
5007             text->HideCaret();
5008     }
5009
5010     // never refresh entirely
5011     return false;
5012 }
5013
5014 #endif // wxUSE_TEXTCTRL