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