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