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