1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richeditctrl.cpp
3 // Purpose: A rich edit control
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextctrl.h"
22 #include "wx/richtext/richtextstyles.h"
26 #include "wx/settings.h"
29 #include "wx/textfile.h"
31 #include "wx/filename.h"
32 #include "wx/dcbuffer.h"
33 #include "wx/arrimpl.cpp"
34 #include "wx/fontenum.h"
36 // DLL options compatibility check:
38 WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
40 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK
)
41 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK
)
42 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK
)
43 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK
)
44 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN
)
45 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CHARACTER
)
46 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_DELETE
)
48 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
)
49 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
)
50 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGING
)
51 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGED
)
53 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
)
54 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
)
55 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
)
56 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_SELECTION_CHANGED
)
57 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
)
59 IMPLEMENT_CLASS( wxRichTextCtrl
, wxControl
)
61 IMPLEMENT_CLASS( wxRichTextEvent
, wxNotifyEvent
)
63 BEGIN_EVENT_TABLE( wxRichTextCtrl
, wxControl
)
64 EVT_PAINT(wxRichTextCtrl::OnPaint
)
65 EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground
)
66 EVT_IDLE(wxRichTextCtrl::OnIdle
)
67 EVT_SCROLLWIN(wxRichTextCtrl::OnScroll
)
68 EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick
)
69 EVT_MOTION(wxRichTextCtrl::OnMoveMouse
)
70 EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp
)
71 EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick
)
72 EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick
)
73 EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick
)
74 EVT_CHAR(wxRichTextCtrl::OnChar
)
75 EVT_SIZE(wxRichTextCtrl::OnSize
)
76 EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus
)
77 EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus
)
78 EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost
)
79 EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu
)
80 EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged
)
82 EVT_MENU(wxID_UNDO
, wxRichTextCtrl::OnUndo
)
83 EVT_UPDATE_UI(wxID_UNDO
, wxRichTextCtrl::OnUpdateUndo
)
85 EVT_MENU(wxID_REDO
, wxRichTextCtrl::OnRedo
)
86 EVT_UPDATE_UI(wxID_REDO
, wxRichTextCtrl::OnUpdateRedo
)
88 EVT_MENU(wxID_COPY
, wxRichTextCtrl::OnCopy
)
89 EVT_UPDATE_UI(wxID_COPY
, wxRichTextCtrl::OnUpdateCopy
)
91 EVT_MENU(wxID_PASTE
, wxRichTextCtrl::OnPaste
)
92 EVT_UPDATE_UI(wxID_PASTE
, wxRichTextCtrl::OnUpdatePaste
)
94 EVT_MENU(wxID_CUT
, wxRichTextCtrl::OnCut
)
95 EVT_UPDATE_UI(wxID_CUT
, wxRichTextCtrl::OnUpdateCut
)
97 EVT_MENU(wxID_CLEAR
, wxRichTextCtrl::OnClear
)
98 EVT_UPDATE_UI(wxID_CLEAR
, wxRichTextCtrl::OnUpdateClear
)
100 EVT_MENU(wxID_SELECTALL
, wxRichTextCtrl::OnSelectAll
)
101 EVT_UPDATE_UI(wxID_SELECTALL
, wxRichTextCtrl::OnUpdateSelectAll
)
108 wxArrayString
wxRichTextCtrl::sm_availableFontNames
;
110 wxRichTextCtrl::wxRichTextCtrl()
111 : wxScrollHelper(this)
116 wxRichTextCtrl::wxRichTextCtrl(wxWindow
* parent
,
118 const wxString
& value
,
122 const wxValidator
& validator
,
123 const wxString
& name
)
124 : wxScrollHelper(this)
127 Create(parent
, id
, value
, pos
, size
, style
, validator
, name
);
131 bool wxRichTextCtrl::Create( wxWindow
* parent
, wxWindowID id
, const wxString
& value
, const wxPoint
& pos
, const wxSize
& size
, long style
,
132 const wxValidator
& validator
, const wxString
& name
)
134 if (!wxControl::Create(parent
, id
, pos
, size
,
135 style
|wxFULL_REPAINT_ON_RESIZE
,
141 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
144 if (style
& wxTE_READONLY
)
147 // The base attributes must all have default values
148 wxTextAttr attributes
;
149 attributes
.SetFont(GetFont());
150 attributes
.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
151 attributes
.SetAlignment(wxTEXT_ALIGNMENT_LEFT
);
152 attributes
.SetLineSpacing(10);
153 attributes
.SetParagraphSpacingAfter(10);
154 attributes
.SetParagraphSpacingBefore(0);
155 attributes
.SetTextEffects(0);
156 attributes
.SetTextEffectFlags(wxTEXT_ATTR_EFFECT_STRIKETHROUGH
|wxTEXT_ATTR_EFFECT_CAPITALS
);
158 SetBasicStyle(attributes
);
160 // The default attributes will be merged with base attributes, so
161 // can be empty to begin with
162 wxTextAttr defaultAttributes
;
163 SetDefaultStyle(defaultAttributes
);
165 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
));
166 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
169 GetBuffer().SetRichTextCtrl(this);
171 SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH
, 16));
174 // Tell the sizers to use the given or best size
175 SetInitialSize(size
);
177 #if wxRICHTEXT_BUFFERED_PAINTING
179 RecreateBuffer(size
);
182 m_textCursor
= wxCursor(wxCURSOR_IBEAM
);
183 m_urlCursor
= wxCursor(wxCURSOR_HAND
);
185 SetCursor(m_textCursor
);
187 if (!value
.IsEmpty())
190 GetBuffer().AddEventHandler(this);
195 wxRichTextCtrl::~wxRichTextCtrl()
197 GetBuffer().RemoveEventHandler(this);
199 delete m_contextMenu
;
202 /// Member initialisation
203 void wxRichTextCtrl::Init()
205 m_contextMenu
= NULL
;
207 m_caretPosition
= -1;
208 m_selectionRange
.SetRange(-2, -2);
209 m_selectionAnchor
= -2;
211 m_caretAtLineStart
= false;
213 m_fullLayoutRequired
= false;
214 m_fullLayoutTime
= 0;
215 m_fullLayoutSavedPosition
= 0;
216 m_delayedLayoutThreshold
= wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD
;
217 m_caretPositionForDefaultStyle
= -2;
220 void wxRichTextCtrl::DoThaw()
222 if (GetBuffer().GetDirty())
230 void wxRichTextCtrl::Clear()
232 m_buffer
.ResetAndClearCommands();
233 m_buffer
.SetDirty(true);
234 m_caretPosition
= -1;
235 m_caretPositionForDefaultStyle
= -2;
236 m_caretAtLineStart
= false;
237 m_selectionRange
.SetRange(-2, -2);
247 wxTextCtrl::SendTextUpdatedEvent(this);
251 void wxRichTextCtrl::OnPaint(wxPaintEvent
& WXUNUSED(event
))
253 if (GetCaret() && GetCaret()->IsVisible())
257 #if wxRICHTEXT_BUFFERED_PAINTING
258 wxBufferedPaintDC
dc(this, m_bufferBitmap
);
268 dc
.SetFont(GetFont());
270 // Paint the background
273 // wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
275 wxRect
drawingArea(GetUpdateRegion().GetBox());
276 drawingArea
.SetPosition(GetLogicalPoint(drawingArea
.GetPosition()));
278 wxRect
availableSpace(GetClientSize());
279 if (GetBuffer().GetDirty())
281 GetBuffer().Layout(dc
, availableSpace
, wxRICHTEXT_FIXED_WIDTH
|wxRICHTEXT_VARIABLE_HEIGHT
);
282 GetBuffer().SetDirty(false);
286 GetBuffer().Draw(dc
, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea
, 0 /* descent */, 0 /* flags */);
289 if (GetCaret() && !GetCaret()->IsVisible())
295 // Empty implementation, to prevent flicker
296 void wxRichTextCtrl::OnEraseBackground(wxEraseEvent
& WXUNUSED(event
))
300 void wxRichTextCtrl::OnSetFocus(wxFocusEvent
& WXUNUSED(event
))
304 if (!GetCaret()->IsVisible())
313 void wxRichTextCtrl::OnKillFocus(wxFocusEvent
& WXUNUSED(event
))
315 if (GetCaret() && GetCaret()->IsVisible())
322 void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent
& WXUNUSED(event
))
328 void wxRichTextCtrl::OnLeftClick(wxMouseEvent
& event
)
334 dc
.SetFont(GetFont());
337 int hit
= GetBuffer().HitTest(dc
, event
.GetLogicalPosition(dc
), position
);
339 if (hit
!= wxRICHTEXT_HITTEST_NONE
)
341 m_dragStart
= event
.GetLogicalPosition(dc
);
345 bool caretAtLineStart
= false;
347 if (hit
& wxRICHTEXT_HITTEST_BEFORE
)
349 // If we're at the start of a line (but not first in para)
350 // then we should keep the caret showing at the start of the line
351 // by showing the m_caretAtLineStart flag.
352 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(position
);
353 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(position
);
355 if (line
&& para
&& line
->GetAbsoluteRange().GetStart() == position
&& para
->GetRange().GetStart() != position
)
356 caretAtLineStart
= true;
360 long oldCaretPos
= m_caretPosition
;
362 MoveCaret(position
, caretAtLineStart
);
363 SetDefaultStyleToCursorStyle();
365 if (event
.ShiftDown())
367 if (m_selectionRange
.GetStart() == -2)
368 ExtendSelection(oldCaretPos
, m_caretPosition
, wxRICHTEXT_SHIFT_DOWN
);
370 ExtendSelection(m_caretPosition
, m_caretPosition
, wxRICHTEXT_SHIFT_DOWN
);
380 void wxRichTextCtrl::OnLeftUp(wxMouseEvent
& event
)
385 if (GetCapture() == this)
388 // See if we clicked on a URL
391 dc
.SetFont(GetFont());
394 wxPoint logicalPt
= event
.GetLogicalPosition(dc
);
395 int hit
= GetBuffer().HitTest(dc
, logicalPt
, position
);
397 if ((hit
!= wxRICHTEXT_HITTEST_NONE
) && !(hit
& wxRICHTEXT_HITTEST_OUTSIDE
))
399 wxRichTextEvent
cmdEvent(
400 wxEVT_COMMAND_RICHTEXT_LEFT_CLICK
,
402 cmdEvent
.SetEventObject(this);
403 cmdEvent
.SetPosition(m_caretPosition
+1);
405 if (!GetEventHandler()->ProcessEvent(cmdEvent
))
408 if (GetStyle(position
, attr
))
410 if (attr
.HasFlag(wxTEXT_ATTR_URL
))
412 wxString urlTarget
= attr
.GetURL();
413 if (!urlTarget
.IsEmpty())
415 wxMouseEvent
mouseEvent(event
);
417 long startPos
= 0, endPos
= 0;
418 wxRichTextObject
* obj
= GetBuffer().GetLeafObjectAtPosition(position
);
421 startPos
= obj
->GetRange().GetStart();
422 endPos
= obj
->GetRange().GetEnd();
425 wxTextUrlEvent
urlEvent(GetId(), mouseEvent
, startPos
, endPos
);
426 InitCommandEvent(urlEvent
);
428 urlEvent
.SetString(urlTarget
);
430 GetEventHandler()->ProcessEvent(urlEvent
);
440 void wxRichTextCtrl::OnMoveMouse(wxMouseEvent
& event
)
444 dc
.SetFont(GetFont());
447 wxPoint logicalPt
= event
.GetLogicalPosition(dc
);
448 int hit
= GetBuffer().HitTest(dc
, logicalPt
, position
);
450 // See if we need to change the cursor
453 if (hit
!= wxRICHTEXT_HITTEST_NONE
&& !(hit
& wxRICHTEXT_HITTEST_OUTSIDE
))
456 if (GetStyle(position
, attr
))
458 if (attr
.HasFlag(wxTEXT_ATTR_URL
))
460 SetCursor(m_urlCursor
);
462 else if (!attr
.HasFlag(wxTEXT_ATTR_URL
))
464 SetCursor(m_textCursor
);
469 SetCursor(m_textCursor
);
472 if (!event
.Dragging())
478 if (m_dragging
&& hit
!= wxRICHTEXT_HITTEST_NONE
)
480 // TODO: test closeness
482 bool caretAtLineStart
= false;
484 if (hit
& wxRICHTEXT_HITTEST_BEFORE
)
486 // If we're at the start of a line (but not first in para)
487 // then we should keep the caret showing at the start of the line
488 // by showing the m_caretAtLineStart flag.
489 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(position
);
490 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(position
);
492 if (line
&& para
&& line
->GetAbsoluteRange().GetStart() == position
&& para
->GetRange().GetStart() != position
)
493 caretAtLineStart
= true;
497 if (m_caretPosition
!= position
)
499 ExtendSelection(m_caretPosition
, position
, wxRICHTEXT_SHIFT_DOWN
);
501 MoveCaret(position
, caretAtLineStart
);
502 SetDefaultStyleToCursorStyle();
508 void wxRichTextCtrl::OnRightClick(wxMouseEvent
& event
)
512 wxRichTextEvent
cmdEvent(
513 wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK
,
515 cmdEvent
.SetEventObject(this);
516 cmdEvent
.SetPosition(m_caretPosition
+1);
518 if (!GetEventHandler()->ProcessEvent(cmdEvent
))
522 /// Left-double-click
523 void wxRichTextCtrl::OnLeftDClick(wxMouseEvent
& WXUNUSED(event
))
525 wxRichTextEvent
cmdEvent(
526 wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK
,
528 cmdEvent
.SetEventObject(this);
529 cmdEvent
.SetPosition(m_caretPosition
+1);
531 if (!GetEventHandler()->ProcessEvent(cmdEvent
))
533 SelectWord(GetCaretPosition()+1);
538 void wxRichTextCtrl::OnMiddleClick(wxMouseEvent
& event
)
540 wxRichTextEvent
cmdEvent(
541 wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK
,
543 cmdEvent
.SetEventObject(this);
544 cmdEvent
.SetPosition(m_caretPosition
+1);
546 if (!GetEventHandler()->ProcessEvent(cmdEvent
))
551 void wxRichTextCtrl::OnChar(wxKeyEvent
& event
)
555 flags
|= wxRICHTEXT_CTRL_DOWN
;
556 if (event
.ShiftDown())
557 flags
|= wxRICHTEXT_SHIFT_DOWN
;
559 flags
|= wxRICHTEXT_ALT_DOWN
;
561 if (event
.GetKeyCode() == WXK_LEFT
||
562 event
.GetKeyCode() == WXK_RIGHT
||
563 event
.GetKeyCode() == WXK_UP
||
564 event
.GetKeyCode() == WXK_DOWN
||
565 event
.GetKeyCode() == WXK_HOME
||
566 event
.GetKeyCode() == WXK_PAGEUP
||
567 event
.GetKeyCode() == WXK_PAGEDOWN
||
568 event
.GetKeyCode() == WXK_END
||
570 event
.GetKeyCode() == WXK_NUMPAD_LEFT
||
571 event
.GetKeyCode() == WXK_NUMPAD_RIGHT
||
572 event
.GetKeyCode() == WXK_NUMPAD_UP
||
573 event
.GetKeyCode() == WXK_NUMPAD_DOWN
||
574 event
.GetKeyCode() == WXK_NUMPAD_HOME
||
575 event
.GetKeyCode() == WXK_NUMPAD_PAGEUP
||
576 event
.GetKeyCode() == WXK_NUMPAD_PAGEDOWN
||
577 event
.GetKeyCode() == WXK_NUMPAD_END
)
579 KeyboardNavigate(event
.GetKeyCode(), flags
);
583 // all the other keys modify the controls contents which shouldn't be
584 // possible if we're read-only
591 if (event
.GetKeyCode() == WXK_RETURN
)
593 BeginBatchUndo(_("Insert Text"));
595 long newPos
= m_caretPosition
;
597 DeleteSelectedContent(& newPos
);
599 if (event
.ShiftDown())
602 text
= wxRichTextLineBreakChar
;
603 GetBuffer().InsertTextWithUndo(newPos
+1, text
, this);
606 GetBuffer().InsertNewlineWithUndo(newPos
+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
|wxRICHTEXT_INSERT_INTERACTIVE
);
609 SetDefaultStyleToCursorStyle();
611 ScrollIntoView(m_caretPosition
, WXK_RIGHT
);
613 wxRichTextEvent
cmdEvent(
614 wxEVT_COMMAND_RICHTEXT_RETURN
,
616 cmdEvent
.SetEventObject(this);
617 cmdEvent
.SetFlags(flags
);
618 cmdEvent
.SetPosition(newPos
+1);
620 if (!GetEventHandler()->ProcessEvent(cmdEvent
))
622 // Generate conventional event
623 wxCommandEvent
textEvent(wxEVT_COMMAND_TEXT_ENTER
, GetId());
624 InitCommandEvent(textEvent
);
626 GetEventHandler()->ProcessEvent(textEvent
);
630 else if (event
.GetKeyCode() == WXK_BACK
)
632 BeginBatchUndo(_("Delete Text"));
634 // Submit range in character positions, which are greater than caret positions,
635 // so subtract 1 for deleted character and add 1 for conversion to character position.
636 if (m_caretPosition
> -1 && !HasSelection())
638 bool processed
= false;
641 long pos
= wxRichTextCtrl::FindNextWordPosition(-1);
642 if (pos
!= -1 && (pos
< m_caretPosition
))
644 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(pos
+1, m_caretPosition
), this);
650 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition
, m_caretPosition
), this);
653 DeleteSelectedContent();
657 if (GetLastPosition() == -1)
661 m_caretPosition
= -1;
663 SetDefaultStyleToCursorStyle();
666 ScrollIntoView(m_caretPosition
, WXK_LEFT
);
668 wxRichTextEvent
cmdEvent(
669 wxEVT_COMMAND_RICHTEXT_DELETE
,
671 cmdEvent
.SetEventObject(this);
672 cmdEvent
.SetFlags(flags
);
673 cmdEvent
.SetPosition(m_caretPosition
+1);
674 GetEventHandler()->ProcessEvent(cmdEvent
);
678 else if (event
.GetKeyCode() == WXK_DELETE
)
680 BeginBatchUndo(_("Delete Text"));
682 // Submit range in character positions, which are greater than caret positions,
683 if (m_caretPosition
< GetBuffer().GetRange().GetEnd()+1 && !HasSelection())
685 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition
+1, m_caretPosition
+1), this);
688 DeleteSelectedContent();
692 if (GetLastPosition() == -1)
696 m_caretPosition
= -1;
698 SetDefaultStyleToCursorStyle();
701 wxRichTextEvent
cmdEvent(
702 wxEVT_COMMAND_RICHTEXT_DELETE
,
704 cmdEvent
.SetEventObject(this);
705 cmdEvent
.SetFlags(flags
);
706 cmdEvent
.SetPosition(m_caretPosition
+1);
707 GetEventHandler()->ProcessEvent(cmdEvent
);
713 long keycode
= event
.GetKeyCode();
786 case WXK_NUMPAD_SPACE
:
788 case WXK_NUMPAD_ENTER
:
793 case WXK_NUMPAD_HOME
:
794 case WXK_NUMPAD_LEFT
:
796 case WXK_NUMPAD_RIGHT
:
797 case WXK_NUMPAD_DOWN
:
798 case WXK_NUMPAD_PAGEUP
:
799 case WXK_NUMPAD_PAGEDOWN
:
801 case WXK_NUMPAD_BEGIN
:
802 case WXK_NUMPAD_INSERT
:
803 case WXK_NUMPAD_DELETE
:
804 case WXK_NUMPAD_EQUAL
:
805 case WXK_NUMPAD_MULTIPLY
:
807 case WXK_NUMPAD_SEPARATOR
:
808 case WXK_NUMPAD_SUBTRACT
:
809 case WXK_NUMPAD_DECIMAL
:
810 case WXK_WINDOWS_LEFT
:
818 if (event
.CmdDown() || event
.AltDown())
824 wxRichTextEvent
cmdEvent(
825 wxEVT_COMMAND_RICHTEXT_CHARACTER
,
827 cmdEvent
.SetEventObject(this);
828 cmdEvent
.SetFlags(flags
);
830 cmdEvent
.SetCharacter(event
.GetUnicodeKey());
832 cmdEvent
.SetCharacter((wxChar
) keycode
);
834 cmdEvent
.SetPosition(m_caretPosition
+1);
836 if (keycode
== wxT('\t'))
838 // See if we need to promote or demote the selection or paragraph at the cursor
839 // position, instead of inserting a tab.
840 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
841 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(pos
);
842 if (para
&& para
->GetRange().GetStart() == pos
&& para
->GetAttributes().HasListStyleName())
844 wxRichTextRange range
;
846 range
= GetSelectionRange();
848 range
= para
->GetRange().FromInternal();
850 int promoteBy
= event
.ShiftDown() ? 1 : -1;
852 PromoteList(promoteBy
, range
, NULL
);
854 GetEventHandler()->ProcessEvent(cmdEvent
);
860 BeginBatchUndo(_("Insert Text"));
862 long newPos
= m_caretPosition
;
863 DeleteSelectedContent(& newPos
);
866 wxString str
= event
.GetUnicodeKey();
868 wxString str
= (wxChar
) event
.GetKeyCode();
870 GetBuffer().InsertTextWithUndo(newPos
+1, str
, this, 0);
874 SetDefaultStyleToCursorStyle();
875 ScrollIntoView(m_caretPosition
, WXK_RIGHT
);
877 GetEventHandler()->ProcessEvent(cmdEvent
);
885 /// Delete content if there is a selection, e.g. when pressing a key.
886 bool wxRichTextCtrl::DeleteSelectedContent(long* newPos
)
890 long pos
= m_selectionRange
.GetStart();
891 GetBuffer().DeleteRangeWithUndo(m_selectionRange
, this);
892 m_selectionRange
.SetRange(-2, -2);
902 /// Keyboard navigation
906 Left: left one character
907 Right: right one character
910 Ctrl-Left: left one word
911 Ctrl-Right: right one word
912 Ctrl-Up: previous paragraph start
913 Ctrl-Down: next start of paragraph
916 Ctrl-Home: start of document
917 Ctrl-End: end of document
919 Page-Down: Down a screen
923 Ctrl-Alt-PgUp: Start of window
924 Ctrl-Alt-PgDn: End of window
925 F8: Start selection mode
926 Esc: End selection mode
928 Adding Shift does the above but starts/extends selection.
933 bool wxRichTextCtrl::KeyboardNavigate(int keyCode
, int flags
)
935 bool success
= false;
937 if (keyCode
== WXK_RIGHT
|| keyCode
== WXK_NUMPAD_RIGHT
)
939 if (flags
& wxRICHTEXT_CTRL_DOWN
)
940 success
= WordRight(1, flags
);
942 success
= MoveRight(1, flags
);
944 else if (keyCode
== WXK_LEFT
|| keyCode
== WXK_NUMPAD_LEFT
)
946 if (flags
& wxRICHTEXT_CTRL_DOWN
)
947 success
= WordLeft(1, flags
);
949 success
= MoveLeft(1, flags
);
951 else if (keyCode
== WXK_UP
|| keyCode
== WXK_NUMPAD_UP
)
953 if (flags
& wxRICHTEXT_CTRL_DOWN
)
954 success
= MoveToParagraphStart(flags
);
956 success
= MoveUp(1, flags
);
958 else if (keyCode
== WXK_DOWN
|| keyCode
== WXK_NUMPAD_DOWN
)
960 if (flags
& wxRICHTEXT_CTRL_DOWN
)
961 success
= MoveToParagraphEnd(flags
);
963 success
= MoveDown(1, flags
);
965 else if (keyCode
== WXK_PAGEUP
|| keyCode
== WXK_NUMPAD_PAGEUP
)
967 success
= PageUp(1, flags
);
969 else if (keyCode
== WXK_PAGEDOWN
|| keyCode
== WXK_NUMPAD_PAGEDOWN
)
971 success
= PageDown(1, flags
);
973 else if (keyCode
== WXK_HOME
|| keyCode
== WXK_NUMPAD_HOME
)
975 if (flags
& wxRICHTEXT_CTRL_DOWN
)
976 success
= MoveHome(flags
);
978 success
= MoveToLineStart(flags
);
980 else if (keyCode
== WXK_END
|| keyCode
== WXK_NUMPAD_END
)
982 if (flags
& wxRICHTEXT_CTRL_DOWN
)
983 success
= MoveEnd(flags
);
985 success
= MoveToLineEnd(flags
);
990 ScrollIntoView(m_caretPosition
, keyCode
);
991 SetDefaultStyleToCursorStyle();
997 /// Extend the selection. Selections are in caret positions.
998 bool wxRichTextCtrl::ExtendSelection(long oldPos
, long newPos
, int flags
)
1000 if (flags
& wxRICHTEXT_SHIFT_DOWN
)
1002 wxRichTextRange oldSelection
= m_selectionRange
;
1004 // If not currently selecting, start selecting
1005 if (m_selectionRange
.GetStart() == -2)
1007 m_selectionAnchor
= oldPos
;
1009 if (oldPos
> newPos
)
1010 m_selectionRange
.SetRange(newPos
+1, oldPos
);
1012 m_selectionRange
.SetRange(oldPos
+1, newPos
);
1016 // Always ensure that the selection range start is greater than
1018 if (newPos
> m_selectionAnchor
)
1019 m_selectionRange
.SetRange(m_selectionAnchor
+1, newPos
);
1021 m_selectionRange
.SetRange(newPos
+1, m_selectionAnchor
);
1024 RefreshForSelectionChange(oldSelection
, m_selectionRange
);
1026 if (m_selectionRange
.GetStart() > m_selectionRange
.GetEnd())
1028 wxLogDebug(wxT("Strange selection range"));
1037 /// Scroll into view, returning true if we scrolled.
1038 /// This takes a _caret_ position.
1039 bool wxRichTextCtrl::ScrollIntoView(long position
, int keyCode
)
1041 wxRichTextLine
* line
= GetVisibleLineForCaretPosition(position
);
1047 GetScrollPixelsPerUnit(& ppuX
, & ppuY
);
1049 int startXUnits
, startYUnits
;
1050 GetViewStart(& startXUnits
, & startYUnits
);
1051 int startY
= startYUnits
* ppuY
;
1054 GetVirtualSize(& sx
, & sy
);
1060 wxRect rect
= line
->GetRect();
1062 bool scrolled
= false;
1064 wxSize clientSize
= GetClientSize();
1067 if (keyCode
== WXK_DOWN
|| keyCode
== WXK_NUMPAD_DOWN
||
1068 keyCode
== WXK_RIGHT
|| keyCode
== WXK_NUMPAD_RIGHT
||
1069 keyCode
== WXK_END
|| keyCode
== WXK_NUMPAD_END
||
1070 keyCode
== WXK_PAGEDOWN
|| keyCode
== WXK_NUMPAD_PAGEDOWN
)
1072 if ((rect
.y
+ rect
.height
) > (clientSize
.y
+ startY
))
1074 // Make it scroll so this item is at the bottom
1076 int y
= rect
.y
- (clientSize
.y
- rect
.height
);
1077 int yUnits
= (int) (0.5 + ((float) y
)/(float) ppuY
);
1079 // If we're still off the screen, scroll another line down
1080 if ((rect
.y
+ rect
.height
) > (clientSize
.y
+ (yUnits
*ppuY
)))
1083 if (startYUnits
!= yUnits
)
1085 SetScrollbars(ppuX
, ppuY
, sxUnits
, syUnits
, 0, yUnits
);
1089 else if (rect
.y
< startY
)
1091 // Make it scroll so this item is at the top
1094 int yUnits
= (int) (0.5 + ((float) y
)/(float) ppuY
);
1096 if (startYUnits
!= yUnits
)
1098 SetScrollbars(ppuX
, ppuY
, sxUnits
, syUnits
, 0, yUnits
);
1104 else if (keyCode
== WXK_UP
|| keyCode
== WXK_NUMPAD_UP
||
1105 keyCode
== WXK_LEFT
|| keyCode
== WXK_NUMPAD_LEFT
||
1106 keyCode
== WXK_HOME
|| keyCode
== WXK_NUMPAD_HOME
||
1107 keyCode
== WXK_PAGEUP
|| keyCode
== WXK_NUMPAD_PAGEUP
)
1109 if (rect
.y
< startY
)
1111 // Make it scroll so this item is at the top
1114 int yUnits
= (int) (0.5 + ((float) y
)/(float) ppuY
);
1116 if (startYUnits
!= yUnits
)
1118 SetScrollbars(ppuX
, ppuY
, sxUnits
, syUnits
, 0, yUnits
);
1122 else if ((rect
.y
+ rect
.height
) > (clientSize
.y
+ startY
))
1124 // Make it scroll so this item is at the bottom
1126 int y
= rect
.y
- (clientSize
.y
- rect
.height
);
1127 int yUnits
= (int) (0.5 + ((float) y
)/(float) ppuY
);
1129 // If we're still off the screen, scroll another line down
1130 if ((rect
.y
+ rect
.height
) > (clientSize
.y
+ (yUnits
*ppuY
)))
1133 if (startYUnits
!= yUnits
)
1135 SetScrollbars(ppuX
, ppuY
, sxUnits
, syUnits
, 0, yUnits
);
1147 /// Is the given position visible on the screen?
1148 bool wxRichTextCtrl::IsPositionVisible(long pos
) const
1150 wxRichTextLine
* line
= GetVisibleLineForCaretPosition(pos
-1);
1156 GetScrollPixelsPerUnit(& ppuX
, & ppuY
);
1159 GetViewStart(& startX
, & startY
);
1161 startY
= startY
* ppuY
;
1163 wxRect rect
= line
->GetRect();
1164 wxSize clientSize
= GetClientSize();
1166 return (rect
.GetBottom() > startY
) && (rect
.GetTop() < (startY
+ clientSize
.y
));
1169 void wxRichTextCtrl::SetCaretPosition(long position
, bool showAtLineStart
)
1171 m_caretPosition
= position
;
1172 m_caretAtLineStart
= showAtLineStart
;
1175 /// Move caret one visual step forward: this may mean setting a flag
1176 /// and keeping the same position if we're going from the end of one line
1177 /// to the start of the next, which may be the exact same caret position.
1178 void wxRichTextCtrl::MoveCaretForward(long oldPosition
)
1180 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(oldPosition
);
1182 // Only do the check if we're not at the end of the paragraph (where things work OK
1184 if (para
&& (oldPosition
!= para
->GetRange().GetEnd() - 1))
1186 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(oldPosition
);
1190 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
1192 // We're at the end of a line. See whether we need to
1193 // stay at the same actual caret position but change visual
1194 // position, or not.
1195 if (oldPosition
== lineRange
.GetEnd())
1197 if (m_caretAtLineStart
)
1199 // We're already at the start of the line, so actually move on now.
1200 m_caretPosition
= oldPosition
+ 1;
1201 m_caretAtLineStart
= false;
1205 // We're showing at the end of the line, so keep to
1206 // the same position but indicate that we're to show
1207 // at the start of the next line.
1208 m_caretPosition
= oldPosition
;
1209 m_caretAtLineStart
= true;
1211 SetDefaultStyleToCursorStyle();
1217 SetDefaultStyleToCursorStyle();
1220 /// Move caret one visual step backward: this may mean setting a flag
1221 /// and keeping the same position if we're going from the end of one line
1222 /// to the start of the next, which may be the exact same caret position.
1223 void wxRichTextCtrl::MoveCaretBack(long oldPosition
)
1225 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(oldPosition
);
1227 // Only do the check if we're not at the start of the paragraph (where things work OK
1229 if (para
&& (oldPosition
!= para
->GetRange().GetStart()))
1231 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(oldPosition
);
1235 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
1237 // We're at the start of a line. See whether we need to
1238 // stay at the same actual caret position but change visual
1239 // position, or not.
1240 if (oldPosition
== lineRange
.GetStart())
1242 m_caretPosition
= oldPosition
-1;
1243 m_caretAtLineStart
= true;
1246 else if (oldPosition
== lineRange
.GetEnd())
1248 if (m_caretAtLineStart
)
1250 // We're at the start of the line, so keep the same caret position
1251 // but clear the start-of-line flag.
1252 m_caretPosition
= oldPosition
;
1253 m_caretAtLineStart
= false;
1257 // We're showing at the end of the line, so go back
1258 // to the previous character position.
1259 m_caretPosition
= oldPosition
- 1;
1261 SetDefaultStyleToCursorStyle();
1267 SetDefaultStyleToCursorStyle();
1271 bool wxRichTextCtrl::MoveRight(int noPositions
, int flags
)
1273 long endPos
= GetBuffer().GetRange().GetEnd();
1275 if (m_caretPosition
+ noPositions
< endPos
)
1277 long oldPos
= m_caretPosition
;
1278 long newPos
= m_caretPosition
+ noPositions
;
1280 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1284 // Determine by looking at oldPos and m_caretPosition whether
1285 // we moved from the end of a line to the start of the next line, in which case
1286 // we want to adjust the caret position such that it is positioned at the
1287 // start of the next line, rather than jumping past the first character of the
1289 if (noPositions
== 1 && !extendSel
)
1290 MoveCaretForward(oldPos
);
1292 SetCaretPosition(newPos
);
1295 SetDefaultStyleToCursorStyle();
1304 bool wxRichTextCtrl::MoveLeft(int noPositions
, int flags
)
1308 if (m_caretPosition
> startPos
- noPositions
+ 1)
1310 long oldPos
= m_caretPosition
;
1311 long newPos
= m_caretPosition
- noPositions
;
1312 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1316 if (noPositions
== 1 && !extendSel
)
1317 MoveCaretBack(oldPos
);
1319 SetCaretPosition(newPos
);
1322 SetDefaultStyleToCursorStyle();
1331 bool wxRichTextCtrl::MoveUp(int noLines
, int flags
)
1333 return MoveDown(- noLines
, flags
);
1337 bool wxRichTextCtrl::MoveDown(int noLines
, int flags
)
1342 long lineNumber
= GetBuffer().GetVisibleLineNumber(m_caretPosition
, true, m_caretAtLineStart
);
1343 wxPoint pt
= GetCaret()->GetPosition();
1344 long newLine
= lineNumber
+ noLines
;
1346 if (lineNumber
!= -1)
1350 long lastLine
= GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd());
1352 if (newLine
> lastLine
)
1362 wxRichTextLine
* lineObj
= GetBuffer().GetLineForVisibleLineNumber(newLine
);
1365 pt
.y
= lineObj
->GetAbsolutePosition().y
+ 2;
1371 wxClientDC
dc(this);
1373 dc
.SetFont(GetFont());
1375 int hitTest
= GetBuffer().HitTest(dc
, pt
, newPos
);
1377 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
1379 // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
1380 // we want to be at the end of the last line but with m_caretAtLineStart set to true,
1381 // so we view the caret at the start of the line.
1382 bool caretLineStart
= false;
1383 if (hitTest
& wxRICHTEXT_HITTEST_BEFORE
)
1385 wxRichTextLine
* thisLine
= GetBuffer().GetLineAtPosition(newPos
-1);
1386 wxRichTextRange lineRange
;
1388 lineRange
= thisLine
->GetAbsoluteRange();
1390 if (thisLine
&& (newPos
-1) == lineRange
.GetEnd())
1393 caretLineStart
= true;
1397 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(newPos
);
1398 if (para
&& para
->GetRange().GetStart() == newPos
)
1403 long newSelEnd
= newPos
;
1405 bool extendSel
= ExtendSelection(m_caretPosition
, newSelEnd
, flags
);
1409 SetCaretPosition(newPos
, caretLineStart
);
1411 SetDefaultStyleToCursorStyle();
1419 /// Move to the end of the paragraph
1420 bool wxRichTextCtrl::MoveToParagraphEnd(int flags
)
1422 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(m_caretPosition
, true);
1425 long newPos
= para
->GetRange().GetEnd() - 1;
1426 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1430 SetCaretPosition(newPos
);
1432 SetDefaultStyleToCursorStyle();
1440 /// Move to the start of the paragraph
1441 bool wxRichTextCtrl::MoveToParagraphStart(int flags
)
1443 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(m_caretPosition
, true);
1446 long newPos
= para
->GetRange().GetStart() - 1;
1447 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1451 SetCaretPosition(newPos
);
1453 SetDefaultStyleToCursorStyle();
1461 /// Move to the end of the line
1462 bool wxRichTextCtrl::MoveToLineEnd(int flags
)
1464 wxRichTextLine
* line
= GetVisibleLineForCaretPosition(m_caretPosition
);
1468 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
1469 long newPos
= lineRange
.GetEnd();
1470 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1474 SetCaretPosition(newPos
);
1476 SetDefaultStyleToCursorStyle();
1484 /// Move to the start of the line
1485 bool wxRichTextCtrl::MoveToLineStart(int flags
)
1487 wxRichTextLine
* line
= GetVisibleLineForCaretPosition(m_caretPosition
);
1490 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
1491 long newPos
= lineRange
.GetStart()-1;
1493 bool extendSel
= ExtendSelection(m_caretPosition
, newPos
, flags
);
1497 wxRichTextParagraph
* para
= GetBuffer().GetParagraphForLine(line
);
1499 SetCaretPosition(newPos
, para
->GetRange().GetStart() != lineRange
.GetStart());
1501 SetDefaultStyleToCursorStyle();
1509 /// Move to the start of the buffer
1510 bool wxRichTextCtrl::MoveHome(int flags
)
1512 if (m_caretPosition
!= -1)
1514 bool extendSel
= ExtendSelection(m_caretPosition
, -1, flags
);
1518 SetCaretPosition(-1);
1520 SetDefaultStyleToCursorStyle();
1528 /// Move to the end of the buffer
1529 bool wxRichTextCtrl::MoveEnd(int flags
)
1531 long endPos
= GetBuffer().GetRange().GetEnd()-1;
1533 if (m_caretPosition
!= endPos
)
1535 bool extendSel
= ExtendSelection(m_caretPosition
, endPos
, flags
);
1539 SetCaretPosition(endPos
);
1541 SetDefaultStyleToCursorStyle();
1549 /// Move noPages pages up
1550 bool wxRichTextCtrl::PageUp(int noPages
, int flags
)
1552 return PageDown(- noPages
, flags
);
1555 /// Move noPages pages down
1556 bool wxRichTextCtrl::PageDown(int noPages
, int flags
)
1558 // Calculate which line occurs noPages * screen height further down.
1559 wxRichTextLine
* line
= GetVisibleLineForCaretPosition(m_caretPosition
);
1562 wxSize clientSize
= GetClientSize();
1563 int newY
= line
->GetAbsolutePosition().y
+ noPages
*clientSize
.y
;
1565 wxRichTextLine
* newLine
= GetBuffer().GetLineAtYPosition(newY
);
1568 wxRichTextRange lineRange
= newLine
->GetAbsoluteRange();
1569 long pos
= lineRange
.GetStart()-1;
1570 if (pos
!= m_caretPosition
)
1572 wxRichTextParagraph
* para
= GetBuffer().GetParagraphForLine(newLine
);
1574 bool extendSel
= ExtendSelection(m_caretPosition
, pos
, flags
);
1578 SetCaretPosition(pos
, para
->GetRange().GetStart() != lineRange
.GetStart());
1580 SetDefaultStyleToCursorStyle();
1590 static bool wxRichTextCtrlIsWhitespace(const wxString
& str
)
1592 return str
== wxT(" ") || str
== wxT("\t");
1595 // Finds the caret position for the next word
1596 long wxRichTextCtrl::FindNextWordPosition(int direction
) const
1598 long endPos
= GetBuffer().GetRange().GetEnd();
1602 long i
= m_caretPosition
+1+direction
; // +1 for conversion to character pos
1604 // First skip current text to space
1605 while (i
< endPos
&& i
> -1)
1607 // i is in character, not caret positions
1608 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(i
, i
));
1609 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(i
, false);
1610 if (line
&& (i
== line
->GetAbsoluteRange().GetEnd()))
1614 else if (!wxRichTextCtrlIsWhitespace(text
) && !text
.empty())
1621 while (i
< endPos
&& i
> -1)
1623 // i is in character, not caret positions
1624 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(i
, i
));
1625 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(i
, false);
1626 if (line
&& (i
== line
->GetAbsoluteRange().GetEnd()))
1627 return wxMax(-1, i
);
1629 if (text
.empty()) // End of paragraph, or maybe an image
1630 return wxMax(-1, i
- 1);
1631 else if (wxRichTextCtrlIsWhitespace(text
) || text
.empty())
1635 // Convert to caret position
1636 return wxMax(-1, i
- 1);
1645 long i
= m_caretPosition
;
1647 // First skip white space
1648 while (i
< endPos
&& i
> -1)
1650 // i is in character, not caret positions
1651 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(i
, i
));
1652 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(i
, false);
1654 if (text
.empty() || (line
&& (i
== line
->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image
1656 else if (wxRichTextCtrlIsWhitespace(text
) || text
.empty())
1661 // Next skip current text to space
1662 while (i
< endPos
&& i
> -1)
1664 // i is in character, not caret positions
1665 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(i
, i
));
1666 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(i
, false);
1667 if (line
&& line
->GetAbsoluteRange().GetStart() == i
)
1670 if (!wxRichTextCtrlIsWhitespace(text
) /* && !text.empty() */)
1683 /// Move n words left
1684 bool wxRichTextCtrl::WordLeft(int WXUNUSED(n
), int flags
)
1686 long pos
= FindNextWordPosition(-1);
1687 if (pos
!= m_caretPosition
)
1689 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(pos
, true);
1691 bool extendSel
= ExtendSelection(m_caretPosition
, pos
, flags
);
1695 SetCaretPosition(pos
, para
->GetRange().GetStart() != pos
);
1697 SetDefaultStyleToCursorStyle();
1705 /// Move n words right
1706 bool wxRichTextCtrl::WordRight(int WXUNUSED(n
), int flags
)
1708 long pos
= FindNextWordPosition(1);
1709 if (pos
!= m_caretPosition
)
1711 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(pos
, true);
1713 bool extendSel
= ExtendSelection(m_caretPosition
, pos
, flags
);
1717 SetCaretPosition(pos
, para
->GetRange().GetStart() != pos
);
1719 SetDefaultStyleToCursorStyle();
1728 void wxRichTextCtrl::OnSize(wxSizeEvent
& event
)
1730 // Only do sizing optimization for large buffers
1731 if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold
)
1733 m_fullLayoutRequired
= true;
1734 m_fullLayoutTime
= wxGetLocalTimeMillis();
1735 m_fullLayoutSavedPosition
= GetFirstVisiblePosition();
1736 LayoutContent(true /* onlyVisibleRect */);
1739 GetBuffer().Invalidate(wxRICHTEXT_ALL
);
1741 #if wxRICHTEXT_BUFFERED_PAINTING
1749 /// Idle-time processing
1750 void wxRichTextCtrl::OnIdle(wxIdleEvent
& event
)
1752 const int layoutInterval
= wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL
;
1754 if (m_fullLayoutRequired
&& (wxGetLocalTimeMillis() > (m_fullLayoutTime
+ layoutInterval
)))
1756 m_fullLayoutRequired
= false;
1757 m_fullLayoutTime
= 0;
1758 GetBuffer().Invalidate(wxRICHTEXT_ALL
);
1759 ShowPosition(m_fullLayoutSavedPosition
);
1763 if (m_caretPositionForDefaultStyle
!= -2)
1765 // If the caret position has changed, no longer reflect the default style
1767 if (GetCaretPosition() != m_caretPositionForDefaultStyle
)
1768 m_caretPositionForDefaultStyle
= -2;
1775 void wxRichTextCtrl::OnScroll(wxScrollWinEvent
& event
)
1781 /// Set up scrollbars, e.g. after a resize
1782 void wxRichTextCtrl::SetupScrollbars(bool atTop
)
1787 if (GetBuffer().IsEmpty())
1789 SetScrollbars(0, 0, 0, 0, 0, 0);
1793 // TODO: reimplement scrolling so we scroll by line, not by fixed number
1794 // of pixels. See e.g. wxVScrolledWindow for ideas.
1795 int pixelsPerUnit
= 5;
1796 wxSize clientSize
= GetClientSize();
1798 int maxHeight
= GetBuffer().GetCachedSize().y
;
1800 // Round up so we have at least maxHeight pixels
1801 int unitsY
= (int) (((float)maxHeight
/(float)pixelsPerUnit
) + 0.5);
1803 int startX
= 0, startY
= 0;
1805 GetViewStart(& startX
, & startY
);
1807 int maxPositionX
= 0;
1808 int maxPositionY
= (int) ((((float)(wxMax((unitsY
*pixelsPerUnit
) - clientSize
.y
, 0)))/((float)pixelsPerUnit
)) + 0.5);
1810 int newStartX
= wxMin(maxPositionX
, startX
);
1811 int newStartY
= wxMin(maxPositionY
, startY
);
1813 int oldPPUX
, oldPPUY
;
1814 int oldStartX
, oldStartY
;
1815 int oldVirtualSizeX
= 0, oldVirtualSizeY
= 0;
1816 GetScrollPixelsPerUnit(& oldPPUX
, & oldPPUY
);
1817 GetViewStart(& oldStartX
, & oldStartY
);
1818 GetVirtualSize(& oldVirtualSizeX
, & oldVirtualSizeY
);
1820 oldVirtualSizeY
/= oldPPUY
;
1822 if (oldPPUX
== 0 && oldPPUY
== pixelsPerUnit
&& oldVirtualSizeY
== unitsY
&& oldStartX
== newStartX
&& oldStartY
== newStartY
)
1825 // Don't set scrollbars if there were none before, and there will be none now.
1826 if (oldPPUY
!= 0 && (oldVirtualSizeY
< clientSize
.y
) && (unitsY
*pixelsPerUnit
< clientSize
.y
))
1829 // Move to previous scroll position if
1831 SetScrollbars(0, pixelsPerUnit
, 0, unitsY
, newStartX
, newStartY
);
1834 /// Paint the background
1835 void wxRichTextCtrl::PaintBackground(wxDC
& dc
)
1837 wxColour backgroundColour
= GetBackgroundColour();
1838 if (!backgroundColour
.Ok())
1839 backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
1841 // Clear the background
1842 dc
.SetBrush(wxBrush(backgroundColour
));
1843 dc
.SetPen(*wxTRANSPARENT_PEN
);
1844 wxRect
windowRect(GetClientSize());
1845 windowRect
.x
-= 2; windowRect
.y
-= 2;
1846 windowRect
.width
+= 4; windowRect
.height
+= 4;
1848 // We need to shift the rectangle to take into account
1849 // scrolling. Converting device to logical coordinates.
1850 CalcUnscrolledPosition(windowRect
.x
, windowRect
.y
, & windowRect
.x
, & windowRect
.y
);
1851 dc
.DrawRectangle(windowRect
);
1854 #if wxRICHTEXT_BUFFERED_PAINTING
1855 /// Recreate buffer bitmap if necessary
1856 bool wxRichTextCtrl::RecreateBuffer(const wxSize
& size
)
1859 if (sz
== wxDefaultSize
)
1860 sz
= GetClientSize();
1862 if (sz
.x
< 1 || sz
.y
< 1)
1865 if (!m_bufferBitmap
.Ok() || m_bufferBitmap
.GetWidth() < sz
.x
|| m_bufferBitmap
.GetHeight() < sz
.y
)
1866 m_bufferBitmap
= wxBitmap(sz
.x
, sz
.y
);
1867 return m_bufferBitmap
.Ok();
1871 // ----------------------------------------------------------------------------
1872 // file IO functions
1873 // ----------------------------------------------------------------------------
1875 bool wxRichTextCtrl::DoLoadFile(const wxString
& filename
, int fileType
)
1877 bool success
= GetBuffer().LoadFile(filename
, fileType
);
1879 m_filename
= filename
;
1882 SetInsertionPoint(0);
1885 SetupScrollbars(true);
1887 wxTextCtrl::SendTextUpdatedEvent(this);
1893 wxLogError(_("File couldn't be loaded."));
1899 bool wxRichTextCtrl::DoSaveFile(const wxString
& filename
, int fileType
)
1901 if (GetBuffer().SaveFile(filename
, fileType
))
1903 m_filename
= filename
;
1910 wxLogError(_("The text couldn't be saved."));
1915 // ----------------------------------------------------------------------------
1916 // wxRichTextCtrl specific functionality
1917 // ----------------------------------------------------------------------------
1919 /// Add a new paragraph of text to the end of the buffer
1920 wxRichTextRange
wxRichTextCtrl::AddParagraph(const wxString
& text
)
1922 wxRichTextRange range
= GetBuffer().AddParagraph(text
);
1928 wxRichTextRange
wxRichTextCtrl::AddImage(const wxImage
& image
)
1930 wxRichTextRange range
= GetBuffer().AddImage(image
);
1935 // ----------------------------------------------------------------------------
1936 // selection and ranges
1937 // ----------------------------------------------------------------------------
1939 void wxRichTextCtrl::SelectAll()
1941 SetSelection(0, GetLastPosition()+1);
1942 m_selectionAnchor
= -1;
1946 void wxRichTextCtrl::SelectNone()
1948 if (!(GetSelectionRange() == wxRichTextRange(-2, -2)))
1950 wxRichTextRange oldSelection
= m_selectionRange
;
1952 m_selectionRange
= wxRichTextRange(-2, -2);
1954 RefreshForSelectionChange(oldSelection
, m_selectionRange
);
1956 m_selectionAnchor
= -2;
1959 static bool wxIsWordDelimiter(const wxString
& text
)
1961 return !text
.IsEmpty() && !wxIsalnum(text
[0]);
1964 /// Select the word at the given character position
1965 bool wxRichTextCtrl::SelectWord(long position
)
1967 if (position
< 0 || position
> GetBuffer().GetRange().GetEnd())
1970 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(position
);
1974 if (position
== para
->GetRange().GetEnd())
1977 long positionStart
= position
;
1978 long positionEnd
= position
;
1980 for (positionStart
= position
; positionStart
>= para
->GetRange().GetStart(); positionStart
--)
1982 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(positionStart
, positionStart
));
1983 if (wxIsWordDelimiter(text
))
1989 if (positionStart
< para
->GetRange().GetStart())
1990 positionStart
= para
->GetRange().GetStart();
1992 for (positionEnd
= position
; positionEnd
< para
->GetRange().GetEnd(); positionEnd
++)
1994 wxString text
= GetBuffer().GetTextForRange(wxRichTextRange(positionEnd
, positionEnd
));
1995 if (wxIsWordDelimiter(text
))
2001 if (positionEnd
>= para
->GetRange().GetEnd())
2002 positionEnd
= para
->GetRange().GetEnd();
2004 if (positionEnd
< positionStart
)
2007 SetSelection(positionStart
, positionEnd
+1);
2009 if (positionStart
>= 0)
2011 MoveCaret(positionStart
-1, true);
2012 SetDefaultStyleToCursorStyle();
2018 wxString
wxRichTextCtrl::GetStringSelection() const
2021 GetSelection(&from
, &to
);
2023 return GetRange(from
, to
);
2026 // ----------------------------------------------------------------------------
2028 // ----------------------------------------------------------------------------
2030 wxTextCtrlHitTestResult
2031 wxRichTextCtrl::HitTest(const wxPoint
& pt
, wxTextCoord
*x
, wxTextCoord
*y
) const
2033 // implement in terms of the other overload as the native ports typically
2034 // can get the position and not (x, y) pair directly (although wxUniv
2035 // directly gets x and y -- and so overrides this method as well)
2037 wxTextCtrlHitTestResult rc
= HitTest(pt
, &pos
);
2039 if ( rc
!= wxTE_HT_UNKNOWN
)
2041 PositionToXY(pos
, x
, y
);
2047 wxTextCtrlHitTestResult
2048 wxRichTextCtrl::HitTest(const wxPoint
& pt
,
2051 wxClientDC
dc((wxRichTextCtrl
*) this);
2052 ((wxRichTextCtrl
*)this)->PrepareDC(dc
);
2054 // Buffer uses logical position (relative to start of buffer)
2056 wxPoint pt2
= GetLogicalPoint(pt
);
2058 int hit
= ((wxRichTextCtrl
*)this)->GetBuffer().HitTest(dc
, pt2
, *pos
);
2060 if ((hit
& wxRICHTEXT_HITTEST_BEFORE
) && (hit
& wxRICHTEXT_HITTEST_OUTSIDE
))
2061 return wxTE_HT_BEFORE
;
2062 else if ((hit
& wxRICHTEXT_HITTEST_AFTER
) && (hit
& wxRICHTEXT_HITTEST_OUTSIDE
))
2063 return wxTE_HT_BEYOND
;
2064 else if (hit
& (wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_AFTER
))
2065 return wxTE_HT_ON_TEXT
;
2067 return wxTE_HT_UNKNOWN
;
2070 // ----------------------------------------------------------------------------
2071 // set/get the controls text
2072 // ----------------------------------------------------------------------------
2074 wxString
wxRichTextCtrl::GetValue() const
2076 return GetBuffer().GetText();
2079 wxString
wxRichTextCtrl::GetRange(long from
, long to
) const
2081 // Public API for range is different from internals
2082 return GetBuffer().GetTextForRange(wxRichTextRange(from
, to
-1));
2085 void wxRichTextCtrl::DoSetValue(const wxString
& value
, int flags
)
2087 // Don't call Clear here, since it always sends a text updated event
2088 m_buffer
.ResetAndClearCommands();
2089 m_buffer
.SetDirty(true);
2090 m_caretPosition
= -1;
2091 m_caretPositionForDefaultStyle
= -2;
2092 m_caretAtLineStart
= false;
2093 m_selectionRange
.SetRange(-2, -2);
2103 if (!value
.IsEmpty())
2105 // Remove empty paragraph
2106 GetBuffer().Clear();
2107 DoWriteText(value
, flags
);
2109 // for compatibility, don't move the cursor when doing SetValue()
2110 SetInsertionPoint(0);
2114 // still send an event for consistency
2115 if (flags
& SetValue_SendEvent
)
2116 wxTextCtrl::SendTextUpdatedEvent(this);
2121 void wxRichTextCtrl::WriteText(const wxString
& value
)
2126 void wxRichTextCtrl::DoWriteText(const wxString
& value
, int flags
)
2128 wxString valueUnix
= wxTextFile::Translate(value
, wxTextFileType_Unix
);
2130 GetBuffer().InsertTextWithUndo(m_caretPosition
+1, valueUnix
, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
2132 if ( flags
& SetValue_SendEvent
)
2133 wxTextCtrl::SendTextUpdatedEvent(this);
2136 void wxRichTextCtrl::AppendText(const wxString
& text
)
2138 SetInsertionPointEnd();
2143 /// Write an image at the current insertion point
2144 bool wxRichTextCtrl::WriteImage(const wxImage
& image
, int bitmapType
)
2146 wxRichTextImageBlock imageBlock
;
2148 wxImage image2
= image
;
2149 if (imageBlock
.MakeImageBlock(image2
, bitmapType
))
2150 return WriteImage(imageBlock
);
2155 bool wxRichTextCtrl::WriteImage(const wxString
& filename
, int bitmapType
)
2157 wxRichTextImageBlock imageBlock
;
2160 if (imageBlock
.MakeImageBlock(filename
, bitmapType
, image
, false))
2161 return WriteImage(imageBlock
);
2166 bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock
& imageBlock
)
2168 return GetBuffer().InsertImageWithUndo(m_caretPosition
+1, imageBlock
, this);
2171 bool wxRichTextCtrl::WriteImage(const wxBitmap
& bitmap
, int bitmapType
)
2175 wxRichTextImageBlock imageBlock
;
2177 wxImage image
= bitmap
.ConvertToImage();
2178 if (image
.Ok() && imageBlock
.MakeImageBlock(image
, bitmapType
))
2179 return WriteImage(imageBlock
);
2185 /// Insert a newline (actually paragraph) at the current insertion point.
2186 bool wxRichTextCtrl::Newline()
2188 return GetBuffer().InsertNewlineWithUndo(m_caretPosition
+1, this);
2191 /// Insert a line break at the current insertion point.
2192 bool wxRichTextCtrl::LineBreak()
2195 text
= wxRichTextLineBreakChar
;
2196 return GetBuffer().InsertTextWithUndo(m_caretPosition
+1, text
, this);
2199 // ----------------------------------------------------------------------------
2200 // Clipboard operations
2201 // ----------------------------------------------------------------------------
2203 void wxRichTextCtrl::Copy()
2207 wxRichTextRange range
= GetInternalSelectionRange();
2208 GetBuffer().CopyToClipboard(range
);
2212 void wxRichTextCtrl::Cut()
2216 wxRichTextRange range
= GetInternalSelectionRange();
2217 GetBuffer().CopyToClipboard(range
);
2219 DeleteSelectedContent();
2225 void wxRichTextCtrl::Paste()
2229 BeginBatchUndo(_("Paste"));
2231 long newPos
= m_caretPosition
;
2232 DeleteSelectedContent(& newPos
);
2234 GetBuffer().PasteFromClipboard(newPos
);
2240 void wxRichTextCtrl::DeleteSelection()
2242 if (CanDeleteSelection())
2244 DeleteSelectedContent();
2248 bool wxRichTextCtrl::HasSelection() const
2250 return m_selectionRange
.GetStart() != -2 && m_selectionRange
.GetEnd() != -2;
2253 bool wxRichTextCtrl::CanCopy() const
2255 // Can copy if there's a selection
2256 return HasSelection();
2259 bool wxRichTextCtrl::CanCut() const
2261 return HasSelection() && IsEditable();
2264 bool wxRichTextCtrl::CanPaste() const
2266 if ( !IsEditable() )
2269 return GetBuffer().CanPasteFromClipboard();
2272 bool wxRichTextCtrl::CanDeleteSelection() const
2274 return HasSelection() && IsEditable();
2278 // ----------------------------------------------------------------------------
2280 // ----------------------------------------------------------------------------
2282 void wxRichTextCtrl::SetEditable(bool editable
)
2284 m_editable
= editable
;
2287 void wxRichTextCtrl::SetInsertionPoint(long pos
)
2291 m_caretPosition
= pos
- 1;
2296 void wxRichTextCtrl::SetInsertionPointEnd()
2298 long pos
= GetLastPosition();
2299 SetInsertionPoint(pos
);
2302 long wxRichTextCtrl::GetInsertionPoint() const
2304 return m_caretPosition
+1;
2307 wxTextPos
wxRichTextCtrl::GetLastPosition() const
2309 return GetBuffer().GetRange().GetEnd();
2312 // If the return values from and to are the same, there is no
2314 void wxRichTextCtrl::GetSelection(long* from
, long* to
) const
2316 *from
= m_selectionRange
.GetStart();
2317 *to
= m_selectionRange
.GetEnd();
2318 if ((*to
) != -1 && (*to
) != -2)
2322 bool wxRichTextCtrl::IsEditable() const
2327 // ----------------------------------------------------------------------------
2329 // ----------------------------------------------------------------------------
2331 void wxRichTextCtrl::SetSelection(long from
, long to
)
2333 // if from and to are both -1, it means (in wxWidgets) that all text should
2335 if ( (from
== -1) && (to
== -1) )
2338 to
= GetLastPosition()+1;
2341 DoSetSelection(from
, to
);
2342 SetDefaultStyleToCursorStyle();
2345 void wxRichTextCtrl::DoSetSelection(long from
, long to
, bool WXUNUSED(scrollCaret
))
2353 wxRichTextRange oldSelection
= m_selectionRange
;
2354 m_selectionAnchor
= from
;
2355 m_selectionRange
.SetRange(from
, to
-1);
2357 m_caretPosition
= from
-1;
2359 RefreshForSelectionChange(oldSelection
, m_selectionRange
);
2364 // ----------------------------------------------------------------------------
2366 // ----------------------------------------------------------------------------
2368 void wxRichTextCtrl::Replace(long WXUNUSED(from
), long WXUNUSED(to
),
2369 const wxString
& value
)
2371 BeginBatchUndo(_("Replace"));
2373 DeleteSelectedContent();
2375 DoWriteText(value
, SetValue_SelectionOnly
);
2380 void wxRichTextCtrl::Remove(long from
, long to
)
2384 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from
, to
-1), this);
2391 bool wxRichTextCtrl::IsModified() const
2393 return m_buffer
.IsModified();
2396 void wxRichTextCtrl::MarkDirty()
2398 m_buffer
.Modify(true);
2401 void wxRichTextCtrl::DiscardEdits()
2403 m_caretPositionForDefaultStyle
= -2;
2404 m_buffer
.Modify(false);
2405 m_buffer
.GetCommandProcessor()->ClearCommands();
2408 int wxRichTextCtrl::GetNumberOfLines() const
2410 return GetBuffer().GetParagraphCount();
2413 // ----------------------------------------------------------------------------
2414 // Positions <-> coords
2415 // ----------------------------------------------------------------------------
2417 long wxRichTextCtrl::XYToPosition(long x
, long y
) const
2419 return GetBuffer().XYToPosition(x
, y
);
2422 bool wxRichTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const
2424 return GetBuffer().PositionToXY(pos
, x
, y
);
2427 // ----------------------------------------------------------------------------
2429 // ----------------------------------------------------------------------------
2431 void wxRichTextCtrl::ShowPosition(long pos
)
2433 if (!IsPositionVisible(pos
))
2434 ScrollIntoView(pos
-1, WXK_DOWN
);
2437 int wxRichTextCtrl::GetLineLength(long lineNo
) const
2439 return GetBuffer().GetParagraphLength(lineNo
);
2442 wxString
wxRichTextCtrl::GetLineText(long lineNo
) const
2444 return GetBuffer().GetParagraphText(lineNo
);
2447 // ----------------------------------------------------------------------------
2449 // ----------------------------------------------------------------------------
2451 void wxRichTextCtrl::Undo()
2455 GetCommandProcessor()->Undo();
2459 void wxRichTextCtrl::Redo()
2463 GetCommandProcessor()->Redo();
2467 bool wxRichTextCtrl::CanUndo() const
2469 return GetCommandProcessor()->CanUndo();
2472 bool wxRichTextCtrl::CanRedo() const
2474 return GetCommandProcessor()->CanRedo();
2477 // ----------------------------------------------------------------------------
2478 // implementation details
2479 // ----------------------------------------------------------------------------
2481 void wxRichTextCtrl::Command(wxCommandEvent
& event
)
2483 SetValue(event
.GetString());
2484 GetEventHandler()->ProcessEvent(event
);
2487 void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent
& event
)
2489 // By default, load the first file into the text window.
2490 if (event
.GetNumberOfFiles() > 0)
2492 LoadFile(event
.GetFiles()[0]);
2496 wxSize
wxRichTextCtrl::DoGetBestSize() const
2498 return wxSize(10, 10);
2501 // ----------------------------------------------------------------------------
2502 // standard handlers for standard edit menu events
2503 // ----------------------------------------------------------------------------
2505 void wxRichTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
))
2510 void wxRichTextCtrl::OnClear(wxCommandEvent
& WXUNUSED(event
))
2515 void wxRichTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
))
2520 void wxRichTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
))
2525 void wxRichTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
))
2530 void wxRichTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
))
2535 void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
)
2537 event
.Enable( CanCut() );
2540 void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
)
2542 event
.Enable( CanCopy() );
2545 void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent
& event
)
2547 event
.Enable( CanDeleteSelection() );
2550 void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
)
2552 event
.Enable( CanPaste() );
2555 void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
)
2557 event
.Enable( CanUndo() );
2558 event
.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
2561 void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
)
2563 event
.Enable( CanRedo() );
2564 event
.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
2567 void wxRichTextCtrl::OnSelectAll(wxCommandEvent
& WXUNUSED(event
))
2572 void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent
& event
)
2574 event
.Enable(GetLastPosition() > 0);
2577 void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent
& event
)
2579 if (event
.GetEventObject() != this)
2587 m_contextMenu
= new wxMenu
;
2588 m_contextMenu
->Append(wxID_UNDO
, _("&Undo"));
2589 m_contextMenu
->Append(wxID_REDO
, _("&Redo"));
2590 m_contextMenu
->AppendSeparator();
2591 m_contextMenu
->Append(wxID_CUT
, _("Cu&t"));
2592 m_contextMenu
->Append(wxID_COPY
, _("&Copy"));
2593 m_contextMenu
->Append(wxID_PASTE
, _("&Paste"));
2594 m_contextMenu
->Append(wxID_CLEAR
, _("&Delete"));
2595 m_contextMenu
->AppendSeparator();
2596 m_contextMenu
->Append(wxID_SELECTALL
, _("Select &All"));
2598 PopupMenu(m_contextMenu
);
2602 bool wxRichTextCtrl::SetStyle(long start
, long end
, const wxTextAttr
& style
)
2604 return GetBuffer().SetStyle(wxRichTextRange(start
, end
-1), wxTextAttr(style
));
2607 bool wxRichTextCtrl::SetStyle(const wxRichTextRange
& range
, const wxTextAttr
& style
)
2609 return GetBuffer().SetStyle(range
.ToInternal(), style
);
2612 // extended style setting operation with flags including:
2613 // wxRICHTEXT_SETSTYLE_WITH_UNDO, wxRICHTEXT_SETSTYLE_OPTIMIZE, wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY.
2614 // see richtextbuffer.h for more details.
2616 bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange
& range
, const wxTextAttr
& style
, int flags
)
2618 return GetBuffer().SetStyle(range
.ToInternal(), style
, flags
);
2621 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr
& style
)
2623 return GetBuffer().SetDefaultStyle(wxTextAttr(style
));
2626 const wxTextAttr
& wxRichTextCtrl::GetDefaultStyle() const
2628 return GetBuffer().GetDefaultStyle();
2631 bool wxRichTextCtrl::GetStyle(long position
, wxTextAttr
& style
)
2633 return GetBuffer().GetStyle(position
, style
);
2636 // get the common set of styles for the range
2637 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange
& range
, wxTextAttr
& style
)
2639 return GetBuffer().GetStyleForRange(range
.ToInternal(), style
);
2642 /// Get the content (uncombined) attributes for this position.
2643 bool wxRichTextCtrl::GetUncombinedStyle(long position
, wxTextAttr
& style
)
2645 return GetBuffer().GetUncombinedStyle(position
, style
);
2648 /// Set font, and also the buffer attributes
2649 bool wxRichTextCtrl::SetFont(const wxFont
& font
)
2651 wxControl::SetFont(font
);
2653 wxTextAttr attr
= GetBuffer().GetAttributes();
2655 GetBuffer().SetBasicStyle(attr
);
2657 GetBuffer().Invalidate(wxRICHTEXT_ALL
);
2663 /// Transform logical to physical
2664 wxPoint
wxRichTextCtrl::GetPhysicalPoint(const wxPoint
& ptLogical
) const
2667 CalcScrolledPosition(ptLogical
.x
, ptLogical
.y
, & pt
.x
, & pt
.y
);
2672 /// Transform physical to logical
2673 wxPoint
wxRichTextCtrl::GetLogicalPoint(const wxPoint
& ptPhysical
) const
2676 CalcUnscrolledPosition(ptPhysical
.x
, ptPhysical
.y
, & pt
.x
, & pt
.y
);
2681 /// Position the caret
2682 void wxRichTextCtrl::PositionCaret()
2687 //wxLogDebug(wxT("PositionCaret"));
2690 if (GetCaretPositionForIndex(GetCaretPosition(), caretRect
))
2692 wxPoint newPt
= caretRect
.GetPosition();
2693 wxSize newSz
= caretRect
.GetSize();
2694 wxPoint pt
= GetPhysicalPoint(newPt
);
2695 if (GetCaret()->GetPosition() != pt
|| GetCaret()->GetSize() != newSz
)
2697 GetCaret()->Move(pt
);
2698 GetCaret()->SetSize(newSz
);
2703 /// Get the caret height and position for the given character position
2704 bool wxRichTextCtrl::GetCaretPositionForIndex(long position
, wxRect
& rect
)
2706 wxClientDC
dc(this);
2707 dc
.SetFont(GetFont());
2714 if (GetBuffer().FindPosition(dc
, position
, pt
, & height
, m_caretAtLineStart
))
2716 // Caret height can't be zero
2718 height
= dc
.GetCharHeight();
2720 rect
= wxRect(pt
, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH
, height
));
2727 /// Gets the line for the visible caret position. If the caret is
2728 /// shown at the very end of the line, it means the next character is actually
2729 /// on the following line. So let's get the line we're expecting to find
2730 /// if this is the case.
2731 wxRichTextLine
* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition
) const
2733 wxRichTextLine
* line
= GetBuffer().GetLineAtPosition(caretPosition
, true);
2734 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(caretPosition
, true);
2737 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2738 if (caretPosition
== lineRange
.GetStart()-1 &&
2739 (para
->GetRange().GetStart() != lineRange
.GetStart()))
2741 if (!m_caretAtLineStart
)
2742 line
= GetBuffer().GetLineAtPosition(caretPosition
-1, true);
2749 /// Move the caret to the given character position
2750 bool wxRichTextCtrl::MoveCaret(long pos
, bool showAtLineStart
)
2752 if (GetBuffer().GetDirty())
2755 if (pos
<= GetBuffer().GetRange().GetEnd())
2757 SetCaretPosition(pos
, showAtLineStart
);
2767 /// Layout the buffer: which we must do before certain operations, such as
2768 /// setting the caret position.
2769 bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect
)
2771 if (GetBuffer().GetDirty() || onlyVisibleRect
)
2773 wxRect
availableSpace(GetClientSize());
2774 if (availableSpace
.width
== 0)
2775 availableSpace
.width
= 10;
2776 if (availableSpace
.height
== 0)
2777 availableSpace
.height
= 10;
2779 int flags
= wxRICHTEXT_FIXED_WIDTH
|wxRICHTEXT_VARIABLE_HEIGHT
;
2780 if (onlyVisibleRect
)
2782 flags
|= wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
2783 availableSpace
.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
2786 wxClientDC
dc(this);
2787 dc
.SetFont(GetFont());
2791 GetBuffer().Defragment();
2792 GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
2793 GetBuffer().Layout(dc
, availableSpace
, flags
);
2794 GetBuffer().SetDirty(false);
2803 /// Is all of the selection bold?
2804 bool wxRichTextCtrl::IsSelectionBold()
2809 wxRichTextRange range
= GetSelectionRange();
2810 attr
.SetFlags(wxTEXT_ATTR_FONT_WEIGHT
);
2811 attr
.SetFontWeight(wxBOLD
);
2813 return HasCharacterAttributes(range
, attr
);
2817 // If no selection, then we need to combine current style with default style
2818 // to see what the effect would be if we started typing.
2820 attr
.SetFlags(wxTEXT_ATTR_FONT_WEIGHT
);
2822 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
2823 if (GetStyle(pos
, attr
))
2825 if (IsDefaultStyleShowing())
2826 wxRichTextApplyStyle(attr
, GetDefaultStyleEx());
2827 return attr
.GetFontWeight() == wxBOLD
;
2833 /// Is all of the selection italics?
2834 bool wxRichTextCtrl::IsSelectionItalics()
2838 wxRichTextRange range
= GetSelectionRange();
2840 attr
.SetFlags(wxTEXT_ATTR_FONT_ITALIC
);
2841 attr
.SetFontStyle(wxITALIC
);
2843 return HasCharacterAttributes(range
, attr
);
2847 // If no selection, then we need to combine current style with default style
2848 // to see what the effect would be if we started typing.
2850 attr
.SetFlags(wxTEXT_ATTR_FONT_ITALIC
);
2852 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
2853 if (GetStyle(pos
, attr
))
2855 if (IsDefaultStyleShowing())
2856 wxRichTextApplyStyle(attr
, GetDefaultStyleEx());
2857 return attr
.GetFontStyle() == wxITALIC
;
2863 /// Is all of the selection underlined?
2864 bool wxRichTextCtrl::IsSelectionUnderlined()
2868 wxRichTextRange range
= GetSelectionRange();
2870 attr
.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE
);
2871 attr
.SetFontUnderlined(true);
2873 return HasCharacterAttributes(range
, attr
);
2877 // If no selection, then we need to combine current style with default style
2878 // to see what the effect would be if we started typing.
2880 attr
.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE
);
2881 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
2883 if (GetStyle(pos
, attr
))
2885 if (IsDefaultStyleShowing())
2886 wxRichTextApplyStyle(attr
, GetDefaultStyleEx());
2887 return attr
.GetFontUnderlined();
2893 /// Apply bold to the selection
2894 bool wxRichTextCtrl::ApplyBoldToSelection()
2897 attr
.SetFlags(wxTEXT_ATTR_FONT_WEIGHT
);
2898 attr
.SetFontWeight(IsSelectionBold() ? wxNORMAL
: wxBOLD
);
2901 return SetStyleEx(GetSelectionRange(), attr
, wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_OPTIMIZE
|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
);
2904 wxRichTextAttr current
= GetDefaultStyleEx();
2905 current
.Apply(attr
);
2906 SetAndShowDefaultStyle(current
);
2911 /// Apply italic to the selection
2912 bool wxRichTextCtrl::ApplyItalicToSelection()
2915 attr
.SetFlags(wxTEXT_ATTR_FONT_ITALIC
);
2916 attr
.SetFontStyle(IsSelectionItalics() ? wxNORMAL
: wxITALIC
);
2919 return SetStyleEx(GetSelectionRange(), attr
, wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_OPTIMIZE
|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
);
2922 wxRichTextAttr current
= GetDefaultStyleEx();
2923 current
.Apply(attr
);
2924 SetAndShowDefaultStyle(current
);
2929 /// Apply underline to the selection
2930 bool wxRichTextCtrl::ApplyUnderlineToSelection()
2933 attr
.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE
);
2934 attr
.SetFontUnderlined(!IsSelectionUnderlined());
2937 return SetStyleEx(GetSelectionRange(), attr
, wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_OPTIMIZE
|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
);
2940 wxRichTextAttr current
= GetDefaultStyleEx();
2941 current
.Apply(attr
);
2942 SetAndShowDefaultStyle(current
);
2947 /// Is all of the selection aligned according to the specified flag?
2948 bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment
)
2950 wxRichTextRange range
;
2952 range
= GetSelectionRange();
2954 range
= wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+2);
2957 attr
.SetAlignment(alignment
);
2959 return HasParagraphAttributes(range
, attr
);
2962 /// Apply alignment to the selection
2963 bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment
)
2966 attr
.SetAlignment(alignment
);
2968 return SetStyle(GetSelectionRange(), attr
);
2971 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
2973 return SetStyleEx(para
->GetRange().FromInternal(), attr
, wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_OPTIMIZE
|wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
);
2978 /// Apply a named style to the selection
2979 bool wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition
* def
)
2981 // Flags are defined within each definition, so only certain
2982 // attributes are applied.
2983 wxTextAttr
attr(GetStyleSheet() ? def
->GetStyleMergedWithBase(GetStyleSheet()) : def
->GetStyle());
2985 int flags
= wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_OPTIMIZE
|wxRICHTEXT_SETSTYLE_RESET
;
2987 if (def
->IsKindOf(CLASSINFO(wxRichTextListStyleDefinition
)))
2989 flags
|= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
;
2991 wxRichTextRange range
;
2994 range
= GetSelectionRange();
2997 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
2998 range
= wxRichTextRange(pos
, pos
+1);
3001 return SetListStyle(range
, (wxRichTextListStyleDefinition
*) def
, flags
);
3004 // Make sure the attr has the style name
3005 if (def
->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition
)))
3007 attr
.SetParagraphStyleName(def
->GetName());
3009 // If applying a paragraph style, we only want the paragraph nodes to adopt these
3010 // attributes, and not the leaf nodes. This will allow the content (e.g. text)
3011 // to change its style independently.
3012 flags
|= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
;
3015 attr
.SetCharacterStyleName(def
->GetName());
3018 return SetStyleEx(GetSelectionRange(), attr
, flags
);
3021 wxRichTextAttr current
= GetDefaultStyleEx();
3022 current
.Apply(attr
);
3023 SetAndShowDefaultStyle(current
);
3028 /// Apply the style sheet to the buffer, for example if the styles have changed.
3029 bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3032 styleSheet
= GetBuffer().GetStyleSheet();
3036 if (GetBuffer().ApplyStyleSheet(styleSheet
))
3038 GetBuffer().Invalidate(wxRICHTEXT_ALL
);
3046 /// Sets the default style to the style under the cursor
3047 bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
3050 attr
.SetFlags(wxTEXT_ATTR_CHARACTER
);
3052 // If at the start of a paragraph, use the next position.
3053 long pos
= GetAdjustedCaretPosition(GetCaretPosition());
3055 if (GetUncombinedStyle(pos
, attr
))
3057 SetDefaultStyle(attr
);
3064 /// Returns the first visible position in the current view
3065 long wxRichTextCtrl::GetFirstVisiblePosition() const
3067 wxRichTextLine
* line
= GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y
);
3069 return line
->GetAbsoluteRange().GetStart();
3074 /// Get the first visible point in the window
3075 wxPoint
wxRichTextCtrl::GetFirstVisiblePoint() const
3078 int startXUnits
, startYUnits
;
3080 GetScrollPixelsPerUnit(& ppuX
, & ppuY
);
3081 GetViewStart(& startXUnits
, & startYUnits
);
3083 return wxPoint(startXUnits
* ppuX
, startYUnits
* ppuY
);
3086 /// The adjusted caret position is the character position adjusted to take
3087 /// into account whether we're at the start of a paragraph, in which case
3088 /// style information should be taken from the next position, not current one.
3089 long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos
) const
3091 wxRichTextParagraph
* para
= GetBuffer().GetParagraphAtPosition(caretPos
+1);
3093 if (para
&& (caretPos
+1 == para
->GetRange().GetStart()))
3098 /// Get/set the selection range in character positions. -1, -1 means no selection.
3099 /// The range is in API convention, i.e. a single character selection is denoted
3101 wxRichTextRange
wxRichTextCtrl::GetSelectionRange() const
3103 wxRichTextRange range
= GetInternalSelectionRange();
3104 if (range
!= wxRichTextRange(-2,-2) && range
!= wxRichTextRange(-1,-1))
3105 range
.SetEnd(range
.GetEnd() + 1);
3109 void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange
& range
)
3111 wxRichTextRange
range1(range
);
3112 if (range1
!= wxRichTextRange(-2,-2) && range1
!= wxRichTextRange(-1,-1) )
3113 range1
.SetEnd(range1
.GetEnd() - 1);
3115 wxASSERT( range1
.GetStart() > range1
.GetEnd() );
3117 SetInternalSelectionRange(range1
);
3121 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3123 return GetBuffer().SetListStyle(range
.ToInternal(), def
, flags
, startFrom
, specifiedLevel
);
3126 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3128 return GetBuffer().SetListStyle(range
.ToInternal(), defName
, flags
, startFrom
, specifiedLevel
);
3131 /// Clear list for given range
3132 bool wxRichTextCtrl::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3134 return GetBuffer().ClearListStyle(range
.ToInternal(), flags
);
3137 /// Number/renumber any list elements in the given range
3138 bool wxRichTextCtrl::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3140 return GetBuffer().NumberList(range
.ToInternal(), def
, flags
, startFrom
, specifiedLevel
);
3143 bool wxRichTextCtrl::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3145 return GetBuffer().NumberList(range
.ToInternal(), defName
, flags
, startFrom
, specifiedLevel
);
3148 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
3149 bool wxRichTextCtrl::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
3151 return GetBuffer().PromoteList(promoteBy
, range
.ToInternal(), def
, flags
, specifiedLevel
);
3154 bool wxRichTextCtrl::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
3156 return GetBuffer().PromoteList(promoteBy
, range
.ToInternal(), defName
, flags
, specifiedLevel
);
3159 /// Deletes the content in the given range
3160 bool wxRichTextCtrl::Delete(const wxRichTextRange
& range
)
3162 return GetBuffer().DeleteRangeWithUndo(range
.ToInternal(), this);
3165 const wxArrayString
& wxRichTextCtrl::GetAvailableFontNames()
3167 if (sm_availableFontNames
.GetCount() == 0)
3169 sm_availableFontNames
= wxFontEnumerator::GetFacenames();
3170 sm_availableFontNames
.Sort();
3172 return sm_availableFontNames
;
3175 void wxRichTextCtrl::ClearAvailableFontNames()
3177 sm_availableFontNames
.Clear();
3180 void wxRichTextCtrl::OnSysColourChanged(wxSysColourChangedEvent
& WXUNUSED(event
))
3182 //wxLogDebug(wxT("wxRichTextCtrl::OnSysColourChanged"));
3184 wxTextAttrEx basicStyle
= GetBasicStyle();
3185 basicStyle
.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
3186 SetBasicStyle(basicStyle
);
3187 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
));
3192 // Refresh the area affected by a selection change
3193 bool wxRichTextCtrl::RefreshForSelectionChange(const wxRichTextRange
& oldSelection
, const wxRichTextRange
& newSelection
)
3195 // Calculate the refresh rectangle - just the affected lines
3196 long firstPos
, lastPos
;
3197 if (oldSelection
.GetStart() == -2 && newSelection
.GetStart() != -2)
3199 firstPos
= newSelection
.GetStart();
3200 lastPos
= newSelection
.GetEnd();
3202 else if (oldSelection
.GetStart() != -2 && newSelection
.GetStart() == -2)
3204 firstPos
= oldSelection
.GetStart();
3205 lastPos
= oldSelection
.GetEnd();
3207 else if (oldSelection
.GetStart() == -2 && newSelection
.GetStart() == -2)
3213 firstPos
= wxMin(oldSelection
.GetStart(), newSelection
.GetStart());
3214 lastPos
= wxMax(oldSelection
.GetEnd(), newSelection
.GetEnd());
3217 wxRichTextLine
* firstLine
= GetBuffer().GetLineAtPosition(firstPos
);
3218 wxRichTextLine
* lastLine
= GetBuffer().GetLineAtPosition(lastPos
);
3220 if (firstLine
&& lastLine
)
3222 wxSize clientSize
= GetClientSize();
3223 wxPoint pt1
= GetPhysicalPoint(firstLine
->GetAbsolutePosition());
3224 wxPoint pt2
= GetPhysicalPoint(lastLine
->GetAbsolutePosition()) + wxPoint(0, lastLine
->GetSize().y
);
3227 pt1
.y
= wxMax(0, pt1
.y
);
3229 pt2
.y
= wxMin(clientSize
.y
, pt2
.y
);
3231 wxRect
rect(pt1
, wxSize(clientSize
.x
, pt2
.y
- pt1
.y
));
3232 RefreshRect(rect
, false);