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