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