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