1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/richtext/richtextctrl.cpp
3 // Purpose:     A rich edit control
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     2005-09-30
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 
15 #if wxUSE_RICHTEXT
16 
17 #include "wx/richtext/richtextctrl.h"
18 #include "wx/richtext/richtextstyles.h"
19 
20 #ifndef WX_PRECOMP
21     #include "wx/wx.h"
22     #include "wx/settings.h"
23 #endif
24 
25 #include "wx/timer.h"
26 #include "wx/textfile.h"
27 #include "wx/ffile.h"
28 #include "wx/filename.h"
29 #include "wx/dcbuffer.h"
30 #include "wx/arrimpl.cpp"
31 #include "wx/fontenum.h"
32 #include "wx/accel.h"
33 
34 #if defined (__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__)
35 #define wxHAVE_PRIMARY_SELECTION 1
36 #else
37 #define wxHAVE_PRIMARY_SELECTION 0
38 #endif
39 
40 #if wxUSE_CLIPBOARD && wxHAVE_PRIMARY_SELECTION
41 #include "wx/clipbrd.h"
42 #endif
43 
44 // DLL options compatibility check:
45 #include "wx/app.h"
46 WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
47 
48 wxDEFINE_EVENT( wxEVT_RICHTEXT_LEFT_CLICK, wxRichTextEvent );
49 wxDEFINE_EVENT( wxEVT_RICHTEXT_MIDDLE_CLICK, wxRichTextEvent );
50 wxDEFINE_EVENT( wxEVT_RICHTEXT_RIGHT_CLICK, wxRichTextEvent );
51 wxDEFINE_EVENT( wxEVT_RICHTEXT_LEFT_DCLICK, wxRichTextEvent );
52 wxDEFINE_EVENT( wxEVT_RICHTEXT_RETURN, wxRichTextEvent );
53 wxDEFINE_EVENT( wxEVT_RICHTEXT_CHARACTER, wxRichTextEvent );
54 wxDEFINE_EVENT( wxEVT_RICHTEXT_CONSUMING_CHARACTER, wxRichTextEvent );
55 wxDEFINE_EVENT( wxEVT_RICHTEXT_DELETE, wxRichTextEvent );
56 
57 wxDEFINE_EVENT( wxEVT_RICHTEXT_STYLESHEET_REPLACING, wxRichTextEvent );
58 wxDEFINE_EVENT( wxEVT_RICHTEXT_STYLESHEET_REPLACED, wxRichTextEvent );
59 wxDEFINE_EVENT( wxEVT_RICHTEXT_STYLESHEET_CHANGING, wxRichTextEvent );
60 wxDEFINE_EVENT( wxEVT_RICHTEXT_STYLESHEET_CHANGED, wxRichTextEvent );
61 
62 wxDEFINE_EVENT( wxEVT_RICHTEXT_CONTENT_INSERTED, wxRichTextEvent );
63 wxDEFINE_EVENT( wxEVT_RICHTEXT_CONTENT_DELETED, wxRichTextEvent );
64 wxDEFINE_EVENT( wxEVT_RICHTEXT_STYLE_CHANGED, wxRichTextEvent );
65 wxDEFINE_EVENT( wxEVT_RICHTEXT_PROPERTIES_CHANGED, wxRichTextEvent );
66 wxDEFINE_EVENT( wxEVT_RICHTEXT_SELECTION_CHANGED, wxRichTextEvent );
67 wxDEFINE_EVENT( wxEVT_RICHTEXT_BUFFER_RESET, wxRichTextEvent );
68 wxDEFINE_EVENT( wxEVT_RICHTEXT_FOCUS_OBJECT_CHANGED, wxRichTextEvent );
69 
70 #if wxRICHTEXT_USE_OWN_CARET
71 
72 /*!
73  * wxRichTextCaret
74  *
75  * This implements a non-flashing cursor in case there
76  * are platform-specific problems with the generic caret.
77  * wxRICHTEXT_USE_OWN_CARET is set in richtextbuffer.h.
78  */
79 
80 class wxRichTextCaret;
81 class wxRichTextCaretTimer: public wxTimer
82 {
83   public:
wxRichTextCaretTimer(wxRichTextCaret * caret)84     wxRichTextCaretTimer(wxRichTextCaret* caret)
85     {
86         m_caret = caret;
87     }
88     virtual void Notify() wxOVERRIDE;
89     wxRichTextCaret* m_caret;
90 };
91 
92 class wxRichTextCaret: public wxCaret
93 {
94 public:
95     // ctors
96     // -----
97         // default - use Create()
wxRichTextCaret()98     wxRichTextCaret(): m_timer(this)  { Init(); }
99         // creates a block caret associated with the given window
wxRichTextCaret(wxRichTextCtrl * window,int width,int height)100     wxRichTextCaret(wxRichTextCtrl *window, int width, int height)
101         : wxCaret(window, width, height), m_timer(this) { Init(); m_richTextCtrl = window; }
wxRichTextCaret(wxRichTextCtrl * window,const wxSize & size)102     wxRichTextCaret(wxRichTextCtrl *window, const wxSize& size)
103         : wxCaret(window, size), m_timer(this) { Init(); m_richTextCtrl = window; }
104 
105     virtual ~wxRichTextCaret();
106 
107     // implementation
108     // --------------
109 
110     // called by wxWindow (not using the event tables)
111     virtual void OnSetFocus() wxOVERRIDE;
112     virtual void OnKillFocus() wxOVERRIDE;
113 
114     // draw the caret on the given DC
115     void DoDraw(wxDC *dc);
116 
117     // get the visible count
GetVisibleCount() const118     int GetVisibleCount() const { return m_countVisible; }
119 
120     // delay repositioning
GetNeedsUpdate() const121     bool GetNeedsUpdate() const { return m_needsUpdate; }
SetNeedsUpdate(bool needsUpdate=true)122     void SetNeedsUpdate(bool needsUpdate = true ) { m_needsUpdate = needsUpdate; }
123 
124     void Notify();
125 
GetRefreshEnabled() const126     bool GetRefreshEnabled() const { return m_refreshEnabled; }
EnableRefresh(bool b)127     void EnableRefresh(bool b) { m_refreshEnabled = b; }
128 
129 protected:
130     virtual void DoShow() wxOVERRIDE;
131     virtual void DoHide() wxOVERRIDE;
132     virtual void DoMove() wxOVERRIDE;
133     virtual void DoSize() wxOVERRIDE;
134 
135     // refresh the caret
136     void Refresh();
137 
138 private:
139     void Init();
140 
141     int           m_xOld,
142                   m_yOld;
143     bool          m_hasFocus;       // true => our window has focus
144     bool          m_needsUpdate;    // must be repositioned
145     bool          m_flashOn;
146     wxRichTextCaretTimer m_timer;
147     wxRichTextCtrl* m_richTextCtrl;
148     bool          m_refreshEnabled;
149     wxPen         m_caretPen;
150     wxBrush       m_caretBrush;
151 };
152 #endif
153 
154 wxIMPLEMENT_DYNAMIC_CLASS(wxRichTextCtrl, wxControl);
155 
156 wxIMPLEMENT_DYNAMIC_CLASS(wxRichTextEvent, wxNotifyEvent);
157 
158 wxBEGIN_EVENT_TABLE(wxRichTextCtrl, wxControl)
159     EVT_PAINT(wxRichTextCtrl::OnPaint)
160     EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
161     EVT_IDLE(wxRichTextCtrl::OnIdle)
162     EVT_SCROLLWIN(wxRichTextCtrl::OnScroll)
163     EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
164     EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
165     EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
166     EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick)
167     EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick)
168     EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick)
169     EVT_CHAR(wxRichTextCtrl::OnChar)
170     EVT_KEY_DOWN(wxRichTextCtrl::OnChar)
171     EVT_SIZE(wxRichTextCtrl::OnSize)
172     EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus)
173     EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus)
174     EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost)
175     EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
176     EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged)
177     EVT_TIMER(wxID_ANY, wxRichTextCtrl::OnTimer)
178 
179     EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
180     EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
181 
182     EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo)
183     EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo)
184 
185     EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy)
186     EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy)
187 
188     EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste)
189     EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste)
190 
191     EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut)
192     EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut)
193 
194     EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear)
195     EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear)
196 
197     EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll)
198     EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll)
199 
200     EVT_MENU(wxID_RICHTEXT_PROPERTIES1, wxRichTextCtrl::OnProperties)
201     EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES1, wxRichTextCtrl::OnUpdateProperties)
202 
203     EVT_MENU(wxID_RICHTEXT_PROPERTIES2, wxRichTextCtrl::OnProperties)
204     EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES2, wxRichTextCtrl::OnUpdateProperties)
205 
206     EVT_MENU(wxID_RICHTEXT_PROPERTIES3, wxRichTextCtrl::OnProperties)
207     EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES3, wxRichTextCtrl::OnUpdateProperties)
208 
209 wxEND_EVENT_TABLE()
210 
211 /*!
212  * wxRichTextCtrl
213  */
214 
215 wxArrayString wxRichTextCtrl::sm_availableFontNames;
216 
wxRichTextCtrl()217 wxRichTextCtrl::wxRichTextCtrl()
218               : wxScrollHelper(this)
219 {
220     Init();
221 }
222 
wxRichTextCtrl(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)223 wxRichTextCtrl::wxRichTextCtrl(wxWindow* parent,
224                                wxWindowID id,
225                                const wxString& value,
226                                const wxPoint& pos,
227                                const wxSize& size,
228                                long style,
229                                const wxValidator& validator,
230                                const wxString& name)
231               : wxScrollHelper(this)
232 {
233     Init();
234     Create(parent, id, value, pos, size, style, validator, name);
235 }
236 
237 /// Creation
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)238 bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style,
239                              const wxValidator& validator, const wxString& name)
240 {
241     style |= wxVSCROLL;
242 
243     // If read-only, the programmer probably wants to retain dialog keyboard navigation.
244     // If you don't, then pass wxWANTS_CHARS explicitly.
245     if ((style & wxTE_READONLY) == 0)
246         style |= wxWANTS_CHARS;
247 
248     if (!wxControl::Create(parent, id, pos, size,
249                            style|wxFULL_REPAINT_ON_RESIZE,
250                            validator, name))
251         return false;
252 
253     if (!GetFont().IsOk())
254     {
255         SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
256     }
257 
258     // No physical scrolling, so we can preserve margins
259     EnableScrolling(false, false);
260 
261     if (style & wxTE_READONLY)
262         SetEditable(false);
263 
264     // The base attributes must all have default values
265     wxRichTextAttr attributes;
266     attributes.SetFont(GetFont());
267     attributes.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
268     attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
269     attributes.SetLineSpacing(10);
270     attributes.SetParagraphSpacingAfter(10);
271     attributes.SetParagraphSpacingBefore(0);
272     SetBasicStyle(attributes);
273 
274     int margin = 5;
275     SetMargins(margin, margin);
276 
277     // The default attributes will be merged with base attributes, so
278     // can be empty to begin with
279     wxRichTextAttr defaultAttributes;
280     SetDefaultStyle(defaultAttributes);
281 
282     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
283     SetBackgroundStyle(wxBG_STYLE_PAINT);
284 
285     GetBuffer().Reset();
286     GetBuffer().SetRichTextCtrl(this);
287 
288 #if wxRICHTEXT_USE_OWN_CARET
289     SetCaret(new wxRichTextCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
290 #else
291     SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
292 #endif
293 
294     // Tell the sizers to use the given or best size
295     SetInitialSize(size);
296 
297 #if wxRICHTEXT_BUFFERED_PAINTING
298     // Create a buffer
299     RecreateBuffer(size);
300 #endif
301 
302     m_textCursor = wxCursor(wxCURSOR_IBEAM);
303     m_urlCursor = wxCursor(wxCURSOR_HAND);
304 
305     SetCursor(m_textCursor);
306 
307     if (!value.IsEmpty())
308         SetValue(value);
309 
310     GetBuffer().AddEventHandler(this);
311 
312 #if wxUSE_ACCEL
313     // Accelerators
314     wxAcceleratorEntry entries[6];
315 
316     entries[0].Set(wxACCEL_CTRL,   (int) 'C',       wxID_COPY);
317     entries[1].Set(wxACCEL_CTRL,   (int) 'X',       wxID_CUT);
318     entries[2].Set(wxACCEL_CTRL,   (int) 'V',       wxID_PASTE);
319     entries[3].Set(wxACCEL_CTRL,   (int) 'A',       wxID_SELECTALL);
320     entries[4].Set(wxACCEL_CTRL,   (int) 'Z',       wxID_UNDO);
321     entries[5].Set(wxACCEL_CTRL,   (int) 'Y',       wxID_REDO);
322 
323     wxAcceleratorTable accel(6, entries);
324     SetAcceleratorTable(accel);
325 #endif // wxUSE_ACCEL
326 
327     m_contextMenu = new wxMenu;
328     m_contextMenu->Append(wxID_UNDO, _("&Undo"));
329     m_contextMenu->Append(wxID_REDO, _("&Redo"));
330     m_contextMenu->AppendSeparator();
331     m_contextMenu->Append(wxID_CUT, _("Cu&t"));
332     m_contextMenu->Append(wxID_COPY, _("&Copy"));
333     m_contextMenu->Append(wxID_PASTE, _("&Paste"));
334     m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
335     m_contextMenu->AppendSeparator();
336     m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
337     m_contextMenu->AppendSeparator();
338     m_contextMenu->Append(wxID_RICHTEXT_PROPERTIES1, _("&Properties"));
339 
340 #if wxUSE_DRAG_AND_DROP
341     SetDropTarget(new wxRichTextDropTarget(this));
342 #endif
343     SetModified( false );
344     return true;
345 }
346 
~wxRichTextCtrl()347 wxRichTextCtrl::~wxRichTextCtrl()
348 {
349     SetFocusObject(& GetBuffer(), false);
350     GetBuffer().RemoveEventHandler(this);
351 
352     delete m_contextMenu;
353 
354     m_delayedImageProcessingTimer.Stop();
355 }
356 
357 /// Member initialisation
Init()358 void wxRichTextCtrl::Init()
359 {
360     m_contextMenu = NULL;
361     m_caret = NULL;
362     m_caretPosition = -1;
363     m_selectionAnchor = -2;
364     m_selectionAnchorObject = NULL;
365     m_selectionState = wxRichTextCtrlSelectionState_Normal;
366     m_editable = true;
367     m_useVirtualAttributes = false;
368     m_verticalScrollbarEnabled = true;
369     m_caretAtLineStart = false;
370     m_dragging = false;
371 #if wxUSE_DRAG_AND_DROP
372     m_preDrag = false;
373 #endif
374     m_fullLayoutRequired = false;
375     m_fullLayoutTime = 0;
376     m_fullLayoutSavedPosition = 0;
377     m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
378     m_caretPositionForDefaultStyle = -2;
379     m_focusObject = & m_buffer;
380     m_scale = 1.0;
381 
382     // Scrollbar hysteresis detection
383     m_setupScrollbarsCount = 0;
384     m_setupScrollbarsCountInOnSize = 0;
385 
386     m_enableImages = true;
387 
388     m_enableDelayedImageLoading = false;
389     m_delayedImageProcessingRequired = false;
390     m_delayedImageProcessingTime = 0;
391 
392     // Line height in pixels
393     m_lineHeight = 5;
394 }
395 
DoThaw()396 void wxRichTextCtrl::DoThaw()
397 {
398     if (GetBuffer().IsDirty())
399         LayoutContent();
400     else
401         SetupScrollbars();
402 
403     wxWindow::DoThaw();
404 }
405 
406 /// Clear all text
Clear()407 void wxRichTextCtrl::Clear()
408 {
409     if (GetFocusObject() == & GetBuffer())
410     {
411         m_buffer.ResetAndClearCommands();
412         m_buffer.Invalidate(wxRICHTEXT_ALL);
413     }
414     else
415     {
416         GetFocusObject()->Reset();
417     }
418 
419     m_caretPosition = -1;
420     m_caretPositionForDefaultStyle = -2;
421     m_caretAtLineStart = false;
422     m_selection.Reset();
423     m_selectionState = wxRichTextCtrlSelectionState_Normal;
424 
425     Scroll(0,0);
426 
427     if (!IsFrozen())
428     {
429         LayoutContent();
430         Refresh(false);
431     }
432 
433     wxTextCtrl::SendTextUpdatedEvent(this);
434 }
435 
436 /// Painting
OnPaint(wxPaintEvent & WXUNUSED (event))437 void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
438 {
439 #if !wxRICHTEXT_USE_OWN_CARET
440     if (GetCaret() && !IsFrozen())
441         GetCaret()->Hide();
442 #else
443     // Stop the caret refreshing the control from within the
444     // paint handler
445     if (GetCaret())
446         ((wxRichTextCaret*) GetCaret())->EnableRefresh(false);
447 #endif
448 
449     {
450 #if wxRICHTEXT_BUFFERED_PAINTING
451         wxBufferedPaintDC dc(this, m_bufferBitmap);
452 #else
453         wxPaintDC dc(this);
454 #endif
455 
456         if (IsFrozen())
457             return;
458 
459         PrepareDC(dc);
460 
461         dc.SetFont(GetFont());
462 
463         wxRect drawingArea(GetUpdateRegion().GetBox());
464         drawingArea.SetPosition(GetUnscaledPoint(GetLogicalPoint(drawingArea.GetPosition())));
465         drawingArea.SetSize(GetUnscaledSize(drawingArea.GetSize()));
466 
467         wxRect availableSpace(GetUnscaledSize(GetClientSize()));
468         wxRichTextDrawingContext context(& GetBuffer());
469         if (GetBuffer().IsDirty())
470         {
471             dc.SetUserScale(GetScale(), GetScale());
472 
473             GetBuffer().Defragment(context);
474             GetBuffer().UpdateRanges();     // If items were deleted, ranges need recalculation
475 
476             DoLayoutBuffer(GetBuffer(), dc, context, availableSpace, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
477 
478             GetBuffer().Invalidate(wxRICHTEXT_NONE);
479 
480             dc.SetUserScale(1.0, 1.0);
481 
482             SetupScrollbars(false, true /* from OnPaint */);
483         }
484 
485         // Paint the background
486         PaintBackground(dc);
487 
488         wxRect clipRect(availableSpace);
489         clipRect.x += GetBuffer().GetLeftMargin();
490         clipRect.y += GetBuffer().GetTopMargin();
491         clipRect.width -= (GetBuffer().GetLeftMargin() + GetBuffer().GetRightMargin());
492         clipRect.height -= (GetBuffer().GetTopMargin() + GetBuffer().GetBottomMargin());
493 
494         clipRect = GetScaledRect(clipRect);
495         clipRect.SetPosition(GetLogicalPoint(clipRect.GetPosition()));
496 
497         dc.SetClippingRegion(clipRect);
498 
499         int flags = 0;
500         if ((GetExtraStyle() & wxRICHTEXT_EX_NO_GUIDELINES) == 0)
501             flags |= wxRICHTEXT_DRAW_GUIDELINES;
502 
503         dc.SetUserScale(GetScale(), GetScale());
504 
505         GetBuffer().Draw(dc, context, GetBuffer().GetOwnRange(), GetSelection(), drawingArea, 0 /* descent */, flags);
506 
507         dc.DestroyClippingRegion();
508 
509         // Other user defined painting after everything else (i.e. all text) is painted
510         PaintAboveContent(dc);
511 
512 #if wxRICHTEXT_USE_OWN_CARET
513         if (GetCaret()->IsVisible())
514         {
515             PositionCaret();
516             ((wxRichTextCaret*) GetCaret())->DoDraw(& dc);
517         }
518 #endif
519 
520         dc.SetUserScale(1.0, 1.0);
521     }
522 
523 #if !wxRICHTEXT_USE_OWN_CARET
524     if (GetCaret())
525         GetCaret()->Show();
526     PositionCaret();
527 #else
528     if (GetCaret())
529         ((wxRichTextCaret*) GetCaret())->EnableRefresh(true);
530 #endif
531 }
532 
533 // Empty implementation, to prevent flicker
OnEraseBackground(wxEraseEvent & WXUNUSED (event))534 void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
535 {
536 }
537 
OnSetFocus(wxFocusEvent & WXUNUSED (event))538 void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
539 {
540     if (GetCaret())
541     {
542 #if !wxRICHTEXT_USE_OWN_CARET
543         PositionCaret();
544 #endif
545         if (!GetCaret()->IsVisible())
546             GetCaret()->Show();
547     }
548 
549 #if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
550     // Work around dropouts when control is focused
551     if (!IsFrozen())
552     {
553         Refresh(false);
554     }
555 #endif
556 }
557 
OnKillFocus(wxFocusEvent & WXUNUSED (event))558 void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
559 {
560     if (GetCaret() && GetCaret()->IsVisible())
561         GetCaret()->Hide();
562 
563 #if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
564     // Work around dropouts when control is focused
565     if (!IsFrozen())
566     {
567         Refresh(false);
568     }
569 #endif
570 }
571 
OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED (event))572 void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
573 {
574     m_dragging = false;
575 }
576 
577 // Set up the caret for the given position and container, after a mouse click
SetCaretPositionAfterClick(wxRichTextParagraphLayoutBox * container,long position,int hitTestFlags,bool extendSelection)578 bool wxRichTextCtrl::SetCaretPositionAfterClick(wxRichTextParagraphLayoutBox* container, long position, int hitTestFlags, bool extendSelection)
579 {
580     bool caretAtLineStart = false;
581 
582     if (hitTestFlags & wxRICHTEXT_HITTEST_BEFORE)
583     {
584         // If we're at the start of a line (but not first in para)
585         // then we should keep the caret showing at the start of the line
586         // by showing the m_caretAtLineStart flag.
587         wxRichTextParagraph* para = container->GetParagraphAtPosition(position);
588         wxRichTextLine* line = container->GetLineAtPosition(position);
589 
590         if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
591             caretAtLineStart = true;
592         position --;
593     }
594 
595     if (extendSelection && (m_caretPosition != position))
596         ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
597 
598     MoveCaret(position, caretAtLineStart);
599     SetDefaultStyleToCursorStyle();
600 
601     return true;
602 }
603 
604 /// Left-click
OnLeftClick(wxMouseEvent & event)605 void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
606 {
607     SetFocus();
608 
609     wxClientDC dc(this);
610     PrepareDC(dc);
611     dc.SetFont(GetFont());
612 
613     // TODO: detect change of focus object
614     long position = 0;
615     wxRichTextObject* hitObj = NULL;
616     wxRichTextObject* contextObj = NULL;
617     wxRichTextDrawingContext context(& GetBuffer());
618     int hit = GetBuffer().HitTest(dc, context, GetUnscaledPoint(event.GetLogicalPosition(dc)), position, & hitObj, & contextObj, wxRICHTEXT_HITTEST_HONOUR_ATOMIC);
619 
620 #if wxUSE_DRAG_AND_DROP
621     // If there's no selection, or we're not inside it, this isn't an attempt to initiate Drag'n'Drop
622     if (IsEditable() && HasSelection() && GetSelectionRange().ToInternal().Contains(position))
623     {
624         // This might be an attempt at initiating Drag'n'Drop. So set the time & flags
625         m_preDrag = true;
626         m_dragStartPoint = event.GetPosition();   // No need to worry about logical positions etc, we only care about the distance from the original pt
627 
628 #if wxUSE_DATETIME
629         m_dragStartTime = wxDateTime::UNow();
630 #endif // wxUSE_DATETIME
631 
632         // Preserve behaviour of clicking on an object within the selection
633         if (hit != wxRICHTEXT_HITTEST_NONE && hitObj)
634             m_dragging = true;
635 
636         return; // Don't skip the event, else the selection will be lost
637     }
638 #endif // wxUSE_DRAG_AND_DROP
639 
640     if (hit != wxRICHTEXT_HITTEST_NONE && hitObj)
641     {
642         wxRichTextParagraphLayoutBox* oldFocusObject = GetFocusObject();
643         wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
644         bool needsCaretSet = false;
645         if (container && container != GetFocusObject() && container->AcceptsFocus())
646         {
647             SetFocusObject(container, false /* don't set caret position yet */);
648             needsCaretSet = true;
649         }
650 
651         m_dragging = true;
652         CaptureMouse();
653 
654         // Don't change the caret position if we clicked on a floating objects such as an image,
655         // unless we changed the focus object.
656         if (wxRichTextBuffer::GetFloatingLayoutMode() && hitObj && hitObj->IsFloating() && !hitObj->AcceptsFocus())
657         {
658             if (needsCaretSet)
659                 SetInsertionPoint(0);
660         }
661         else
662         {
663             long oldCaretPos = m_caretPosition;
664 
665             SetCaretPositionAfterClick(container, position, hit);
666 
667             // For now, don't handle shift-click when we're selecting multiple objects.
668             if (event.ShiftDown() && GetFocusObject() == oldFocusObject && m_selectionState == wxRichTextCtrlSelectionState_Normal)
669                 ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
670             else
671                 SelectNone();
672         }
673     }
674 
675     event.Skip();
676 }
677 
678 /// Left-up
OnLeftUp(wxMouseEvent & event)679 void wxRichTextCtrl::OnLeftUp(wxMouseEvent& event)
680 {
681     if (m_dragging)
682     {
683         m_dragging = false;
684         if (GetCapture() == this)
685             ReleaseMouse();
686 
687         // See if we clicked on a URL
688         wxClientDC dc(this);
689         PrepareDC(dc);
690         dc.SetFont(GetFont());
691 
692         long position = 0;
693         wxPoint logicalPt = event.GetLogicalPosition(dc);
694         wxRichTextObject* hitObj = NULL;
695         wxRichTextObject* contextObj = NULL;
696         wxRichTextDrawingContext context(& GetBuffer());
697         // Only get objects at this level, not nested, because otherwise we couldn't swipe text at a single level.
698         int hit = GetFocusObject()->HitTest(dc, context, GetUnscaledPoint(logicalPt), position, & hitObj, & contextObj, wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS|wxRICHTEXT_HITTEST_HONOUR_ATOMIC);
699 
700 #if wxUSE_DRAG_AND_DROP
701         if (m_preDrag)
702         {
703             // Preserve the behaviour that would have happened without drag-and-drop detection, in OnLeftClick
704             m_preDrag = false; // Tell DnD not to happen now: we are processing Left Up ourselves.
705 
706             // Do the actions that would have been done in OnLeftClick if we hadn't tried to drag
707             long prePosition = 0;
708             wxRichTextObject* preHitObj = NULL;
709             wxRichTextObject* preContextObj = NULL;
710             int preHit = GetBuffer().HitTest(dc, context, GetUnscaledPoint(event.GetLogicalPosition(dc)), prePosition, & preHitObj, & preContextObj, wxRICHTEXT_HITTEST_HONOUR_ATOMIC);
711             wxRichTextParagraphLayoutBox* oldFocusObject = GetFocusObject();
712             wxRichTextParagraphLayoutBox* container = wxDynamicCast(preContextObj, wxRichTextParagraphLayoutBox);
713             bool needsCaretSet = false;
714             if (container && container != GetFocusObject() && container->AcceptsFocus())
715             {
716                 SetFocusObject(container, false /* don't set caret position yet */);
717                 needsCaretSet = true;
718             }
719 
720             if (wxRichTextBuffer::GetFloatingLayoutMode() && preHitObj && preHitObj->IsFloating() && !preHitObj->AcceptsFocus())
721             {
722                 if (needsCaretSet)
723                     SetInsertionPoint(0);
724             }
725             else
726             {
727                 long oldCaretPos = m_caretPosition;
728 
729                 SetCaretPositionAfterClick(container, prePosition, preHit);
730 
731                 // For now, don't handle shift-click when we're selecting multiple objects.
732                 if (event.ShiftDown() && GetFocusObject() == oldFocusObject && m_selectionState == wxRichTextCtrlSelectionState_Normal)
733                     ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
734                 else
735                     SelectNone();
736             }
737         }
738 #endif
739 
740         // Don't process left click if there was a selection, which implies that a selection may just have been
741         // extended
742         if ((hit != wxRICHTEXT_HITTEST_NONE) && !(hit & wxRICHTEXT_HITTEST_OUTSIDE) && !HasSelection())
743         {
744             wxRichTextEvent cmdEvent(
745                 wxEVT_RICHTEXT_LEFT_CLICK,
746                 GetId());
747             cmdEvent.SetEventObject(this);
748             cmdEvent.SetPosition(position);
749             if (hitObj)
750                 cmdEvent.SetContainer(hitObj->GetContainer());
751 
752             if (!GetEventHandler()->ProcessEvent(cmdEvent))
753             {
754                 wxRichTextAttr attr;
755                 if (GetStyle(position, attr))
756                 {
757                     if (attr.HasFlag(wxTEXT_ATTR_URL))
758                     {
759                         wxString urlTarget = attr.GetURL();
760                         if (!urlTarget.IsEmpty())
761                         {
762                             wxMouseEvent mouseEvent(event);
763 
764                             long startPos = 0, endPos = 0;
765                             wxRichTextObject* obj = GetFocusObject()->GetLeafObjectAtPosition(position);
766                             if (obj)
767                             {
768                                 startPos = obj->GetRange().GetStart();
769                                 endPos = obj->GetRange().GetEnd();
770                             }
771 
772                             wxTextUrlEvent urlEvent(GetId(), mouseEvent, startPos, endPos);
773                             InitCommandEvent(urlEvent);
774 
775                             urlEvent.SetString(urlTarget);
776 
777                             GetEventHandler()->ProcessEvent(urlEvent);
778                         }
779                     }
780                 }
781             }
782         }
783     }
784 
785 #if wxUSE_DRAG_AND_DROP
786     m_preDrag = false;
787 #endif // wxUSE_DRAG_AND_DROP
788 
789 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ && wxHAVE_PRIMARY_SELECTION
790     if (HasSelection() && GetFocusObject() && GetFocusObject()->GetBuffer())
791     {
792         // Put the selection in PRIMARY, if it exists
793         wxTheClipboard->UsePrimarySelection(true);
794 
795         wxRichTextRange range = GetInternalSelectionRange();
796         GetFocusObject()->GetBuffer()->CopyToClipboard(range);
797 
798         wxTheClipboard->UsePrimarySelection(false);
799     }
800 #endif
801 }
802 
803 /// Mouse-movements
OnMoveMouse(wxMouseEvent & event)804 void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
805 {
806     if (!event.Dragging() && m_dragging)
807     {
808         // We may have accidentally lost a mouse-up event, especially on Linux
809         m_dragging = false;
810         if (GetCapture() == this)
811             ReleaseMouse();
812     }
813 
814 #if wxUSE_DRAG_AND_DROP
815     size_t distance = 0;
816     if (m_preDrag || m_dragging)
817     {
818         int x = m_dragStartPoint.x - event.GetPosition().x;
819         int y = m_dragStartPoint.y - event.GetPosition().y;
820         distance = abs(x) + abs(y);
821     }
822 
823     // See if we're starting Drag'n'Drop
824     if (m_preDrag)
825     {
826 #if wxUSE_DATETIME
827         wxTimeSpan diff = wxDateTime::UNow() - m_dragStartTime;
828 #endif
829         if ((distance > 10)
830 #if wxUSE_DATETIME
831              && (diff.GetMilliseconds() > 100)
832 #endif
833            )
834         {
835             m_dragging = false;
836 
837             // Start drag'n'drop
838             wxRichTextRange range = GetInternalSelectionRange();
839             if (range == wxRICHTEXT_NONE)
840             {
841               // Don't try to drag an empty range
842               m_preDrag = false;
843               return;
844             }
845 
846             // Cache the current situation, to be restored if Drag'n'Drop is cancelled
847             long oldPos = GetCaretPosition();
848             wxRichTextParagraphLayoutBox* oldFocus = GetFocusObject();
849 
850             wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
851             wxString text = GetFocusObject()->GetTextForRange(range);
852 #ifdef __WXMSW__
853             text = wxTextFile::Translate(text, wxTextFileType_Dos);
854 #endif
855             compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
856 
857             wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
858             GetFocusObject()->CopyFragment(range, *richTextBuf);
859             compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
860 
861             wxRichTextDropSource source(*compositeObject, this);
862             // Use wxDrag_DefaultMove, not because it's the likelier choice but because pressing Ctrl for Copy obeys the principle of least surprise
863             // The alternative, wxDrag_DefaultCopy, requires the user to know that Move needs the Shift key pressed
864             BeginBatchUndo(_("Drag"));
865             switch (source.DoDragDrop(wxDrag_AllowMove | wxDrag_DefaultMove))
866             {
867                 case wxDragMove:
868                 case wxDragCopy:  break;
869 
870                 case wxDragError:
871                     wxLogError(wxT("An error occurred during drag and drop operation"));
872                     wxFALLTHROUGH;
873                 case wxDragNone:
874                 case wxDragCancel:
875                     Refresh(); // This is needed in wxMSW, otherwise resetting the position doesn't 'take'
876                     SetCaretPosition(oldPos);
877                     SetFocusObject(oldFocus, false);
878                     wxFALLTHROUGH;
879                 default: break;
880             }
881             EndBatchUndo();
882 
883             m_preDrag = false;
884             return;
885         }
886     }
887 #endif // wxUSE_DRAG_AND_DROP
888 
889     wxClientDC dc(this);
890     PrepareDC(dc);
891     dc.SetFont(GetFont());
892 
893     long position = 0;
894     wxPoint logicalPt = event.GetLogicalPosition(dc);
895     wxRichTextObject* hitObj = NULL;
896     wxRichTextObject* contextObj = NULL;
897 
898     int flags = 0;
899 
900     // If we're dragging, let's only consider positions at this level; otherwise
901     // selecting a range is not going to work.
902     wxRichTextParagraphLayoutBox* container = & GetBuffer();
903     if (m_dragging)
904     {
905         flags = wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS;
906         container = GetFocusObject();
907     }
908     wxRichTextDrawingContext context(& GetBuffer());
909     int hit = container->HitTest(dc, context, GetUnscaledPoint(logicalPt), position, & hitObj, & contextObj, flags);
910 
911     // See if we need to change the cursor
912 
913     {
914         if (hit != wxRICHTEXT_HITTEST_NONE && !(hit & wxRICHTEXT_HITTEST_OUTSIDE) && hitObj)
915         {
916             wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
917             if (actualContainer)
918                 ProcessMouseMovement(actualContainer, hitObj, position, logicalPt);
919         }
920         else
921             SetCursor(m_textCursor);
922     }
923 
924     if (!event.Dragging())
925     {
926         event.Skip();
927         return;
928     }
929 
930     if (m_dragging
931 #if wxUSE_DRAG_AND_DROP
932         && !m_preDrag
933         && (distance > 4)
934 #endif
935         )
936     {
937         wxRichTextParagraphLayoutBox* commonAncestor = NULL;
938         wxRichTextParagraphLayoutBox* otherContainer = NULL;
939         wxRichTextParagraphLayoutBox* firstContainer = NULL;
940 
941         // Check for dragging across multiple containers
942         long position2 = 0;
943         wxRichTextObject* hitObj2 = NULL, *contextObj2 = NULL;
944         int hit2 = GetBuffer().HitTest(dc, context, GetUnscaledPoint(logicalPt), position2, & hitObj2, & contextObj2, 0);
945         if (hit2 != wxRICHTEXT_HITTEST_NONE && hitObj2 && hitObj != hitObj2)
946         {
947             // See if we can find a common ancestor
948             if (m_selectionState == wxRichTextCtrlSelectionState_Normal)
949             {
950                 firstContainer = GetFocusObject();
951                 commonAncestor = wxDynamicCast(firstContainer->GetParent(), wxRichTextParagraphLayoutBox);
952             }
953             else
954             {
955                 firstContainer = wxDynamicCast(m_selectionAnchorObject, wxRichTextParagraphLayoutBox);
956                 //commonAncestor = GetFocusObject(); // when the selection state is not normal, the focus object (e.g. table)
957                                                    // is the common ancestor.
958                 commonAncestor = wxDynamicCast(firstContainer->GetParent(), wxRichTextParagraphLayoutBox);
959             }
960 
961             if (commonAncestor && commonAncestor->HandlesChildSelections())
962             {
963                 wxRichTextObject* p = hitObj2;
964                 while (p)
965                 {
966                     if (p->GetParent() == commonAncestor)
967                     {
968                         otherContainer = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
969                         break;
970                     }
971                     p = p->GetParent();
972                 }
973             }
974 
975             if (commonAncestor && firstContainer && otherContainer)
976             {
977                 // We have now got a second container that shares a parent with the current or anchor object.
978                 if (m_selectionState == wxRichTextCtrlSelectionState_Normal)
979                 {
980                     // Don't go into common-ancestor selection mode if we still have the same
981                     // container.
982                     if (otherContainer != firstContainer)
983                     {
984                         m_selectionState = wxRichTextCtrlSelectionState_CommonAncestor;
985                         m_selectionAnchorObject = firstContainer;
986                         m_selectionAnchor = firstContainer->GetRange().GetStart();
987 
988                         // The common ancestor, such as a table, returns the cell selection
989                         // between the anchor and current position.
990                         m_selection = commonAncestor->GetSelection(m_selectionAnchor, otherContainer->GetRange().GetStart());
991                     }
992                 }
993                 else
994                 {
995                     m_selection = commonAncestor->GetSelection(m_selectionAnchor, otherContainer->GetRange().GetStart());
996                 }
997 
998                 Refresh();
999 
1000                 if (otherContainer->AcceptsFocus())
1001                     SetFocusObject(otherContainer, false /* don't set caret and clear selection */);
1002                 MoveCaret(-1, false);
1003                 SetDefaultStyleToCursorStyle();
1004             }
1005         }
1006     }
1007 
1008     if (hitObj && m_dragging && hit != wxRICHTEXT_HITTEST_NONE && m_selectionState == wxRichTextCtrlSelectionState_Normal
1009 #if wxUSE_DRAG_AND_DROP
1010         && !m_preDrag
1011         && (distance > 4)
1012 #endif
1013         // Don't select to the end of the container when going outside the window
1014         // For analysis, see https://trac.wxwidgets.org/ticket/15714
1015         && (! (hitObj == (& m_buffer) && ((hit & wxRICHTEXT_HITTEST_OUTSIDE) != 0)))
1016         )
1017     {
1018         SetCaretPositionAfterClick(container, position, hit, true /* extend selection */);
1019     }
1020 }
1021 
1022 /// Right-click
OnRightClick(wxMouseEvent & event)1023 void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
1024 {
1025     SetFocus();
1026 
1027     wxClientDC dc(this);
1028     PrepareDC(dc);
1029     dc.SetFont(GetFont());
1030 
1031     long position = 0;
1032     wxPoint logicalPt = event.GetLogicalPosition(dc);
1033     wxRichTextObject* hitObj = NULL;
1034     wxRichTextObject* contextObj = NULL;
1035     wxRichTextDrawingContext context(& GetBuffer());
1036     int hit = GetFocusObject()->HitTest(dc, context, GetUnscaledPoint(logicalPt), position, & hitObj, & contextObj, wxRICHTEXT_HITTEST_HONOUR_ATOMIC);
1037 
1038     if (hitObj && hitObj->GetContainer() != GetFocusObject())
1039     {
1040         wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
1041         if (actualContainer && actualContainer->AcceptsFocus())
1042         {
1043             SetFocusObject(actualContainer, false /* don't set caret position yet */);
1044             SetCaretPositionAfterClick(actualContainer, position, hit);
1045         }
1046     }
1047 
1048     wxRichTextEvent cmdEvent(
1049         wxEVT_RICHTEXT_RIGHT_CLICK,
1050         GetId());
1051     cmdEvent.SetEventObject(this);
1052     cmdEvent.SetPosition(position);
1053     if (hitObj)
1054         cmdEvent.SetContainer(hitObj->GetContainer());
1055 
1056     if (!GetEventHandler()->ProcessEvent(cmdEvent))
1057         event.Skip();
1058 }
1059 
1060 /// Left-double-click
OnLeftDClick(wxMouseEvent & event)1061 void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
1062 {
1063     wxRichTextEvent cmdEvent(
1064         wxEVT_RICHTEXT_LEFT_DCLICK,
1065         GetId());
1066     cmdEvent.SetEventObject(this);
1067     cmdEvent.SetPosition(m_caretPosition+1);
1068     cmdEvent.SetContainer(GetFocusObject());
1069 
1070     if (!GetEventHandler()->ProcessEvent(cmdEvent))
1071     {
1072         bool okToSelectWord = true;
1073         // Don't try to select a word if we clicked on a floating objects such as an image.
1074         // Instead, select or deselect the object.
1075         if (wxRichTextBuffer::GetFloatingLayoutMode())
1076         {
1077             wxClientDC dc(this);
1078             PrepareDC(dc);
1079             dc.SetFont(GetFont());
1080 
1081             long position = 0;
1082             wxPoint logicalPt = event.GetLogicalPosition(dc);
1083             wxRichTextObject* hitObj = NULL;
1084             wxRichTextObject* contextObj = NULL;
1085             wxRichTextDrawingContext context(& GetBuffer());
1086             int hit = GetFocusObject()->HitTest(dc, context, GetUnscaledPoint(logicalPt), position, & hitObj, & contextObj, wxRICHTEXT_HITTEST_HONOUR_ATOMIC);
1087             wxUnusedVar(hit);
1088             if (hitObj && hitObj->IsFloating() && !hitObj->AcceptsFocus())
1089             {
1090                 if ((GetFocusObject() == m_selection.GetContainer()) && m_selection.WithinSelection(hitObj->GetRange().GetStart()))
1091                 {
1092                 }
1093                 else
1094                 {
1095                     int from = hitObj->GetRange().GetStart();
1096                     int to = hitObj->GetRange().GetStart()+1;
1097 
1098                     wxRichTextSelection oldSelection = m_selection;
1099                     m_selectionAnchor = from-1;
1100                     m_selectionAnchorObject = NULL;
1101                     m_selection.Set(wxRichTextRange(from, to-1), GetFocusObject());
1102                     RefreshForSelectionChange(oldSelection, m_selection);
1103                 }
1104                 okToSelectWord = false;
1105             }
1106         }
1107         if (okToSelectWord)
1108         {
1109             SelectWord(GetCaretPosition()+1);
1110         }
1111     }
1112 }
1113 
1114 /// Middle-click
OnMiddleClick(wxMouseEvent & event)1115 void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
1116 {
1117     wxRichTextEvent cmdEvent(
1118         wxEVT_RICHTEXT_MIDDLE_CLICK,
1119         GetId());
1120     cmdEvent.SetEventObject(this);
1121     cmdEvent.SetPosition(m_caretPosition+1);
1122     cmdEvent.SetContainer(GetFocusObject());
1123 
1124     if (!GetEventHandler()->ProcessEvent(cmdEvent))
1125         event.Skip();
1126 
1127 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ && wxHAVE_PRIMARY_SELECTION
1128     // Paste any PRIMARY selection, if it exists
1129     wxTheClipboard->UsePrimarySelection(true);
1130     Paste();
1131     wxTheClipboard->UsePrimarySelection(false);
1132 #endif
1133 }
1134 
1135 /// Key press
OnChar(wxKeyEvent & event)1136 void wxRichTextCtrl::OnChar(wxKeyEvent& event)
1137 {
1138     int flags = 0;
1139     if (event.CmdDown())
1140         flags |= wxRICHTEXT_CTRL_DOWN;
1141     if (event.ShiftDown())
1142         flags |= wxRICHTEXT_SHIFT_DOWN;
1143     if (event.AltDown())
1144         flags |= wxRICHTEXT_ALT_DOWN;
1145 
1146     if (event.GetEventType() == wxEVT_KEY_DOWN)
1147     {
1148         if (event.IsKeyInCategory(WXK_CATEGORY_NAVIGATION))
1149         {
1150             KeyboardNavigate(event.GetKeyCode(), flags);
1151             return;
1152         }
1153 
1154         long keycode = event.GetKeyCode();
1155         switch ( keycode )
1156         {
1157             case WXK_ESCAPE:
1158             case WXK_START:
1159             case WXK_LBUTTON:
1160             case WXK_RBUTTON:
1161             case WXK_CANCEL:
1162             case WXK_MBUTTON:
1163             case WXK_CLEAR:
1164             case WXK_SHIFT:
1165             case WXK_ALT:
1166             case WXK_CONTROL:
1167             case WXK_PAUSE:
1168             case WXK_CAPITAL:
1169             case WXK_END:
1170             case WXK_HOME:
1171             case WXK_LEFT:
1172             case WXK_UP:
1173             case WXK_RIGHT:
1174             case WXK_DOWN:
1175             case WXK_SELECT:
1176             case WXK_PRINT:
1177             case WXK_EXECUTE:
1178             case WXK_SNAPSHOT:
1179             case WXK_INSERT:
1180             case WXK_HELP:
1181             case WXK_F1:
1182             case WXK_F2:
1183             case WXK_F3:
1184             case WXK_F4:
1185             case WXK_F5:
1186             case WXK_F6:
1187             case WXK_F7:
1188             case WXK_F8:
1189             case WXK_F9:
1190             case WXK_F10:
1191             case WXK_F11:
1192             case WXK_F12:
1193             case WXK_F13:
1194             case WXK_F14:
1195             case WXK_F15:
1196             case WXK_F16:
1197             case WXK_F17:
1198             case WXK_F18:
1199             case WXK_F19:
1200             case WXK_F20:
1201             case WXK_F21:
1202             case WXK_F22:
1203             case WXK_F23:
1204             case WXK_F24:
1205             case WXK_NUMLOCK:
1206             case WXK_SCROLL:
1207             case WXK_PAGEUP:
1208             case WXK_PAGEDOWN:
1209             case WXK_NUMPAD_F1:
1210             case WXK_NUMPAD_F2:
1211             case WXK_NUMPAD_F3:
1212             case WXK_NUMPAD_F4:
1213             case WXK_NUMPAD_HOME:
1214             case WXK_NUMPAD_LEFT:
1215             case WXK_NUMPAD_UP:
1216             case WXK_NUMPAD_RIGHT:
1217             case WXK_NUMPAD_DOWN:
1218             case WXK_NUMPAD_PAGEUP:
1219             case WXK_NUMPAD_PAGEDOWN:
1220             case WXK_NUMPAD_END:
1221             case WXK_NUMPAD_BEGIN:
1222             case WXK_NUMPAD_INSERT:
1223             case WXK_WINDOWS_LEFT:
1224             case WXK_BROWSER_BACK:
1225             case WXK_BROWSER_FORWARD:
1226             case WXK_BROWSER_REFRESH:
1227             case WXK_BROWSER_STOP:
1228             case WXK_BROWSER_SEARCH:
1229             case WXK_BROWSER_FAVORITES:
1230             case WXK_BROWSER_HOME:
1231             case WXK_VOLUME_MUTE:
1232             case WXK_VOLUME_DOWN:
1233             case WXK_VOLUME_UP:
1234             case WXK_MEDIA_NEXT_TRACK:
1235             case WXK_MEDIA_PREV_TRACK:
1236             case WXK_MEDIA_STOP:
1237             case WXK_MEDIA_PLAY_PAUSE:
1238             case WXK_LAUNCH_MAIL:
1239             case WXK_LAUNCH_APP1:
1240             case WXK_LAUNCH_APP2:
1241             {
1242                 return;
1243             }
1244             case WXK_MENU:
1245             {
1246                 // Necessary for the context menu to work on wxGTK
1247                 event.Skip();
1248                 return;
1249             }
1250             default:
1251             {
1252             }
1253         }
1254 
1255         // Must process this before translation, otherwise it's translated into a WXK_DELETE event.
1256         if (event.CmdDown() && event.GetKeyCode() == WXK_BACK)
1257         {
1258             if (!ProcessBackKey(event, flags))
1259                 return;
1260         }
1261         else
1262             event.Skip();
1263 
1264         return;
1265     }
1266 
1267     // all the other keys modify the controls contents which shouldn't be
1268     // possible if we're read-only
1269     if ( !IsEditable() )
1270     {
1271         event.Skip();
1272         return;
1273     }
1274 
1275     if (event.GetKeyCode() == WXK_RETURN)
1276     {
1277         if (!CanInsertContent(* GetFocusObject(), m_caretPosition+1))
1278             return;
1279 
1280         long newPos = m_caretPosition;
1281 
1282         if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
1283         {
1284             return;
1285         }
1286 
1287         BeginBatchUndo(_("Insert Text"));
1288 
1289         DeleteSelectedContent(& newPos);
1290 
1291         if (event.ShiftDown())
1292         {
1293             wxString text;
1294             text = wxRichTextLineBreakChar;
1295             GetFocusObject()->InsertTextWithUndo(& GetBuffer(), newPos+1, text, this);
1296             m_caretAtLineStart = true;
1297             PositionCaret();
1298         }
1299         else
1300             GetFocusObject()->InsertNewlineWithUndo(& GetBuffer(), newPos+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE|wxRICHTEXT_INSERT_INTERACTIVE);
1301 
1302         // Automatically renumber list
1303         bool isNumberedList = false;
1304         wxRichTextRange numberedListRange = FindRangeForList(newPos+1, isNumberedList);
1305         if (isNumberedList && numberedListRange != wxRichTextRange(-1, -1))
1306         {
1307             NumberList(numberedListRange, NULL, wxRICHTEXT_SETSTYLE_RENUMBER|wxRICHTEXT_SETSTYLE_WITH_UNDO);
1308         }
1309 
1310         EndBatchUndo();
1311         SetDefaultStyleToCursorStyle();
1312 
1313         ScrollIntoView(m_caretPosition, WXK_RIGHT);
1314 
1315         wxRichTextEvent cmdEvent(
1316             wxEVT_RICHTEXT_RETURN,
1317             GetId());
1318         cmdEvent.SetEventObject(this);
1319         cmdEvent.SetFlags(flags);
1320         cmdEvent.SetPosition(newPos+1);
1321         cmdEvent.SetContainer(GetFocusObject());
1322 
1323         if (!GetEventHandler()->ProcessEvent(cmdEvent))
1324         {
1325             // Generate conventional event
1326             wxCommandEvent textEvent(wxEVT_TEXT_ENTER, GetId());
1327             InitCommandEvent(textEvent);
1328 
1329             GetEventHandler()->ProcessEvent(textEvent);
1330         }
1331         Update();
1332     }
1333     else if (event.GetKeyCode() == WXK_BACK)
1334     {
1335         ProcessBackKey(event, flags);
1336     }
1337     else if (event.GetKeyCode() == WXK_DELETE)
1338     {
1339         long newPos = m_caretPosition;
1340 
1341         if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
1342         {
1343             return;
1344         }
1345 
1346         BeginBatchUndo(_("Delete Text"));
1347 
1348         bool processed = DeleteSelectedContent(& newPos);
1349 
1350         int deletions = 0;
1351         if (processed)
1352             deletions ++;
1353 
1354         // Submit range in character positions, which are greater than caret positions,
1355         if (newPos < GetFocusObject()->GetOwnRange().GetEnd()+1)
1356         {
1357             if (event.CmdDown())
1358             {
1359                 long pos = wxRichTextCtrl::FindNextWordPosition(1);
1360                 if (pos != -1 && (pos > newPos))
1361                 {
1362                     wxRichTextRange range(newPos+1, pos);
1363                     if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
1364                     {
1365                         GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1366                         deletions ++;
1367                     }
1368                     processed = true;
1369                 }
1370             }
1371 
1372             if (!processed && newPos < (GetLastPosition()-1))
1373             {
1374                 wxRichTextRange range(newPos+1, newPos+1);
1375                 if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
1376                 {
1377                     GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1378                     deletions ++;
1379                 }
1380             }
1381         }
1382 
1383         EndBatchUndo();
1384 
1385         if (GetLastPosition() == -1)
1386         {
1387             GetFocusObject()->Reset();
1388 
1389             m_caretPosition = -1;
1390             PositionCaret();
1391             SetDefaultStyleToCursorStyle();
1392         }
1393 
1394         ScrollIntoView(m_caretPosition, WXK_LEFT);
1395 
1396         // Always send this event; wxEVT_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
1397         {
1398             wxRichTextEvent cmdEvent(
1399                 wxEVT_RICHTEXT_DELETE,
1400                 GetId());
1401             cmdEvent.SetEventObject(this);
1402             cmdEvent.SetFlags(flags);
1403             cmdEvent.SetPosition(m_caretPosition+1);
1404             cmdEvent.SetContainer(GetFocusObject());
1405             GetEventHandler()->ProcessEvent(cmdEvent);
1406         }
1407 
1408         Update();
1409     }
1410     else
1411     {
1412         long keycode = event.GetKeyCode();
1413         switch ( keycode )
1414         {
1415             case WXK_ESCAPE:
1416             case WXK_MENU: // Necessary for the context menu to work on wxGTK
1417             {
1418                 event.Skip();
1419                 return;
1420             }
1421 
1422             default:
1423             {
1424 #ifdef __WXMAC__
1425                 if (event.CmdDown())
1426 #else
1427                 // Fixes AltGr+key with European input languages on Windows
1428                 if ((event.CmdDown() && !event.AltDown()) || (event.AltDown() && !event.CmdDown()))
1429 #endif
1430                 {
1431                     event.Skip();
1432                     return;
1433                 }
1434 
1435                 wxRichTextEvent cmdEvent1(
1436                     wxEVT_RICHTEXT_CONSUMING_CHARACTER,
1437                     GetId());
1438                 cmdEvent1.SetEventObject(this);
1439                 cmdEvent1.SetFlags(flags);
1440 #if wxUSE_UNICODE
1441                 cmdEvent1.SetCharacter(event.GetUnicodeKey());
1442 #else
1443                 cmdEvent1.SetCharacter((wxChar) keycode);
1444 #endif
1445                 cmdEvent1.SetPosition(m_caretPosition+1);
1446                 cmdEvent1.SetContainer(GetFocusObject());
1447                 if (GetEventHandler()->ProcessEvent(cmdEvent1) && !cmdEvent1.IsAllowed())
1448                 {
1449                     return;
1450                 }
1451 
1452                 wxRichTextEvent cmdEvent(
1453                     wxEVT_RICHTEXT_CHARACTER,
1454                     GetId());
1455                 cmdEvent.SetEventObject(this);
1456                 cmdEvent.SetFlags(flags);
1457 #if wxUSE_UNICODE
1458                 cmdEvent.SetCharacter(event.GetUnicodeKey());
1459 #else
1460                 cmdEvent.SetCharacter((wxChar) keycode);
1461 #endif
1462                 cmdEvent.SetPosition(m_caretPosition+1);
1463                 cmdEvent.SetContainer(GetFocusObject());
1464 
1465                 if (keycode == wxT('\t'))
1466                 {
1467                     // See if we need to promote or demote the selection or paragraph at the cursor
1468                     // position, instead of inserting a tab.
1469                     long pos = GetAdjustedCaretPosition(GetCaretPosition());
1470                     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos);
1471                     if (para && para->GetRange().GetStart() == pos && para->GetAttributes().HasListStyleName())
1472                     {
1473                         wxRichTextRange range;
1474                         if (HasSelection())
1475                             range = GetSelectionRange();
1476                         else
1477                             range = para->GetRange().FromInternal();
1478 
1479                         int promoteBy = event.ShiftDown() ? 1 : -1;
1480 
1481                         PromoteList(promoteBy, range, NULL);
1482 
1483                         GetEventHandler()->ProcessEvent(cmdEvent);
1484 
1485                         return;
1486                     }
1487                 }
1488 
1489                 if (!CanInsertContent(* GetFocusObject(), m_caretPosition+1))
1490                     return;
1491 
1492                 if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
1493                     return;
1494 
1495                 BeginBatchUndo(_("Insert Text"));
1496 
1497                 long newPos = m_caretPosition;
1498                 DeleteSelectedContent(& newPos);
1499 
1500 #if wxUSE_UNICODE
1501                 wxString str = event.GetUnicodeKey();
1502 #else
1503                 wxString str = (wxChar) event.GetKeyCode();
1504 #endif
1505                 GetFocusObject()->InsertTextWithUndo(& GetBuffer(), newPos+1, str, this, 0);
1506 
1507                 EndBatchUndo();
1508 
1509                 SetDefaultStyleToCursorStyle();
1510                 ScrollIntoView(m_caretPosition, WXK_RIGHT);
1511 
1512                 cmdEvent.SetPosition(m_caretPosition);
1513                 GetEventHandler()->ProcessEvent(cmdEvent);
1514 
1515                 Update();
1516             }
1517         }
1518     }
1519 }
1520 
ProcessMouseMovement(wxRichTextParagraphLayoutBox * container,wxRichTextObject * WXUNUSED (obj),long position,const wxPoint & WXUNUSED (pos))1521 bool wxRichTextCtrl::ProcessMouseMovement(wxRichTextParagraphLayoutBox* container, wxRichTextObject* WXUNUSED(obj), long position, const wxPoint& WXUNUSED(pos))
1522 {
1523     wxRichTextAttr attr;
1524     if (container && GetStyle(position, attr, container))
1525     {
1526         if (attr.HasFlag(wxTEXT_ATTR_URL))
1527         {
1528             SetCursor(m_urlCursor);
1529         }
1530         else if (!attr.HasFlag(wxTEXT_ATTR_URL))
1531         {
1532             SetCursor(m_textCursor);
1533         }
1534         return true;
1535     }
1536     else
1537         return false;
1538 }
1539 
1540 // Processes the back key
ProcessBackKey(wxKeyEvent & event,int flags)1541 bool wxRichTextCtrl::ProcessBackKey(wxKeyEvent& event, int flags)
1542 {
1543     if (!IsEditable())
1544     {
1545         return false;
1546     }
1547 
1548     if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
1549     {
1550         return false;
1551     }
1552 
1553     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
1554 
1555     // If we're at the start of a list item with a bullet, let's 'delete' the bullet, i.e.
1556     // make it a continuation paragraph.
1557     if (!HasSelection() && para && ((m_caretPosition+1) == para->GetRange().GetStart()) &&
1558         para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
1559     {
1560         wxRichTextParagraph* newPara = wxDynamicCast(para->Clone(), wxRichTextParagraph);
1561         newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle() | wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
1562 
1563         wxRichTextAction* action = new wxRichTextAction(NULL, _("Remove Bullet"), wxRICHTEXT_CHANGE_STYLE, & GetBuffer(), GetFocusObject(), this);
1564         action->SetRange(newPara->GetRange());
1565         action->SetPosition(GetCaretPosition());
1566         action->GetNewParagraphs().AppendChild(newPara);
1567         // Also store the old ones for Undo
1568         action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
1569 
1570         GetBuffer().Invalidate(para->GetRange());
1571         GetBuffer().SubmitAction(action);
1572 
1573         // Automatically renumber list
1574         bool isNumberedList = false;
1575         wxRichTextRange numberedListRange = FindRangeForList(m_caretPosition, isNumberedList);
1576         if (isNumberedList && numberedListRange != wxRichTextRange(-1, -1))
1577         {
1578             NumberList(numberedListRange, NULL, wxRICHTEXT_SETSTYLE_RENUMBER|wxRICHTEXT_SETSTYLE_WITH_UNDO);
1579         }
1580 
1581         Update();
1582     }
1583     else
1584     {
1585         BeginBatchUndo(_("Delete Text"));
1586 
1587         long newPos = m_caretPosition;
1588 
1589         bool processed = DeleteSelectedContent(& newPos);
1590 
1591         int deletions = 0;
1592         if (processed)
1593             deletions ++;
1594 
1595         // Submit range in character positions, which are greater than caret positions,
1596         // so subtract 1 for deleted character and add 1 for conversion to character position.
1597         if (newPos > -1)
1598         {
1599             if (event.CmdDown())
1600             {
1601                 long pos = wxRichTextCtrl::FindNextWordPosition(-1);
1602                 if (pos < newPos)
1603                 {
1604                     wxRichTextRange range(pos+1, newPos);
1605                     if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
1606                     {
1607                         GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1608                         deletions ++;
1609                     }
1610                     processed = true;
1611                 }
1612             }
1613 
1614             if (!processed)
1615             {
1616                 wxRichTextRange range(newPos, newPos);
1617                 if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
1618                 {
1619                     GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1620                     deletions ++;
1621                 }
1622             }
1623         }
1624 
1625         EndBatchUndo();
1626 
1627         if (GetLastPosition() == -1)
1628         {
1629             GetFocusObject()->Reset();
1630 
1631             m_caretPosition = -1;
1632             PositionCaret();
1633             SetDefaultStyleToCursorStyle();
1634         }
1635 
1636         ScrollIntoView(m_caretPosition, WXK_LEFT);
1637 
1638         // Always send this event; wxEVT_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
1639         {
1640             wxRichTextEvent cmdEvent(
1641                 wxEVT_RICHTEXT_DELETE,
1642                 GetId());
1643             cmdEvent.SetEventObject(this);
1644             cmdEvent.SetFlags(flags);
1645             cmdEvent.SetPosition(m_caretPosition+1);
1646             cmdEvent.SetContainer(GetFocusObject());
1647             GetEventHandler()->ProcessEvent(cmdEvent);
1648         }
1649 
1650         Update();
1651     }
1652 
1653     return true;
1654 }
1655 
1656 /// Delete content if there is a selection, e.g. when pressing a key.
DeleteSelectedContent(long * newPos)1657 bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
1658 {
1659     if (HasSelection())
1660     {
1661         long pos = m_selection.GetRange().GetStart();
1662         wxRichTextRange range = m_selection.GetRange();
1663 
1664         // SelectAll causes more to be selected than doing it interactively,
1665         // and causes a new paragraph to be inserted. So for multiline buffers,
1666         // don't delete the final position.
1667         if (range.GetEnd() == GetLastPosition() && GetNumberOfLines() > 0)
1668             range.SetEnd(range.GetEnd()-1);
1669 
1670         GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1671         m_selection.Reset();
1672         m_selectionState = wxRichTextCtrlSelectionState_Normal;
1673 
1674         if (newPos)
1675             *newPos = pos-1;
1676         return true;
1677     }
1678     else
1679         return false;
1680 }
1681 
1682 /// Keyboard navigation
1683 
1684 /*
1685 
1686 Left:       left one character
1687 Right:      right one character
1688 Up:         up one line
1689 Down:       down one line
1690 Ctrl-Left:  left one word
1691 Ctrl-Right: right one word
1692 Ctrl-Up:    previous paragraph start
1693 Ctrl-Down:  next start of paragraph
1694 Home:       start of line
1695 End:        end of line
1696 Ctrl-Home:  start of document
1697 Ctrl-End:   end of document
1698 Page-Up:    Up a screen
1699 Page-Down:  Down a screen
1700 
1701 Maybe:
1702 
1703 Ctrl-Alt-PgUp: Start of window
1704 Ctrl-Alt-PgDn: End of window
1705 F8:         Start selection mode
1706 Esc:        End selection mode
1707 
1708 Adding Shift does the above but starts/extends selection.
1709 
1710 
1711  */
1712 
KeyboardNavigate(int keyCode,int flags)1713 bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags)
1714 {
1715     bool success = false;
1716 
1717     if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT)
1718     {
1719         if (flags & wxRICHTEXT_CTRL_DOWN)
1720             success = WordRight(1, flags);
1721         else
1722             success = MoveRight(1, flags);
1723     }
1724     else if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT)
1725     {
1726         if (flags & wxRICHTEXT_CTRL_DOWN)
1727             success = WordLeft(1, flags);
1728         else
1729             success = MoveLeft(1, flags);
1730     }
1731     else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP)
1732     {
1733         if (flags & wxRICHTEXT_CTRL_DOWN)
1734             success = MoveToParagraphStart(flags);
1735         else
1736             success = MoveUp(1, flags);
1737     }
1738     else if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN)
1739     {
1740         if (flags & wxRICHTEXT_CTRL_DOWN)
1741             success = MoveToParagraphEnd(flags);
1742         else
1743             success = MoveDown(1, flags);
1744     }
1745     else if (keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP)
1746     {
1747         success = PageUp(1, flags);
1748     }
1749     else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1750     {
1751         success = PageDown(1, flags);
1752     }
1753     else if (keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME)
1754     {
1755         if (flags & wxRICHTEXT_CTRL_DOWN)
1756             success = MoveHome(flags);
1757         else
1758             success = MoveToLineStart(flags);
1759     }
1760     else if (keyCode == WXK_END || keyCode == WXK_NUMPAD_END)
1761     {
1762         if (flags & wxRICHTEXT_CTRL_DOWN)
1763             success = MoveEnd(flags);
1764         else
1765             success = MoveToLineEnd(flags);
1766     }
1767 
1768     if (success)
1769     {
1770         ScrollIntoView(m_caretPosition, keyCode);
1771         SetDefaultStyleToCursorStyle();
1772     }
1773 
1774     return success;
1775 }
1776 
1777 /// Extend the selection. Selections are in caret positions.
ExtendSelection(long oldPos,long newPos,int flags)1778 bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
1779 {
1780     if (flags & wxRICHTEXT_SHIFT_DOWN)
1781     {
1782         if (oldPos == newPos)
1783             return false;
1784 
1785         wxRichTextSelection oldSelection = m_selection;
1786 
1787         m_selection.SetContainer(GetFocusObject());
1788 
1789         wxRichTextRange oldRange;
1790         if (m_selection.IsValid())
1791             oldRange = m_selection.GetRange();
1792         else
1793             oldRange = wxRICHTEXT_NO_SELECTION;
1794         wxRichTextRange newRange;
1795 
1796         // If not currently selecting, start selecting
1797         if (oldRange.GetStart() == -2)
1798         {
1799             m_selectionAnchor = oldPos;
1800 
1801             if (oldPos > newPos)
1802                 newRange.SetRange(newPos+1, oldPos);
1803             else
1804                 newRange.SetRange(oldPos+1, newPos);
1805         }
1806         else
1807         {
1808             // Always ensure that the selection range start is greater than
1809             // the end.
1810             if (newPos > m_selectionAnchor)
1811                 newRange.SetRange(m_selectionAnchor+1, newPos);
1812             else if (newPos == m_selectionAnchor)
1813                 newRange = wxRichTextRange(-2, -2);
1814             else
1815                 newRange.SetRange(newPos+1, m_selectionAnchor);
1816         }
1817 
1818         m_selection.SetRange(newRange);
1819 
1820         RefreshForSelectionChange(oldSelection, m_selection);
1821 
1822         if (newRange.GetStart() > newRange.GetEnd())
1823         {
1824             wxLogDebug(wxT("Strange selection range"));
1825         }
1826 
1827         return true;
1828     }
1829     else
1830         return false;
1831 }
1832 
1833 /// Scroll into view, returning true if we scrolled.
1834 /// This takes a _caret_ position.
ScrollIntoView(long position,int keyCode)1835 bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
1836 {
1837     wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
1838 
1839     if (!line)
1840         return false;
1841 
1842     int ppuX, ppuY;
1843     GetScrollPixelsPerUnit(& ppuX, & ppuY);
1844 
1845     int startXUnits, startYUnits;
1846     GetViewStart(& startXUnits, & startYUnits);
1847     int startY = startYUnits * ppuY;
1848 
1849     int sx = 0, sy = 0;
1850     GetVirtualSize(& sx, & sy);
1851     int sxUnits = 0;
1852     int syUnits = 0;
1853     if (ppuY != 0)
1854         syUnits = sy/ppuY;
1855 
1856     wxRect rect = GetScaledRect(line->GetRect());
1857 
1858     bool scrolled = false;
1859 
1860     wxSize clientSize = GetClientSize();
1861 
1862     int leftMargin, rightMargin, topMargin, bottomMargin;
1863 
1864     {
1865         wxClientDC dc(this);
1866         wxRichTextObject::GetTotalMargin(dc, & GetBuffer(), GetBuffer().GetAttributes(), leftMargin, rightMargin,
1867             topMargin, bottomMargin);
1868     }
1869     clientSize.y -= (int) (0.5 + bottomMargin * GetScale());
1870 
1871     if (GetWindowStyle() & wxRE_CENTRE_CARET)
1872     {
1873         int y = rect.y - GetClientSize().y/2;
1874         int yUnits = (y + ppuY - 1) / ppuY;
1875         if (y >= 0 && (y + clientSize.y) < (int) (0.5 + GetBuffer().GetCachedSize().y * GetScale()))
1876         {
1877             if (startYUnits != yUnits)
1878             {
1879                 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1880                 scrolled = true;
1881             }
1882 #if !wxRICHTEXT_USE_OWN_CARET
1883             if (scrolled)
1884 #endif
1885                 PositionCaret();
1886 
1887             return scrolled;
1888         }
1889     }
1890 
1891     // Going down
1892     if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN ||
1893         keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT ||
1894         keyCode == WXK_END || keyCode == WXK_NUMPAD_END ||
1895         keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1896     {
1897         if ((rect.y + rect.height) > (clientSize.y + startY))
1898         {
1899             // Make it scroll so this item is at the bottom
1900             // of the window
1901             int y = rect.y - (clientSize.y - rect.height);
1902             int yUnits = (y + ppuY - 1) / ppuY;
1903 
1904             // If we're still off the screen, scroll another line down
1905             if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1906                 yUnits ++;
1907 
1908             if (startYUnits != yUnits)
1909             {
1910                 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1911                 scrolled = true;
1912             }
1913         }
1914         else if (rect.y < (startY + (int) (0.5 + GetBuffer().GetTopMargin() * GetScale())))
1915         {
1916             // Make it scroll so this item is at the top
1917             // of the window
1918             int y = rect.y - (int) (0.5 + GetBuffer().GetTopMargin() * GetScale());
1919             int yUnits = (y + ppuY - 1) / ppuY;
1920 
1921             if (startYUnits != yUnits)
1922             {
1923                 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1924                 scrolled = true;
1925             }
1926         }
1927     }
1928     // Going up
1929     else if (keyCode == WXK_UP  || keyCode == WXK_NUMPAD_UP ||
1930              keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT ||
1931              keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME ||
1932              keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP )
1933     {
1934         if (rect.y < (startY + (int) (0.5 + GetBuffer().GetBottomMargin() * GetScale())))
1935         {
1936             // Make it scroll so this item is at the top
1937             // of the window
1938             int y = rect.y - (int) (0.5 + GetBuffer().GetTopMargin() * GetScale());
1939             int yUnits = (y + ppuY - 1) / ppuY;
1940 
1941             if (startYUnits != yUnits)
1942             {
1943                 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1944                 scrolled = true;
1945             }
1946         }
1947         else if ((rect.y + rect.height) > (clientSize.y + startY))
1948         {
1949             // Make it scroll so this item is at the bottom
1950             // of the window
1951             int y = rect.y - (clientSize.y - rect.height);
1952             int yUnits = (y + ppuY - 1) / ppuY;
1953 
1954             // If we're still off the screen, scroll another line down
1955             if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1956                 yUnits ++;
1957 
1958             if (startYUnits != yUnits)
1959             {
1960                 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1961                 scrolled = true;
1962             }
1963         }
1964     }
1965 
1966 #if !wxRICHTEXT_USE_OWN_CARET
1967     if (scrolled)
1968 #endif
1969         PositionCaret();
1970 
1971     return scrolled;
1972 }
1973 
1974 /// Is the given position visible on the screen?
IsPositionVisible(long pos) const1975 bool wxRichTextCtrl::IsPositionVisible(long pos) const
1976 {
1977     wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
1978 
1979     if (!line)
1980         return false;
1981 
1982     int ppuX, ppuY;
1983     GetScrollPixelsPerUnit(& ppuX, & ppuY);
1984 
1985     int startX, startY;
1986     GetViewStart(& startX, & startY);
1987     startX = 0;
1988     startY = startY * ppuY;
1989 
1990     wxRect rect = GetScaledRect(line->GetRect());
1991     wxSize clientSize = GetClientSize();
1992 
1993     int topMargin = (int) (0.5 + GetBuffer().GetTopMargin() * GetScale());
1994     int bottomMargin = (int) (0.5 + GetBuffer().GetBottomMargin() * GetScale());
1995 
1996     return (rect.GetTop() >= (startY + topMargin)) &&
1997            (rect.GetBottom() <= (startY + clientSize.y - bottomMargin));
1998 }
1999 
SetCaretPosition(long position,bool showAtLineStart)2000 void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
2001 {
2002     m_caretPosition = position;
2003     m_caretAtLineStart = showAtLineStart;
2004 }
2005 
2006 /// Move caret one visual step forward: this may mean setting a flag
2007 /// and keeping the same position if we're going from the end of one line
2008 /// to the start of the next, which may be the exact same caret position.
MoveCaretForward(long oldPosition)2009 void wxRichTextCtrl::MoveCaretForward(long oldPosition)
2010 {
2011     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
2012 
2013     // Only do the check if we're not at the end of the paragraph (where things work OK
2014     // anyway)
2015     if (para && (oldPosition != para->GetRange().GetEnd() - 1))
2016     {
2017         wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
2018 
2019         if (line)
2020         {
2021             wxRichTextRange lineRange = line->GetAbsoluteRange();
2022 
2023             // We're at the end of a line. See whether we need to
2024             // stay at the same actual caret position but change visual
2025             // position, or not.
2026             if (oldPosition == lineRange.GetEnd())
2027             {
2028                 if (m_caretAtLineStart)
2029                 {
2030                     // We're already at the start of the line, so actually move on now.
2031                     m_caretPosition = oldPosition + 1;
2032                     m_caretAtLineStart = false;
2033                 }
2034                 else
2035                 {
2036                     // We're showing at the end of the line, so keep to
2037                     // the same position but indicate that we're to show
2038                     // at the start of the next line.
2039                     m_caretPosition = oldPosition;
2040                     m_caretAtLineStart = true;
2041                 }
2042                 SetDefaultStyleToCursorStyle();
2043                 return;
2044             }
2045         }
2046     }
2047     m_caretPosition ++;
2048     SetDefaultStyleToCursorStyle();
2049 }
2050 
2051 /// Move caret one visual step backward: this may mean setting a flag
2052 /// and keeping the same position if we're going from the end of one line
2053 /// to the start of the next, which may be the exact same caret position.
MoveCaretBack(long oldPosition)2054 void wxRichTextCtrl::MoveCaretBack(long oldPosition)
2055 {
2056     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
2057 
2058     // Only do the check if we're not at the start of the paragraph (where things work OK
2059     // anyway)
2060     if (para && (oldPosition != para->GetRange().GetStart()))
2061     {
2062         wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
2063 
2064         if (line)
2065         {
2066             wxRichTextRange lineRange = line->GetAbsoluteRange();
2067 
2068             // We're at the start of a line. See whether we need to
2069             // stay at the same actual caret position but change visual
2070             // position, or not.
2071             if (oldPosition == lineRange.GetStart())
2072             {
2073                 m_caretPosition = oldPosition-1;
2074                 m_caretAtLineStart = true;
2075                 return;
2076             }
2077             else if (oldPosition == lineRange.GetEnd())
2078             {
2079                 if (m_caretAtLineStart)
2080                 {
2081                     // We're at the start of the line, so keep the same caret position
2082                     // but clear the start-of-line flag.
2083                     m_caretPosition = oldPosition;
2084                     m_caretAtLineStart = false;
2085                 }
2086                 else
2087                 {
2088                     // We're showing at the end of the line, so go back
2089                     // to the previous character position.
2090                     m_caretPosition = oldPosition - 1;
2091                 }
2092                 SetDefaultStyleToCursorStyle();
2093                 return;
2094             }
2095         }
2096     }
2097     m_caretPosition --;
2098     SetDefaultStyleToCursorStyle();
2099 }
2100 
2101 /// Move right
MoveRight(int noPositions,int flags)2102 bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
2103 {
2104     // Test for continuing table selection
2105     if (flags & wxRICHTEXT_SHIFT_DOWN)
2106     {
2107         if (m_selection.GetContainer() && m_selection.GetContainer()->IsKindOf(CLASSINFO(wxRichTextTable)))
2108         {
2109             wxRichTextTable* table = wxDynamicCast(m_selection.GetContainer(), wxRichTextTable);
2110             if (GetFocusObject() && GetFocusObject()->GetParent() == m_selection.GetContainer())
2111             {
2112                 ExtendCellSelection(table, 0, noPositions);
2113                 return true;
2114             }
2115         }
2116     }
2117 
2118     long startPos = -1;
2119     long endPos = GetFocusObject()->GetOwnRange().GetEnd();
2120 
2121     bool beyondBottom = (noPositions > 0 && (m_caretPosition + noPositions >= endPos));
2122     bool beyondTop = (noPositions < 0 && (m_caretPosition <= startPos + noPositions + 1));
2123 
2124     if (beyondBottom || beyondTop)
2125     {
2126         wxPoint pt = GetLogicalPoint(GetCaret()->GetPosition());
2127 
2128         int hitTestFlags = wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS|wxRICHTEXT_HITTEST_HONOUR_ATOMIC;
2129 
2130         if (beyondBottom)
2131             pt.x = GetFocusObject()->GetPosition().x + GetFocusObject()->GetCachedSize().x + 2;
2132         else
2133             pt.x = GetFocusObject()->GetPosition().x - 2;
2134 
2135         pt.y += 2;
2136 
2137         long newPos = 0;
2138         wxClientDC dc(this);
2139         PrepareDC(dc);
2140         dc.SetFont(GetFont());
2141 
2142         wxRichTextObject* hitObj = NULL;
2143         wxRichTextObject* contextObj = NULL;
2144         wxRichTextDrawingContext context(& GetBuffer());
2145         int hitTest = GetBuffer().HitTest(dc, context, pt, newPos, & hitObj, & contextObj, hitTestFlags);
2146 
2147         if (hitObj &&
2148             ((hitTest & wxRICHTEXT_HITTEST_NONE) == 0) &&
2149             (! (hitObj == (& m_buffer) && ((hitTest & wxRICHTEXT_HITTEST_OUTSIDE) != 0))) // outside the buffer counts as 'do nothing'
2150             )
2151         {
2152             wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
2153             if (actualContainer && actualContainer != GetFocusObject() && actualContainer->AcceptsFocus() && actualContainer->IsShown())
2154             {
2155                 if ((flags & wxRICHTEXT_SHIFT_DOWN) &&
2156                     GetFocusObject()->IsKindOf(CLASSINFO(wxRichTextCell)) &&
2157                     actualContainer->IsKindOf(CLASSINFO(wxRichTextCell)) &&
2158                     GetFocusObject()->GetParent() == actualContainer->GetParent())
2159                 {
2160                     // Start selecting cells in a table
2161                     wxRichTextTable* table = wxDynamicCast(actualContainer->GetParent(), wxRichTextTable);
2162                     if (table)
2163                     {
2164                         StartCellSelection(table, actualContainer);
2165                         return true;
2166                     }
2167                 }
2168 
2169                 // If the new container is a cell, go to the top or bottom of it.
2170                 if (actualContainer->IsKindOf(CLASSINFO(wxRichTextCell)))
2171                 {
2172                     if (beyondBottom)
2173                         newPos = 0;
2174                     else
2175                         newPos = actualContainer->GetOwnRange().GetEnd()-1;
2176                 }
2177 
2178                 SetFocusObject(actualContainer, false /* don't set caret position yet */);
2179                 bool caretLineStart = true;
2180                 long caretPosition = FindCaretPositionForCharacterPosition(newPos, hitTest, actualContainer, caretLineStart);
2181 
2182                 SelectNone();
2183 
2184                 SetCaretPosition(caretPosition, caretLineStart);
2185                 PositionCaret();
2186                 SetDefaultStyleToCursorStyle();
2187 
2188                 return true;
2189             }
2190         }
2191     }
2192     else if (!beyondTop && !beyondBottom)
2193     {
2194         long oldPos = m_caretPosition;
2195         long newPos = m_caretPosition + noPositions;
2196 
2197         bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2198         if (!extendSel)
2199             SelectNone();
2200 
2201         // Determine by looking at oldPos and m_caretPosition whether
2202         // we moved from the end of a line to the start of the next line, in which case
2203         // we want to adjust the caret position such that it is positioned at the
2204         // start of the next line, rather than jumping past the first character of the
2205         // line.
2206         if (noPositions == 1)
2207             MoveCaretForward(oldPos);
2208         else if (noPositions == -1)
2209             MoveCaretBack(oldPos);
2210         else
2211             SetCaretPosition(newPos);
2212 
2213         PositionCaret();
2214         SetDefaultStyleToCursorStyle();
2215 
2216         return true;
2217     }
2218     return false;
2219 }
2220 
2221 /// Move left
MoveLeft(int noPositions,int flags)2222 bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
2223 {
2224     return MoveRight(-noPositions, flags);
2225 }
2226 
2227 // Find the caret position for the combination of hit-test flags and character position.
2228 // Returns the caret position and also an indication of where to place the caret (caretLineStart)
2229 // since this is ambiguous (same position used for end of line and start of next).
FindCaretPositionForCharacterPosition(long position,int hitTestFlags,wxRichTextParagraphLayoutBox * container,bool & caretLineStart)2230 long wxRichTextCtrl::FindCaretPositionForCharacterPosition(long position, int hitTestFlags, wxRichTextParagraphLayoutBox* container,
2231                                                    bool& caretLineStart)
2232 {
2233     // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
2234     // we want to be at the end of the last line but with m_caretAtLineStart set to true,
2235     // so we view the caret at the start of the line.
2236     caretLineStart = false;
2237     long caretPosition = position;
2238 
2239     if (hitTestFlags & wxRICHTEXT_HITTEST_BEFORE)
2240     {
2241         wxRichTextLine* thisLine = container->GetLineAtPosition(position-1);
2242         wxRichTextRange lineRange;
2243         if (thisLine)
2244             lineRange = thisLine->GetAbsoluteRange();
2245 
2246         if (thisLine && (position-1) == lineRange.GetEnd())
2247         {
2248             caretPosition --;
2249             caretLineStart = true;
2250         }
2251         else
2252         {
2253             wxRichTextParagraph* para = container->GetParagraphAtPosition(position);
2254             if (para && para->GetRange().GetStart() == position)
2255                 caretPosition --;
2256         }
2257     }
2258     return caretPosition;
2259 }
2260 
2261 /// Move up
MoveDown(int noLines,int flags)2262 bool wxRichTextCtrl::MoveDown(int noLines, int flags)
2263 {
2264     if (!GetCaret())
2265         return false;
2266 
2267     // Test for continuing table selection
2268     if (flags & wxRICHTEXT_SHIFT_DOWN)
2269     {
2270         if (m_selection.GetContainer() && m_selection.GetContainer()->IsKindOf(CLASSINFO(wxRichTextTable)))
2271         {
2272             wxRichTextTable* table = wxDynamicCast(m_selection.GetContainer(), wxRichTextTable);
2273             if (GetFocusObject() && GetFocusObject()->GetParent() == m_selection.GetContainer())
2274             {
2275                 ExtendCellSelection(table, noLines, 0);
2276                 return true;
2277             }
2278         }
2279     }
2280 
2281     long lineNumber = GetFocusObject()->GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
2282     wxPoint pt = GetLogicalPoint(GetCaret()->GetPosition());
2283     long newLine = lineNumber + noLines;
2284     bool notInThisObject = false;
2285 
2286     if (lineNumber != -1)
2287     {
2288         if (noLines > 0)
2289         {
2290             long lastLine = GetFocusObject()->GetVisibleLineNumber(GetFocusObject()->GetOwnRange().GetEnd());
2291             if (newLine > lastLine)
2292                 notInThisObject = true;
2293         }
2294         else
2295         {
2296             if (newLine < 0)
2297                 notInThisObject = true;
2298         }
2299     }
2300 
2301     wxRichTextParagraphLayoutBox* container = GetFocusObject();
2302     int hitTestFlags = wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS|wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS|wxRICHTEXT_HITTEST_HONOUR_ATOMIC;
2303 
2304     bool lineIsEmpty = false;
2305     if (notInThisObject)
2306     {
2307         // If we know we're navigating out of the current object,
2308         // try to find an object anywhere in the buffer at the new position (up or down a bit)
2309         container = & GetBuffer();
2310         hitTestFlags &= ~wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS;
2311 
2312         if (noLines > 0) // going down
2313         {
2314             pt.y = GetFocusObject()->GetPosition().y + GetFocusObject()->GetCachedSize().y + 2;
2315         }
2316         else // going up
2317         {
2318             pt.y = GetFocusObject()->GetPosition().y - 2;
2319         }
2320     }
2321     else
2322     {
2323         wxRichTextLine* lineObj = GetFocusObject()->GetLineForVisibleLineNumber(newLine);
2324         if (lineObj)
2325         {
2326             pt.y = lineObj->GetAbsolutePosition().y + 2;
2327             if (lineObj->GetRange().GetStart() == lineObj->GetRange().GetEnd())
2328                 lineIsEmpty = true;
2329         }
2330         else
2331             return false;
2332     }
2333 
2334     long newPos = 0;
2335     wxClientDC dc(this);
2336     PrepareDC(dc);
2337     dc.SetFont(GetFont());
2338 
2339     wxRichTextObject* hitObj = NULL;
2340     wxRichTextObject* contextObj = NULL;
2341     wxRichTextDrawingContext context(& GetBuffer());
2342     int hitTest = container->HitTest(dc, context, pt, newPos, & hitObj, & contextObj, hitTestFlags);
2343 
2344     if (hitObj &&
2345         ((hitTest & wxRICHTEXT_HITTEST_NONE) == 0) &&
2346         (! (hitObj == (& m_buffer) && ((hitTest & wxRICHTEXT_HITTEST_OUTSIDE) != 0))) // outside the buffer counts as 'do nothing'
2347         )
2348     {
2349         if (notInThisObject)
2350         {
2351             wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
2352             if (actualContainer && actualContainer != GetFocusObject() && actualContainer->AcceptsFocus())
2353             {
2354                 if ((flags & wxRICHTEXT_SHIFT_DOWN) &&
2355                     GetFocusObject()->IsKindOf(CLASSINFO(wxRichTextCell)) &&
2356                     actualContainer->IsKindOf(CLASSINFO(wxRichTextCell)) &&
2357                     GetFocusObject()->GetParent() == actualContainer->GetParent())
2358                 {
2359                     // Start selecting cells in a table
2360                     wxRichTextTable* table = wxDynamicCast(actualContainer->GetParent(), wxRichTextTable);
2361                     if (table)
2362                     {
2363                         StartCellSelection(table, actualContainer);
2364                         return true;
2365                     }
2366                 }
2367 
2368                 SetFocusObject(actualContainer, false /* don't set caret position yet */);
2369 
2370                 container = actualContainer;
2371             }
2372         }
2373 
2374         bool caretLineStart = true;
2375 
2376         // If the line is empty, there is only one possible position for the caret,
2377         // so force the 'before' state so FindCaretPositionForCharacterPosition doesn't
2378         // just return the same position.
2379         if (lineIsEmpty)
2380         {
2381             hitTest &= ~wxRICHTEXT_HITTEST_AFTER;
2382             hitTest |= wxRICHTEXT_HITTEST_BEFORE;
2383         }
2384         long caretPosition = FindCaretPositionForCharacterPosition(newPos, hitTest, container, caretLineStart);
2385         long newSelEnd = caretPosition;
2386         bool extendSel;
2387 
2388         if (notInThisObject)
2389             extendSel = false;
2390         else
2391             extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
2392 
2393         if (!extendSel)
2394             SelectNone();
2395 
2396         SetCaretPosition(caretPosition, caretLineStart);
2397         PositionCaret();
2398         SetDefaultStyleToCursorStyle();
2399 
2400         return true;
2401     }
2402 
2403     return false;
2404 }
2405 
2406 /// Extend a table selection in the given direction
ExtendCellSelection(wxRichTextTable * table,int noRowSteps,int noColSteps)2407 bool wxRichTextCtrl::ExtendCellSelection(wxRichTextTable* table, int noRowSteps, int noColSteps)
2408 {
2409     int thisRow = -1;
2410     int thisCol = -1;
2411     int r, c;
2412     for (r = 0; r < table->GetRowCount(); r ++)
2413     {
2414         for (c = 0; c < table->GetColumnCount(); c++)
2415         {
2416             wxRichTextCell* cell = table->GetCell(r, c);
2417             if (cell == GetFocusObject())
2418             {
2419                 thisRow = r;
2420                 thisCol = c;
2421             }
2422         }
2423     }
2424     if (thisRow != -1)
2425     {
2426         int newRow = wxMax(0, wxMin((thisRow + noRowSteps), table->GetRowCount()-1));
2427         int newCol = wxMax(0, wxMin((thisCol + noColSteps), table->GetColumnCount()-1));
2428 
2429         if (newRow != thisRow || newCol != thisCol)
2430         {
2431             // Make sure we're on a visible row or column
2432             r = newRow;
2433             c = newCol;
2434             int rowInc = noRowSteps > 0 ? 1 : -1;
2435             int colInc = noColSteps > 0 ? 1 : -1;
2436             bool visibleRow = false;
2437             bool visibleCol = false;
2438             if (noRowSteps != 0)
2439             {
2440                 while (r >= 0 && r < table->GetRowCount())
2441                 {
2442                     wxRichTextCell* cell = table->GetCell(r, newCol);
2443                     if (cell->IsShown())
2444                     {
2445                         newRow = r;
2446                         visibleRow = true;
2447                         break;
2448                     }
2449                     else
2450                     {
2451                         r += rowInc;
2452                     }
2453                 }
2454                 // No change if the cell would not be visible
2455                 if (!visibleRow)
2456                     return true;
2457             }
2458 
2459             if (noColSteps != 0)
2460             {
2461                 while (c >= 0 && c < table->GetColumnCount())
2462                 {
2463                     wxRichTextCell* cell = table->GetCell(newRow, c);
2464                     if (cell->IsShown())
2465                     {
2466                         newCol = c;
2467                         visibleCol = true;
2468                         break;
2469                     }
2470                     else
2471                     {
2472                         c += colInc;
2473                     }
2474                 }
2475                 // No change if the cell would not be visible
2476                 if (!visibleCol)
2477                     return true;
2478             }
2479 
2480             wxRichTextCell* newCell = table->GetCell(newRow, newCol);
2481             if (newCell)
2482             {
2483                 m_selection = table->GetSelection(m_selectionAnchor, newCell->GetRange().GetStart());
2484                 Refresh();
2485                 if (newCell->AcceptsFocus())
2486                     SetFocusObject(newCell, false);
2487                 MoveCaret(-1, false);
2488                 SetDefaultStyleToCursorStyle();
2489             }
2490         }
2491     }
2492     return true;
2493 }
2494 
2495 /// Start selecting cells
StartCellSelection(wxRichTextTable * table,wxRichTextParagraphLayoutBox * newCell)2496 bool wxRichTextCtrl::StartCellSelection(wxRichTextTable* table, wxRichTextParagraphLayoutBox* newCell)
2497 {
2498     // Start selecting cells in a table
2499     m_selectionState = wxRichTextCtrlSelectionState_CommonAncestor;
2500     m_selectionAnchorObject = GetFocusObject();
2501     m_selectionAnchor = GetFocusObject()->GetRange().GetStart();
2502 
2503     // The common ancestor, such as a table, returns the cell selection
2504     // between the anchor and current position.
2505     m_selection = table->GetSelection(m_selectionAnchor, newCell->GetRange().GetStart());
2506     Refresh();
2507 
2508     if (newCell->AcceptsFocus())
2509         SetFocusObject(newCell, false /* don't set caret and clear selection */);
2510     MoveCaret(-1, false);
2511     SetDefaultStyleToCursorStyle();
2512 
2513     return true;
2514 }
2515 
2516 /// Move up
MoveUp(int noLines,int flags)2517 bool wxRichTextCtrl::MoveUp(int noLines, int flags)
2518 {
2519     return MoveDown(- noLines, flags);
2520 }
2521 
2522 /// Move to the end of the paragraph
MoveToParagraphEnd(int flags)2523 bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
2524 {
2525     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
2526     if (para)
2527     {
2528         long newPos = para->GetRange().GetEnd() - 1;
2529         bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2530         if (!extendSel)
2531             SelectNone();
2532 
2533         SetCaretPosition(newPos);
2534         PositionCaret();
2535         SetDefaultStyleToCursorStyle();
2536 
2537         return true;
2538     }
2539 
2540     return false;
2541 }
2542 
2543 /// Move to the start of the paragraph
MoveToParagraphStart(int flags)2544 bool wxRichTextCtrl::MoveToParagraphStart(int flags)
2545 {
2546     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
2547     if (para)
2548     {
2549         long newPos = para->GetRange().GetStart() - 1;
2550         bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2551         if (!extendSel)
2552             SelectNone();
2553 
2554         SetCaretPosition(newPos, true);
2555         PositionCaret();
2556         SetDefaultStyleToCursorStyle();
2557 
2558         return true;
2559     }
2560 
2561     return false;
2562 }
2563 
2564 /// Move to the end of the line
MoveToLineEnd(int flags)2565 bool wxRichTextCtrl::MoveToLineEnd(int flags)
2566 {
2567     wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2568 
2569     if (line)
2570     {
2571         wxRichTextRange lineRange = line->GetAbsoluteRange();
2572         long newPos = lineRange.GetEnd();
2573         bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2574         if (!extendSel)
2575             SelectNone();
2576 
2577         SetCaretPosition(newPos);
2578         PositionCaret();
2579         SetDefaultStyleToCursorStyle();
2580 
2581         return true;
2582     }
2583 
2584     return false;
2585 }
2586 
2587 /// Move to the start of the line
MoveToLineStart(int flags)2588 bool wxRichTextCtrl::MoveToLineStart(int flags)
2589 {
2590     wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2591     if (line)
2592     {
2593         wxRichTextRange lineRange = line->GetAbsoluteRange();
2594         long newPos = lineRange.GetStart()-1;
2595 
2596         bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2597         if (!extendSel)
2598             SelectNone();
2599 
2600         wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(line);
2601 
2602         SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
2603         PositionCaret();
2604         SetDefaultStyleToCursorStyle();
2605 
2606         return true;
2607     }
2608 
2609     return false;
2610 }
2611 
2612 /// Move to the start of the buffer
MoveHome(int flags)2613 bool wxRichTextCtrl::MoveHome(int flags)
2614 {
2615     if (m_caretPosition != -1)
2616     {
2617         bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
2618         if (!extendSel)
2619             SelectNone();
2620 
2621         SetCaretPosition(-1);
2622         PositionCaret();
2623         SetDefaultStyleToCursorStyle();
2624 
2625         return true;
2626     }
2627     else
2628         return false;
2629 }
2630 
2631 /// Move to the end of the buffer
MoveEnd(int flags)2632 bool wxRichTextCtrl::MoveEnd(int flags)
2633 {
2634     long endPos = GetFocusObject()->GetOwnRange().GetEnd()-1;
2635 
2636     if (m_caretPosition != endPos)
2637     {
2638         bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
2639         if (!extendSel)
2640             SelectNone();
2641 
2642         SetCaretPosition(endPos);
2643         PositionCaret();
2644         SetDefaultStyleToCursorStyle();
2645 
2646         return true;
2647     }
2648     else
2649         return false;
2650 }
2651 
2652 /// Move noPages pages up
PageUp(int noPages,int flags)2653 bool wxRichTextCtrl::PageUp(int noPages, int flags)
2654 {
2655     return PageDown(- noPages, flags);
2656 }
2657 
2658 /// Move noPages pages down
PageDown(int noPages,int flags)2659 bool wxRichTextCtrl::PageDown(int noPages, int flags)
2660 {
2661     // Calculate which line occurs noPages * screen height further down.
2662     wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2663     if (line)
2664     {
2665         wxSize clientSize = GetClientSize();
2666         int topMargin = GetBuffer().GetTopMargin();
2667         int bottomMargin = GetBuffer().GetBottomMargin();
2668         int height = int( 0.5 + ((clientSize.y - topMargin - bottomMargin) / GetScale()));
2669         int newY = line->GetAbsolutePosition().y + noPages*height;
2670 
2671         wxRichTextLine* newLine = GetFocusObject()->GetLineAtYPosition(newY);
2672         if (newLine)
2673         {
2674             wxRichTextRange lineRange = newLine->GetAbsoluteRange();
2675             long pos = lineRange.GetStart()-1;
2676             if (pos != m_caretPosition)
2677             {
2678                 wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(newLine);
2679 
2680                 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2681                 if (!extendSel)
2682                     SelectNone();
2683 
2684                 SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
2685                 PositionCaret();
2686                 SetDefaultStyleToCursorStyle();
2687 
2688                 return true;
2689             }
2690         }
2691     }
2692 
2693     return false;
2694 }
2695 
wxRichTextCtrlIsWhitespace(const wxString & str)2696 static bool wxRichTextCtrlIsWhitespace(const wxString& str)
2697 {
2698     return str == wxT(" ") || str == wxT("\t") || (!str.empty() && (str[0] == (wxChar) 160));
2699 }
2700 
2701 // Finds the caret position for the next word
FindNextWordPosition(int direction) const2702 long wxRichTextCtrl::FindNextWordPosition(int direction) const
2703 {
2704     long endPos = GetFocusObject()->GetOwnRange().GetEnd();
2705 
2706     if (direction > 0)
2707     {
2708         long i = m_caretPosition+1+direction; // +1 for conversion to character pos
2709 
2710         // First skip current text to space
2711         while (i < endPos && i > -1)
2712         {
2713             // i is in character, not caret positions
2714             wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2715             wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2716             if (line && (i == line->GetAbsoluteRange().GetEnd()))
2717             {
2718                 break;
2719             }
2720             else if (!wxRichTextCtrlIsWhitespace(text) && !text.empty())
2721                 i += direction;
2722             else
2723             {
2724                 break;
2725             }
2726         }
2727         while (i < endPos && i > -1)
2728         {
2729             // i is in character, not caret positions
2730             wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2731             wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2732             if (line && (i == line->GetAbsoluteRange().GetEnd()))
2733                 return wxMax(-1, i);
2734 
2735             if (text.empty()) // End of paragraph, or maybe an image
2736                 return wxMax(-1, i - 1);
2737             else if (wxRichTextCtrlIsWhitespace(text))
2738                 i += direction;
2739             else
2740             {
2741                 // Convert to caret position
2742                 return wxMax(-1, i - 1);
2743             }
2744         }
2745         if (i >= endPos)
2746             return endPos-1;
2747         return i-1;
2748     }
2749     else
2750     {
2751         long i = m_caretPosition;
2752 
2753         // First skip white space
2754         while (i < endPos && i > -1)
2755         {
2756             // i is in character, not caret positions
2757             wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2758             wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2759 
2760             if (text.empty() || (line && (i == line->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image
2761                 break;
2762             else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
2763                 i += direction;
2764             else
2765                 break;
2766         }
2767         // Next skip current text to space
2768         while (i < endPos && i > -1)
2769         {
2770             // i is in character, not caret positions
2771             wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2772             wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2773             if (line && line->GetAbsoluteRange().GetStart() == i)
2774                 return i-1;
2775 
2776             if (!wxRichTextCtrlIsWhitespace(text) /* && !text.empty() */)
2777                 i += direction;
2778             else
2779             {
2780                 return i;
2781             }
2782         }
2783         if (i < -1)
2784             return -1;
2785         return i;
2786     }
2787 }
2788 
2789 /// Move n words left
WordLeft(int WXUNUSED (n),int flags)2790 bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
2791 {
2792     long pos = FindNextWordPosition(-1);
2793     if (pos != m_caretPosition)
2794     {
2795         wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
2796 
2797         bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2798         if (!extendSel)
2799             SelectNone();
2800 
2801         SetCaretPosition(pos, para->GetRange().GetStart() != pos);
2802         PositionCaret();
2803         SetDefaultStyleToCursorStyle();
2804 
2805         return true;
2806     }
2807 
2808     return false;
2809 }
2810 
2811 /// Move n words right
WordRight(int WXUNUSED (n),int flags)2812 bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
2813 {
2814     long pos = FindNextWordPosition(1);
2815     if (pos != m_caretPosition)
2816     {
2817         wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
2818 
2819         bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2820         if (!extendSel)
2821             SelectNone();
2822 
2823         SetCaretPosition(pos, para->GetRange().GetStart() != pos);
2824         PositionCaret();
2825         SetDefaultStyleToCursorStyle();
2826 
2827         return true;
2828     }
2829 
2830     return false;
2831 }
2832 
2833 /// Sizing
OnSize(wxSizeEvent & event)2834 void wxRichTextCtrl::OnSize(wxSizeEvent& event)
2835 {
2836     // Only do sizing optimization for large buffers
2837     if (GetBuffer().GetOwnRange().GetEnd() > m_delayedLayoutThreshold)
2838     {
2839         m_fullLayoutRequired = true;
2840         m_fullLayoutTime = wxGetLocalTimeMillis();
2841         m_fullLayoutSavedPosition = GetFirstVisiblePosition();
2842         LayoutContent(true /* onlyVisibleRect */);
2843     }
2844     else
2845         GetBuffer().Invalidate(wxRICHTEXT_ALL);
2846 
2847 #if wxRICHTEXT_BUFFERED_PAINTING
2848     RecreateBuffer();
2849 #endif
2850 
2851     // Anti-hysteresis code: a way to determine whether a combination of OnPaint and
2852     // OnSize was the source of a scrollbar change.
2853     m_setupScrollbarsCountInOnSize = m_setupScrollbarsCount;
2854 
2855     if (GetDelayedImageLoading())
2856         RequestDelayedImageProcessing();
2857 
2858     event.Skip();
2859 }
2860 
2861 // Force any pending layout due to large buffer
ForceDelayedLayout()2862 void wxRichTextCtrl::ForceDelayedLayout()
2863 {
2864     if (m_fullLayoutRequired)
2865     {
2866         m_fullLayoutRequired = false;
2867         m_fullLayoutTime = 0;
2868         GetBuffer().Invalidate(wxRICHTEXT_ALL);
2869         ShowPosition(m_fullLayoutSavedPosition);
2870         Refresh(false);
2871         Update();
2872     }
2873 }
2874 
2875 /// Idle-time processing
OnIdle(wxIdleEvent & event)2876 void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
2877 {
2878 #if wxRICHTEXT_USE_OWN_CARET
2879     if (((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
2880     {
2881         ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(false);
2882         PositionCaret();
2883         if (!GetCaret()->IsVisible())
2884             GetCaret()->Show();
2885     }
2886 #endif
2887 
2888     const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
2889 
2890     if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
2891     {
2892         m_fullLayoutRequired = false;
2893         m_fullLayoutTime = 0;
2894         GetBuffer().Invalidate(wxRICHTEXT_ALL);
2895         ShowPosition(m_fullLayoutSavedPosition);
2896         Refresh(false);
2897     }
2898 
2899     const int imageProcessingInterval = wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL;
2900 
2901     if (m_enableDelayedImageLoading && m_delayedImageProcessingRequired && (wxGetLocalTimeMillis() > (m_delayedImageProcessingTime + imageProcessingInterval)))
2902     {
2903         m_delayedImageProcessingTimer.Stop();
2904         m_delayedImageProcessingRequired = false;
2905         m_delayedImageProcessingTime = 0;
2906         ProcessDelayedImageLoading(true);
2907     }
2908 
2909     if (m_caretPositionForDefaultStyle != -2)
2910     {
2911         // If the caret position has changed, no longer reflect the default style
2912         // in the UI.
2913         if (GetCaretPosition() != m_caretPositionForDefaultStyle)
2914             m_caretPositionForDefaultStyle = -2;
2915     }
2916 
2917     event.Skip();
2918 }
2919 
2920 /// Scrolling
OnScroll(wxScrollWinEvent & event)2921 void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
2922 {
2923 #if wxRICHTEXT_USE_OWN_CARET
2924     if (!((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
2925     {
2926         if (GetCaret()->IsVisible())
2927             GetCaret()->Hide();
2928         ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate();
2929     }
2930 #endif
2931 
2932     event.Skip();
2933 }
2934 
2935 /// Set up scrollbars, e.g. after a resize
SetupScrollbars(bool atTop,bool fromOnPaint)2936 void wxRichTextCtrl::SetupScrollbars(bool atTop, bool fromOnPaint)
2937 {
2938     if (IsFrozen())
2939         return;
2940 
2941     if (GetBuffer().IsEmpty() || !m_verticalScrollbarEnabled)
2942     {
2943         SetScrollbars(0, 0, 0, 0, 0, 0);
2944         return;
2945     }
2946 
2947     // TODO: reimplement scrolling so we scroll by line, not by fixed number
2948     // of pixels. See e.g. wxVScrolledWindow for ideas.
2949     int pixelsPerUnit = GetLineHeight();
2950     wxSize clientSize = GetClientSize();
2951 
2952     int maxHeight = (int) (0.5 + GetScale() * (GetBuffer().GetCachedSize().y + GetBuffer().GetTopMargin()));
2953 
2954     // Round up so we have at least maxHeight pixels
2955     int unitsY = (maxHeight + pixelsPerUnit - 1) / pixelsPerUnit;
2956 
2957     int startX = 0, startY = 0;
2958     if (!atTop)
2959         GetViewStart(& startX, & startY);
2960 
2961     int maxPositionX = 0;
2962     int maxPositionY = (wxMax(unitsY * pixelsPerUnit - clientSize.y, 0) + pixelsPerUnit - 1) / pixelsPerUnit;
2963 
2964     int newStartX = wxMin(maxPositionX, startX);
2965     int newStartY = wxMin(maxPositionY, startY);
2966 
2967     int oldPPUX, oldPPUY;
2968     int oldStartX, oldStartY;
2969     int oldVirtualSizeX = 0, oldVirtualSizeY = 0;
2970     GetScrollPixelsPerUnit(& oldPPUX, & oldPPUY);
2971     GetViewStart(& oldStartX, & oldStartY);
2972     GetVirtualSize(& oldVirtualSizeX, & oldVirtualSizeY);
2973     if (oldPPUY > 0)
2974         oldVirtualSizeY /= oldPPUY;
2975 
2976     if (oldPPUX == 0 && oldPPUY == pixelsPerUnit && oldVirtualSizeY == unitsY && oldStartX == newStartX && oldStartY == newStartY)
2977         return;
2978 
2979     // Don't set scrollbars if there were none before, and there will be none now.
2980     if (oldPPUY != 0 && (oldVirtualSizeY*oldPPUY < clientSize.y) && (unitsY*pixelsPerUnit < clientSize.y))
2981         return;
2982 
2983     // Hysteresis detection. If an object width is relative to the window width, then there can be
2984     // interaction between image width and total content height, causing the scrollbar to appear
2985     // and disappear rapidly. We need to see if we're getting this looping via OnSize/OnPaint,
2986     // and if so, keep the scrollbar shown. We use a counter to see whether the SetupScrollbars
2987     // call is caused by OnSize, versus any other operation such as editing.
2988     // There may still be some flickering when editing at the boundary of scrollbar/no scrollbar
2989     // states, but looping will be avoided.
2990     bool doSetScrollbars = true;
2991     wxSize windowSize = GetSize();
2992     if (fromOnPaint)
2993     {
2994         if ((windowSize == m_lastWindowSize) && (m_setupScrollbarsCountInOnSize == m_setupScrollbarsCount))
2995         {
2996             // If we will be going from scrollbar to no scrollbar, we're now probably in hysteresis.
2997             // So don't set the scrollbars this time.
2998             if ((oldPPUY != 0) && (oldVirtualSizeY*oldPPUY > clientSize.y) && (unitsY*pixelsPerUnit <= clientSize.y))
2999                 doSetScrollbars = false;
3000         }
3001     }
3002 
3003     m_lastWindowSize = windowSize;
3004     m_setupScrollbarsCount ++;
3005     if (m_setupScrollbarsCount > 32000)
3006         m_setupScrollbarsCount = 0;
3007 
3008     // Move to previous scroll position if
3009     // possible
3010     if (doSetScrollbars)
3011         SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY);
3012 }
3013 
3014 /// Paint the background
PaintBackground(wxDC & dc)3015 void wxRichTextCtrl::PaintBackground(wxDC& dc)
3016 {
3017     wxColour backgroundColour = GetBackgroundColour();
3018     if (!backgroundColour.IsOk())
3019         backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
3020 
3021     // Clear the background
3022     dc.SetBrush(wxBrush(backgroundColour));
3023     dc.SetPen(*wxTRANSPARENT_PEN);
3024     wxRect windowRect(GetClientSize());
3025     windowRect.x -= 2; windowRect.y -= 2;
3026     windowRect.width += 4; windowRect.height += 4;
3027 
3028     // We need to shift the rectangle to take into account
3029     // scrolling. Converting device to logical coordinates.
3030     CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
3031     dc.DrawRectangle(windowRect);
3032 }
3033 
3034 #if wxRICHTEXT_BUFFERED_PAINTING
3035 /// Recreate buffer bitmap if necessary
RecreateBuffer(const wxSize & size)3036 bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
3037 {
3038     wxSize sz = size;
3039     if (sz == wxDefaultSize)
3040         sz = GetClientSize();
3041 
3042     if (sz.x < 1 || sz.y < 1)
3043         return false;
3044 
3045     if (!m_bufferBitmap.IsOk() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
3046         // As per https://trac.wxwidgets.org/ticket/14403, prevent very inefficient fix to alpha bits of
3047         // destination by making the backing bitmap 24-bit. Note that using 24-bit depth breaks painting of
3048         // scrolled areas on wxWidgets 2.8.
3049 #if defined(__WXMSW__) && wxCHECK_VERSION(3,0,0)
3050         m_bufferBitmap = wxBitmap(sz.x, sz.y, 24);
3051 #else
3052         m_bufferBitmap = wxBitmap(sz.x, sz.y);
3053 #endif
3054     return m_bufferBitmap.IsOk();
3055 }
3056 #endif
3057 
3058 // ----------------------------------------------------------------------------
3059 // file IO functions
3060 // ----------------------------------------------------------------------------
3061 #if wxUSE_FFILE && wxUSE_STREAMS
DoLoadFile(const wxString & filename,int fileType)3062 bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType)
3063 {
3064     SetFocusObject(& GetBuffer(), true);
3065 
3066     bool success = GetBuffer().LoadFile(filename, (wxRichTextFileType)fileType);
3067     if (success)
3068         m_filename = filename;
3069 
3070     DiscardEdits();
3071     SetInsertionPoint(0);
3072     LayoutContent();
3073     PositionCaret();
3074     SetupScrollbars(true);
3075     Refresh(false);
3076     wxTextCtrl::SendTextUpdatedEvent(this);
3077 
3078     if (success)
3079         return true;
3080     else
3081     {
3082         wxLogError(_("File couldn't be loaded."));
3083 
3084         return false;
3085     }
3086 }
3087 
DoSaveFile(const wxString & filename,int fileType)3088 bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
3089 {
3090     if (GetBuffer().SaveFile(filename, (wxRichTextFileType)fileType))
3091     {
3092         m_filename = filename;
3093 
3094         DiscardEdits();
3095 
3096         return true;
3097     }
3098 
3099     wxLogError(_("The text couldn't be saved."));
3100 
3101     return false;
3102 }
3103 #endif // wxUSE_FFILE && wxUSE_STREAMS
3104 
3105 // ----------------------------------------------------------------------------
3106 // wxRichTextCtrl specific functionality
3107 // ----------------------------------------------------------------------------
3108 
3109 /// Add a new paragraph of text to the end of the buffer
AddParagraph(const wxString & text)3110 wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
3111 {
3112     wxRichTextRange range = GetFocusObject()->AddParagraph(text);
3113     GetBuffer().Invalidate();
3114     LayoutContent();
3115     return range;
3116 }
3117 
3118 /// Add an image
AddImage(const wxImage & image)3119 wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
3120 {
3121     wxRichTextRange range = GetFocusObject()->AddImage(image);
3122     GetBuffer().Invalidate();
3123     LayoutContent();
3124     return range;
3125 }
3126 
3127 // ----------------------------------------------------------------------------
3128 // selection and ranges
3129 // ----------------------------------------------------------------------------
3130 
3131 /// Select none
SelectNone()3132 void wxRichTextCtrl::SelectNone()
3133 {
3134     if (m_selection.IsValid())
3135     {
3136         wxRichTextSelection oldSelection = m_selection;
3137 
3138         m_selection.Reset();
3139 
3140         RefreshForSelectionChange(oldSelection, m_selection);
3141     }
3142     m_selectionAnchor = -2;
3143     m_selectionAnchorObject = NULL;
3144     m_selectionState = wxRichTextCtrlSelectionState_Normal;
3145 }
3146 
wxIsWordDelimiter(const wxString & text)3147 static bool wxIsWordDelimiter(const wxString& text)
3148 {
3149     return !text.IsEmpty() && !wxIsalnum(text[0]);
3150 }
3151 
3152 /// Select the word at the given character position
SelectWord(long position)3153 bool wxRichTextCtrl::SelectWord(long position)
3154 {
3155     if (position < 0 || position > GetFocusObject()->GetOwnRange().GetEnd())
3156         return false;
3157 
3158     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(position);
3159     if (!para)
3160         return false;
3161 
3162     if (position == para->GetRange().GetEnd())
3163         position --;
3164 
3165     long positionStart = position;
3166     long positionEnd = position;
3167 
3168     for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --)
3169     {
3170         wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionStart, positionStart));
3171         if (wxIsWordDelimiter(text))
3172         {
3173             positionStart ++;
3174             break;
3175         }
3176     }
3177     if (positionStart < para->GetRange().GetStart())
3178         positionStart = para->GetRange().GetStart();
3179 
3180     for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++)
3181     {
3182         wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionEnd, positionEnd));
3183         if (wxIsWordDelimiter(text))
3184         {
3185             positionEnd --;
3186             break;
3187         }
3188     }
3189     if (positionEnd >= para->GetRange().GetEnd())
3190         positionEnd = para->GetRange().GetEnd()-1;
3191 
3192     if (positionEnd < positionStart)
3193         return false;
3194 
3195     SetSelection(positionStart, positionEnd+1);
3196 
3197     if (positionStart >= 0)
3198     {
3199         MoveCaret(positionStart-1, true);
3200         SetDefaultStyleToCursorStyle();
3201     }
3202 
3203     return true;
3204 }
3205 
GetStringSelection() const3206 wxString wxRichTextCtrl::GetStringSelection() const
3207 {
3208     long from, to;
3209     GetSelection(&from, &to);
3210 
3211     return GetRange(from, to);
3212 }
3213 
3214 // ----------------------------------------------------------------------------
3215 // hit testing
3216 // ----------------------------------------------------------------------------
3217 
3218 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,wxTextCoord * x,wxTextCoord * y) const3219 wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
3220 {
3221     // implement in terms of the other overload as the native ports typically
3222     // can get the position and not (x, y) pair directly (although wxUniv
3223     // directly gets x and y -- and so overrides this method as well)
3224     long pos;
3225     wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
3226 
3227     if ( rc != wxTE_HT_UNKNOWN )
3228     {
3229         PositionToXY(pos, x, y);
3230     }
3231 
3232     return rc;
3233 }
3234 
3235 wxTextCtrlHitTestResult
HitTest(const wxPoint & pt,long * pos) const3236 wxRichTextCtrl::HitTest(const wxPoint& pt,
3237                         long * pos) const
3238 {
3239     wxClientDC dc(const_cast<wxRichTextCtrl*>(this));
3240     const_cast<wxRichTextCtrl*>(this)->PrepareDC(dc);
3241 
3242     // Buffer uses logical position (relative to start of buffer)
3243     // so convert
3244     wxPoint pt2 = GetLogicalPoint(pt);
3245 
3246     wxRichTextObject* hitObj = NULL;
3247     wxRichTextObject* contextObj = NULL;
3248     wxRichTextDrawingContext context(const_cast<wxRichTextBuffer*>(&GetBuffer()));
3249     int hit = const_cast<wxRichTextCtrl*>(this)->GetFocusObject()->HitTest(dc, context, pt2, *pos, &hitObj, &contextObj, wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS);
3250 
3251     if ((hit & wxRICHTEXT_HITTEST_BEFORE) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
3252         return wxTE_HT_BEFORE;
3253     else if ((hit & wxRICHTEXT_HITTEST_AFTER) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
3254         return wxTE_HT_BEYOND;
3255     else if (hit & (wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_AFTER))
3256         return wxTE_HT_ON_TEXT;
3257 
3258     return wxTE_HT_UNKNOWN;
3259 }
3260 
3261 wxRichTextParagraphLayoutBox*
FindContainerAtPoint(const wxPoint & pt,long & position,int & hit,wxRichTextObject * hitObj,int flags)3262 wxRichTextCtrl::FindContainerAtPoint(const wxPoint& pt, long& position, int& hit, wxRichTextObject* hitObj, int flags/* = 0*/)
3263 {
3264     wxClientDC dc(this);
3265     PrepareDC(dc);
3266     dc.SetFont(GetFont());
3267 
3268     wxPoint logicalPt = GetLogicalPoint(pt);
3269 
3270     wxRichTextObject* contextObj = NULL;
3271     wxRichTextDrawingContext context(& GetBuffer());
3272     hit = GetBuffer().HitTest(dc, context, GetUnscaledPoint(logicalPt), position, &hitObj, &contextObj, flags);
3273     wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
3274 
3275     return container;
3276 }
3277 
3278 
3279 // ----------------------------------------------------------------------------
3280 // set/get the controls text
3281 // ----------------------------------------------------------------------------
3282 
DoGetValue() const3283 wxString wxRichTextCtrl::DoGetValue() const
3284 {
3285     return GetBuffer().GetText();
3286 }
3287 
GetRange(long from,long to) const3288 wxString wxRichTextCtrl::GetRange(long from, long to) const
3289 {
3290     // Public API for range is different from internals
3291     return GetFocusObject()->GetTextForRange(wxRichTextRange(from, to-1));
3292 }
3293 
DoSetValue(const wxString & value,int flags)3294 void wxRichTextCtrl::DoSetValue(const wxString& value, int flags)
3295 {
3296     // Don't call Clear here, since it always sends a text updated event
3297     m_buffer.ResetAndClearCommands();
3298     m_buffer.Invalidate(wxRICHTEXT_ALL);
3299     m_caretPosition = -1;
3300     m_caretPositionForDefaultStyle = -2;
3301     m_caretAtLineStart = false;
3302     m_selection.Reset();
3303     m_selectionState = wxRichTextCtrlSelectionState_Normal;
3304 
3305     Scroll(0,0);
3306 
3307     if (!IsFrozen())
3308     {
3309         LayoutContent();
3310         Refresh(false);
3311     }
3312 
3313     if (!value.IsEmpty())
3314     {
3315         // Remove empty paragraph
3316         GetBuffer().Clear();
3317         DoWriteText(value, flags);
3318 
3319         // for compatibility, don't move the cursor when doing SetValue()
3320         SetInsertionPoint(0);
3321     }
3322     else
3323     {
3324         // still send an event for consistency
3325         if (flags & SetValue_SendEvent)
3326             wxTextCtrl::SendTextUpdatedEvent(this);
3327     }
3328     DiscardEdits();
3329 }
3330 
WriteText(const wxString & value)3331 void wxRichTextCtrl::WriteText(const wxString& value)
3332 {
3333     DoWriteText(value);
3334 }
3335 
DoWriteText(const wxString & value,int flags)3336 void wxRichTextCtrl::DoWriteText(const wxString& value, int flags)
3337 {
3338     wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
3339 
3340     GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
3341     if (!IsFrozen())
3342     {
3343         wxRichTextDrawingContext context(& GetBuffer());
3344         GetBuffer().Defragment(context);
3345     }
3346 
3347     if ( flags & SetValue_SendEvent )
3348         wxTextCtrl::SendTextUpdatedEvent(this);
3349 }
3350 
AppendText(const wxString & text)3351 void wxRichTextCtrl::AppendText(const wxString& text)
3352 {
3353     SetInsertionPointEnd();
3354 
3355     WriteText(text);
3356 }
3357 
3358 /// Write an image at the current insertion point
WriteImage(const wxImage & image,wxBitmapType bitmapType,const wxRichTextAttr & textAttr)3359 bool wxRichTextCtrl::WriteImage(const wxImage& image, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
3360 {
3361     wxRichTextImageBlock imageBlock;
3362 
3363     wxImage image2 = image;
3364     if (imageBlock.MakeImageBlock(image2, bitmapType))
3365         return WriteImage(imageBlock, textAttr);
3366 
3367     return false;
3368 }
3369 
WriteImage(const wxString & filename,wxBitmapType bitmapType,const wxRichTextAttr & textAttr)3370 bool wxRichTextCtrl::WriteImage(const wxString& filename, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
3371 {
3372     wxRichTextImageBlock imageBlock;
3373 
3374     wxImage image;
3375     if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
3376         return WriteImage(imageBlock, textAttr);
3377 
3378     return false;
3379 }
3380 
WriteImage(const wxRichTextImageBlock & imageBlock,const wxRichTextAttr & textAttr)3381 bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock, const wxRichTextAttr& textAttr)
3382 {
3383     return GetFocusObject()->InsertImageWithUndo(& GetBuffer(), m_caretPosition+1, imageBlock, this, 0, textAttr);
3384 }
3385 
WriteImage(const wxBitmap & bitmap,wxBitmapType bitmapType,const wxRichTextAttr & textAttr)3386 bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
3387 {
3388     if (bitmap.IsOk())
3389     {
3390         wxRichTextImageBlock imageBlock;
3391 
3392         wxImage image = bitmap.ConvertToImage();
3393         if (image.IsOk() && imageBlock.MakeImageBlock(image, bitmapType))
3394             return WriteImage(imageBlock, textAttr);
3395     }
3396 
3397     return false;
3398 }
3399 
3400 // Write a text box at the current insertion point.
WriteTextBox(const wxRichTextAttr & textAttr)3401 wxRichTextBox* wxRichTextCtrl::WriteTextBox(const wxRichTextAttr& textAttr)
3402 {
3403     wxRichTextBox* textBox = new wxRichTextBox;
3404     textBox->SetAttributes(textAttr);
3405     textBox->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
3406     textBox->AddParagraph(wxEmptyString);
3407     textBox->SetParent(NULL);
3408 
3409     // If the box has an invalid foreground colour, its text will mimic any upstream value (see #15224)
3410     if (!textBox->GetAttributes().GetTextColour().IsOk())
3411     {
3412         textBox->GetAttributes().SetTextColour(GetBasicStyle().GetTextColour());
3413     }
3414 
3415     // The object returned is the one actually inserted into the buffer,
3416     // while the original one is deleted.
3417     wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(& GetBuffer(), m_caretPosition+1, textBox, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
3418     wxRichTextBox* box = wxDynamicCast(obj, wxRichTextBox);
3419     return box;
3420 }
3421 
WriteField(const wxString & fieldType,const wxRichTextProperties & properties,const wxRichTextAttr & textAttr)3422 wxRichTextField* wxRichTextCtrl::WriteField(const wxString& fieldType, const wxRichTextProperties& properties,
3423                             const wxRichTextAttr& textAttr)
3424 {
3425     return GetFocusObject()->InsertFieldWithUndo(& GetBuffer(), m_caretPosition+1, fieldType, properties,
3426                 this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE, textAttr);
3427 }
3428 
3429 // Write a table at the current insertion point, returning the table.
WriteTable(int rows,int cols,const wxRichTextAttr & tableAttr,const wxRichTextAttr & cellAttr)3430 wxRichTextTable* wxRichTextCtrl::WriteTable(int rows, int cols, const wxRichTextAttr& tableAttr, const wxRichTextAttr& cellAttr)
3431 {
3432     wxASSERT(rows > 0 && cols > 0);
3433 
3434     if (rows <= 0 || cols <= 0)
3435         return NULL;
3436 
3437     wxRichTextTable* table = new wxRichTextTable;
3438     table->SetAttributes(tableAttr);
3439     table->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
3440     table->SetBasicStyle(GetBasicStyle());
3441 
3442     table->CreateTable(rows, cols);
3443 
3444     table->SetParent(NULL);
3445 
3446     // If cells have an invalid foreground colour, their text will mimic any upstream value (see #15224)
3447     wxRichTextAttr attr = cellAttr;
3448     if (!attr.GetTextColour().IsOk())
3449     {
3450         attr.SetTextColour(GetBasicStyle().GetTextColour());
3451     }
3452 
3453     int i, j;
3454     for (j = 0; j < rows; j++)
3455     {
3456         for (i = 0; i < cols; i++)
3457         {
3458             table->GetCell(j, i)->GetAttributes() = attr;
3459         }
3460     }
3461 
3462     // The object returned is the one actually inserted into the buffer,
3463     // while the original one is deleted.
3464     wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(& GetBuffer(), m_caretPosition+1, table, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
3465     wxRichTextTable* tableResult = wxDynamicCast(obj, wxRichTextTable);
3466     return tableResult;
3467 }
3468 
3469 
3470 /// Insert a newline (actually paragraph) at the current insertion point.
Newline()3471 bool wxRichTextCtrl::Newline()
3472 {
3473     return GetFocusObject()->InsertNewlineWithUndo(& GetBuffer(), m_caretPosition+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
3474 }
3475 
3476 /// Insert a line break at the current insertion point.
LineBreak()3477 bool wxRichTextCtrl::LineBreak()
3478 {
3479     wxString text;
3480     text = wxRichTextLineBreakChar;
3481     return GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, text, this);
3482 }
3483 
3484 // ----------------------------------------------------------------------------
3485 // Clipboard operations
3486 // ----------------------------------------------------------------------------
3487 
Copy()3488 void wxRichTextCtrl::Copy()
3489 {
3490     if (CanCopy())
3491     {
3492         wxRichTextRange range = GetInternalSelectionRange();
3493         GetBuffer().CopyToClipboard(range);
3494     }
3495 }
3496 
Cut()3497 void wxRichTextCtrl::Cut()
3498 {
3499     if (CanCut())
3500     {
3501         wxRichTextRange range = GetInternalSelectionRange();
3502         GetBuffer().CopyToClipboard(range);
3503 
3504         DeleteSelectedContent();
3505         LayoutContent();
3506         Refresh(false);
3507     }
3508 }
3509 
Paste()3510 void wxRichTextCtrl::Paste()
3511 {
3512     if (CanPaste())
3513     {
3514         BeginBatchUndo(_("Paste"));
3515 
3516         long newPos = m_caretPosition;
3517         DeleteSelectedContent(& newPos);
3518 
3519         GetBuffer().PasteFromClipboard(newPos);
3520 
3521         EndBatchUndo();
3522     }
3523 }
3524 
DeleteSelection()3525 void wxRichTextCtrl::DeleteSelection()
3526 {
3527     if (CanDeleteSelection())
3528     {
3529         DeleteSelectedContent();
3530     }
3531 }
3532 
HasSelection() const3533 bool wxRichTextCtrl::HasSelection() const
3534 {
3535     return (m_selection.IsValid() && m_selection.GetContainer() == GetFocusObject());
3536 }
3537 
HasUnfocusedSelection() const3538 bool wxRichTextCtrl::HasUnfocusedSelection() const
3539 {
3540     return m_selection.IsValid();
3541 }
3542 
CanCopy() const3543 bool wxRichTextCtrl::CanCopy() const
3544 {
3545     // Can copy if there's a selection
3546     return HasSelection();
3547 }
3548 
CanCut() const3549 bool wxRichTextCtrl::CanCut() const
3550 {
3551     return CanDeleteSelection();
3552 }
3553 
CanPaste() const3554 bool wxRichTextCtrl::CanPaste() const
3555 {
3556     if ( !IsEditable() || !GetFocusObject() || !CanInsertContent(* GetFocusObject(), m_caretPosition+1))
3557         return false;
3558 
3559     return GetBuffer().CanPasteFromClipboard();
3560 }
3561 
CanDeleteSelection() const3562 bool wxRichTextCtrl::CanDeleteSelection() const
3563 {
3564     return HasSelection() && IsEditable() && CanDeleteRange(* GetFocusObject(), GetSelectionRange());
3565 }
3566 
3567 
3568 // ----------------------------------------------------------------------------
3569 // Accessors
3570 // ----------------------------------------------------------------------------
3571 
SetContextMenu(wxMenu * menu)3572 void wxRichTextCtrl::SetContextMenu(wxMenu* menu)
3573 {
3574     if (m_contextMenu && m_contextMenu != menu)
3575         delete m_contextMenu;
3576     m_contextMenu = menu;
3577 }
3578 
SetEditable(bool editable)3579 void wxRichTextCtrl::SetEditable(bool editable)
3580 {
3581     m_editable = editable;
3582 }
3583 
SetInsertionPoint(long pos)3584 void wxRichTextCtrl::SetInsertionPoint(long pos)
3585 {
3586     SelectNone();
3587 
3588     m_caretPosition = pos - 1;
3589     m_caretAtLineStart = true;
3590 
3591     PositionCaret();
3592 
3593     SetDefaultStyleToCursorStyle();
3594 }
3595 
SetInsertionPointEnd()3596 void wxRichTextCtrl::SetInsertionPointEnd()
3597 {
3598     long pos = GetLastPosition();
3599     SetInsertionPoint(pos);
3600 }
3601 
GetInsertionPoint() const3602 long wxRichTextCtrl::GetInsertionPoint() const
3603 {
3604     return m_caretPosition+1;
3605 }
3606 
GetLastPosition() const3607 wxTextPos wxRichTextCtrl::GetLastPosition() const
3608 {
3609     return GetFocusObject()->GetOwnRange().GetEnd();
3610 }
3611 
3612 // If the return values from and to are the same, there is no
3613 // selection.
GetSelection(long * from,long * to) const3614 void wxRichTextCtrl::GetSelection(long* from, long* to) const
3615 {
3616     if (m_selection.IsValid())
3617     {
3618         *from = m_selection.GetRange().GetStart();
3619         *to = m_selection.GetRange().GetEnd();
3620         (*to) ++;
3621     }
3622     else
3623     {
3624         *from = -2;
3625         *to = -2;
3626     }
3627 }
3628 
IsEditable() const3629 bool wxRichTextCtrl::IsEditable() const
3630 {
3631     return m_editable;
3632 }
3633 
3634 // ----------------------------------------------------------------------------
3635 // selection
3636 // ----------------------------------------------------------------------------
3637 
SetSelection(long from,long to)3638 void wxRichTextCtrl::SetSelection(long from, long to)
3639 {
3640     // if from and to are both -1, it means (in wxWidgets) that all text should
3641     // be selected.
3642     if ( (from == -1) && (to == -1) )
3643     {
3644         from = 0;
3645         to = GetLastPosition()+1;
3646     }
3647 
3648     if (from == to)
3649     {
3650         SelectNone();
3651     }
3652     else
3653     {
3654         wxRichTextSelection oldSelection = m_selection;
3655 
3656         m_selectionAnchor = from-1;
3657         m_selectionAnchorObject = NULL;
3658         m_selection.Set(wxRichTextRange(from, to-1), GetFocusObject());
3659 
3660         m_caretPosition = wxMax(-1, to-1);
3661 
3662         RefreshForSelectionChange(oldSelection, m_selection);
3663         PositionCaret();
3664     }
3665 }
3666 
3667 // ----------------------------------------------------------------------------
3668 // Editing
3669 // ----------------------------------------------------------------------------
3670 
Replace(long from,long to,const wxString & value)3671 void wxRichTextCtrl::Replace(long from, long to,
3672                              const wxString& value)
3673 {
3674     BeginBatchUndo(_("Replace"));
3675 
3676     SetSelection(from, to);
3677 
3678     wxRichTextAttr attr(GetDefaultStyle());
3679 
3680     DeleteSelectedContent();
3681 
3682     SetDefaultStyle(attr);
3683 
3684     if (!value.IsEmpty())
3685         DoWriteText(value, SetValue_SelectionOnly);
3686 
3687     EndBatchUndo();
3688 }
3689 
Remove(long from,long to)3690 void wxRichTextCtrl::Remove(long from, long to)
3691 {
3692     SelectNone();
3693 
3694     GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(from, to-1), this, & GetBuffer());
3695 
3696     LayoutContent();
3697     if (!IsFrozen())
3698         Refresh(false);
3699 }
3700 
IsModified() const3701 bool wxRichTextCtrl::IsModified() const
3702 {
3703     return m_buffer.IsModified();
3704 }
3705 
MarkDirty()3706 void wxRichTextCtrl::MarkDirty()
3707 {
3708     m_buffer.Modify(true);
3709 }
3710 
DiscardEdits()3711 void wxRichTextCtrl::DiscardEdits()
3712 {
3713     m_caretPositionForDefaultStyle = -2;
3714     m_buffer.Modify(false);
3715     m_buffer.GetCommandProcessor()->ClearCommands();
3716 }
3717 
GetNumberOfLines() const3718 int wxRichTextCtrl::GetNumberOfLines() const
3719 {
3720     return GetFocusObject()->GetParagraphCount();
3721 }
3722 
3723 // ----------------------------------------------------------------------------
3724 // Positions <-> coords
3725 // ----------------------------------------------------------------------------
3726 
XYToPosition(long x,long y) const3727 long wxRichTextCtrl::XYToPosition(long x, long y) const
3728 {
3729     return GetFocusObject()->XYToPosition(x, y);
3730 }
3731 
PositionToXY(long pos,long * x,long * y) const3732 bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
3733 {
3734     return GetFocusObject()->PositionToXY(pos, x, y);
3735 }
3736 
3737 // ----------------------------------------------------------------------------
3738 //
3739 // ----------------------------------------------------------------------------
3740 
ShowPosition(long pos)3741 void wxRichTextCtrl::ShowPosition(long pos)
3742 {
3743     if (!IsPositionVisible(pos))
3744         ScrollIntoView(pos-1, WXK_DOWN);
3745 }
3746 
GetLineLength(long lineNo) const3747 int wxRichTextCtrl::GetLineLength(long lineNo) const
3748 {
3749     return GetFocusObject()->GetParagraphLength(lineNo);
3750 }
3751 
GetLineText(long lineNo) const3752 wxString wxRichTextCtrl::GetLineText(long lineNo) const
3753 {
3754     return GetFocusObject()->GetParagraphText(lineNo);
3755 }
3756 
3757 // ----------------------------------------------------------------------------
3758 // Undo/redo
3759 // ----------------------------------------------------------------------------
3760 
Undo()3761 void wxRichTextCtrl::Undo()
3762 {
3763     if (CanUndo())
3764     {
3765         GetCommandProcessor()->Undo();
3766     }
3767 }
3768 
Redo()3769 void wxRichTextCtrl::Redo()
3770 {
3771     if (CanRedo())
3772     {
3773         GetCommandProcessor()->Redo();
3774     }
3775 }
3776 
CanUndo() const3777 bool wxRichTextCtrl::CanUndo() const
3778 {
3779     return GetCommandProcessor()->CanUndo() && IsEditable();
3780 }
3781 
CanRedo() const3782 bool wxRichTextCtrl::CanRedo() const
3783 {
3784     return GetCommandProcessor()->CanRedo() && IsEditable();
3785 }
3786 
3787 // ----------------------------------------------------------------------------
3788 // implementation details
3789 // ----------------------------------------------------------------------------
3790 
Command(wxCommandEvent & event)3791 void wxRichTextCtrl::Command(wxCommandEvent& event)
3792 {
3793     SetValue(event.GetString());
3794     GetEventHandler()->ProcessEvent(event);
3795 }
3796 
OnDropFiles(wxDropFilesEvent & event)3797 void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
3798 {
3799     // By default, load the first file into the text window.
3800     if (event.GetNumberOfFiles() > 0)
3801     {
3802         LoadFile(event.GetFiles()[0]);
3803     }
3804 }
3805 
DoGetBestSize() const3806 wxSize wxRichTextCtrl::DoGetBestSize() const
3807 {
3808     return FromDIP(wxSize(10, 10));
3809 }
3810 
3811 // ----------------------------------------------------------------------------
3812 // standard handlers for standard edit menu events
3813 // ----------------------------------------------------------------------------
3814 
OnCut(wxCommandEvent & WXUNUSED (event))3815 void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
3816 {
3817     Cut();
3818 }
3819 
OnClear(wxCommandEvent & WXUNUSED (event))3820 void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
3821 {
3822     DeleteSelection();
3823 }
3824 
OnCopy(wxCommandEvent & WXUNUSED (event))3825 void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
3826 {
3827     Copy();
3828 }
3829 
OnPaste(wxCommandEvent & WXUNUSED (event))3830 void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
3831 {
3832     Paste();
3833 }
3834 
OnUndo(wxCommandEvent & WXUNUSED (event))3835 void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
3836 {
3837     Undo();
3838 }
3839 
OnRedo(wxCommandEvent & WXUNUSED (event))3840 void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
3841 {
3842     Redo();
3843 }
3844 
OnUpdateCut(wxUpdateUIEvent & event)3845 void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
3846 {
3847     event.Enable( CanCut() );
3848 }
3849 
OnUpdateCopy(wxUpdateUIEvent & event)3850 void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
3851 {
3852     event.Enable( CanCopy() );
3853 }
3854 
OnUpdateClear(wxUpdateUIEvent & event)3855 void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
3856 {
3857     event.Enable( CanDeleteSelection() );
3858 }
3859 
OnUpdatePaste(wxUpdateUIEvent & event)3860 void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
3861 {
3862     event.Enable( CanPaste() );
3863 }
3864 
OnUpdateUndo(wxUpdateUIEvent & event)3865 void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
3866 {
3867     event.Enable( CanUndo() );
3868     event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
3869 }
3870 
OnUpdateRedo(wxUpdateUIEvent & event)3871 void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
3872 {
3873     event.Enable( CanRedo() );
3874     event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
3875 }
3876 
OnSelectAll(wxCommandEvent & WXUNUSED (event))3877 void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
3878 {
3879     if (GetLastPosition() > 0)
3880         SelectAll();
3881 }
3882 
OnUpdateSelectAll(wxUpdateUIEvent & event)3883 void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
3884 {
3885     event.Enable(GetLastPosition() > 0);
3886 }
3887 
OnProperties(wxCommandEvent & event)3888 void wxRichTextCtrl::OnProperties(wxCommandEvent& event)
3889 {
3890     int idx = event.GetId() - wxID_RICHTEXT_PROPERTIES1;
3891     if (idx >= 0 && idx < m_contextMenuPropertiesInfo.GetCount())
3892     {
3893         wxRichTextObject* obj = m_contextMenuPropertiesInfo.GetObject(idx);
3894         if (obj && CanEditProperties(obj))
3895             EditProperties(obj, this);
3896 
3897         m_contextMenuPropertiesInfo.Clear();
3898     }
3899 }
3900 
OnUpdateProperties(wxUpdateUIEvent & event)3901 void wxRichTextCtrl::OnUpdateProperties(wxUpdateUIEvent& event)
3902 {
3903     int idx = event.GetId() - wxID_RICHTEXT_PROPERTIES1;
3904     event.Enable(idx >= 0 && idx < m_contextMenuPropertiesInfo.GetCount());
3905 }
3906 
OnContextMenu(wxContextMenuEvent & event)3907 void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& event)
3908 {
3909     if (event.GetEventObject() != this)
3910     {
3911         event.Skip();
3912         return;
3913     }
3914 
3915     ShowContextMenu(m_contextMenu, event.GetPosition());
3916 }
3917 
3918 // Prepares the context menu, adding appropriate property-editing commands.
3919 // Returns the number of property commands added.
PrepareContextMenu(wxMenu * menu,const wxPoint & pt,bool addPropertyCommands)3920 int wxRichTextCtrl::PrepareContextMenu(wxMenu* menu, const wxPoint& pt, bool addPropertyCommands)
3921 {
3922     wxClientDC dc(this);
3923     PrepareDC(dc);
3924     dc.SetFont(GetFont());
3925 
3926     m_contextMenuPropertiesInfo.Clear();
3927 
3928     long position = 0;
3929     wxRichTextObject* hitObj = NULL;
3930     wxRichTextObject* contextObj = NULL;
3931     if (pt != wxDefaultPosition)
3932     {
3933         wxPoint logicalPt = GetLogicalPoint(ScreenToClient(pt));
3934         wxRichTextDrawingContext context(& GetBuffer());
3935         int hit = GetBuffer().HitTest(dc, context, GetUnscaledPoint(logicalPt), position, & hitObj, & contextObj);
3936 
3937         if (hit == wxRICHTEXT_HITTEST_ON || hit == wxRICHTEXT_HITTEST_BEFORE || hit == wxRICHTEXT_HITTEST_AFTER)
3938         {
3939             wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
3940             if (hitObj && actualContainer)
3941             {
3942                 if (actualContainer->AcceptsFocus())
3943                 {
3944                     SetFocusObject(actualContainer, false /* don't set caret position yet */);
3945                     SetCaretPositionAfterClick(actualContainer, position, hit);
3946                 }
3947 
3948                 if (addPropertyCommands)
3949                     m_contextMenuPropertiesInfo.AddItems(this, actualContainer, hitObj);
3950             }
3951             else
3952             {
3953                 if (addPropertyCommands)
3954                     m_contextMenuPropertiesInfo.AddItems(this, GetFocusObject(), hitObj);
3955             }
3956         }
3957         else
3958         {
3959             if (addPropertyCommands)
3960                 m_contextMenuPropertiesInfo.AddItems(this, GetFocusObject(), NULL);
3961         }
3962     }
3963     else
3964     {
3965         // Invoked from the keyboard, so don't set the caret position and don't use the event
3966         // position
3967         hitObj = GetFocusObject()->GetLeafObjectAtPosition(m_caretPosition+1);
3968         if (hitObj)
3969             contextObj = hitObj->GetParentContainer();
3970         else
3971             contextObj = GetFocusObject();
3972 
3973         wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
3974         if (hitObj && actualContainer)
3975         {
3976             if (addPropertyCommands)
3977                 m_contextMenuPropertiesInfo.AddItems(this, actualContainer, hitObj);
3978         }
3979         else
3980         {
3981             if (addPropertyCommands)
3982                 m_contextMenuPropertiesInfo.AddItems(this, GetFocusObject(), NULL);
3983         }
3984     }
3985 
3986     if (menu)
3987     {
3988         if (addPropertyCommands)
3989             m_contextMenuPropertiesInfo.AddMenuItems(menu);
3990         return m_contextMenuPropertiesInfo.GetCount();
3991     }
3992     else
3993         return 0;
3994 }
3995 
3996 // Shows the context menu, adding appropriate property-editing commands
ShowContextMenu(wxMenu * menu,const wxPoint & pt,bool addPropertyCommands)3997 bool wxRichTextCtrl::ShowContextMenu(wxMenu* menu, const wxPoint& pt, bool addPropertyCommands)
3998 {
3999     if (menu)
4000     {
4001         PrepareContextMenu(menu, pt, addPropertyCommands);
4002         PopupMenu(menu);
4003         return true;
4004     }
4005     else
4006         return false;
4007 }
4008 
SetStyle(long start,long end,const wxTextAttr & style)4009 bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
4010 {
4011     return GetFocusObject()->SetStyle(wxRichTextRange(start, end-1), wxRichTextAttr(style));
4012 }
4013 
SetStyle(long start,long end,const wxRichTextAttr & style)4014 bool wxRichTextCtrl::SetStyle(long start, long end, const wxRichTextAttr& style)
4015 {
4016     return GetFocusObject()->SetStyle(wxRichTextRange(start, end-1), style);
4017 }
4018 
SetStyle(const wxRichTextRange & range,const wxTextAttr & style)4019 bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxTextAttr& style)
4020 {
4021     return GetFocusObject()->SetStyle(range.ToInternal(), wxRichTextAttr(style));
4022 }
4023 
SetStyle(const wxRichTextRange & range,const wxRichTextAttr & style)4024 bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
4025 {
4026     return GetFocusObject()->SetStyle(range.ToInternal(), style);
4027 }
4028 
SetStyle(wxRichTextObject * obj,const wxRichTextAttr & textAttr,int flags)4029 void wxRichTextCtrl::SetStyle(wxRichTextObject *obj, const wxRichTextAttr& textAttr, int flags)
4030 {
4031     GetFocusObject()->SetStyle(obj, textAttr, flags);
4032 }
4033 
4034 // extended style setting operation with flags including:
4035 // wxRICHTEXT_SETSTYLE_WITH_UNDO, wxRICHTEXT_SETSTYLE_OPTIMIZE, wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY.
4036 // see richtextbuffer.h for more details.
4037 
SetStyleEx(const wxRichTextRange & range,const wxRichTextAttr & style,int flags)4038 bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
4039 {
4040     return GetFocusObject()->SetStyle(range.ToInternal(), style, flags);
4041 }
4042 
SetDefaultStyle(const wxTextAttr & style)4043 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style)
4044 {
4045     return GetBuffer().SetDefaultStyle(style);
4046 }
4047 
SetDefaultStyle(const wxRichTextAttr & style)4048 bool wxRichTextCtrl::SetDefaultStyle(const wxRichTextAttr& style)
4049 {
4050     wxRichTextAttr attr1(style);
4051     attr1.GetTextBoxAttr().Reset();
4052     return GetBuffer().SetDefaultStyle(attr1);
4053 }
4054 
GetDefaultStyleEx() const4055 const wxRichTextAttr& wxRichTextCtrl::GetDefaultStyleEx() const
4056 {
4057     return GetBuffer().GetDefaultStyle();
4058 }
4059 
GetStyle(long position,wxTextAttr & style)4060 bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style)
4061 {
4062     wxRichTextAttr attr;
4063     if (GetFocusObject()->GetStyle(position, attr))
4064     {
4065         style = attr;
4066         return true;
4067     }
4068     else
4069         return false;
4070 }
4071 
GetStyle(long position,wxRichTextAttr & style)4072 bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style)
4073 {
4074     return GetFocusObject()->GetStyle(position, style);
4075 }
4076 
GetStyle(long position,wxRichTextAttr & style,wxRichTextParagraphLayoutBox * container)4077 bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
4078 {
4079     wxRichTextAttr attr;
4080     if (container->GetStyle(position, attr))
4081     {
4082         style = attr;
4083         return true;
4084     }
4085     else
4086         return false;
4087 }
4088 
4089 // get the common set of styles for the range
GetStyleForRange(const wxRichTextRange & range,wxTextAttr & style)4090 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxTextAttr& style)
4091 {
4092     wxRichTextAttr attr;
4093     if (GetFocusObject()->GetStyleForRange(range.ToInternal(), attr))
4094     {
4095         style = attr;
4096         return true;
4097     }
4098     else
4099         return false;
4100 }
4101 
GetStyleForRange(const wxRichTextRange & range,wxRichTextAttr & style)4102 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
4103 {
4104     return GetFocusObject()->GetStyleForRange(range.ToInternal(), style);
4105 }
4106 
GetStyleForRange(const wxRichTextRange & range,wxRichTextAttr & style,wxRichTextParagraphLayoutBox * container)4107 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
4108 {
4109     return container->GetStyleForRange(range.ToInternal(), style);
4110 }
4111 
4112 /// Get the content (uncombined) attributes for this position.
GetUncombinedStyle(long position,wxRichTextAttr & style)4113 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style)
4114 {
4115     return GetFocusObject()->GetUncombinedStyle(position, style);
4116 }
4117 
4118 /// Get the content (uncombined) attributes for this position.
GetUncombinedStyle(long position,wxRichTextAttr & style,wxRichTextParagraphLayoutBox * container)4119 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
4120 {
4121     return container->GetUncombinedStyle(position, style);
4122 }
4123 
SetProperties(const wxRichTextRange & range,const wxRichTextProperties & properties,int flags)4124 bool wxRichTextCtrl::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
4125 {
4126     return GetFocusObject()->SetProperties(range.ToInternal(), properties, flags);
4127 }
4128 
4129 /// Set font, and also the buffer attributes
SetFont(const wxFont & font)4130 bool wxRichTextCtrl::SetFont(const wxFont& font)
4131 {
4132     wxControl::SetFont(font);
4133 
4134     wxRichTextAttr attr = GetBuffer().GetAttributes();
4135     attr.SetFont(font);
4136     GetBuffer().SetBasicStyle(attr);
4137 
4138     GetBuffer().Invalidate(wxRICHTEXT_ALL);
4139     Refresh(false);
4140 
4141     return true;
4142 }
4143 
4144 /// Transform logical to physical
GetPhysicalPoint(const wxPoint & ptLogical) const4145 wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
4146 {
4147     wxPoint pt;
4148     CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
4149 
4150     return pt;
4151 }
4152 
4153 /// Transform physical to logical
GetLogicalPoint(const wxPoint & ptPhysical) const4154 wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
4155 {
4156     wxPoint pt;
4157     CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
4158 
4159     return pt;
4160 }
4161 
4162 /// Position the caret
PositionCaret(wxRichTextParagraphLayoutBox * container)4163 void wxRichTextCtrl::PositionCaret(wxRichTextParagraphLayoutBox* container)
4164 {
4165     if (!GetCaret())
4166         return;
4167 
4168     //wxLogDebug(wxT("PositionCaret"));
4169 
4170     wxRect caretRect;
4171     if (GetCaretPositionForIndex(GetCaretPosition(), caretRect, container))
4172     {
4173 #if !wxRICHTEXT_USE_OWN_CARET
4174         caretRect = GetScaledRect(caretRect);
4175 #endif
4176         int topMargin = (int) (0.5 + GetScale()*GetBuffer().GetTopMargin());
4177         int bottomMargin = (int) (0.5 + GetScale()*GetBuffer().GetBottomMargin());
4178         wxPoint newPt = caretRect.GetPosition();
4179         wxSize newSz = caretRect.GetSize();
4180         wxPoint pt = GetPhysicalPoint(newPt);
4181         if (GetCaret()->GetPosition() != pt || GetCaret()->GetSize() != newSz)
4182         {
4183             GetCaret()->Hide();
4184             if (GetCaret()->GetSize() != newSz)
4185                 GetCaret()->SetSize(newSz);
4186 
4187             // Adjust size so the caret size and position doesn't appear in the margins
4188             if (((pt.y + newSz.y) <= topMargin) || (pt.y >= (GetClientSize().y - bottomMargin)))
4189             {
4190                 pt.x = -200;
4191                 pt.y = -200;
4192             }
4193             else if (pt.y < topMargin && (pt.y + newSz.y) > topMargin)
4194             {
4195                 newSz.y -= (topMargin - pt.y);
4196                 if (newSz.y > 0)
4197                 {
4198                     pt.y = topMargin;
4199                     GetCaret()->SetSize(newSz);
4200                 }
4201             }
4202             else if (pt.y < (GetClientSize().y - bottomMargin) && (pt.y + newSz.y) > (GetClientSize().y - bottomMargin))
4203             {
4204                 newSz.y = GetClientSize().y - bottomMargin - pt.y;
4205                 GetCaret()->SetSize(newSz);
4206             }
4207 
4208             GetCaret()->Move(pt);
4209             GetCaret()->Show();
4210         }
4211     }
4212 }
4213 
4214 /// Get the caret height and position for the given character position
GetCaretPositionForIndex(long position,wxRect & rect,wxRichTextParagraphLayoutBox * container)4215 bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect, wxRichTextParagraphLayoutBox* container)
4216 {
4217     wxClientDC dc(this);
4218     PrepareDC(dc);
4219     dc.SetUserScale(GetScale(), GetScale());
4220     dc.SetFont(GetFont());
4221 
4222     wxPoint pt;
4223     int height = 0;
4224 
4225     if (!container)
4226         container = GetFocusObject();
4227 
4228     wxRichTextDrawingContext context(& GetBuffer());
4229     if (container->FindPosition(dc, context, position, pt, & height, m_caretAtLineStart))
4230     {
4231         // Caret height can't be zero
4232         if (height == 0)
4233             height = dc.GetCharHeight();
4234 
4235         rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
4236         return true;
4237     }
4238 
4239     return false;
4240 }
4241 
4242 /// Gets the line for the visible caret position. If the caret is
4243 /// shown at the very end of the line, it means the next character is actually
4244 /// on the following line. So let's get the line we're expecting to find
4245 /// if this is the case.
GetVisibleLineForCaretPosition(long caretPosition) const4246 wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
4247 {
4248     wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(caretPosition, true);
4249     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(caretPosition, true);
4250     if (line)
4251     {
4252         wxRichTextRange lineRange = line->GetAbsoluteRange();
4253         if (caretPosition == lineRange.GetStart()-1 &&
4254             (para->GetRange().GetStart() != lineRange.GetStart()))
4255         {
4256             // Only test for caret start/end position if we're looking at the current caret position,
4257             // otherwise m_caretAtLineStart is meaningless
4258             if (!m_caretAtLineStart && (caretPosition == m_caretPosition))
4259                 line = GetFocusObject()->GetLineAtPosition(caretPosition-1, true);
4260         }
4261     }
4262     return line;
4263 }
4264 
4265 
4266 /// Move the caret to the given character position
MoveCaret(long pos,bool showAtLineStart,wxRichTextParagraphLayoutBox * container)4267 bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart, wxRichTextParagraphLayoutBox* container)
4268 {
4269     if (GetBuffer().IsDirty())
4270         LayoutContent();
4271 
4272     if (!container)
4273         container = GetFocusObject();
4274 
4275     if (pos <= container->GetOwnRange().GetEnd())
4276     {
4277         SetCaretPosition(pos, showAtLineStart);
4278 
4279         PositionCaret(container);
4280 
4281         return true;
4282     }
4283     else
4284         return false;
4285 }
4286 
4287 /// Layout the buffer: which we must do before certain operations, such as
4288 /// setting the caret position.
LayoutContent(bool onlyVisibleRect)4289 bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
4290 {
4291     if (GetBuffer().IsDirty() || onlyVisibleRect)
4292     {
4293         wxRect availableSpace(GetUnscaledSize(GetClientSize()));
4294         if (availableSpace.width == 0)
4295             availableSpace.width = 10;
4296         if (availableSpace.height == 0)
4297             availableSpace.height = 10;
4298 
4299         int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
4300         if (onlyVisibleRect)
4301         {
4302             flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
4303             availableSpace.SetPosition(GetUnscaledPoint(GetLogicalPoint(wxPoint(0, 0))));
4304         }
4305 
4306         wxClientDC dc(this);
4307 
4308         PrepareDC(dc);
4309         dc.SetFont(GetFont());
4310         dc.SetUserScale(GetScale(), GetScale());
4311 
4312         wxRichTextDrawingContext context(& GetBuffer());
4313         GetBuffer().Defragment(context);
4314         GetBuffer().UpdateRanges();     // If items were deleted, ranges need recalculation
4315         DoLayoutBuffer(GetBuffer(), dc, context, availableSpace, availableSpace, flags);
4316         GetBuffer().Invalidate(wxRICHTEXT_NONE);
4317 
4318         dc.SetUserScale(1.0, 1.0);
4319 
4320         if (!IsFrozen() && !onlyVisibleRect)
4321             SetupScrollbars();
4322 
4323         if (GetDelayedImageLoading())
4324             RequestDelayedImageProcessing();
4325     }
4326 
4327     return true;
4328 }
4329 
DoLayoutBuffer(wxRichTextBuffer & buffer,wxDC & dc,wxRichTextDrawingContext & context,const wxRect & rect,const wxRect & parentRect,int flags)4330 void wxRichTextCtrl::DoLayoutBuffer(wxRichTextBuffer& buffer, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int flags)
4331 {
4332     buffer.Layout(dc, context, rect, parentRect, flags);
4333 }
4334 
4335 /// Is all of the selection, or the current caret position, bold?
IsSelectionBold()4336 bool wxRichTextCtrl::IsSelectionBold()
4337 {
4338     if (HasSelection())
4339     {
4340         wxRichTextAttr attr;
4341         wxRichTextRange range = GetSelectionRange();
4342         attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
4343         attr.SetFontWeight(wxFONTWEIGHT_BOLD);
4344 
4345         return HasCharacterAttributes(range, attr);
4346     }
4347     else
4348     {
4349         // If no selection, then we need to combine current style with default style
4350         // to see what the effect would be if we started typing.
4351         wxRichTextAttr attr;
4352         attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
4353 
4354         long pos = GetAdjustedCaretPosition(GetCaretPosition());
4355         if (GetStyle(pos, attr))
4356         {
4357             if (IsDefaultStyleShowing())
4358                 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
4359             return attr.GetFontWeight() == wxFONTWEIGHT_BOLD;
4360         }
4361     }
4362     return false;
4363 }
4364 
4365 /// Is all of the selection, or the current caret position, italics?
IsSelectionItalics()4366 bool wxRichTextCtrl::IsSelectionItalics()
4367 {
4368     if (HasSelection())
4369     {
4370         wxRichTextRange range = GetSelectionRange();
4371         wxRichTextAttr attr;
4372         attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
4373         attr.SetFontStyle(wxFONTSTYLE_ITALIC);
4374 
4375         return HasCharacterAttributes(range, attr);
4376     }
4377     else
4378     {
4379         // If no selection, then we need to combine current style with default style
4380         // to see what the effect would be if we started typing.
4381         wxRichTextAttr attr;
4382         attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
4383 
4384         long pos = GetAdjustedCaretPosition(GetCaretPosition());
4385         if (GetStyle(pos, attr))
4386         {
4387             if (IsDefaultStyleShowing())
4388                 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
4389             return attr.GetFontStyle() == wxFONTSTYLE_ITALIC;
4390         }
4391     }
4392     return false;
4393 }
4394 
4395 /// Is all of the selection, or the current caret position, underlined?
IsSelectionUnderlined()4396 bool wxRichTextCtrl::IsSelectionUnderlined()
4397 {
4398     if (HasSelection())
4399     {
4400         wxRichTextRange range = GetSelectionRange();
4401         wxRichTextAttr attr;
4402         attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
4403         attr.SetFontUnderlined(true);
4404 
4405         return HasCharacterAttributes(range, attr);
4406     }
4407     else
4408     {
4409         // If no selection, then we need to combine current style with default style
4410         // to see what the effect would be if we started typing.
4411         wxRichTextAttr attr;
4412         attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
4413         long pos = GetAdjustedCaretPosition(GetCaretPosition());
4414 
4415         if (GetStyle(pos, attr))
4416         {
4417             if (IsDefaultStyleShowing())
4418                 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
4419             return attr.GetFontUnderlined();
4420         }
4421     }
4422     return false;
4423 }
4424 
4425 /// Does all of the selection, or the current caret position, have this wxTextAttrEffects flag(s)?
DoesSelectionHaveTextEffectFlag(int flag)4426 bool wxRichTextCtrl::DoesSelectionHaveTextEffectFlag(int flag)
4427 {
4428     wxRichTextAttr attr;
4429     attr.SetFlags(wxTEXT_ATTR_EFFECTS);
4430     attr.SetTextEffectFlags(flag);
4431     attr.SetTextEffects(flag);
4432 
4433     if (HasSelection())
4434     {
4435         return HasCharacterAttributes(GetSelectionRange(), attr);
4436     }
4437     else
4438     {
4439         // If no selection, then we need to combine current style with default style
4440         // to see what the effect would be if we started typing.
4441         long pos = GetAdjustedCaretPosition(GetCaretPosition());
4442         if (GetStyle(pos, attr))
4443         {
4444             if (IsDefaultStyleShowing())
4445                 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
4446             return (attr.GetTextEffectFlags() & flag) != 0;
4447         }
4448     }
4449     return false;
4450 }
4451 
4452 /// Apply bold to the selection
ApplyBoldToSelection()4453 bool wxRichTextCtrl::ApplyBoldToSelection()
4454 {
4455     wxRichTextAttr attr;
4456     attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
4457     attr.SetFontWeight(IsSelectionBold() ? wxFONTWEIGHT_NORMAL : wxFONTWEIGHT_BOLD);
4458 
4459     if (HasSelection())
4460         return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
4461     else
4462     {
4463         wxRichTextAttr current = GetDefaultStyleEx();
4464         current.Apply(attr);
4465         SetAndShowDefaultStyle(current);
4466     }
4467     return true;
4468 }
4469 
4470 /// Apply italic to the selection
ApplyItalicToSelection()4471 bool wxRichTextCtrl::ApplyItalicToSelection()
4472 {
4473     wxRichTextAttr attr;
4474     attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
4475     attr.SetFontStyle(IsSelectionItalics() ? wxFONTSTYLE_NORMAL : wxFONTSTYLE_ITALIC);
4476 
4477     if (HasSelection())
4478         return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
4479     else
4480     {
4481         wxRichTextAttr current = GetDefaultStyleEx();
4482         current.Apply(attr);
4483         SetAndShowDefaultStyle(current);
4484     }
4485     return true;
4486 }
4487 
4488 /// Apply underline to the selection
ApplyUnderlineToSelection()4489 bool wxRichTextCtrl::ApplyUnderlineToSelection()
4490 {
4491     wxRichTextAttr attr;
4492     attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
4493     attr.SetFontUnderlined(!IsSelectionUnderlined());
4494 
4495     if (HasSelection())
4496         return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
4497     else
4498     {
4499         wxRichTextAttr current = GetDefaultStyleEx();
4500         current.Apply(attr);
4501         SetAndShowDefaultStyle(current);
4502     }
4503     return true;
4504 }
4505 
4506 /// Apply the wxTextAttrEffects flag(s) to the selection, or the current caret position if there's no selection
ApplyTextEffectToSelection(int flags)4507 bool wxRichTextCtrl::ApplyTextEffectToSelection(int flags)
4508 {
4509     wxRichTextAttr attr;
4510     attr.SetFlags(wxTEXT_ATTR_EFFECTS);
4511     attr.SetTextEffectFlags(flags);
4512     if (!DoesSelectionHaveTextEffectFlag(flags))
4513         attr.SetTextEffects(flags);
4514      else
4515         attr.SetTextEffects(attr.GetTextEffectFlags() & ~flags);
4516 
4517     if (HasSelection())
4518         return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
4519     else
4520     {
4521         wxRichTextAttr current = GetDefaultStyleEx();
4522         current.Apply(attr);
4523         SetAndShowDefaultStyle(current);
4524     }
4525     return true;
4526 }
4527 
4528 /// Is all of the selection aligned according to the specified flag?
IsSelectionAligned(wxTextAttrAlignment alignment)4529 bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment)
4530 {
4531     wxRichTextRange range;
4532     if (HasSelection())
4533         range = GetSelectionRange();
4534     else
4535         range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+2);
4536 
4537     wxRichTextAttr attr;
4538     attr.SetAlignment(alignment);
4539 
4540     return HasParagraphAttributes(range, attr);
4541 }
4542 
4543 /// Apply alignment to the selection
ApplyAlignmentToSelection(wxTextAttrAlignment alignment)4544 bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
4545 {
4546     wxRichTextAttr attr;
4547     attr.SetAlignment(alignment);
4548     if (HasSelection())
4549         return SetStyle(GetSelectionRange(), attr);
4550     else
4551     {
4552         wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(GetCaretPosition()+1);
4553         if (para)
4554             return SetStyleEx(para->GetRange().FromInternal(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY);
4555     }
4556     return true;
4557 }
4558 
4559 /// Apply a named style to the selection
ApplyStyle(wxRichTextStyleDefinition * def)4560 bool wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def)
4561 {
4562     // Flags are defined within each definition, so only certain
4563     // attributes are applied.
4564     wxRichTextAttr attr(GetStyleSheet() ? def->GetStyleMergedWithBase(GetStyleSheet()) : def->GetStyle());
4565 
4566     int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_RESET;
4567 
4568     if (wxDynamicCast(def, wxRichTextListStyleDefinition))
4569     {
4570         flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
4571 
4572         wxRichTextRange range;
4573 
4574         if (HasSelection())
4575             range = GetSelectionRange();
4576         else
4577         {
4578             long pos = GetAdjustedCaretPosition(GetCaretPosition());
4579             range = wxRichTextRange(pos, pos+1);
4580         }
4581 
4582         return SetListStyle(range, (wxRichTextListStyleDefinition*) def, flags);
4583     }
4584 
4585     bool isPara = false;
4586 
4587     // Make sure the attr has the style name
4588     if (wxDynamicCast(def, wxRichTextParagraphStyleDefinition))
4589     {
4590         isPara = true;
4591         attr.SetParagraphStyleName(def->GetName());
4592 
4593         // If applying a paragraph style, we only want the paragraph nodes to adopt these
4594         // attributes, and not the leaf nodes. This will allow the content (e.g. text)
4595         // to change its style independently.
4596         flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
4597     }
4598     else if (wxDynamicCast(def, wxRichTextCharacterStyleDefinition))
4599         attr.SetCharacterStyleName(def->GetName());
4600     else if (wxDynamicCast(def, wxRichTextBoxStyleDefinition))
4601         attr.GetTextBoxAttr().SetBoxStyleName(def->GetName());
4602 
4603     if (wxDynamicCast(def, wxRichTextBoxStyleDefinition))
4604     {
4605         if (GetFocusObject() && (GetFocusObject() != & GetBuffer()))
4606         {
4607             SetStyle(GetFocusObject(), attr);
4608             return true;
4609         }
4610         else
4611             return false;
4612     }
4613     else if (HasSelection())
4614         return SetStyleEx(GetSelectionRange(), attr, flags);
4615     else
4616     {
4617         wxRichTextAttr current = GetDefaultStyleEx();
4618         wxRichTextAttr defaultStyle(attr);
4619         if (isPara)
4620         {
4621             // Don't apply extra character styles since they are already implied
4622             // in the paragraph style
4623             defaultStyle.SetFlags(defaultStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER);
4624         }
4625         current.Apply(defaultStyle);
4626         SetAndShowDefaultStyle(current);
4627 
4628         // If it's a paragraph style, we want to apply the style to the
4629         // current paragraph even if we didn't select any text.
4630         if (isPara)
4631         {
4632             long pos = GetAdjustedCaretPosition(GetCaretPosition());
4633             wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos);
4634             if (para)
4635             {
4636                 return SetStyleEx(para->GetRange().FromInternal(), attr, flags);
4637             }
4638         }
4639         return true;
4640     }
4641 }
4642 
4643 /// Apply the style sheet to the buffer, for example if the styles have changed.
ApplyStyleSheet(wxRichTextStyleSheet * styleSheet)4644 bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
4645 {
4646     if (!styleSheet)
4647         styleSheet = GetBuffer().GetStyleSheet();
4648     if (!styleSheet)
4649         return false;
4650 
4651     if (GetBuffer().ApplyStyleSheet(styleSheet))
4652     {
4653         GetBuffer().Invalidate(wxRICHTEXT_ALL);
4654         Refresh(false);
4655         return true;
4656     }
4657     else
4658         return false;
4659 }
4660 
4661 /// Sets the default style to the style under the cursor
SetDefaultStyleToCursorStyle()4662 bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
4663 {
4664     wxRichTextAttr attr;
4665     attr.SetFlags(wxTEXT_ATTR_CHARACTER);
4666 
4667     // If at the start of a paragraph, use the next position.
4668     long pos = GetAdjustedCaretPosition(GetCaretPosition());
4669 
4670     wxRichTextObject* obj = GetFocusObject()->GetLeafObjectAtPosition(pos);
4671     if (obj && obj->IsTopLevel())
4672     {
4673         // Don't use the attributes of a top-level object, since they might apply
4674         // to content of the object, e.g. background colour.
4675         SetDefaultStyle(wxRichTextAttr());
4676         return true;
4677     }
4678     else if (GetUncombinedStyle(pos, attr))
4679     {
4680         SetDefaultStyle(attr);
4681         return true;
4682     }
4683 
4684     return false;
4685 }
4686 
4687 /// Returns the first visible position in the current view
GetFirstVisiblePosition() const4688 long wxRichTextCtrl::GetFirstVisiblePosition() const
4689 {
4690     wxRichTextLine* line = GetFocusObject()->GetLineAtYPosition(GetUnscaledPoint(GetLogicalPoint(wxPoint(0, 0))).y);
4691     if (line)
4692         return line->GetAbsoluteRange().GetStart();
4693     else
4694         return 0;
4695 }
4696 
4697 /// Get the first visible point in the window
GetFirstVisiblePoint() const4698 wxPoint wxRichTextCtrl::GetFirstVisiblePoint() const
4699 {
4700     int ppuX, ppuY;
4701     int startXUnits, startYUnits;
4702 
4703     GetScrollPixelsPerUnit(& ppuX, & ppuY);
4704     GetViewStart(& startXUnits, & startYUnits);
4705 
4706     return wxPoint(startXUnits * ppuX, startYUnits * ppuY);
4707 }
4708 
4709 /// The adjusted caret position is the character position adjusted to take
4710 /// into account whether we're at the start of a paragraph, in which case
4711 /// style information should be taken from the next position, not current one.
GetAdjustedCaretPosition(long caretPos) const4712 long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const
4713 {
4714     wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(caretPos+1);
4715 
4716     if (para && (caretPos+1 == para->GetRange().GetStart()))
4717         caretPos ++;
4718     return caretPos;
4719 }
4720 
4721 /// Get/set the selection range in character positions. -1, -1 means no selection.
4722 /// The range is in API convention, i.e. a single character selection is denoted
4723 /// by (n, n+1)
GetSelectionRange() const4724 wxRichTextRange wxRichTextCtrl::GetSelectionRange() const
4725 {
4726     wxRichTextRange range = GetInternalSelectionRange();
4727     if (range != wxRichTextRange(-2,-2) && range != wxRichTextRange(-1,-1))
4728         range.SetEnd(range.GetEnd() + 1);
4729     return range;
4730 }
4731 
SetSelectionRange(const wxRichTextRange & range)4732 void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range)
4733 {
4734     SetSelection(range.GetStart(), range.GetEnd());
4735 }
4736 
4737 /// Set list style
SetListStyle(const wxRichTextRange & range,wxRichTextListStyleDefinition * def,int flags,int startFrom,int specifiedLevel)4738 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4739 {
4740     return GetFocusObject()->SetListStyle(range.ToInternal(), def, flags, startFrom, specifiedLevel);
4741 }
4742 
SetListStyle(const wxRichTextRange & range,const wxString & defName,int flags,int startFrom,int specifiedLevel)4743 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4744 {
4745     return GetFocusObject()->SetListStyle(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
4746 }
4747 
4748 /// Clear list for given range
ClearListStyle(const wxRichTextRange & range,int flags)4749 bool wxRichTextCtrl::ClearListStyle(const wxRichTextRange& range, int flags)
4750 {
4751     return GetFocusObject()->ClearListStyle(range.ToInternal(), flags);
4752 }
4753 
4754 /// Number/renumber any list elements in the given range
NumberList(const wxRichTextRange & range,wxRichTextListStyleDefinition * def,int flags,int startFrom,int specifiedLevel)4755 bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4756 {
4757     return GetFocusObject()->NumberList(range.ToInternal(), def, flags, startFrom, specifiedLevel);
4758 }
4759 
NumberList(const wxRichTextRange & range,const wxString & defName,int flags,int startFrom,int specifiedLevel)4760 bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4761 {
4762     return GetFocusObject()->NumberList(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
4763 }
4764 
4765 /// 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)4766 bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4767 {
4768     return GetFocusObject()->PromoteList(promoteBy, range.ToInternal(), def, flags, specifiedLevel);
4769 }
4770 
PromoteList(int promoteBy,const wxRichTextRange & range,const wxString & defName,int flags,int specifiedLevel)4771 bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4772 {
4773     return GetFocusObject()->PromoteList(promoteBy, range.ToInternal(), defName, flags, specifiedLevel);
4774 }
4775 
4776 // Given a character position at which there is a list style, find the range
4777 // encompassing the same list style by looking backwards and forwards.
FindRangeForList(long pos,bool & isNumberedList)4778 wxRichTextRange wxRichTextCtrl::FindRangeForList(long pos, bool& isNumberedList)
4779 {
4780     wxRichTextParagraphLayoutBox* focusObject = GetFocusObject();
4781     wxRichTextRange range = wxRichTextRange(-1, -1);
4782     wxRichTextParagraph* para = focusObject->GetParagraphAtPosition(pos);
4783     if (!para || !para->GetAttributes().HasListStyleName())
4784         return range;
4785     else
4786     {
4787         wxString listStyle = para->GetAttributes().GetListStyleName();
4788         range = para->GetRange();
4789 
4790         isNumberedList = para->GetAttributes().HasBulletNumber();
4791 
4792         // Search back
4793         wxRichTextObjectList::compatibility_iterator initialNode = focusObject->GetChildren().Find(para);
4794         if (initialNode)
4795         {
4796             wxRichTextObjectList::compatibility_iterator startNode = initialNode->GetPrevious();
4797             while (startNode)
4798             {
4799                 wxRichTextParagraph* p = wxDynamicCast(startNode->GetData(), wxRichTextParagraph);
4800                 if (p)
4801                 {
4802                     if (!p->GetAttributes().HasListStyleName() || p->GetAttributes().GetListStyleName() != listStyle)
4803                         break;
4804                     else
4805                         range.SetStart(p->GetRange().GetStart());
4806                 }
4807 
4808                 startNode = startNode->GetPrevious();
4809             }
4810 
4811             // Search forward
4812             wxRichTextObjectList::compatibility_iterator endNode = initialNode->GetNext();
4813             while (endNode)
4814             {
4815                 wxRichTextParagraph* p = wxDynamicCast(endNode->GetData(), wxRichTextParagraph);
4816                 if (p)
4817                 {
4818                     if (!p->GetAttributes().HasListStyleName() || p->GetAttributes().GetListStyleName() != listStyle)
4819                         break;
4820                     else
4821                         range.SetEnd(p->GetRange().GetEnd());
4822                 }
4823 
4824                 endNode = endNode->GetNext();
4825             }
4826         }
4827     }
4828     return range;
4829 }
4830 
4831 /// Deletes the content in the given range
Delete(const wxRichTextRange & range)4832 bool wxRichTextCtrl::Delete(const wxRichTextRange& range)
4833 {
4834     return GetFocusObject()->DeleteRangeWithUndo(range.ToInternal(), this, & GetBuffer());
4835 }
4836 
GetAvailableFontNames()4837 const wxArrayString& wxRichTextCtrl::GetAvailableFontNames()
4838 {
4839     if (sm_availableFontNames.GetCount() == 0)
4840     {
4841         sm_availableFontNames = wxFontEnumerator::GetFacenames();
4842         sm_availableFontNames.Sort();
4843     }
4844     return sm_availableFontNames;
4845 }
4846 
ClearAvailableFontNames()4847 void wxRichTextCtrl::ClearAvailableFontNames()
4848 {
4849     sm_availableFontNames.Clear();
4850 }
4851 
OnSysColourChanged(wxSysColourChangedEvent & WXUNUSED (event))4852 void wxRichTextCtrl::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
4853 {
4854     //wxLogDebug(wxT("wxRichTextCtrl::OnSysColourChanged"));
4855 
4856     wxTextAttrEx basicStyle = GetBasicStyle();
4857     basicStyle.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
4858     SetBasicStyle(basicStyle);
4859     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
4860 
4861     Refresh();
4862 }
4863 
4864 // Refresh the area affected by a selection change
RefreshForSelectionChange(const wxRichTextSelection & oldSelection,const wxRichTextSelection & newSelection)4865 bool wxRichTextCtrl::RefreshForSelectionChange(const wxRichTextSelection& oldSelection, const wxRichTextSelection& newSelection)
4866 {
4867     // If the selection is not part of the focus object, or we have multiple ranges, then the chances are that
4868     // the selection contains whole containers rather than just text, so refresh everything
4869     // for now as it would be hard to compute the rectangle bounding all selections.
4870     // TODO: improve on this.
4871     if ((oldSelection.IsValid() && (oldSelection.GetContainer() != GetFocusObject() || oldSelection.GetCount() > 1)) ||
4872         (newSelection.IsValid() && (newSelection.GetContainer() != GetFocusObject() || newSelection.GetCount() > 1)))
4873     {
4874         Refresh(false);
4875         return true;
4876     }
4877 
4878     wxRichTextRange oldRange, newRange;
4879     if (oldSelection.IsValid())
4880         oldRange = oldSelection.GetRange();
4881     else
4882         oldRange = wxRICHTEXT_NO_SELECTION;
4883     if (newSelection.IsValid())
4884         newRange = newSelection.GetRange();
4885     else
4886         newRange = wxRICHTEXT_NO_SELECTION;
4887 
4888     // Calculate the refresh rectangle - just the affected lines
4889     long firstPos, lastPos;
4890     if (oldRange.GetStart() == -2 && newRange.GetStart() != -2)
4891     {
4892         firstPos = newRange.GetStart();
4893         lastPos = newRange.GetEnd();
4894     }
4895     else if (oldRange.GetStart() != -2 && newRange.GetStart() == -2)
4896     {
4897         firstPos = oldRange.GetStart();
4898         lastPos = oldRange.GetEnd();
4899     }
4900     else if (oldRange.GetStart() == -2 && newRange.GetStart() == -2)
4901     {
4902         return false;
4903     }
4904     else
4905     {
4906         firstPos = wxMin(oldRange.GetStart(), newRange.GetStart());
4907         lastPos = wxMax(oldRange.GetEnd(), newRange.GetEnd());
4908     }
4909 
4910     wxRichTextLine* firstLine = GetFocusObject()->GetLineAtPosition(firstPos);
4911     wxRichTextLine* lastLine = GetFocusObject()->GetLineAtPosition(lastPos);
4912 
4913     if (firstLine && lastLine)
4914     {
4915         wxSize clientSize = GetClientSize();
4916         wxPoint pt1 = GetPhysicalPoint(GetScaledPoint(firstLine->GetAbsolutePosition()));
4917         wxPoint pt2 = GetPhysicalPoint(GetScaledPoint(lastLine->GetAbsolutePosition())) + wxPoint(0, (int) (0.5 + lastLine->GetSize().y * GetScale()));
4918 
4919         pt1.x = 0;
4920         pt1.y = wxMax(0, pt1.y);
4921         pt2.x = 0;
4922         pt2.y = wxMin(clientSize.y, pt2.y);
4923 
4924         // Take into account any floating objects within the selection
4925         if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFocusObject()->GetFloatingObjectCount() > 0)
4926         {
4927             wxRichTextObjectList floatingObjects;
4928             GetFocusObject()->GetFloatingObjects(floatingObjects);
4929             wxRichTextObjectList::compatibility_iterator node = floatingObjects.GetFirst();
4930             while (node)
4931             {
4932                 wxRichTextObject* obj = node->GetData();
4933                 if (obj->GetRange().GetStart() >= firstPos && obj->GetRange().GetStart() <= lastPos)
4934                 {
4935                     wxPoint pt1Obj = GetPhysicalPoint(GetScaledPoint(obj->GetPosition()));
4936                     wxPoint pt2Obj = GetPhysicalPoint(GetScaledPoint(obj->GetPosition())) + wxPoint(0, (int) (0.5 + obj->GetCachedSize().y * GetScale()));
4937                     pt1.y = wxMin(pt1.y, pt1Obj.y);
4938                     pt2.y = wxMax(pt2.y, pt2Obj.y);
4939                 }
4940                 node = node->GetNext();
4941             }
4942         }
4943 
4944         wxRect rect(pt1, wxSize(clientSize.x, pt2.y - pt1.y));
4945         RefreshRect(rect, false);
4946     }
4947     else
4948         Refresh(false);
4949 
4950     return true;
4951 }
4952 
4953 // Overrides standard refresh in order to provoke delayed image loading.
Refresh(bool eraseBackground,const wxRect * rect)4954 void wxRichTextCtrl::Refresh( bool eraseBackground, const wxRect *rect)
4955 {
4956     if (GetDelayedImageLoading())
4957         RequestDelayedImageProcessing();
4958 
4959     wxWindow::Refresh(eraseBackground, rect);
4960 }
4961 
4962 // margins functions
DoSetMargins(const wxPoint & pt)4963 bool wxRichTextCtrl::DoSetMargins(const wxPoint& pt)
4964 {
4965     GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(pt.x, wxTEXT_ATTR_UNITS_PIXELS);
4966     GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(pt.x, wxTEXT_ATTR_UNITS_PIXELS);
4967     GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(pt.y, wxTEXT_ATTR_UNITS_PIXELS);
4968     GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(pt.y, wxTEXT_ATTR_UNITS_PIXELS);
4969 
4970     return true;
4971 }
4972 
DoGetMargins() const4973 wxPoint wxRichTextCtrl::DoGetMargins() const
4974 {
4975     return wxPoint(GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue(),
4976                    GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue());
4977 }
4978 
SetFocusObject(wxRichTextParagraphLayoutBox * obj,bool setCaretPosition)4979 bool wxRichTextCtrl::SetFocusObject(wxRichTextParagraphLayoutBox* obj, bool setCaretPosition)
4980 {
4981     if (obj && !obj->AcceptsFocus())
4982         return false;
4983 
4984     wxRichTextParagraphLayoutBox* oldContainer = GetFocusObject();
4985     bool changingContainer = (m_focusObject != obj);
4986 
4987     if (changingContainer && HasSelection())
4988         SelectNone();
4989 
4990     m_focusObject = obj;
4991 
4992     if (!obj)
4993         m_focusObject = & m_buffer;
4994 
4995     if (setCaretPosition && changingContainer)
4996     {
4997         m_selection.Reset();
4998         m_selectionAnchor = -2;
4999         m_selectionAnchorObject = NULL;
5000         m_selectionState = wxRichTextCtrlSelectionState_Normal;
5001 
5002         long pos = -1;
5003 
5004         m_caretAtLineStart = false;
5005         MoveCaret(pos, m_caretAtLineStart);
5006         SetDefaultStyleToCursorStyle();
5007 
5008         wxRichTextEvent cmdEvent(
5009             wxEVT_RICHTEXT_FOCUS_OBJECT_CHANGED,
5010             GetId());
5011         cmdEvent.SetEventObject(this);
5012         cmdEvent.SetPosition(m_caretPosition+1);
5013         cmdEvent.SetOldContainer(oldContainer);
5014         cmdEvent.SetContainer(m_focusObject);
5015 
5016         GetEventHandler()->ProcessEvent(cmdEvent);
5017     }
5018     return true;
5019 }
5020 
5021 #if wxUSE_DRAG_AND_DROP
5022 // Helper function for OnDrop. Returns true if the target is contained within source,
5023 // and if it is, also returns the position relative to the source so we can properly check if it's
5024 // within the current selection.
wxRichTextCtrlIsContainedIn(wxRichTextParagraphLayoutBox * source,wxRichTextParagraphLayoutBox * target,long & position)5025 static bool wxRichTextCtrlIsContainedIn(wxRichTextParagraphLayoutBox* source, wxRichTextParagraphLayoutBox* target,
5026                                         long& position)
5027 {
5028     wxRichTextObject* t = target;
5029     while (t)
5030     {
5031         if (t->GetParent() == source)
5032         {
5033             position = t->GetRange().GetStart();
5034             return true;
5035         }
5036 
5037         t = t->GetParent();
5038     }
5039     return false;
5040 }
5041 
OnDrop(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y),wxDragResult def,wxDataObject * DataObj)5042 void wxRichTextCtrl::OnDrop(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxDragResult def, wxDataObject* DataObj)
5043 {
5044     m_preDrag = false;
5045 
5046     if ((def != wxDragCopy) && (def != wxDragMove))
5047     {
5048         return;
5049     }
5050 
5051     if (!GetSelection().IsValid())
5052     {
5053         return;
5054     }
5055 
5056     wxRichTextParagraphLayoutBox* originContainer = GetSelection().GetContainer();
5057     wxRichTextParagraphLayoutBox* destContainer = GetFocusObject(); // This will be the drop container, not necessarily the same as the origin one
5058 
5059     wxRichTextBuffer* richTextBuffer = ((wxRichTextBufferDataObject*)DataObj)->GetRichTextBuffer();
5060     if (richTextBuffer)
5061     {
5062         long position = GetCaretPosition();
5063         long positionRelativeToSource = position;
5064         wxRichTextRange selectionrange = GetInternalSelectionRange();
5065         if (def == wxDragMove)
5066         {
5067             // Are the containers the same, or is one contained within the other?
5068             if (((originContainer == destContainer) ||
5069                  wxRichTextCtrlIsContainedIn(originContainer, destContainer, positionRelativeToSource)) &&
5070                 selectionrange.Contains(positionRelativeToSource))
5071             {
5072                 // It doesn't make sense to move onto itself
5073                 return;
5074             }
5075         }
5076 
5077         // If we're moving, and the data is being moved forward, we need to drop first, then delete the selection
5078         // If moving backwards, we need to delete then drop. If we're copying (or doing nothing) we don't delete anyway
5079         bool DeleteAfter = (def == wxDragMove) && (positionRelativeToSource > selectionrange.GetEnd());
5080         if ((def == wxDragMove) && !DeleteAfter)
5081         {
5082             // We can't use e.g. DeleteSelectedContent() as it uses the focus container
5083             originContainer->DeleteRangeWithUndo(selectionrange, this, &GetBuffer());
5084         }
5085 
5086         destContainer->InsertParagraphsWithUndo(&GetBuffer(), position+1, *richTextBuffer, this, 0);
5087         ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
5088 
5089         delete richTextBuffer;
5090 
5091         if (DeleteAfter)
5092         {
5093             // We can't use e.g. DeleteSelectedContent() as it uses the focus container
5094             originContainer->DeleteRangeWithUndo(selectionrange, this, &GetBuffer());
5095         }
5096 
5097         SelectNone();
5098         Refresh();
5099     }
5100 }
5101 #endif // wxUSE_DRAG_AND_DROP
5102 
5103 
5104 #if wxUSE_DRAG_AND_DROP
GiveFeedback(wxDragResult WXUNUSED (effect))5105 bool wxRichTextDropSource::GiveFeedback(wxDragResult WXUNUSED(effect))
5106 {
5107     wxCHECK_MSG(m_rtc, false, wxT("NULL m_rtc"));
5108 
5109     long position = 0;
5110     int hit = 0;
5111     wxRichTextObject* hitObj = NULL;
5112     wxRichTextParagraphLayoutBox* container = m_rtc->FindContainerAtPoint(m_rtc->GetUnscaledPoint(m_rtc->ScreenToClient(wxGetMousePosition())), position, hit, hitObj);
5113 
5114     if (!(hit & wxRICHTEXT_HITTEST_NONE) && container && container->AcceptsFocus())
5115     {
5116         m_rtc->StoreFocusObject(container);
5117         m_rtc->SetCaretPositionAfterClick(container, position, hit);
5118     }
5119 
5120     return false;  // so that the base-class sets a cursor
5121 }
5122 #endif // wxUSE_DRAG_AND_DROP
5123 
CanDeleteRange(wxRichTextParagraphLayoutBox & WXUNUSED (container),const wxRichTextRange & WXUNUSED (range)) const5124 bool wxRichTextCtrl::CanDeleteRange(wxRichTextParagraphLayoutBox& WXUNUSED(container), const wxRichTextRange& WXUNUSED(range)) const
5125 {
5126     return true;
5127 }
5128 
CanInsertContent(wxRichTextParagraphLayoutBox & WXUNUSED (container),long WXUNUSED (pos)) const5129 bool wxRichTextCtrl::CanInsertContent(wxRichTextParagraphLayoutBox& WXUNUSED(container), long WXUNUSED(pos)) const
5130 {
5131     return true;
5132 }
5133 
EnableVerticalScrollbar(bool enable)5134 void wxRichTextCtrl::EnableVerticalScrollbar(bool enable)
5135 {
5136     m_verticalScrollbarEnabled = enable;
5137     SetupScrollbars();
5138 }
5139 
SetFontScale(double fontScale,bool refresh)5140 void wxRichTextCtrl::SetFontScale(double fontScale, bool refresh)
5141 {
5142     GetBuffer().SetFontScale(fontScale);
5143     if (refresh)
5144     {
5145         GetBuffer().Invalidate(wxRICHTEXT_ALL);
5146         Refresh();
5147     }
5148 }
5149 
SetDimensionScale(double dimScale,bool refresh)5150 void wxRichTextCtrl::SetDimensionScale(double dimScale, bool refresh)
5151 {
5152     GetBuffer().SetDimensionScale(dimScale);
5153     if (refresh)
5154     {
5155         GetBuffer().Invalidate(wxRICHTEXT_ALL);
5156         Refresh();
5157     }
5158 }
5159 
5160 // Sets an overall scale factor for displaying and editing the content.
SetScale(double scale,bool refresh)5161 void wxRichTextCtrl::SetScale(double scale, bool refresh)
5162 {
5163     m_scale = scale;
5164     if (refresh)
5165     {
5166         GetBuffer().Invalidate(wxRICHTEXT_ALL);
5167         Refresh();
5168     }
5169 }
5170 
5171 // Get an unscaled point
GetUnscaledPoint(const wxPoint & pt) const5172 wxPoint wxRichTextCtrl::GetUnscaledPoint(const wxPoint& pt) const
5173 {
5174     if (GetScale() == 1.0)
5175         return pt;
5176     else
5177         return wxPoint((int) (0.5 + double(pt.x) / GetScale()), (int) (0.5 + double(pt.y) / GetScale()));
5178 }
5179 
5180 // Get a scaled point
GetScaledPoint(const wxPoint & pt) const5181 wxPoint wxRichTextCtrl::GetScaledPoint(const wxPoint& pt) const
5182 {
5183     if (GetScale() == 1.0)
5184         return pt;
5185     else
5186         return wxPoint((int) (0.5 + double(pt.x) * GetScale()), (int) (0.5 + double(pt.y) * GetScale()));
5187 }
5188 
5189 // Get an unscaled size
GetUnscaledSize(const wxSize & sz) const5190 wxSize wxRichTextCtrl::GetUnscaledSize(const wxSize& sz) const
5191 {
5192     if (GetScale() == 1.0)
5193         return sz;
5194     else
5195         return wxSize((int) (0.5 + double(sz.x) / GetScale()), (int) (0.5 + double(sz.y) / GetScale()));
5196 }
5197 
5198 // Get a scaled size
GetScaledSize(const wxSize & sz) const5199 wxSize wxRichTextCtrl::GetScaledSize(const wxSize& sz) const
5200 {
5201     if (GetScale() == 1.0)
5202         return sz;
5203     else
5204         return wxSize((int) (0.5 + double(sz.x) * GetScale()), (int) (0.5 + double(sz.y) * GetScale()));
5205 }
5206 
5207 // Get an unscaled rect
GetUnscaledRect(const wxRect & rect) const5208 wxRect wxRichTextCtrl::GetUnscaledRect(const wxRect& rect) const
5209 {
5210     if (GetScale() == 1.0)
5211         return rect;
5212     else
5213         return wxRect((int) (0.5 + double(rect.x) / GetScale()), (int) (0.5 + double(rect.y) / GetScale()),
5214                       (int) (0.5 + double(rect.width) / GetScale()), (int) (0.5 + double(rect.height) / GetScale()));
5215 }
5216 
5217 // Get a scaled rect
GetScaledRect(const wxRect & rect) const5218 wxRect wxRichTextCtrl::GetScaledRect(const wxRect& rect) const
5219 {
5220     if (GetScale() == 1.0)
5221         return rect;
5222     else
5223         return wxRect((int) (0.5 + double(rect.x) * GetScale()), (int) (0.5 + double(rect.y) * GetScale()),
5224                       (int) (0.5 + double(rect.width) * GetScale()), (int) (0.5 + double(rect.height) * GetScale()));
5225 }
5226 
5227 // Do delayed image loading and garbage-collect other images
ProcessDelayedImageLoading(bool refresh)5228 bool wxRichTextCtrl::ProcessDelayedImageLoading(bool refresh)
5229 {
5230     int loadCount = 0;
5231 
5232     wxSize clientSize = GetUnscaledSize(GetClientSize());
5233     wxPoint firstVisiblePt = GetUnscaledPoint(GetFirstVisiblePoint());
5234     wxRect screenRect(firstVisiblePt, clientSize);
5235 
5236     // Expand screen rect so that we actually process images in the vicinity,
5237     // for smoother paging and scrolling.
5238     screenRect.y -= (clientSize.y*3);
5239     screenRect.height += (clientSize.y*6);
5240     ProcessDelayedImageLoading(screenRect, & GetBuffer(), loadCount);
5241 
5242     if (loadCount > 0 && refresh)
5243     {
5244         wxWindow::Refresh(false);
5245     }
5246 
5247     return loadCount > 0;
5248 }
5249 
ProcessDelayedImageLoading(const wxRect & screenRect,wxRichTextParagraphLayoutBox * box,int & loadCount)5250 bool wxRichTextCtrl::ProcessDelayedImageLoading(const wxRect& screenRect, wxRichTextParagraphLayoutBox* box, int& loadCount)
5251 {
5252     if (!box || !box->IsShown())
5253         return true;
5254 
5255     wxRichTextObjectList::compatibility_iterator node = box->GetChildren().GetFirst();
5256     while (node)
5257     {
5258         // Could be a cell or a paragraph
5259         wxRichTextCompositeObject* composite = wxDynamicCast(node->GetData(), wxRichTextCompositeObject);
5260         if (composite->IsTopLevel())
5261             ProcessDelayedImageLoading(screenRect, wxDynamicCast(composite, wxRichTextParagraphLayoutBox), loadCount);
5262         else // assume a paragraph
5263         {
5264             wxRichTextObjectList::compatibility_iterator node2 = composite->GetChildren().GetFirst();
5265             while (node2)
5266             {
5267                 wxRichTextObject* obj = node2->GetData();
5268                 if (obj->IsTopLevel())
5269                     ProcessDelayedImageLoading(screenRect, wxDynamicCast(obj, wxRichTextParagraphLayoutBox), loadCount);
5270                 else
5271                 {
5272                     wxRichTextImage* imageObj = wxDynamicCast(obj, wxRichTextImage);
5273                     if (imageObj && imageObj->IsShown())
5274                     {
5275                         const wxRect& rect(imageObj->GetRect());
5276                         if ((rect.GetBottom() < screenRect.GetTop()) || (rect.GetTop() > screenRect.GetBottom()))
5277                         {
5278                             // Off-screen
5279                             imageObj->ResetImageCache();
5280                         }
5281                         else
5282                         {
5283                             // On-screen
5284                             wxRichTextDrawingContext context(& GetBuffer());
5285                             context.SetLayingOut(true);
5286                             context.EnableDelayedImageLoading(false);
5287 
5288                             wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5289                             marginRect = imageObj->GetRect(); // outer rectangle, will calculate contentRect
5290                             if (marginRect.GetSize() != wxDefaultSize)
5291                             {
5292                                 wxClientDC dc(this);
5293                                 wxRichTextAttr attr(imageObj->GetAttributes());
5294                                 imageObj->AdjustAttributes(attr, context);
5295                                 imageObj->GetBoxRects(dc, & GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5296 
5297                                 wxImage image;
5298                                 bool changed = false;
5299                                 if (imageObj->LoadAndScaleImageCache(image, contentRect.GetSize(), context, changed) && changed)
5300                                 {
5301                                     loadCount ++;
5302                                 }
5303                             }
5304                         }
5305                     }
5306                 }
5307                 node2 = node2->GetNext();
5308             }
5309         }
5310 
5311         node = node->GetNext();
5312     }
5313 
5314     return true;
5315 }
5316 
RequestDelayedImageProcessing()5317 void wxRichTextCtrl::RequestDelayedImageProcessing()
5318 {
5319     SetDelayedImageProcessingRequired(true);
5320     SetDelayedImageProcessingTime(wxGetLocalTimeMillis());
5321     m_delayedImageProcessingTimer.SetOwner(this, GetId());
5322     m_delayedImageProcessingTimer.Start(wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL);
5323 }
5324 
OnTimer(wxTimerEvent & event)5325 void wxRichTextCtrl::OnTimer(wxTimerEvent& event)
5326 {
5327     if (event.GetId() == GetId())
5328         wxWakeUpIdle();
5329     else
5330         event.Skip();
5331 }
5332 
5333 #if wxRICHTEXT_USE_OWN_CARET
5334 
5335 // ----------------------------------------------------------------------------
5336 // initialization and destruction
5337 // ----------------------------------------------------------------------------
5338 
Init()5339 void wxRichTextCaret::Init()
5340 {
5341     m_hasFocus = true;
5342     m_refreshEnabled = true;
5343 
5344     m_xOld =
5345     m_yOld = -1;
5346     m_richTextCtrl = NULL;
5347     m_needsUpdate = false;
5348     m_flashOn = true;
5349 }
5350 
~wxRichTextCaret()5351 wxRichTextCaret::~wxRichTextCaret()
5352 {
5353     if (m_timer.IsRunning())
5354         m_timer.Stop();
5355 }
5356 
5357 // ----------------------------------------------------------------------------
5358 // showing/hiding/moving the caret (base class interface)
5359 // ----------------------------------------------------------------------------
5360 
DoShow()5361 void wxRichTextCaret::DoShow()
5362 {
5363     m_flashOn = true;
5364 
5365     if (!m_timer.IsRunning() && GetBlinkTime() > 0)
5366         m_timer.Start(GetBlinkTime());
5367 
5368     Refresh();
5369 }
5370 
DoHide()5371 void wxRichTextCaret::DoHide()
5372 {
5373     if (m_timer.IsRunning())
5374         m_timer.Stop();
5375 
5376     Refresh();
5377 }
5378 
DoMove()5379 void wxRichTextCaret::DoMove()
5380 {
5381     if (IsVisible())
5382     {
5383         Refresh();
5384 
5385         if (m_xOld != -1 && m_yOld != -1)
5386         {
5387             if (m_richTextCtrl && m_refreshEnabled)
5388             {
5389                 wxRect rect(wxPoint(m_xOld, m_yOld), GetSize());
5390                 wxRect scaledRect = m_richTextCtrl->GetScaledRect(rect);
5391                 m_richTextCtrl->RefreshRect(scaledRect, false);
5392             }
5393         }
5394     }
5395 
5396     m_xOld = m_x;
5397     m_yOld = m_y;
5398 }
5399 
DoSize()5400 void wxRichTextCaret::DoSize()
5401 {
5402     int countVisible = m_countVisible;
5403     if (countVisible > 0)
5404     {
5405         m_countVisible = 0;
5406         DoHide();
5407 
5408         m_countVisible = countVisible;
5409         DoShow();
5410     }
5411 }
5412 
5413 // ----------------------------------------------------------------------------
5414 // handling the focus
5415 // ----------------------------------------------------------------------------
5416 
OnSetFocus()5417 void wxRichTextCaret::OnSetFocus()
5418 {
5419     m_hasFocus = true;
5420 
5421     if ( IsVisible() )
5422         Refresh();
5423 }
5424 
OnKillFocus()5425 void wxRichTextCaret::OnKillFocus()
5426 {
5427     m_hasFocus = false;
5428 }
5429 
5430 // ----------------------------------------------------------------------------
5431 // drawing the caret
5432 // ----------------------------------------------------------------------------
5433 
Refresh()5434 void wxRichTextCaret::Refresh()
5435 {
5436     if (m_richTextCtrl && m_refreshEnabled)
5437     {
5438         wxRect rect(GetPosition(), GetSize());
5439         wxRect rectScaled = m_richTextCtrl->GetScaledRect(rect);
5440         m_richTextCtrl->RefreshRect(rectScaled, false);
5441     }
5442 }
5443 
DoDraw(wxDC * dc)5444 void wxRichTextCaret::DoDraw(wxDC *dc)
5445 {
5446     wxBrush brush(m_caretBrush);
5447     wxPen pen(m_caretPen);
5448     if (m_richTextCtrl && m_richTextCtrl->GetBasicStyle().HasTextColour())
5449     {
5450         brush = wxBrush(m_richTextCtrl->GetBasicStyle().GetTextColour());
5451         pen = wxPen(m_richTextCtrl->GetBasicStyle().GetTextColour());
5452     }
5453     dc->SetBrush((m_hasFocus ? brush : *wxTRANSPARENT_BRUSH));
5454     dc->SetPen(pen);
5455 
5456     wxPoint pt(m_x, m_y);
5457 
5458     if (m_richTextCtrl)
5459     {
5460         pt = m_richTextCtrl->GetLogicalPoint(pt);
5461     }
5462     if (IsVisible() && m_flashOn)
5463         dc->DrawRectangle(pt.x, pt.y, m_width, m_height);
5464 }
5465 
Notify()5466 void wxRichTextCaret::Notify()
5467 {
5468 #ifdef __WXMAC__
5469     // Workaround for lack of kill focus event in wxOSX
5470     if (m_richTextCtrl && !m_richTextCtrl->HasFocus())
5471     {
5472         if (IsVisible())
5473             Hide();
5474         return;
5475     }
5476 #endif
5477 
5478     m_flashOn = !m_flashOn;
5479     Refresh();
5480 }
5481 
Notify()5482 void wxRichTextCaretTimer::Notify()
5483 {
5484     m_caret->Notify();
5485 }
5486 #endif
5487     // wxRICHTEXT_USE_OWN_CARET
5488 
5489 // Add an item
AddItem(const wxString & label,wxRichTextObject * obj)5490 bool wxRichTextContextMenuPropertiesInfo::AddItem(const wxString& label, wxRichTextObject* obj)
5491 {
5492     if (GetCount() < 3)
5493     {
5494         m_labels.Add(label);
5495         m_objects.Add(obj);
5496         return true;
5497     }
5498     else
5499         return false;
5500 }
5501 
5502 // Returns number of menu items were added.
AddMenuItems(wxMenu * menu,int startCmd) const5503 int wxRichTextContextMenuPropertiesInfo::AddMenuItems(wxMenu* menu, int startCmd) const
5504 {
5505     wxMenuItem* item = menu->FindItem(startCmd);
5506     // If none of the standard properties identifiers are in the menu, add them if necessary.
5507     // If no items to add, just set the text to something generic
5508     if (GetCount() == 0)
5509     {
5510         if (item)
5511         {
5512             menu->SetLabel(startCmd, _("&Properties"));
5513 
5514             // Delete the others if necessary
5515             int i;
5516             for (i = startCmd+1; i < startCmd+3; i++)
5517             {
5518                 if (menu->FindItem(i))
5519                 {
5520                     menu->Delete(i);
5521                 }
5522             }
5523         }
5524     }
5525     else
5526     {
5527         int i;
5528         int pos = -1;
5529         // Find the position of the first properties item
5530         for (i = 0; i < (int) menu->GetMenuItemCount(); i++)
5531         {
5532             wxMenuItem* searchItem = menu->FindItemByPosition(i);
5533             if (searchItem && searchItem->GetId() == startCmd)
5534             {
5535                 pos = i;
5536                 break;
5537             }
5538         }
5539 
5540         if (pos != -1)
5541         {
5542             int insertBefore = pos+1;
5543             for (i = startCmd; i < startCmd+GetCount(); i++)
5544             {
5545                 if (menu->FindItem(i))
5546                 {
5547                     menu->SetLabel(i, m_labels[i - startCmd]);
5548                 }
5549                 else
5550                 {
5551                     if (insertBefore >= (int) menu->GetMenuItemCount())
5552                         menu->Append(i, m_labels[i - startCmd]);
5553                     else
5554                         menu->Insert(insertBefore, i, m_labels[i - startCmd]);
5555                 }
5556                 insertBefore ++;
5557             }
5558 
5559             // Delete any old items still left on the menu
5560             for (i = startCmd + GetCount(); i < startCmd+3; i++)
5561             {
5562                 if (menu->FindItem(i))
5563                 {
5564                     menu->Delete(i);
5565                 }
5566             }
5567         }
5568         else
5569         {
5570             // No existing property identifiers were found, so append to the end of the menu.
5571             menu->AppendSeparator();
5572             for (i = startCmd; i < startCmd+GetCount(); i++)
5573             {
5574                 menu->Append(i, m_labels[i - startCmd]);
5575             }
5576         }
5577     }
5578 
5579     return GetCount();
5580 }
5581 
5582 // Add appropriate menu items for the current container and clicked on object
5583 // (and container's parent, if appropriate).
AddItems(wxRichTextCtrl * ctrl,wxRichTextObject * container,wxRichTextObject * obj)5584 int wxRichTextContextMenuPropertiesInfo::AddItems(wxRichTextCtrl* ctrl, wxRichTextObject* container, wxRichTextObject* obj)
5585 {
5586     Clear();
5587     if (obj && ctrl->CanEditProperties(obj))
5588         AddItem(ctrl->GetPropertiesMenuLabel(obj), obj);
5589 
5590     if (container && container != obj && ctrl->CanEditProperties(container) && m_labels.Index(ctrl->GetPropertiesMenuLabel(container)) == wxNOT_FOUND)
5591         AddItem(ctrl->GetPropertiesMenuLabel(container), container);
5592 
5593     if (container && container->GetParent() && ctrl->CanEditProperties(container->GetParent()) && m_labels.Index(ctrl->GetPropertiesMenuLabel(container->GetParent())) == wxNOT_FOUND)
5594         AddItem(ctrl->GetPropertiesMenuLabel(container->GetParent()), container->GetParent());
5595 
5596     return GetCount();
5597 }
5598 
5599 #endif
5600     // wxUSE_RICHTEXT
5601