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