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