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