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