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