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