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