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