1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/x11/textctrl.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: textctrl.cpp 41754 2006-10-08 22:40:14Z VZ $
6// Copyright:   (c) 1998 Robert Roebling
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// for compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#include "wx/textctrl.h"
14
15#ifndef WX_PRECOMP
16    #include "wx/intl.h"
17    #include "wx/log.h"
18    #include "wx/utils.h"
19    #include "wx/panel.h"
20    #include "wx/dcclient.h"
21    #include "wx/settings.h"
22#endif
23
24#include "wx/clipbrd.h"
25#include "wx/tokenzr.h"
26
27#include "wx/univ/inphand.h"
28#include "wx/univ/renderer.h"
29#include "wx/univ/colschem.h"
30#include "wx/univ/theme.h"
31
32//-----------------------------------------------------------------------------
33//  helpers
34//-----------------------------------------------------------------------------
35
36wxSourceUndoStep::wxSourceUndoStep( wxSourceUndo type, int y1, int y2, wxTextCtrl *owner )
37{
38    m_type = type;
39    m_y1 = y1;
40    m_y2 = y2;
41    m_owner = owner;
42
43    m_cursorX = m_owner->GetCursorX();
44    m_cursorY = m_owner->GetCursorY();
45
46    if (m_type == wxSOURCE_UNDO_LINE)
47    {
48        m_text = m_owner->m_lines[m_y1].m_text;
49    } else
50    if (m_type == wxSOURCE_UNDO_ENTER)
51    {
52        m_text = m_owner->m_lines[m_y1].m_text;
53    } else
54    if (m_type == wxSOURCE_UNDO_BACK)
55    {
56        for (int i = m_y1; i < m_y2+2; i++)
57        {
58            if (i >= (int)m_owner->m_lines.GetCount())
59                m_lines.Add( wxEmptyString );
60            else
61                m_lines.Add( m_owner->m_lines[i].m_text );
62        }
63    } else
64    if (m_type == wxSOURCE_UNDO_DELETE)
65    {
66        for (int i = m_y1; i < m_y2+1; i++)
67        {
68            m_lines.Add( m_owner->m_lines[i].m_text );
69        }
70    } else
71    if (m_type == wxSOURCE_UNDO_PASTE)
72    {
73        m_text = m_owner->m_lines[m_y1].m_text;
74    }
75}
76
77void wxSourceUndoStep::Undo()
78{
79    if (m_type == wxSOURCE_UNDO_LINE)
80    {
81        m_owner->m_lines[m_y1].m_text = m_text;
82        m_owner->MoveCursor( m_cursorX, m_cursorY );
83        m_owner->RefreshLine( m_y1 );
84    } else
85    if (m_type == wxSOURCE_UNDO_ENTER)
86    {
87        m_owner->m_lines[m_y1].m_text = m_text;
88        m_owner->m_lines.RemoveAt( m_y1+1 );
89        m_owner->MoveCursor( m_cursorX, m_cursorY );
90        m_owner->RefreshDown( m_y1 );
91    } else
92    if (m_type == wxSOURCE_UNDO_BACK)
93    {
94        m_owner->m_lines[m_y1].m_text = m_lines[0];
95        m_owner->m_lines.Insert( new wxSourceLine( m_lines[1] ), m_y1+1 );
96        m_owner->MyAdjustScrollbars();
97        m_owner->MoveCursor( m_cursorX, m_cursorY );
98        m_owner->RefreshDown( m_y1 );
99    } else
100    if (m_type == wxSOURCE_UNDO_DELETE)
101    {
102        m_owner->m_lines[m_y1].m_text = m_lines[0];
103        for (int i = 1; i < (int)m_lines.GetCount(); i++)
104            m_owner->m_lines.Insert( new wxSourceLine( m_lines[i] ), m_y1+i );
105        m_owner->MyAdjustScrollbars();
106        m_owner->MoveCursor( m_cursorX, m_cursorY );
107        m_owner->RefreshDown( m_y1 );
108    } else
109    if (m_type == wxSOURCE_UNDO_PASTE)
110    {
111        m_owner->m_lines[m_y1].m_text = m_text;
112        for (int i = 0; i < m_y2-m_y1; i++)
113            m_owner->m_lines.RemoveAt( m_y1+1 );
114        m_owner->MyAdjustScrollbars();
115        m_owner->MoveCursor( m_cursorX, m_cursorY );
116        m_owner->RefreshDown( m_y1 );
117    } else
118    if (m_type == wxSOURCE_UNDO_INSERT_LINE)
119    {
120        m_owner->m_lines.RemoveAt( m_y1 );
121        m_owner->MyAdjustScrollbars();
122        m_owner->MoveCursor( 0, m_y1 );
123        m_owner->RefreshDown( m_y1 );
124    }
125}
126
127#include "wx/arrimpl.cpp"
128WX_DEFINE_OBJARRAY(wxSourceLineArray);
129
130//-----------------------------------------------------------------------------
131//  wxTextCtrl
132//-----------------------------------------------------------------------------
133
134IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
135
136BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
137    EVT_PAINT(wxTextCtrl::OnPaint)
138    EVT_ERASE_BACKGROUND(wxTextCtrl::OnEraseBackground)
139    EVT_CHAR(wxTextCtrl::OnChar)
140    EVT_MOUSE_EVENTS(wxTextCtrl::OnMouse)
141    EVT_KILL_FOCUS(wxTextCtrl::OnKillFocus)
142    EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
143
144    EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
145    EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
146    EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
147    EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
148    EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
149
150    EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
151    EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
152    EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
153    EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
154    EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
155END_EVENT_TABLE()
156
157void wxTextCtrl::Init()
158{
159    m_editable = true;
160    m_modified = false;
161
162    m_lang = wxSOURCE_LANG_NONE;
163
164    m_capturing = false;
165
166    m_cursorX = 0;
167    m_cursorY = 0;
168
169    m_longestLine = 0;
170
171    m_bracketX = -1;
172    m_bracketY = -1;
173
174    m_overwrite = false;
175    m_ignoreInput = false;
176
177    ClearSelection();
178
179    m_keywordColour = wxColour( 10, 140, 10 );
180
181    m_defineColour = *wxRED;
182
183    m_variableColour = wxColour( 50, 120, 150 );
184
185    m_commentColour = wxColour( 130, 130, 130 );
186
187    m_stringColour = wxColour( 10, 140, 10 );
188}
189
190wxTextCtrl::wxTextCtrl( wxWindow *parent,
191                        wxWindowID id,
192                        const wxString &value,
193                        const wxPoint &pos,
194                        const wxSize &size,
195                        long style,
196                        const wxValidator& validator,
197                        const wxString &name )
198    : wxScrollHelper(this)
199{
200    Init();
201
202    Create( parent, id, value, pos, size, style, validator, name );
203}
204
205wxTextCtrl::~wxTextCtrl()
206{
207    WX_CLEAR_LIST(wxList, m_undos);
208}
209
210bool wxTextCtrl::Create( wxWindow *parent,
211                         wxWindowID id,
212                         const wxString &value,
213                         const wxPoint &pos,
214                         const wxSize &size,
215                         long style,
216                         const wxValidator& validator,
217                         const wxString &name )
218{
219    if ((style & wxBORDER_MASK) == 0)
220        style |= wxBORDER_SUNKEN;
221
222    if ((style & wxTE_MULTILINE) != 0)
223        style |= wxALWAYS_SHOW_SB;
224
225    wxTextCtrlBase::Create( parent, id, pos /* wxDefaultPosition */, size,
226                            style | wxVSCROLL | wxHSCROLL);
227
228    SetBackgroundColour( *wxWHITE );
229
230    SetCursor( wxCursor( wxCURSOR_IBEAM ) );
231
232    m_editable = ((m_windowStyle & wxTE_READONLY) == 0);
233
234    if (HasFlag(wxTE_PASSWORD))
235        m_sourceFont = wxFont( 12, wxMODERN, wxNORMAL, wxNORMAL );
236    else
237        m_sourceFont = GetFont();
238
239    wxClientDC dc(this);
240    dc.SetFont( m_sourceFont );
241    m_lineHeight = dc.GetCharHeight();
242    m_charWidth = dc.GetCharWidth();
243
244    SetValue( value );
245
246    wxSize size_best( DoGetBestSize() );
247    wxSize new_size( size );
248    if (new_size.x == -1)
249        new_size.x = size_best.x;
250    if (new_size.y == -1)
251        new_size.y = size_best.y;
252    if ((new_size.x != size.x) || (new_size.y != size.y))
253        SetSize( new_size.x, new_size.y );
254
255    // We create an input handler since it might be useful
256    CreateInputHandler(wxINP_HANDLER_TEXTCTRL);
257
258    MyAdjustScrollbars();
259
260    return true;
261}
262
263//-----------------------------------------------------------------------------
264//  public methods
265//-----------------------------------------------------------------------------
266
267wxString wxTextCtrl::GetValue() const
268{
269    wxString ret;
270    for (size_t i = 0; i < m_lines.GetCount(); i++)
271    {
272        ret += m_lines[i].m_text;
273        if (i+1 < m_lines.GetCount())
274            ret += wxT('\n');
275    }
276
277    return ret;
278}
279
280void wxTextCtrl::DoSetValue(const wxString& value, int flags)
281{
282    m_modified = false;
283
284    wxString oldValue = GetValue();
285
286    m_cursorX = 0;
287    m_cursorY = 0;
288    ClearSelection();
289    m_lines.Clear();
290    m_longestLine = 0;
291
292    if (value.empty())
293    {
294        m_lines.Add( new wxSourceLine( wxEmptyString ) );
295    }
296    else
297    {
298        int begin = 0;
299        int pos = 0;
300        for (;;)
301        {
302            pos = value.find( wxT('\n'), begin );
303            if (pos < 0)
304            {
305                wxSourceLine *sl = new wxSourceLine( value.Mid( begin, value.Len()-begin ) );
306                m_lines.Add( sl );
307
308                // if (sl->m_text.Len() > m_longestLine)
309                //    m_longestLine = sl->m_text.Len();
310                int ww = 0;
311                GetTextExtent( sl->m_text, &ww, NULL, NULL, NULL );
312                ww /= m_charWidth;
313                if (ww > m_longestLine)
314                    m_longestLine = ww;
315
316                break;
317            }
318            else
319            {
320                wxSourceLine *sl = new wxSourceLine( value.Mid( begin, pos-begin ) );
321                m_lines.Add( sl );
322
323                // if (sl->m_text.Len() > m_longestLine)
324                //      m_longestLine = sl->m_text.Len();
325                int ww = 0;
326                GetTextExtent( sl->m_text, &ww, NULL, NULL, NULL );
327                ww /= m_charWidth;
328                if (ww > m_longestLine)
329                    m_longestLine = ww;
330
331                begin = pos+1;
332            }
333        }
334    }
335
336    // Don't need to refresh if the value hasn't changed
337    if ((GetWindowStyle() & wxTE_MULTILINE) == 0)
338    {
339        if (value == oldValue)
340            return;
341    }
342
343    MyAdjustScrollbars();
344
345    Refresh();
346
347    if ( flags & SetValue_SendEvent )
348        SendTextUpdatedEvent();
349}
350
351int wxTextCtrl::GetLineLength(long lineNo) const
352{
353    if (lineNo >= (long)m_lines.GetCount())
354        return 0;
355
356    return m_lines[lineNo].m_text.Len();
357}
358
359wxString wxTextCtrl::GetLineText(long lineNo) const
360{
361    if (lineNo >= (long)m_lines.GetCount())
362        return wxEmptyString;
363
364    return m_lines[lineNo].m_text;
365}
366
367int wxTextCtrl::GetNumberOfLines() const
368{
369    return  m_lines.GetCount();
370}
371
372bool wxTextCtrl::IsModified() const
373{
374    return m_modified;
375}
376
377bool wxTextCtrl::IsEditable() const
378{
379    return m_editable;
380}
381
382void wxTextCtrl::GetSelection(long* from, long* to) const
383{
384    if (m_selStartX == -1 || m_selStartY == -1 ||
385        m_selEndX == -1 || m_selEndY == -1)
386    {
387        *from = GetInsertionPoint();
388        *to = GetInsertionPoint();
389    }
390    else
391    {
392        *from = XYToPosition(m_selStartX, m_selStartY);
393        *to = XYToPosition(m_selEndX, m_selEndY);
394    }
395}
396
397void wxTextCtrl::Clear()
398{
399    m_modified = true;
400    m_cursorX = 0;
401    m_cursorY = 0;
402    ClearSelection();
403
404    m_lines.Clear();
405    m_lines.Add( new wxSourceLine( wxEmptyString ) );
406
407    SetScrollbars( m_charWidth, m_lineHeight, 0, 0, 0, 0 );
408    Refresh();
409    WX_CLEAR_LIST(wxList, m_undos);
410}
411
412void wxTextCtrl::Replace(long from, long to, const wxString& value)
413{
414}
415
416void wxTextCtrl::Remove(long from, long to)
417{
418}
419
420void wxTextCtrl::DiscardEdits()
421{
422    ClearSelection();
423    Refresh();
424}
425
426void wxTextCtrl::SetMaxLength(unsigned long len)
427{
428}
429
430int wxTextCtrl::PosToPixel( int line, int pos )
431{
432    // TODO add support for Tabs
433
434    if (line >= (int)m_lines.GetCount()) return 0;
435    if (pos < 0) return 0;
436
437    wxString text = m_lines[line].m_text;
438
439    if (text.empty()) return 0;
440
441    if (pos < (int)text.Len())
442        text.Remove( pos, text.Len()-pos );
443
444    int w = 0;
445
446    GetTextExtent( text, &w, NULL, NULL, NULL );
447
448    return w;
449}
450
451int wxTextCtrl::PixelToPos( int line, int pixel )
452{
453    if (pixel < 2) return 0;
454
455    if (line >= (int)m_lines.GetCount()) return 0;
456
457    wxString text = m_lines[line].m_text;
458
459    int w = 0;
460    int res = text.Len();
461    while (res > 0)
462    {
463        GetTextExtent( text, &w, NULL, NULL, NULL );
464
465        if (w < pixel)
466            return res;
467
468        res--;
469        text.Remove( res,1 );
470    }
471
472    return 0;
473}
474
475void wxTextCtrl::SetLanguage( wxSourceLanguage lang )
476{
477    m_lang = lang;
478
479    m_keywords.Clear();
480}
481
482void wxTextCtrl::WriteText(const wxString& text2)
483{
484    if (text2.empty()) return;
485
486    m_modified = true;
487
488    wxString text( text2 );
489    wxArrayString lines;
490    int pos;
491    while ( (pos = text.Find('\n')) != -1 )
492    {
493       lines.Add( text.Left( pos ) );
494       text.Remove( 0, pos+1 );
495    }
496    lines.Add( text );
497    int count = (int)lines.GetCount();
498
499    wxString tmp1( m_lines[m_cursorY].m_text );
500    wxString tmp2( tmp1 );
501    int len = (int)tmp1.Len();
502
503    if (len < m_cursorX)
504    {
505        wxString tmp;
506        for (int i = 0; i < m_cursorX-len; i++)
507            tmp.Append( ' ' );
508        m_lines[m_cursorY].m_text.Append( tmp );
509        tmp1.Append( tmp );
510        tmp2.Append( tmp );
511    }
512
513    tmp1.Remove( m_cursorX );
514    tmp2.Remove( 0, m_cursorX );
515    tmp1.Append( lines[0] );
516
517    if (count == 1)
518    {
519        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, m_cursorY, m_cursorY, this ) );
520
521        tmp1.Append( tmp2 );
522        m_lines[m_cursorY].m_text = tmp1;
523        RefreshLine( m_cursorY );
524    }
525    else
526    {
527        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_PASTE, m_cursorY, m_cursorY+count-1, this ) );
528
529        m_lines[m_cursorY].m_text = tmp1;
530        int i;
531        for (i = 1; i < count; i++)
532            m_lines.Insert( new wxSourceLine( lines[i] ), m_cursorY+i );
533        m_lines[m_cursorY+i-1].m_text.Append( tmp2 );
534
535        MyAdjustScrollbars();
536        RefreshDown( m_cursorY );
537    }
538}
539
540void wxTextCtrl::AppendText(const wxString& text2)
541{
542    if (text2.empty()) return;
543
544    m_modified = true;
545
546    wxString text( text2 );
547    wxArrayString lines;
548    int pos;
549    while ( (pos = text.Find('\n')) != -1 )
550    {
551       lines.Add( text.Left( pos ) );
552       text.Remove( 0, pos+1 );
553    }
554    lines.Add( text );
555    int count = (int)lines.GetCount();
556
557    size_t y = m_lines.GetCount()-1;
558
559    wxString tmp( m_lines[y].m_text );
560    tmp.Append( lines[0] );
561
562    if (count == 1)
563    {
564        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, y, y, this ) );
565
566        m_lines[y].m_text = tmp;
567        RefreshLine( y );
568    }
569    else
570    {
571        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_PASTE, y, y+count-1, this ) );
572
573        m_lines[y].m_text = tmp;
574        int i;
575        for (i = 1; i < count; i++)
576            m_lines.Insert( new wxSourceLine( lines[i] ), y+i );
577
578        MyAdjustScrollbars();
579        RefreshDown( y );
580    }
581}
582
583bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
584{
585    return false;
586}
587
588long wxTextCtrl::XYToPosition(long x, long y) const
589{
590    long ret = 0;
591
592    for (size_t i = 0; i < m_lines.GetCount(); i++)
593    {
594        if (i < (size_t)y)
595        {
596            // Add one for the end-of-line character
597            ret += m_lines[i].m_text.Len() + 1;
598            continue;
599        }
600
601        if ((size_t)x < (m_lines[i].m_text.Len()+1))
602            return (ret + x);
603        else
604            return (ret + m_lines[i].m_text.Len() + 1);
605    }
606
607    return ret;
608}
609
610bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
611{
612    if (m_lines.GetCount() == 0)
613    {
614        if (x) *x = 0;
615        if (y) *y = 0;
616
617        return (pos == 0);
618    }
619
620    long xx = 0;
621    long yy = 0;
622
623    for (size_t i = 0; i < m_lines.GetCount(); i++)
624    {
625        //pos -= m_lines[i].m_text.Len();
626        //if (pos <= 0)
627
628        // Add one for the end-of-line character. (In Windows,
629        // there are _two_ positions for each end of line.)
630        if (pos <= ((int)m_lines[i].m_text.Len()))
631        {
632            xx = pos;
633            if (x) *x = xx;
634            if (y) *y = yy;
635            return true;
636        }
637        pos -= (m_lines[i].m_text.Len() + 1);
638        yy++;
639    }
640
641    // Last pos
642    //xx = m_lines[ m_lines.GetCount()-1 ].m_text.Len();
643    xx = pos;
644    if (x) *x = xx;
645    if (y) *y = yy;
646
647    return false;
648}
649
650void wxTextCtrl::ShowPosition(long pos)
651{
652}
653
654void wxTextCtrl::Copy()
655{
656    if (!HasSelection()) return;
657
658    wxString sel;
659
660    int selStartY = m_selStartY;
661    int selEndY = m_selEndY;
662    int selStartX = m_selStartX;
663    int selEndX = m_selEndX;
664
665    if ((selStartY > selEndY) ||
666        ((selStartY == selEndY) && (selStartX > selEndX)))
667    {
668        int tmp = selStartX;
669        selStartX = selEndX;
670        selEndX = tmp;
671        tmp = selStartY;
672        selStartY = selEndY;
673        selEndY = tmp;
674    }
675
676    if (selStartY == selEndY)
677    {
678        sel = m_lines[selStartY].m_text;
679
680        if (selStartX >= (int)sel.Len()) return;
681        if (selEndX > (int)sel.Len())
682            selEndX = sel.Len();
683
684        sel.Remove( selEndX, sel.Len()-selEndX );
685        sel.Remove( 0, selStartX );
686    }
687    else
688    {
689        wxString tmp( m_lines[selStartY].m_text );
690
691        if (selStartX < (int)tmp.Len())
692        {
693            tmp.Remove( 0, selStartX );
694            sel = tmp;
695            sel.Append( wxT("\n") );
696        }
697        for (int i = selStartY+1; i < selEndY; i++)
698        {
699            sel.Append( m_lines[i].m_text );
700            sel.Append( wxT("\n") );
701        }
702        tmp = m_lines[selEndY].m_text;
703        if (selEndX > (int)tmp.Len())
704            selEndX = tmp.Len();
705        if (selEndX > 0)
706        {
707            tmp.Remove( selEndX, tmp.Len()-selEndX );
708            sel.Append( tmp );
709        }
710    }
711
712    if (wxTheClipboard->Open())
713    {
714        wxTheClipboard->SetData( new wxTextDataObject( sel ) );
715        wxTheClipboard->Close();
716    }
717}
718
719void wxTextCtrl::Cut()
720{
721    Copy();
722
723    Delete();
724}
725
726void wxTextCtrl::Paste()
727{
728    Delete();
729
730    if (!wxTheClipboard->Open()) return;
731
732    if (!wxTheClipboard->IsSupported( wxDF_TEXT ))
733    {
734        wxTheClipboard->Close();
735
736        return;
737    }
738
739    wxTextDataObject data;
740
741    bool ret = wxTheClipboard->GetData( data );
742
743    wxTheClipboard->Close();
744
745    if (!ret) return;
746
747    m_modified = true;
748
749    wxString text( data.GetText() );
750    wxArrayString lines;
751    int pos;
752    while ( (pos = text.Find('\n')) != -1 )
753    {
754       lines.Add( text.Left( pos ) );
755       text.Remove( 0, pos+1 );
756    }
757    lines.Add( text );
758    int count = (int)lines.GetCount();
759
760    wxString tmp1( m_lines[m_cursorY].m_text );
761    wxString tmp2( tmp1 );
762    int len = (int)tmp1.Len();
763
764    if (len < m_cursorX)
765    {
766        wxString tmp;
767        for (int i = 0; i < m_cursorX-len; i++)
768            tmp.Append( ' ' );
769        m_lines[m_cursorY].m_text.Append( tmp );
770        tmp1.Append( tmp );
771        tmp2.Append( tmp );
772    }
773
774    tmp1.Remove( m_cursorX );
775    tmp2.Remove( 0, m_cursorX );
776    tmp1.Append( lines[0] );
777
778    if (count == 1)
779    {
780        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, m_cursorY, m_cursorY, this ) );
781
782        tmp1.Append( tmp2 );
783        m_lines[m_cursorY].m_text = tmp1;
784        RefreshLine( m_cursorY );
785    }
786    else
787    {
788        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_PASTE, m_cursorY, m_cursorY+count-1, this ) );
789
790        m_lines[m_cursorY].m_text = tmp1;
791        int i;
792        for (i = 1; i < count; i++)
793            m_lines.Insert( new wxSourceLine( lines[i] ), m_cursorY+i );
794        m_lines[m_cursorY+i-1].m_text.Append( tmp2 );
795
796        MyAdjustScrollbars();
797        RefreshDown( m_cursorY );
798    }
799}
800
801void wxTextCtrl::Undo()
802{
803    if (m_undos.GetCount() == 0) return;
804
805    wxList::compatibility_iterator node = m_undos.Item( m_undos.GetCount()-1 );
806    wxSourceUndoStep *undo = (wxSourceUndoStep*) node->GetData();
807
808    undo->Undo();
809
810    delete undo;
811    m_undos.Erase( node );
812
813    m_modified = true;
814}
815
816void wxTextCtrl::SetInsertionPoint(long pos)
817{
818    ClearSelection();
819    long x, y;
820    PositionToXY(pos, & x, & y);
821    m_cursorX = x;
822    m_cursorY = y;
823    // TODO: scroll to this position if necessary
824    Refresh();
825}
826
827void wxTextCtrl::SetInsertionPointEnd()
828{
829    SetInsertionPoint(GetLastPosition());
830}
831
832long wxTextCtrl::GetInsertionPoint() const
833{
834    return XYToPosition( m_cursorX, m_cursorY );
835}
836
837wxTextPos wxTextCtrl::GetLastPosition() const
838{
839    size_t lineCount = m_lines.GetCount() - 1;
840    // It's the length of the line, not the length - 1,
841    // because there's a position after the last character.
842    return XYToPosition( m_lines[lineCount].m_text.Len(), lineCount );
843}
844
845void wxTextCtrl::SetSelection(long from, long to)
846{
847}
848
849void wxTextCtrl::SetEditable(bool editable)
850{
851    m_editable = editable;
852}
853
854bool wxTextCtrl::Enable( bool enable )
855{
856    return false;
857}
858
859bool wxTextCtrl::SetFont(const wxFont& font)
860{
861    wxTextCtrlBase::SetFont( font );
862
863    m_sourceFont = font;
864
865    wxClientDC dc(this);
866    dc.SetFont( m_sourceFont );
867    m_lineHeight = dc.GetCharHeight();
868    m_charWidth = dc.GetCharWidth();
869
870    // TODO: recalc longest lines
871
872    MyAdjustScrollbars();
873
874    return true;
875}
876
877bool wxTextCtrl::SetForegroundColour(const wxColour& colour)
878{
879    return wxWindow::SetForegroundColour( colour );
880}
881
882bool wxTextCtrl::SetBackgroundColour(const wxColour& colour)
883{
884    return wxWindow::SetBackgroundColour( colour );
885}
886
887//-----------------------------------------------------------------------------
888//  private code and handlers
889//-----------------------------------------------------------------------------
890
891void wxTextCtrl::SearchForBrackets()
892{
893    int oldBracketY = m_bracketY;
894    int oldBracketX = m_bracketX;
895
896    if (m_cursorY < 0 || m_cursorY >= (int)m_lines.GetCount()) return;
897
898    wxString current = m_lines[m_cursorY].m_text;
899
900    // reverse search first
901
902    char bracket = ' ';
903
904    if (m_cursorX > 0)
905        bracket = current[(size_t) (m_cursorX-1)];
906
907    if (bracket == ')' || bracket == ']' || bracket == '}')
908    {
909        char antibracket = '(';
910        if (bracket == ']') antibracket = '[';
911        if (bracket == '}') antibracket = '{';
912
913        int count = 1;
914
915        int endY = m_cursorY-60;
916        if (endY < 0) endY = 0;
917        for (int y = m_cursorY; y >= endY; y--)
918        {
919            current = m_lines[y].m_text;
920            if (y == m_cursorY)
921                current.erase(m_cursorX-1,current.Len()-m_cursorX+1);
922
923            for (int n = current.Len()-1; n >= 0; n--)
924            {
925                // ignore chars
926                if (current[(size_t) (n)] == '\'')
927                {
928                    for (int m = n-1; m >= 0; m--)
929                    {
930                        if (current[(size_t) (m)] == '\'')
931                        {
932                            if (m == 0 || current[(size_t) (m-1)] != '\\')
933                                break;
934                        }
935                        n = m-1;
936                    }
937                    continue;
938                }
939
940                // ignore strings
941                if (current[(size_t) (n)] == '\"')
942                {
943                    for (int m = n-1; m >= 0; m--)
944                    {
945                        if (current[(size_t) (m)] == '\"')
946                        {
947                            if (m == 0 || current[(size_t) (m-1)] != '\\')
948                                break;
949                        }
950                        n = m-1;
951                    }
952                    continue;
953                }
954
955                if (current[(size_t) (n)] == antibracket)
956                {
957                    count--;
958                    if (count == 0)
959                    {
960                        m_bracketY = y;
961                        m_bracketX = n;
962                        if (oldBracketY != m_bracketY && oldBracketY != -1)
963                            RefreshLine( oldBracketY );
964                        if (m_bracketY != oldBracketY || m_bracketX != oldBracketX)
965                            RefreshLine( m_bracketY );
966                        return;
967                    }
968                }
969                else if (current[(size_t) (n)] == bracket)
970                {
971                    count++;
972                }
973            }
974        }
975    }
976
977    // then forward
978
979    bracket = ' ';
980    if ((int)current.Len() > m_cursorX)
981        bracket = current[(size_t) (m_cursorX)];
982    if (bracket == '(' || bracket == '[' || bracket == '{')
983    {
984        char antibracket = ')';
985        if (bracket == '[') antibracket = ']';
986        if (bracket == '{') antibracket = '}';
987
988        int count = 1;
989
990        int endY = m_cursorY+60;
991        if (endY > (int)(m_lines.GetCount()-1)) endY = m_lines.GetCount()-1;
992        for (int y = m_cursorY; y <= endY; y++)
993        {
994            current = m_lines[y].m_text;
995            int start = 0;
996            if (y == m_cursorY)
997                start = m_cursorX+1;
998
999            for (int n = start; n < (int)current.Len(); n++)
1000            {
1001                // ignore chars
1002                if (current[(size_t) (n)] == '\'')
1003                {
1004                    for (int m = n+1; m < (int)current.Len(); m++)
1005                    {
1006                        if (current[(size_t) (m)] == '\'')
1007                        {
1008                            if (m == 0 || (current[(size_t) (m-1)] != '\\') || (m >= 2 && current[(size_t) (m-2)] == '\\'))
1009                                break;
1010                        }
1011                        n = m+1;
1012                    }
1013                    continue;
1014                }
1015
1016                // ignore strings
1017                if (current[(size_t) (n)] == '\"')
1018                {
1019                    for (int m = n+1; m < (int)current.Len(); m++)
1020                    {
1021                        if (current[(size_t) (m)] == '\"')
1022                        {
1023                            if (m == 0 || (current[(size_t) (m-1)] != '\\') || (m >= 2 && current[(size_t) (m-2)] == '\\'))
1024                                break;
1025                        }
1026                        n = m+1;
1027                    }
1028                    continue;
1029                }
1030
1031                if (current[(size_t) (n)] == antibracket)
1032                {
1033                    count--;
1034                    if (count == 0)
1035                    {
1036                        m_bracketY = y;
1037                        m_bracketX = n;
1038                        if (oldBracketY != m_bracketY && oldBracketY != -1)
1039                            RefreshLine( oldBracketY );
1040                        if (m_bracketY != oldBracketY || m_bracketX != oldBracketX)
1041                            RefreshLine( m_bracketY );
1042                        return;
1043                    }
1044                }
1045                else if (current[(size_t) (n)] == bracket)
1046                {
1047                    count++;
1048                }
1049            }
1050        }
1051    }
1052
1053    if (oldBracketY != -1)
1054    {
1055        m_bracketY = -1;
1056        RefreshLine( oldBracketY );
1057    }
1058}
1059
1060void wxTextCtrl::Delete()
1061{
1062    if (!HasSelection()) return;
1063
1064    m_modified = true;
1065
1066    int selStartY = m_selStartY;
1067    int selEndY = m_selEndY;
1068    int selStartX = m_selStartX;
1069    int selEndX = m_selEndX;
1070
1071    if ((selStartY > selEndY) ||
1072        ((selStartY == selEndY) && (selStartX > selEndX)))
1073    {
1074        int tmp = selStartX;
1075        selStartX = selEndX;
1076        selEndX = tmp;
1077        tmp = selStartY;
1078        selStartY = selEndY;
1079        selEndY = tmp;
1080    }
1081
1082    int len = (int)m_lines[selStartY].m_text.Len();
1083
1084    if (selStartY == selEndY)
1085    {
1086        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, selStartY, selStartY, this ) );
1087
1088        wxString tmp( m_lines[selStartY].m_text );
1089        if (selStartX < len)
1090        {
1091            if (selEndX > len)
1092                selEndX = len;
1093            tmp.Remove( selStartX, selEndX-selStartX );
1094            m_lines[selStartY].m_text = tmp;
1095        }
1096        ClearSelection();
1097        m_cursorX = selStartX;
1098        RefreshLine( selStartY );
1099    }
1100    else
1101    {
1102        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_DELETE, selStartY, selEndY, this ) );
1103
1104        if (selStartX < len)
1105            m_lines[selStartY].m_text.Remove( selStartX );
1106
1107        for (int i = 0; i < selEndY-selStartY-1; i++)
1108            m_lines.RemoveAt( selStartY+1 );
1109
1110        if (selEndX < (int)m_lines[selStartY+1].m_text.Len())
1111            m_lines[selStartY+1].m_text.Remove( 0, selEndX );
1112        else
1113            m_lines[selStartY+1].m_text.Remove( 0 );
1114
1115        m_lines[selStartY].m_text.Append( m_lines[selStartY+1].m_text );
1116        m_lines.RemoveAt( selStartY+1 );
1117
1118        ClearSelection();
1119        MoveCursor( selStartX, selStartY );
1120        MyAdjustScrollbars();
1121
1122        RefreshDown( selStartY );
1123    }
1124}
1125
1126void wxTextCtrl::DeleteLine()
1127{
1128    if (HasSelection()) return;
1129
1130    if (m_cursorY < 0 || m_cursorY >= (int)m_lines.GetCount()-1) return;  // TODO
1131
1132    m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_DELETE, m_cursorY, m_cursorY+1, this ) );
1133
1134    m_lines.RemoveAt( m_cursorY );
1135    m_cursorX = 0;
1136    if (m_cursorY >= (int)m_lines.GetCount()) m_cursorY--;
1137
1138    MyAdjustScrollbars();
1139    RefreshDown( m_cursorY );
1140}
1141
1142void wxTextCtrl::DoChar( char c )
1143{
1144    m_modified = true;
1145
1146    m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, m_cursorY, m_cursorY, this ) );
1147
1148    wxString tmp( m_lines[m_cursorY].m_text );
1149    tmp.Trim();
1150    if (m_cursorX >= (int)tmp.Len())
1151    {
1152        int len = tmp.Len();
1153        for (int i = 0; i < m_cursorX - len; i++)
1154            tmp.Append( ' ' );
1155        tmp.Append( c );
1156    }
1157    else
1158    {
1159        if (m_overwrite)
1160            tmp.SetChar( m_cursorX, c );
1161        else
1162            tmp.insert( m_cursorX, 1, c );
1163    }
1164
1165    m_lines[m_cursorY].m_text = tmp;
1166
1167//    if (tmp.Len() > m_longestLine)
1168//    {
1169//        m_longestLine = tmp.Len();
1170//        MyAdjustScrollbars();
1171//    }
1172
1173    int ww = 0;
1174    GetTextExtent( tmp, &ww, NULL, NULL, NULL );
1175    ww /= m_charWidth;
1176    if (ww > m_longestLine)
1177    {
1178        m_longestLine = ww;
1179        MyAdjustScrollbars();
1180    }
1181
1182    m_cursorX++;
1183
1184    int y = m_cursorY*m_lineHeight;
1185    // int x = (m_cursorX-1)*m_charWidth;
1186    int x = PosToPixel( m_cursorY, m_cursorX-1 );
1187    CalcScrolledPosition( x, y, &x, &y );
1188    wxRect rect( x+2, y+2, 10000, m_lineHeight );
1189    Refresh( true, &rect );
1190    // refresh whole line for syntax colour highlighting
1191    rect.x = 0;
1192    Refresh( false, &rect );
1193
1194    int size_x = 0;
1195    int size_y = 0;
1196    GetClientSize( &size_x, &size_y );
1197    size_x /= m_charWidth;
1198
1199    int view_x = 0;
1200    int view_y = 0;
1201    GetViewStart( &view_x, &view_y );
1202
1203    //int xx = m_cursorX;
1204    int xx = PosToPixel( m_cursorY, m_cursorX ) / m_charWidth;
1205
1206    if (xx < view_x)
1207        Scroll( xx, -1 );
1208    else if (xx > view_x+size_x-1)
1209        Scroll( xx-size_x+1, -1 );
1210}
1211
1212void wxTextCtrl::DoBack()
1213{
1214    m_modified = true;
1215
1216    if (m_cursorX == 0)
1217    {
1218        if (m_cursorY == 0) return;
1219
1220        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_BACK, m_cursorY-1, m_cursorY, this ) );
1221
1222        wxString tmp1( m_lines[m_cursorY-1].m_text );
1223        tmp1.Trim();
1224        wxString tmp2( m_lines[m_cursorY].m_text );
1225        tmp2.Trim();
1226        m_cursorX = tmp1.Len();
1227        m_cursorY--;
1228        tmp1.Append( tmp2 );
1229        m_lines[m_cursorY].m_text = tmp1;
1230        m_lines.RemoveAt( m_cursorY+1 );
1231
1232        MyAdjustScrollbars();
1233        RefreshDown( m_cursorY-1 );
1234    }
1235    else
1236    {
1237        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, m_cursorY, m_cursorY, this ) );
1238
1239        if (m_cursorX <= (int)m_lines[m_cursorY].m_text.Len())
1240            m_lines[m_cursorY].m_text.Remove( m_cursorX-1, 1 );
1241        m_cursorX--;
1242
1243        int y = m_cursorY*m_lineHeight;
1244        // int x = m_cursorX*m_charWidth;
1245        int x = PosToPixel( m_cursorY, m_cursorX );
1246        CalcScrolledPosition( x, y, &x, &y );
1247        wxRect rect( x+2, y+2, 10000, m_lineHeight );
1248        Refresh( true, &rect );
1249        // refresh whole line for syntax colour highlighting
1250        rect.x = 0;
1251        Refresh( false, &rect );
1252    }
1253}
1254
1255void wxTextCtrl::DoDelete()
1256{
1257    m_modified = true;
1258
1259    wxString tmp( m_lines[m_cursorY].m_text );
1260    tmp.Trim();
1261    int len = (int)tmp.Len();
1262    if (m_cursorX >= len)
1263    {
1264        if (m_cursorY == (int)m_lines.GetCount()-1) return;
1265
1266        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_DELETE, m_cursorY, m_cursorY+1, this ) );
1267
1268        for (int i = 0; i < (m_cursorX-len); i++)
1269            tmp += ' ';
1270
1271        tmp += m_lines[m_cursorY+1].m_text;
1272
1273        m_lines[m_cursorY] = tmp;
1274        m_lines.RemoveAt( m_cursorY+1 );
1275
1276        MyAdjustScrollbars();
1277        RefreshDown( m_cursorY );
1278    }
1279    else
1280    {
1281        m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, m_cursorY, m_cursorY, this ) );
1282
1283        tmp.Remove( m_cursorX, 1 );
1284        m_lines[m_cursorY].m_text = tmp;
1285
1286        int y = m_cursorY*m_lineHeight;
1287        // int x = m_cursorX*m_charWidth;
1288        int x = PosToPixel( m_cursorY, m_cursorX );
1289        CalcScrolledPosition( x, y, &x, &y );
1290        wxRect rect( x+2, y+2, 10000, m_lineHeight );
1291        Refresh( true, &rect );
1292        // refresh whole line for syntax colour highlighting
1293        rect.x = 0;
1294        Refresh( false, &rect );
1295    }
1296}
1297
1298void wxTextCtrl::DoReturn()
1299{
1300    m_modified = true;
1301
1302    m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_ENTER, m_cursorY, m_cursorY, this ) );
1303
1304    wxString tmp( m_lines[m_cursorY].m_text );
1305    size_t indent = tmp.find_first_not_of( ' ' );
1306    if (indent == wxSTRING_MAXLEN) indent = 0;
1307    tmp.Trim();
1308    if (m_cursorX >= (int)tmp.Len())
1309    {
1310        int cursorX = indent;
1311        int cursorY = m_cursorY + 1;
1312
1313        wxString new_tmp;
1314        for (size_t i = 0; i < indent; i++) new_tmp.Append( ' ' );
1315        m_lines.Insert( new wxSourceLine( new_tmp ), cursorY );
1316
1317        MyAdjustScrollbars();
1318        MoveCursor( cursorX, cursorY );
1319        RefreshDown( m_cursorY );
1320    }
1321    else
1322    {
1323        wxString tmp1( tmp );
1324        tmp1.Remove( m_cursorX, tmp.Len()-m_cursorX );
1325        m_lines[m_cursorY].m_text = tmp1;
1326
1327        wxString tmp2( tmp );
1328        tmp2.Remove( 0, m_cursorX );
1329
1330        int cursorX = indent;
1331        int cursorY = m_cursorY + 1;
1332
1333        wxString new_tmp;
1334        for (size_t i = 0; i < indent; i++) new_tmp.Append( ' ' );
1335        new_tmp.Append( tmp2 );
1336        m_lines.Insert( new wxSourceLine( new_tmp ), cursorY );
1337
1338        MyAdjustScrollbars();
1339        MoveCursor( cursorX, cursorY );
1340        RefreshDown( m_cursorY-1 );
1341    }
1342}
1343
1344void wxTextCtrl::DoDClick()
1345{
1346    wxString line( m_lines[ m_cursorY ].m_text );
1347    if (m_cursorX >= (int)line.Len()) return;
1348    int p = m_cursorX;
1349    char ch = line[(size_t) (p)];
1350    if (((ch >= 'a') && (ch <= 'z')) ||
1351        ((ch >= 'A') && (ch <= 'Z')) ||
1352        ((ch >= '0') && (ch <= '9')) ||
1353        (ch == '_'))
1354    {
1355        m_selStartY = m_cursorY;
1356        m_selEndY = m_cursorY;
1357        if (p > 0)
1358        {
1359            ch = line[(size_t) (p-1)];
1360            while (((ch >= 'a') && (ch <= 'z')) ||
1361                   ((ch >= 'A') && (ch <= 'Z')) ||
1362                   ((ch >= '0') && (ch <= '9')) ||
1363                   (ch == '_'))
1364            {
1365                p--;
1366                if (p == 0) break;
1367                ch = line[(size_t) (p-1)];
1368            }
1369        }
1370        m_selStartX = p;
1371
1372        p = m_cursorX;
1373        if (p < (int)line.Len())
1374        {
1375            ch = line[(size_t) (p)];
1376            while (((ch >= 'a') && (ch <= 'z')) ||
1377                   ((ch >= 'A') && (ch <= 'Z')) ||
1378                   ((ch >= '0') && (ch <= '9')) ||
1379                   (ch == '_'))
1380            {
1381                if (p >= (int)line.Len()) break;
1382                p++;
1383                ch = line[(size_t) (p)];
1384            }
1385        }
1386        m_selEndX = p;
1387        RefreshLine( m_cursorY );
1388    }
1389}
1390
1391wxString wxTextCtrl::GetNextToken( wxString &line, size_t &pos )
1392{
1393    wxString ret;
1394    size_t len = line.Len();
1395    for (size_t p = pos; p < len; p++)
1396    {
1397        if ((m_lang == wxSOURCE_LANG_PYTHON) || (m_lang == wxSOURCE_LANG_PERL))
1398        {
1399            if (line[p] == '#')
1400            {
1401                for (size_t q = p; q < len; q++)
1402                    ret.Append( line[q] );
1403                pos = p;
1404                return ret;
1405            }
1406        }
1407        else
1408        {
1409            if ((line[p] == '/') && (p+1 < len) && (line[(size_t) (p+1)] == '/'))
1410            {
1411                for (size_t q = p; q < len; q++)
1412                    ret.Append( line[q] );
1413                pos = p;
1414                return ret;
1415            }
1416        }
1417
1418        if (line[p] == '"')
1419        {
1420            ret.Append( line[p] );
1421            for (size_t q = p+1; q < len; q++)
1422            {
1423                ret.Append( line[q] );
1424                if ((line[q] == '"') && ((line[(size_t) (q-1)] != '\\') || (q >= 2 && line[(size_t) (q-2)] == '\\')))
1425                   break;
1426            }
1427            pos = p;
1428            return ret;
1429        }
1430
1431        if (line[p] == '\'')
1432        {
1433            ret.Append( line[p] );
1434            for (size_t q = p+1; q < len; q++)
1435            {
1436                ret.Append( line[q] );
1437                if ((line[q] == '\'') && ((line[(size_t) (q-1)] != '\\') || (q >= 2 && line[(size_t) (q-2)] == '\\')))
1438                   break;
1439            }
1440            pos = p;
1441            return ret;
1442        }
1443
1444        if (((line[p] >= 'a') && (line[p] <= 'z')) ||
1445            ((line[p] >= 'A') && (line[p] <= 'Z')) ||
1446            (line[p] == '_') ||
1447            (line[p] == '#'))
1448        {
1449           ret.Append( line[p] );
1450           for (size_t q = p+1; q < len; q++)
1451           {
1452                if (((line[q] >= 'a') && (line[q] <= 'z')) ||
1453                   ((line[q] >= 'A') && (line[q] <= 'Z')) ||
1454                   ((line[q] >= '0') && (line[q] <= '9')) ||
1455                   (line[q] == '_'))
1456                {
1457                    ret.Append( line[q] );
1458                    continue;
1459                }
1460                else
1461                {
1462                    pos = p;
1463                    return ret;
1464                }
1465           }
1466           pos = p;
1467           return ret;
1468        }
1469    }
1470
1471    return ret;
1472}
1473
1474void wxTextCtrl::OnEraseBackground( wxEraseEvent &event )
1475{
1476    event.Skip();
1477}
1478
1479void wxTextCtrl::DrawLinePart( wxDC &dc, int x, int y, const wxString &toDraw, const wxString &origin, const wxColour &colour )
1480{
1481    size_t pos = 0;
1482    size_t len = origin.Len();
1483    dc.SetTextForeground( colour );
1484    while (pos < len)
1485    {
1486        while (toDraw[pos] == wxT(' '))
1487        {
1488            pos++;
1489            if (pos == len) return;
1490        }
1491
1492        size_t start = pos;
1493
1494        wxString current;
1495        current += toDraw[pos];
1496        pos++;
1497        while ( (toDraw[pos] == origin[pos]) && (pos < len))
1498        {
1499            current += toDraw[pos];
1500            pos++;
1501        }
1502
1503        int xx = 0;
1504        wxString tmp = origin.Left( start );
1505        GetTextExtent( tmp, &xx, NULL, NULL, NULL );
1506        xx += x;
1507        int yy = y;
1508        dc.DrawText( current, xx, yy );
1509    }
1510}
1511
1512void wxTextCtrl::DrawLine( wxDC &dc, int x, int y, const wxString &line2, int lineNum )
1513{
1514    int selStartY = m_selStartY;
1515    int selEndY = m_selEndY;
1516    int selStartX = m_selStartX;
1517    int selEndX = m_selEndX;
1518
1519    if ((selStartY > selEndY) ||
1520        ((selStartY == selEndY) && (selStartX > selEndX)))
1521    {
1522        int tmp = selStartX;
1523        selStartX = selEndX;
1524        selEndX = tmp;
1525        tmp = selStartY;
1526        selStartY = selEndY;
1527        selEndY = tmp;
1528    }
1529
1530    wxString line( line2 );
1531    if (HasFlag(wxTE_PASSWORD))
1532    {
1533        size_t len = line.Len();
1534        line = wxString( wxT('*'), len );
1535    }
1536
1537    wxString keyword( ' ', line.Len() );
1538    wxString define( ' ', line.Len() );
1539    wxString variable( ' ', line.Len() );
1540    wxString comment( ' ', line.Len() );
1541    wxString my_string( ' ', line.Len() );
1542    wxString selection( ' ', line.Len() );
1543
1544    if (m_lang != wxSOURCE_LANG_NONE)
1545    {
1546        if (lineNum == m_bracketY)
1547        {
1548            wxString red( ' ', line.Len() );
1549            if (m_bracketX < (int)line.Len())
1550            {
1551                red.SetChar( m_bracketX, line[(size_t) (m_bracketX)] );
1552                line.SetChar( m_bracketX, ' ' );
1553                dc.SetTextForeground( *wxRED );
1554                dc.DrawText( red, x, y );
1555                dc.SetTextForeground( *wxBLACK );
1556            }
1557        }
1558
1559        size_t pos = 0;
1560        wxString token( GetNextToken( line, pos ) );
1561        while (!token.IsNull())
1562        {
1563            if (m_keywords.Index( token ) != wxNOT_FOUND)
1564            {
1565                size_t end_pos = pos + token.Len();
1566                for (size_t i = pos; i < end_pos; i++)
1567                {
1568                    keyword[i] = line[i];
1569                    line[i] = ' ';
1570                }
1571            } else
1572            if (m_defines.Index( token ) != wxNOT_FOUND)
1573            {
1574                size_t end_pos = pos + token.Len();
1575                for (size_t i = pos; i < end_pos; i++)
1576                {
1577                    define[i] = line[i];
1578                    line[i] = ' ';
1579                }
1580            } else
1581            if ((m_variables.Index( token ) != wxNOT_FOUND) ||
1582                ((token.Len() > 2) && (token[(size_t) (0)] == 'w') && (token[(size_t) (1)] == 'x')))
1583            {
1584                size_t end_pos = pos + token.Len();
1585                for (size_t i = pos; i < end_pos; i++)
1586                {
1587                    variable[i] = line[i];
1588                    line[i] = ' ';
1589                }
1590            } else
1591            if ((token.Len() >= 2) && (token[(size_t) (0)] == '/') && (token[(size_t) (1)] == '/') && (m_lang == wxSOURCE_LANG_CPP))
1592            {
1593                size_t end_pos = pos + token.Len();
1594                for (size_t i = pos; i < end_pos; i++)
1595                {
1596                    comment[i] = line[i];
1597                    line[i] = ' ';
1598                }
1599            } else
1600            if ((token[(size_t) (0)] == '#') &&
1601                ((m_lang == wxSOURCE_LANG_PYTHON) || (m_lang == wxSOURCE_LANG_PERL)))
1602            {
1603                size_t end_pos = pos + token.Len();
1604                for (size_t i = pos; i < end_pos; i++)
1605                {
1606                    comment[i] = line[i];
1607                    line[i] = ' ';
1608                }
1609            } else
1610            if ((token[(size_t) (0)] == '"') || (token[(size_t) (0)] == '\''))
1611            {
1612                size_t end_pos = pos + token.Len();
1613                for (size_t i = pos; i < end_pos; i++)
1614                {
1615                    my_string[i] = line[i];
1616                    line[i] = ' ';
1617                }
1618            }
1619            pos += token.Len();
1620            token = GetNextToken( line, pos );
1621        }
1622    }
1623
1624    if ((lineNum < selStartY) || (lineNum > selEndY))
1625    {
1626        DrawLinePart( dc, x, y, line, line2, *wxBLACK );
1627        DrawLinePart( dc, x, y, selection, line2, *wxWHITE );
1628        DrawLinePart( dc, x, y, keyword, line2, m_keywordColour );
1629        DrawLinePart( dc, x, y, define, line2, m_defineColour );
1630        DrawLinePart( dc, x, y, variable, line2, m_variableColour );
1631        DrawLinePart( dc, x, y, comment, line2, m_commentColour );
1632        DrawLinePart( dc, x, y, my_string, line2, m_stringColour );
1633        return;
1634    }
1635
1636    if (selStartY == selEndY)
1637    {
1638        // int xx = selStartX*m_charWidth;
1639        int xx = PosToPixel( lineNum, selStartX );
1640        // int ww = (selEndX-selStartX)*m_charWidth;
1641        int ww = PosToPixel( lineNum, selEndX ) - xx;
1642        dc.DrawRectangle( xx+2, lineNum*m_lineHeight+2, ww, m_lineHeight );
1643
1644        for (size_t i = (size_t)selStartX; i < (size_t)selEndX; i++)
1645        {
1646            selection[i] = line[i];
1647            line[i] = ' ';
1648        }
1649    } else
1650    if ((lineNum > selStartY) && (lineNum < selEndY))
1651    {
1652        dc.DrawRectangle( 0+2, lineNum*m_lineHeight+2, 10000, m_lineHeight );
1653
1654        for (size_t i = 0; i < line.Len(); i++)
1655        {
1656            selection[i] = line[i];
1657            line[i] = ' ';
1658        }
1659    } else
1660    if (lineNum == selStartY)
1661    {
1662        // int xx = selStartX*m_charWidth;
1663        int xx = PosToPixel( lineNum, selStartX );
1664        dc.DrawRectangle( xx+2, lineNum*m_lineHeight+2, 10000, m_lineHeight );
1665
1666        for (size_t i = (size_t)selStartX; i < line.Len(); i++)
1667        {
1668            selection[i] = line[i];
1669            line[i] = ' ';
1670        }
1671    } else
1672    if (lineNum == selEndY)
1673    {
1674        // int ww = selEndX*m_charWidth;
1675        int ww = PosToPixel( lineNum, selEndX );
1676        dc.DrawRectangle( 0+2, lineNum*m_lineHeight+2, ww, m_lineHeight );
1677
1678        for (size_t i = 0; i < (size_t)selEndX; i++)
1679        {
1680            selection[i] = line[i];
1681            line[i] = ' ';
1682        }
1683    }
1684
1685    DrawLinePart( dc, x, y, line, line2, *wxBLACK );
1686    DrawLinePart( dc, x, y, selection, line2, *wxWHITE );
1687    DrawLinePart( dc, x, y, keyword, line2, m_keywordColour );
1688    DrawLinePart( dc, x, y, define, line2, m_defineColour );
1689    DrawLinePart( dc, x, y, variable, line2, m_variableColour );
1690    DrawLinePart( dc, x, y, comment, line2, m_commentColour );
1691    DrawLinePart( dc, x, y, my_string, line2, m_stringColour );
1692}
1693
1694void wxTextCtrl::OnPaint( wxPaintEvent &event )
1695{
1696    wxPaintDC dc(this);
1697
1698    if (m_lines.GetCount() == 0) return;
1699
1700    PrepareDC( dc );
1701
1702    dc.SetFont( m_sourceFont );
1703
1704    int scroll_y = 0;
1705    GetViewStart( NULL, &scroll_y );
1706
1707    // We have a inner border of two pixels
1708    // around the text, so scroll units do
1709    // not correspond to lines.
1710    if (scroll_y > 0) scroll_y--;
1711
1712    int size_x = 0;
1713    int size_y = 0;
1714    GetClientSize( &size_x, &size_y );
1715
1716    dc.SetPen( *wxTRANSPARENT_PEN );
1717    dc.SetBrush( wxBrush( wxTHEME_COLOUR(HIGHLIGHT), wxSOLID ) );
1718    int upper = wxMin( (int)m_lines.GetCount(), scroll_y+(size_y/m_lineHeight)+2 );
1719    for (int i = scroll_y; i < upper; i++)
1720    {
1721        int x = 0+2;
1722        int y = i*m_lineHeight+2;
1723        int w = 10000;
1724        int h = m_lineHeight;
1725        CalcScrolledPosition( x,y,&x,&y );
1726        if (IsExposed(x,y,w,h))
1727            DrawLine( dc, 0+2, i*m_lineHeight+2, m_lines[i].m_text, i );
1728    }
1729
1730    if (m_editable && (FindFocus() == this))
1731    {
1732        ///dc.SetBrush( *wxRED_BRUSH );
1733        dc.SetBrush( *wxBLACK_BRUSH );
1734        // int xx = m_cursorX*m_charWidth;
1735        int xx = PosToPixel( m_cursorY, m_cursorX );
1736        dc.DrawRectangle( xx+2, m_cursorY*m_lineHeight+2, 2, m_lineHeight );
1737    }
1738}
1739
1740void wxTextCtrl::OnMouse( wxMouseEvent &event )
1741{
1742    if (m_lines.GetCount() == 0) return;
1743
1744
1745#if 0  // there is no middle button on iPAQs
1746    if (event.MiddleDown())
1747    {
1748        Paste( true );
1749        return;
1750    }
1751#endif
1752
1753    if (event.LeftDClick())
1754    {
1755        DoDClick();
1756        return;
1757    }
1758
1759    if (event.LeftDown())
1760    {
1761        m_capturing = true;
1762        CaptureMouse();
1763    }
1764
1765    if (event.LeftUp())
1766    {
1767        m_capturing = false;
1768        ReleaseMouse();
1769    }
1770
1771    if (event.LeftDown() ||
1772        (event.LeftIsDown() && m_capturing))
1773    {
1774        int x = event.GetX();
1775        int y = event.GetY();
1776        CalcUnscrolledPosition( x, y, &x, &y );
1777        y /= m_lineHeight;
1778        // x /= m_charWidth;
1779        x = PixelToPos( y, x );
1780        MoveCursor(
1781            wxMin( 1000, wxMax( 0, x ) ),
1782            wxMin( (int)m_lines.GetCount()-1, wxMax( 0, y ) ),
1783            event.ShiftDown() || !event.LeftDown() );
1784    }
1785}
1786
1787void wxTextCtrl::OnChar( wxKeyEvent &event )
1788{
1789    if (m_lines.GetCount() == 0) return;
1790
1791    if (!m_editable) return;
1792
1793    int size_x = 0;
1794    int size_y = 0;
1795    GetClientSize( &size_x, &size_y );
1796    size_x /= m_charWidth;
1797    size_y /= m_lineHeight;
1798    size_y--;
1799
1800    if (event.ShiftDown())
1801    {
1802        switch (event.GetKeyCode())
1803        {
1804            case '4': event.m_keyCode = WXK_LEFT;     break;
1805            case '8': event.m_keyCode = WXK_UP;       break;
1806            case '6': event.m_keyCode = WXK_RIGHT;    break;
1807            case '2': event.m_keyCode = WXK_DOWN;     break;
1808            case '9': event.m_keyCode = WXK_PAGEUP;   break;
1809            case '3': event.m_keyCode = WXK_PAGEDOWN; break;
1810            case '7': event.m_keyCode = WXK_HOME;     break;
1811            case '1': event.m_keyCode = WXK_END;      break;
1812            case '0': event.m_keyCode = WXK_INSERT;   break;
1813        }
1814    }
1815
1816    switch (event.GetKeyCode())
1817    {
1818        case WXK_UP:
1819        {
1820            if (m_ignoreInput) return;
1821            if (m_cursorY > 0)
1822                MoveCursor( m_cursorX, m_cursorY-1, event.ShiftDown() );
1823            m_ignoreInput = true;
1824            return;
1825        }
1826        case WXK_DOWN:
1827        {
1828            if (m_ignoreInput) return;
1829            if (m_cursorY < (int)(m_lines.GetCount()-1))
1830                MoveCursor( m_cursorX, m_cursorY+1, event.ShiftDown() );
1831            m_ignoreInput = true;
1832            return;
1833        }
1834        case WXK_LEFT:
1835        {
1836            if (m_ignoreInput) return;
1837            if (m_cursorX > 0)
1838            {
1839                MoveCursor( m_cursorX-1, m_cursorY, event.ShiftDown() );
1840            }
1841            else
1842            {
1843                if (m_cursorY > 0)
1844                    MoveCursor( m_lines[m_cursorY-1].m_text.Len(), m_cursorY-1, event.ShiftDown() );
1845            }
1846            m_ignoreInput = true;
1847            return;
1848        }
1849        case WXK_RIGHT:
1850        {
1851            if (m_ignoreInput) return;
1852            if (m_cursorX < 1000)
1853                MoveCursor( m_cursorX+1, m_cursorY, event.ShiftDown() );
1854            m_ignoreInput = true;
1855            return;
1856        }
1857        case WXK_HOME:
1858        {
1859            if (event.ControlDown())
1860                MoveCursor( 0, 0, event.ShiftDown() );
1861            else
1862                MoveCursor( 0, m_cursorY, event.ShiftDown() );
1863            return;
1864        }
1865        case WXK_END:
1866        {
1867            if (event.ControlDown())
1868                MoveCursor( 0, m_lines.GetCount()-1, event.ShiftDown() );
1869            else
1870                MoveCursor( m_lines[m_cursorY].m_text.Len(), m_cursorY, event.ShiftDown() );
1871            return;
1872        }
1873        case WXK_NEXT:
1874        {
1875            if (m_ignoreInput) return;
1876            MoveCursor( m_cursorX, wxMin( (int)(m_lines.GetCount()-1), m_cursorY+size_y ), event.ShiftDown() );
1877            m_ignoreInput = true;
1878            return;
1879        }
1880        case WXK_PAGEUP:
1881        {
1882            if (m_ignoreInput) return;
1883            MoveCursor( m_cursorX, wxMax( 0, m_cursorY-size_y ), event.ShiftDown() );
1884            m_ignoreInput = true;
1885            return;
1886        }
1887        case WXK_INSERT:
1888        {
1889            if (event.ShiftDown())
1890                Paste();
1891            else if (event.ControlDown())
1892                Copy();
1893            else
1894                m_overwrite = !m_overwrite;
1895            return;
1896        }
1897        case WXK_RETURN:
1898        {
1899            if (m_windowStyle & wxTE_PROCESS_ENTER)
1900            {
1901                wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
1902                event.SetEventObject(this);
1903                event.SetString(GetValue());
1904                if (GetEventHandler()->ProcessEvent(event)) return;
1905            }
1906
1907            if (IsSingleLine())
1908            {
1909                event.Skip();
1910                return;
1911            }
1912
1913            if (HasSelection())
1914                Delete();
1915            DoReturn();
1916            return;
1917        }
1918        case WXK_TAB:
1919        {
1920            if (HasSelection())
1921                Delete();
1922            bool save_overwrite = m_overwrite;
1923            m_overwrite = false;
1924            int i = 4-(m_cursorX % 4);
1925            if (i == 0) i = 4;
1926            for (int c = 0; c < i; c++)
1927                 DoChar( ' ' );
1928            m_overwrite = save_overwrite;
1929            return;
1930        }
1931        case WXK_BACK:
1932        {
1933            if (HasSelection())
1934                Delete();
1935            else
1936                DoBack();
1937            return;
1938        }
1939        case WXK_DELETE:
1940        {
1941            if (HasSelection())
1942                Delete();
1943            else
1944                DoDelete();
1945            return;
1946        }
1947        default:
1948        {
1949            if (  (event.GetKeyCode() >= 'a') &&
1950                  (event.GetKeyCode() <= 'z') &&
1951                  (event.AltDown()) )
1952            {
1953                // Alt-F etc.
1954                event.Skip();
1955                return;
1956            }
1957
1958            if (  (event.GetKeyCode() >= 32) &&
1959                  (event.GetKeyCode() <= 255) &&
1960                 !(event.ControlDown() && !event.AltDown()) ) // filters out Ctrl-X but leaves Alt-Gr
1961            {
1962                if (HasSelection())
1963                    Delete();
1964                DoChar( (char) event.GetKeyCode() );
1965                return;
1966            }
1967        }
1968    }
1969
1970    event.Skip();
1971}
1972
1973void wxTextCtrl::OnInternalIdle()
1974{
1975    wxControl::OnInternalIdle();
1976
1977    m_ignoreInput = false;
1978
1979    if (m_lang != wxSOURCE_LANG_NONE)
1980        SearchForBrackets();
1981}
1982
1983void wxTextCtrl::Indent()
1984{
1985    int startY = m_cursorY;
1986    int endY = m_cursorY;
1987    if (HasSelection())
1988    {
1989        startY = m_selStartY;
1990        endY = m_selEndY;
1991        if (endY < startY)
1992        {
1993            int tmp = startY;
1994            startY = endY;
1995            endY = tmp;
1996        }
1997    }
1998
1999    m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, startY, endY, this ) );
2000
2001    for (int i = startY; i <= endY; i++)
2002    {
2003        m_lines[i].m_text.insert( 0u, wxT("    ") );
2004        RefreshLine( i );
2005    }
2006}
2007
2008void wxTextCtrl::Unindent()
2009{
2010    int startY = m_cursorY;
2011    int endY = m_cursorY;
2012    if (HasSelection())
2013    {
2014        startY = m_selStartY;
2015        endY = m_selEndY;
2016        if (endY < startY)
2017        {
2018            int tmp = startY;
2019            startY = endY;
2020            endY = tmp;
2021        }
2022    }
2023
2024    m_undos.Append( new wxSourceUndoStep( wxSOURCE_UNDO_LINE, startY, endY, this ) );
2025
2026    for (int i = startY; i <= endY; i++)
2027    {
2028        for (int n = 0; n < 4; n++)
2029        {
2030            if (m_lines[i].m_text[0u] == wxT(' '))
2031                m_lines[i].m_text.erase(0u,1u);
2032        }
2033        RefreshLine( i );
2034    }
2035}
2036bool wxTextCtrl::HasSelection()
2037{
2038    return ((m_selStartY != m_selEndY) || (m_selStartX != m_selEndX));
2039}
2040
2041void wxTextCtrl::ClearSelection()
2042{
2043    m_selStartX = -1;
2044    m_selStartY = -1;
2045    m_selEndX = -1;
2046    m_selEndY = -1;
2047}
2048
2049void wxTextCtrl::RefreshLine( int n )
2050{
2051    int y = n*m_lineHeight;
2052    int x = 0;
2053    CalcScrolledPosition( x, y, &x, &y );
2054    wxRect rect( 0+2, y+2, 10000, m_lineHeight );
2055    Refresh( true, &rect );
2056}
2057
2058void wxTextCtrl::RefreshDown( int n )
2059{
2060    int size_x = 0;
2061    int size_y = 0;
2062    GetClientSize( &size_x, &size_y );
2063
2064    int view_x = 0;
2065    int view_y = 0;
2066    GetViewStart( &view_x, &view_y );
2067
2068    if (n < view_y)
2069    {
2070        Refresh();
2071    }
2072    else
2073    {
2074        int y = n*m_lineHeight;
2075        int x = 0;
2076        CalcScrolledPosition( x, y, &x, &y );
2077
2078        wxRect rect( 0+2, y+2, 10000, size_y );
2079        Refresh( true, &rect );
2080    }
2081}
2082
2083void wxTextCtrl::MoveCursor( int new_x, int new_y, bool shift, bool centre )
2084{
2085    if (!m_editable) return;
2086
2087    // if (IsSingleLine() || (m_lang == wxSOURCE_LANG_NONE))
2088    {
2089        if (new_x > (int) (m_lines[new_y].m_text.Len()))
2090            new_x = m_lines[new_y].m_text.Len();
2091    }
2092
2093    if ((new_x == m_cursorX) && (new_y == m_cursorY)) return;
2094
2095    bool no_cursor_refresh = false;
2096    bool has_selection = HasSelection();
2097
2098    if (shift)
2099    {
2100        int x,y,w,h;
2101        bool erase_background = true;
2102
2103        if (!has_selection)
2104        {
2105            m_selStartX = m_cursorX;
2106            m_selStartY = m_cursorY;
2107
2108            x = 0;
2109            w = 10000;
2110            if (new_y > m_selStartY)
2111            {
2112                y = m_selStartY*m_lineHeight;
2113                h = (new_y-m_selStartY+1)*m_lineHeight;
2114            }
2115            else if (new_y == m_selStartY)
2116            {
2117                x = PosToPixel( new_y, m_selStartX );
2118                w = PosToPixel( new_y, new_x ) - x;
2119                if (w < 0)
2120                {
2121                    x += w;
2122                    w = -w + 2; // +2 for the cursor
2123                }
2124                y = m_selStartY*m_lineHeight;
2125                h = m_lineHeight;
2126            }
2127            else
2128            {
2129                y = new_y*m_lineHeight;
2130                h = (-new_y+m_selStartY+1)*m_lineHeight;
2131            }
2132
2133            no_cursor_refresh = true;
2134            m_cursorX = new_x;
2135            m_cursorY = new_y;
2136        }
2137        else
2138        {
2139            if (new_y == m_selEndY)
2140            {
2141                y = new_y *m_lineHeight;
2142                h = m_lineHeight;
2143                if (m_selEndX > new_x)
2144                {
2145                    // x = new_x*m_charWidth;
2146                    x = PosToPixel( new_y, new_x );
2147                    // w = (m_selEndX-new_x)*m_charWidth;
2148                    w = PosToPixel( new_y, m_selEndX ) - x;
2149                }
2150                else
2151                {
2152                    // x = m_selEndX*m_charWidth;
2153                    x = PosToPixel( new_y, m_selEndX );
2154                    // w = (-m_selEndX+new_x)*m_charWidth;
2155                    w = PosToPixel( new_y, new_x ) - x;
2156                }
2157            }
2158            else
2159            {
2160                x = 0;
2161                w = 10000;
2162                if (new_y > m_selEndY)
2163                {
2164                    y = m_selEndY*m_lineHeight;
2165                    h = (new_y-m_selEndY+1) * m_lineHeight;
2166
2167                    erase_background = ((m_selEndY < m_selStartY) ||
2168                                        ((m_selEndY == m_selStartY) && (m_selEndX < m_selStartX)));
2169                }
2170                else
2171                {
2172                    y = new_y*m_lineHeight;
2173                    h = (-new_y+m_selEndY+1) * m_lineHeight;
2174
2175                    erase_background = ((m_selEndY > m_selStartY) ||
2176                                        ((m_selEndY == m_selStartY) && (m_selEndX > m_selStartX)));
2177                }
2178                no_cursor_refresh = true;
2179                m_cursorX = new_x;
2180                m_cursorY = new_y;
2181            }
2182        }
2183
2184        m_selEndX = new_x;
2185        m_selEndY = new_y;
2186
2187        CalcScrolledPosition( x, y, &x, &y );
2188        wxRect rect( x+2, y+2, w, h );
2189        Refresh( erase_background, &rect );
2190    }
2191    else
2192    {
2193        if (has_selection)
2194        {
2195            int ry1 = m_selEndY;
2196            int ry2 = m_selStartY;
2197            m_selEndX = -1;
2198            m_selEndY = -1;
2199            m_selStartX = -1;
2200            m_selStartY = -1;
2201
2202            if (ry1 > ry2)
2203            {
2204                int tmp = ry2;
2205                ry2 = ry1;
2206                ry1 = tmp;
2207            }
2208
2209            int x = 0;
2210            int y = ry1*m_lineHeight;
2211            CalcScrolledPosition( x, y, &x, &y );
2212            wxRect rect( 0, y+2, 10000, (ry2-ry1+1)*m_lineHeight );
2213
2214            Refresh( true, &rect );
2215        }
2216    }
2217
2218/*
2219    printf( "startx %d starty %d endx %d endy %d\n",
2220            m_selStartX, m_selStartY, m_selEndX, m_selEndY );
2221
2222    printf( "has %d\n", (int)HasSelection() );
2223*/
2224
2225    if (!no_cursor_refresh)
2226    {
2227        // int x = m_cursorX*m_charWidth;
2228        int x = PosToPixel( m_cursorY, m_cursorX );
2229        int y = m_cursorY*m_lineHeight;
2230        CalcScrolledPosition( x, y, &x, &y );
2231        wxRect rect( x+2, y+2, 4, m_lineHeight+2 );
2232
2233        m_cursorX = new_x;
2234        m_cursorY = new_y;
2235
2236        Refresh( true, &rect );
2237
2238        if (FindFocus() == this)
2239        {
2240            wxClientDC dc(this);
2241            PrepareDC( dc );
2242            dc.SetPen( *wxTRANSPARENT_PEN );
2243            //dc.SetBrush( *wxRED_BRUSH );
2244            dc.SetBrush( *wxBLACK_BRUSH );
2245            // int xx = m_cursorX*m_charWidth;
2246            int xx = PosToPixel( m_cursorY, m_cursorX );
2247            dc.DrawRectangle( xx+2, m_cursorY*m_lineHeight+2, 2, m_lineHeight );
2248        }
2249    }
2250
2251    int size_x = 0;
2252    int size_y = 0;
2253    GetClientSize( &size_x, &size_y );
2254    size_x /= m_charWidth;
2255    size_y /= m_lineHeight;
2256
2257    int view_x = 0;
2258    int view_y = 0;
2259    GetViewStart( &view_x, &view_y );
2260
2261    if (centre)
2262    {
2263        int sy = m_cursorY - (size_y/2);
2264        if (sy < 0) sy = 0;
2265        Scroll( -1, sy );
2266    }
2267    else
2268    {
2269        if (m_cursorY < view_y)
2270            Scroll( -1, m_cursorY );
2271        else if (m_cursorY > view_y+size_y-1)
2272            Scroll( -1, m_cursorY-size_y+1 );
2273    }
2274
2275    //int xx = m_cursorX;
2276    int xx = PosToPixel( m_cursorY, m_cursorX ) / m_charWidth;
2277
2278    if (xx < view_x)
2279        Scroll( xx, -1 );
2280    else if (xx > view_x+size_x-1)
2281        Scroll( xx-size_x+1, -1 );
2282}
2283
2284void wxTextCtrl::MyAdjustScrollbars()
2285{
2286    if (IsSingleLine())
2287        return;
2288
2289    int y_range = m_lines.GetCount();
2290
2291    int height = 0;
2292    GetClientSize( NULL, &height );
2293    height -= 4;
2294    if (height >= (int)m_lines.GetCount() *m_lineHeight)
2295        y_range = 0;
2296
2297    int view_x = 0;
2298    int view_y = 0;
2299    GetViewStart( &view_x, &view_y );
2300
2301    SetScrollbars( m_charWidth, m_lineHeight, m_longestLine+2, y_range, view_x, view_y );
2302}
2303
2304//-----------------------------------------------------------------------------
2305//  clipboard handlers
2306//-----------------------------------------------------------------------------
2307
2308void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2309{
2310    Cut();
2311}
2312
2313void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2314{
2315    Copy();
2316}
2317
2318void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2319{
2320    Paste();
2321}
2322
2323void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2324{
2325    Undo();
2326}
2327
2328void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2329{
2330    Redo();
2331}
2332
2333void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2334{
2335    event.Enable( CanCut() );
2336}
2337
2338void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2339{
2340    event.Enable( CanCopy() );
2341}
2342
2343void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2344{
2345    event.Enable( CanPaste() );
2346}
2347
2348void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2349{
2350    event.Enable( CanUndo() );
2351}
2352
2353void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2354{
2355    event.Enable( CanRedo() );
2356}
2357
2358wxSize wxTextCtrl::DoGetBestSize() const
2359{
2360    if (IsSingleLine())
2361    {
2362        wxSize ret(80, m_lineHeight + 4);
2363
2364        if (HasFlag(wxBORDER_SUNKEN) || HasFlag(wxBORDER_RAISED))
2365            ret.y += 4;
2366
2367        if (HasFlag(wxBORDER_SIMPLE))
2368            ret.y += 2;
2369
2370        return ret;
2371    }
2372    else
2373    {
2374        return wxSize(80, 60);
2375    }
2376}
2377
2378// ----------------------------------------------------------------------------
2379// freeze/thaw
2380// ----------------------------------------------------------------------------
2381
2382void wxTextCtrl::Freeze()
2383{
2384}
2385
2386void wxTextCtrl::Thaw()
2387{
2388}
2389
2390void wxTextCtrl::OnSetFocus( wxFocusEvent& event )
2391{
2392    // To hide or show caret, as appropriate
2393    Refresh();
2394}
2395
2396void wxTextCtrl::OnKillFocus( wxFocusEvent& event )
2397{
2398    // To hide or show caret, as appropriate
2399    Refresh();
2400}
2401
2402// ----------------------------------------------------------------------------
2403// text control scrolling
2404// ----------------------------------------------------------------------------
2405
2406bool wxTextCtrl::ScrollLines(int lines)
2407{
2408    wxFAIL_MSG( "wxTextCtrl::ScrollLines not implemented");
2409
2410    return false;
2411}
2412
2413bool wxTextCtrl::ScrollPages(int pages)
2414{
2415    wxFAIL_MSG( "wxTextCtrl::ScrollPages not implemented");
2416
2417    return false;
2418}
2419