Reduced unnecessary painting; delete/recreate caret as WIN32 requires
[wxWidgets.git] / src / richtext / richtextctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22
23 #include "wx/image.h"
24
25 #if wxUSE_RICHTEXT
26
27 #include "wx/textfile.h"
28 #include "wx/ffile.h"
29 #include "wx/settings.h"
30 #include "wx/filename.h"
31 #include "wx/dcbuffer.h"
32
33 #include "wx/richtext/richtextctrl.h"
34 #include "wx/arrimpl.cpp"
35
36 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_SELECTED)
37 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_DESELECTED)
38 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
39 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK)
40 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK)
41 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK)
42 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN)
43
44 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
45 IMPLEMENT_CLASS( wxRichTextCtrl, wxControl )
46 #else
47 IMPLEMENT_CLASS( wxRichTextCtrl, wxScrolledWindow )
48 #endif
49
50 IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent )
51
52 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
53 BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl )
54 #else
55 BEGIN_EVENT_TABLE( wxRichTextCtrl, wxScrolledWindow )
56 #endif
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 wxRichTextCtrl::wxRichTextCtrl()
100 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
101 : wxScrollHelper(this)
102 #endif
103 {
104 Init();
105 }
106
107 wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
108 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
109 : wxScrollHelper(this)
110 #endif
111 {
112 Init();
113 Create(parent, id, pos, size, style);
114 }
115
116 /// Creation
117 bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
118 {
119 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
120 if (!wxTextCtrlBase::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
121 ))
122 return false;
123 #else
124 if (!wxScrolledWindow::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
125 ))
126 return false;
127 #endif
128
129 if (!GetFont().Ok())
130 {
131 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
132 }
133
134 GetBuffer().SetRichTextCtrl(this);
135
136 wxTextAttrEx attributes;
137 attributes.SetFont(GetFont());
138 attributes.SetTextColour(*wxBLACK);
139 attributes.SetBackgroundColour(*wxWHITE);
140 attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
141 attributes.SetFlags(wxTEXT_ATTR_ALL);
142
143 SetDefaultStyle(attributes);
144 SetBasicStyle(attributes);
145
146 SetBackgroundColour(*wxWHITE);
147 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
148
149 // Tell the sizers to use the given or best size
150 SetBestFittingSize(size);
151
152 // Create a buffer
153 RecreateBuffer(size);
154
155 SetCursor(wxCursor(wxCURSOR_IBEAM));
156
157 return true;
158 }
159
160 wxRichTextCtrl::~wxRichTextCtrl()
161 {
162 delete m_contextMenu;
163 }
164
165 /// Member initialisation
166 void wxRichTextCtrl::Init()
167 {
168 m_freezeCount = 0;
169 m_contextMenu = NULL;
170 m_caret = NULL;
171 m_caretPosition = -1;
172 m_selectionRange.SetRange(-2, -2);
173 m_selectionAnchor = -2;
174 m_editable = true;
175 m_caretAtLineStart = false;
176 m_dragging = false;
177 m_fullLayoutRequired = false;
178 m_fullLayoutTime = 0;
179 m_fullLayoutSavedPosition = 0;
180 m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
181 }
182
183 /// Call Freeze to prevent refresh
184 void wxRichTextCtrl::Freeze()
185 {
186 m_freezeCount ++;
187 }
188
189 /// Call Thaw to refresh
190 void wxRichTextCtrl::Thaw(bool refresh)
191 {
192 m_freezeCount --;
193
194 if (m_freezeCount == 0 && refresh)
195 {
196 SetupScrollbars();
197 Refresh();
198 }
199 }
200
201 /// Clear all text
202 void wxRichTextCtrl::Clear()
203 {
204 m_buffer.Reset();
205 m_buffer.SetDirty(true);
206 m_caretPosition = -1;
207 m_caretAtLineStart = false;
208 m_selectionRange.SetRange(-2, -2);
209
210 if (m_freezeCount == 0)
211 {
212 SetupScrollbars();
213 Refresh();
214 }
215 SendUpdateEvent();
216 }
217
218 /// Painting
219 void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
220 {
221 {
222 wxBufferedPaintDC dc(this, m_bufferBitmap);
223 //wxLogDebug(wxT("OnPaint"));
224
225 PrepareDC(dc);
226
227 if (m_freezeCount > 0)
228 return;
229
230 dc.SetFont(GetFont());
231
232 // Paint the background
233 PaintBackground(dc);
234
235 wxRegion dirtyRegion = GetUpdateRegion();
236
237 wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
238 wxRect availableSpace(GetClientSize());
239 if (GetBuffer().GetDirty())
240 {
241 GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
242 GetBuffer().SetDirty(false);
243 SetupScrollbars();
244 }
245
246 GetBuffer().Draw(dc, GetBuffer().GetRange(), GetSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
247 }
248 PositionCaret();
249 }
250
251 // Empty implementation, to prevent flicker
252 void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
253 {
254 }
255
256 void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
257 {
258 wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16);
259 SetCaret(caret);
260 caret->Show();
261 PositionCaret();
262
263 if (!IsFrozen())
264 Refresh();
265 }
266
267 void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
268 {
269 SetCaret(NULL);
270
271 if (!IsFrozen())
272 Refresh();
273 }
274
275 /// Left-click
276 void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
277 {
278 SetFocus();
279
280 wxClientDC dc(this);
281 PrepareDC(dc);
282 dc.SetFont(GetFont());
283
284 long position = 0;
285 int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position);
286
287 if (hit != wxRICHTEXT_HITTEST_NONE)
288 {
289 m_dragStart = event.GetLogicalPosition(dc);
290 m_dragging = true;
291 CaptureMouse();
292
293 SelectNone();
294
295 bool caretAtLineStart = false;
296
297 if (hit & wxRICHTEXT_HITTEST_BEFORE)
298 {
299 // If we're at the start of a line (but not first in para)
300 // then we should keep the caret showing at the start of the line
301 // by showing the m_caretAtLineStart flag.
302 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
303 wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
304
305 if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
306 caretAtLineStart = true;
307 position --;
308 }
309
310 MoveCaret(position, caretAtLineStart);
311 SetDefaultStyleToCursorStyle();
312
313 #if 0
314 wxWindow* p = GetParent();
315 while (p && !p->IsKindOf(CLASSINFO(wxFrame)))
316 p = p->GetParent();
317
318 wxFrame* frame = wxDynamicCast(p, wxFrame);
319 if (frame)
320 {
321 wxString msg = wxString::Format(wxT("Found position %ld"), position);
322 frame->SetStatusText(msg, 1);
323 }
324 #endif
325 }
326
327 event.Skip();
328 }
329
330 /// Left-up
331 void wxRichTextCtrl::OnLeftUp(wxMouseEvent& WXUNUSED(event))
332 {
333 if (m_dragging)
334 {
335 m_dragging = false;
336 if (GetCapture() == this)
337 ReleaseMouse();
338 }
339 }
340
341 /// Left-click
342 void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
343 {
344 if (!event.Dragging())
345 {
346 event.Skip();
347 return;
348 }
349
350 wxClientDC dc(this);
351 PrepareDC(dc);
352 dc.SetFont(GetFont());
353
354 long position = 0;
355 wxPoint logicalPt = event.GetLogicalPosition(dc);
356 int hit = GetBuffer().HitTest(dc, logicalPt, position);
357
358 if (m_dragging && hit != wxRICHTEXT_HITTEST_NONE)
359 {
360 // TODO: test closeness
361
362 bool caretAtLineStart = false;
363
364 if (hit & wxRICHTEXT_HITTEST_BEFORE)
365 {
366 // If we're at the start of a line (but not first in para)
367 // then we should keep the caret showing at the start of the line
368 // by showing the m_caretAtLineStart flag.
369 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
370 wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
371
372 if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
373 caretAtLineStart = true;
374 position --;
375 }
376
377 if (m_caretPosition != position)
378 {
379 bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
380
381 MoveCaret(position, caretAtLineStart);
382 SetDefaultStyleToCursorStyle();
383
384 if (extendSel)
385 Refresh();
386 }
387 }
388 }
389
390 /// Right-click
391 void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
392 {
393 SetFocus();
394 event.Skip();
395 }
396
397 /// Left-double-click
398 void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
399 {
400 event.Skip();
401 }
402
403 /// Middle-click
404 void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
405 {
406 event.Skip();
407 }
408
409 /// Key press
410 void wxRichTextCtrl::OnChar(wxKeyEvent& event)
411 {
412 int flags = 0;
413 if (event.ControlDown())
414 flags |= wxRICHTEXT_CTRL_DOWN;
415 if (event.ShiftDown())
416 flags |= wxRICHTEXT_SHIFT_DOWN;
417 if (event.AltDown())
418 flags |= wxRICHTEXT_ALT_DOWN;
419
420 if (event.GetKeyCode() == WXK_LEFT ||
421 event.GetKeyCode() == WXK_RIGHT ||
422 event.GetKeyCode() == WXK_UP ||
423 event.GetKeyCode() == WXK_DOWN ||
424 event.GetKeyCode() == WXK_HOME ||
425 event.GetKeyCode() == WXK_PAGEUP ||
426 event.GetKeyCode() == WXK_PAGEDOWN ||
427 event.GetKeyCode() == WXK_PRIOR ||
428 event.GetKeyCode() == WXK_NEXT ||
429 event.GetKeyCode() == WXK_END)
430 {
431 Navigate(event.GetKeyCode(), flags);
432 }
433 else if (event.GetKeyCode() == WXK_RETURN)
434 {
435 BeginBatchUndo(_("Insert Text"));
436
437 long newPos = m_caretPosition;
438
439 DeleteSelectedContent(& newPos);
440
441 GetBuffer().InsertNewlineWithUndo(newPos+1, this);
442
443 wxRichTextEvent cmdEvent(
444 wxEVT_COMMAND_RICHTEXT_RETURN,
445 GetId());
446 cmdEvent.SetEventObject(this);
447 cmdEvent.SetFlags(flags);
448 GetEventHandler()->ProcessEvent(cmdEvent);
449
450 EndBatchUndo();
451 SetDefaultStyleToCursorStyle();
452 }
453 else if (event.GetKeyCode() == WXK_BACK)
454 {
455 BeginBatchUndo(_("Delete Text"));
456
457 // Submit range in character positions, which are greater than caret positions,
458 // so subtract 1 for deleted character and add 1 for conversion to character position.
459 if (m_caretPosition > -1 && !HasSelection())
460 {
461 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition, m_caretPosition),
462 m_caretPosition, // Current caret position
463 m_caretPosition-1, // New caret position
464 this);
465 }
466 else
467 DeleteSelectedContent();
468
469 EndBatchUndo();
470
471 // Shouldn't this be in Do()?
472 if (GetLastPosition() == -1)
473 {
474 GetBuffer().Reset();
475
476 m_caretPosition = -1;
477 PositionCaret();
478 SetDefaultStyleToCursorStyle();
479 }
480
481 }
482 else if (event.GetKeyCode() == WXK_DELETE)
483 {
484 BeginBatchUndo(_("Delete Text"));
485
486 // Submit range in character positions, which are greater than caret positions,
487 if (m_caretPosition < GetBuffer().GetRange().GetEnd()+1 && !HasSelection())
488 {
489 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition+1, m_caretPosition+1),
490 m_caretPosition, // Current caret position
491 m_caretPosition+1, // New caret position
492 this);
493 }
494 else
495 DeleteSelectedContent();
496
497 EndBatchUndo();
498
499 // Shouldn't this be in Do()?
500 if (GetLastPosition() == -1)
501 {
502 GetBuffer().Reset();
503
504 m_caretPosition = -1;
505 PositionCaret();
506 SetDefaultStyleToCursorStyle();
507 }
508 }
509 else
510 {
511 BeginBatchUndo(_("Insert Text"));
512
513 long newPos = m_caretPosition;
514 DeleteSelectedContent(& newPos);
515
516 wxString str = (wxChar) event.GetKeyCode();
517 GetBuffer().InsertTextWithUndo(newPos+1, str, this);
518
519 EndBatchUndo();
520
521 SetDefaultStyleToCursorStyle();
522 }
523 #if 0
524 else
525 event.Skip();
526 #endif
527 }
528
529 /// Delete content if there is a selection, e.g. when pressing a key.
530 bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
531 {
532 if (HasSelection())
533 {
534 long pos = m_selectionRange.GetStart();
535 GetBuffer().DeleteRangeWithUndo(m_selectionRange,
536 m_caretPosition, // Current caret position
537 pos, // New caret position
538 this);
539 m_selectionRange.SetRange(-2, -2);
540
541 if (newPos)
542 *newPos = pos-1;
543 return true;
544 }
545 else
546 return false;
547 }
548
549 /// Keyboard navigation
550
551 /*
552
553 Left: left one character
554 Right: right one character
555 Up: up one line
556 Down: down one line
557 Ctrl-Left: left one word
558 Ctrl-Right: right one word
559 Ctrl-Up: previous paragraph start
560 Ctrl-Down: next start of paragraph
561 Home: start of line
562 End: end of line
563 Ctrl-Home: start of document
564 Ctrl-End: end of document
565 Page-Up: Up a screen
566 Page-Down: Down a screen
567
568 Maybe:
569
570 Ctrl-Alt-PgUp: Start of window
571 Ctrl-Alt-PgDn: End of window
572 F8: Start selection mode
573 Esc: End selection mode
574
575 Adding Shift does the above but starts/extends selection.
576
577
578 */
579
580 bool wxRichTextCtrl::Navigate(int keyCode, int flags)
581 {
582 bool success = false;
583 Freeze();
584
585 if (keyCode == WXK_RIGHT)
586 {
587 if (flags & wxRICHTEXT_CTRL_DOWN)
588 success = WordRight(1, flags);
589 else
590 success = MoveRight(1, flags);
591 }
592 else if (keyCode == WXK_LEFT)
593 {
594 if (flags & wxRICHTEXT_CTRL_DOWN)
595 success = WordLeft(1, flags);
596 else
597 success = MoveLeft(1, flags);
598 }
599 else if (keyCode == WXK_UP)
600 {
601 if (flags & wxRICHTEXT_CTRL_DOWN)
602 success = MoveToParagraphStart(flags);
603 else
604 success = MoveUp(1, flags);
605 }
606 else if (keyCode == WXK_DOWN)
607 {
608 if (flags & wxRICHTEXT_CTRL_DOWN)
609 success = MoveToParagraphEnd(flags);
610 else
611 success = MoveDown(1, flags);
612 }
613 else if (keyCode == WXK_PAGEUP || keyCode == WXK_PRIOR)
614 {
615 success = PageUp(1, flags);
616 }
617 else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NEXT)
618 {
619 success = PageDown(1, flags);
620 }
621 else if (keyCode == WXK_HOME)
622 {
623 if (flags & wxRICHTEXT_CTRL_DOWN)
624 success = MoveHome(flags);
625 else
626 success = MoveToLineStart(flags);
627 }
628 else if (keyCode == WXK_END)
629 {
630 if (flags & wxRICHTEXT_CTRL_DOWN)
631 success = MoveEnd(flags);
632 else
633 success = MoveToLineEnd(flags);
634 }
635
636 if (success)
637 {
638 ScrollIntoView(m_caretPosition, keyCode);
639 SetDefaultStyleToCursorStyle();
640 }
641
642 Thaw(false);
643
644 return success;
645 }
646
647 /// Extend the selection. Selections are in caret positions.
648 bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
649 {
650 if (flags & wxRICHTEXT_SHIFT_DOWN)
651 {
652 // If not currently selecting, start selecting
653 if (m_selectionRange.GetStart() == -2)
654 {
655 m_selectionAnchor = oldPos;
656
657 if (oldPos > newPos)
658 m_selectionRange.SetRange(newPos+1, oldPos);
659 else
660 m_selectionRange.SetRange(oldPos+1, newPos);
661 }
662 else
663 {
664 // Always ensure that the selection range start is greater than
665 // the end.
666 if (newPos > m_selectionAnchor)
667 m_selectionRange.SetRange(m_selectionAnchor+1, newPos);
668 else
669 m_selectionRange.SetRange(newPos+1, m_selectionAnchor);
670 }
671
672 if (m_selectionRange.GetStart() > m_selectionRange.GetEnd())
673 {
674 wxLogDebug(wxT("Strange selection range"));
675 }
676
677 return true;
678 }
679 else
680 return false;
681 }
682
683 /// Scroll into view, returning true if we scrolled.
684 /// This takes a _caret_ position.
685 bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
686 {
687 wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
688
689 if (!line)
690 return false;
691
692 int ppuX, ppuY;
693 GetScrollPixelsPerUnit(& ppuX, & ppuY);
694
695 int startX, startY;
696 GetViewStart(& startX, & startY);
697 startX = 0;
698 startY = startY * ppuY;
699
700 int sx, sy;
701 GetVirtualSize(& sx, & sy);
702 sx = 0;
703 if (ppuY != 0)
704 sy = sy/ppuY;
705
706 wxRect rect = line->GetRect();
707
708 bool scrolled = false;
709
710 wxSize clientSize = GetClientSize();
711
712 // Going down
713 if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_NEXT || keyCode == WXK_PAGEDOWN)
714 {
715 if ((rect.y + rect.height) > (clientSize.y + startY))
716 {
717 // Make it scroll so this item is at the bottom
718 // of the window
719 int y = rect.y - (clientSize.y - rect.height);
720 y = (int) (0.5 + y/ppuY);
721
722 if (startY != y)
723 {
724 SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
725 scrolled = true;
726 }
727 }
728 else if (rect.y < startY)
729 {
730 // Make it scroll so this item is at the top
731 // of the window
732 int y = rect.y ;
733 y = (int) (0.5 + y/ppuY);
734
735 if (startY != y)
736 {
737 SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
738 scrolled = true;
739 }
740 }
741 }
742 // Going up
743 else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_PRIOR || keyCode == WXK_PAGEUP)
744 {
745 if (rect.y < startY)
746 {
747 // Make it scroll so this item is at the top
748 // of the window
749 int y = rect.y ;
750 y = (int) (0.5 + y/ppuY);
751
752 if (startY != y)
753 {
754 SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
755 scrolled = true;
756 }
757 }
758 else if ((rect.y + rect.height) > (clientSize.y + startY))
759 {
760 // Make it scroll so this item is at the bottom
761 // of the window
762 int y = rect.y - (clientSize.y - rect.height);
763 y = (int) (0.5 + y/ppuY);
764
765 if (startY != y)
766 {
767 SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
768 scrolled = true;
769 }
770 }
771 }
772 PositionCaret();
773
774 return scrolled;
775 }
776
777 /// Is the given position visible on the screen?
778 bool wxRichTextCtrl::IsPositionVisible(long pos) const
779 {
780 wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
781
782 if (!line)
783 return false;
784
785 int ppuX, ppuY;
786 GetScrollPixelsPerUnit(& ppuX, & ppuY);
787
788 int startX, startY;
789 GetViewStart(& startX, & startY);
790 startX = 0;
791 startY = startY * ppuY;
792
793 int sx, sy;
794 GetVirtualSize(& sx, & sy);
795 sx = 0;
796 if (ppuY != 0)
797 sy = sy/ppuY;
798
799 wxRect rect = line->GetRect();
800
801 wxSize clientSize = GetClientSize();
802
803 return !(((rect.y + rect.height) > (clientSize.y + startY)) || rect.y < startY);
804 }
805
806 void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
807 {
808 m_caretPosition = position;
809 m_caretAtLineStart = showAtLineStart;
810 }
811
812 /// Move caret one visual step forward: this may mean setting a flag
813 /// and keeping the same position if we're going from the end of one line
814 /// to the start of the next, which may be the exact same caret position.
815 void wxRichTextCtrl::MoveCaretForward(long oldPosition)
816 {
817 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
818
819 // Only do the check if we're not at the end of the paragraph (where things work OK
820 // anyway)
821 if (para && (oldPosition != para->GetRange().GetEnd() - 1))
822 {
823 wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
824
825 if (line)
826 {
827 wxRichTextRange lineRange = line->GetAbsoluteRange();
828
829 // We're at the end of a line. See whether we need to
830 // stay at the same actual caret position but change visual
831 // position, or not.
832 if (oldPosition == lineRange.GetEnd())
833 {
834 if (m_caretAtLineStart)
835 {
836 // We're already at the start of the line, so actually move on now.
837 m_caretPosition = oldPosition + 1;
838 m_caretAtLineStart = false;
839 }
840 else
841 {
842 // We're showing at the end of the line, so keep to
843 // the same position but indicate that we're to show
844 // at the start of the next line.
845 m_caretPosition = oldPosition;
846 m_caretAtLineStart = true;
847 }
848 SetDefaultStyleToCursorStyle();
849 return;
850 }
851 }
852 }
853 m_caretPosition ++;
854 SetDefaultStyleToCursorStyle();
855 }
856
857 /// Move caret one visual step backward: this may mean setting a flag
858 /// and keeping the same position if we're going from the end of one line
859 /// to the start of the next, which may be the exact same caret position.
860 void wxRichTextCtrl::MoveCaretBack(long oldPosition)
861 {
862 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
863
864 // Only do the check if we're not at the start of the paragraph (where things work OK
865 // anyway)
866 if (para && (oldPosition != para->GetRange().GetStart()))
867 {
868 wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
869
870 if (line)
871 {
872 wxRichTextRange lineRange = line->GetAbsoluteRange();
873
874 // We're at the start of a line. See whether we need to
875 // stay at the same actual caret position but change visual
876 // position, or not.
877 if (oldPosition == lineRange.GetStart())
878 {
879 m_caretPosition = oldPosition-1;
880 m_caretAtLineStart = true;
881 return;
882 }
883 else if (oldPosition == lineRange.GetEnd())
884 {
885 if (m_caretAtLineStart)
886 {
887 // We're at the start of the line, so keep the same caret position
888 // but clear the start-of-line flag.
889 m_caretPosition = oldPosition;
890 m_caretAtLineStart = false;
891 }
892 else
893 {
894 // We're showing at the end of the line, so go back
895 // to the previous character position.
896 m_caretPosition = oldPosition - 1;
897 }
898 SetDefaultStyleToCursorStyle();
899 return;
900 }
901 }
902 }
903 m_caretPosition --;
904 SetDefaultStyleToCursorStyle();
905 }
906
907 /// Move right
908 bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
909 {
910 long endPos = GetBuffer().GetRange().GetEnd();
911
912 if (m_caretPosition + noPositions < endPos)
913 {
914 long oldPos = m_caretPosition;
915 long newPos = m_caretPosition + noPositions;
916
917 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
918 if (!extendSel)
919 SelectNone();
920
921 // Determine by looking at oldPos and m_caretPosition whether
922 // we moved from the end of a line to the start of the next line, in which case
923 // we want to adjust the caret position such that it is positioned at the
924 // start of the next line, rather than jumping past the first character of the
925 // line.
926 if (noPositions == 1 && !extendSel)
927 MoveCaretForward(oldPos);
928 else
929 SetCaretPosition(newPos);
930
931 PositionCaret();
932 SetDefaultStyleToCursorStyle();
933
934 if (extendSel)
935 Refresh();
936 return true;
937 }
938 else
939 return false;
940 }
941
942 /// Move left
943 bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
944 {
945 long startPos = -1;
946
947 if (m_caretPosition > startPos - noPositions + 1)
948 {
949 long oldPos = m_caretPosition;
950 long newPos = m_caretPosition - noPositions;
951 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
952 if (!extendSel)
953 SelectNone();
954
955 if (noPositions == 1 && !extendSel)
956 MoveCaretBack(oldPos);
957 else
958 SetCaretPosition(newPos);
959
960 PositionCaret();
961 SetDefaultStyleToCursorStyle();
962
963 if (extendSel)
964 Refresh();
965 return true;
966 }
967 else
968 return false;
969 }
970
971 /// Move up
972 bool wxRichTextCtrl::MoveUp(int noLines, int flags)
973 {
974 return MoveDown(- noLines, flags);
975 }
976
977 /// Move up
978 bool wxRichTextCtrl::MoveDown(int noLines, int flags)
979 {
980 if (!GetCaret())
981 return false;
982
983 long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
984 wxPoint pt = GetCaret()->GetPosition();
985 long newLine = lineNumber + noLines;
986
987 if (lineNumber != -1)
988 {
989 if (noLines > 0)
990 {
991 long lastLine = GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd());
992
993 if (newLine > lastLine)
994 return false;
995 }
996 else
997 {
998 if (newLine < 0)
999 return false;
1000 }
1001 }
1002
1003 wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine);
1004 if (lineObj)
1005 {
1006 pt.y = lineObj->GetAbsolutePosition().y + 2;
1007 }
1008 else
1009 return false;
1010
1011 long newPos = 0;
1012 wxClientDC dc(this);
1013 PrepareDC(dc);
1014 dc.SetFont(GetFont());
1015
1016 int hitTest = GetBuffer().HitTest(dc, pt, newPos);
1017
1018 if (hitTest != wxRICHTEXT_HITTEST_NONE)
1019 {
1020 // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
1021 // we want to be at the end of the last line but with m_caretAtLineStart set to true,
1022 // so we view the caret at the start of the line.
1023 bool caretLineStart = false;
1024 if (hitTest == wxRICHTEXT_HITTEST_BEFORE)
1025 {
1026 wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1);
1027 wxRichTextRange lineRange;
1028 if (thisLine)
1029 lineRange = thisLine->GetAbsoluteRange();
1030
1031 if (thisLine && (newPos-1) == lineRange.GetEnd())
1032 {
1033 newPos --;
1034 caretLineStart = true;
1035 }
1036 else
1037 {
1038 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(newPos);
1039 if (para && para->GetRange().GetStart() == newPos)
1040 newPos --;
1041 }
1042 }
1043
1044 long newSelEnd = newPos;
1045
1046 bool extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
1047 if (!extendSel)
1048 SelectNone();
1049
1050 SetCaretPosition(newPos, caretLineStart);
1051 PositionCaret();
1052 SetDefaultStyleToCursorStyle();
1053
1054 if (extendSel)
1055 Refresh();
1056 return true;
1057 }
1058 else
1059 return false;
1060 }
1061
1062 /// Move to the end of the paragraph
1063 bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
1064 {
1065 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
1066 if (para)
1067 {
1068 long newPos = para->GetRange().GetEnd() - 1;
1069 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1070 if (!extendSel)
1071 SelectNone();
1072
1073 SetCaretPosition(newPos);
1074 PositionCaret();
1075 SetDefaultStyleToCursorStyle();
1076
1077 if (extendSel)
1078 Refresh();
1079 return true;
1080 }
1081
1082 return false;
1083 }
1084
1085 /// Move to the start of the paragraph
1086 bool wxRichTextCtrl::MoveToParagraphStart(int flags)
1087 {
1088 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
1089 if (para)
1090 {
1091 long newPos = para->GetRange().GetStart() - 1;
1092 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1093 if (!extendSel)
1094 SelectNone();
1095
1096 SetCaretPosition(newPos);
1097 PositionCaret();
1098 SetDefaultStyleToCursorStyle();
1099
1100 if (extendSel)
1101 Refresh();
1102 return true;
1103 }
1104
1105 return false;
1106 }
1107
1108 /// Move to the end of the line
1109 bool wxRichTextCtrl::MoveToLineEnd(int flags)
1110 {
1111 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1112
1113 if (line)
1114 {
1115 wxRichTextRange lineRange = line->GetAbsoluteRange();
1116 long newPos = lineRange.GetEnd();
1117 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1118 if (!extendSel)
1119 SelectNone();
1120
1121 SetCaretPosition(newPos);
1122 PositionCaret();
1123 SetDefaultStyleToCursorStyle();
1124
1125 if (extendSel)
1126 Refresh();
1127 return true;
1128 }
1129
1130 return false;
1131 }
1132
1133 /// Move to the start of the line
1134 bool wxRichTextCtrl::MoveToLineStart(int flags)
1135 {
1136 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1137 if (line)
1138 {
1139 wxRichTextRange lineRange = line->GetAbsoluteRange();
1140 long newPos = lineRange.GetStart()-1;
1141
1142 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1143 if (!extendSel)
1144 SelectNone();
1145
1146 wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line);
1147
1148 SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
1149 PositionCaret();
1150 SetDefaultStyleToCursorStyle();
1151
1152 if (extendSel)
1153 Refresh();
1154 return true;
1155 }
1156
1157 return false;
1158 }
1159
1160 /// Move to the start of the buffer
1161 bool wxRichTextCtrl::MoveHome(int flags)
1162 {
1163 if (m_caretPosition != -1)
1164 {
1165 bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
1166 if (!extendSel)
1167 SelectNone();
1168
1169 SetCaretPosition(-1);
1170 PositionCaret();
1171 SetDefaultStyleToCursorStyle();
1172
1173 if (extendSel)
1174 Refresh();
1175 return true;
1176 }
1177 else
1178 return false;
1179 }
1180
1181 /// Move to the end of the buffer
1182 bool wxRichTextCtrl::MoveEnd(int flags)
1183 {
1184 long endPos = GetBuffer().GetRange().GetEnd()-1;
1185
1186 if (m_caretPosition != endPos)
1187 {
1188 bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
1189 if (!extendSel)
1190 SelectNone();
1191
1192 SetCaretPosition(endPos);
1193 PositionCaret();
1194 SetDefaultStyleToCursorStyle();
1195
1196 if (extendSel)
1197 Refresh();
1198 return true;
1199 }
1200 else
1201 return false;
1202 }
1203
1204 /// Move noPages pages up
1205 bool wxRichTextCtrl::PageUp(int noPages, int flags)
1206 {
1207 return PageDown(- noPages, flags);
1208 }
1209
1210 /// Move noPages pages down
1211 bool wxRichTextCtrl::PageDown(int noPages, int flags)
1212 {
1213 // Calculate which line occurs noPages * screen height further down.
1214 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
1215 if (line)
1216 {
1217 wxSize clientSize = GetClientSize();
1218 int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
1219
1220 wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY);
1221 if (newLine)
1222 {
1223 wxRichTextRange lineRange = newLine->GetAbsoluteRange();
1224 long pos = lineRange.GetStart()-1;
1225 if (pos != m_caretPosition)
1226 {
1227 wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine);
1228
1229 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1230 if (!extendSel)
1231 SelectNone();
1232
1233 SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
1234 PositionCaret();
1235 SetDefaultStyleToCursorStyle();
1236
1237 if (extendSel)
1238 Refresh();
1239 return true;
1240 }
1241 }
1242 }
1243
1244 return false;
1245 }
1246
1247 // Finds the caret position for the next word
1248 long wxRichTextCtrl::FindNextWordPosition(int direction) const
1249 {
1250 long endPos = GetBuffer().GetRange().GetEnd();
1251
1252 if (direction > 0)
1253 {
1254 long i = m_caretPosition+1+direction; // +1 for conversion to character pos
1255
1256 // First skip current text to space
1257 while (i < endPos && i > -1)
1258 {
1259 // i is in character, not caret positions
1260 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1261 if (text != wxT(" ") && !text.empty())
1262 i += direction;
1263 else
1264 {
1265 break;
1266 }
1267 }
1268 while (i < endPos && i > -1)
1269 {
1270 // i is in character, not caret positions
1271 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1272 if (text.empty()) // End of paragraph, or maybe an image
1273 return wxMax(-1, i - 1);
1274 else if (text == wxT(" ") || text.empty())
1275 i += direction;
1276 else
1277 {
1278 // Convert to caret position
1279 return wxMax(-1, i - 1);
1280 }
1281 }
1282 if (i >= endPos)
1283 return endPos-1;
1284 return i-1;
1285 }
1286 else
1287 {
1288 long i = m_caretPosition;
1289
1290 // First skip white space
1291 while (i < endPos && i > -1)
1292 {
1293 // i is in character, not caret positions
1294 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1295 if (text.empty()) // End of paragraph, or maybe an image
1296 break;
1297 else if (text == wxT(" ") || text.empty())
1298 i += direction;
1299 else
1300 break;
1301 }
1302 // Next skip current text to space
1303 while (i < endPos && i > -1)
1304 {
1305 // i is in character, not caret positions
1306 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
1307 if (text != wxT(" ") /* && !text.empty() */)
1308 i += direction;
1309 else
1310 {
1311 return i;
1312 }
1313 }
1314 if (i < -1)
1315 return -1;
1316 return i;
1317 }
1318 }
1319
1320 /// Move n words left
1321 bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
1322 {
1323 long pos = FindNextWordPosition(-1);
1324 if (pos != m_caretPosition)
1325 {
1326 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
1327
1328 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1329 if (!extendSel)
1330 SelectNone();
1331
1332 SetCaretPosition(pos, para->GetRange().GetStart() != pos);
1333 PositionCaret();
1334 SetDefaultStyleToCursorStyle();
1335
1336 if (extendSel)
1337 Refresh();
1338 return true;
1339 }
1340
1341 return false;
1342 }
1343
1344 /// Move n words right
1345 bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
1346 {
1347 long pos = FindNextWordPosition(1);
1348 if (pos != m_caretPosition)
1349 {
1350 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
1351
1352 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
1353 if (!extendSel)
1354 SelectNone();
1355
1356 SetCaretPosition(pos, para->GetRange().GetStart() != pos);
1357 PositionCaret();
1358 SetDefaultStyleToCursorStyle();
1359
1360 if (extendSel)
1361 Refresh();
1362 return true;
1363 }
1364
1365 return false;
1366 }
1367
1368 /// Sizing
1369 void wxRichTextCtrl::OnSize(wxSizeEvent& event)
1370 {
1371 // Only do sizing optimization for large buffers
1372 if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold)
1373 {
1374 m_fullLayoutRequired = true;
1375 m_fullLayoutTime = wxGetLocalTimeMillis();
1376 m_fullLayoutSavedPosition = GetFirstVisiblePosition();
1377 Layout(true /* onlyVisibleRect */);
1378 }
1379 else
1380 GetBuffer().Invalidate(wxRICHTEXT_ALL);
1381
1382 RecreateBuffer();
1383
1384 event.Skip();
1385 }
1386
1387
1388 /// Idle-time processing
1389 void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
1390 {
1391 const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
1392
1393 if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
1394 {
1395 m_fullLayoutRequired = false;
1396 m_fullLayoutTime = 0;
1397 GetBuffer().Invalidate(wxRICHTEXT_ALL);
1398 ShowPosition(m_fullLayoutSavedPosition);
1399 Refresh();
1400 }
1401 event.Skip();
1402 }
1403
1404 /// Scrolling
1405 void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
1406 {
1407 // Not used
1408 event.Skip();
1409 }
1410
1411 /// Set up scrollbars, e.g. after a resize
1412 void wxRichTextCtrl::SetupScrollbars(bool atTop)
1413 {
1414 if (m_freezeCount)
1415 return;
1416
1417 if (GetBuffer().IsEmpty())
1418 {
1419 SetScrollbars(0, 0, 0, 0, 0, 0);
1420 return;
1421 }
1422
1423 // TODO: reimplement scrolling so we scroll by line, not by fixed number
1424 // of pixels. See e.g. wxVScrolledWindow for ideas.
1425 int pixelsPerUnit = 5; // 10;
1426 wxSize clientSize = GetClientSize();
1427
1428 int maxHeight = GetBuffer().GetCachedSize().y;
1429
1430 int unitsY = maxHeight/pixelsPerUnit;
1431
1432 int startX = 0, startY = 0;
1433 if (!atTop)
1434 GetViewStart(& startX, & startY);
1435
1436 int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
1437 int maxPositionY = (wxMax(maxHeight - clientSize.y, 0))/pixelsPerUnit;
1438
1439 // Move to previous scroll position if
1440 // possible
1441 SetScrollbars(0, pixelsPerUnit,
1442 0, unitsY,
1443 wxMin(maxPositionX, startX), wxMin(maxPositionY, startY));
1444 }
1445
1446 /// Paint the background
1447 void wxRichTextCtrl::PaintBackground(wxDC& dc)
1448 {
1449 wxColour backgroundColour = GetBackgroundColour();
1450 if (!backgroundColour.Ok())
1451 backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
1452
1453 // Clear the background
1454 dc.SetBrush(wxBrush(backgroundColour));
1455 dc.SetPen(*wxTRANSPARENT_PEN);
1456 wxRect windowRect(GetClientSize());
1457 windowRect.x -= 2; windowRect.y -= 2;
1458 windowRect.width += 4; windowRect.height += 4;
1459
1460 // We need to shift the rectangle to take into account
1461 // scrolling. Converting device to logical coordinates.
1462 CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
1463 dc.DrawRectangle(windowRect);
1464 }
1465
1466 /// Recreate buffer bitmap if necessary
1467 bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
1468 {
1469 wxSize sz = size;
1470 if (sz == wxDefaultSize)
1471 sz = GetClientSize();
1472
1473 if (sz.x < 1 || sz.y < 1)
1474 return false;
1475
1476 if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
1477 m_bufferBitmap = wxBitmap(sz.x, sz.y);
1478 return m_bufferBitmap.Ok();
1479 }
1480
1481 // ----------------------------------------------------------------------------
1482 // file IO functions
1483 // ----------------------------------------------------------------------------
1484
1485 bool wxRichTextCtrl::LoadFile(const wxString& filename, int type)
1486 {
1487 bool success = GetBuffer().LoadFile(filename, type);
1488 if (success)
1489 m_filename = filename;
1490
1491 DiscardEdits();
1492 SetInsertionPoint(0);
1493 Layout();
1494 PositionCaret();
1495 SetupScrollbars(true);
1496 Refresh();
1497 SendUpdateEvent();
1498
1499 if (success)
1500 return true;
1501 else
1502 {
1503 wxLogError(_("File couldn't be loaded."));
1504
1505 return false;
1506 }
1507 }
1508
1509 bool wxRichTextCtrl::SaveFile(const wxString& filename, int type)
1510 {
1511 wxString filenameToUse = filename.empty() ? m_filename : filename;
1512 if ( filenameToUse.empty() )
1513 {
1514 // what kind of message to give? is it an error or a program bug?
1515 wxLogDebug(wxT("Can't save textctrl to file without filename."));
1516
1517 return false;
1518 }
1519
1520 if (GetBuffer().SaveFile(filenameToUse, type))
1521 {
1522 m_filename = filenameToUse;
1523
1524 DiscardEdits();
1525
1526 return true;
1527
1528 }
1529
1530 wxLogError(_("The text couldn't be saved."));
1531
1532 return false;
1533 }
1534
1535 // ----------------------------------------------------------------------------
1536 // wxRichTextCtrl specific functionality
1537 // ----------------------------------------------------------------------------
1538
1539 /// Add a new paragraph of text to the end of the buffer
1540 wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
1541 {
1542 return GetBuffer().AddParagraph(text);
1543 }
1544
1545 /// Add an image
1546 wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
1547 {
1548 return GetBuffer().AddImage(image);
1549 }
1550
1551 // ----------------------------------------------------------------------------
1552 // selection and ranges
1553 // ----------------------------------------------------------------------------
1554
1555 void wxRichTextCtrl::SelectAll()
1556 {
1557 SetSelection(0, GetLastPosition());
1558 m_selectionAnchor = -1;
1559 }
1560
1561 /// Select none
1562 void wxRichTextCtrl::SelectNone()
1563 {
1564 if (!(GetSelectionRange() == wxRichTextRange(-2, -2)))
1565 SetSelection(-2, -2);
1566 m_selectionAnchor = -2;
1567 }
1568
1569 wxString wxRichTextCtrl::GetStringSelection() const
1570 {
1571 long from, to;
1572 GetSelection(&from, &to);
1573
1574 return GetRange(from, to);
1575 }
1576
1577 // do the window-specific processing after processing the update event
1578 void wxRichTextCtrl::DoUpdateWindowUI(wxUpdateUIEvent& event)
1579 {
1580 if ( event.GetSetEnabled() )
1581 Enable(event.GetEnabled());
1582
1583 if ( event.GetSetText() )
1584 {
1585 if ( event.GetText() != GetValue() )
1586 SetValue(event.GetText());
1587 }
1588 }
1589
1590 // ----------------------------------------------------------------------------
1591 // hit testing
1592 // ----------------------------------------------------------------------------
1593
1594 wxTextCtrlHitTestResult
1595 wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
1596 {
1597 // implement in terms of the other overload as the native ports typically
1598 // can get the position and not (x, y) pair directly (although wxUniv
1599 // directly gets x and y -- and so overrides this method as well)
1600 long pos;
1601 wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
1602
1603 if ( rc != wxTE_HT_UNKNOWN )
1604 {
1605 PositionToXY(pos, x, y);
1606 }
1607
1608 return rc;
1609 }
1610
1611 wxTextCtrlHitTestResult
1612 wxRichTextCtrl::HitTest(const wxPoint& pt,
1613 long * pos) const
1614 {
1615 wxClientDC dc((wxRichTextCtrl*) this);
1616 ((wxRichTextCtrl*)this)->PrepareDC(dc);
1617
1618 int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt, *pos);
1619 if (hit == wxRICHTEXT_HITTEST_BEFORE)
1620 return wxTE_HT_BEFORE;
1621 else if (hit == wxRICHTEXT_HITTEST_AFTER)
1622 return wxTE_HT_BEYOND;
1623 else if (hit == wxRICHTEXT_HITTEST_ON)
1624 return wxTE_HT_ON_TEXT;
1625 else
1626 return wxTE_HT_UNKNOWN;
1627 }
1628
1629 // ----------------------------------------------------------------------------
1630 // set/get the controls text
1631 // ----------------------------------------------------------------------------
1632
1633 wxString wxRichTextCtrl::GetValue() const
1634 {
1635 return GetBuffer().GetText();
1636 }
1637
1638 wxString wxRichTextCtrl::GetRange(long from, long to) const
1639 {
1640 return GetBuffer().GetTextForRange(wxRichTextRange(from, to));
1641 }
1642
1643 void wxRichTextCtrl::SetValue(const wxString& value)
1644 {
1645 Clear();
1646
1647 // if the text is long enough, it's faster to just set it instead of first
1648 // comparing it with the old one (chances are that it will be different
1649 // anyhow, this comparison is there to avoid flicker for small single-line
1650 // edit controls mostly)
1651 if ( (value.length() > 0x400) || (value != GetValue()) )
1652 {
1653 DoWriteText(value, false /* not selection only */);
1654
1655 // for compatibility, don't move the cursor when doing SetValue()
1656 SetInsertionPoint(0);
1657 }
1658 else // same text
1659 {
1660 // still send an event for consistency
1661 SendUpdateEvent();
1662 }
1663
1664 // we should reset the modified flag even if the value didn't really change
1665
1666 // mark the control as being not dirty - we changed its text, not the
1667 // user
1668 DiscardEdits();
1669 }
1670
1671 void wxRichTextCtrl::WriteText(const wxString& value)
1672 {
1673 DoWriteText(value);
1674 }
1675
1676 void wxRichTextCtrl::DoWriteText(const wxString& value, bool WXUNUSED(selectionOnly))
1677 {
1678 GetBuffer().InsertTextWithUndo(m_caretPosition+1, value, this);
1679 }
1680
1681 void wxRichTextCtrl::AppendText(const wxString& text)
1682 {
1683 SetInsertionPointEnd();
1684
1685 WriteText(text);
1686 }
1687
1688 /// Write an image at the current insertion point
1689 bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType)
1690 {
1691 wxRichTextImageBlock imageBlock;
1692
1693 wxImage image2 = image;
1694 if (imageBlock.MakeImageBlock(image2, bitmapType))
1695 return WriteImage(imageBlock);
1696 else
1697 return false;
1698 }
1699
1700 bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType)
1701 {
1702 wxRichTextImageBlock imageBlock;
1703
1704 wxImage image;
1705 if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
1706 return WriteImage(imageBlock);
1707 else
1708 return false;
1709 }
1710
1711 bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock)
1712 {
1713 return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this);
1714 }
1715
1716 bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType)
1717 {
1718 if (bitmap.Ok())
1719 {
1720 wxRichTextImageBlock imageBlock;
1721
1722 wxImage image = bitmap.ConvertToImage();
1723 if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType))
1724 return WriteImage(imageBlock);
1725 else
1726 return false;
1727 }
1728 return false;
1729 }
1730
1731 /// Insert a newline (actually paragraph) at the current insertion point.
1732 bool wxRichTextCtrl::Newline()
1733 {
1734 return GetBuffer().InsertNewlineWithUndo(m_caretPosition+1, this);
1735 }
1736
1737
1738 // ----------------------------------------------------------------------------
1739 // Clipboard operations
1740 // ----------------------------------------------------------------------------
1741
1742 void wxRichTextCtrl::Copy()
1743 {
1744 if (CanCopy())
1745 {
1746 wxRichTextRange range = GetSelectionRange();
1747 GetBuffer().CopyToClipboard(range);
1748 }
1749 }
1750
1751 void wxRichTextCtrl::Cut()
1752 {
1753 if (CanCut())
1754 {
1755 wxRichTextRange range = GetSelectionRange();
1756 GetBuffer().CopyToClipboard(range);
1757
1758 DeleteSelectedContent();
1759 Layout();
1760 Refresh();
1761 }
1762 }
1763
1764 void wxRichTextCtrl::Paste()
1765 {
1766 if (CanPaste())
1767 {
1768 BeginBatchUndo(_("Paste"));
1769
1770 long newPos = m_caretPosition;
1771 DeleteSelectedContent(& newPos);
1772
1773 GetBuffer().PasteFromClipboard(newPos);
1774
1775 EndBatchUndo();
1776 }
1777 }
1778
1779 void wxRichTextCtrl::DeleteSelection()
1780 {
1781 if (CanDeleteSelection())
1782 {
1783 DeleteSelectedContent();
1784 }
1785 }
1786
1787 bool wxRichTextCtrl::HasSelection() const
1788 {
1789 return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2;
1790 }
1791
1792 bool wxRichTextCtrl::CanCopy() const
1793 {
1794 // Can copy if there's a selection
1795 return HasSelection();
1796 }
1797
1798 bool wxRichTextCtrl::CanCut() const
1799 {
1800 return HasSelection() && IsEditable();
1801 }
1802
1803 bool wxRichTextCtrl::CanPaste() const
1804 {
1805 if ( !IsEditable() )
1806 return false;
1807
1808 return GetBuffer().CanPasteFromClipboard();
1809 }
1810
1811 bool wxRichTextCtrl::CanDeleteSelection() const
1812 {
1813 return HasSelection() && IsEditable();
1814 }
1815
1816
1817 // ----------------------------------------------------------------------------
1818 // Accessors
1819 // ----------------------------------------------------------------------------
1820
1821 void wxRichTextCtrl::SetEditable(bool editable)
1822 {
1823 m_editable = editable;
1824 }
1825
1826 void wxRichTextCtrl::SetInsertionPoint(long pos)
1827 {
1828 SelectNone();
1829
1830 m_caretPosition = pos - 1;
1831 }
1832
1833 void wxRichTextCtrl::SetInsertionPointEnd()
1834 {
1835 long pos = GetLastPosition();
1836 SetInsertionPoint(pos);
1837 }
1838
1839 long wxRichTextCtrl::GetInsertionPoint() const
1840 {
1841 return m_caretPosition+1;
1842 }
1843
1844 wxTextPos wxRichTextCtrl::GetLastPosition() const
1845 {
1846 return GetBuffer().GetRange().GetEnd();
1847 }
1848
1849 // If the return values from and to are the same, there is no
1850 // selection.
1851 void wxRichTextCtrl::GetSelection(long* from, long* to) const
1852 {
1853 *from = m_selectionRange.GetStart();
1854 *to = m_selectionRange.GetEnd();
1855 }
1856
1857 bool wxRichTextCtrl::IsEditable() const
1858 {
1859 return m_editable;
1860 }
1861
1862 // ----------------------------------------------------------------------------
1863 // selection
1864 // ----------------------------------------------------------------------------
1865
1866 void wxRichTextCtrl::SetSelection(long from, long to)
1867 {
1868 // if from and to are both -1, it means (in wxWidgets) that all text should
1869 // be selected.
1870 if ( (from == -1) && (to == -1) )
1871 {
1872 from = 0;
1873 to = GetLastPosition();
1874 }
1875
1876 DoSetSelection(from, to);
1877 }
1878
1879 void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret))
1880 {
1881 m_selectionAnchor = from;
1882 m_selectionRange.SetRange(from, to);
1883 Refresh();
1884 PositionCaret();
1885 }
1886
1887 // ----------------------------------------------------------------------------
1888 // Editing
1889 // ----------------------------------------------------------------------------
1890
1891 void wxRichTextCtrl::Replace(long WXUNUSED(from), long WXUNUSED(to), const wxString& value)
1892 {
1893 BeginBatchUndo(_("Replace"));
1894
1895 DeleteSelectedContent();
1896
1897 DoWriteText(value, true /* selection only */);
1898
1899 EndBatchUndo();
1900 }
1901
1902 void wxRichTextCtrl::Remove(long from, long to)
1903 {
1904 SelectNone();
1905
1906 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from, to),
1907 m_caretPosition, // Current caret position
1908 from, // New caret position
1909 this);
1910
1911 Layout();
1912 if (!IsFrozen())
1913 Refresh();
1914 }
1915
1916 bool wxRichTextCtrl::IsModified() const
1917 {
1918 return m_buffer.IsModified();
1919 }
1920
1921 void wxRichTextCtrl::MarkDirty()
1922 {
1923 m_buffer.Modify(true);
1924 }
1925
1926 void wxRichTextCtrl::DiscardEdits()
1927 {
1928 m_buffer.Modify(false);
1929 m_buffer.GetCommandProcessor()->ClearCommands();
1930 }
1931
1932 int wxRichTextCtrl::GetNumberOfLines() const
1933 {
1934 return GetBuffer().GetParagraphCount();
1935 }
1936
1937 // ----------------------------------------------------------------------------
1938 // Positions <-> coords
1939 // ----------------------------------------------------------------------------
1940
1941 long wxRichTextCtrl::XYToPosition(long x, long y) const
1942 {
1943 return GetBuffer().XYToPosition(x, y);
1944 }
1945
1946 bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
1947 {
1948 return GetBuffer().PositionToXY(pos, x, y);
1949 }
1950
1951 // ----------------------------------------------------------------------------
1952 //
1953 // ----------------------------------------------------------------------------
1954
1955 void wxRichTextCtrl::ShowPosition(long pos)
1956 {
1957 if (!IsPositionVisible(pos))
1958 ScrollIntoView(pos-1, WXK_DOWN);
1959 }
1960
1961 int wxRichTextCtrl::GetLineLength(long lineNo) const
1962 {
1963 return GetBuffer().GetParagraphLength(lineNo);
1964 }
1965
1966 wxString wxRichTextCtrl::GetLineText(long lineNo) const
1967 {
1968 return GetBuffer().GetParagraphText(lineNo);
1969 }
1970
1971 // ----------------------------------------------------------------------------
1972 // Undo/redo
1973 // ----------------------------------------------------------------------------
1974
1975 void wxRichTextCtrl::Undo()
1976 {
1977 if (CanUndo())
1978 {
1979 GetCommandProcessor()->Undo();
1980 }
1981 }
1982
1983 void wxRichTextCtrl::Redo()
1984 {
1985 if (CanRedo())
1986 {
1987 GetCommandProcessor()->Redo();
1988 }
1989 }
1990
1991 bool wxRichTextCtrl::CanUndo() const
1992 {
1993 return GetCommandProcessor()->CanUndo();
1994 }
1995
1996 bool wxRichTextCtrl::CanRedo() const
1997 {
1998 return GetCommandProcessor()->CanRedo();
1999 }
2000
2001 // ----------------------------------------------------------------------------
2002 // implemenation details
2003 // ----------------------------------------------------------------------------
2004
2005 void wxRichTextCtrl::Command(wxCommandEvent & event)
2006 {
2007 SetValue(event.GetString());
2008 GetEventHandler()->ProcessEvent(event);
2009 }
2010
2011 void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
2012 {
2013 // By default, load the first file into the text window.
2014 if (event.GetNumberOfFiles() > 0)
2015 {
2016 LoadFile(event.GetFiles()[0]);
2017 }
2018 }
2019
2020 // ----------------------------------------------------------------------------
2021 // text control event processing
2022 // ----------------------------------------------------------------------------
2023
2024 bool wxRichTextCtrl::SendUpdateEvent()
2025 {
2026 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
2027 InitCommandEvent(event);
2028
2029 return GetEventHandler()->ProcessEvent(event);
2030 }
2031
2032 void wxRichTextCtrl::InitCommandEvent(wxCommandEvent& event) const
2033 {
2034 event.SetEventObject((wxControlBase *)this); // const_cast
2035
2036 switch ( m_clientDataType )
2037 {
2038 case wxClientData_Void:
2039 event.SetClientData(GetClientData());
2040 break;
2041
2042 case wxClientData_Object:
2043 event.SetClientObject(GetClientObject());
2044 break;
2045
2046 case wxClientData_None:
2047 // nothing to do
2048 ;
2049 }
2050 }
2051
2052
2053 wxSize wxRichTextCtrl::DoGetBestSize() const
2054 {
2055 return wxSize(10, 10);
2056 }
2057
2058 // ----------------------------------------------------------------------------
2059 // standard handlers for standard edit menu events
2060 // ----------------------------------------------------------------------------
2061
2062 void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
2063 {
2064 Cut();
2065 }
2066
2067 void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
2068 {
2069 DeleteSelection();
2070 }
2071
2072 void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
2073 {
2074 Copy();
2075 }
2076
2077 void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
2078 {
2079 Paste();
2080 }
2081
2082 void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
2083 {
2084 Undo();
2085 }
2086
2087 void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
2088 {
2089 Redo();
2090 }
2091
2092 void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
2093 {
2094 event.Enable( CanCut() );
2095 }
2096
2097 void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
2098 {
2099 event.Enable( CanCopy() );
2100 }
2101
2102 void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
2103 {
2104 event.Enable( CanDeleteSelection() );
2105 }
2106
2107 void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
2108 {
2109 event.Enable( CanPaste() );
2110 }
2111
2112 void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
2113 {
2114 event.Enable( CanUndo() );
2115 event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
2116 }
2117
2118 void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
2119 {
2120 event.Enable( CanRedo() );
2121 event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
2122 }
2123
2124 void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
2125 {
2126 SelectAll();
2127 }
2128
2129 void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
2130 {
2131 event.Enable(GetLastPosition() > 0);
2132 }
2133
2134 void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& WXUNUSED(event))
2135 {
2136 if (!m_contextMenu)
2137 {
2138 m_contextMenu = new wxMenu;
2139 m_contextMenu->Append(wxID_UNDO, _("&Undo"));
2140 m_contextMenu->Append(wxID_REDO, _("&Redo"));
2141 m_contextMenu->AppendSeparator();
2142 m_contextMenu->Append(wxID_CUT, _("Cu&t"));
2143 m_contextMenu->Append(wxID_COPY, _("&Copy"));
2144 m_contextMenu->Append(wxID_PASTE, _("&Paste"));
2145 m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
2146 m_contextMenu->AppendSeparator();
2147 m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
2148 }
2149 PopupMenu(m_contextMenu);
2150 return;
2151 }
2152
2153 bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
2154 {
2155 return GetBuffer().SetStyle(wxRichTextRange(start, end), style);
2156 }
2157
2158 bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
2159 {
2160 return GetBuffer().SetStyle(range, style);
2161 }
2162
2163 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style)
2164 {
2165 return GetBuffer().SetDefaultStyle(style);
2166 }
2167
2168 const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const
2169 {
2170 return GetBuffer().GetDefaultStyle();
2171 }
2172
2173 bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style) const
2174 {
2175 return GetBuffer().GetStyle(position, style);
2176 }
2177
2178 bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style) const
2179 {
2180 return GetBuffer().GetStyle(position, style);
2181 }
2182
2183 /// Set font, and also the buffer attributes
2184 bool wxRichTextCtrl::SetFont(const wxFont& font)
2185 {
2186 #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
2187 wxControl::SetFont(font);
2188 #else
2189 wxScrolledWindow::SetFont(font);
2190 #endif
2191
2192 wxTextAttrEx attr = GetBuffer().GetAttributes();
2193 attr.SetFont(font);
2194 GetBuffer().SetBasicStyle(attr);
2195 GetBuffer().SetDefaultStyle(attr);
2196
2197 return true;
2198 }
2199
2200 /// Transform logical to physical
2201 wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
2202 {
2203 wxPoint pt;
2204 CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
2205
2206 return pt;
2207 }
2208
2209 /// Transform physical to logical
2210 wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
2211 {
2212 wxPoint pt;
2213 CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
2214
2215 return pt;
2216 }
2217
2218 /// Position the caret
2219 void wxRichTextCtrl::PositionCaret()
2220 {
2221 if (!GetCaret())
2222 return;
2223
2224 //wxLogDebug(wxT("PositionCaret"));
2225
2226 wxRect caretRect;
2227 if (GetCaretPositionForIndex(GetCaretPosition(), caretRect))
2228 {
2229 wxPoint originalPt = caretRect.GetPosition();
2230 wxPoint pt = GetPhysicalPoint(originalPt);
2231 if (GetCaret()->GetPosition() != pt)
2232 {
2233 GetCaret()->Move(pt);
2234 GetCaret()->SetSize(caretRect.GetSize());
2235 }
2236 }
2237 }
2238
2239 /// Get the caret height and position for the given character position
2240 bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect)
2241 {
2242 wxClientDC dc(this);
2243 dc.SetFont(GetFont());
2244
2245 PrepareDC(dc);
2246
2247 wxPoint pt;
2248 int height = 0;
2249
2250 if (GetBuffer().FindPosition(dc, position, pt, & height, m_caretAtLineStart))
2251 {
2252 rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
2253 return true;
2254 }
2255 else
2256 return false;
2257 }
2258
2259 /// Gets the line for the visible caret position. If the caret is
2260 /// shown at the very end of the line, it means the next character is actually
2261 /// on the following line. So let's get the line we're expecting to find
2262 /// if this is the case.
2263 wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
2264 {
2265 wxRichTextLine* line = GetBuffer().GetLineAtPosition(caretPosition, true);
2266 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true);
2267 if (line)
2268 {
2269 wxRichTextRange lineRange = line->GetAbsoluteRange();
2270 if (caretPosition == lineRange.GetStart()-1 &&
2271 (para->GetRange().GetStart() != lineRange.GetStart()))
2272 {
2273 if (!m_caretAtLineStart)
2274 line = GetBuffer().GetLineAtPosition(caretPosition-1, true);
2275 }
2276 }
2277 return line;
2278 }
2279
2280
2281 /// Move the caret to the given character position
2282 bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart)
2283 {
2284 if (GetBuffer().GetDirty())
2285 Layout();
2286
2287 if (pos <= GetBuffer().GetRange().GetEnd())
2288 {
2289 SetCaretPosition(pos, showAtLineStart);
2290
2291 PositionCaret();
2292
2293 return true;
2294 }
2295 else
2296 return false;
2297 }
2298
2299 /// Layout the buffer: which we must do before certain operations, such as
2300 /// setting the caret position.
2301 bool wxRichTextCtrl::Layout(bool onlyVisibleRect)
2302 {
2303 if (GetBuffer().GetDirty() || onlyVisibleRect)
2304 {
2305 wxRect availableSpace(GetClientSize());
2306 if (availableSpace.width == 0)
2307 availableSpace.width = 10;
2308 if (availableSpace.height == 0)
2309 availableSpace.height = 10;
2310
2311 int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
2312 if (onlyVisibleRect)
2313 {
2314 flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
2315 availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
2316 }
2317
2318 wxClientDC dc(this);
2319 dc.SetFont(GetFont());
2320
2321 PrepareDC(dc);
2322
2323 GetBuffer().Defragment();
2324 GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
2325 GetBuffer().Layout(dc, availableSpace, flags);
2326 GetBuffer().SetDirty(false);
2327
2328 if (!IsFrozen())
2329 SetupScrollbars();
2330 }
2331
2332 return true;
2333 }
2334
2335 /// Is all of the selection bold?
2336 bool wxRichTextCtrl::IsSelectionBold() const
2337 {
2338 if (HasSelection())
2339 {
2340 wxRichTextAttr attr;
2341 wxRichTextRange range = GetSelectionRange();
2342 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
2343 attr.SetFontWeight(wxBOLD);
2344
2345 return HasCharacterAttributes(range, attr);
2346 }
2347 else
2348 {
2349 // If no selection, then we need to combine current style with default style
2350 // to see what the effect would be if we started typing.
2351 wxRichTextAttr attr;
2352 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
2353 if (GetStyle(GetCaretPosition()+1, attr))
2354 {
2355 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
2356 return attr.GetFontWeight() == wxBOLD;
2357 }
2358 }
2359 return false;
2360 }
2361
2362 /// Is all of the selection italics?
2363 bool wxRichTextCtrl::IsSelectionItalics() const
2364 {
2365 if (HasSelection())
2366 {
2367 wxRichTextRange range = GetSelectionRange();
2368 wxRichTextAttr attr;
2369 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
2370 attr.SetFontStyle(wxITALIC);
2371
2372 return HasCharacterAttributes(range, attr);
2373 }
2374 else
2375 {
2376 // If no selection, then we need to combine current style with default style
2377 // to see what the effect would be if we started typing.
2378 wxRichTextAttr attr;
2379 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
2380 if (GetStyle(GetCaretPosition()+1, attr))
2381 {
2382 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
2383 return attr.GetFontStyle() == wxITALIC;
2384 }
2385 }
2386 return false;
2387 }
2388
2389 /// Is all of the selection underlined?
2390 bool wxRichTextCtrl::IsSelectionUnderlined() const
2391 {
2392 if (HasSelection())
2393 {
2394 wxRichTextRange range = GetSelectionRange();
2395 wxRichTextAttr attr;
2396 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
2397 attr.SetFontUnderlined(true);
2398
2399 return HasCharacterAttributes(range, attr);
2400 }
2401 else
2402 {
2403 // If no selection, then we need to combine current style with default style
2404 // to see what the effect would be if we started typing.
2405 wxRichTextAttr attr;
2406 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
2407 if (GetStyle(GetCaretPosition()+1, attr))
2408 {
2409 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
2410 return attr.GetFontUnderlined();
2411 }
2412 }
2413 return false;
2414 }
2415
2416 /// Apply bold to the selection
2417 bool wxRichTextCtrl::ApplyBoldToSelection()
2418 {
2419 wxRichTextAttr attr;
2420 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
2421 attr.SetFontWeight(IsSelectionBold() ? wxNORMAL : wxBOLD);
2422
2423 if (HasSelection())
2424 return SetStyle(GetSelectionRange(), attr);
2425 else
2426 SetDefaultStyle(attr);
2427 return true;
2428 }
2429
2430 /// Apply italic to the selection
2431 bool wxRichTextCtrl::ApplyItalicToSelection()
2432 {
2433 wxRichTextAttr attr;
2434 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
2435 attr.SetFontStyle(IsSelectionItalics() ? wxNORMAL : wxITALIC);
2436
2437 if (HasSelection())
2438 return SetStyle(GetSelectionRange(), attr);
2439 else
2440 SetDefaultStyle(attr);
2441 return true;
2442 }
2443
2444 /// Apply underline to the selection
2445 bool wxRichTextCtrl::ApplyUnderlineToSelection()
2446 {
2447 wxRichTextAttr attr;
2448 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
2449 attr.SetFontUnderlined(!IsSelectionUnderlined());
2450
2451 if (HasSelection())
2452 return SetStyle(GetSelectionRange(), attr);
2453 else
2454 SetDefaultStyle(attr);
2455 return true;
2456 }
2457
2458 /// Is all of the selection aligned according to the specified flag?
2459 bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) const
2460 {
2461 if (HasSelection())
2462 {
2463 wxRichTextRange range = GetSelectionRange();
2464 wxRichTextAttr attr;
2465 attr.SetAlignment(alignment);
2466
2467 return HasParagraphAttributes(range, attr);
2468 }
2469 else
2470 {
2471 // If no selection, then we need to get information from the current paragraph.
2472 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
2473 if (para)
2474 return para->GetAttributes().GetAlignment() == alignment;
2475 }
2476 return false;
2477 }
2478
2479 /// Apply alignment to the selection
2480 bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
2481 {
2482 wxRichTextAttr attr;
2483 attr.SetAlignment(alignment);
2484 if (HasSelection())
2485 return SetStyle(GetSelectionRange(), attr);
2486 else
2487 {
2488 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
2489 if (para)
2490 return SetStyle(para->GetRange(), attr);
2491 }
2492 return true;
2493 }
2494
2495 /// Sets the default style to the style under the cursor
2496 bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
2497 {
2498 wxTextAttrEx attr;
2499 attr.SetFlags(wxTEXT_ATTR_CHARACTER);
2500
2501 if (GetStyle(GetCaretPosition(), attr))
2502 {
2503 SetDefaultStyle(attr);
2504 return true;
2505 }
2506 else
2507 return false;
2508 }
2509
2510 /// Returns the first visible position in the current view
2511 long wxRichTextCtrl::GetFirstVisiblePosition() const
2512 {
2513 wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y);
2514 if (line)
2515 return line->GetAbsoluteRange().GetStart();
2516 else
2517 return 0;
2518 }
2519
2520 #endif
2521 // wxUSE_RICHTEXT
2522