]> git.saurik.com Git - wxWidgets.git/blob - src/richtext/richtextctrl.cpp
Disable drag and drop if the control is not editable
[wxWidgets.git] / src / richtext / richtextctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextctrl.cpp
3 // Purpose: A rich edit control
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
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__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextctrl.h"
22 #include "wx/richtext/richtextstyles.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/wx.h"
26 #include "wx/settings.h"
27 #endif
28
29 #include "wx/timer.h"
30 #include "wx/textfile.h"
31 #include "wx/ffile.h"
32 #include "wx/filename.h"
33 #include "wx/dcbuffer.h"
34 #include "wx/arrimpl.cpp"
35 #include "wx/fontenum.h"
36 #include "wx/accel.h"
37
38 #if defined (__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__)
39 #define wxHAVE_PRIMARY_SELECTION 1
40 #else
41 #define wxHAVE_PRIMARY_SELECTION 0
42 #endif
43
44 #if wxUSE_CLIPBOARD && wxHAVE_PRIMARY_SELECTION
45 #include "wx/clipbrd.h"
46 #endif
47
48 // DLL options compatibility check:
49 #include "wx/app.h"
50 WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
51
52 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_LEFT_CLICK, wxRichTextEvent );
53 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK, wxRichTextEvent );
54 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK, wxRichTextEvent );
55 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK, wxRichTextEvent );
56 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_RETURN, wxRichTextEvent );
57 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_CHARACTER, wxRichTextEvent );
58 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_DELETE, wxRichTextEvent );
59
60 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, wxRichTextEvent );
61 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED, wxRichTextEvent );
62 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGING, wxRichTextEvent );
63 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGED, wxRichTextEvent );
64
65 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, wxRichTextEvent );
66 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, wxRichTextEvent );
67 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED, wxRichTextEvent );
68 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_SELECTION_CHANGED, wxRichTextEvent );
69 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, wxRichTextEvent );
70 wxDEFINE_EVENT( wxEVT_COMMAND_RICHTEXT_FOCUS_OBJECT_CHANGED, wxRichTextEvent );
71
72 #if wxRICHTEXT_USE_OWN_CARET
73
74 /*!
75 * wxRichTextCaret
76 *
77 * This implements a non-flashing cursor in case there
78 * are platform-specific problems with the generic caret.
79 * wxRICHTEXT_USE_OWN_CARET is set in richtextbuffer.h.
80 */
81
82 class wxRichTextCaret;
83 class wxRichTextCaretTimer: public wxTimer
84 {
85 public:
86 wxRichTextCaretTimer(wxRichTextCaret* caret)
87 {
88 m_caret = caret;
89 }
90 virtual void Notify();
91 wxRichTextCaret* m_caret;
92 };
93
94 class wxRichTextCaret: public wxCaret
95 {
96 public:
97 // ctors
98 // -----
99 // default - use Create()
100 wxRichTextCaret(): m_timer(this) { Init(); }
101 // creates a block caret associated with the given window
102 wxRichTextCaret(wxRichTextCtrl *window, int width, int height)
103 : wxCaret(window, width, height), m_timer(this) { Init(); m_richTextCtrl = window; }
104 wxRichTextCaret(wxRichTextCtrl *window, const wxSize& size)
105 : wxCaret(window, size), m_timer(this) { Init(); m_richTextCtrl = window; }
106
107 virtual ~wxRichTextCaret();
108
109 // implementation
110 // --------------
111
112 // called by wxWindow (not using the event tables)
113 virtual void OnSetFocus();
114 virtual void OnKillFocus();
115
116 // draw the caret on the given DC
117 void DoDraw(wxDC *dc);
118
119 // get the visible count
120 int GetVisibleCount() const { return m_countVisible; }
121
122 // delay repositioning
123 bool GetNeedsUpdate() const { return m_needsUpdate; }
124 void SetNeedsUpdate(bool needsUpdate = true ) { m_needsUpdate = needsUpdate; }
125
126 void Notify();
127
128 protected:
129 virtual void DoShow();
130 virtual void DoHide();
131 virtual void DoMove();
132 virtual void DoSize();
133
134 // refresh the caret
135 void Refresh();
136
137 private:
138 void Init();
139
140 int m_xOld,
141 m_yOld;
142 bool m_hasFocus; // true => our window has focus
143 bool m_needsUpdate; // must be repositioned
144 bool m_flashOn;
145 wxRichTextCaretTimer m_timer;
146 wxRichTextCtrl* m_richTextCtrl;
147 };
148 #endif
149
150 IMPLEMENT_DYNAMIC_CLASS( wxRichTextCtrl, wxControl )
151
152 IMPLEMENT_DYNAMIC_CLASS( wxRichTextEvent, wxNotifyEvent )
153
154 BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl )
155 EVT_PAINT(wxRichTextCtrl::OnPaint)
156 EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
157 EVT_IDLE(wxRichTextCtrl::OnIdle)
158 EVT_SCROLLWIN(wxRichTextCtrl::OnScroll)
159 EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
160 EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
161 EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
162 EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick)
163 EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick)
164 EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick)
165 EVT_CHAR(wxRichTextCtrl::OnChar)
166 EVT_KEY_DOWN(wxRichTextCtrl::OnChar)
167 EVT_SIZE(wxRichTextCtrl::OnSize)
168 EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus)
169 EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus)
170 EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost)
171 EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
172 EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged)
173
174 EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
175 EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
176
177 EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo)
178 EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo)
179
180 EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy)
181 EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy)
182
183 EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste)
184 EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste)
185
186 EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut)
187 EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut)
188
189 EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear)
190 EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear)
191
192 EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll)
193 EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll)
194
195 EVT_MENU(wxID_RICHTEXT_PROPERTIES1, wxRichTextCtrl::OnProperties)
196 EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES1, wxRichTextCtrl::OnUpdateProperties)
197
198 EVT_MENU(wxID_RICHTEXT_PROPERTIES2, wxRichTextCtrl::OnProperties)
199 EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES2, wxRichTextCtrl::OnUpdateProperties)
200
201 EVT_MENU(wxID_RICHTEXT_PROPERTIES3, wxRichTextCtrl::OnProperties)
202 EVT_UPDATE_UI(wxID_RICHTEXT_PROPERTIES3, wxRichTextCtrl::OnUpdateProperties)
203
204 END_EVENT_TABLE()
205
206 /*!
207 * wxRichTextCtrl
208 */
209
210 wxArrayString wxRichTextCtrl::sm_availableFontNames;
211
212 wxRichTextCtrl::wxRichTextCtrl()
213 : wxScrollHelper(this)
214 {
215 Init();
216 }
217
218 wxRichTextCtrl::wxRichTextCtrl(wxWindow* parent,
219 wxWindowID id,
220 const wxString& value,
221 const wxPoint& pos,
222 const wxSize& size,
223 long style,
224 const wxValidator& validator,
225 const wxString& name)
226 : wxScrollHelper(this)
227 {
228 Init();
229 Create(parent, id, value, pos, size, style, validator, name);
230
231 SetDropTarget(new wxRichTextDropTarget(this));
232 }
233
234 /// Creation
235 bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style,
236 const wxValidator& validator, const wxString& name)
237 {
238 style |= wxVSCROLL;
239
240 if (!wxControl::Create(parent, id, pos, size,
241 style|wxFULL_REPAINT_ON_RESIZE,
242 validator, name))
243 return false;
244
245 if (!GetFont().IsOk())
246 {
247 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
248 }
249
250 // No physical scrolling, so we can preserve margins
251 EnableScrolling(false, false);
252
253 if (style & wxTE_READONLY)
254 SetEditable(false);
255
256 // The base attributes must all have default values
257 wxRichTextAttr attributes;
258 attributes.SetFont(GetFont());
259 attributes.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
260 attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
261 attributes.SetLineSpacing(10);
262 attributes.SetParagraphSpacingAfter(10);
263 attributes.SetParagraphSpacingBefore(0);
264
265 SetBasicStyle(attributes);
266
267 int margin = 5;
268 SetMargins(margin, margin);
269
270 // The default attributes will be merged with base attributes, so
271 // can be empty to begin with
272 wxRichTextAttr defaultAttributes;
273 SetDefaultStyle(defaultAttributes);
274
275 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
276 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
277
278 GetBuffer().Reset();
279 GetBuffer().SetRichTextCtrl(this);
280
281 #if wxRICHTEXT_USE_OWN_CARET
282 SetCaret(new wxRichTextCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
283 #else
284 SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
285 #endif
286
287 // Tell the sizers to use the given or best size
288 SetInitialSize(size);
289
290 #if wxRICHTEXT_BUFFERED_PAINTING
291 // Create a buffer
292 RecreateBuffer(size);
293 #endif
294
295 m_textCursor = wxCursor(wxCURSOR_IBEAM);
296 m_urlCursor = wxCursor(wxCURSOR_HAND);
297
298 SetCursor(m_textCursor);
299
300 if (!value.IsEmpty())
301 SetValue(value);
302
303 GetBuffer().AddEventHandler(this);
304
305 // Accelerators
306 wxAcceleratorEntry entries[6];
307
308 entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
309 entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
310 entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
311 entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
312 entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO);
313 entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO);
314
315 wxAcceleratorTable accel(6, entries);
316 SetAcceleratorTable(accel);
317
318 m_contextMenu = new wxMenu;
319 m_contextMenu->Append(wxID_UNDO, _("&Undo"));
320 m_contextMenu->Append(wxID_REDO, _("&Redo"));
321 m_contextMenu->AppendSeparator();
322 m_contextMenu->Append(wxID_CUT, _("Cu&t"));
323 m_contextMenu->Append(wxID_COPY, _("&Copy"));
324 m_contextMenu->Append(wxID_PASTE, _("&Paste"));
325 m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
326 m_contextMenu->AppendSeparator();
327 m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
328 m_contextMenu->AppendSeparator();
329 m_contextMenu->Append(wxID_RICHTEXT_PROPERTIES1, _("&Properties"));
330
331 return true;
332 }
333
334 wxRichTextCtrl::~wxRichTextCtrl()
335 {
336 SetFocusObject(& GetBuffer(), false);
337 GetBuffer().RemoveEventHandler(this);
338
339 delete m_contextMenu;
340 }
341
342 /// Member initialisation
343 void wxRichTextCtrl::Init()
344 {
345 m_contextMenu = NULL;
346 m_caret = NULL;
347 m_caretPosition = -1;
348 m_selectionAnchor = -2;
349 m_selectionAnchorObject = NULL;
350 m_selectionState = wxRichTextCtrlSelectionState_Normal;
351 m_editable = true;
352 m_caretAtLineStart = false;
353 m_dragging = false;
354 m_preDrag = false;
355 m_fullLayoutRequired = false;
356 m_fullLayoutTime = 0;
357 m_fullLayoutSavedPosition = 0;
358 m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
359 m_caretPositionForDefaultStyle = -2;
360 m_focusObject = & m_buffer;
361 }
362
363 void wxRichTextCtrl::DoThaw()
364 {
365 if (GetBuffer().IsDirty())
366 LayoutContent();
367 else
368 SetupScrollbars();
369
370 wxWindow::DoThaw();
371 }
372
373 /// Clear all text
374 void wxRichTextCtrl::Clear()
375 {
376 if (GetFocusObject() == & GetBuffer())
377 {
378 m_buffer.ResetAndClearCommands();
379 m_buffer.Invalidate(wxRICHTEXT_ALL);
380 }
381 else
382 {
383 GetFocusObject()->Reset();
384 }
385
386 m_caretPosition = -1;
387 m_caretPositionForDefaultStyle = -2;
388 m_caretAtLineStart = false;
389 m_selection.Reset();
390 m_selectionState = wxRichTextCtrlSelectionState_Normal;
391
392 Scroll(0,0);
393
394 if (!IsFrozen())
395 {
396 LayoutContent();
397 Refresh(false);
398 }
399
400 wxTextCtrl::SendTextUpdatedEvent(this);
401 }
402
403 /// Painting
404 void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
405 {
406 #if !wxRICHTEXT_USE_OWN_CARET
407 if (GetCaret() && !IsFrozen())
408 GetCaret()->Hide();
409 #endif
410
411 {
412 #if wxRICHTEXT_BUFFERED_PAINTING
413 wxBufferedPaintDC dc(this, m_bufferBitmap);
414 #else
415 wxPaintDC dc(this);
416 #endif
417
418 if (IsFrozen())
419 return;
420
421 PrepareDC(dc);
422
423 dc.SetFont(GetFont());
424
425 // Paint the background
426 PaintBackground(dc);
427
428 // wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
429
430 wxRect drawingArea(GetUpdateRegion().GetBox());
431 drawingArea.SetPosition(GetLogicalPoint(drawingArea.GetPosition()));
432
433 wxRect availableSpace(GetClientSize());
434 if (GetBuffer().IsDirty())
435 {
436 GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
437 GetBuffer().Invalidate(wxRICHTEXT_NONE);
438 SetupScrollbars();
439 }
440
441 wxRect clipRect(availableSpace);
442 clipRect.x += GetBuffer().GetLeftMargin();
443 clipRect.y += GetBuffer().GetTopMargin();
444 clipRect.width -= (GetBuffer().GetLeftMargin() + GetBuffer().GetRightMargin());
445 clipRect.height -= (GetBuffer().GetTopMargin() + GetBuffer().GetBottomMargin());
446 clipRect.SetPosition(GetLogicalPoint(clipRect.GetPosition()));
447 dc.SetClippingRegion(clipRect);
448
449 int flags = 0;
450 if ((GetExtraStyle() & wxRICHTEXT_EX_NO_GUIDELINES) == 0)
451 flags |= wxRICHTEXT_DRAW_GUIDELINES;
452
453 GetBuffer().Draw(dc, GetBuffer().GetOwnRange(), GetSelection(), drawingArea, 0 /* descent */, flags);
454
455 dc.DestroyClippingRegion();
456
457 // Other user defined painting after everything else (i.e. all text) is painted
458 PaintAboveContent(dc);
459
460 #if wxRICHTEXT_USE_OWN_CARET
461 if (GetCaret()->IsVisible())
462 {
463 ((wxRichTextCaret*) GetCaret())->DoDraw(& dc);
464 }
465 #endif
466 }
467
468 #if !wxRICHTEXT_USE_OWN_CARET
469 if (GetCaret())
470 GetCaret()->Show();
471 PositionCaret();
472 #else
473 #if !defined(__WXMAC__)
474 // Causes caret dropouts on Mac
475 PositionCaret();
476 #endif
477 #endif
478 }
479
480 // Empty implementation, to prevent flicker
481 void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
482 {
483 }
484
485 void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
486 {
487 if (GetCaret())
488 {
489 #if !wxRICHTEXT_USE_OWN_CARET
490 PositionCaret();
491 #endif
492 GetCaret()->Show();
493 }
494
495 #if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
496 // Work around dropouts when control is focused
497 if (!IsFrozen())
498 {
499 Refresh(false);
500 }
501 #endif
502 }
503
504 void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
505 {
506 if (GetCaret())
507 GetCaret()->Hide();
508
509 #if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET
510 // Work around dropouts when control is focused
511 if (!IsFrozen())
512 {
513 Refresh(false);
514 }
515 #endif
516 }
517
518 void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
519 {
520 m_dragging = false;
521 }
522
523 // Set up the caret for the given position and container, after a mouse click
524 bool wxRichTextCtrl::SetCaretPositionAfterClick(wxRichTextParagraphLayoutBox* container, long position, int hitTestFlags, bool extendSelection)
525 {
526 bool caretAtLineStart = false;
527
528 if (hitTestFlags & wxRICHTEXT_HITTEST_BEFORE)
529 {
530 // If we're at the start of a line (but not first in para)
531 // then we should keep the caret showing at the start of the line
532 // by showing the m_caretAtLineStart flag.
533 wxRichTextParagraph* para = container->GetParagraphAtPosition(position);
534 wxRichTextLine* line = container->GetLineAtPosition(position);
535
536 if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
537 caretAtLineStart = true;
538 position --;
539 }
540
541 if (extendSelection && (m_caretPosition != position))
542 ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
543
544 MoveCaret(position, caretAtLineStart);
545 SetDefaultStyleToCursorStyle();
546
547 return true;
548 }
549
550 /// Left-click
551 void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
552 {
553 SetFocus();
554
555 wxClientDC dc(this);
556 PrepareDC(dc);
557 dc.SetFont(GetFont());
558
559 // TODO: detect change of focus object
560 long position = 0;
561 wxRichTextObject* hitObj = NULL;
562 wxRichTextObject* contextObj = NULL;
563 int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position, & hitObj, & contextObj);
564
565 #if wxUSE_DRAG_AND_DROP
566 // If there's no selection, or we're not inside it, this isn't an attempt to initiate Drag'n'Drop
567 if (IsEditable() && HasSelection() && GetSelectionRange().ToInternal().Contains(position))
568 {
569 // This might be an attempt at initiating Drag'n'Drop. So set the time & flags
570 m_preDrag = true;
571 m_dragStartPoint = event.GetPosition(); // No need to worry about logical positions etc, we only care about the distance from the original pt
572
573 #if wxUSE_DATETIME
574 m_dragStartTime = wxDateTime::UNow();
575 #endif // wxUSE_DATETIME
576
577 // Preserve behaviour of clicking on an object within the selection
578 if (hit != wxRICHTEXT_HITTEST_NONE && hitObj)
579 m_dragging = true;
580
581 return; // Don't skip the event, else the selection will be lost
582 }
583 #endif // wxUSE_DRAG_AND_DROP
584
585 if (hit != wxRICHTEXT_HITTEST_NONE && hitObj)
586 {
587 wxRichTextParagraphLayoutBox* oldFocusObject = GetFocusObject();
588 wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
589 if (container && container != GetFocusObject() && container->AcceptsFocus())
590 {
591 SetFocusObject(container, false /* don't set caret position yet */);
592 }
593
594 m_dragging = true;
595 CaptureMouse();
596
597 long oldCaretPos = m_caretPosition;
598
599 SetCaretPositionAfterClick(container, position, hit);
600
601 // For now, don't handle shift-click when we're selecting multiple objects.
602 if (event.ShiftDown() && GetFocusObject() == oldFocusObject && m_selectionState == wxRichTextCtrlSelectionState_Normal)
603 ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
604 else
605 SelectNone();
606 }
607
608 event.Skip();
609 }
610
611 /// Left-up
612 void wxRichTextCtrl::OnLeftUp(wxMouseEvent& event)
613 {
614 if (m_dragging)
615 {
616 m_dragging = false;
617 if (GetCapture() == this)
618 ReleaseMouse();
619
620 // See if we clicked on a URL
621 wxClientDC dc(this);
622 PrepareDC(dc);
623 dc.SetFont(GetFont());
624
625 long position = 0;
626 wxPoint logicalPt = event.GetLogicalPosition(dc);
627 wxRichTextObject* hitObj = NULL;
628 wxRichTextObject* contextObj = NULL;
629 // Only get objects at this level, not nested, because otherwise we couldn't swipe text at a single level.
630 int hit = GetFocusObject()->HitTest(dc, logicalPt, position, & hitObj, & contextObj, wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS);
631
632 #if wxUSE_DRAG_AND_DROP
633 if (m_preDrag)
634 {
635 // Preserve the behaviour that would have happened without drag-and-drop detection, in OnLeftClick
636 m_preDrag = false; // Tell DnD not to happen now: we are processing Left Up ourselves.
637
638 // Do the actions that would have been done in OnLeftClick if we hadn't tried to drag
639 long position = 0;
640 wxRichTextObject* hitObj = NULL;
641 wxRichTextObject* contextObj = NULL;
642 int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position, & hitObj, & contextObj);
643 wxRichTextParagraphLayoutBox* oldFocusObject = GetFocusObject();
644 wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
645 if (container && container != GetFocusObject() && container->AcceptsFocus())
646 {
647 SetFocusObject(container, false /* don't set caret position yet */);
648 }
649
650 long oldCaretPos = m_caretPosition;
651
652 SetCaretPositionAfterClick(container, position, hit);
653
654 // For now, don't handle shift-click when we're selecting multiple objects.
655 if (event.ShiftDown() && GetFocusObject() == oldFocusObject && m_selectionState == wxRichTextCtrlSelectionState_Normal)
656 ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
657 else
658 SelectNone();
659 }
660 #endif
661
662 if ((hit != wxRICHTEXT_HITTEST_NONE) && !(hit & wxRICHTEXT_HITTEST_OUTSIDE))
663 {
664 wxRichTextEvent cmdEvent(
665 wxEVT_COMMAND_RICHTEXT_LEFT_CLICK,
666 GetId());
667 cmdEvent.SetEventObject(this);
668 cmdEvent.SetPosition(position);
669 if (hitObj)
670 cmdEvent.SetContainer(hitObj->GetContainer());
671
672 if (!GetEventHandler()->ProcessEvent(cmdEvent))
673 {
674 wxRichTextAttr attr;
675 if (GetStyle(position, attr))
676 {
677 if (attr.HasFlag(wxTEXT_ATTR_URL))
678 {
679 wxString urlTarget = attr.GetURL();
680 if (!urlTarget.IsEmpty())
681 {
682 wxMouseEvent mouseEvent(event);
683
684 long startPos = 0, endPos = 0;
685 wxRichTextObject* obj = GetFocusObject()->GetLeafObjectAtPosition(position);
686 if (obj)
687 {
688 startPos = obj->GetRange().GetStart();
689 endPos = obj->GetRange().GetEnd();
690 }
691
692 wxTextUrlEvent urlEvent(GetId(), mouseEvent, startPos, endPos);
693 InitCommandEvent(urlEvent);
694
695 urlEvent.SetString(urlTarget);
696
697 GetEventHandler()->ProcessEvent(urlEvent);
698 }
699 }
700 }
701 }
702 }
703 }
704
705 #if wxUSE_DRAG_AND_DROP
706 m_preDrag = false;
707 #endif // wxUSE_DRAG_AND_DROP
708
709 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ && wxHAVE_PRIMARY_SELECTION
710 if (HasSelection() && GetFocusObject() && GetFocusObject()->GetBuffer())
711 {
712 // Put the selection in PRIMARY, if it exists
713 wxTheClipboard->UsePrimarySelection(true);
714
715 wxRichTextRange range = GetInternalSelectionRange();
716 GetFocusObject()->GetBuffer()->CopyToClipboard(range);
717
718 wxTheClipboard->UsePrimarySelection(false);
719 }
720 #endif
721 }
722
723 /// Mouse-movements
724 void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
725 {
726 #if wxUSE_DRAG_AND_DROP
727 // See if we're starting Drag'n'Drop
728 if (m_preDrag)
729 {
730 int x = m_dragStartPoint.x - event.GetPosition().x;
731 int y = m_dragStartPoint.y - event.GetPosition().y;
732 size_t distance = abs(x) + abs(y);
733 #if wxUSE_DATETIME
734 wxTimeSpan diff = wxDateTime::UNow() - m_dragStartTime;
735 #endif
736 if ((distance > 10)
737 #if wxUSE_DATETIME
738 && (diff.GetMilliseconds() > 100)
739 #endif
740 )
741 {
742 m_dragging = false;
743
744 // Start drag'n'drop
745 wxRichTextRange range = GetInternalSelectionRange();
746 if (range == wxRICHTEXT_NONE)
747 {
748 // Don't try to drag an empty range
749 m_preDrag = false;
750 return;
751 }
752
753 // Cache the current situation, to be restored if Drag'n'Drop is cancelled
754 long oldPos = GetCaretPosition();
755 wxRichTextParagraphLayoutBox* oldFocus = GetFocusObject();
756
757 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
758 wxString text = GetFocusObject()->GetTextForRange(range);
759 #ifdef __WXMSW__
760 text = wxTextFile::Translate(text, wxTextFileType_Dos);
761 #endif
762 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
763
764 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
765 GetFocusObject()->CopyFragment(range, *richTextBuf);
766 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
767
768 wxRichTextDropSource source(*compositeObject, this);
769 // Use wxDrag_DefaultMove, not because it's the likelier choice but because pressing Ctrl for Copy obeys the principle of least surprise
770 // The alternative, wxDrag_DefaultCopy, requires the user to know that Move needs the Shift key pressed
771 BeginBatchUndo(_("Drag"));
772 switch (source.DoDragDrop(wxDrag_AllowMove | wxDrag_DefaultMove))
773 {
774 case wxDragMove:
775 case wxDragCopy: break;
776
777 case wxDragError:
778 wxLogError(wxT("An error occurred during drag and drop operation"));
779 case wxDragNone:
780 case wxDragCancel:
781 Refresh(); // This is needed in wxMSW, otherwise resetting the position doesn't 'take'
782 SetCaretPosition(oldPos);
783 SetFocusObject(oldFocus, false);
784 default: break;
785 }
786 EndBatchUndo();
787
788 m_preDrag = false;
789 return;
790 }
791 }
792 #endif // wxUSE_DRAG_AND_DROP
793
794 wxClientDC dc(this);
795 PrepareDC(dc);
796 dc.SetFont(GetFont());
797
798 long position = 0;
799 wxPoint logicalPt = event.GetLogicalPosition(dc);
800 wxRichTextObject* hitObj = NULL;
801 wxRichTextObject* contextObj = NULL;
802
803 int flags = 0;
804
805 // If we're dragging, let's only consider positions at this level; otherwise
806 // selecting a range is not going to work.
807 wxRichTextParagraphLayoutBox* container = & GetBuffer();
808 if (m_dragging)
809 {
810 flags = wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS;
811 container = GetFocusObject();
812 }
813 int hit = container->HitTest(dc, logicalPt, position, & hitObj, & contextObj, flags);
814
815 // See if we need to change the cursor
816
817 {
818 if (hit != wxRICHTEXT_HITTEST_NONE && !(hit & wxRICHTEXT_HITTEST_OUTSIDE) && hitObj)
819 {
820 wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
821 wxRichTextAttr attr;
822 if (actualContainer && GetStyle(position, attr, actualContainer))
823 {
824 if (attr.HasFlag(wxTEXT_ATTR_URL))
825 {
826 SetCursor(m_urlCursor);
827 }
828 else if (!attr.HasFlag(wxTEXT_ATTR_URL))
829 {
830 SetCursor(m_textCursor);
831 }
832 }
833 }
834 else
835 SetCursor(m_textCursor);
836 }
837
838 if (!event.Dragging())
839 {
840 event.Skip();
841 return;
842 }
843
844 if (m_dragging
845 #if wxUSE_DRAG_AND_DROP
846 && !m_preDrag
847 #endif
848 )
849 {
850 wxRichTextParagraphLayoutBox* commonAncestor = NULL;
851 wxRichTextParagraphLayoutBox* otherContainer = NULL;
852 wxRichTextParagraphLayoutBox* firstContainer = NULL;
853
854 // Check for dragging across multiple containers
855 long position2 = 0;
856 wxRichTextObject* hitObj2 = NULL, *contextObj2 = NULL;
857 int hit2 = GetBuffer().HitTest(dc, logicalPt, position2, & hitObj2, & contextObj2, 0);
858 if (hit2 != wxRICHTEXT_HITTEST_NONE && !(hit2 & wxRICHTEXT_HITTEST_OUTSIDE) && hitObj2 && hitObj != hitObj2)
859 {
860 // See if we can find a common ancestor
861 if (m_selectionState == wxRichTextCtrlSelectionState_Normal)
862 {
863 firstContainer = GetFocusObject();
864 commonAncestor = wxDynamicCast(firstContainer->GetParent(), wxRichTextParagraphLayoutBox);
865 }
866 else
867 {
868 firstContainer = wxDynamicCast(m_selectionAnchorObject, wxRichTextParagraphLayoutBox);
869 //commonAncestor = GetFocusObject(); // when the selection state is not normal, the focus object (e.g. table)
870 // is the common ancestor.
871 commonAncestor = wxDynamicCast(firstContainer->GetParent(), wxRichTextParagraphLayoutBox);
872 }
873
874 if (commonAncestor && commonAncestor->HandlesChildSelections())
875 {
876 wxRichTextObject* p = hitObj2;
877 while (p)
878 {
879 if (p->GetParent() == commonAncestor)
880 {
881 otherContainer = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
882 break;
883 }
884 p = p->GetParent();
885 }
886 }
887
888 if (commonAncestor && firstContainer && otherContainer)
889 {
890 // We have now got a second container that shares a parent with the current or anchor object.
891 if (m_selectionState == wxRichTextCtrlSelectionState_Normal)
892 {
893 // Don't go into common-ancestor selection mode if we still have the same
894 // container.
895 if (otherContainer != firstContainer)
896 {
897 m_selectionState = wxRichTextCtrlSelectionState_CommonAncestor;
898 m_selectionAnchorObject = firstContainer;
899 m_selectionAnchor = firstContainer->GetRange().GetStart();
900
901 // The common ancestor, such as a table, returns the cell selection
902 // between the anchor and current position.
903 m_selection = commonAncestor->GetSelection(m_selectionAnchor, otherContainer->GetRange().GetStart());
904 }
905 }
906 else
907 {
908 m_selection = commonAncestor->GetSelection(m_selectionAnchor, otherContainer->GetRange().GetStart());
909 }
910
911 Refresh();
912
913 if (otherContainer->AcceptsFocus())
914 SetFocusObject(otherContainer, false /* don't set caret and clear selection */);
915 MoveCaret(-1, false);
916 SetDefaultStyleToCursorStyle();
917 }
918 }
919 }
920
921 if (hitObj && m_dragging && hit != wxRICHTEXT_HITTEST_NONE && m_selectionState == wxRichTextCtrlSelectionState_Normal
922 #if wxUSE_DRAG_AND_DROP
923 && !m_preDrag
924 #endif
925 )
926 {
927 // TODO: test closeness
928 SetCaretPositionAfterClick(container, position, hit, true /* extend selection */);
929 }
930 }
931
932 /// Right-click
933 void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
934 {
935 SetFocus();
936
937 wxClientDC dc(this);
938 PrepareDC(dc);
939 dc.SetFont(GetFont());
940
941 long position = 0;
942 wxPoint logicalPt = event.GetLogicalPosition(dc);
943 wxRichTextObject* hitObj = NULL;
944 wxRichTextObject* contextObj = NULL;
945 int hit = GetFocusObject()->HitTest(dc, logicalPt, position, & hitObj, & contextObj);
946
947 if (hitObj && hitObj->GetContainer() != GetFocusObject())
948 {
949 wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
950 if (actualContainer && actualContainer->AcceptsFocus())
951 {
952 SetFocusObject(actualContainer, false /* don't set caret position yet */);
953 SetCaretPositionAfterClick(actualContainer, position, hit);
954 }
955 }
956
957 wxRichTextEvent cmdEvent(
958 wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK,
959 GetId());
960 cmdEvent.SetEventObject(this);
961 cmdEvent.SetPosition(position);
962 if (hitObj)
963 cmdEvent.SetContainer(hitObj->GetContainer());
964
965 if (!GetEventHandler()->ProcessEvent(cmdEvent))
966 event.Skip();
967 }
968
969 /// Left-double-click
970 void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& WXUNUSED(event))
971 {
972 wxRichTextEvent cmdEvent(
973 wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK,
974 GetId());
975 cmdEvent.SetEventObject(this);
976 cmdEvent.SetPosition(m_caretPosition+1);
977 cmdEvent.SetContainer(GetFocusObject());
978
979 if (!GetEventHandler()->ProcessEvent(cmdEvent))
980 {
981 SelectWord(GetCaretPosition()+1);
982 }
983 }
984
985 /// Middle-click
986 void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
987 {
988 wxRichTextEvent cmdEvent(
989 wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK,
990 GetId());
991 cmdEvent.SetEventObject(this);
992 cmdEvent.SetPosition(m_caretPosition+1);
993 cmdEvent.SetContainer(GetFocusObject());
994
995 if (!GetEventHandler()->ProcessEvent(cmdEvent))
996 event.Skip();
997
998 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ && wxHAVE_PRIMARY_SELECTION
999 // Paste any PRIMARY selection, if it exists
1000 wxTheClipboard->UsePrimarySelection(true);
1001 Paste();
1002 wxTheClipboard->UsePrimarySelection(false);
1003 #endif
1004 }
1005
1006 /// Key press
1007 void wxRichTextCtrl::OnChar(wxKeyEvent& event)
1008 {
1009 int flags = 0;
1010 if (event.CmdDown())
1011 flags |= wxRICHTEXT_CTRL_DOWN;
1012 if (event.ShiftDown())
1013 flags |= wxRICHTEXT_SHIFT_DOWN;
1014 if (event.AltDown())
1015 flags |= wxRICHTEXT_ALT_DOWN;
1016
1017 if (event.GetEventType() == wxEVT_KEY_DOWN)
1018 {
1019 if (event.IsKeyInCategory(WXK_CATEGORY_NAVIGATION))
1020 {
1021 KeyboardNavigate(event.GetKeyCode(), flags);
1022 return;
1023 }
1024
1025 long keycode = event.GetKeyCode();
1026 switch ( keycode )
1027 {
1028 case WXK_ESCAPE:
1029 case WXK_START:
1030 case WXK_LBUTTON:
1031 case WXK_RBUTTON:
1032 case WXK_CANCEL:
1033 case WXK_MBUTTON:
1034 case WXK_CLEAR:
1035 case WXK_SHIFT:
1036 case WXK_ALT:
1037 case WXK_CONTROL:
1038 case WXK_MENU:
1039 case WXK_PAUSE:
1040 case WXK_CAPITAL:
1041 case WXK_END:
1042 case WXK_HOME:
1043 case WXK_LEFT:
1044 case WXK_UP:
1045 case WXK_RIGHT:
1046 case WXK_DOWN:
1047 case WXK_SELECT:
1048 case WXK_PRINT:
1049 case WXK_EXECUTE:
1050 case WXK_SNAPSHOT:
1051 case WXK_INSERT:
1052 case WXK_HELP:
1053 case WXK_F1:
1054 case WXK_F2:
1055 case WXK_F3:
1056 case WXK_F4:
1057 case WXK_F5:
1058 case WXK_F6:
1059 case WXK_F7:
1060 case WXK_F8:
1061 case WXK_F9:
1062 case WXK_F10:
1063 case WXK_F11:
1064 case WXK_F12:
1065 case WXK_F13:
1066 case WXK_F14:
1067 case WXK_F15:
1068 case WXK_F16:
1069 case WXK_F17:
1070 case WXK_F18:
1071 case WXK_F19:
1072 case WXK_F20:
1073 case WXK_F21:
1074 case WXK_F22:
1075 case WXK_F23:
1076 case WXK_F24:
1077 case WXK_NUMLOCK:
1078 case WXK_SCROLL:
1079 case WXK_PAGEUP:
1080 case WXK_PAGEDOWN:
1081 case WXK_NUMPAD_F1:
1082 case WXK_NUMPAD_F2:
1083 case WXK_NUMPAD_F3:
1084 case WXK_NUMPAD_F4:
1085 case WXK_NUMPAD_HOME:
1086 case WXK_NUMPAD_LEFT:
1087 case WXK_NUMPAD_UP:
1088 case WXK_NUMPAD_RIGHT:
1089 case WXK_NUMPAD_DOWN:
1090 case WXK_NUMPAD_PAGEUP:
1091 case WXK_NUMPAD_PAGEDOWN:
1092 case WXK_NUMPAD_END:
1093 case WXK_NUMPAD_BEGIN:
1094 case WXK_NUMPAD_INSERT:
1095 case WXK_WINDOWS_LEFT:
1096 {
1097 return;
1098 }
1099 default:
1100 {
1101 }
1102 }
1103
1104 // Must process this before translation, otherwise it's translated into a WXK_DELETE event.
1105 if (event.CmdDown() && event.GetKeyCode() == WXK_BACK)
1106 {
1107 BeginBatchUndo(_("Delete Text"));
1108
1109 long newPos = m_caretPosition;
1110
1111 bool processed = DeleteSelectedContent(& newPos);
1112
1113 // Submit range in character positions, which are greater than caret positions,
1114 // so subtract 1 for deleted character and add 1 for conversion to character position.
1115 if (newPos > -1)
1116 {
1117 if (event.CmdDown())
1118 {
1119 long pos = wxRichTextCtrl::FindNextWordPosition(-1);
1120 if (pos < newPos)
1121 {
1122 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this, & GetBuffer());
1123 processed = true;
1124 }
1125 }
1126
1127 if (!processed)
1128 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this, & GetBuffer());
1129 }
1130
1131 EndBatchUndo();
1132
1133 if (GetLastPosition() == -1)
1134 {
1135 GetFocusObject()->Reset();
1136
1137 m_caretPosition = -1;
1138 PositionCaret();
1139 SetDefaultStyleToCursorStyle();
1140 }
1141
1142 ScrollIntoView(m_caretPosition, WXK_LEFT);
1143
1144 wxRichTextEvent cmdEvent(
1145 wxEVT_COMMAND_RICHTEXT_DELETE,
1146 GetId());
1147 cmdEvent.SetEventObject(this);
1148 cmdEvent.SetFlags(flags);
1149 cmdEvent.SetPosition(m_caretPosition+1);
1150 cmdEvent.SetContainer(GetFocusObject());
1151 GetEventHandler()->ProcessEvent(cmdEvent);
1152
1153 Update();
1154 }
1155 else
1156 event.Skip();
1157
1158 return;
1159 }
1160
1161 // all the other keys modify the controls contents which shouldn't be
1162 // possible if we're read-only
1163 if ( !IsEditable() )
1164 {
1165 event.Skip();
1166 return;
1167 }
1168
1169 if (event.GetKeyCode() == WXK_RETURN)
1170 {
1171 BeginBatchUndo(_("Insert Text"));
1172
1173 long newPos = m_caretPosition;
1174
1175 DeleteSelectedContent(& newPos);
1176
1177 if (event.ShiftDown())
1178 {
1179 wxString text;
1180 text = wxRichTextLineBreakChar;
1181 GetFocusObject()->InsertTextWithUndo(newPos+1, text, this, & GetBuffer());
1182 m_caretAtLineStart = true;
1183 PositionCaret();
1184 }
1185 else
1186 GetFocusObject()->InsertNewlineWithUndo(newPos+1, this, & GetBuffer(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE|wxRICHTEXT_INSERT_INTERACTIVE);
1187
1188 EndBatchUndo();
1189 SetDefaultStyleToCursorStyle();
1190
1191 ScrollIntoView(m_caretPosition, WXK_RIGHT);
1192
1193 wxRichTextEvent cmdEvent(
1194 wxEVT_COMMAND_RICHTEXT_RETURN,
1195 GetId());
1196 cmdEvent.SetEventObject(this);
1197 cmdEvent.SetFlags(flags);
1198 cmdEvent.SetPosition(newPos+1);
1199 cmdEvent.SetContainer(GetFocusObject());
1200
1201 if (!GetEventHandler()->ProcessEvent(cmdEvent))
1202 {
1203 // Generate conventional event
1204 wxCommandEvent textEvent(wxEVT_COMMAND_TEXT_ENTER, GetId());
1205 InitCommandEvent(textEvent);
1206
1207 GetEventHandler()->ProcessEvent(textEvent);
1208 }
1209 Update();
1210 }
1211 else if (event.GetKeyCode() == WXK_BACK)
1212 {
1213 BeginBatchUndo(_("Delete Text"));
1214
1215 long newPos = m_caretPosition;
1216
1217 bool processed = DeleteSelectedContent(& newPos);
1218
1219 // Submit range in character positions, which are greater than caret positions,
1220 // so subtract 1 for deleted character and add 1 for conversion to character position.
1221 if (newPos > -1)
1222 {
1223 if (event.CmdDown())
1224 {
1225 long pos = wxRichTextCtrl::FindNextWordPosition(-1);
1226 if (pos < newPos)
1227 {
1228 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this, & GetBuffer());
1229 processed = true;
1230 }
1231 }
1232
1233 if (!processed)
1234 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this, & GetBuffer());
1235 }
1236
1237 EndBatchUndo();
1238
1239 if (GetLastPosition() == -1)
1240 {
1241 GetFocusObject()->Reset();
1242
1243 m_caretPosition = -1;
1244 PositionCaret();
1245 SetDefaultStyleToCursorStyle();
1246 }
1247
1248 ScrollIntoView(m_caretPosition, WXK_LEFT);
1249
1250 wxRichTextEvent cmdEvent(
1251 wxEVT_COMMAND_RICHTEXT_DELETE,
1252 GetId());
1253 cmdEvent.SetEventObject(this);
1254 cmdEvent.SetFlags(flags);
1255 cmdEvent.SetPosition(m_caretPosition+1);
1256 cmdEvent.SetContainer(GetFocusObject());
1257 GetEventHandler()->ProcessEvent(cmdEvent);
1258
1259 Update();
1260 }
1261 else if (event.GetKeyCode() == WXK_DELETE)
1262 {
1263 BeginBatchUndo(_("Delete Text"));
1264
1265 long newPos = m_caretPosition;
1266
1267 bool processed = DeleteSelectedContent(& newPos);
1268
1269 // Submit range in character positions, which are greater than caret positions,
1270 if (newPos < GetFocusObject()->GetOwnRange().GetEnd()+1)
1271 {
1272 if (event.CmdDown())
1273 {
1274 long pos = wxRichTextCtrl::FindNextWordPosition(1);
1275 if (pos != -1 && (pos > newPos))
1276 {
1277 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos+1, pos), this, & GetBuffer());
1278 processed = true;
1279 }
1280 }
1281
1282 if (!processed && newPos < (GetLastPosition()-1))
1283 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos+1, newPos+1), this, & GetBuffer());
1284 }
1285
1286 EndBatchUndo();
1287
1288 if (GetLastPosition() == -1)
1289 {
1290 GetFocusObject()->Reset();
1291
1292 m_caretPosition = -1;
1293 PositionCaret();
1294 SetDefaultStyleToCursorStyle();
1295 }
1296
1297 ScrollIntoView(m_caretPosition, WXK_LEFT);
1298
1299 wxRichTextEvent cmdEvent(
1300 wxEVT_COMMAND_RICHTEXT_DELETE,
1301 GetId());
1302 cmdEvent.SetEventObject(this);
1303 cmdEvent.SetFlags(flags);
1304 cmdEvent.SetPosition(m_caretPosition+1);
1305 cmdEvent.SetContainer(GetFocusObject());
1306 GetEventHandler()->ProcessEvent(cmdEvent);
1307
1308 Update();
1309 }
1310 else
1311 {
1312 long keycode = event.GetKeyCode();
1313 switch ( keycode )
1314 {
1315 case WXK_ESCAPE:
1316 {
1317 event.Skip();
1318 return;
1319 }
1320
1321 default:
1322 {
1323 #ifdef __WXMAC__
1324 if (event.CmdDown())
1325 #else
1326 // Fixes AltGr+key with European input languages on Windows
1327 if ((event.CmdDown() && !event.AltDown()) || (event.AltDown() && !event.CmdDown()))
1328 #endif
1329 {
1330 event.Skip();
1331 return;
1332 }
1333
1334 wxRichTextEvent cmdEvent(
1335 wxEVT_COMMAND_RICHTEXT_CHARACTER,
1336 GetId());
1337 cmdEvent.SetEventObject(this);
1338 cmdEvent.SetFlags(flags);
1339 #if wxUSE_UNICODE
1340 cmdEvent.SetCharacter(event.GetUnicodeKey());
1341 #else
1342 cmdEvent.SetCharacter((wxChar) keycode);
1343 #endif
1344 cmdEvent.SetPosition(m_caretPosition+1);
1345 cmdEvent.SetContainer(GetFocusObject());
1346
1347 if (keycode == wxT('\t'))
1348 {
1349 // See if we need to promote or demote the selection or paragraph at the cursor
1350 // position, instead of inserting a tab.
1351 long pos = GetAdjustedCaretPosition(GetCaretPosition());
1352 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos);
1353 if (para && para->GetRange().GetStart() == pos && para->GetAttributes().HasListStyleName())
1354 {
1355 wxRichTextRange range;
1356 if (HasSelection())
1357 range = GetSelectionRange();
1358 else
1359 range = para->GetRange().FromInternal();
1360
1361 int promoteBy = event.ShiftDown() ? 1 : -1;
1362
1363 PromoteList(promoteBy, range, NULL);
1364
1365 GetEventHandler()->ProcessEvent(cmdEvent);
1366
1367 return;
1368 }
1369 }
1370
1371 BeginBatchUndo(_("Insert Text"));
1372
1373 long newPos = m_caretPosition;
1374 DeleteSelectedContent(& newPos);
1375
1376 #if wxUSE_UNICODE
1377 wxString str = event.GetUnicodeKey();
1378 #else
1379 wxString str = (wxChar) event.GetKeyCode();
1380 #endif
1381 GetFocusObject()->InsertTextWithUndo(newPos+1, str, this, & GetBuffer(), 0);
1382
1383 EndBatchUndo();
1384
1385 SetDefaultStyleToCursorStyle();
1386 ScrollIntoView(m_caretPosition, WXK_RIGHT);
1387
1388 cmdEvent.SetPosition(m_caretPosition);
1389 GetEventHandler()->ProcessEvent(cmdEvent);
1390
1391 Update();
1392 }
1393 }
1394 }
1395 }
1396
1397 /// Delete content if there is a selection, e.g. when pressing a key.
1398 bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
1399 {
1400 if (HasSelection())
1401 {
1402 long pos = m_selection.GetRange().GetStart();
1403 wxRichTextRange range = m_selection.GetRange();
1404
1405 // SelectAll causes more to be selected than doing it interactively,
1406 // and causes a new paragraph to be inserted. So for multiline buffers,
1407 // don't delete the final position.
1408 if (range.GetEnd() == GetLastPosition() && GetNumberOfLines() > 0)
1409 range.SetEnd(range.GetEnd()-1);
1410
1411 GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
1412 m_selection.Reset();
1413 m_selectionState = wxRichTextCtrlSelectionState_Normal;
1414
1415 if (newPos)
1416 *newPos = pos-1;
1417 return true;
1418 }
1419 else
1420 return false;
1421 }
1422
1423 /// Keyboard navigation
1424
1425 /*
1426
1427 Left: left one character
1428 Right: right one character
1429 Up: up one line
1430 Down: down one line
1431 Ctrl-Left: left one word
1432 Ctrl-Right: right one word
1433 Ctrl-Up: previous paragraph start
1434 Ctrl-Down: next start of paragraph
1435 Home: start of line
1436 End: end of line
1437 Ctrl-Home: start of document
1438 Ctrl-End: end of document
1439 Page-Up: Up a screen
1440 Page-Down: Down a screen
1441
1442 Maybe:
1443
1444 Ctrl-Alt-PgUp: Start of window
1445 Ctrl-Alt-PgDn: End of window
1446 F8: Start selection mode
1447 Esc: End selection mode
1448
1449 Adding Shift does the above but starts/extends selection.
1450
1451
1452 */
1453
1454 bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags)
1455 {
1456 bool success = false;
1457
1458 if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT)
1459 {
1460 if (flags & wxRICHTEXT_CTRL_DOWN)
1461 success = WordRight(1, flags);
1462 else
1463 success = MoveRight(1, flags);
1464 }
1465 else if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT)
1466 {
1467 if (flags & wxRICHTEXT_CTRL_DOWN)
1468 success = WordLeft(1, flags);
1469 else
1470 success = MoveLeft(1, flags);
1471 }
1472 else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP)
1473 {
1474 if (flags & wxRICHTEXT_CTRL_DOWN)
1475 success = MoveToParagraphStart(flags);
1476 else
1477 success = MoveUp(1, flags);
1478 }
1479 else if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN)
1480 {
1481 if (flags & wxRICHTEXT_CTRL_DOWN)
1482 success = MoveToParagraphEnd(flags);
1483 else
1484 success = MoveDown(1, flags);
1485 }
1486 else if (keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP)
1487 {
1488 success = PageUp(1, flags);
1489 }
1490 else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1491 {
1492 success = PageDown(1, flags);
1493 }
1494 else if (keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME)
1495 {
1496 if (flags & wxRICHTEXT_CTRL_DOWN)
1497 success = MoveHome(flags);
1498 else
1499 success = MoveToLineStart(flags);
1500 }
1501 else if (keyCode == WXK_END || keyCode == WXK_NUMPAD_END)
1502 {
1503 if (flags & wxRICHTEXT_CTRL_DOWN)
1504 success = MoveEnd(flags);
1505 else
1506 success = MoveToLineEnd(flags);
1507 }
1508
1509 if (success)
1510 {
1511 ScrollIntoView(m_caretPosition, keyCode);
1512 SetDefaultStyleToCursorStyle();
1513 }
1514
1515 return success;
1516 }
1517
1518 /// Extend the selection. Selections are in caret positions.
1519 bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
1520 {
1521 if (flags & wxRICHTEXT_SHIFT_DOWN)
1522 {
1523 if (oldPos == newPos)
1524 return false;
1525
1526 wxRichTextSelection oldSelection = m_selection;
1527
1528 m_selection.SetContainer(GetFocusObject());
1529
1530 wxRichTextRange oldRange;
1531 if (m_selection.IsValid())
1532 oldRange = m_selection.GetRange();
1533 else
1534 oldRange = wxRICHTEXT_NO_SELECTION;
1535 wxRichTextRange newRange;
1536
1537 // If not currently selecting, start selecting
1538 if (oldRange.GetStart() == -2)
1539 {
1540 m_selectionAnchor = oldPos;
1541
1542 if (oldPos > newPos)
1543 newRange.SetRange(newPos+1, oldPos);
1544 else
1545 newRange.SetRange(oldPos+1, newPos);
1546 }
1547 else
1548 {
1549 // Always ensure that the selection range start is greater than
1550 // the end.
1551 if (newPos > m_selectionAnchor)
1552 newRange.SetRange(m_selectionAnchor+1, newPos);
1553 else if (newPos == m_selectionAnchor)
1554 newRange = wxRichTextRange(-2, -2);
1555 else
1556 newRange.SetRange(newPos+1, m_selectionAnchor);
1557 }
1558
1559 m_selection.SetRange(newRange);
1560
1561 RefreshForSelectionChange(oldSelection, m_selection);
1562
1563 if (newRange.GetStart() > newRange.GetEnd())
1564 {
1565 wxLogDebug(wxT("Strange selection range"));
1566 }
1567
1568 return true;
1569 }
1570 else
1571 return false;
1572 }
1573
1574 /// Scroll into view, returning true if we scrolled.
1575 /// This takes a _caret_ position.
1576 bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
1577 {
1578 wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
1579
1580 if (!line)
1581 return false;
1582
1583 int ppuX, ppuY;
1584 GetScrollPixelsPerUnit(& ppuX, & ppuY);
1585
1586 int startXUnits, startYUnits;
1587 GetViewStart(& startXUnits, & startYUnits);
1588 int startY = startYUnits * ppuY;
1589
1590 int sx = 0, sy = 0;
1591 GetVirtualSize(& sx, & sy);
1592 int sxUnits = 0;
1593 int syUnits = 0;
1594 if (ppuY != 0)
1595 syUnits = sy/ppuY;
1596
1597 wxRect rect = line->GetRect();
1598
1599 bool scrolled = false;
1600
1601 wxSize clientSize = GetClientSize();
1602
1603 int leftMargin, rightMargin, topMargin, bottomMargin;
1604
1605 {
1606 wxClientDC dc(this);
1607 wxRichTextObject::GetTotalMargin(dc, & GetBuffer(), GetBuffer().GetAttributes(), leftMargin, rightMargin,
1608 topMargin, bottomMargin);
1609 }
1610 // clientSize.y -= GetBuffer().GetBottomMargin();
1611 clientSize.y -= bottomMargin;
1612
1613 if (GetWindowStyle() & wxRE_CENTRE_CARET)
1614 {
1615 int y = rect.y - GetClientSize().y/2;
1616 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1617 if (y >= 0 && (y + clientSize.y) < GetBuffer().GetCachedSize().y)
1618 {
1619 if (startYUnits != yUnits)
1620 {
1621 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1622 scrolled = true;
1623 }
1624 #if !wxRICHTEXT_USE_OWN_CARET
1625 if (scrolled)
1626 #endif
1627 PositionCaret();
1628
1629 return scrolled;
1630 }
1631 }
1632
1633 // Going down
1634 if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN ||
1635 keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT ||
1636 keyCode == WXK_END || keyCode == WXK_NUMPAD_END ||
1637 keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
1638 {
1639 if ((rect.y + rect.height) > (clientSize.y + startY))
1640 {
1641 // Make it scroll so this item is at the bottom
1642 // of the window
1643 int y = rect.y - (clientSize.y - rect.height);
1644 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1645
1646 // If we're still off the screen, scroll another line down
1647 if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1648 yUnits ++;
1649
1650 if (startYUnits != yUnits)
1651 {
1652 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1653 scrolled = true;
1654 }
1655 }
1656 else if (rect.y < (startY + GetBuffer().GetTopMargin()))
1657 {
1658 // Make it scroll so this item is at the top
1659 // of the window
1660 int y = rect.y - GetBuffer().GetTopMargin();
1661 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1662
1663 if (startYUnits != yUnits)
1664 {
1665 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1666 scrolled = true;
1667 }
1668 }
1669 }
1670 // Going up
1671 else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP ||
1672 keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT ||
1673 keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME ||
1674 keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP )
1675 {
1676 if (rect.y < (startY + GetBuffer().GetBottomMargin()))
1677 {
1678 // Make it scroll so this item is at the top
1679 // of the window
1680 int y = rect.y - GetBuffer().GetTopMargin();
1681 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1682
1683 if (startYUnits != yUnits)
1684 {
1685 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1686 scrolled = true;
1687 }
1688 }
1689 else if ((rect.y + rect.height) > (clientSize.y + startY))
1690 {
1691 // Make it scroll so this item is at the bottom
1692 // of the window
1693 int y = rect.y - (clientSize.y - rect.height);
1694 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
1695
1696 // If we're still off the screen, scroll another line down
1697 if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
1698 yUnits ++;
1699
1700 if (startYUnits != yUnits)
1701 {
1702 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
1703 scrolled = true;
1704 }
1705 }
1706 }
1707
1708 #if !wxRICHTEXT_USE_OWN_CARET
1709 if (scrolled)
1710 #endif
1711 PositionCaret();
1712
1713 return scrolled;
1714 }
1715
1716 /// Is the given position visible on the screen?
1717 bool wxRichTextCtrl::IsPositionVisible(long pos) const
1718 {
1719 wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
1720
1721 if (!line)
1722 return false;
1723
1724 int ppuX, ppuY;
1725 GetScrollPixelsPerUnit(& ppuX, & ppuY);
1726
1727 int startX, startY;
1728 GetViewStart(& startX, & startY);
1729 startX = 0;
1730 startY = startY * ppuY;
1731
1732 wxRect rect = line->GetRect();
1733 wxSize clientSize = GetClientSize();
1734 clientSize.y -= GetBuffer().GetBottomMargin();
1735
1736 return (rect.GetTop() >= (startY + GetBuffer().GetTopMargin())) && (rect.GetBottom() <= (startY + clientSize.y));
1737 }
1738
1739 void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
1740 {
1741 m_caretPosition = position;
1742 m_caretAtLineStart = showAtLineStart;
1743 }
1744
1745 /// Move caret one visual step forward: this may mean setting a flag
1746 /// and keeping the same position if we're going from the end of one line
1747 /// to the start of the next, which may be the exact same caret position.
1748 void wxRichTextCtrl::MoveCaretForward(long oldPosition)
1749 {
1750 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
1751
1752 // Only do the check if we're not at the end of the paragraph (where things work OK
1753 // anyway)
1754 if (para && (oldPosition != para->GetRange().GetEnd() - 1))
1755 {
1756 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
1757
1758 if (line)
1759 {
1760 wxRichTextRange lineRange = line->GetAbsoluteRange();
1761
1762 // We're at the end of a line. See whether we need to
1763 // stay at the same actual caret position but change visual
1764 // position, or not.
1765 if (oldPosition == lineRange.GetEnd())
1766 {
1767 if (m_caretAtLineStart)
1768 {
1769 // We're already at the start of the line, so actually move on now.
1770 m_caretPosition = oldPosition + 1;
1771 m_caretAtLineStart = false;
1772 }
1773 else
1774 {
1775 // We're showing at the end of the line, so keep to
1776 // the same position but indicate that we're to show
1777 // at the start of the next line.
1778 m_caretPosition = oldPosition;
1779 m_caretAtLineStart = true;
1780 }
1781 SetDefaultStyleToCursorStyle();
1782 return;
1783 }
1784 }
1785 }
1786 m_caretPosition ++;
1787 SetDefaultStyleToCursorStyle();
1788 }
1789
1790 /// Move caret one visual step backward: this may mean setting a flag
1791 /// and keeping the same position if we're going from the end of one line
1792 /// to the start of the next, which may be the exact same caret position.
1793 void wxRichTextCtrl::MoveCaretBack(long oldPosition)
1794 {
1795 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
1796
1797 // Only do the check if we're not at the start of the paragraph (where things work OK
1798 // anyway)
1799 if (para && (oldPosition != para->GetRange().GetStart()))
1800 {
1801 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
1802
1803 if (line)
1804 {
1805 wxRichTextRange lineRange = line->GetAbsoluteRange();
1806
1807 // We're at the start of a line. See whether we need to
1808 // stay at the same actual caret position but change visual
1809 // position, or not.
1810 if (oldPosition == lineRange.GetStart())
1811 {
1812 m_caretPosition = oldPosition-1;
1813 m_caretAtLineStart = true;
1814 return;
1815 }
1816 else if (oldPosition == lineRange.GetEnd())
1817 {
1818 if (m_caretAtLineStart)
1819 {
1820 // We're at the start of the line, so keep the same caret position
1821 // but clear the start-of-line flag.
1822 m_caretPosition = oldPosition;
1823 m_caretAtLineStart = false;
1824 }
1825 else
1826 {
1827 // We're showing at the end of the line, so go back
1828 // to the previous character position.
1829 m_caretPosition = oldPosition - 1;
1830 }
1831 SetDefaultStyleToCursorStyle();
1832 return;
1833 }
1834 }
1835 }
1836 m_caretPosition --;
1837 SetDefaultStyleToCursorStyle();
1838 }
1839
1840 /// Move right
1841 bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
1842 {
1843 long endPos = GetFocusObject()->GetOwnRange().GetEnd();
1844
1845 if (m_caretPosition + noPositions < endPos)
1846 {
1847 long oldPos = m_caretPosition;
1848 long newPos = m_caretPosition + noPositions;
1849
1850 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1851 if (!extendSel)
1852 SelectNone();
1853
1854 // Determine by looking at oldPos and m_caretPosition whether
1855 // we moved from the end of a line to the start of the next line, in which case
1856 // we want to adjust the caret position such that it is positioned at the
1857 // start of the next line, rather than jumping past the first character of the
1858 // line.
1859 if (noPositions == 1 && !extendSel)
1860 MoveCaretForward(oldPos);
1861 else
1862 SetCaretPosition(newPos);
1863
1864 PositionCaret();
1865 SetDefaultStyleToCursorStyle();
1866
1867 return true;
1868 }
1869 else
1870 return false;
1871 }
1872
1873 /// Move left
1874 bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
1875 {
1876 long startPos = -1;
1877
1878 if (m_caretPosition > startPos - noPositions + 1)
1879 {
1880 long oldPos = m_caretPosition;
1881 long newPos = m_caretPosition - noPositions;
1882 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
1883 if (!extendSel)
1884 SelectNone();
1885
1886 if (noPositions == 1 && !extendSel)
1887 MoveCaretBack(oldPos);
1888 else
1889 SetCaretPosition(newPos);
1890
1891 PositionCaret();
1892 SetDefaultStyleToCursorStyle();
1893
1894 return true;
1895 }
1896 else
1897 return false;
1898 }
1899
1900 // Find the caret position for the combination of hit-test flags and character position.
1901 // Returns the caret position and also an indication of where to place the caret (caretLineStart)
1902 // since this is ambiguous (same position used for end of line and start of next).
1903 long wxRichTextCtrl::FindCaretPositionForCharacterPosition(long position, int hitTestFlags, wxRichTextParagraphLayoutBox* container,
1904 bool& caretLineStart)
1905 {
1906 // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
1907 // we want to be at the end of the last line but with m_caretAtLineStart set to true,
1908 // so we view the caret at the start of the line.
1909 caretLineStart = false;
1910 long caretPosition = position;
1911
1912 if (hitTestFlags & wxRICHTEXT_HITTEST_BEFORE)
1913 {
1914 wxRichTextLine* thisLine = container->GetLineAtPosition(position-1);
1915 wxRichTextRange lineRange;
1916 if (thisLine)
1917 lineRange = thisLine->GetAbsoluteRange();
1918
1919 if (thisLine && (position-1) == lineRange.GetEnd())
1920 {
1921 caretPosition --;
1922 caretLineStart = true;
1923 }
1924 else
1925 {
1926 wxRichTextParagraph* para = container->GetParagraphAtPosition(position);
1927 if (para && para->GetRange().GetStart() == position)
1928 caretPosition --;
1929 }
1930 }
1931 return caretPosition;
1932 }
1933
1934 /// Move up
1935 bool wxRichTextCtrl::MoveUp(int noLines, int flags)
1936 {
1937 return MoveDown(- noLines, flags);
1938 }
1939
1940 /// Move up
1941 bool wxRichTextCtrl::MoveDown(int noLines, int flags)
1942 {
1943 if (!GetCaret())
1944 return false;
1945
1946 long lineNumber = GetFocusObject()->GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
1947 wxPoint pt = GetCaret()->GetPosition();
1948 long newLine = lineNumber + noLines;
1949 bool notInThisObject = false;
1950
1951 if (lineNumber != -1)
1952 {
1953 if (noLines > 0)
1954 {
1955 long lastLine = GetFocusObject()->GetVisibleLineNumber(GetFocusObject()->GetOwnRange().GetEnd());
1956 if (newLine > lastLine)
1957 notInThisObject = true;
1958 }
1959 else
1960 {
1961 if (newLine < 0)
1962 notInThisObject = true;
1963 }
1964 }
1965
1966 wxRichTextParagraphLayoutBox* container = GetFocusObject();
1967 int hitTestFlags = wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS|wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS;
1968
1969 if (notInThisObject)
1970 {
1971 // If we know we're navigating out of the current object,
1972 // try to find an object anywhere in the buffer at the new position (up or down a bit)
1973 container = & GetBuffer();
1974 hitTestFlags &= ~wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS;
1975
1976 if (noLines > 0) // going down
1977 {
1978 pt.y = GetFocusObject()->GetPosition().y + GetFocusObject()->GetCachedSize().y + 2;
1979 }
1980 else // going up
1981 {
1982 pt.y = GetFocusObject()->GetPosition().y - 2;
1983 }
1984 }
1985 else
1986 {
1987 wxRichTextLine* lineObj = GetFocusObject()->GetLineForVisibleLineNumber(newLine);
1988 if (lineObj)
1989 pt.y = lineObj->GetAbsolutePosition().y + 2;
1990 else
1991 return false;
1992 }
1993
1994 long newPos = 0;
1995 wxClientDC dc(this);
1996 PrepareDC(dc);
1997 dc.SetFont(GetFont());
1998
1999 wxRichTextObject* hitObj = NULL;
2000 wxRichTextObject* contextObj = NULL;
2001 int hitTest = container->HitTest(dc, pt, newPos, & hitObj, & contextObj, hitTestFlags);
2002
2003 if (hitObj &&
2004 ((hitTest & wxRICHTEXT_HITTEST_NONE) == 0) &&
2005 (! (hitObj == (& m_buffer) && ((hitTest & wxRICHTEXT_HITTEST_OUTSIDE) != 0))) // outside the buffer counts as 'do nothing'
2006 )
2007 {
2008 if (notInThisObject)
2009 {
2010 wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
2011 if (actualContainer && actualContainer != GetFocusObject() && actualContainer->AcceptsFocus())
2012 {
2013 SetFocusObject(actualContainer, false /* don't set caret position yet */);
2014
2015 container = actualContainer;
2016 }
2017 }
2018
2019 bool caretLineStart = true;
2020 long caretPosition = FindCaretPositionForCharacterPosition(newPos, hitTest, container, caretLineStart);
2021 long newSelEnd = caretPosition;
2022 bool extendSel;
2023
2024 if (notInThisObject)
2025 extendSel = false;
2026 else
2027 extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
2028
2029 if (!extendSel)
2030 SelectNone();
2031
2032 SetCaretPosition(caretPosition, caretLineStart);
2033 PositionCaret();
2034 SetDefaultStyleToCursorStyle();
2035
2036 return true;
2037 }
2038
2039 return false;
2040 }
2041
2042 /// Move to the end of the paragraph
2043 bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
2044 {
2045 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
2046 if (para)
2047 {
2048 long newPos = para->GetRange().GetEnd() - 1;
2049 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2050 if (!extendSel)
2051 SelectNone();
2052
2053 SetCaretPosition(newPos);
2054 PositionCaret();
2055 SetDefaultStyleToCursorStyle();
2056
2057 return true;
2058 }
2059
2060 return false;
2061 }
2062
2063 /// Move to the start of the paragraph
2064 bool wxRichTextCtrl::MoveToParagraphStart(int flags)
2065 {
2066 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
2067 if (para)
2068 {
2069 long newPos = para->GetRange().GetStart() - 1;
2070 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2071 if (!extendSel)
2072 SelectNone();
2073
2074 SetCaretPosition(newPos);
2075 PositionCaret();
2076 SetDefaultStyleToCursorStyle();
2077
2078 return true;
2079 }
2080
2081 return false;
2082 }
2083
2084 /// Move to the end of the line
2085 bool wxRichTextCtrl::MoveToLineEnd(int flags)
2086 {
2087 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2088
2089 if (line)
2090 {
2091 wxRichTextRange lineRange = line->GetAbsoluteRange();
2092 long newPos = lineRange.GetEnd();
2093 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2094 if (!extendSel)
2095 SelectNone();
2096
2097 SetCaretPosition(newPos);
2098 PositionCaret();
2099 SetDefaultStyleToCursorStyle();
2100
2101 return true;
2102 }
2103
2104 return false;
2105 }
2106
2107 /// Move to the start of the line
2108 bool wxRichTextCtrl::MoveToLineStart(int flags)
2109 {
2110 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2111 if (line)
2112 {
2113 wxRichTextRange lineRange = line->GetAbsoluteRange();
2114 long newPos = lineRange.GetStart()-1;
2115
2116 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
2117 if (!extendSel)
2118 SelectNone();
2119
2120 wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(line);
2121
2122 SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
2123 PositionCaret();
2124 SetDefaultStyleToCursorStyle();
2125
2126 return true;
2127 }
2128
2129 return false;
2130 }
2131
2132 /// Move to the start of the buffer
2133 bool wxRichTextCtrl::MoveHome(int flags)
2134 {
2135 if (m_caretPosition != -1)
2136 {
2137 bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
2138 if (!extendSel)
2139 SelectNone();
2140
2141 SetCaretPosition(-1);
2142 PositionCaret();
2143 SetDefaultStyleToCursorStyle();
2144
2145 return true;
2146 }
2147 else
2148 return false;
2149 }
2150
2151 /// Move to the end of the buffer
2152 bool wxRichTextCtrl::MoveEnd(int flags)
2153 {
2154 long endPos = GetFocusObject()->GetOwnRange().GetEnd()-1;
2155
2156 if (m_caretPosition != endPos)
2157 {
2158 bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
2159 if (!extendSel)
2160 SelectNone();
2161
2162 SetCaretPosition(endPos);
2163 PositionCaret();
2164 SetDefaultStyleToCursorStyle();
2165
2166 return true;
2167 }
2168 else
2169 return false;
2170 }
2171
2172 /// Move noPages pages up
2173 bool wxRichTextCtrl::PageUp(int noPages, int flags)
2174 {
2175 return PageDown(- noPages, flags);
2176 }
2177
2178 /// Move noPages pages down
2179 bool wxRichTextCtrl::PageDown(int noPages, int flags)
2180 {
2181 // Calculate which line occurs noPages * screen height further down.
2182 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
2183 if (line)
2184 {
2185 wxSize clientSize = GetClientSize();
2186 int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
2187
2188 wxRichTextLine* newLine = GetFocusObject()->GetLineAtYPosition(newY);
2189 if (newLine)
2190 {
2191 wxRichTextRange lineRange = newLine->GetAbsoluteRange();
2192 long pos = lineRange.GetStart()-1;
2193 if (pos != m_caretPosition)
2194 {
2195 wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(newLine);
2196
2197 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2198 if (!extendSel)
2199 SelectNone();
2200
2201 SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
2202 PositionCaret();
2203 SetDefaultStyleToCursorStyle();
2204
2205 return true;
2206 }
2207 }
2208 }
2209
2210 return false;
2211 }
2212
2213 static bool wxRichTextCtrlIsWhitespace(const wxString& str)
2214 {
2215 return str == wxT(" ") || str == wxT("\t");
2216 }
2217
2218 // Finds the caret position for the next word
2219 long wxRichTextCtrl::FindNextWordPosition(int direction) const
2220 {
2221 long endPos = GetFocusObject()->GetOwnRange().GetEnd();
2222
2223 if (direction > 0)
2224 {
2225 long i = m_caretPosition+1+direction; // +1 for conversion to character pos
2226
2227 // First skip current text to space
2228 while (i < endPos && i > -1)
2229 {
2230 // i is in character, not caret positions
2231 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2232 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2233 if (line && (i == line->GetAbsoluteRange().GetEnd()))
2234 {
2235 break;
2236 }
2237 else if (!wxRichTextCtrlIsWhitespace(text) && !text.empty())
2238 i += direction;
2239 else
2240 {
2241 break;
2242 }
2243 }
2244 while (i < endPos && i > -1)
2245 {
2246 // i is in character, not caret positions
2247 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2248 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2249 if (line && (i == line->GetAbsoluteRange().GetEnd()))
2250 return wxMax(-1, i);
2251
2252 if (text.empty()) // End of paragraph, or maybe an image
2253 return wxMax(-1, i - 1);
2254 else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
2255 i += direction;
2256 else
2257 {
2258 // Convert to caret position
2259 return wxMax(-1, i - 1);
2260 }
2261 }
2262 if (i >= endPos)
2263 return endPos-1;
2264 return i-1;
2265 }
2266 else
2267 {
2268 long i = m_caretPosition;
2269
2270 // First skip white space
2271 while (i < endPos && i > -1)
2272 {
2273 // i is in character, not caret positions
2274 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2275 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2276
2277 if (text.empty() || (line && (i == line->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image
2278 break;
2279 else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
2280 i += direction;
2281 else
2282 break;
2283 }
2284 // Next skip current text to space
2285 while (i < endPos && i > -1)
2286 {
2287 // i is in character, not caret positions
2288 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
2289 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
2290 if (line && line->GetAbsoluteRange().GetStart() == i)
2291 return i-1;
2292
2293 if (!wxRichTextCtrlIsWhitespace(text) /* && !text.empty() */)
2294 i += direction;
2295 else
2296 {
2297 return i;
2298 }
2299 }
2300 if (i < -1)
2301 return -1;
2302 return i;
2303 }
2304 }
2305
2306 /// Move n words left
2307 bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
2308 {
2309 long pos = FindNextWordPosition(-1);
2310 if (pos != m_caretPosition)
2311 {
2312 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
2313
2314 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2315 if (!extendSel)
2316 SelectNone();
2317
2318 SetCaretPosition(pos, para->GetRange().GetStart() != pos);
2319 PositionCaret();
2320 SetDefaultStyleToCursorStyle();
2321
2322 return true;
2323 }
2324
2325 return false;
2326 }
2327
2328 /// Move n words right
2329 bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
2330 {
2331 long pos = FindNextWordPosition(1);
2332 if (pos != m_caretPosition)
2333 {
2334 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
2335
2336 bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
2337 if (!extendSel)
2338 SelectNone();
2339
2340 SetCaretPosition(pos, para->GetRange().GetStart() != pos);
2341 PositionCaret();
2342 SetDefaultStyleToCursorStyle();
2343
2344 return true;
2345 }
2346
2347 return false;
2348 }
2349
2350 /// Sizing
2351 void wxRichTextCtrl::OnSize(wxSizeEvent& event)
2352 {
2353 // Only do sizing optimization for large buffers
2354 if (GetBuffer().GetOwnRange().GetEnd() > m_delayedLayoutThreshold)
2355 {
2356 m_fullLayoutRequired = true;
2357 m_fullLayoutTime = wxGetLocalTimeMillis();
2358 m_fullLayoutSavedPosition = GetFirstVisiblePosition();
2359 LayoutContent(true /* onlyVisibleRect */);
2360 }
2361 else
2362 GetBuffer().Invalidate(wxRICHTEXT_ALL);
2363
2364 #if wxRICHTEXT_BUFFERED_PAINTING
2365 RecreateBuffer();
2366 #endif
2367
2368 event.Skip();
2369 }
2370
2371 // Force any pending layout due to large buffer
2372 void wxRichTextCtrl::ForceDelayedLayout()
2373 {
2374 if (m_fullLayoutRequired)
2375 {
2376 m_fullLayoutRequired = false;
2377 m_fullLayoutTime = 0;
2378 GetBuffer().Invalidate(wxRICHTEXT_ALL);
2379 ShowPosition(m_fullLayoutSavedPosition);
2380 Refresh(false);
2381 Update();
2382 }
2383 }
2384
2385 /// Idle-time processing
2386 void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
2387 {
2388 #if wxRICHTEXT_USE_OWN_CARET
2389 if (((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
2390 {
2391 ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(false);
2392 PositionCaret();
2393 GetCaret()->Show();
2394 }
2395 #endif
2396
2397 const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
2398
2399 if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
2400 {
2401 m_fullLayoutRequired = false;
2402 m_fullLayoutTime = 0;
2403 GetBuffer().Invalidate(wxRICHTEXT_ALL);
2404 ShowPosition(m_fullLayoutSavedPosition);
2405 Refresh(false);
2406 }
2407
2408 if (m_caretPositionForDefaultStyle != -2)
2409 {
2410 // If the caret position has changed, no longer reflect the default style
2411 // in the UI.
2412 if (GetCaretPosition() != m_caretPositionForDefaultStyle)
2413 m_caretPositionForDefaultStyle = -2;
2414 }
2415
2416 event.Skip();
2417 }
2418
2419 /// Scrolling
2420 void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
2421 {
2422 #if wxRICHTEXT_USE_OWN_CARET
2423 if (!((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
2424 {
2425 GetCaret()->Hide();
2426 ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate();
2427 }
2428 #endif
2429
2430 event.Skip();
2431 }
2432
2433 /// Set up scrollbars, e.g. after a resize
2434 void wxRichTextCtrl::SetupScrollbars(bool atTop)
2435 {
2436 if (IsFrozen())
2437 return;
2438
2439 if (GetBuffer().IsEmpty())
2440 {
2441 SetScrollbars(0, 0, 0, 0, 0, 0);
2442 return;
2443 }
2444
2445 // TODO: reimplement scrolling so we scroll by line, not by fixed number
2446 // of pixels. See e.g. wxVScrolledWindow for ideas.
2447 int pixelsPerUnit = 5;
2448 wxSize clientSize = GetClientSize();
2449
2450 int maxHeight = GetBuffer().GetCachedSize().y + GetBuffer().GetTopMargin();
2451
2452 // Round up so we have at least maxHeight pixels
2453 int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5);
2454
2455 int startX = 0, startY = 0;
2456 if (!atTop)
2457 GetViewStart(& startX, & startY);
2458
2459 int maxPositionX = 0;
2460 int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5);
2461
2462 int newStartX = wxMin(maxPositionX, startX);
2463 int newStartY = wxMin(maxPositionY, startY);
2464
2465 int oldPPUX, oldPPUY;
2466 int oldStartX, oldStartY;
2467 int oldVirtualSizeX = 0, oldVirtualSizeY = 0;
2468 GetScrollPixelsPerUnit(& oldPPUX, & oldPPUY);
2469 GetViewStart(& oldStartX, & oldStartY);
2470 GetVirtualSize(& oldVirtualSizeX, & oldVirtualSizeY);
2471 if (oldPPUY > 0)
2472 oldVirtualSizeY /= oldPPUY;
2473
2474 if (oldPPUX == 0 && oldPPUY == pixelsPerUnit && oldVirtualSizeY == unitsY && oldStartX == newStartX && oldStartY == newStartY)
2475 return;
2476
2477 // Don't set scrollbars if there were none before, and there will be none now.
2478 if (oldPPUY != 0 && (oldVirtualSizeY*oldPPUY < clientSize.y) && (unitsY*pixelsPerUnit < clientSize.y))
2479 return;
2480
2481 // Move to previous scroll position if
2482 // possible
2483 SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY);
2484 }
2485
2486 /// Paint the background
2487 void wxRichTextCtrl::PaintBackground(wxDC& dc)
2488 {
2489 wxColour backgroundColour = GetBackgroundColour();
2490 if (!backgroundColour.IsOk())
2491 backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
2492
2493 // Clear the background
2494 dc.SetBrush(wxBrush(backgroundColour));
2495 dc.SetPen(*wxTRANSPARENT_PEN);
2496 wxRect windowRect(GetClientSize());
2497 windowRect.x -= 2; windowRect.y -= 2;
2498 windowRect.width += 4; windowRect.height += 4;
2499
2500 // We need to shift the rectangle to take into account
2501 // scrolling. Converting device to logical coordinates.
2502 CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
2503 dc.DrawRectangle(windowRect);
2504 }
2505
2506 #if wxRICHTEXT_BUFFERED_PAINTING
2507 /// Recreate buffer bitmap if necessary
2508 bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
2509 {
2510 wxSize sz = size;
2511 if (sz == wxDefaultSize)
2512 sz = GetClientSize();
2513
2514 if (sz.x < 1 || sz.y < 1)
2515 return false;
2516
2517 if (!m_bufferBitmap.IsOk() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
2518 m_bufferBitmap = wxBitmap(sz.x, sz.y);
2519 return m_bufferBitmap.IsOk();
2520 }
2521 #endif
2522
2523 // ----------------------------------------------------------------------------
2524 // file IO functions
2525 // ----------------------------------------------------------------------------
2526
2527 bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType)
2528 {
2529 bool success = GetBuffer().LoadFile(filename, (wxRichTextFileType)fileType);
2530 if (success)
2531 m_filename = filename;
2532
2533 DiscardEdits();
2534 SetInsertionPoint(0);
2535 LayoutContent();
2536 PositionCaret();
2537 SetupScrollbars(true);
2538 Refresh(false);
2539 wxTextCtrl::SendTextUpdatedEvent(this);
2540
2541 if (success)
2542 return true;
2543 else
2544 {
2545 wxLogError(_("File couldn't be loaded."));
2546
2547 return false;
2548 }
2549 }
2550
2551 bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
2552 {
2553 if (GetBuffer().SaveFile(filename, (wxRichTextFileType)fileType))
2554 {
2555 m_filename = filename;
2556
2557 DiscardEdits();
2558
2559 return true;
2560 }
2561
2562 wxLogError(_("The text couldn't be saved."));
2563
2564 return false;
2565 }
2566
2567 // ----------------------------------------------------------------------------
2568 // wxRichTextCtrl specific functionality
2569 // ----------------------------------------------------------------------------
2570
2571 /// Add a new paragraph of text to the end of the buffer
2572 wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
2573 {
2574 wxRichTextRange range = GetFocusObject()->AddParagraph(text);
2575 GetBuffer().Invalidate();
2576 LayoutContent();
2577 return range;
2578 }
2579
2580 /// Add an image
2581 wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
2582 {
2583 wxRichTextRange range = GetFocusObject()->AddImage(image);
2584 GetBuffer().Invalidate();
2585 LayoutContent();
2586 return range;
2587 }
2588
2589 // ----------------------------------------------------------------------------
2590 // selection and ranges
2591 // ----------------------------------------------------------------------------
2592
2593 void wxRichTextCtrl::SelectAll()
2594 {
2595 SetSelection(-1, -1);
2596 }
2597
2598 /// Select none
2599 void wxRichTextCtrl::SelectNone()
2600 {
2601 if (m_selection.IsValid())
2602 {
2603 wxRichTextSelection oldSelection = m_selection;
2604
2605 m_selection.Reset();
2606
2607 RefreshForSelectionChange(oldSelection, m_selection);
2608 }
2609 m_selectionAnchor = -2;
2610 m_selectionAnchorObject = NULL;
2611 m_selectionState = wxRichTextCtrlSelectionState_Normal;
2612 }
2613
2614 static bool wxIsWordDelimiter(const wxString& text)
2615 {
2616 return !text.IsEmpty() && !wxIsalnum(text[0]);
2617 }
2618
2619 /// Select the word at the given character position
2620 bool wxRichTextCtrl::SelectWord(long position)
2621 {
2622 if (position < 0 || position > GetFocusObject()->GetOwnRange().GetEnd())
2623 return false;
2624
2625 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(position);
2626 if (!para)
2627 return false;
2628
2629 if (position == para->GetRange().GetEnd())
2630 position --;
2631
2632 long positionStart = position;
2633 long positionEnd = position;
2634
2635 for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --)
2636 {
2637 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionStart, positionStart));
2638 if (wxIsWordDelimiter(text))
2639 {
2640 positionStart ++;
2641 break;
2642 }
2643 }
2644 if (positionStart < para->GetRange().GetStart())
2645 positionStart = para->GetRange().GetStart();
2646
2647 for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++)
2648 {
2649 wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionEnd, positionEnd));
2650 if (wxIsWordDelimiter(text))
2651 {
2652 positionEnd --;
2653 break;
2654 }
2655 }
2656 if (positionEnd >= para->GetRange().GetEnd())
2657 positionEnd = para->GetRange().GetEnd();
2658
2659 if (positionEnd < positionStart)
2660 return false;
2661
2662 SetSelection(positionStart, positionEnd+1);
2663
2664 if (positionStart >= 0)
2665 {
2666 MoveCaret(positionStart-1, true);
2667 SetDefaultStyleToCursorStyle();
2668 }
2669
2670 return true;
2671 }
2672
2673 wxString wxRichTextCtrl::GetStringSelection() const
2674 {
2675 long from, to;
2676 GetSelection(&from, &to);
2677
2678 return GetRange(from, to);
2679 }
2680
2681 // ----------------------------------------------------------------------------
2682 // hit testing
2683 // ----------------------------------------------------------------------------
2684
2685 wxTextCtrlHitTestResult
2686 wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
2687 {
2688 // implement in terms of the other overload as the native ports typically
2689 // can get the position and not (x, y) pair directly (although wxUniv
2690 // directly gets x and y -- and so overrides this method as well)
2691 long pos;
2692 wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
2693
2694 if ( rc != wxTE_HT_UNKNOWN )
2695 {
2696 PositionToXY(pos, x, y);
2697 }
2698
2699 return rc;
2700 }
2701
2702 wxTextCtrlHitTestResult
2703 wxRichTextCtrl::HitTest(const wxPoint& pt,
2704 long * pos) const
2705 {
2706 wxClientDC dc((wxRichTextCtrl*) this);
2707 ((wxRichTextCtrl*)this)->PrepareDC(dc);
2708
2709 // Buffer uses logical position (relative to start of buffer)
2710 // so convert
2711 wxPoint pt2 = GetLogicalPoint(pt);
2712
2713 wxRichTextObject* hitObj = NULL;
2714 wxRichTextObject* contextObj = NULL;
2715 int hit = ((wxRichTextCtrl*)this)->GetFocusObject()->HitTest(dc, pt2, *pos, & hitObj, & contextObj, wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS);
2716
2717 if ((hit & wxRICHTEXT_HITTEST_BEFORE) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
2718 return wxTE_HT_BEFORE;
2719 else if ((hit & wxRICHTEXT_HITTEST_AFTER) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
2720 return wxTE_HT_BEYOND;
2721 else if (hit & (wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_AFTER))
2722 return wxTE_HT_ON_TEXT;
2723
2724 return wxTE_HT_UNKNOWN;
2725 }
2726
2727 wxRichTextParagraphLayoutBox*
2728 wxRichTextCtrl::FindContainerAtPoint(const wxPoint pt, long& position, int& hit, wxRichTextObject* hitObj, int flags/* = 0*/)
2729 {
2730 wxClientDC dc(this);
2731 PrepareDC(dc);
2732 dc.SetFont(GetFont());
2733
2734 wxPoint logicalPt = GetLogicalPoint(pt);
2735
2736 wxRichTextObject* contextObj = NULL;
2737 hit = GetBuffer().HitTest(dc, logicalPt, position, &hitObj, &contextObj, flags);
2738 wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
2739
2740 return container;
2741 }
2742
2743
2744 // ----------------------------------------------------------------------------
2745 // set/get the controls text
2746 // ----------------------------------------------------------------------------
2747
2748 wxString wxRichTextCtrl::DoGetValue() const
2749 {
2750 return GetBuffer().GetText();
2751 }
2752
2753 wxString wxRichTextCtrl::GetRange(long from, long to) const
2754 {
2755 // Public API for range is different from internals
2756 return GetFocusObject()->GetTextForRange(wxRichTextRange(from, to-1));
2757 }
2758
2759 void wxRichTextCtrl::DoSetValue(const wxString& value, int flags)
2760 {
2761 // Don't call Clear here, since it always sends a text updated event
2762 m_buffer.ResetAndClearCommands();
2763 m_buffer.Invalidate(wxRICHTEXT_ALL);
2764 m_caretPosition = -1;
2765 m_caretPositionForDefaultStyle = -2;
2766 m_caretAtLineStart = false;
2767 m_selection.Reset();
2768 m_selectionState = wxRichTextCtrlSelectionState_Normal;
2769
2770 Scroll(0,0);
2771
2772 if (!IsFrozen())
2773 {
2774 LayoutContent();
2775 Refresh(false);
2776 }
2777
2778 if (!value.IsEmpty())
2779 {
2780 // Remove empty paragraph
2781 GetBuffer().Clear();
2782 DoWriteText(value, flags);
2783
2784 // for compatibility, don't move the cursor when doing SetValue()
2785 SetInsertionPoint(0);
2786 }
2787 else
2788 {
2789 // still send an event for consistency
2790 if (flags & SetValue_SendEvent)
2791 wxTextCtrl::SendTextUpdatedEvent(this);
2792 }
2793 DiscardEdits();
2794 }
2795
2796 void wxRichTextCtrl::WriteText(const wxString& value)
2797 {
2798 DoWriteText(value);
2799 }
2800
2801 void wxRichTextCtrl::DoWriteText(const wxString& value, int flags)
2802 {
2803 wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
2804
2805 GetFocusObject()->InsertTextWithUndo(m_caretPosition+1, valueUnix, this, & GetBuffer(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2806
2807 if ( flags & SetValue_SendEvent )
2808 wxTextCtrl::SendTextUpdatedEvent(this);
2809 }
2810
2811 void wxRichTextCtrl::AppendText(const wxString& text)
2812 {
2813 SetInsertionPointEnd();
2814
2815 WriteText(text);
2816 }
2817
2818 /// Write an image at the current insertion point
2819 bool wxRichTextCtrl::WriteImage(const wxImage& image, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
2820 {
2821 wxRichTextImageBlock imageBlock;
2822
2823 wxImage image2 = image;
2824 if (imageBlock.MakeImageBlock(image2, bitmapType))
2825 return WriteImage(imageBlock, textAttr);
2826
2827 return false;
2828 }
2829
2830 bool wxRichTextCtrl::WriteImage(const wxString& filename, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
2831 {
2832 wxRichTextImageBlock imageBlock;
2833
2834 wxImage image;
2835 if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
2836 return WriteImage(imageBlock, textAttr);
2837
2838 return false;
2839 }
2840
2841 bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock, const wxRichTextAttr& textAttr)
2842 {
2843 return GetFocusObject()->InsertImageWithUndo(m_caretPosition+1, imageBlock, this, & GetBuffer(), 0, textAttr);
2844 }
2845
2846 bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
2847 {
2848 if (bitmap.IsOk())
2849 {
2850 wxRichTextImageBlock imageBlock;
2851
2852 wxImage image = bitmap.ConvertToImage();
2853 if (image.IsOk() && imageBlock.MakeImageBlock(image, bitmapType))
2854 return WriteImage(imageBlock, textAttr);
2855 }
2856
2857 return false;
2858 }
2859
2860 // Write a text box at the current insertion point.
2861 wxRichTextBox* wxRichTextCtrl::WriteTextBox(const wxRichTextAttr& textAttr)
2862 {
2863 wxRichTextBox* textBox = new wxRichTextBox;
2864 textBox->SetAttributes(textAttr);
2865 textBox->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
2866 textBox->AddParagraph(wxEmptyString);
2867 textBox->SetParent(NULL);
2868
2869 // The object returned is the one actually inserted into the buffer,
2870 // while the original one is deleted.
2871 wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(m_caretPosition+1, textBox, this, & GetBuffer(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2872 wxRichTextBox* box = wxDynamicCast(obj, wxRichTextBox);
2873 return box;
2874 }
2875
2876 // Write a table at the current insertion point, returning the table.
2877 wxRichTextTable* wxRichTextCtrl::WriteTable(int rows, int cols, const wxRichTextAttr& tableAttr, const wxRichTextAttr& cellAttr)
2878 {
2879 wxASSERT(rows > 0 && cols > 0);
2880
2881 if (rows <= 0 || cols <= 0)
2882 return NULL;
2883
2884 wxRichTextTable* table = new wxRichTextTable;
2885 table->SetAttributes(tableAttr);
2886 table->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
2887
2888 table->CreateTable(rows, cols);
2889
2890 table->SetParent(NULL);
2891
2892 int i, j;
2893 for (j = 0; j < rows; j++)
2894 {
2895 for (i = 0; i < cols; i++)
2896 {
2897 table->GetCell(j, i)->GetAttributes() = cellAttr;
2898 }
2899 }
2900
2901 // The object returned is the one actually inserted into the buffer,
2902 // while the original one is deleted.
2903 wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(m_caretPosition+1, table, this, & GetBuffer(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2904 wxRichTextTable* tableResult = wxDynamicCast(obj, wxRichTextTable);
2905 return tableResult;
2906 }
2907
2908
2909 /// Insert a newline (actually paragraph) at the current insertion point.
2910 bool wxRichTextCtrl::Newline()
2911 {
2912 return GetFocusObject()->InsertNewlineWithUndo(m_caretPosition+1, this, & GetBuffer(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
2913 }
2914
2915 /// Insert a line break at the current insertion point.
2916 bool wxRichTextCtrl::LineBreak()
2917 {
2918 wxString text;
2919 text = wxRichTextLineBreakChar;
2920 return GetFocusObject()->InsertTextWithUndo(m_caretPosition+1, text, this, & GetBuffer());
2921 }
2922
2923 // ----------------------------------------------------------------------------
2924 // Clipboard operations
2925 // ----------------------------------------------------------------------------
2926
2927 void wxRichTextCtrl::Copy()
2928 {
2929 if (CanCopy())
2930 {
2931 wxRichTextRange range = GetInternalSelectionRange();
2932 GetBuffer().CopyToClipboard(range);
2933 }
2934 }
2935
2936 void wxRichTextCtrl::Cut()
2937 {
2938 if (CanCut())
2939 {
2940 wxRichTextRange range = GetInternalSelectionRange();
2941 GetBuffer().CopyToClipboard(range);
2942
2943 DeleteSelectedContent();
2944 LayoutContent();
2945 Refresh(false);
2946 }
2947 }
2948
2949 void wxRichTextCtrl::Paste()
2950 {
2951 if (CanPaste())
2952 {
2953 BeginBatchUndo(_("Paste"));
2954
2955 long newPos = m_caretPosition;
2956 DeleteSelectedContent(& newPos);
2957
2958 GetBuffer().PasteFromClipboard(newPos);
2959
2960 EndBatchUndo();
2961 }
2962 }
2963
2964 void wxRichTextCtrl::DeleteSelection()
2965 {
2966 if (CanDeleteSelection())
2967 {
2968 DeleteSelectedContent();
2969 }
2970 }
2971
2972 bool wxRichTextCtrl::HasSelection() const
2973 {
2974 return (m_selection.IsValid() && m_selection.GetContainer() == GetFocusObject());
2975 }
2976
2977 bool wxRichTextCtrl::HasUnfocusedSelection() const
2978 {
2979 return m_selection.IsValid();
2980 }
2981
2982 bool wxRichTextCtrl::CanCopy() const
2983 {
2984 // Can copy if there's a selection
2985 return HasSelection();
2986 }
2987
2988 bool wxRichTextCtrl::CanCut() const
2989 {
2990 return HasSelection() && IsEditable();
2991 }
2992
2993 bool wxRichTextCtrl::CanPaste() const
2994 {
2995 if ( !IsEditable() )
2996 return false;
2997
2998 return GetBuffer().CanPasteFromClipboard();
2999 }
3000
3001 bool wxRichTextCtrl::CanDeleteSelection() const
3002 {
3003 return HasSelection() && IsEditable();
3004 }
3005
3006
3007 // ----------------------------------------------------------------------------
3008 // Accessors
3009 // ----------------------------------------------------------------------------
3010
3011 void wxRichTextCtrl::SetContextMenu(wxMenu* menu)
3012 {
3013 if (m_contextMenu && m_contextMenu != menu)
3014 delete m_contextMenu;
3015 m_contextMenu = menu;
3016 }
3017
3018 void wxRichTextCtrl::SetEditable(bool editable)
3019 {
3020 m_editable = editable;
3021 }
3022
3023 void wxRichTextCtrl::SetInsertionPoint(long pos)
3024 {
3025 SelectNone();
3026
3027 m_caretPosition = pos - 1;
3028
3029 PositionCaret();
3030
3031 SetDefaultStyleToCursorStyle();
3032 }
3033
3034 void wxRichTextCtrl::SetInsertionPointEnd()
3035 {
3036 long pos = GetLastPosition();
3037 SetInsertionPoint(pos);
3038 }
3039
3040 long wxRichTextCtrl::GetInsertionPoint() const
3041 {
3042 return m_caretPosition+1;
3043 }
3044
3045 wxTextPos wxRichTextCtrl::GetLastPosition() const
3046 {
3047 return GetFocusObject()->GetOwnRange().GetEnd();
3048 }
3049
3050 // If the return values from and to are the same, there is no
3051 // selection.
3052 void wxRichTextCtrl::GetSelection(long* from, long* to) const
3053 {
3054 if (m_selection.IsValid())
3055 {
3056 *from = m_selection.GetRange().GetStart();
3057 *to = m_selection.GetRange().GetEnd();
3058 (*to) ++;
3059 }
3060 else
3061 {
3062 *from = -2;
3063 *to = -2;
3064 }
3065 }
3066
3067 bool wxRichTextCtrl::IsEditable() const
3068 {
3069 return m_editable;
3070 }
3071
3072 // ----------------------------------------------------------------------------
3073 // selection
3074 // ----------------------------------------------------------------------------
3075
3076 void wxRichTextCtrl::SetSelection(long from, long to)
3077 {
3078 // if from and to are both -1, it means (in wxWidgets) that all text should
3079 // be selected.
3080 if ( (from == -1) && (to == -1) )
3081 {
3082 from = 0;
3083 to = GetLastPosition()+1;
3084 }
3085
3086 if (from == to)
3087 {
3088 SelectNone();
3089 }
3090 else
3091 {
3092 wxRichTextSelection oldSelection = m_selection;
3093
3094 m_selectionAnchor = from-1;
3095 m_selectionAnchorObject = NULL;
3096 m_selection.Set(wxRichTextRange(from, to-1), GetFocusObject());
3097
3098 m_caretPosition = wxMax(-1, to-1);
3099
3100 RefreshForSelectionChange(oldSelection, m_selection);
3101 PositionCaret();
3102 }
3103 }
3104
3105 // ----------------------------------------------------------------------------
3106 // Editing
3107 // ----------------------------------------------------------------------------
3108
3109 void wxRichTextCtrl::Replace(long from, long to,
3110 const wxString& value)
3111 {
3112 BeginBatchUndo(_("Replace"));
3113
3114 SetSelection(from, to);
3115
3116 wxRichTextAttr attr = GetDefaultStyle();
3117
3118 DeleteSelectedContent();
3119
3120 SetDefaultStyle(attr);
3121
3122 DoWriteText(value, SetValue_SelectionOnly);
3123
3124 EndBatchUndo();
3125 }
3126
3127 void wxRichTextCtrl::Remove(long from, long to)
3128 {
3129 SelectNone();
3130
3131 GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(from, to-1), this, & GetBuffer());
3132
3133 LayoutContent();
3134 if (!IsFrozen())
3135 Refresh(false);
3136 }
3137
3138 bool wxRichTextCtrl::IsModified() const
3139 {
3140 return m_buffer.IsModified();
3141 }
3142
3143 void wxRichTextCtrl::MarkDirty()
3144 {
3145 m_buffer.Modify(true);
3146 }
3147
3148 void wxRichTextCtrl::DiscardEdits()
3149 {
3150 m_caretPositionForDefaultStyle = -2;
3151 m_buffer.Modify(false);
3152 m_buffer.GetCommandProcessor()->ClearCommands();
3153 }
3154
3155 int wxRichTextCtrl::GetNumberOfLines() const
3156 {
3157 return GetFocusObject()->GetParagraphCount();
3158 }
3159
3160 // ----------------------------------------------------------------------------
3161 // Positions <-> coords
3162 // ----------------------------------------------------------------------------
3163
3164 long wxRichTextCtrl::XYToPosition(long x, long y) const
3165 {
3166 return GetFocusObject()->XYToPosition(x, y);
3167 }
3168
3169 bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
3170 {
3171 return GetFocusObject()->PositionToXY(pos, x, y);
3172 }
3173
3174 // ----------------------------------------------------------------------------
3175 //
3176 // ----------------------------------------------------------------------------
3177
3178 void wxRichTextCtrl::ShowPosition(long pos)
3179 {
3180 if (!IsPositionVisible(pos))
3181 ScrollIntoView(pos-1, WXK_DOWN);
3182 }
3183
3184 int wxRichTextCtrl::GetLineLength(long lineNo) const
3185 {
3186 return GetFocusObject()->GetParagraphLength(lineNo);
3187 }
3188
3189 wxString wxRichTextCtrl::GetLineText(long lineNo) const
3190 {
3191 return GetFocusObject()->GetParagraphText(lineNo);
3192 }
3193
3194 // ----------------------------------------------------------------------------
3195 // Undo/redo
3196 // ----------------------------------------------------------------------------
3197
3198 void wxRichTextCtrl::Undo()
3199 {
3200 if (CanUndo())
3201 {
3202 GetCommandProcessor()->Undo();
3203 }
3204 }
3205
3206 void wxRichTextCtrl::Redo()
3207 {
3208 if (CanRedo())
3209 {
3210 GetCommandProcessor()->Redo();
3211 }
3212 }
3213
3214 bool wxRichTextCtrl::CanUndo() const
3215 {
3216 return GetCommandProcessor()->CanUndo() && IsEditable();
3217 }
3218
3219 bool wxRichTextCtrl::CanRedo() const
3220 {
3221 return GetCommandProcessor()->CanRedo() && IsEditable();
3222 }
3223
3224 // ----------------------------------------------------------------------------
3225 // implementation details
3226 // ----------------------------------------------------------------------------
3227
3228 void wxRichTextCtrl::Command(wxCommandEvent& event)
3229 {
3230 SetValue(event.GetString());
3231 GetEventHandler()->ProcessEvent(event);
3232 }
3233
3234 void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
3235 {
3236 // By default, load the first file into the text window.
3237 if (event.GetNumberOfFiles() > 0)
3238 {
3239 LoadFile(event.GetFiles()[0]);
3240 }
3241 }
3242
3243 wxSize wxRichTextCtrl::DoGetBestSize() const
3244 {
3245 return wxSize(10, 10);
3246 }
3247
3248 // ----------------------------------------------------------------------------
3249 // standard handlers for standard edit menu events
3250 // ----------------------------------------------------------------------------
3251
3252 void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
3253 {
3254 Cut();
3255 }
3256
3257 void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
3258 {
3259 DeleteSelection();
3260 }
3261
3262 void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
3263 {
3264 Copy();
3265 }
3266
3267 void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
3268 {
3269 Paste();
3270 }
3271
3272 void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
3273 {
3274 Undo();
3275 }
3276
3277 void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
3278 {
3279 Redo();
3280 }
3281
3282 void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
3283 {
3284 event.Enable( CanCut() );
3285 }
3286
3287 void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
3288 {
3289 event.Enable( CanCopy() );
3290 }
3291
3292 void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
3293 {
3294 event.Enable( CanDeleteSelection() );
3295 }
3296
3297 void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
3298 {
3299 event.Enable( CanPaste() );
3300 }
3301
3302 void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
3303 {
3304 event.Enable( CanUndo() );
3305 event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
3306 }
3307
3308 void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
3309 {
3310 event.Enable( CanRedo() );
3311 event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
3312 }
3313
3314 void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
3315 {
3316 if (GetLastPosition() > 0)
3317 SelectAll();
3318 }
3319
3320 void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
3321 {
3322 event.Enable(GetLastPosition() > 0);
3323 }
3324
3325 void wxRichTextCtrl::OnProperties(wxCommandEvent& event)
3326 {
3327 int idx = event.GetId() - wxID_RICHTEXT_PROPERTIES1;
3328 if (idx >= 0 && idx < m_contextMenuPropertiesInfo.GetCount())
3329 {
3330 wxRichTextObject* obj = m_contextMenuPropertiesInfo.GetObject(idx);
3331 if (obj && obj->CanEditProperties())
3332 obj->EditProperties(this, & GetBuffer());
3333
3334 m_contextMenuPropertiesInfo.Clear();
3335 }
3336 }
3337
3338 void wxRichTextCtrl::OnUpdateProperties(wxUpdateUIEvent& event)
3339 {
3340 int idx = event.GetId() - wxID_RICHTEXT_PROPERTIES1;
3341 event.Enable(idx >= 0 && idx < m_contextMenuPropertiesInfo.GetCount());
3342 }
3343
3344 void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& event)
3345 {
3346 if (event.GetEventObject() != this)
3347 {
3348 event.Skip();
3349 return;
3350 }
3351
3352 ShowContextMenu(m_contextMenu, event.GetPosition());
3353 }
3354
3355 // Prepares the context menu, adding appropriate property-editing commands.
3356 // Returns the number of property commands added.
3357 int wxRichTextCtrl::PrepareContextMenu(wxMenu* menu, const wxPoint& pt, bool addPropertyCommands)
3358 {
3359 wxClientDC dc(this);
3360 PrepareDC(dc);
3361 dc.SetFont(GetFont());
3362
3363 m_contextMenuPropertiesInfo.Clear();
3364
3365 long position = 0;
3366 wxRichTextObject* hitObj = NULL;
3367 wxRichTextObject* contextObj = NULL;
3368 if (pt != wxDefaultPosition)
3369 {
3370 wxPoint logicalPt = GetLogicalPoint(ScreenToClient(pt));
3371 int hit = GetBuffer().HitTest(dc, logicalPt, position, & hitObj, & contextObj);
3372
3373 if (hit == wxRICHTEXT_HITTEST_ON || hit == wxRICHTEXT_HITTEST_BEFORE || hit == wxRICHTEXT_HITTEST_AFTER)
3374 {
3375 wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
3376 if (hitObj && actualContainer)
3377 {
3378 if (actualContainer->AcceptsFocus())
3379 {
3380 SetFocusObject(actualContainer, false /* don't set caret position yet */);
3381 SetCaretPositionAfterClick(actualContainer, position, hit);
3382 }
3383
3384 if (addPropertyCommands)
3385 m_contextMenuPropertiesInfo.AddItems(actualContainer, hitObj);
3386 }
3387 else
3388 {
3389 if (addPropertyCommands)
3390 m_contextMenuPropertiesInfo.AddItems(GetFocusObject(), NULL);
3391 }
3392 }
3393 else
3394 {
3395 if (addPropertyCommands)
3396 m_contextMenuPropertiesInfo.AddItems(GetFocusObject(), NULL);
3397 }
3398 }
3399 else
3400 {
3401 // Invoked from the keyboard, so don't set the caret position and don't use the event
3402 // position
3403 hitObj = GetFocusObject()->GetLeafObjectAtPosition(m_caretPosition+1);
3404 if (hitObj)
3405 contextObj = hitObj->GetParentContainer();
3406 else
3407 contextObj = GetFocusObject();
3408
3409 wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
3410 if (hitObj && actualContainer)
3411 {
3412 if (addPropertyCommands)
3413 m_contextMenuPropertiesInfo.AddItems(actualContainer, hitObj);
3414 }
3415 else
3416 {
3417 if (addPropertyCommands)
3418 m_contextMenuPropertiesInfo.AddItems(GetFocusObject(), NULL);
3419 }
3420 }
3421
3422 if (menu)
3423 {
3424 if (addPropertyCommands)
3425 m_contextMenuPropertiesInfo.AddMenuItems(menu);
3426 return m_contextMenuPropertiesInfo.GetCount();
3427 }
3428 else
3429 return 0;
3430 }
3431
3432 // Shows the context menu, adding appropriate property-editing commands
3433 bool wxRichTextCtrl::ShowContextMenu(wxMenu* menu, const wxPoint& pt, bool addPropertyCommands)
3434 {
3435 if (menu)
3436 {
3437 PrepareContextMenu(menu, pt, addPropertyCommands);
3438 PopupMenu(menu);
3439 return true;
3440 }
3441 else
3442 return false;
3443 }
3444
3445 bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
3446 {
3447 return GetFocusObject()->SetStyle(wxRichTextRange(start, end-1), wxRichTextAttr(style));
3448 }
3449
3450 bool wxRichTextCtrl::SetStyle(long start, long end, const wxRichTextAttr& style)
3451 {
3452 return GetFocusObject()->SetStyle(wxRichTextRange(start, end-1), style);
3453 }
3454
3455 bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxTextAttr& style)
3456 {
3457 return GetFocusObject()->SetStyle(range.ToInternal(), wxRichTextAttr(style));
3458 }
3459
3460 bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
3461 {
3462 return GetFocusObject()->SetStyle(range.ToInternal(), style);
3463 }
3464
3465 void wxRichTextCtrl::SetStyle(wxRichTextObject *obj, const wxRichTextAttr& textAttr)
3466 {
3467 GetFocusObject()->SetStyle(obj, textAttr);
3468 }
3469
3470 // extended style setting operation with flags including:
3471 // wxRICHTEXT_SETSTYLE_WITH_UNDO, wxRICHTEXT_SETSTYLE_OPTIMIZE, wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY.
3472 // see richtextbuffer.h for more details.
3473
3474 bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3475 {
3476 return GetFocusObject()->SetStyle(range.ToInternal(), style, flags);
3477 }
3478
3479 bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style)
3480 {
3481 return GetBuffer().SetDefaultStyle(style);
3482 }
3483
3484 bool wxRichTextCtrl::SetDefaultStyle(const wxRichTextAttr& style)
3485 {
3486 wxRichTextAttr attr1(style);
3487 attr1.GetTextBoxAttr().Reset();
3488 return GetBuffer().SetDefaultStyle(attr1);
3489 }
3490
3491 const wxRichTextAttr& wxRichTextCtrl::GetDefaultStyleEx() const
3492 {
3493 return GetBuffer().GetDefaultStyle();
3494 }
3495
3496 bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style)
3497 {
3498 wxRichTextAttr attr;
3499 if (GetFocusObject()->GetStyle(position, attr))
3500 {
3501 style = attr;
3502 return true;
3503 }
3504 else
3505 return false;
3506 }
3507
3508 bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style)
3509 {
3510 return GetFocusObject()->GetStyle(position, style);
3511 }
3512
3513 bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
3514 {
3515 wxRichTextAttr attr;
3516 if (container->GetStyle(position, attr))
3517 {
3518 style = attr;
3519 return true;
3520 }
3521 else
3522 return false;
3523 }
3524
3525 // get the common set of styles for the range
3526 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxTextAttr& style)
3527 {
3528 wxRichTextAttr attr;
3529 if (GetFocusObject()->GetStyleForRange(range.ToInternal(), attr))
3530 {
3531 style = attr;
3532 return true;
3533 }
3534 else
3535 return false;
3536 }
3537
3538 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3539 {
3540 return GetFocusObject()->GetStyleForRange(range.ToInternal(), style);
3541 }
3542
3543 bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
3544 {
3545 return container->GetStyleForRange(range.ToInternal(), style);
3546 }
3547
3548 /// Get the content (uncombined) attributes for this position.
3549 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style)
3550 {
3551 return GetFocusObject()->GetUncombinedStyle(position, style);
3552 }
3553
3554 /// Get the content (uncombined) attributes for this position.
3555 bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style, wxRichTextParagraphLayoutBox* container)
3556 {
3557 return container->GetUncombinedStyle(position, style);
3558 }
3559
3560 /// Set font, and also the buffer attributes
3561 bool wxRichTextCtrl::SetFont(const wxFont& font)
3562 {
3563 wxControl::SetFont(font);
3564
3565 wxRichTextAttr attr = GetBuffer().GetAttributes();
3566 attr.SetFont(font);
3567 GetBuffer().SetBasicStyle(attr);
3568
3569 GetBuffer().Invalidate(wxRICHTEXT_ALL);
3570 Refresh(false);
3571
3572 return true;
3573 }
3574
3575 /// Transform logical to physical
3576 wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
3577 {
3578 wxPoint pt;
3579 CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
3580
3581 return pt;
3582 }
3583
3584 /// Transform physical to logical
3585 wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
3586 {
3587 wxPoint pt;
3588 CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
3589
3590 return pt;
3591 }
3592
3593 /// Position the caret
3594 void wxRichTextCtrl::PositionCaret(wxRichTextParagraphLayoutBox* container)
3595 {
3596 if (!GetCaret())
3597 return;
3598
3599 //wxLogDebug(wxT("PositionCaret"));
3600
3601 wxRect caretRect;
3602 if (GetCaretPositionForIndex(GetCaretPosition(), caretRect, container))
3603 {
3604 wxPoint newPt = caretRect.GetPosition();
3605 wxSize newSz = caretRect.GetSize();
3606 wxPoint pt = GetPhysicalPoint(newPt);
3607 if (GetCaret()->GetPosition() != pt || GetCaret()->GetSize() != newSz)
3608 {
3609 GetCaret()->Hide();
3610 if (GetCaret()->GetSize() != newSz)
3611 GetCaret()->SetSize(newSz);
3612
3613 // Adjust size so the caret size and position doesn't appear in the margins
3614 if (((pt.y + newSz.y) <= GetBuffer().GetTopMargin()) || (pt.y >= (GetClientSize().y - GetBuffer().GetBottomMargin())))
3615 {
3616 pt.x = -200;
3617 pt.y = -200;
3618 }
3619 else if (pt.y < GetBuffer().GetTopMargin() && (pt.y + newSz.y) > GetBuffer().GetTopMargin())
3620 {
3621 newSz.y -= (GetBuffer().GetTopMargin() - pt.y);
3622 if (newSz.y > 0)
3623 {
3624 pt.y = GetBuffer().GetTopMargin();
3625 GetCaret()->SetSize(newSz);
3626 }
3627 }
3628 else if (pt.y < (GetClientSize().y - GetBuffer().GetBottomMargin()) && (pt.y + newSz.y) > (GetClientSize().y - GetBuffer().GetBottomMargin()))
3629 {
3630 newSz.y = GetClientSize().y - GetBuffer().GetBottomMargin() - pt.y;
3631 GetCaret()->SetSize(newSz);
3632 }
3633
3634 GetCaret()->Move(pt);
3635 GetCaret()->Show();
3636 }
3637 }
3638 }
3639
3640 /// Get the caret height and position for the given character position
3641 bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect, wxRichTextParagraphLayoutBox* container)
3642 {
3643 wxClientDC dc(this);
3644 dc.SetFont(GetFont());
3645
3646 PrepareDC(dc);
3647
3648 wxPoint pt;
3649 int height = 0;
3650
3651 if (!container)
3652 container = GetFocusObject();
3653
3654 if (container->FindPosition(dc, position, pt, & height, m_caretAtLineStart))
3655 {
3656 // Caret height can't be zero
3657 if (height == 0)
3658 height = dc.GetCharHeight();
3659
3660 rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
3661 return true;
3662 }
3663
3664 return false;
3665 }
3666
3667 /// Gets the line for the visible caret position. If the caret is
3668 /// shown at the very end of the line, it means the next character is actually
3669 /// on the following line. So let's get the line we're expecting to find
3670 /// if this is the case.
3671 wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
3672 {
3673 wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(caretPosition, true);
3674 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(caretPosition, true);
3675 if (line)
3676 {
3677 wxRichTextRange lineRange = line->GetAbsoluteRange();
3678 if (caretPosition == lineRange.GetStart()-1 &&
3679 (para->GetRange().GetStart() != lineRange.GetStart()))
3680 {
3681 if (!m_caretAtLineStart)
3682 line = GetFocusObject()->GetLineAtPosition(caretPosition-1, true);
3683 }
3684 }
3685 return line;
3686 }
3687
3688
3689 /// Move the caret to the given character position
3690 bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart, wxRichTextParagraphLayoutBox* container)
3691 {
3692 if (GetBuffer().IsDirty())
3693 LayoutContent();
3694
3695 if (!container)
3696 container = GetFocusObject();
3697
3698 if (pos <= container->GetOwnRange().GetEnd())
3699 {
3700 SetCaretPosition(pos, showAtLineStart);
3701
3702 PositionCaret(container);
3703
3704 return true;
3705 }
3706 else
3707 return false;
3708 }
3709
3710 /// Layout the buffer: which we must do before certain operations, such as
3711 /// setting the caret position.
3712 bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
3713 {
3714 if (GetBuffer().IsDirty() || onlyVisibleRect)
3715 {
3716 wxRect availableSpace(GetClientSize());
3717 if (availableSpace.width == 0)
3718 availableSpace.width = 10;
3719 if (availableSpace.height == 0)
3720 availableSpace.height = 10;
3721
3722 int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
3723 if (onlyVisibleRect)
3724 {
3725 flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
3726 availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
3727 }
3728
3729 wxClientDC dc(this);
3730 dc.SetFont(GetFont());
3731
3732 PrepareDC(dc);
3733
3734 GetBuffer().Defragment();
3735 GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
3736 GetBuffer().Layout(dc, availableSpace, flags);
3737 GetBuffer().Invalidate(wxRICHTEXT_NONE);
3738
3739 if (!IsFrozen())
3740 SetupScrollbars();
3741 }
3742
3743 return true;
3744 }
3745
3746 /// Is all of the selection, or the current caret position, bold?
3747 bool wxRichTextCtrl::IsSelectionBold()
3748 {
3749 if (HasSelection())
3750 {
3751 wxRichTextAttr attr;
3752 wxRichTextRange range = GetSelectionRange();
3753 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3754 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
3755
3756 return HasCharacterAttributes(range, attr);
3757 }
3758 else
3759 {
3760 // If no selection, then we need to combine current style with default style
3761 // to see what the effect would be if we started typing.
3762 wxRichTextAttr attr;
3763 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3764
3765 long pos = GetAdjustedCaretPosition(GetCaretPosition());
3766 if (GetStyle(pos, attr))
3767 {
3768 if (IsDefaultStyleShowing())
3769 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3770 return attr.GetFontWeight() == wxFONTWEIGHT_BOLD;
3771 }
3772 }
3773 return false;
3774 }
3775
3776 /// Is all of the selection, or the current caret position, italics?
3777 bool wxRichTextCtrl::IsSelectionItalics()
3778 {
3779 if (HasSelection())
3780 {
3781 wxRichTextRange range = GetSelectionRange();
3782 wxRichTextAttr attr;
3783 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3784 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
3785
3786 return HasCharacterAttributes(range, attr);
3787 }
3788 else
3789 {
3790 // If no selection, then we need to combine current style with default style
3791 // to see what the effect would be if we started typing.
3792 wxRichTextAttr attr;
3793 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3794
3795 long pos = GetAdjustedCaretPosition(GetCaretPosition());
3796 if (GetStyle(pos, attr))
3797 {
3798 if (IsDefaultStyleShowing())
3799 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3800 return attr.GetFontStyle() == wxFONTSTYLE_ITALIC;
3801 }
3802 }
3803 return false;
3804 }
3805
3806 /// Is all of the selection, or the current caret position, underlined?
3807 bool wxRichTextCtrl::IsSelectionUnderlined()
3808 {
3809 if (HasSelection())
3810 {
3811 wxRichTextRange range = GetSelectionRange();
3812 wxRichTextAttr attr;
3813 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3814 attr.SetFontUnderlined(true);
3815
3816 return HasCharacterAttributes(range, attr);
3817 }
3818 else
3819 {
3820 // If no selection, then we need to combine current style with default style
3821 // to see what the effect would be if we started typing.
3822 wxRichTextAttr attr;
3823 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3824 long pos = GetAdjustedCaretPosition(GetCaretPosition());
3825
3826 if (GetStyle(pos, attr))
3827 {
3828 if (IsDefaultStyleShowing())
3829 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3830 return attr.GetFontUnderlined();
3831 }
3832 }
3833 return false;
3834 }
3835
3836 /// Does all of the selection, or the current caret position, have this wxTextAttrEffects flag(s)?
3837 bool wxRichTextCtrl::DoesSelectionHaveTextEffectFlag(int flag)
3838 {
3839 wxRichTextAttr attr;
3840 attr.SetFlags(wxTEXT_ATTR_EFFECTS);
3841 attr.SetTextEffectFlags(flag);
3842 attr.SetTextEffects(flag);
3843
3844 if (HasSelection())
3845 {
3846 return HasCharacterAttributes(GetSelectionRange(), attr);
3847 }
3848 else
3849 {
3850 // If no selection, then we need to combine current style with default style
3851 // to see what the effect would be if we started typing.
3852 long pos = GetAdjustedCaretPosition(GetCaretPosition());
3853 if (GetStyle(pos, attr))
3854 {
3855 if (IsDefaultStyleShowing())
3856 wxRichTextApplyStyle(attr, GetDefaultStyleEx());
3857 return (attr.GetTextEffectFlags() & flag) != 0;
3858 }
3859 }
3860 return false;
3861 }
3862
3863 /// Apply bold to the selection
3864 bool wxRichTextCtrl::ApplyBoldToSelection()
3865 {
3866 wxRichTextAttr attr;
3867 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
3868 attr.SetFontWeight(IsSelectionBold() ? wxFONTWEIGHT_NORMAL : wxFONTWEIGHT_BOLD);
3869
3870 if (HasSelection())
3871 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3872 else
3873 {
3874 wxRichTextAttr current = GetDefaultStyleEx();
3875 current.Apply(attr);
3876 SetAndShowDefaultStyle(current);
3877 }
3878 return true;
3879 }
3880
3881 /// Apply italic to the selection
3882 bool wxRichTextCtrl::ApplyItalicToSelection()
3883 {
3884 wxRichTextAttr attr;
3885 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
3886 attr.SetFontStyle(IsSelectionItalics() ? wxFONTSTYLE_NORMAL : wxFONTSTYLE_ITALIC);
3887
3888 if (HasSelection())
3889 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3890 else
3891 {
3892 wxRichTextAttr current = GetDefaultStyleEx();
3893 current.Apply(attr);
3894 SetAndShowDefaultStyle(current);
3895 }
3896 return true;
3897 }
3898
3899 /// Apply underline to the selection
3900 bool wxRichTextCtrl::ApplyUnderlineToSelection()
3901 {
3902 wxRichTextAttr attr;
3903 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
3904 attr.SetFontUnderlined(!IsSelectionUnderlined());
3905
3906 if (HasSelection())
3907 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3908 else
3909 {
3910 wxRichTextAttr current = GetDefaultStyleEx();
3911 current.Apply(attr);
3912 SetAndShowDefaultStyle(current);
3913 }
3914 return true;
3915 }
3916
3917 /// Apply the wxTextAttrEffects flag(s) to the selection, or the current caret position if there's no selection
3918 bool wxRichTextCtrl::ApplyTextEffectToSelection(int flags)
3919 {
3920 wxRichTextAttr attr;
3921 attr.SetFlags(wxTEXT_ATTR_EFFECTS);
3922 attr.SetTextEffectFlags(flags);
3923 if (!DoesSelectionHaveTextEffectFlag(flags))
3924 attr.SetTextEffects(flags);
3925 else
3926 attr.SetTextEffects(attr.GetTextEffectFlags() & ~flags);
3927
3928 if (HasSelection())
3929 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY);
3930 else
3931 {
3932 wxRichTextAttr current = GetDefaultStyleEx();
3933 current.Apply(attr);
3934 SetAndShowDefaultStyle(current);
3935 }
3936 return true;
3937 }
3938
3939 /// Is all of the selection aligned according to the specified flag?
3940 bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment)
3941 {
3942 wxRichTextRange range;
3943 if (HasSelection())
3944 range = GetSelectionRange();
3945 else
3946 range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+2);
3947
3948 wxRichTextAttr attr;
3949 attr.SetAlignment(alignment);
3950
3951 return HasParagraphAttributes(range, attr);
3952 }
3953
3954 /// Apply alignment to the selection
3955 bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
3956 {
3957 wxRichTextAttr attr;
3958 attr.SetAlignment(alignment);
3959 if (HasSelection())
3960 return SetStyle(GetSelectionRange(), attr);
3961 else
3962 {
3963 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(GetCaretPosition()+1);
3964 if (para)
3965 return SetStyleEx(para->GetRange().FromInternal(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY);
3966 }
3967 return true;
3968 }
3969
3970 /// Apply a named style to the selection
3971 bool wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def)
3972 {
3973 // Flags are defined within each definition, so only certain
3974 // attributes are applied.
3975 wxRichTextAttr attr(GetStyleSheet() ? def->GetStyleMergedWithBase(GetStyleSheet()) : def->GetStyle());
3976
3977 int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_RESET;
3978
3979 if (def->IsKindOf(CLASSINFO(wxRichTextListStyleDefinition)))
3980 {
3981 flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
3982
3983 wxRichTextRange range;
3984
3985 if (HasSelection())
3986 range = GetSelectionRange();
3987 else
3988 {
3989 long pos = GetAdjustedCaretPosition(GetCaretPosition());
3990 range = wxRichTextRange(pos, pos+1);
3991 }
3992
3993 return SetListStyle(range, (wxRichTextListStyleDefinition*) def, flags);
3994 }
3995
3996 bool isPara = false;
3997
3998 // Make sure the attr has the style name
3999 if (def->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition)))
4000 {
4001 isPara = true;
4002 attr.SetParagraphStyleName(def->GetName());
4003
4004 // If applying a paragraph style, we only want the paragraph nodes to adopt these
4005 // attributes, and not the leaf nodes. This will allow the content (e.g. text)
4006 // to change its style independently.
4007 flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY;
4008 }
4009 else
4010 attr.SetCharacterStyleName(def->GetName());
4011
4012 if (HasSelection())
4013 return SetStyleEx(GetSelectionRange(), attr, flags);
4014 else
4015 {
4016 wxRichTextAttr current = GetDefaultStyleEx();
4017 wxRichTextAttr defaultStyle(attr);
4018 if (isPara)
4019 {
4020 // Don't apply extra character styles since they are already implied
4021 // in the paragraph style
4022 defaultStyle.SetFlags(defaultStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER);
4023 }
4024 current.Apply(defaultStyle);
4025 SetAndShowDefaultStyle(current);
4026
4027 // If it's a paragraph style, we want to apply the style to the
4028 // current paragraph even if we didn't select any text.
4029 if (isPara)
4030 {
4031 long pos = GetAdjustedCaretPosition(GetCaretPosition());
4032 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos);
4033 if (para)
4034 {
4035 return SetStyleEx(para->GetRange().FromInternal(), attr, flags);
4036 }
4037 }
4038 return true;
4039 }
4040 }
4041
4042 /// Apply the style sheet to the buffer, for example if the styles have changed.
4043 bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
4044 {
4045 if (!styleSheet)
4046 styleSheet = GetBuffer().GetStyleSheet();
4047 if (!styleSheet)
4048 return false;
4049
4050 if (GetBuffer().ApplyStyleSheet(styleSheet))
4051 {
4052 GetBuffer().Invalidate(wxRICHTEXT_ALL);
4053 Refresh(false);
4054 return true;
4055 }
4056 else
4057 return false;
4058 }
4059
4060 /// Sets the default style to the style under the cursor
4061 bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
4062 {
4063 wxRichTextAttr attr;
4064 attr.SetFlags(wxTEXT_ATTR_CHARACTER);
4065
4066 // If at the start of a paragraph, use the next position.
4067 long pos = GetAdjustedCaretPosition(GetCaretPosition());
4068
4069 if (GetUncombinedStyle(pos, attr))
4070 {
4071 SetDefaultStyle(attr);
4072 return true;
4073 }
4074
4075 return false;
4076 }
4077
4078 /// Returns the first visible position in the current view
4079 long wxRichTextCtrl::GetFirstVisiblePosition() const
4080 {
4081 wxRichTextLine* line = GetFocusObject()->GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y);
4082 if (line)
4083 return line->GetAbsoluteRange().GetStart();
4084 else
4085 return 0;
4086 }
4087
4088 /// Get the first visible point in the window
4089 wxPoint wxRichTextCtrl::GetFirstVisiblePoint() const
4090 {
4091 int ppuX, ppuY;
4092 int startXUnits, startYUnits;
4093
4094 GetScrollPixelsPerUnit(& ppuX, & ppuY);
4095 GetViewStart(& startXUnits, & startYUnits);
4096
4097 return wxPoint(startXUnits * ppuX, startYUnits * ppuY);
4098 }
4099
4100 /// The adjusted caret position is the character position adjusted to take
4101 /// into account whether we're at the start of a paragraph, in which case
4102 /// style information should be taken from the next position, not current one.
4103 long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const
4104 {
4105 wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(caretPos+1);
4106
4107 if (para && (caretPos+1 == para->GetRange().GetStart()))
4108 caretPos ++;
4109 return caretPos;
4110 }
4111
4112 /// Get/set the selection range in character positions. -1, -1 means no selection.
4113 /// The range is in API convention, i.e. a single character selection is denoted
4114 /// by (n, n+1)
4115 wxRichTextRange wxRichTextCtrl::GetSelectionRange() const
4116 {
4117 wxRichTextRange range = GetInternalSelectionRange();
4118 if (range != wxRichTextRange(-2,-2) && range != wxRichTextRange(-1,-1))
4119 range.SetEnd(range.GetEnd() + 1);
4120 return range;
4121 }
4122
4123 void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range)
4124 {
4125 SetSelection(range.GetStart(), range.GetEnd());
4126 }
4127
4128 /// Set list style
4129 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4130 {
4131 return GetFocusObject()->SetListStyle(range.ToInternal(), def, flags, startFrom, specifiedLevel);
4132 }
4133
4134 bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4135 {
4136 return GetFocusObject()->SetListStyle(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
4137 }
4138
4139 /// Clear list for given range
4140 bool wxRichTextCtrl::ClearListStyle(const wxRichTextRange& range, int flags)
4141 {
4142 return GetFocusObject()->ClearListStyle(range.ToInternal(), flags);
4143 }
4144
4145 /// Number/renumber any list elements in the given range
4146 bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4147 {
4148 return GetFocusObject()->NumberList(range.ToInternal(), def, flags, startFrom, specifiedLevel);
4149 }
4150
4151 bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4152 {
4153 return GetFocusObject()->NumberList(range.ToInternal(), defName, flags, startFrom, specifiedLevel);
4154 }
4155
4156 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4157 bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4158 {
4159 return GetFocusObject()->PromoteList(promoteBy, range.ToInternal(), def, flags, specifiedLevel);
4160 }
4161
4162 bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4163 {
4164 return GetFocusObject()->PromoteList(promoteBy, range.ToInternal(), defName, flags, specifiedLevel);
4165 }
4166
4167 /// Deletes the content in the given range
4168 bool wxRichTextCtrl::Delete(const wxRichTextRange& range)
4169 {
4170 return GetFocusObject()->DeleteRangeWithUndo(range.ToInternal(), this, & GetBuffer());
4171 }
4172
4173 const wxArrayString& wxRichTextCtrl::GetAvailableFontNames()
4174 {
4175 if (sm_availableFontNames.GetCount() == 0)
4176 {
4177 sm_availableFontNames = wxFontEnumerator::GetFacenames();
4178 sm_availableFontNames.Sort();
4179 }
4180 return sm_availableFontNames;
4181 }
4182
4183 void wxRichTextCtrl::ClearAvailableFontNames()
4184 {
4185 sm_availableFontNames.Clear();
4186 }
4187
4188 void wxRichTextCtrl::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
4189 {
4190 //wxLogDebug(wxT("wxRichTextCtrl::OnSysColourChanged"));
4191
4192 wxTextAttrEx basicStyle = GetBasicStyle();
4193 basicStyle.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
4194 SetBasicStyle(basicStyle);
4195 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
4196
4197 Refresh();
4198 }
4199
4200 // Refresh the area affected by a selection change
4201 bool wxRichTextCtrl::RefreshForSelectionChange(const wxRichTextSelection& oldSelection, const wxRichTextSelection& newSelection)
4202 {
4203 // If the selection is not part of the focus object, or we have multiple ranges, then the chances are that
4204 // the selection contains whole containers rather than just text, so refresh everything
4205 // for now as it would be hard to compute the rectangle bounding all selections.
4206 // TODO: improve on this.
4207 if ((oldSelection.IsValid() && (oldSelection.GetContainer() != GetFocusObject() || oldSelection.GetCount() > 1)) ||
4208 (newSelection.IsValid() && (newSelection.GetContainer() != GetFocusObject() || newSelection.GetCount() > 1)))
4209 {
4210 Refresh(false);
4211 return true;
4212 }
4213
4214 wxRichTextRange oldRange, newRange;
4215 if (oldSelection.IsValid())
4216 oldRange = oldSelection.GetRange();
4217 else
4218 oldRange = wxRICHTEXT_NO_SELECTION;
4219 if (newSelection.IsValid())
4220 newRange = newSelection.GetRange();
4221 else
4222 newRange = wxRICHTEXT_NO_SELECTION;
4223
4224 // Calculate the refresh rectangle - just the affected lines
4225 long firstPos, lastPos;
4226 if (oldRange.GetStart() == -2 && newRange.GetStart() != -2)
4227 {
4228 firstPos = newRange.GetStart();
4229 lastPos = newRange.GetEnd();
4230 }
4231 else if (oldRange.GetStart() != -2 && newRange.GetStart() == -2)
4232 {
4233 firstPos = oldRange.GetStart();
4234 lastPos = oldRange.GetEnd();
4235 }
4236 else if (oldRange.GetStart() == -2 && newRange.GetStart() == -2)
4237 {
4238 return false;
4239 }
4240 else
4241 {
4242 firstPos = wxMin(oldRange.GetStart(), newRange.GetStart());
4243 lastPos = wxMax(oldRange.GetEnd(), newRange.GetEnd());
4244 }
4245
4246 wxRichTextLine* firstLine = GetFocusObject()->GetLineAtPosition(firstPos);
4247 wxRichTextLine* lastLine = GetFocusObject()->GetLineAtPosition(lastPos);
4248
4249 if (firstLine && lastLine)
4250 {
4251 wxSize clientSize = GetClientSize();
4252 wxPoint pt1 = GetPhysicalPoint(firstLine->GetAbsolutePosition());
4253 wxPoint pt2 = GetPhysicalPoint(lastLine->GetAbsolutePosition()) + wxPoint(0, lastLine->GetSize().y);
4254
4255 pt1.x = 0;
4256 pt1.y = wxMax(0, pt1.y);
4257 pt2.x = 0;
4258 pt2.y = wxMin(clientSize.y, pt2.y);
4259
4260 wxRect rect(pt1, wxSize(clientSize.x, pt2.y - pt1.y));
4261 RefreshRect(rect, false);
4262 }
4263 else
4264 Refresh(false);
4265
4266 return true;
4267 }
4268
4269 // margins functions
4270 bool wxRichTextCtrl::DoSetMargins(const wxPoint& pt)
4271 {
4272 GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(pt.x, wxTEXT_ATTR_UNITS_PIXELS);
4273 GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(pt.x, wxTEXT_ATTR_UNITS_PIXELS);
4274 GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(pt.y, wxTEXT_ATTR_UNITS_PIXELS);
4275 GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(pt.y, wxTEXT_ATTR_UNITS_PIXELS);
4276
4277 return true;
4278 }
4279
4280 wxPoint wxRichTextCtrl::DoGetMargins() const
4281 {
4282 return wxPoint(GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue(),
4283 GetBuffer().GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue());
4284 }
4285
4286 bool wxRichTextCtrl::SetFocusObject(wxRichTextParagraphLayoutBox* obj, bool setCaretPosition)
4287 {
4288 if (obj && !obj->AcceptsFocus())
4289 return false;
4290
4291 wxRichTextParagraphLayoutBox* oldContainer = GetFocusObject();
4292 bool changingContainer = (m_focusObject != obj);
4293
4294 if (changingContainer && HasSelection())
4295 SelectNone();
4296
4297 m_focusObject = obj;
4298
4299 if (!obj)
4300 m_focusObject = & m_buffer;
4301
4302 if (setCaretPosition && changingContainer)
4303 {
4304 m_selection.Reset();
4305 m_selectionAnchor = -2;
4306 m_selectionAnchorObject = NULL;
4307 m_selectionState = wxRichTextCtrlSelectionState_Normal;
4308
4309 long pos = -1;
4310
4311 m_caretAtLineStart = false;
4312 MoveCaret(pos, m_caretAtLineStart);
4313 SetDefaultStyleToCursorStyle();
4314
4315 wxRichTextEvent cmdEvent(
4316 wxEVT_COMMAND_RICHTEXT_FOCUS_OBJECT_CHANGED,
4317 GetId());
4318 cmdEvent.SetEventObject(this);
4319 cmdEvent.SetPosition(m_caretPosition+1);
4320 cmdEvent.SetOldContainer(oldContainer);
4321 cmdEvent.SetContainer(m_focusObject);
4322
4323 GetEventHandler()->ProcessEvent(cmdEvent);
4324 }
4325 return true;
4326 }
4327
4328 #if wxUSE_DRAG_AND_DROP
4329 void wxRichTextCtrl::OnDrop(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxDragResult def, wxDataObject* DataObj)
4330 {
4331 m_preDrag = false;
4332
4333 if ((def != wxDragCopy) && (def != wxDragMove))
4334 {
4335 return;
4336 }
4337
4338 if (!GetSelection().IsValid())
4339 {
4340 return;
4341 }
4342
4343 wxRichTextParagraphLayoutBox* originContainer = GetSelection().GetContainer();
4344 wxRichTextParagraphLayoutBox* destContainer = GetFocusObject(); // This will be the drop container, not necessarily the same as the origin one
4345
4346
4347 wxRichTextBuffer* richTextBuffer = ((wxRichTextBufferDataObject*)DataObj)->GetRichTextBuffer();
4348 if (richTextBuffer)
4349 {
4350 long position = GetCaretPosition();
4351 wxRichTextRange selectionrange = GetInternalSelectionRange();
4352 if (selectionrange.Contains(position) && (def == wxDragMove))
4353 {
4354 // It doesn't make sense to move onto itself
4355 return;
4356 }
4357
4358 // If we're moving, and the data is being moved forward, we need to drop first, then delete the selection
4359 // If moving backwards, we need to delete then drop. If we're copying (or doing nothing) we don't delete anyway
4360 bool DeleteAfter = (def == wxDragMove) && (position > selectionrange.GetEnd());
4361 if ((def == wxDragMove) && !DeleteAfter)
4362 {
4363 // We can't use e.g. DeleteSelectedContent() as it uses the focus container
4364 originContainer->DeleteRangeWithUndo(selectionrange, this, &GetBuffer());
4365 }
4366
4367 destContainer->InsertParagraphsWithUndo(position+1, *richTextBuffer, this, &GetBuffer(), 0);
4368 ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
4369
4370 delete richTextBuffer;
4371
4372 if (DeleteAfter)
4373 {
4374 // We can't use e.g. DeleteSelectedContent() as it uses the focus container
4375 originContainer->DeleteRangeWithUndo(selectionrange, this, &GetBuffer());
4376 }
4377
4378
4379 SelectNone();
4380 Refresh();
4381 }
4382 }
4383 #endif // wxUSE_DRAG_AND_DROP
4384
4385
4386 #if wxUSE_DRAG_AND_DROP
4387 bool wxRichTextDropSource::GiveFeedback(wxDragResult WXUNUSED(effect))
4388 {
4389 wxCHECK_MSG(m_rtc, false, wxT("NULL m_rtc"));
4390
4391 long position = 0;
4392 int hit = 0;
4393 wxRichTextObject* hitObj = NULL;
4394 wxRichTextParagraphLayoutBox* container = m_rtc->FindContainerAtPoint(m_rtc->ScreenToClient(wxGetMousePosition()), position, hit, hitObj);
4395
4396 if (!(hit & wxRICHTEXT_HITTEST_NONE) && container && container->AcceptsFocus())
4397 {
4398 m_rtc->StoreFocusObject(container);
4399 m_rtc->SetCaretPositionAfterClick(container, position, hit);
4400 }
4401
4402 return false; // so that the base-class sets a cursor
4403 }
4404 #endif // wxUSE_DRAG_AND_DROP
4405
4406
4407 #if wxRICHTEXT_USE_OWN_CARET
4408
4409 // ----------------------------------------------------------------------------
4410 // initialization and destruction
4411 // ----------------------------------------------------------------------------
4412
4413 void wxRichTextCaret::Init()
4414 {
4415 m_hasFocus = true;
4416
4417 m_xOld =
4418 m_yOld = -1;
4419 m_richTextCtrl = NULL;
4420 m_needsUpdate = false;
4421 m_flashOn = true;
4422 }
4423
4424 wxRichTextCaret::~wxRichTextCaret()
4425 {
4426 if (m_timer.IsRunning())
4427 m_timer.Stop();
4428 }
4429
4430 // ----------------------------------------------------------------------------
4431 // showing/hiding/moving the caret (base class interface)
4432 // ----------------------------------------------------------------------------
4433
4434 void wxRichTextCaret::DoShow()
4435 {
4436 m_flashOn = true;
4437
4438 if (!m_timer.IsRunning())
4439 m_timer.Start(GetBlinkTime());
4440
4441 Refresh();
4442 }
4443
4444 void wxRichTextCaret::DoHide()
4445 {
4446 if (m_timer.IsRunning())
4447 m_timer.Stop();
4448
4449 Refresh();
4450 }
4451
4452 void wxRichTextCaret::DoMove()
4453 {
4454 if (IsVisible())
4455 {
4456 Refresh();
4457
4458 if (m_xOld != -1 && m_yOld != -1)
4459 {
4460 if (m_richTextCtrl)
4461 {
4462 wxRect rect(GetPosition(), GetSize());
4463 m_richTextCtrl->RefreshRect(rect, false);
4464 }
4465 }
4466 }
4467
4468 m_xOld = m_x;
4469 m_yOld = m_y;
4470 }
4471
4472 void wxRichTextCaret::DoSize()
4473 {
4474 int countVisible = m_countVisible;
4475 if (countVisible > 0)
4476 {
4477 m_countVisible = 0;
4478 DoHide();
4479 }
4480
4481 if (countVisible > 0)
4482 {
4483 m_countVisible = countVisible;
4484 DoShow();
4485 }
4486 }
4487
4488 // ----------------------------------------------------------------------------
4489 // handling the focus
4490 // ----------------------------------------------------------------------------
4491
4492 void wxRichTextCaret::OnSetFocus()
4493 {
4494 m_hasFocus = true;
4495
4496 if ( IsVisible() )
4497 Refresh();
4498 }
4499
4500 void wxRichTextCaret::OnKillFocus()
4501 {
4502 m_hasFocus = false;
4503 }
4504
4505 // ----------------------------------------------------------------------------
4506 // drawing the caret
4507 // ----------------------------------------------------------------------------
4508
4509 void wxRichTextCaret::Refresh()
4510 {
4511 if (m_richTextCtrl)
4512 {
4513 wxRect rect(GetPosition(), GetSize());
4514 m_richTextCtrl->RefreshRect(rect, false);
4515 }
4516 }
4517
4518 void wxRichTextCaret::DoDraw(wxDC *dc)
4519 {
4520 dc->SetPen( *wxBLACK_PEN );
4521
4522 dc->SetBrush(*(m_hasFocus ? wxBLACK_BRUSH : wxTRANSPARENT_BRUSH));
4523 dc->SetPen(*wxBLACK_PEN);
4524
4525 wxPoint pt(m_x, m_y);
4526
4527 if (m_richTextCtrl)
4528 {
4529 pt = m_richTextCtrl->GetLogicalPoint(pt);
4530 }
4531 if (IsVisible() && m_flashOn)
4532 dc->DrawRectangle(pt.x, pt.y, m_width, m_height);
4533 }
4534
4535 void wxRichTextCaret::Notify()
4536 {
4537 m_flashOn = !m_flashOn;
4538 Refresh();
4539 }
4540
4541 void wxRichTextCaretTimer::Notify()
4542 {
4543 m_caret->Notify();
4544 }
4545 #endif
4546 // wxRICHTEXT_USE_OWN_CARET
4547
4548 // Add an item
4549 bool wxRichTextContextMenuPropertiesInfo::AddItem(const wxString& label, wxRichTextObject* obj)
4550 {
4551 if (GetCount() < 3)
4552 {
4553 m_labels.Add(label);
4554 m_objects.Add(obj);
4555 return true;
4556 }
4557 else
4558 return false;
4559 }
4560
4561 // Returns number of menu items were added.
4562 int wxRichTextContextMenuPropertiesInfo::AddMenuItems(wxMenu* menu, int startCmd) const
4563 {
4564 wxMenuItem* item = menu->FindItem(startCmd);
4565 // If none of the standard properties identifiers are in the menu, add them if necessary.
4566 // If no items to add, just set the text to something generic
4567 if (GetCount() == 0)
4568 {
4569 if (item)
4570 {
4571 menu->SetLabel(startCmd, _("&Properties"));
4572
4573 // Delete the others if necessary
4574 int i;
4575 for (i = startCmd+1; i < startCmd+3; i++)
4576 {
4577 if (menu->FindItem(i))
4578 {
4579 menu->Delete(i);
4580 }
4581 }
4582 }
4583 }
4584 else
4585 {
4586 int i;
4587 int pos = -1;
4588 // Find the position of the first properties item
4589 for (i = 0; i < (int) menu->GetMenuItemCount(); i++)
4590 {
4591 wxMenuItem* item = menu->FindItemByPosition(i);
4592 if (item && item->GetId() == startCmd)
4593 {
4594 pos = i;
4595 break;
4596 }
4597 }
4598
4599 if (pos != -1)
4600 {
4601 int insertBefore = pos+1;
4602 for (i = startCmd; i < startCmd+GetCount(); i++)
4603 {
4604 if (menu->FindItem(i))
4605 {
4606 menu->SetLabel(i, m_labels[i - startCmd]);
4607 }
4608 else
4609 {
4610 if (insertBefore >= (int) menu->GetMenuItemCount())
4611 menu->Append(i, m_labels[i - startCmd]);
4612 else
4613 menu->Insert(insertBefore, i, m_labels[i - startCmd]);
4614 }
4615 insertBefore ++;
4616 }
4617
4618 // Delete any old items still left on the menu
4619 for (i = startCmd + GetCount(); i < startCmd+3; i++)
4620 {
4621 if (menu->FindItem(i))
4622 {
4623 menu->Delete(i);
4624 }
4625 }
4626 }
4627 else
4628 {
4629 // No existing property identifiers were found, so append to the end of the menu.
4630 menu->AppendSeparator();
4631 for (i = startCmd; i < startCmd+GetCount(); i++)
4632 {
4633 menu->Append(i, m_labels[i - startCmd]);
4634 }
4635 }
4636 }
4637
4638 return GetCount();
4639 }
4640
4641 // Add appropriate menu items for the current container and clicked on object
4642 // (and container's parent, if appropriate).
4643 int wxRichTextContextMenuPropertiesInfo::AddItems(wxRichTextObject* container, wxRichTextObject* obj)
4644 {
4645 Clear();
4646 if (obj && obj->CanEditProperties())
4647 AddItem(obj->GetPropertiesMenuLabel(), obj);
4648
4649 if (container && container != obj && container->CanEditProperties() && m_labels.Index(container->GetPropertiesMenuLabel()) == wxNOT_FOUND)
4650 AddItem(container->GetPropertiesMenuLabel(), container);
4651
4652 if (container && container->GetParent() && container->GetParent()->CanEditProperties() && m_labels.Index(container->GetParent()->GetPropertiesMenuLabel()) == wxNOT_FOUND)
4653 AddItem(container->GetParent()->GetPropertiesMenuLabel(), container->GetParent());
4654
4655 return GetCount();
4656 }
4657
4658 #endif
4659 // wxUSE_RICHTEXT