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