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