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