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