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