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
42 bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection);
43 
44 WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
45 
46 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
47 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK)
48 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK)
49 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK)
50 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN)
51 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CHARACTER)
52 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_DELETE)
53 
54 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING)
55 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED)
56 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGING)
57 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGED)
58 
59 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED)
60 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED)
61 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED)
62 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_SELECTION_CHANGED)
63 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET)
64 
65 #if wxRICHTEXT_USE_OWN_CARET
66 
67 class wxRichTextCaret;
68 class wxRichTextCaretTimer: public wxTimer
69 {
70   public:
wxRichTextCaretTimer(wxRichTextCaret * caret)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 
87 class wxRichTextCaret: public wxCaret
88 {
89 public:
90     // ctors
91     // -----
92         // default - use Create()
wxRichTextCaret()93     wxRichTextCaret(): m_timer(this)  { Init(); }
94         // creates a block caret associated with the given window
wxRichTextCaret(wxRichTextCtrl * window,int width,int height)95     wxRichTextCaret(wxRichTextCtrl *window, int width, int height)
96         : wxCaret(window, width, height), m_timer(this) { Init(); m_richTextCtrl = window; }
wxRichTextCaret(wxRichTextCtrl * window,const wxSize & size)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
GetVisibleCount() const113     int GetVisibleCount() const { return m_countVisible; }
114 
115     // delay repositioning
GetNeedsUpdate() const116     bool GetNeedsUpdate() const { return m_needsUpdate; }
SetNeedsUpdate(bool needsUpdate=true)117     void SetNeedsUpdate(bool needsUpdate = true ) { m_needsUpdate = needsUpdate; }
118 
119     void Notify();
120 
121 protected:
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 
130 private:
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 
143 IMPLEMENT_CLASS( wxRichTextCtrl, wxTextCtrlBase )
144 
145 IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent )
146 
147 BEGIN_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)
186 END_EVENT_TABLE()
187 
188 /*!
189  * wxRichTextCtrl
190  */
191 
192 wxArrayString wxRichTextCtrl::sm_availableFontNames;
193 
wxRichTextCtrl()194 wxRichTextCtrl::wxRichTextCtrl()
195               : wxScrollHelper(this)
196 {
197     Init();
198 }
199 
wxRichTextCtrl(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)200 wxRichTextCtrl::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
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)215 bool 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 
~wxRichTextCtrl()314 wxRichTextCtrl::~wxRichTextCtrl()
315 {
316     GetBuffer().RemoveEventHandler(this);
317 
318     delete m_contextMenu;
319 }
320 
321 /// Member initialisation
Init()322 void 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
Freeze()341 void wxRichTextCtrl::Freeze()
342 {
343     m_freezeCount ++;
344 }
345 
346 /// Call Thaw to refresh
Thaw()347 void 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
Clear()363 void 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
OnPaint(wxPaintEvent & WXUNUSED (event))383 void 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
OnEraseBackground(wxEraseEvent & WXUNUSED (event))450 void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
451 {
452 }
453 
OnSetFocus(wxFocusEvent & WXUNUSED (event))454 void 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 
OnKillFocus(wxFocusEvent & WXUNUSED (event))473 void 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 
OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED (event))487 void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
488 {
489     m_dragging = false;
490 }
491 
492 /// Left-click
OnLeftClick(wxMouseEvent & event)493 void 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
OnLeftUp(wxMouseEvent & event)545 void 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
OnMoveMouse(wxMouseEvent & event)596 void 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
OnRightClick(wxMouseEvent & event)664 void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
665 {
666     SetFocus();
667     event.Skip();
668 }
669 
670 /// Left-double-click
OnLeftDClick(wxMouseEvent & event)671 void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
672 {
673     SelectWord(GetCaretPosition()+1);
674     event.Skip();
675 }
676 
677 /// Middle-click
OnMiddleClick(wxMouseEvent & event)678 void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
679 {
680     event.Skip();
681 }
682 
683 /// Key press
OnChar(wxKeyEvent & event)684 void 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.
DeleteSelectedContent(long * newPos)1083 bool 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 
1111 Left:       left one character
1112 Right:      right one character
1113 Up:         up one line
1114 Down:       down one line
1115 Ctrl-Left:  left one word
1116 Ctrl-Right: right one word
1117 Ctrl-Up:    previous paragraph start
1118 Ctrl-Down:  next start of paragraph
1119 Home:       start of line
1120 End:        end of line
1121 Ctrl-Home:  start of document
1122 Ctrl-End:   end of document
1123 Page-Up:    Up a screen
1124 Page-Down:  Down a screen
1125 
1126 Maybe:
1127 
1128 Ctrl-Alt-PgUp: Start of window
1129 Ctrl-Alt-PgDn: End of window
1130 F8:         Start selection mode
1131 Esc:        End selection mode
1132 
1133 Adding Shift does the above but starts/extends selection.
1134 
1135 
1136  */
1137 
KeyboardNavigate(int keyCode,int flags)1138 bool 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.
ExtendSelection(long oldPos,long newPos,int flags)1203 bool 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.
ScrollIntoView(long position,int keyCode)1249 bool 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?
IsPositionVisible(long pos) const1381 bool 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 
SetCaretPosition(long position,bool showAtLineStart)1403 void 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.
MoveCaretForward(long oldPosition)1412 void 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.
MoveCaretBack(long oldPosition)1457 void 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
MoveRight(int noPositions,int flags)1505 bool 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
MoveLeft(int noPositions,int flags)1538 bool 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
MoveUp(int noLines,int flags)1565 bool wxRichTextCtrl::MoveUp(int noLines, int flags)
1566 {
1567     return MoveDown(- noLines, flags);
1568 }
1569 
1570 /// Move up
MoveDown(int noLines,int flags)1571 bool 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
MoveToParagraphEnd(int flags)1654 bool 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
MoveToParagraphStart(int flags)1675 bool 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
MoveToLineEnd(int flags)1696 bool 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
MoveToLineStart(int flags)1719 bool 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
MoveHome(int flags)1744 bool 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
MoveEnd(int flags)1763 bool 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
PageUp(int noPages,int flags)1784 bool wxRichTextCtrl::PageUp(int noPages, int flags)
1785 {
1786     return PageDown(- noPages, flags);
1787 }
1788 
1789 /// Move noPages pages down
PageDown(int noPages,int flags)1790 bool 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 
wxRichTextCtrlIsWhitespace(const wxString & str)1824 static bool wxRichTextCtrlIsWhitespace(const wxString& str)
1825 {
1826     return str == wxT(" ") || str == wxT("\t");
1827 }
1828 
1829 // Finds the caret position for the next word
FindNextWordPosition(int direction) const1830 long 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
WordLeft(int WXUNUSED (n),int flags)1918 bool 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
WordRight(int WXUNUSED (n),int flags)1940 bool 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
OnSize(wxSizeEvent & event)1962 void 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
OnIdle(wxIdleEvent & event)1983 void 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
OnScroll(wxScrollWinEvent & event)2017 void 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
SetupScrollbars(bool atTop)2031 void 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
PaintBackground(wxDC & dc)2080 void 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
RecreateBuffer(const wxSize & size)2101 bool 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 
DoLoadFile(const wxString & filename,int fileType)2120 bool 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 
DoSaveFile(const wxString & filename,int fileType)2144 bool 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
AddParagraph(const wxString & text)2165 wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
2166 {
2167     wxRichTextRange range = GetBuffer().AddParagraph(text);
2168     LayoutContent();
2169     return range;
2170 }
2171 
2172 /// Add an image
AddImage(const wxImage & image)2173 wxRichTextRange 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 
SelectAll()2184 void wxRichTextCtrl::SelectAll()
2185 {
2186     SetSelection(-1, -1);
2187 }
2188 
2189 /// Select none
SelectNone()2190 void 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 
wxIsWordDelimiter(const wxString & text)2203 static bool wxIsWordDelimiter(const wxString& text)
2204 {
2205     return !text.IsEmpty() && !wxIsalnum(text[0]);
2206 }
2207 
2208 /// Select the word at the given character position
SelectWord(long position)2209 bool 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 
GetStringSelection() const2262 wxString 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 
2274 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,wxTextCoord * x,wxTextCoord * y) const2275 wxRichTextCtrl::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 
2291 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,long * pos) const2292 wxRichTextCtrl::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 
GetValue() const2318 wxString wxRichTextCtrl::GetValue() const
2319 {
2320     return GetBuffer().GetText();
2321 }
2322 
GetRange(long from,long to) const2323 wxString 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 
DoSetValue(const wxString & value,int flags)2329 void 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 
WriteText(const wxString & value)2365 void wxRichTextCtrl::WriteText(const wxString& value)
2366 {
2367     DoWriteText(value);
2368 }
2369 
DoWriteText(const wxString & value,int flags)2370 void 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 
AppendText(const wxString & text)2380 void wxRichTextCtrl::AppendText(const wxString& text)
2381 {
2382     SetInsertionPointEnd();
2383 
2384     WriteText(text);
2385 }
2386 
2387 /// Write an image at the current insertion point
WriteImage(const wxImage & image,int bitmapType)2388 bool 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 
WriteImage(const wxString & filename,int bitmapType)2399 bool 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 
WriteImage(const wxRichTextImageBlock & imageBlock)2410 bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock)
2411 {
2412     return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this);
2413 }
2414 
WriteImage(const wxBitmap & bitmap,int bitmapType)2415 bool 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.
Newline()2430 bool 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.
LineBreak()2436 bool 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 
Copy()2447 void wxRichTextCtrl::Copy()
2448 {
2449     if (CanCopy())
2450     {
2451         wxRichTextRange range = GetInternalSelectionRange();
2452         GetBuffer().CopyToClipboard(range);
2453     }
2454 }
2455 
Cut()2456 void 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 
Paste()2469 void 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 
DeleteSelection()2484 void wxRichTextCtrl::DeleteSelection()
2485 {
2486     if (CanDeleteSelection())
2487     {
2488         DeleteSelectedContent();
2489     }
2490 }
2491 
HasSelection() const2492 bool wxRichTextCtrl::HasSelection() const
2493 {
2494     return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2;
2495 }
2496 
CanCopy() const2497 bool wxRichTextCtrl::CanCopy() const
2498 {
2499     // Can copy if there's a selection
2500     return HasSelection();
2501 }
2502 
CanCut() const2503 bool wxRichTextCtrl::CanCut() const
2504 {
2505     return HasSelection() && IsEditable();
2506 }
2507 
CanPaste() const2508 bool wxRichTextCtrl::CanPaste() const
2509 {
2510     if ( !IsEditable() )
2511         return false;
2512 
2513     return GetBuffer().CanPasteFromClipboard();
2514 }
2515 
CanDeleteSelection() const2516 bool wxRichTextCtrl::CanDeleteSelection() const
2517 {
2518     return HasSelection() && IsEditable();
2519 }
2520 
2521 
2522 // ----------------------------------------------------------------------------
2523 // Accessors
2524 // ----------------------------------------------------------------------------
2525 
SetContextMenu(wxMenu * menu)2526 void wxRichTextCtrl::SetContextMenu(wxMenu* menu)
2527 {
2528     if (m_contextMenu && m_contextMenu != menu)
2529         delete m_contextMenu;
2530     m_contextMenu = menu;
2531 }
2532 
SetEditable(bool editable)2533 void wxRichTextCtrl::SetEditable(bool editable)
2534 {
2535     m_editable = editable;
2536 }
2537 
SetInsertionPoint(long pos)2538 void wxRichTextCtrl::SetInsertionPoint(long pos)
2539 {
2540     SelectNone();
2541 
2542     m_caretPosition = pos - 1;
2543 
2544     PositionCaret();
2545 
2546     SetDefaultStyleToCursorStyle();
2547 }
2548 
SetInsertionPointEnd()2549 void wxRichTextCtrl::SetInsertionPointEnd()
2550 {
2551     long pos = GetLastPosition();
2552     SetInsertionPoint(pos);
2553 }
2554 
GetInsertionPoint() const2555 long wxRichTextCtrl::GetInsertionPoint() const
2556 {
2557     return m_caretPosition+1;
2558 }
2559 
GetLastPosition() const2560 wxTextPos 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.
GetSelection(long * from,long * to) const2567 void 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 
IsEditable() const2575 bool wxRichTextCtrl::IsEditable() const
2576 {
2577     return m_editable;
2578 }
2579 
2580 // ----------------------------------------------------------------------------
2581 // selection
2582 // ----------------------------------------------------------------------------
2583 
SetSelection(long from,long to)2584 void 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 
DoSetSelection(long from,long to,bool WXUNUSED (scrollCaret))2597 void 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 
Replace(long WXUNUSED (from),long WXUNUSED (to),const wxString & value)2620 void 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 
Remove(long from,long to)2632 void 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 
IsModified() const2643 bool wxRichTextCtrl::IsModified() const
2644 {
2645     return m_buffer.IsModified();
2646 }
2647 
MarkDirty()2648 void wxRichTextCtrl::MarkDirty()
2649 {
2650     m_buffer.Modify(true);
2651 }
2652 
DiscardEdits()2653 void wxRichTextCtrl::DiscardEdits()
2654 {
2655     m_caretPositionForDefaultStyle = -2;
2656     m_buffer.Modify(false);
2657     m_buffer.GetCommandProcessor()->ClearCommands();
2658 }
2659 
GetNumberOfLines() const2660 int wxRichTextCtrl::GetNumberOfLines() const
2661 {
2662     return GetBuffer().GetParagraphCount();
2663 }
2664 
2665 // ----------------------------------------------------------------------------
2666 // Positions <-> coords
2667 // ----------------------------------------------------------------------------
2668 
XYToPosition(long x,long y) const2669 long wxRichTextCtrl::XYToPosition(long x, long y) const
2670 {
2671     return GetBuffer().XYToPosition(x, y);
2672 }
2673 
PositionToXY(long pos,long * x,long * y) const2674 bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
2675 {
2676     return GetBuffer().PositionToXY(pos, x, y);
2677 }
2678 
2679 // ----------------------------------------------------------------------------
2680 //
2681 // ----------------------------------------------------------------------------
2682 
ShowPosition(long pos)2683 void wxRichTextCtrl::ShowPosition(long pos)
2684 {
2685     if (!IsPositionVisible(pos))
2686         ScrollIntoView(pos-1, WXK_DOWN);
2687 }
2688 
GetLineLength(long lineNo) const2689 int wxRichTextCtrl::GetLineLength(long lineNo) const
2690 {
2691     return GetBuffer().GetParagraphLength(lineNo);
2692 }
2693 
GetLineText(long lineNo) const2694 wxString wxRichTextCtrl::GetLineText(long lineNo) const
2695 {
2696     return GetBuffer().GetParagraphText(lineNo);
2697 }
2698 
2699 // ----------------------------------------------------------------------------
2700 // Undo/redo
2701 // ----------------------------------------------------------------------------
2702 
Undo()2703 void wxRichTextCtrl::Undo()
2704 {
2705     if (CanUndo())
2706     {
2707         GetCommandProcessor()->Undo();
2708     }
2709 }
2710 
Redo()2711 void wxRichTextCtrl::Redo()
2712 {
2713     if (CanRedo())
2714     {
2715         GetCommandProcessor()->Redo();
2716     }
2717 }
2718 
CanUndo() const2719 bool wxRichTextCtrl::CanUndo() const
2720 {
2721     return GetCommandProcessor()->CanUndo() && IsEditable();
2722 }
2723 
CanRedo() const2724 bool wxRichTextCtrl::CanRedo() const
2725 {
2726     return GetCommandProcessor()->CanRedo() && IsEditable();
2727 }
2728 
2729 // ----------------------------------------------------------------------------
2730 // implementation details
2731 // ----------------------------------------------------------------------------
2732 
Command(wxCommandEvent & event)2733 void wxRichTextCtrl::Command(wxCommandEvent& event)
2734 {
2735     SetValue(event.GetString());
2736     GetEventHandler()->ProcessEvent(event);
2737 }
2738 
OnDropFiles(wxDropFilesEvent & event)2739 void 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 
DoGetBestSize() const2748 wxSize wxRichTextCtrl::DoGetBestSize() const
2749 {
2750     return wxSize(10, 10);
2751 }
2752 
2753 // ----------------------------------------------------------------------------
2754 // standard handlers for standard edit menu events
2755 // ----------------------------------------------------------------------------
2756 
OnCut(wxCommandEvent & WXUNUSED (event))2757 void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2758 {
2759     Cut();
2760 }
2761 
OnClear(wxCommandEvent & WXUNUSED (event))2762 void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
2763 {
2764     DeleteSelection();
2765 }
2766 
OnCopy(wxCommandEvent & WXUNUSED (event))2767 void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2768 {
2769     Copy();
2770 }
2771 
OnPaste(wxCommandEvent & WXUNUSED (event))2772 void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2773 {
2774     Paste();
2775 }
2776 
OnUndo(wxCommandEvent & WXUNUSED (event))2777 void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2778 {
2779     Undo();
2780 }
2781 
OnRedo(wxCommandEvent & WXUNUSED (event))2782 void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2783 {
2784     Redo();
2785 }
2786 
OnUpdateCut(wxUpdateUIEvent & event)2787 void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2788 {
2789     event.Enable( CanCut() );
2790 }
2791 
OnUpdateCopy(wxUpdateUIEvent & event)2792 void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2793 {
2794     event.Enable( CanCopy() );
2795 }
2796 
OnUpdateClear(wxUpdateUIEvent & event)2797 void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
2798 {
2799     event.Enable( CanDeleteSelection() );
2800 }
2801 
OnUpdatePaste(wxUpdateUIEvent & event)2802 void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2803 {
2804     event.Enable( CanPaste() );
2805 }
2806 
OnUpdateUndo(wxUpdateUIEvent & event)2807 void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2808 {
2809     event.Enable( CanUndo() );
2810     event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
2811 }
2812 
OnUpdateRedo(wxUpdateUIEvent & event)2813 void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2814 {
2815     event.Enable( CanRedo() );
2816     event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
2817 }
2818 
OnSelectAll(wxCommandEvent & WXUNUSED (event))2819 void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2820 {
2821     if (GetLastPosition() > 0)
2822         SelectAll();
2823 }
2824 
OnUpdateSelectAll(wxUpdateUIEvent & event)2825 void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
2826 {
2827     event.Enable(GetLastPosition() > 0);
2828 }
2829 
OnContextMenu(wxContextMenuEvent & event)2830 void 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 
SetStyle(long start,long end,const wxTextAttrEx & style)2843 bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
2844 {
2845     return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style);
2846 }
2847 
SetStyle(long start,long end,const wxTextAttr & style)2848 bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
2849 {
2850     return GetBuffer().SetStyle(wxRichTextRange(start, end-1), wxTextAttrEx(style));
2851 }
2852 
SetStyle(const wxRichTextRange & range,const wxRichTextAttr & style)2853 bool 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.
SetStyleEx(long start,long end,const wxTextAttrEx & style,int flags)2861 bool wxRichTextCtrl::SetStyleEx(long start, long end, const wxTextAttrEx& style, int flags)
2862 {
2863     return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style, flags);
2864 }
2865 
SetStyleEx(const wxRichTextRange & range,const wxTextAttrEx & style,int flags)2866 bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxTextAttrEx& style, int flags)
2867 {
2868     return GetBuffer().SetStyle(range.ToInternal(), style, flags);
2869 }
2870 
SetStyleEx(const wxRichTextRange & range,const wxRichTextAttr & style,int flags)2871 bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
2872 {
2873     return GetBuffer().SetStyle(range.ToInternal(), style, flags);
2874 }
2875 
SetDefaultStyle(const wxTextAttrEx & style)2876 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style)
2877 {
2878     return GetBuffer().SetDefaultStyle(style);
2879 }
2880 
SetDefaultStyle(const wxTextAttr & style)2881 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style)
2882 {
2883     return GetBuffer().SetDefaultStyle(wxTextAttrEx(style));
2884 }
2885 
GetDefaultStyleEx() const2886 const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const
2887 {
2888     return GetBuffer().GetDefaultStyle();
2889 }
2890 
GetDefaultStyle() const2891 const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const
2892 {
2893     return GetBuffer().GetDefaultStyle();
2894 }
2895 
GetStyle(long position,wxTextAttr & style)2896 bool 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 
GetStyle(long position,wxTextAttrEx & style)2908 bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style)
2909 {
2910     return GetBuffer().GetStyle(position, style);
2911 }
2912 
GetStyle(long position,wxRichTextAttr & style)2913 bool 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
GetStyleForRange(const wxRichTextRange & range,wxRichTextAttr & style)2919 bool 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 
GetStyleForRange(const wxRichTextRange & range,wxTextAttrEx & style)2931 bool 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 
GetUncombinedStyle(long position,wxTextAttr & style)2938 bool 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 
GetUncombinedStyle(long position,wxTextAttrEx & style)2950 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttrEx& style)
2951 {
2952     return GetBuffer().GetUncombinedStyle(position, style);
2953 }
2954 
GetUncombinedStyle(long position,wxRichTextAttr & style)2955 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style)
2956 {
2957     return GetBuffer().GetUncombinedStyle(position, style);
2958 }
2959 
2960 /// Set font, and also the buffer attributes
SetFont(const wxFont & font)2961 bool 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
GetPhysicalPoint(const wxPoint & ptLogical) const2976 wxPoint 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
GetLogicalPoint(const wxPoint & ptPhysical) const2985 wxPoint 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
PositionCaret()2994 void 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
GetCaretPositionForIndex(long position,wxRect & rect)3024 bool 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.
GetVisibleLineForCaretPosition(long caretPosition) const3051 wxRichTextLine* 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
MoveCaret(long pos,bool showAtLineStart)3070 bool 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.
LayoutContent(bool onlyVisibleRect)3089 bool 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?
IsSelectionBold()3124 bool 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?
IsSelectionItalics()3154 bool 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?
IsSelectionUnderlined()3184 bool 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
ApplyBoldToSelection()3214 bool 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
ApplyItalicToSelection()3232 bool 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
ApplyUnderlineToSelection()3250 bool 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?
IsSelectionAligned(wxTextAttrAlignment alignment)3268 bool 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
ApplyAlignmentToSelection(wxTextAttrAlignment alignment)3283 bool 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
ApplyStyle(wxRichTextStyleDefinition * def)3299 bool 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.
ApplyStyleSheet(wxRichTextStyleSheet * styleSheet)3371 bool 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
SetDefaultStyleToCursorStyle()3389 bool 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
GetFirstVisiblePosition() const3407 long 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
GetFirstVisiblePoint() const3417 wxPoint 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.
GetAdjustedCaretPosition(long caretPos) const3431 long 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)
GetSelectionRange() const3443 wxRichTextRange 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 
SetSelectionRange(const wxRichTextRange & range)3451 void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range)
3452 {
3453     SetSelection(range.GetStart(), range.GetEnd());
3454 }
3455 
3456 /// Set list style
SetListStyle(const wxRichTextRange & range,wxRichTextListStyleDefinition * def,int flags,int startFrom,int specifiedLevel)3457 bool 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 
SetListStyle(const wxRichTextRange & range,const wxString & defName,int flags,int startFrom,int specifiedLevel)3462 bool 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
ClearListStyle(const wxRichTextRange & range,int flags)3468 bool 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
NumberList(const wxRichTextRange & range,wxRichTextListStyleDefinition * def,int flags,int startFrom,int specifiedLevel)3474 bool 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 
NumberList(const wxRichTextRange & range,const wxString & defName,int flags,int startFrom,int specifiedLevel)3479 bool 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
PromoteList(int promoteBy,const wxRichTextRange & range,wxRichTextListStyleDefinition * def,int flags,int specifiedLevel)3485 bool 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 
PromoteList(int promoteBy,const wxRichTextRange & range,const wxString & defName,int flags,int specifiedLevel)3490 bool 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
Delete(const wxRichTextRange & range)3496 bool wxRichTextCtrl::Delete(const wxRichTextRange& range)
3497 {
3498     return GetBuffer().DeleteRangeWithUndo(range.ToInternal(), this);
3499 }
3500 
GetAvailableFontNames()3501 const 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 
ClearAvailableFontNames()3511 void wxRichTextCtrl::ClearAvailableFontNames()
3512 {
3513     sm_availableFontNames.Clear();
3514 }
3515 
3516 // Refresh the area affected by a selection change
wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl & ctrl,const wxRichTextRange & oldSelection,const wxRichTextRange & newSelection)3517 bool 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 
Init()3570 void 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 
~wxRichTextCaret()3581 wxRichTextCaret::~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 
DoShow()3591 void wxRichTextCaret::DoShow()
3592 {
3593     m_flashOn = true;
3594 
3595     if (!m_timer.IsRunning())
3596         m_timer.Start(GetBlinkTime());
3597 
3598     Refresh();
3599 }
3600 
DoHide()3601 void wxRichTextCaret::DoHide()
3602 {
3603     if (m_timer.IsRunning())
3604         m_timer.Stop();
3605 
3606     Refresh();
3607 }
3608 
DoMove()3609 void 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 
DoSize()3629 void 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 
OnSetFocus()3649 void wxRichTextCaret::OnSetFocus()
3650 {
3651     m_hasFocus = true;
3652 
3653     if ( IsVisible() )
3654         Refresh();
3655 }
3656 
OnKillFocus()3657 void wxRichTextCaret::OnKillFocus()
3658 {
3659     m_hasFocus = false;
3660 }
3661 
3662 // ----------------------------------------------------------------------------
3663 // drawing the caret
3664 // ----------------------------------------------------------------------------
3665 
Refresh()3666 void wxRichTextCaret::Refresh()
3667 {
3668     if (m_richTextCtrl)
3669     {
3670         wxRect rect(GetPosition(), GetSize());
3671         m_richTextCtrl->RefreshRect(rect, false);
3672     }
3673 }
3674 
DoDraw(wxDC * dc)3675 void 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 
Notify()3692 void wxRichTextCaret::Notify()
3693 {
3694     m_flashOn = !m_flashOn;
3695     Refresh();
3696 }
3697 
Notify()3698 void wxRichTextCaretTimer::Notify()
3699 {
3700     m_caret->Notify();
3701 }
3702 
3703 #endif
3704     // wxRICHTEXT_USE_OWN_CARET
3705 #endif
3706     // wxUSE_RICHTEXT
3707