1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/richtext/richeditctrl.cpp
3// Purpose:     A rich edit control
4// Author:      Julian Smart
5// Modified by:
6// Created:     2005-09-30
7// RCS-ID:      $Id: richtextctrl.cpp 67025 2011-02-25 17:28:13Z JS $
8// Copyright:   (c) Julian Smart
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16    #pragma hdrstop
17#endif
18
19#if wxUSE_RICHTEXT
20
21#include "wx/richtext/richtextctrl.h"
22#include "wx/richtext/richtextstyles.h"
23
24#ifndef WX_PRECOMP
25    #include "wx/wx.h"
26    #include "wx/settings.h"
27#endif
28
29#include "wx/timer.h"
30#include "wx/textfile.h"
31#include "wx/ffile.h"
32#include "wx/filename.h"
33#include "wx/dcbuffer.h"
34#include "wx/arrimpl.cpp"
35#include "wx/fontenum.h"
36#include "wx/accel.h"
37
38// DLL options compatibility check:
39#include "wx/app.h"
40
41// Refresh the area affected by a selection change
42bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection);
43
44WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
45
46DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
47DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK)
48DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK)
49DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK)
50DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN)
51DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CHARACTER)
52DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_DELETE)
53
54DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING)
55DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED)
56DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGING)
57DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGED)
58
59DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED)
60DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED)
61DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED)
62DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_SELECTION_CHANGED)
63DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET)
64
65#if wxRICHTEXT_USE_OWN_CARET
66
67class wxRichTextCaret;
68class wxRichTextCaretTimer: public wxTimer
69{
70  public:
71    wxRichTextCaretTimer(wxRichTextCaret* caret)
72    {
73        m_caret = caret;
74    }
75    virtual void Notify();
76    wxRichTextCaret* m_caret;
77};
78
79/*!
80 * wxRichTextCaret
81 *
82 * This implements a non-flashing cursor in case there
83 * are platform-specific problems with the generic caret.
84 * wxRICHTEXT_USE_OWN_CARET is set in richtextbuffer.h.
85 */
86
87class wxRichTextCaret: public wxCaret
88{
89public:
90    // ctors
91    // -----
92        // default - use Create()
93    wxRichTextCaret(): m_timer(this)  { Init(); }
94        // creates a block caret associated with the given window
95    wxRichTextCaret(wxRichTextCtrl *window, int width, int height)
96        : wxCaret(window, width, height), m_timer(this) { Init(); m_richTextCtrl = window; }
97    wxRichTextCaret(wxRichTextCtrl *window, const wxSize& size)
98        : wxCaret(window, size), m_timer(this) { Init(); m_richTextCtrl = window; }
99
100    virtual ~wxRichTextCaret();
101
102    // implementation
103    // --------------
104
105    // called by wxWindow (not using the event tables)
106    virtual void OnSetFocus();
107    virtual void OnKillFocus();
108
109    // draw the caret on the given DC
110    void DoDraw(wxDC *dc);
111
112    // get the visible count
113    int GetVisibleCount() const { return m_countVisible; }
114
115    // delay repositioning
116    bool GetNeedsUpdate() const { return m_needsUpdate; }
117    void SetNeedsUpdate(bool needsUpdate = true ) { m_needsUpdate = needsUpdate; }
118
119    void Notify();
120
121protected:
122    virtual void DoShow();
123    virtual void DoHide();
124    virtual void DoMove();
125    virtual void DoSize();
126
127    // refresh the caret
128    void Refresh();
129
130private:
131    void Init();
132
133    int           m_xOld,
134                  m_yOld;
135    bool          m_hasFocus;       // true => our window has focus
136    bool          m_needsUpdate;    // must be repositioned
137    bool          m_flashOn;
138    wxRichTextCaretTimer m_timer;
139    wxRichTextCtrl* m_richTextCtrl;
140};
141#endif
142
143IMPLEMENT_CLASS( wxRichTextCtrl, wxTextCtrlBase )
144
145IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent )
146
147BEGIN_EVENT_TABLE( wxRichTextCtrl, wxTextCtrlBase )
148    EVT_PAINT(wxRichTextCtrl::OnPaint)
149    EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
150    EVT_IDLE(wxRichTextCtrl::OnIdle)
151    EVT_SCROLLWIN(wxRichTextCtrl::OnScroll)
152    EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
153    EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
154    EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
155    EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick)
156    EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick)
157    EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick)
158    EVT_CHAR(wxRichTextCtrl::OnChar)
159    EVT_KEY_DOWN(wxRichTextCtrl::OnChar)
160    EVT_SIZE(wxRichTextCtrl::OnSize)
161    EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus)
162    EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus)
163    EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost)
164    EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
165
166    EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
167    EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
168
169    EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo)
170    EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo)
171
172    EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy)
173    EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy)
174
175    EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste)
176    EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste)
177
178    EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut)
179    EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut)
180
181    EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear)
182    EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear)
183
184    EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll)
185    EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll)
186END_EVENT_TABLE()
187
188/*!
189 * wxRichTextCtrl
190 */
191
192wxArrayString wxRichTextCtrl::sm_availableFontNames;
193
194wxRichTextCtrl::wxRichTextCtrl()
195              : wxScrollHelper(this)
196{
197    Init();
198}
199
200wxRichTextCtrl::wxRichTextCtrl(wxWindow* parent,
201                               wxWindowID id,
202                               const wxString& value,
203                               const wxPoint& pos,
204                               const wxSize& size,
205                               long style,
206                               const wxValidator& validator,
207                               const wxString& name)
208              : wxScrollHelper(this)
209{
210    Init();
211    Create(parent, id, value, pos, size, style, validator, name);
212}
213
214/// Creation
215bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style,
216                             const wxValidator& validator, const wxString& name)
217{
218    if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT)
219#ifdef __WXMSW__
220        style |= GetThemedBorderStyle();
221#else
222        style |= wxBORDER_SUNKEN;
223#endif
224
225    if (!wxTextCtrlBase::Create(parent, id, pos, size,
226                                style|wxFULL_REPAINT_ON_RESIZE,
227                                validator, name))
228        return false;
229
230    if (!GetFont().Ok())
231    {
232        SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
233    }
234
235    // No physical scrolling, so we can preserve margins
236    EnableScrolling(false, false);
237
238    if (style & wxTE_READONLY)
239        SetEditable(false);
240
241    // The base attributes must all have default values
242    wxTextAttrEx attributes;
243    attributes.SetFont(GetFont());
244    attributes.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
245    attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
246    attributes.SetLineSpacing(10);
247    attributes.SetParagraphSpacingAfter(10);
248    attributes.SetParagraphSpacingBefore(0);
249
250    SetBasicStyle(attributes);
251
252    // The default attributes will be merged with base attributes, so
253    // can be empty to begin with
254    wxTextAttrEx defaultAttributes;
255    SetDefaultStyle(defaultAttributes);
256
257    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
258    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
259
260    GetBuffer().Reset();
261    GetBuffer().SetRichTextCtrl(this);
262
263#if wxRICHTEXT_USE_OWN_CARET
264    SetCaret(new wxRichTextCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
265#else
266    SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
267#endif
268
269    // Tell the sizers to use the given or best size
270    SetInitialSize(size);
271
272#if wxRICHTEXT_BUFFERED_PAINTING
273    // Create a buffer
274    RecreateBuffer(size);
275#endif
276
277    m_textCursor = wxCursor(wxCURSOR_IBEAM);
278    m_urlCursor = wxCursor(wxCURSOR_HAND);
279
280    SetCursor(m_textCursor);
281
282    if (!value.IsEmpty())
283        SetValue(value);
284
285    GetBuffer().AddEventHandler(this);
286
287    // Accelerators
288    wxAcceleratorEntry entries[6];
289
290    entries[0].Set(wxACCEL_CMD,   (int) 'C',       wxID_COPY);
291    entries[1].Set(wxACCEL_CMD,   (int) 'X',       wxID_CUT);
292    entries[2].Set(wxACCEL_CMD,   (int) 'V',       wxID_PASTE);
293    entries[3].Set(wxACCEL_CMD,   (int) 'A',       wxID_SELECTALL);
294    entries[4].Set(wxACCEL_CMD,   (int) 'Z',       wxID_UNDO);
295    entries[5].Set(wxACCEL_CMD,   (int) 'Y',       wxID_REDO);
296
297    wxAcceleratorTable accel(6, entries);
298    SetAcceleratorTable(accel);
299
300    m_contextMenu = new wxMenu;
301    m_contextMenu->Append(wxID_UNDO, _("&Undo"));
302    m_contextMenu->Append(wxID_REDO, _("&Redo"));
303    m_contextMenu->AppendSeparator();
304    m_contextMenu->Append(wxID_CUT, _("Cu&t"));
305    m_contextMenu->Append(wxID_COPY, _("&Copy"));
306    m_contextMenu->Append(wxID_PASTE, _("&Paste"));
307    m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
308    m_contextMenu->AppendSeparator();
309    m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
310
311    return true;
312}
313
314wxRichTextCtrl::~wxRichTextCtrl()
315{
316    GetBuffer().RemoveEventHandler(this);
317
318    delete m_contextMenu;
319}
320
321/// Member initialisation
322void wxRichTextCtrl::Init()
323{
324    m_freezeCount = 0;
325    m_contextMenu = NULL;
326    m_caret = NULL;
327    m_caretPosition = -1;
328    m_selectionRange.SetRange(-2, -2);
329    m_selectionAnchor = -2;
330    m_editable = true;
331    m_caretAtLineStart = false;
332    m_dragging = false;
333    m_fullLayoutRequired = false;
334    m_fullLayoutTime = 0;
335    m_fullLayoutSavedPosition = 0;
336    m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
337    m_caretPositionForDefaultStyle = -2;
338}
339
340/// Call Freeze to prevent refresh
341void wxRichTextCtrl::Freeze()
342{
343    m_freezeCount ++;
344}
345
346/// Call Thaw to refresh
347void wxRichTextCtrl::Thaw()
348{
349    m_freezeCount --;
350
351    if (m_freezeCount == 0)
352    {
353        if (GetBuffer().GetDirty())
354            LayoutContent();
355        else
356            SetupScrollbars();
357
358        Refresh(false);
359    }
360}
361
362/// Clear all text
363void wxRichTextCtrl::Clear()
364{
365    m_buffer.ResetAndClearCommands();
366    m_buffer.SetDirty(true);
367    m_caretPosition = -1;
368    m_caretPositionForDefaultStyle = -2;
369    m_caretAtLineStart = false;
370    m_selectionRange.SetRange(-2, -2);
371
372    Scroll(0,0);
373
374    if (m_freezeCount == 0)
375    {
376        LayoutContent();
377        Refresh(false);
378    }
379    SendTextUpdatedEvent();
380}
381
382/// Painting
383void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
384{
385#if !wxRICHTEXT_USE_OWN_CARET
386    if (GetCaret() && !IsFrozen())
387        GetCaret()->Hide();
388#endif
389
390    {
391#if wxRICHTEXT_BUFFERED_PAINTING
392        wxBufferedPaintDC dc(this, m_bufferBitmap);
393#else
394        wxPaintDC dc(this);
395#endif
396        PrepareDC(dc);
397
398        if (IsFrozen())
399        {
400            return;
401        }
402
403        dc.SetFont(GetFont());
404
405        // Paint the background
406        PaintBackground(dc);
407
408        // wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
409
410        wxRect drawingArea(GetUpdateRegion().GetBox());
411        drawingArea.SetPosition(GetLogicalPoint(drawingArea.GetPosition()));
412
413        wxRect availableSpace(GetClientSize());
414        if (GetBuffer().GetDirty())
415        {
416            GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
417            GetBuffer().SetDirty(false);
418            SetupScrollbars();
419        }
420
421        wxRect clipRect(availableSpace);
422        clipRect.x += GetBuffer().GetLeftMargin();
423        clipRect.y += GetBuffer().GetTopMargin();
424        clipRect.width -= (GetBuffer().GetLeftMargin() + GetBuffer().GetRightMargin());
425        clipRect.height -= (GetBuffer().GetTopMargin() + GetBuffer().GetBottomMargin());
426        clipRect.SetPosition(GetLogicalPoint(clipRect.GetPosition()));
427        dc.SetClippingRegion(clipRect);
428
429        GetBuffer().Draw(dc, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
430
431        dc.DestroyClippingRegion();
432
433#if wxRICHTEXT_USE_OWN_CARET
434        if (GetCaret()->IsVisible())
435        {
436            ((wxRichTextCaret*) GetCaret())->DoDraw(& dc);
437        }
438#endif
439
440    }
441
442#if !wxRICHTEXT_USE_OWN_CARET
443    if (GetCaret())
444        GetCaret()->Show();
445    PositionCaret();
446#endif
447}
448
449// Empty implementation, to prevent flicker
450void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
451{
452}
453
454void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
455{
456    if (GetCaret())
457    {
458#if !wxRICHTEXT_USE_OWN_CARET
459        PositionCaret();
460#endif
461        GetCaret()->Show();
462    }
463
464#if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
465    // Work around dropouts when control is focused
466    if (!IsFrozen())
467    {
468        Refresh(false);
469    }
470#endif
471}
472
473void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
474{
475    if (GetCaret())
476        GetCaret()->Hide();
477
478#if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
479    // Work around dropouts when control is focused
480    if (!IsFrozen())
481    {
482        Refresh(false);
483    }
484#endif
485}
486
487void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
488{
489    m_dragging = false;
490}
491
492/// Left-click
493void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
494{
495    SetFocus();
496
497    wxClientDC dc(this);
498    PrepareDC(dc);
499    dc.SetFont(GetFont());
500
501    long position = 0;
502    int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position);
503
504    if (hit != wxRICHTEXT_HITTEST_NONE)
505    {
506        m_dragStart = event.GetLogicalPosition(dc);
507        m_dragging = true;
508        CaptureMouse();
509
510        bool caretAtLineStart = false;
511
512        if (hit & wxRICHTEXT_HITTEST_BEFORE)
513        {
514            // If we're at the start of a line (but not first in para)
515            // then we should keep the caret showing at the start of the line
516            // by showing the m_caretAtLineStart flag.
517            wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
518            wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
519
520            if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
521                caretAtLineStart = true;
522            position --;
523        }
524
525        long oldCaretPos = m_caretPosition;
526
527        MoveCaret(position, caretAtLineStart);
528        SetDefaultStyleToCursorStyle();
529
530        if (event.ShiftDown())
531        {
532            if (m_selectionRange.GetStart() == -2)
533                ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
534            else
535                ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
536        }
537        else
538            SelectNone();
539    }
540
541    event.Skip();
542}
543
544/// Left-up
545void wxRichTextCtrl::OnLeftUp(wxMouseEvent& event)
546{
547    if (m_dragging)
548    {
549        m_dragging = false;
550        if (GetCapture() == this)
551            ReleaseMouse();
552
553        // See if we clicked on a URL
554        wxClientDC dc(this);
555        PrepareDC(dc);
556        dc.SetFont(GetFont());
557
558        long position = 0;
559        wxPoint logicalPt = event.GetLogicalPosition(dc);
560        int hit = GetBuffer().HitTest(dc, logicalPt, position);
561
562        if ((hit != wxRICHTEXT_HITTEST_NONE) && !(hit & wxRICHTEXT_HITTEST_OUTSIDE))
563        {
564            wxTextAttrEx attr;
565            if (GetStyle(position, attr))
566            {
567                if (attr.HasFlag(wxTEXT_ATTR_URL))
568                {
569                    wxString urlTarget = attr.GetURL();
570                    if (!urlTarget.IsEmpty())
571                    {
572                        wxMouseEvent mouseEvent(event);
573
574                        long startPos = 0, endPos = 0;
575                        wxRichTextObject* obj = GetBuffer().GetLeafObjectAtPosition(position);
576                        if (obj)
577                        {
578                            startPos = obj->GetRange().GetStart();
579                            endPos = obj->GetRange().GetEnd();
580                        }
581
582                        wxTextUrlEvent urlEvent(GetId(), mouseEvent, startPos, endPos);
583                        InitCommandEvent(urlEvent);
584
585                        urlEvent.SetString(urlTarget);
586
587                        GetEventHandler()->ProcessEvent(urlEvent);
588                    }
589                }
590            }
591        }
592    }
593}
594
595/// Left-click
596void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
597{
598    wxClientDC dc(this);
599    PrepareDC(dc);
600    dc.SetFont(GetFont());
601
602    long position = 0;
603    wxPoint logicalPt = event.GetLogicalPosition(dc);
604    int hit = GetBuffer().HitTest(dc, logicalPt, position);
605
606    // See if we need to change the cursor
607
608    {
609        if (hit != wxRICHTEXT_HITTEST_NONE && !(hit & wxRICHTEXT_HITTEST_OUTSIDE))
610        {
611            wxTextAttrEx attr;
612            if (GetStyle(position, attr))
613            {
614                if (attr.HasFlag(wxTEXT_ATTR_URL))
615                {
616                    SetCursor(m_urlCursor);
617                }
618                else if (!attr.HasFlag(wxTEXT_ATTR_URL))
619                {
620                    SetCursor(m_textCursor);
621                }
622            }
623        }
624        else
625            SetCursor(m_textCursor);
626    }
627
628    if (!event.Dragging())
629    {
630        event.Skip();
631        return;
632    }
633
634    if (m_dragging && hit != wxRICHTEXT_HITTEST_NONE)
635    {
636        // TODO: test closeness
637
638        bool caretAtLineStart = false;
639
640        if (hit & wxRICHTEXT_HITTEST_BEFORE)
641        {
642            // If we're at the start of a line (but not first in para)
643            // then we should keep the caret showing at the start of the line
644            // by showing the m_caretAtLineStart flag.
645            wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
646            wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
647
648            if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
649                caretAtLineStart = true;
650            position --;
651        }
652
653        if (m_caretPosition != position)
654        {
655            ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
656
657            MoveCaret(position, caretAtLineStart);
658            SetDefaultStyleToCursorStyle();
659        }
660    }
661}
662
663/// Right-click
664void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
665{
666    SetFocus();
667    event.Skip();
668}
669
670/// Left-double-click
671void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
672{
673    SelectWord(GetCaretPosition()+1);
674    event.Skip();
675}
676
677/// Middle-click
678void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
679{
680    event.Skip();
681}
682
683/// Key press
684void wxRichTextCtrl::OnChar(wxKeyEvent& event)
685{
686    int flags = 0;
687    if (event.CmdDown())
688        flags |= wxRICHTEXT_CTRL_DOWN;
689    if (event.ShiftDown())
690        flags |= wxRICHTEXT_SHIFT_DOWN;
691    if (event.AltDown())
692        flags |= wxRICHTEXT_ALT_DOWN;
693
694    if (event.GetEventType() == wxEVT_KEY_DOWN)
695    {
696        if (event.GetKeyCode() == WXK_LEFT ||
697            event.GetKeyCode() == WXK_RIGHT ||
698            event.GetKeyCode() == WXK_UP ||
699            event.GetKeyCode() == WXK_DOWN ||
700            event.GetKeyCode() == WXK_HOME ||
701            event.GetKeyCode() == WXK_PAGEUP ||
702            event.GetKeyCode() == WXK_PAGEDOWN ||
703            event.GetKeyCode() == WXK_END ||
704
705            event.GetKeyCode() == WXK_NUMPAD_LEFT ||
706            event.GetKeyCode() == WXK_NUMPAD_RIGHT ||
707            event.GetKeyCode() == WXK_NUMPAD_UP ||
708            event.GetKeyCode() == WXK_NUMPAD_DOWN ||
709            event.GetKeyCode() == WXK_NUMPAD_HOME ||
710            event.GetKeyCode() == WXK_NUMPAD_PAGEUP ||
711            event.GetKeyCode() == WXK_NUMPAD_PAGEDOWN ||
712            event.GetKeyCode() == WXK_NUMPAD_END)
713        {
714            KeyboardNavigate(event.GetKeyCode(), flags);
715            return;
716        }
717
718        long keycode = event.GetKeyCode();
719        switch ( keycode )
720        {
721            case WXK_ESCAPE:
722            case WXK_START:
723            case WXK_LBUTTON:
724            case WXK_RBUTTON:
725            case WXK_CANCEL:
726            case WXK_MBUTTON:
727            case WXK_CLEAR:
728            case WXK_SHIFT:
729            case WXK_ALT:
730            case WXK_CONTROL:
731            case WXK_MENU:
732            case WXK_PAUSE:
733            case WXK_CAPITAL:
734            case WXK_END:
735            case WXK_HOME:
736            case WXK_LEFT:
737            case WXK_UP:
738            case WXK_RIGHT:
739            case WXK_DOWN:
740            case WXK_SELECT:
741            case WXK_PRINT:
742            case WXK_EXECUTE:
743            case WXK_SNAPSHOT:
744            case WXK_INSERT:
745            case WXK_HELP:
746            case WXK_F1:
747            case WXK_F2:
748            case WXK_F3:
749            case WXK_F4:
750            case WXK_F5:
751            case WXK_F6:
752            case WXK_F7:
753            case WXK_F8:
754            case WXK_F9:
755            case WXK_F10:
756            case WXK_F11:
757            case WXK_F12:
758            case WXK_F13:
759            case WXK_F14:
760            case WXK_F15:
761            case WXK_F16:
762            case WXK_F17:
763            case WXK_F18:
764            case WXK_F19:
765            case WXK_F20:
766            case WXK_F21:
767            case WXK_F22:
768            case WXK_F23:
769            case WXK_F24:
770            case WXK_NUMLOCK:
771            case WXK_SCROLL:
772            case WXK_PAGEUP:
773            case WXK_PAGEDOWN:
774            case WXK_NUMPAD_F1:
775            case WXK_NUMPAD_F2:
776            case WXK_NUMPAD_F3:
777            case WXK_NUMPAD_F4:
778            case WXK_NUMPAD_HOME:
779            case WXK_NUMPAD_LEFT:
780            case WXK_NUMPAD_UP:
781            case WXK_NUMPAD_RIGHT:
782            case WXK_NUMPAD_DOWN:
783            case WXK_NUMPAD_PAGEUP:
784            case WXK_NUMPAD_PAGEDOWN:
785            case WXK_NUMPAD_END:
786            case WXK_NUMPAD_BEGIN:
787            case WXK_NUMPAD_INSERT:
788            case WXK_WINDOWS_LEFT:
789            {
790                return;
791            }
792            default:
793            {
794            }
795        }
796
797        // Must process this before translation, otherwise it's translated into a WXK_DELETE event.
798        if (event.CmdDown() && event.GetKeyCode() == WXK_BACK)
799        {
800            BeginBatchUndo(_("Delete Text"));
801
802            long newPos = m_caretPosition;
803
804            bool processed = DeleteSelectedContent(& newPos);
805
806            // Submit range in character positions, which are greater than caret positions,
807            // so subtract 1 for deleted character and add 1 for conversion to character position.
808            if (newPos > -1)
809            {
810                if (event.CmdDown())
811                {
812                    long pos = wxRichTextCtrl::FindNextWordPosition(-1);
813                    if (pos < newPos)
814                    {
815                        GetBuffer().DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this);
816                        processed = true;
817                    }
818                }
819
820                if (!processed)
821                    GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this);
822            }
823
824            EndBatchUndo();
825
826            if (GetLastPosition() == -1)
827            {
828                GetBuffer().Reset();
829
830                m_caretPosition = -1;
831                PositionCaret();
832                SetDefaultStyleToCursorStyle();
833            }
834
835            ScrollIntoView(m_caretPosition, WXK_LEFT);
836
837            wxRichTextEvent cmdEvent(
838                wxEVT_COMMAND_RICHTEXT_DELETE,
839                GetId());
840            cmdEvent.SetEventObject(this);
841            cmdEvent.SetFlags(flags);
842            cmdEvent.SetPosition(m_caretPosition+1);
843            GetEventHandler()->ProcessEvent(cmdEvent);
844
845            Update();
846        }
847        else
848            event.Skip();
849
850        return;
851    }
852
853    // all the other keys modify the controls contents which shouldn't be
854    // possible if we're read-only
855    if ( !IsEditable() )
856    {
857        event.Skip();
858        return;
859    }
860
861    if (event.GetKeyCode() == WXK_RETURN)
862    {
863        BeginBatchUndo(_("Insert Text"));
864
865        long newPos = m_caretPosition;
866
867        DeleteSelectedContent(& newPos);
868
869        if (event.ShiftDown())
870        {
871            wxString text;
872            text = wxRichTextLineBreakChar;
873            GetBuffer().InsertTextWithUndo(newPos+1, text, this);
874            m_caretAtLineStart = true;
875            PositionCaret();
876        }
877        else
878            GetBuffer().InsertNewlineWithUndo(newPos+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE|wxRICHTEXT_INSERT_INTERACTIVE);
879
880        EndBatchUndo();
881        SetDefaultStyleToCursorStyle();
882
883        ScrollIntoView(m_caretPosition, WXK_RIGHT);
884
885        wxRichTextEvent cmdEvent(
886            wxEVT_COMMAND_RICHTEXT_RETURN,
887            GetId());
888        cmdEvent.SetEventObject(this);
889        cmdEvent.SetFlags(flags);
890        cmdEvent.SetPosition(newPos+1);
891
892        if (!GetEventHandler()->ProcessEvent(cmdEvent))
893        {
894            // Generate conventional event
895            wxCommandEvent textEvent(wxEVT_COMMAND_TEXT_ENTER, GetId());
896            InitCommandEvent(textEvent);
897
898            GetEventHandler()->ProcessEvent(textEvent);
899        }
900        Update();
901    }
902    else if (event.GetKeyCode() == WXK_BACK)
903    {
904        BeginBatchUndo(_("Delete Text"));
905
906        long newPos = m_caretPosition;
907
908        bool processed = DeleteSelectedContent(& newPos);
909
910        // Submit range in character positions, which are greater than caret positions,
911        // so subtract 1 for deleted character and add 1 for conversion to character position.
912        if (newPos > -1)
913        {
914            if (event.CmdDown())
915            {
916                long pos = wxRichTextCtrl::FindNextWordPosition(-1);
917                if (pos < newPos)
918                {
919                    GetBuffer().DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this);
920                    processed = true;
921                }
922            }
923
924            if (!processed)
925                GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this);
926        }
927
928        EndBatchUndo();
929
930        if (GetLastPosition() == -1)
931        {
932            GetBuffer().Reset();
933
934            m_caretPosition = -1;
935            PositionCaret();
936            SetDefaultStyleToCursorStyle();
937        }
938
939        ScrollIntoView(m_caretPosition, WXK_LEFT);
940
941        wxRichTextEvent cmdEvent(
942            wxEVT_COMMAND_RICHTEXT_DELETE,
943            GetId());
944        cmdEvent.SetEventObject(this);
945        cmdEvent.SetFlags(flags);
946        cmdEvent.SetPosition(m_caretPosition+1);
947        GetEventHandler()->ProcessEvent(cmdEvent);
948
949        Update();
950    }
951    else if (event.GetKeyCode() == WXK_DELETE)
952    {
953        BeginBatchUndo(_("Delete Text"));
954
955        long newPos = m_caretPosition;
956
957        bool processed = DeleteSelectedContent(& newPos);
958
959        // Submit range in character positions, which are greater than caret positions,
960        if (newPos < GetBuffer().GetRange().GetEnd()+1)
961        {
962            if (event.CmdDown())
963            {
964                long pos = wxRichTextCtrl::FindNextWordPosition(1);
965                if (pos != -1 && (pos > newPos))
966                {
967                    GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos+1, pos), this);
968                    processed = true;
969                }
970            }
971
972            if (!processed && newPos < (GetLastPosition()-1))
973                GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos+1, newPos+1), this);
974        }
975
976        EndBatchUndo();
977
978        if (GetLastPosition() == -1)
979        {
980            GetBuffer().Reset();
981
982            m_caretPosition = -1;
983            PositionCaret();
984            SetDefaultStyleToCursorStyle();
985        }
986
987        wxRichTextEvent cmdEvent(
988            wxEVT_COMMAND_RICHTEXT_DELETE,
989            GetId());
990        cmdEvent.SetEventObject(this);
991        cmdEvent.SetFlags(flags);
992        cmdEvent.SetPosition(m_caretPosition+1);
993        GetEventHandler()->ProcessEvent(cmdEvent);
994
995        Update();
996    }
997    else
998    {
999        long keycode = event.GetKeyCode();
1000        switch ( keycode )
1001        {
1002            case WXK_ESCAPE:
1003            {
1004                event.Skip();
1005                return;
1006            }
1007
1008            default:
1009            {
1010#ifdef __WXMAC__
1011                if (event.CmdDown())
1012#else
1013                // Fixes AltGr+key with European input languages on Windows
1014                if ((event.CmdDown() && !event.AltDown()) || (event.AltDown() && !event.CmdDown()))
1015#endif
1016                {
1017                    event.Skip();
1018                    return;
1019                }
1020
1021                wxRichTextEvent cmdEvent(
1022                    wxEVT_COMMAND_RICHTEXT_CHARACTER,
1023                    GetId());
1024                cmdEvent.SetEventObject(this);
1025                cmdEvent.SetFlags(flags);
1026#if wxUSE_UNICODE
1027                cmdEvent.SetCharacter(event.GetUnicodeKey());
1028#else
1029                cmdEvent.SetCharacter((wxChar) keycode);
1030#endif
1031                cmdEvent.SetPosition(m_caretPosition+1);
1032
1033                if (keycode == wxT('\t'))
1034                {
1035                    // See if we need to promote or demote the selection or paragraph at the cursor
1036                    // position, instead of inserting a tab.
1037                    long pos = GetAdjustedCaretPosition(GetCaretPosition());
1038                    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos);
1039                    if (para && para->GetRange().GetStart() == pos && para->GetAttributes().HasListStyleName())
1040                    {
1041                        wxRichTextRange range;
1042                        if (HasSelection())
1043                            range = GetSelectionRange();
1044                        else
1045                            range = para->GetRange().FromInternal();
1046
1047                        int promoteBy = event.ShiftDown() ? 1 : -1;
1048
1049                        PromoteList(promoteBy, range, NULL);
1050
1051                        GetEventHandler()->ProcessEvent(cmdEvent);
1052
1053                        return;
1054                    }
1055                }
1056
1057                BeginBatchUndo(_("Insert Text"));
1058
1059                long newPos = m_caretPosition;
1060                DeleteSelectedContent(& newPos);
1061
1062#if wxUSE_UNICODE
1063                wxString str = event.GetUnicodeKey();
1064#else
1065                wxString str = (wxChar) event.GetKeyCode();
1066#endif
1067                GetBuffer().InsertTextWithUndo(newPos+1, str, this, 0);
1068
1069                EndBatchUndo();
1070
1071                SetDefaultStyleToCursorStyle();
1072                ScrollIntoView(m_caretPosition, WXK_RIGHT);
1073
1074                GetEventHandler()->ProcessEvent(cmdEvent);
1075
1076                Update();
1077            }
1078        }
1079    }
1080}
1081
1082/// Delete content if there is a selection, e.g. when pressing a key.
1083bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
1084{
1085    if (HasSelection())
1086    {
1087        long pos = m_selectionRange.GetStart();
1088        wxRichTextRange range = m_selectionRange;
1089
1090        // SelectAll causes more to be selected than doing it interactively,
1091        // and causes a new paragraph to be inserted. So for multiline buffers,
1092        // don't delete the final position.
1093        if (range.GetEnd() == GetLastPosition() && GetNumberOfLines() > 0)
1094            range.SetEnd(range.GetEnd()-1);
1095
1096        GetBuffer().DeleteRangeWithUndo(range, this);
1097        m_selectionRange.SetRange(-2, -2);
1098
1099        if (newPos)
1100            *newPos = pos-1;
1101        return true;
1102    }
1103    else
1104        return false;
1105}
1106
1107/// Keyboard navigation
1108
1109/*
1110
1111Left:       left one character
1112Right:      right one character
1113Up:         up one line
1114Down:       down one line
1115Ctrl-Left:  left one word
1116Ctrl-Right: right one word
1117Ctrl-Up:    previous paragraph start
1118Ctrl-Down:  next start of paragraph
1119Home:       start of line
1120End:        end of line
1121Ctrl-Home:  start of document
1122Ctrl-End:   end of document
1123Page-Up:    Up a screen
1124Page-Down:  Down a screen
1125
1126Maybe:
1127
1128Ctrl-Alt-PgUp: Start of window
1129Ctrl-Alt-PgDn: End of window
1130F8:         Start selection mode
1131Esc:        End selection mode
1132
1133Adding Shift does the above but starts/extends selection.
1134
1135
1136 */
1137
1138bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags)
1139{
1140    bool success = false;
1141
1142    if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT)
1143    {
1144        if (flags & wxRICHTEXT_CTRL_DOWN)
1145            success = WordRight(1, flags);
1146        else
1147            success = MoveRight(1, flags);
1148    }
1149    else if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT)
1150    {
1151        if (flags & wxRICHTEXT_CTRL_DOWN)
1152            success = WordLeft(1, flags);
1153        else
1154            success = MoveLeft(1, flags);
1155    }
1156    else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP)
1157    {
1158        if (flags & wxRICHTEXT_CTRL_DOWN)
1159            success = MoveToParagraphStart(flags);
1160        else
1161            success = MoveUp(1, flags);
1162    }
1163    else if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN)
1164    {
1165        if (flags & wxRICHTEXT_CTRL_DOWN)
1166            success = MoveToParagraphEnd(flags);
1167        else
1168            success = MoveDown(1, flags);
1169    }
1170    else if (keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP)
1171    {
1172        success = PageUp(1, flags);
1173    }
1174    else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1175    {
1176        success = PageDown(1, flags);
1177    }
1178    else if (keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME)
1179    {
1180        if (flags & wxRICHTEXT_CTRL_DOWN)
1181            success = MoveHome(flags);
1182        else
1183            success = MoveToLineStart(flags);
1184    }
1185    else if (keyCode == WXK_END || keyCode == WXK_NUMPAD_END)
1186    {
1187        if (flags & wxRICHTEXT_CTRL_DOWN)
1188            success = MoveEnd(flags);
1189        else
1190            success = MoveToLineEnd(flags);
1191    }
1192
1193    if (success)
1194    {
1195        ScrollIntoView(m_caretPosition, keyCode);
1196        SetDefaultStyleToCursorStyle();
1197    }
1198
1199    return success;
1200}
1201
1202/// Extend the selection. Selections are in caret positions.
1203bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
1204{
1205    if (flags & wxRICHTEXT_SHIFT_DOWN)
1206    {
1207        if (oldPos == newPos)
1208            return false;
1209
1210        wxRichTextRange oldSelection = m_selectionRange;
1211
1212        // If not currently selecting, start selecting
1213        if (m_selectionRange.GetStart() == -2)
1214        {
1215            m_selectionAnchor = oldPos;
1216
1217            if (oldPos > newPos)
1218                m_selectionRange.SetRange(newPos+1, oldPos);
1219            else
1220                m_selectionRange.SetRange(oldPos+1, newPos);
1221        }
1222        else
1223        {
1224            // Always ensure that the selection range start is greater than
1225            // the end.
1226            if (newPos > m_selectionAnchor)
1227                m_selectionRange.SetRange(m_selectionAnchor+1, newPos);
1228            else if (newPos == m_selectionAnchor)
1229                m_selectionRange = wxRichTextRange(-2, -2);
1230            else
1231                m_selectionRange.SetRange(newPos+1, m_selectionAnchor);
1232        }
1233
1234        wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
1235
1236        if (m_selectionRange.GetStart() > m_selectionRange.GetEnd())
1237        {
1238            wxLogDebug(wxT("Strange selection range"));
1239        }
1240
1241        return true;
1242    }
1243    else
1244        return false;
1245}
1246
1247/// Scroll into view, returning true if we scrolled.
1248/// This takes a _caret_ position.
1249bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
1250{
1251    wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
1252
1253    if (!line)
1254        return false;
1255
1256    int ppuX, ppuY;
1257    GetScrollPixelsPerUnit(& ppuX, & ppuY);
1258
1259    int startXUnits, startYUnits;
1260    GetViewStart(& startXUnits, & startYUnits);
1261    int startY = startYUnits * ppuY;
1262
1263    int sx = 0, sy = 0;
1264    GetVirtualSize(& sx, & sy);
1265    int sxUnits = 0;
1266    int syUnits = 0;
1267    if (ppuY != 0)
1268        syUnits = sy/ppuY;
1269
1270    wxRect rect = line->GetRect();
1271
1272    bool scrolled = false;
1273
1274    wxSize clientSize = GetClientSize();
1275    clientSize.y -= GetBuffer().GetBottomMargin();
1276
1277    if (GetWindowStyle() & wxRE_CENTRE_CARET)
1278    {
1279        int y = rect.y - GetClientSize().y/2;
1280        int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1281        if (y >= 0 && (y + clientSize.y) < GetBuffer().GetCachedSize().y)
1282        {
1283            if (startYUnits != yUnits)
1284            {
1285                SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1286                scrolled = true;
1287            }
1288#if !wxRICHTEXT_USE_OWN_CARET
1289            if (scrolled)
1290#endif
1291                PositionCaret();
1292
1293            return scrolled;
1294        }
1295    }
1296
1297    // Going down
1298    if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN ||
1299        keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT ||
1300        keyCode == WXK_END || keyCode == WXK_NUMPAD_END ||
1301        keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1302    {
1303        if ((rect.y + rect.height) > (clientSize.y + startY))
1304        {
1305            // Make it scroll so this item is at the bottom
1306            // of the window
1307            int y = rect.y - (clientSize.y - rect.height);
1308            int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1309
1310            // If we're still off the screen, scroll another line down
1311            if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1312                yUnits ++;
1313
1314            if (startYUnits != yUnits)
1315            {
1316                SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1317                scrolled = true;
1318            }
1319        }
1320        else if (rect.y < (startY + GetBuffer().GetTopMargin()))
1321        {
1322            // Make it scroll so this item is at the top
1323            // of the window
1324            int y = rect.y - GetBuffer().GetTopMargin();
1325            int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1326
1327            if (startYUnits != yUnits)
1328            {
1329                SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1330                scrolled = true;
1331            }
1332        }
1333    }
1334    // Going up
1335    else if (keyCode == WXK_UP  || keyCode == WXK_NUMPAD_UP ||
1336             keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT ||
1337             keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME ||
1338             keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP )
1339    {
1340        if (rect.y < (startY + GetBuffer().GetBottomMargin()))
1341        {
1342            // Make it scroll so this item is at the top
1343            // of the window
1344            int y = rect.y - GetBuffer().GetTopMargin();
1345            int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1346
1347            if (startYUnits != yUnits)
1348            {
1349                SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1350                scrolled = true;
1351            }
1352        }
1353        else if ((rect.y + rect.height) > (clientSize.y + startY))
1354        {
1355            // Make it scroll so this item is at the bottom
1356            // of the window
1357            int y = rect.y - (clientSize.y - rect.height);
1358            int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1359
1360            // If we're still off the screen, scroll another line down
1361            if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1362                yUnits ++;
1363
1364            if (startYUnits != yUnits)
1365            {
1366                SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1367                scrolled = true;
1368            }
1369        }
1370    }
1371
1372#if !wxRICHTEXT_USE_OWN_CARET
1373    if (scrolled)
1374#endif
1375        PositionCaret();
1376
1377    return scrolled;
1378}
1379
1380/// Is the given position visible on the screen?
1381bool wxRichTextCtrl::IsPositionVisible(long pos) const
1382{
1383    wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
1384
1385    if (!line)
1386        return false;
1387
1388    int ppuX, ppuY;
1389    GetScrollPixelsPerUnit(& ppuX, & ppuY);
1390
1391    int startX, startY;
1392    GetViewStart(& startX, & startY);
1393    startX = 0;
1394    startY = startY * ppuY;
1395
1396    wxRect rect = line->GetRect();
1397    wxSize clientSize = GetClientSize();
1398    clientSize.y -= GetBuffer().GetBottomMargin();
1399
1400    return (rect.GetTop() >= (startY + GetBuffer().GetTopMargin())) && (rect.GetBottom() <= (startY + clientSize.y));
1401}
1402
1403void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
1404{
1405    m_caretPosition = position;
1406    m_caretAtLineStart = showAtLineStart;
1407}
1408
1409/// Move caret one visual step forward: this may mean setting a flag
1410/// and keeping the same position if we're going from the end of one line
1411/// to the start of the next, which may be the exact same caret position.
1412void wxRichTextCtrl::MoveCaretForward(long oldPosition)
1413{
1414    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
1415
1416    // Only do the check if we're not at the end of the paragraph (where things work OK
1417    // anyway)
1418    if (para && (oldPosition != para->GetRange().GetEnd() - 1))
1419    {
1420        wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
1421
1422        if (line)
1423        {
1424            wxRichTextRange lineRange = line->GetAbsoluteRange();
1425
1426            // We're at the end of a line. See whether we need to
1427            // stay at the same actual caret position but change visual
1428            // position, or not.
1429            if (oldPosition == lineRange.GetEnd())
1430            {
1431                if (m_caretAtLineStart)
1432                {
1433                    // We're already at the start of the line, so actually move on now.
1434                    m_caretPosition = oldPosition + 1;
1435                    m_caretAtLineStart = false;
1436                }
1437                else
1438                {
1439                    // We're showing at the end of the line, so keep to
1440                    // the same position but indicate that we're to show
1441                    // at the start of the next line.
1442                    m_caretPosition = oldPosition;
1443                    m_caretAtLineStart = true;
1444                }
1445                SetDefaultStyleToCursorStyle();
1446                return;
1447            }
1448        }
1449    }
1450    m_caretPosition ++;
1451    SetDefaultStyleToCursorStyle();
1452}
1453
1454/// Move caret one visual step backward: this may mean setting a flag
1455/// and keeping the same position if we're going from the end of one line
1456/// to the start of the next, which may be the exact same caret position.
1457void wxRichTextCtrl::MoveCaretBack(long oldPosition)
1458{
1459    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
1460
1461    // Only do the check if we're not at the start of the paragraph (where things work OK
1462    // anyway)
1463    if (para && (oldPosition != para->GetRange().GetStart()))
1464    {
1465        wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
1466
1467        if (line)
1468        {
1469            wxRichTextRange lineRange = line->GetAbsoluteRange();
1470
1471            // We're at the start of a line. See whether we need to
1472            // stay at the same actual caret position but change visual
1473            // position, or not.
1474            if (oldPosition == lineRange.GetStart())
1475            {
1476                m_caretPosition = oldPosition-1;
1477                m_caretAtLineStart = true;
1478                return;
1479            }
1480            else if (oldPosition == lineRange.GetEnd())
1481            {
1482                if (m_caretAtLineStart)
1483                {
1484                    // We're at the start of the line, so keep the same caret position
1485                    // but clear the start-of-line flag.
1486                    m_caretPosition = oldPosition;
1487                    m_caretAtLineStart = false;
1488                }
1489                else
1490                {
1491                    // We're showing at the end of the line, so go back
1492                    // to the previous character position.
1493                    m_caretPosition = oldPosition - 1;
1494                }
1495                SetDefaultStyleToCursorStyle();
1496                return;
1497            }
1498        }
1499    }
1500    m_caretPosition --;
1501    SetDefaultStyleToCursorStyle();
1502}
1503
1504/// Move right
1505bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
1506{
1507    long endPos = GetBuffer().GetRange().GetEnd();
1508
1509    if (m_caretPosition + noPositions < endPos)
1510    {
1511        long oldPos = m_caretPosition;
1512        long newPos = m_caretPosition + noPositions;
1513
1514        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1515        if (!extendSel)
1516            SelectNone();
1517
1518        // Determine by looking at oldPos and m_caretPosition whether
1519        // we moved from the end of a line to the start of the next line, in which case
1520        // we want to adjust the caret position such that it is positioned at the
1521        // start of the next line, rather than jumping past the first character of the
1522        // line.
1523        if (noPositions == 1 && !extendSel)
1524            MoveCaretForward(oldPos);
1525        else
1526            SetCaretPosition(newPos);
1527
1528        PositionCaret();
1529        SetDefaultStyleToCursorStyle();
1530
1531        return true;
1532    }
1533    else
1534        return false;
1535}
1536
1537/// Move left
1538bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
1539{
1540    long startPos = -1;
1541
1542    if (m_caretPosition > startPos - noPositions + 1)
1543    {
1544        long oldPos = m_caretPosition;
1545        long newPos = m_caretPosition - noPositions;
1546        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1547        if (!extendSel)
1548            SelectNone();
1549
1550        if (noPositions == 1 && !extendSel)
1551            MoveCaretBack(oldPos);
1552        else
1553            SetCaretPosition(newPos);
1554
1555        PositionCaret();
1556        SetDefaultStyleToCursorStyle();
1557
1558        return true;
1559    }
1560    else
1561        return false;
1562}
1563
1564/// Move up
1565bool wxRichTextCtrl::MoveUp(int noLines, int flags)
1566{
1567    return MoveDown(- noLines, flags);
1568}
1569
1570/// Move up
1571bool wxRichTextCtrl::MoveDown(int noLines, int flags)
1572{
1573    if (!GetCaret())
1574        return false;
1575
1576    long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
1577    wxPoint pt = GetCaret()->GetPosition();
1578    long newLine = lineNumber + noLines;
1579
1580    if (lineNumber != -1)
1581    {
1582        if (noLines > 0)
1583        {
1584            long lastLine = GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd());
1585
1586            if (newLine > lastLine)
1587                return false;
1588        }
1589        else
1590        {
1591            if (newLine < 0)
1592                return false;
1593        }
1594    }
1595
1596    wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine);
1597    if (lineObj)
1598    {
1599        pt.y = lineObj->GetAbsolutePosition().y + 2;
1600    }
1601    else
1602        return false;
1603
1604    long newPos = 0;
1605    wxClientDC dc(this);
1606    PrepareDC(dc);
1607    dc.SetFont(GetFont());
1608
1609    int hitTest = GetBuffer().HitTest(dc, pt, newPos);
1610
1611    if (hitTest != wxRICHTEXT_HITTEST_NONE)
1612    {
1613        // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
1614        // we want to be at the end of the last line but with m_caretAtLineStart set to true,
1615        // so we view the caret at the start of the line.
1616        bool caretLineStart = false;
1617        if (hitTest & wxRICHTEXT_HITTEST_BEFORE)
1618        {
1619            wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1);
1620            wxRichTextRange lineRange;
1621            if (thisLine)
1622                lineRange = thisLine->GetAbsoluteRange();
1623
1624            if (thisLine && (newPos-1) == lineRange.GetEnd())
1625            {
1626                newPos --;
1627                caretLineStart = true;
1628            }
1629            else
1630            {
1631                wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(newPos);
1632                if (para && para->GetRange().GetStart() == newPos)
1633                    newPos --;
1634            }
1635        }
1636
1637        long newSelEnd = newPos;
1638
1639        bool extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
1640        if (!extendSel)
1641            SelectNone();
1642
1643        SetCaretPosition(newPos, caretLineStart);
1644        PositionCaret();
1645        SetDefaultStyleToCursorStyle();
1646
1647        return true;
1648    }
1649
1650    return false;
1651}
1652
1653/// Move to the end of the paragraph
1654bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
1655{
1656    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
1657    if (para)
1658    {
1659        long newPos = para->GetRange().GetEnd() - 1;
1660        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1661        if (!extendSel)
1662            SelectNone();
1663
1664        SetCaretPosition(newPos);
1665        PositionCaret();
1666        SetDefaultStyleToCursorStyle();
1667
1668        return true;
1669    }
1670
1671    return false;
1672}
1673
1674/// Move to the start of the paragraph
1675bool wxRichTextCtrl::MoveToParagraphStart(int flags)
1676{
1677    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
1678    if (para)
1679    {
1680        long newPos = para->GetRange().GetStart() - 1;
1681        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1682        if (!extendSel)
1683            SelectNone();
1684
1685        SetCaretPosition(newPos);
1686        PositionCaret();
1687        SetDefaultStyleToCursorStyle();
1688
1689        return true;
1690    }
1691
1692    return false;
1693}
1694
1695/// Move to the end of the line
1696bool wxRichTextCtrl::MoveToLineEnd(int flags)
1697{
1698    wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1699
1700    if (line)
1701    {
1702        wxRichTextRange lineRange = line->GetAbsoluteRange();
1703        long newPos = lineRange.GetEnd();
1704        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1705        if (!extendSel)
1706            SelectNone();
1707
1708        SetCaretPosition(newPos);
1709        PositionCaret();
1710        SetDefaultStyleToCursorStyle();
1711
1712        return true;
1713    }
1714
1715    return false;
1716}
1717
1718/// Move to the start of the line
1719bool wxRichTextCtrl::MoveToLineStart(int flags)
1720{
1721    wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1722    if (line)
1723    {
1724        wxRichTextRange lineRange = line->GetAbsoluteRange();
1725        long newPos = lineRange.GetStart()-1;
1726
1727        bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1728        if (!extendSel)
1729            SelectNone();
1730
1731        wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line);
1732
1733        SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
1734        PositionCaret();
1735        SetDefaultStyleToCursorStyle();
1736
1737        return true;
1738    }
1739
1740    return false;
1741}
1742
1743/// Move to the start of the buffer
1744bool wxRichTextCtrl::MoveHome(int flags)
1745{
1746    if (m_caretPosition != -1)
1747    {
1748        bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
1749        if (!extendSel)
1750            SelectNone();
1751
1752        SetCaretPosition(-1);
1753        PositionCaret();
1754        SetDefaultStyleToCursorStyle();
1755
1756        return true;
1757    }
1758    else
1759        return false;
1760}
1761
1762/// Move to the end of the buffer
1763bool wxRichTextCtrl::MoveEnd(int flags)
1764{
1765    long endPos = GetBuffer().GetRange().GetEnd()-1;
1766
1767    if (m_caretPosition != endPos)
1768    {
1769        bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
1770        if (!extendSel)
1771            SelectNone();
1772
1773        SetCaretPosition(endPos);
1774        PositionCaret();
1775        SetDefaultStyleToCursorStyle();
1776
1777        return true;
1778    }
1779    else
1780        return false;
1781}
1782
1783/// Move noPages pages up
1784bool wxRichTextCtrl::PageUp(int noPages, int flags)
1785{
1786    return PageDown(- noPages, flags);
1787}
1788
1789/// Move noPages pages down
1790bool wxRichTextCtrl::PageDown(int noPages, int flags)
1791{
1792    // Calculate which line occurs noPages * screen height further down.
1793    wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1794    if (line)
1795    {
1796        wxSize clientSize = GetClientSize();
1797        int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
1798
1799        wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY);
1800        if (newLine)
1801        {
1802            wxRichTextRange lineRange = newLine->GetAbsoluteRange();
1803            long pos = lineRange.GetStart()-1;
1804            if (pos != m_caretPosition)
1805            {
1806                wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine);
1807
1808                bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1809                if (!extendSel)
1810                    SelectNone();
1811
1812                SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
1813                PositionCaret();
1814                SetDefaultStyleToCursorStyle();
1815
1816                return true;
1817            }
1818        }
1819    }
1820
1821    return false;
1822}
1823
1824static bool wxRichTextCtrlIsWhitespace(const wxString& str)
1825{
1826    return str == wxT(" ") || str == wxT("\t");
1827}
1828
1829// Finds the caret position for the next word
1830long wxRichTextCtrl::FindNextWordPosition(int direction) const
1831{
1832    long endPos = GetBuffer().GetRange().GetEnd();
1833
1834    if (direction > 0)
1835    {
1836        long i = m_caretPosition+1+direction; // +1 for conversion to character pos
1837
1838        // First skip current text to space
1839        while (i < endPos && i > -1)
1840        {
1841            // i is in character, not caret positions
1842            wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1843            wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false);
1844            if (line && (i == line->GetAbsoluteRange().GetEnd()))
1845            {
1846                break;
1847            }
1848            else if (!wxRichTextCtrlIsWhitespace(text) && !text.empty())
1849                i += direction;
1850            else
1851            {
1852                break;
1853            }
1854        }
1855        while (i < endPos && i > -1)
1856        {
1857            // i is in character, not caret positions
1858            wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1859            wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false);
1860            if (line && (i == line->GetAbsoluteRange().GetEnd()))
1861                return wxMax(-1, i);
1862
1863            if (text.empty()) // End of paragraph, or maybe an image
1864                return wxMax(-1, i - 1);
1865            else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
1866                i += direction;
1867            else
1868            {
1869                // Convert to caret position
1870                return wxMax(-1, i - 1);
1871            }
1872        }
1873        if (i >= endPos)
1874            return endPos-1;
1875        return i-1;
1876    }
1877    else
1878    {
1879        long i = m_caretPosition;
1880
1881        // First skip white space
1882        while (i < endPos && i > -1)
1883        {
1884            // i is in character, not caret positions
1885            wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1886            wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false);
1887
1888            if (text.empty() || (line && (i == line->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image
1889                break;
1890            else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
1891                i += direction;
1892            else
1893                break;
1894        }
1895        // Next skip current text to space
1896        while (i < endPos && i > -1)
1897        {
1898            // i is in character, not caret positions
1899            wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1900            wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false);
1901            if (line && line->GetAbsoluteRange().GetStart() == i)
1902                return i-1;
1903
1904            if (!wxRichTextCtrlIsWhitespace(text) /* && !text.empty() */)
1905                i += direction;
1906            else
1907            {
1908                return i;
1909            }
1910        }
1911        if (i < -1)
1912            return -1;
1913        return i;
1914    }
1915}
1916
1917/// Move n words left
1918bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
1919{
1920    long pos = FindNextWordPosition(-1);
1921    if (pos != m_caretPosition)
1922    {
1923        wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
1924
1925        bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1926        if (!extendSel)
1927            SelectNone();
1928
1929        SetCaretPosition(pos, para->GetRange().GetStart() != pos);
1930        PositionCaret();
1931        SetDefaultStyleToCursorStyle();
1932
1933        return true;
1934    }
1935
1936    return false;
1937}
1938
1939/// Move n words right
1940bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
1941{
1942    long pos = FindNextWordPosition(1);
1943    if (pos != m_caretPosition)
1944    {
1945        wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
1946
1947        bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1948        if (!extendSel)
1949            SelectNone();
1950
1951        SetCaretPosition(pos, para->GetRange().GetStart() != pos);
1952        PositionCaret();
1953        SetDefaultStyleToCursorStyle();
1954
1955        return true;
1956    }
1957
1958    return false;
1959}
1960
1961/// Sizing
1962void wxRichTextCtrl::OnSize(wxSizeEvent& event)
1963{
1964    // Only do sizing optimization for large buffers
1965    if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold)
1966    {
1967        m_fullLayoutRequired = true;
1968        m_fullLayoutTime = wxGetLocalTimeMillis();
1969        m_fullLayoutSavedPosition = GetFirstVisiblePosition();
1970        LayoutContent(true /* onlyVisibleRect */);
1971    }
1972    else
1973        GetBuffer().Invalidate(wxRICHTEXT_ALL);
1974
1975#if wxRICHTEXT_BUFFERED_PAINTING
1976    RecreateBuffer();
1977#endif
1978
1979    event.Skip();
1980}
1981
1982/// Idle-time processing
1983void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
1984{
1985#if wxRICHTEXT_USE_OWN_CARET
1986    if (((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
1987    {
1988        ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(false);
1989        PositionCaret();
1990        GetCaret()->Show();
1991    }
1992#endif
1993
1994    const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
1995
1996    if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
1997    {
1998        m_fullLayoutRequired = false;
1999        m_fullLayoutTime = 0;
2000        GetBuffer().Invalidate(wxRICHTEXT_ALL);
2001        ShowPosition(m_fullLayoutSavedPosition);
2002        Refresh(false);
2003    }
2004
2005    if (m_caretPositionForDefaultStyle != -2)
2006    {
2007        // If the caret position has changed, no longer reflect the default style
2008        // in the UI.
2009        if (GetCaretPosition() != m_caretPositionForDefaultStyle)
2010            m_caretPositionForDefaultStyle = -2;
2011    }
2012
2013    event.Skip();
2014}
2015
2016/// Scrolling
2017void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
2018{
2019#if wxRICHTEXT_USE_OWN_CARET
2020    if (!((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
2021    {
2022        GetCaret()->Hide();
2023        ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate();
2024    }
2025#endif
2026
2027    event.Skip();
2028}
2029
2030/// Set up scrollbars, e.g. after a resize
2031void wxRichTextCtrl::SetupScrollbars(bool atTop)
2032{
2033    if (m_freezeCount)
2034        return;
2035
2036    if (GetBuffer().IsEmpty())
2037    {
2038        SetScrollbars(0, 0, 0, 0, 0, 0);
2039        return;
2040    }
2041
2042    // TODO: reimplement scrolling so we scroll by line, not by fixed number
2043    // of pixels. See e.g. wxVScrolledWindow for ideas.
2044    int pixelsPerUnit = 5;
2045    wxSize clientSize = GetClientSize();
2046
2047    int maxHeight = GetBuffer().GetCachedSize().y + GetBuffer().GetTopMargin();
2048
2049    // Round up so we have at least maxHeight pixels
2050    int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5);
2051
2052    int startX = 0, startY = 0;
2053    if (!atTop)
2054        GetViewStart(& startX, & startY);
2055
2056    int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
2057    int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5);
2058
2059    int newStartX = wxMin(maxPositionX, startX);
2060    int newStartY = wxMin(maxPositionY, startY);
2061
2062    int oldPPUX, oldPPUY;
2063    int oldStartX, oldStartY;
2064    int oldVirtualSizeX = 0, oldVirtualSizeY = 0;
2065    GetScrollPixelsPerUnit(& oldPPUX, & oldPPUY);
2066    GetViewStart(& oldStartX, & oldStartY);
2067    GetVirtualSize(& oldVirtualSizeX, & oldVirtualSizeY);
2068    if (oldPPUY > 0)
2069        oldVirtualSizeY /= oldPPUY;
2070
2071    if (oldPPUX == 0 && oldPPUY == pixelsPerUnit && oldVirtualSizeY == unitsY && oldStartX == newStartX && oldStartY == newStartY)
2072        return;
2073
2074    // Move to previous scroll position if
2075    // possible
2076    SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY);
2077}
2078
2079/// Paint the background
2080void wxRichTextCtrl::PaintBackground(wxDC& dc)
2081{
2082    wxColour backgroundColour = GetBackgroundColour();
2083    if (!backgroundColour.Ok())
2084        backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
2085
2086    // Clear the background
2087    dc.SetBrush(wxBrush(backgroundColour));
2088    dc.SetPen(*wxTRANSPARENT_PEN);
2089    wxRect windowRect(GetClientSize());
2090    windowRect.x -= 2; windowRect.y -= 2;
2091    windowRect.width += 4; windowRect.height += 4;
2092
2093    // We need to shift the rectangle to take into account
2094    // scrolling. Converting device to logical coordinates.
2095    CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
2096    dc.DrawRectangle(windowRect);
2097}
2098
2099#if wxRICHTEXT_BUFFERED_PAINTING
2100/// Recreate buffer bitmap if necessary
2101bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
2102{
2103    wxSize sz = size;
2104    if (sz == wxDefaultSize)
2105        sz = GetClientSize();
2106
2107    if (sz.x < 1 || sz.y < 1)
2108        return false;
2109
2110    if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
2111        m_bufferBitmap = wxBitmap(sz.x, sz.y);
2112    return m_bufferBitmap.Ok();
2113}
2114#endif
2115
2116// ----------------------------------------------------------------------------
2117// file IO functions
2118// ----------------------------------------------------------------------------
2119
2120bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType)
2121{
2122    bool success = GetBuffer().LoadFile(filename, fileType);
2123    if (success)
2124        m_filename = filename;
2125
2126    DiscardEdits();
2127    SetInsertionPoint(0);
2128    LayoutContent();
2129    PositionCaret();
2130    SetupScrollbars(true);
2131    Refresh(false);
2132    SendTextUpdatedEvent();
2133
2134    if (success)
2135        return true;
2136    else
2137    {
2138        wxLogError(_("File couldn't be loaded."));
2139
2140        return false;
2141    }
2142}
2143
2144bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
2145{
2146    if (GetBuffer().SaveFile(filename, fileType))
2147    {
2148        m_filename = filename;
2149
2150        DiscardEdits();
2151
2152        return true;
2153    }
2154
2155    wxLogError(_("The text couldn't be saved."));
2156
2157    return false;
2158}
2159
2160// ----------------------------------------------------------------------------
2161// wxRichTextCtrl specific functionality
2162// ----------------------------------------------------------------------------
2163
2164/// Add a new paragraph of text to the end of the buffer
2165wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
2166{
2167    wxRichTextRange range = GetBuffer().AddParagraph(text);
2168    LayoutContent();
2169    return range;
2170}
2171
2172/// Add an image
2173wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
2174{
2175    wxRichTextRange range = GetBuffer().AddImage(image);
2176    LayoutContent();
2177    return range;
2178}
2179
2180// ----------------------------------------------------------------------------
2181// selection and ranges
2182// ----------------------------------------------------------------------------
2183
2184void wxRichTextCtrl::SelectAll()
2185{
2186    SetSelection(-1, -1);
2187}
2188
2189/// Select none
2190void wxRichTextCtrl::SelectNone()
2191{
2192    if (!(GetSelectionRange() == wxRichTextRange(-2, -2)))
2193    {
2194        wxRichTextRange oldSelection = m_selectionRange;
2195
2196        m_selectionRange = wxRichTextRange(-2, -2);
2197
2198        wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
2199    }
2200    m_selectionAnchor = -2;
2201}
2202
2203static bool wxIsWordDelimiter(const wxString& text)
2204{
2205    return !text.IsEmpty() && !wxIsalnum(text[0]);
2206}
2207
2208/// Select the word at the given character position
2209bool wxRichTextCtrl::SelectWord(long position)
2210{
2211    if (position < 0 || position > GetBuffer().GetRange().GetEnd())
2212        return false;
2213
2214    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
2215    if (!para)
2216        return false;
2217
2218    if (position == para->GetRange().GetEnd())
2219        position --;
2220
2221    long positionStart = position;
2222    long positionEnd = position;
2223
2224    for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --)
2225    {
2226        wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionStart, positionStart));
2227        if (wxIsWordDelimiter(text))
2228        {
2229            positionStart ++;
2230            break;
2231        }
2232    }
2233    if (positionStart < para->GetRange().GetStart())
2234        positionStart = para->GetRange().GetStart();
2235
2236    for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++)
2237    {
2238        wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionEnd, positionEnd));
2239        if (wxIsWordDelimiter(text))
2240        {
2241            positionEnd --;
2242            break;
2243        }
2244    }
2245    if (positionEnd >= para->GetRange().GetEnd())
2246        positionEnd = para->GetRange().GetEnd();
2247
2248    if (positionEnd < positionStart)
2249        return false;
2250
2251    SetSelection(positionStart, positionEnd+1);
2252
2253    if (positionStart >= 0)
2254    {
2255        MoveCaret(positionStart-1, true);
2256        SetDefaultStyleToCursorStyle();
2257    }
2258
2259    return true;
2260}
2261
2262wxString wxRichTextCtrl::GetStringSelection() const
2263{
2264    long from, to;
2265    GetSelection(&from, &to);
2266
2267    return GetRange(from, to);
2268}
2269
2270// ----------------------------------------------------------------------------
2271// hit testing
2272// ----------------------------------------------------------------------------
2273
2274wxTextCtrlHitTestResult
2275wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
2276{
2277    // implement in terms of the other overload as the native ports typically
2278    // can get the position and not (x, y) pair directly (although wxUniv
2279    // directly gets x and y -- and so overrides this method as well)
2280    long pos;
2281    wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
2282
2283    if ( rc != wxTE_HT_UNKNOWN )
2284    {
2285        PositionToXY(pos, x, y);
2286    }
2287
2288    return rc;
2289}
2290
2291wxTextCtrlHitTestResult
2292wxRichTextCtrl::HitTest(const wxPoint& pt,
2293                        long * pos) const
2294{
2295    wxClientDC dc((wxRichTextCtrl*) this);
2296    ((wxRichTextCtrl*)this)->PrepareDC(dc);
2297
2298    // Buffer uses logical position (relative to start of buffer)
2299    // so convert
2300    wxPoint pt2 = GetLogicalPoint(pt);
2301
2302    int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt2, *pos);
2303
2304    if ((hit & wxRICHTEXT_HITTEST_BEFORE) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
2305        return wxTE_HT_BEFORE;
2306    else if ((hit & wxRICHTEXT_HITTEST_AFTER) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
2307        return wxTE_HT_BEYOND;
2308    else if (hit & (wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_AFTER))
2309        return wxTE_HT_ON_TEXT;
2310
2311    return wxTE_HT_UNKNOWN;
2312}
2313
2314// ----------------------------------------------------------------------------
2315// set/get the controls text
2316// ----------------------------------------------------------------------------
2317
2318wxString wxRichTextCtrl::GetValue() const
2319{
2320    return GetBuffer().GetText();
2321}
2322
2323wxString wxRichTextCtrl::GetRange(long from, long to) const
2324{
2325    // Public API for range is different from internals
2326    return GetBuffer().GetTextForRange(wxRichTextRange(from, to-1));
2327}
2328
2329void wxRichTextCtrl::DoSetValue(const wxString& value, int flags)
2330{
2331    // Don't call Clear here, since it always sends a text updated event
2332    m_buffer.ResetAndClearCommands();
2333    m_buffer.SetDirty(true);
2334    m_caretPosition = -1;
2335    m_caretPositionForDefaultStyle = -2;
2336    m_caretAtLineStart = false;
2337    m_selectionRange.SetRange(-2, -2);
2338
2339    Scroll(0,0);
2340
2341    if (m_freezeCount == 0)
2342    {
2343        LayoutContent();
2344        Refresh(false);
2345    }
2346
2347    if (!value.IsEmpty())
2348    {
2349        // Remove empty paragraph
2350        GetBuffer().Clear();
2351        DoWriteText(value, flags);
2352
2353        // for compatibility, don't move the cursor when doing SetValue()
2354        SetInsertionPoint(0);
2355    }
2356    else
2357    {
2358        // still send an event for consistency
2359        if (flags & SetValue_SendEvent)
2360            SendTextUpdatedEvent();
2361    }
2362    DiscardEdits();
2363}
2364
2365void wxRichTextCtrl::WriteText(const wxString& value)
2366{
2367    DoWriteText(value);
2368}
2369
2370void wxRichTextCtrl::DoWriteText(const wxString& value, int flags)
2371{
2372    wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
2373
2374    GetBuffer().InsertTextWithUndo(m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2375
2376    if ( flags & SetValue_SendEvent )
2377        SendTextUpdatedEvent();
2378}
2379
2380void wxRichTextCtrl::AppendText(const wxString& text)
2381{
2382    SetInsertionPointEnd();
2383
2384    WriteText(text);
2385}
2386
2387/// Write an image at the current insertion point
2388bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType)
2389{
2390    wxRichTextImageBlock imageBlock;
2391
2392    wxImage image2 = image;
2393    if (imageBlock.MakeImageBlock(image2, bitmapType))
2394        return WriteImage(imageBlock);
2395
2396    return false;
2397}
2398
2399bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType)
2400{
2401    wxRichTextImageBlock imageBlock;
2402
2403    wxImage image;
2404    if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
2405        return WriteImage(imageBlock);
2406
2407    return false;
2408}
2409
2410bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock)
2411{
2412    return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this);
2413}
2414
2415bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType)
2416{
2417    if (bitmap.Ok())
2418    {
2419        wxRichTextImageBlock imageBlock;
2420
2421        wxImage image = bitmap.ConvertToImage();
2422        if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType))
2423            return WriteImage(imageBlock);
2424    }
2425
2426    return false;
2427}
2428
2429/// Insert a newline (actually paragraph) at the current insertion point.
2430bool wxRichTextCtrl::Newline()
2431{
2432    return GetBuffer().InsertNewlineWithUndo(m_caretPosition+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2433}
2434
2435/// Insert a line break at the current insertion point.
2436bool wxRichTextCtrl::LineBreak()
2437{
2438    wxString text;
2439    text = wxRichTextLineBreakChar;
2440    return GetBuffer().InsertTextWithUndo(m_caretPosition+1, text, this);
2441}
2442
2443// ----------------------------------------------------------------------------
2444// Clipboard operations
2445// ----------------------------------------------------------------------------
2446
2447void wxRichTextCtrl::Copy()
2448{
2449    if (CanCopy())
2450    {
2451        wxRichTextRange range = GetInternalSelectionRange();
2452        GetBuffer().CopyToClipboard(range);
2453    }
2454}
2455
2456void wxRichTextCtrl::Cut()
2457{
2458    if (CanCut())
2459    {
2460        wxRichTextRange range = GetInternalSelectionRange();
2461        GetBuffer().CopyToClipboard(range);
2462
2463        DeleteSelectedContent();
2464        LayoutContent();
2465        Refresh(false);
2466    }
2467}
2468
2469void wxRichTextCtrl::Paste()
2470{
2471    if (CanPaste())
2472    {
2473        BeginBatchUndo(_("Paste"));
2474
2475        long newPos = m_caretPosition;
2476        DeleteSelectedContent(& newPos);
2477
2478        GetBuffer().PasteFromClipboard(newPos);
2479
2480        EndBatchUndo();
2481    }
2482}
2483
2484void wxRichTextCtrl::DeleteSelection()
2485{
2486    if (CanDeleteSelection())
2487    {
2488        DeleteSelectedContent();
2489    }
2490}
2491
2492bool wxRichTextCtrl::HasSelection() const
2493{
2494    return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2;
2495}
2496
2497bool wxRichTextCtrl::CanCopy() const
2498{
2499    // Can copy if there's a selection
2500    return HasSelection();
2501}
2502
2503bool wxRichTextCtrl::CanCut() const
2504{
2505    return HasSelection() && IsEditable();
2506}
2507
2508bool wxRichTextCtrl::CanPaste() const
2509{
2510    if ( !IsEditable() )
2511        return false;
2512
2513    return GetBuffer().CanPasteFromClipboard();
2514}
2515
2516bool wxRichTextCtrl::CanDeleteSelection() const
2517{
2518    return HasSelection() && IsEditable();
2519}
2520
2521
2522// ----------------------------------------------------------------------------
2523// Accessors
2524// ----------------------------------------------------------------------------
2525
2526void wxRichTextCtrl::SetContextMenu(wxMenu* menu)
2527{
2528    if (m_contextMenu && m_contextMenu != menu)
2529        delete m_contextMenu;
2530    m_contextMenu = menu;
2531}
2532
2533void wxRichTextCtrl::SetEditable(bool editable)
2534{
2535    m_editable = editable;
2536}
2537
2538void wxRichTextCtrl::SetInsertionPoint(long pos)
2539{
2540    SelectNone();
2541
2542    m_caretPosition = pos - 1;
2543
2544    PositionCaret();
2545
2546    SetDefaultStyleToCursorStyle();
2547}
2548
2549void wxRichTextCtrl::SetInsertionPointEnd()
2550{
2551    long pos = GetLastPosition();
2552    SetInsertionPoint(pos);
2553}
2554
2555long wxRichTextCtrl::GetInsertionPoint() const
2556{
2557    return m_caretPosition+1;
2558}
2559
2560wxTextPos wxRichTextCtrl::GetLastPosition() const
2561{
2562    return GetBuffer().GetRange().GetEnd();
2563}
2564
2565// If the return values from and to are the same, there is no
2566// selection.
2567void wxRichTextCtrl::GetSelection(long* from, long* to) const
2568{
2569    *from = m_selectionRange.GetStart();
2570    *to = m_selectionRange.GetEnd();
2571    if ((*to) != -1 && (*to) != -2)
2572        (*to) ++;
2573}
2574
2575bool wxRichTextCtrl::IsEditable() const
2576{
2577    return m_editable;
2578}
2579
2580// ----------------------------------------------------------------------------
2581// selection
2582// ----------------------------------------------------------------------------
2583
2584void wxRichTextCtrl::SetSelection(long from, long to)
2585{
2586    // if from and to are both -1, it means (in wxWidgets) that all text should
2587    // be selected.
2588    if ( (from == -1) && (to == -1) )
2589    {
2590        from = 0;
2591        to = GetLastPosition()+1;
2592    }
2593
2594    DoSetSelection(from, to);
2595}
2596
2597void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret))
2598{
2599    if (from == to)
2600    {
2601        SelectNone();
2602    }
2603    else
2604    {
2605        wxRichTextRange oldSelection = m_selectionRange;
2606        m_selectionAnchor = from-1;
2607        m_selectionRange.SetRange(from, to-1);
2608
2609        m_caretPosition = wxMax(-1, to-1);
2610
2611        wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
2612        PositionCaret();
2613    }
2614}
2615
2616// ----------------------------------------------------------------------------
2617// Editing
2618// ----------------------------------------------------------------------------
2619
2620void wxRichTextCtrl::Replace(long WXUNUSED(from), long WXUNUSED(to),
2621                             const wxString& value)
2622{
2623    BeginBatchUndo(_("Replace"));
2624
2625    DeleteSelectedContent();
2626
2627    DoWriteText(value, SetValue_SelectionOnly);
2628
2629    EndBatchUndo();
2630}
2631
2632void wxRichTextCtrl::Remove(long from, long to)
2633{
2634    SelectNone();
2635
2636    GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from, to-1), this);
2637
2638    LayoutContent();
2639    if (!IsFrozen())
2640        Refresh(false);
2641}
2642
2643bool wxRichTextCtrl::IsModified() const
2644{
2645    return m_buffer.IsModified();
2646}
2647
2648void wxRichTextCtrl::MarkDirty()
2649{
2650    m_buffer.Modify(true);
2651}
2652
2653void wxRichTextCtrl::DiscardEdits()
2654{
2655    m_caretPositionForDefaultStyle = -2;
2656    m_buffer.Modify(false);
2657    m_buffer.GetCommandProcessor()->ClearCommands();
2658}
2659
2660int wxRichTextCtrl::GetNumberOfLines() const
2661{
2662    return GetBuffer().GetParagraphCount();
2663}
2664
2665// ----------------------------------------------------------------------------
2666// Positions <-> coords
2667// ----------------------------------------------------------------------------
2668
2669long wxRichTextCtrl::XYToPosition(long x, long y) const
2670{
2671    return GetBuffer().XYToPosition(x, y);
2672}
2673
2674bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
2675{
2676    return GetBuffer().PositionToXY(pos, x, y);
2677}
2678
2679// ----------------------------------------------------------------------------
2680//
2681// ----------------------------------------------------------------------------
2682
2683void wxRichTextCtrl::ShowPosition(long pos)
2684{
2685    if (!IsPositionVisible(pos))
2686        ScrollIntoView(pos-1, WXK_DOWN);
2687}
2688
2689int wxRichTextCtrl::GetLineLength(long lineNo) const
2690{
2691    return GetBuffer().GetParagraphLength(lineNo);
2692}
2693
2694wxString wxRichTextCtrl::GetLineText(long lineNo) const
2695{
2696    return GetBuffer().GetParagraphText(lineNo);
2697}
2698
2699// ----------------------------------------------------------------------------
2700// Undo/redo
2701// ----------------------------------------------------------------------------
2702
2703void wxRichTextCtrl::Undo()
2704{
2705    if (CanUndo())
2706    {
2707        GetCommandProcessor()->Undo();
2708    }
2709}
2710
2711void wxRichTextCtrl::Redo()
2712{
2713    if (CanRedo())
2714    {
2715        GetCommandProcessor()->Redo();
2716    }
2717}
2718
2719bool wxRichTextCtrl::CanUndo() const
2720{
2721    return GetCommandProcessor()->CanUndo() && IsEditable();
2722}
2723
2724bool wxRichTextCtrl::CanRedo() const
2725{
2726    return GetCommandProcessor()->CanRedo() && IsEditable();
2727}
2728
2729// ----------------------------------------------------------------------------
2730// implementation details
2731// ----------------------------------------------------------------------------
2732
2733void wxRichTextCtrl::Command(wxCommandEvent& event)
2734{
2735    SetValue(event.GetString());
2736    GetEventHandler()->ProcessEvent(event);
2737}
2738
2739void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
2740{
2741    // By default, load the first file into the text window.
2742    if (event.GetNumberOfFiles() > 0)
2743    {
2744        LoadFile(event.GetFiles()[0]);
2745    }
2746}
2747
2748wxSize wxRichTextCtrl::DoGetBestSize() const
2749{
2750    return wxSize(10, 10);
2751}
2752
2753// ----------------------------------------------------------------------------
2754// standard handlers for standard edit menu events
2755// ----------------------------------------------------------------------------
2756
2757void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2758{
2759    Cut();
2760}
2761
2762void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
2763{
2764    DeleteSelection();
2765}
2766
2767void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2768{
2769    Copy();
2770}
2771
2772void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2773{
2774    Paste();
2775}
2776
2777void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2778{
2779    Undo();
2780}
2781
2782void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2783{
2784    Redo();
2785}
2786
2787void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2788{
2789    event.Enable( CanCut() );
2790}
2791
2792void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2793{
2794    event.Enable( CanCopy() );
2795}
2796
2797void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
2798{
2799    event.Enable( CanDeleteSelection() );
2800}
2801
2802void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2803{
2804    event.Enable( CanPaste() );
2805}
2806
2807void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2808{
2809    event.Enable( CanUndo() );
2810    event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
2811}
2812
2813void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2814{
2815    event.Enable( CanRedo() );
2816    event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
2817}
2818
2819void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2820{
2821    if (GetLastPosition() > 0)
2822        SelectAll();
2823}
2824
2825void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
2826{
2827    event.Enable(GetLastPosition() > 0);
2828}
2829
2830void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& event)
2831{
2832    if (event.GetEventObject() != this)
2833    {
2834        event.Skip();
2835        return;
2836    }
2837
2838    if (m_contextMenu)
2839        PopupMenu(m_contextMenu);
2840    return;
2841}
2842
2843bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
2844{
2845    return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style);
2846}
2847
2848bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
2849{
2850    return GetBuffer().SetStyle(wxRichTextRange(start, end-1), wxTextAttrEx(style));
2851}
2852
2853bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
2854{
2855    return GetBuffer().SetStyle(range.ToInternal(), style);
2856}
2857
2858// extended style setting operation with flags including:
2859// wxRICHTEXT_SETSTYLE_WITH_UNDO, wxRICHTEXT_SETSTYLE_OPTIMIZE, wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY.
2860// see richtextbuffer.h for more details.
2861bool wxRichTextCtrl::SetStyleEx(long start, long end, const wxTextAttrEx& style, int flags)
2862{
2863    return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style, flags);
2864}
2865
2866bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxTextAttrEx& style, int flags)
2867{
2868    return GetBuffer().SetStyle(range.ToInternal(), style, flags);
2869}
2870
2871bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
2872{
2873    return GetBuffer().SetStyle(range.ToInternal(), style, flags);
2874}
2875
2876bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style)
2877{
2878    return GetBuffer().SetDefaultStyle(style);
2879}
2880
2881bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style)
2882{
2883    return GetBuffer().SetDefaultStyle(wxTextAttrEx(style));
2884}
2885
2886const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const
2887{
2888    return GetBuffer().GetDefaultStyle();
2889}
2890
2891const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const
2892{
2893    return GetBuffer().GetDefaultStyle();
2894}
2895
2896bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style)
2897{
2898    wxTextAttrEx attr(style);
2899    if (GetBuffer().GetStyle(position, attr))
2900    {
2901        style = attr;
2902        return true;
2903    }
2904    else
2905        return false;
2906}
2907
2908bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style)
2909{
2910    return GetBuffer().GetStyle(position, style);
2911}
2912
2913bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style)
2914{
2915    return GetBuffer().GetStyle(position, style);
2916}
2917
2918// get the common set of styles for the range
2919bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
2920{
2921    wxTextAttrEx styleEx;
2922    if (GetBuffer().GetStyleForRange(range.ToInternal(), styleEx))
2923    {
2924        style = styleEx;
2925        return true;
2926    }
2927    else
2928        return false;
2929}
2930
2931bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxTextAttrEx& style)
2932{
2933    return GetBuffer().GetStyleForRange(range.ToInternal(), style);
2934}
2935
2936/// Get the content (uncombined) attributes for this position.
2937
2938bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttr& style)
2939{
2940    wxTextAttrEx attr(style);
2941    if (GetBuffer().GetUncombinedStyle(position, attr))
2942    {
2943        style = attr;
2944        return true;
2945    }
2946    else
2947        return false;
2948}
2949
2950bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttrEx& style)
2951{
2952    return GetBuffer().GetUncombinedStyle(position, style);
2953}
2954
2955bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style)
2956{
2957    return GetBuffer().GetUncombinedStyle(position, style);
2958}
2959
2960/// Set font, and also the buffer attributes
2961bool wxRichTextCtrl::SetFont(const wxFont& font)
2962{
2963    wxControl::SetFont(font);
2964
2965    wxTextAttrEx attr = GetBuffer().GetAttributes();
2966    attr.SetFont(font);
2967    GetBuffer().SetBasicStyle(attr);
2968
2969    GetBuffer().Invalidate(wxRICHTEXT_ALL);
2970    Refresh(false);
2971
2972    return true;
2973}
2974
2975/// Transform logical to physical
2976wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
2977{
2978    wxPoint pt;
2979    CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
2980
2981    return pt;
2982}
2983
2984/// Transform physical to logical
2985wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
2986{
2987    wxPoint pt;
2988    CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
2989
2990    return pt;
2991}
2992
2993/// Position the caret
2994void wxRichTextCtrl::PositionCaret()
2995{
2996    if (!GetCaret())
2997        return;
2998
2999    wxRect caretRect;
3000    if (GetCaretPositionForIndex(GetCaretPosition(), caretRect))
3001    {
3002        wxPoint newPt = caretRect.GetPosition();
3003        wxSize newSz = caretRect.GetSize();
3004        wxPoint pt = GetPhysicalPoint(newPt);
3005        if (GetCaret()->GetPosition() != pt || GetCaret()->GetSize() != newSz)
3006        {
3007            //wxLogDebug(wxT("Positioning caret %d, %d"), pt.x, pt.y);
3008            GetCaret()->Hide();
3009            if (GetCaret()->GetSize() != newSz)
3010                GetCaret()->SetSize(newSz);
3011
3012            int halfSize = newSz.y/2;
3013            // If the caret is beyond the margin, hide it by moving it out of the way
3014            if (((pt.y + halfSize) < GetBuffer().GetTopMargin()) || ((pt.y + halfSize) > (GetClientSize().y - GetBuffer().GetBottomMargin())))
3015                pt.y = -200;
3016
3017            GetCaret()->Move(pt);
3018            GetCaret()->Show();
3019        }
3020    }
3021}
3022
3023/// Get the caret height and position for the given character position
3024bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect)
3025{
3026    wxClientDC dc(this);
3027    dc.SetFont(GetFont());
3028
3029    PrepareDC(dc);
3030
3031    wxPoint pt;
3032    int height = 0;
3033
3034    if (GetBuffer().FindPosition(dc, position, pt, & height, m_caretAtLineStart))
3035    {
3036        // Caret height can't be zero
3037        if (height == 0)
3038            height = dc.GetCharHeight();
3039
3040        rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
3041        return true;
3042    }
3043
3044    return false;
3045}
3046
3047/// Gets the line for the visible caret position. If the caret is
3048/// shown at the very end of the line, it means the next character is actually
3049/// on the following line. So let's get the line we're expecting to find
3050/// if this is the case.
3051wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
3052{
3053    wxRichTextLine* line = GetBuffer().GetLineAtPosition(caretPosition, true);
3054    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true);
3055    if (line)
3056    {
3057        wxRichTextRange lineRange = line->GetAbsoluteRange();
3058        if (caretPosition == lineRange.GetStart()-1 &&
3059            (para->GetRange().GetStart() != lineRange.GetStart()))
3060        {
3061            if (!m_caretAtLineStart)
3062                line = GetBuffer().GetLineAtPosition(caretPosition-1, true);
3063        }
3064    }
3065    return line;
3066}
3067
3068
3069/// Move the caret to the given character position
3070bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart)
3071{
3072    if (GetBuffer().GetDirty())
3073        LayoutContent();
3074
3075    if (pos <= GetBuffer().GetRange().GetEnd())
3076    {
3077        SetCaretPosition(pos, showAtLineStart);
3078
3079        PositionCaret();
3080
3081        return true;
3082    }
3083    else
3084        return false;
3085}
3086
3087/// Layout the buffer: which we must do before certain operations, such as
3088/// setting the caret position.
3089bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
3090{
3091    if (GetBuffer().GetDirty() || onlyVisibleRect)
3092    {
3093        wxRect availableSpace(GetClientSize());
3094        if (availableSpace.width == 0)
3095            availableSpace.width = 10;
3096        if (availableSpace.height == 0)
3097            availableSpace.height = 10;
3098
3099        int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
3100        if (onlyVisibleRect)
3101        {
3102            flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
3103            availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
3104        }
3105
3106        wxClientDC dc(this);
3107        dc.SetFont(GetFont());
3108
3109        PrepareDC(dc);
3110
3111        GetBuffer().Defragment();
3112        GetBuffer().UpdateRanges();     // If items were deleted, ranges need recalculation
3113        GetBuffer().Layout(dc, availableSpace, flags);
3114        GetBuffer().SetDirty(false);
3115
3116        if (!IsFrozen())
3117            SetupScrollbars();
3118    }
3119
3120    return true;
3121}
3122
3123/// Is all of the selection bold?
3124bool wxRichTextCtrl::IsSelectionBold()
3125{
3126    if (HasSelection())
3127    {
3128        wxRichTextAttr attr;
3129        wxRichTextRange range = GetSelectionRange();
3130        attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3131        attr.SetFontWeight(wxBOLD);
3132
3133        return HasCharacterAttributes(range, attr);
3134    }
3135    else
3136    {
3137        // If no selection, then we need to combine current style with default style
3138        // to see what the effect would be if we started typing.
3139        wxRichTextAttr attr;
3140        attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3141
3142        long pos = GetAdjustedCaretPosition(GetCaretPosition());
3143        if (GetStyle(pos, attr))
3144        {
3145            if (IsDefaultStyleShowing())
3146                wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3147            return attr.GetFontWeight() == wxBOLD;
3148        }
3149    }
3150    return false;
3151}
3152
3153/// Is all of the selection italics?
3154bool wxRichTextCtrl::IsSelectionItalics()
3155{
3156    if (HasSelection())
3157    {
3158        wxRichTextRange range = GetSelectionRange();
3159        wxRichTextAttr attr;
3160        attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3161        attr.SetFontStyle(wxITALIC);
3162
3163        return HasCharacterAttributes(range, attr);
3164    }
3165    else
3166    {
3167        // If no selection, then we need to combine current style with default style
3168        // to see what the effect would be if we started typing.
3169        wxRichTextAttr attr;
3170        attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3171
3172        long pos = GetAdjustedCaretPosition(GetCaretPosition());
3173        if (GetStyle(pos, attr))
3174        {
3175            if (IsDefaultStyleShowing())
3176                wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3177            return attr.GetFontStyle() == wxITALIC;
3178        }
3179    }
3180    return false;
3181}
3182
3183/// Is all of the selection underlined?
3184bool wxRichTextCtrl::IsSelectionUnderlined()
3185{
3186    if (HasSelection())
3187    {
3188        wxRichTextRange range = GetSelectionRange();
3189        wxRichTextAttr attr;
3190        attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3191        attr.SetFontUnderlined(true);
3192
3193        return HasCharacterAttributes(range, attr);
3194    }
3195    else
3196    {
3197        // If no selection, then we need to combine current style with default style
3198        // to see what the effect would be if we started typing.
3199        wxRichTextAttr attr;
3200        attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3201        long pos = GetAdjustedCaretPosition(GetCaretPosition());
3202
3203        if (GetStyle(pos, attr))
3204        {
3205            if (IsDefaultStyleShowing())
3206                wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3207            return attr.GetFontUnderlined();
3208        }
3209    }
3210    return false;
3211}
3212
3213/// Apply bold to the selection
3214bool wxRichTextCtrl::ApplyBoldToSelection()
3215{
3216    wxRichTextAttr attr;
3217    attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3218    attr.SetFontWeight(IsSelectionBold() ? wxNORMAL : wxBOLD);
3219
3220    if (HasSelection())
3221        return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3222    else
3223    {
3224        wxRichTextAttr current = GetDefaultStyleEx();
3225        current.Apply(attr);
3226        SetAndShowDefaultStyle(current);
3227    }
3228    return true;
3229}
3230
3231/// Apply italic to the selection
3232bool wxRichTextCtrl::ApplyItalicToSelection()
3233{
3234    wxRichTextAttr attr;
3235    attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3236    attr.SetFontStyle(IsSelectionItalics() ? wxNORMAL : wxITALIC);
3237
3238    if (HasSelection())
3239        return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3240    else
3241    {
3242        wxRichTextAttr current = GetDefaultStyleEx();
3243        current.Apply(attr);
3244        SetAndShowDefaultStyle(current);
3245    }
3246    return true;
3247}
3248
3249/// Apply underline to the selection
3250bool wxRichTextCtrl::ApplyUnderlineToSelection()
3251{
3252    wxRichTextAttr attr;
3253    attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3254    attr.SetFontUnderlined(!IsSelectionUnderlined());
3255
3256    if (HasSelection())
3257        return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3258    else
3259    {
3260        wxRichTextAttr current = GetDefaultStyleEx();
3261        current.Apply(attr);
3262        SetAndShowDefaultStyle(current);
3263    }
3264    return true;
3265}
3266
3267/// Is all of the selection aligned according to the specified flag?
3268bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment)
3269{
3270    wxRichTextRange range;
3271    if (HasSelection())
3272        range = GetSelectionRange();
3273    else
3274        range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+2);
3275
3276    wxRichTextAttr attr;
3277    attr.SetAlignment(alignment);
3278
3279    return HasParagraphAttributes(range, attr);
3280}
3281
3282/// Apply alignment to the selection
3283bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
3284{
3285    wxRichTextAttr attr;
3286    attr.SetAlignment(alignment);
3287    if (HasSelection())
3288        return SetStyle(GetSelectionRange(), attr);
3289    else
3290    {
3291        wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
3292        if (para)
3293            return SetStyleEx(para->GetRange().FromInternal(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY);
3294    }
3295    return true;
3296}
3297
3298/// Apply a named style to the selection
3299bool wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def)
3300{
3301    // Flags are defined within each definition, so only certain
3302    // attributes are applied.
3303    wxRichTextAttr attr(GetStyleSheet() ? def->GetStyleMergedWithBase(GetStyleSheet()) : def->GetStyle());
3304
3305    int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_RESET;
3306
3307    if (def->IsKindOf(CLASSINFO(wxRichTextListStyleDefinition)))
3308    {
3309        flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
3310
3311        wxRichTextRange range;
3312
3313        if (HasSelection())
3314            range = GetSelectionRange();
3315        else
3316        {
3317            long pos = GetAdjustedCaretPosition(GetCaretPosition());
3318            range = wxRichTextRange(pos, pos+1);
3319        }
3320
3321        return SetListStyle(range, (wxRichTextListStyleDefinition*) def, flags);
3322    }
3323
3324    bool isPara = false;
3325
3326    // Make sure the attr has the style name
3327    if (def->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition)))
3328    {
3329        isPara = true;
3330        attr.SetParagraphStyleName(def->GetName());
3331
3332        // If applying a paragraph style, we only want the paragraph nodes to adopt these
3333        // attributes, and not the leaf nodes. This will allow the content (e.g. text)
3334        // to change its style independently.
3335        flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
3336    }
3337    else
3338        attr.SetCharacterStyleName(def->GetName());
3339
3340    if (HasSelection())
3341        return SetStyleEx(GetSelectionRange(), attr, flags);
3342    else
3343    {
3344        wxRichTextAttr current = GetDefaultStyleEx();
3345        wxRichTextAttr defaultStyle(attr);
3346        if (isPara)
3347        {
3348            // Don't apply extra character styles since they are already implied
3349            // in the paragraph style
3350            defaultStyle.SetFlags(defaultStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER);
3351        }
3352        current.Apply(defaultStyle);
3353        SetAndShowDefaultStyle(current);
3354
3355        // If it's a paragraph style, we want to apply the style to the
3356        // current paragraph even if we didn't select any text.
3357        if (isPara)
3358        {
3359            long pos = GetAdjustedCaretPosition(GetCaretPosition());
3360            wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos);
3361            if (para)
3362            {
3363                return SetStyleEx(para->GetRange().FromInternal(), attr, flags);
3364            }
3365        }
3366        return true;
3367    }
3368}
3369
3370/// Apply the style sheet to the buffer, for example if the styles have changed.
3371bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3372{
3373    if (!styleSheet)
3374        styleSheet = GetBuffer().GetStyleSheet();
3375    if (!styleSheet)
3376        return false;
3377
3378    if (GetBuffer().ApplyStyleSheet(styleSheet))
3379    {
3380        GetBuffer().Invalidate(wxRICHTEXT_ALL);
3381        Refresh(false);
3382        return true;
3383    }
3384    else
3385        return false;
3386}
3387
3388/// Sets the default style to the style under the cursor
3389bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
3390{
3391    wxTextAttrEx attr;
3392    attr.SetFlags(wxTEXT_ATTR_CHARACTER);
3393
3394    // If at the start of a paragraph, use the next position.
3395    long pos = GetAdjustedCaretPosition(GetCaretPosition());
3396
3397    if (GetUncombinedStyle(pos, attr))
3398    {
3399        SetDefaultStyle(attr);
3400        return true;
3401    }
3402
3403    return false;
3404}
3405
3406/// Returns the first visible position in the current view
3407long wxRichTextCtrl::GetFirstVisiblePosition() const
3408{
3409    wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y);
3410    if (line)
3411        return line->GetAbsoluteRange().GetStart();
3412    else
3413        return 0;
3414}
3415
3416/// Get the first visible point in the window
3417wxPoint wxRichTextCtrl::GetFirstVisiblePoint() const
3418{
3419    int ppuX, ppuY;
3420    int startXUnits, startYUnits;
3421
3422    GetScrollPixelsPerUnit(& ppuX, & ppuY);
3423    GetViewStart(& startXUnits, & startYUnits);
3424
3425    return wxPoint(startXUnits * ppuX, startYUnits * ppuY);
3426}
3427
3428/// The adjusted caret position is the character position adjusted to take
3429/// into account whether we're at the start of a paragraph, in which case
3430/// style information should be taken from the next position, not current one.
3431long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const
3432{
3433    wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPos+1);
3434
3435    if (para && (caretPos+1 == para->GetRange().GetStart()))
3436        caretPos ++;
3437    return caretPos;
3438}
3439
3440/// Get/set the selection range in character positions. -1, -1 means no selection.
3441/// The range is in API convention, i.e. a single character selection is denoted
3442/// by (n, n+1)
3443wxRichTextRange wxRichTextCtrl::GetSelectionRange() const
3444{
3445    wxRichTextRange range = GetInternalSelectionRange();
3446    if (range != wxRichTextRange(-2,-2) && range != wxRichTextRange(-1,-1))
3447        range.SetEnd(range.GetEnd() + 1);
3448    return range;
3449}
3450
3451void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range)
3452{
3453    SetSelection(range.GetStart(), range.GetEnd());
3454}
3455
3456/// Set list style
3457bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3458{
3459    return GetBuffer().SetListStyle(range.ToInternal(), def, flags, startFrom, specifiedLevel);
3460}
3461
3462bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
3463{
3464    return GetBuffer().SetListStyle(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
3465}
3466
3467/// Clear list for given range
3468bool wxRichTextCtrl::ClearListStyle(const wxRichTextRange& range, int flags)
3469{
3470    return GetBuffer().ClearListStyle(range.ToInternal(), flags);
3471}
3472
3473/// Number/renumber any list elements in the given range
3474bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3475{
3476    return GetBuffer().NumberList(range.ToInternal(), def, flags, startFrom, specifiedLevel);
3477}
3478
3479bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
3480{
3481    return GetBuffer().NumberList(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
3482}
3483
3484/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
3485bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
3486{
3487    return GetBuffer().PromoteList(promoteBy, range.ToInternal(), def, flags, specifiedLevel);
3488}
3489
3490bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
3491{
3492    return GetBuffer().PromoteList(promoteBy, range.ToInternal(), defName, flags, specifiedLevel);
3493}
3494
3495/// Deletes the content in the given range
3496bool wxRichTextCtrl::Delete(const wxRichTextRange& range)
3497{
3498    return GetBuffer().DeleteRangeWithUndo(range.ToInternal(), this);
3499}
3500
3501const wxArrayString& wxRichTextCtrl::GetAvailableFontNames()
3502{
3503    if (sm_availableFontNames.GetCount() == 0)
3504    {
3505        sm_availableFontNames = wxFontEnumerator::GetFacenames();
3506        sm_availableFontNames.Sort();
3507    }
3508    return sm_availableFontNames;
3509}
3510
3511void wxRichTextCtrl::ClearAvailableFontNames()
3512{
3513    sm_availableFontNames.Clear();
3514}
3515
3516// Refresh the area affected by a selection change
3517bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection)
3518{
3519    // Calculate the refresh rectangle - just the affected lines
3520    long firstPos, lastPos;
3521    if (oldSelection.GetStart() == -2 && newSelection.GetStart() != -2)
3522    {
3523        firstPos = newSelection.GetStart();
3524        lastPos = newSelection.GetEnd();
3525    }
3526    else if (oldSelection.GetStart() != -2 && newSelection.GetStart() == -2)
3527    {
3528        firstPos = oldSelection.GetStart();
3529        lastPos = oldSelection.GetEnd();
3530    }
3531    else if (oldSelection.GetStart() == -2 && newSelection.GetStart() == -2)
3532    {
3533        return false;
3534    }
3535    else
3536    {
3537        firstPos = wxMin(oldSelection.GetStart(), newSelection.GetStart());
3538        lastPos = wxMax(oldSelection.GetEnd(), newSelection.GetEnd());
3539    }
3540
3541    wxRichTextLine* firstLine = ctrl.GetBuffer().GetLineAtPosition(firstPos);
3542    wxRichTextLine* lastLine = ctrl.GetBuffer().GetLineAtPosition(lastPos);
3543
3544    if (firstLine && lastLine)
3545    {
3546        wxSize clientSize = ctrl.GetClientSize();
3547        wxPoint pt1 = ctrl.GetPhysicalPoint(firstLine->GetAbsolutePosition());
3548        wxPoint pt2 = ctrl.GetPhysicalPoint(lastLine->GetAbsolutePosition()) + wxPoint(0, lastLine->GetSize().y);
3549
3550        pt1.x = 0;
3551        pt1.y = wxMax(0, pt1.y);
3552        pt2.x = 0;
3553        pt2.y = wxMin(clientSize.y, pt2.y);
3554
3555        wxRect rect(pt1, wxSize(clientSize.x, pt2.y - pt1.y));
3556        ctrl.RefreshRect(rect, false);
3557    }
3558    else
3559        ctrl.Refresh(false);
3560
3561    return true;
3562}
3563
3564#if wxRICHTEXT_USE_OWN_CARET
3565
3566// ----------------------------------------------------------------------------
3567// initialization and destruction
3568// ----------------------------------------------------------------------------
3569
3570void wxRichTextCaret::Init()
3571{
3572    m_hasFocus = true;
3573
3574    m_xOld =
3575    m_yOld = -1;
3576    m_richTextCtrl = NULL;
3577    m_needsUpdate = false;
3578    m_flashOn = true;
3579}
3580
3581wxRichTextCaret::~wxRichTextCaret()
3582{
3583    if (m_timer.IsRunning())
3584        m_timer.Stop();
3585}
3586
3587// ----------------------------------------------------------------------------
3588// showing/hiding/moving the caret (base class interface)
3589// ----------------------------------------------------------------------------
3590
3591void wxRichTextCaret::DoShow()
3592{
3593    m_flashOn = true;
3594
3595    if (!m_timer.IsRunning())
3596        m_timer.Start(GetBlinkTime());
3597
3598    Refresh();
3599}
3600
3601void wxRichTextCaret::DoHide()
3602{
3603    if (m_timer.IsRunning())
3604        m_timer.Stop();
3605
3606    Refresh();
3607}
3608
3609void wxRichTextCaret::DoMove()
3610{
3611    if (IsVisible())
3612    {
3613        Refresh();
3614
3615        if (m_xOld != -1 && m_yOld != -1)
3616        {
3617            if (m_richTextCtrl)
3618            {
3619                wxRect rect(GetPosition(), GetSize());
3620                m_richTextCtrl->RefreshRect(rect, false);
3621            }
3622        }
3623    }
3624
3625    m_xOld = m_x;
3626    m_yOld = m_y;
3627}
3628
3629void wxRichTextCaret::DoSize()
3630{
3631    int countVisible = m_countVisible;
3632    if (countVisible > 0)
3633    {
3634        m_countVisible = 0;
3635        DoHide();
3636    }
3637
3638    if (countVisible > 0)
3639    {
3640        m_countVisible = countVisible;
3641        DoShow();
3642    }
3643}
3644
3645// ----------------------------------------------------------------------------
3646// handling the focus
3647// ----------------------------------------------------------------------------
3648
3649void wxRichTextCaret::OnSetFocus()
3650{
3651    m_hasFocus = true;
3652
3653    if ( IsVisible() )
3654        Refresh();
3655}
3656
3657void wxRichTextCaret::OnKillFocus()
3658{
3659    m_hasFocus = false;
3660}
3661
3662// ----------------------------------------------------------------------------
3663// drawing the caret
3664// ----------------------------------------------------------------------------
3665
3666void wxRichTextCaret::Refresh()
3667{
3668    if (m_richTextCtrl)
3669    {
3670        wxRect rect(GetPosition(), GetSize());
3671        m_richTextCtrl->RefreshRect(rect, false);
3672    }
3673}
3674
3675void wxRichTextCaret::DoDraw(wxDC *dc)
3676{
3677    dc->SetPen( *wxBLACK_PEN );
3678
3679    dc->SetBrush(*(m_hasFocus ? wxBLACK_BRUSH : wxTRANSPARENT_BRUSH));
3680    dc->SetPen(*wxBLACK_PEN);
3681
3682    wxPoint pt(m_x, m_y);
3683
3684    if (m_richTextCtrl)
3685    {
3686        pt = m_richTextCtrl->GetLogicalPoint(pt);
3687    }
3688    if (IsVisible() && m_flashOn)
3689        dc->DrawRectangle(pt.x, pt.y, m_width, m_height);
3690}
3691
3692void wxRichTextCaret::Notify()
3693{
3694    m_flashOn = !m_flashOn;
3695    Refresh();
3696}
3697
3698void wxRichTextCaretTimer::Notify()
3699{
3700    m_caret->Notify();
3701}
3702
3703#endif
3704    // wxRICHTEXT_USE_OWN_CARET
3705#endif
3706    // wxUSE_RICHTEXT
3707