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