+/// Delete content if there is a selection, e.g. when pressing a key.
+bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
+{
+ if (HasSelection())
+ {
+ long pos = m_selection.GetRange().GetStart();
+ wxRichTextRange range = m_selection.GetRange();
+
+ // SelectAll causes more to be selected than doing it interactively,
+ // and causes a new paragraph to be inserted. So for multiline buffers,
+ // don't delete the final position.
+ if (range.GetEnd() == GetLastPosition() && GetNumberOfLines() > 0)
+ range.SetEnd(range.GetEnd()-1);
+
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ m_selection.Reset();
+ m_selectionState = wxRichTextCtrlSelectionState_Normal;
+
+ if (newPos)
+ *newPos = pos-1;
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Keyboard navigation
+
+/*
+
+Left: left one character
+Right: right one character
+Up: up one line
+Down: down one line
+Ctrl-Left: left one word
+Ctrl-Right: right one word
+Ctrl-Up: previous paragraph start
+Ctrl-Down: next start of paragraph
+Home: start of line
+End: end of line
+Ctrl-Home: start of document
+Ctrl-End: end of document
+Page-Up: Up a screen
+Page-Down: Down a screen
+
+Maybe:
+
+Ctrl-Alt-PgUp: Start of window
+Ctrl-Alt-PgDn: End of window
+F8: Start selection mode
+Esc: End selection mode
+
+Adding Shift does the above but starts/extends selection.
+
+
+ */
+
+bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags)
+{
+ bool success = false;
+
+ if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = WordRight(1, flags);
+ else
+ success = MoveRight(1, flags);
+ }
+ else if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = WordLeft(1, flags);
+ else
+ success = MoveLeft(1, flags);
+ }
+ else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveToParagraphStart(flags);
+ else
+ success = MoveUp(1, flags);
+ }
+ else if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveToParagraphEnd(flags);
+ else
+ success = MoveDown(1, flags);
+ }
+ else if (keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP)
+ {
+ success = PageUp(1, flags);
+ }
+ else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
+ {
+ success = PageDown(1, flags);
+ }
+ else if (keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveHome(flags);
+ else
+ success = MoveToLineStart(flags);
+ }
+ else if (keyCode == WXK_END || keyCode == WXK_NUMPAD_END)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveEnd(flags);
+ else
+ success = MoveToLineEnd(flags);
+ }
+
+ if (success)
+ {
+ ScrollIntoView(m_caretPosition, keyCode);
+ SetDefaultStyleToCursorStyle();
+ }
+
+ return success;
+}
+
+/// Extend the selection. Selections are in caret positions.
+bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
+{
+ if (flags & wxRICHTEXT_SHIFT_DOWN)
+ {
+ if (oldPos == newPos)
+ return false;
+
+ wxRichTextSelection oldSelection = m_selection;
+
+ m_selection.SetContainer(GetFocusObject());
+
+ wxRichTextRange oldRange;
+ if (m_selection.IsValid())
+ oldRange = m_selection.GetRange();
+ else
+ oldRange = wxRICHTEXT_NO_SELECTION;
+ wxRichTextRange newRange;
+
+ // If not currently selecting, start selecting
+ if (oldRange.GetStart() == -2)
+ {
+ m_selectionAnchor = oldPos;
+
+ if (oldPos > newPos)
+ newRange.SetRange(newPos+1, oldPos);
+ else
+ newRange.SetRange(oldPos+1, newPos);
+ }
+ else
+ {
+ // Always ensure that the selection range start is greater than
+ // the end.
+ if (newPos > m_selectionAnchor)
+ newRange.SetRange(m_selectionAnchor+1, newPos);
+ else if (newPos == m_selectionAnchor)
+ newRange = wxRichTextRange(-2, -2);
+ else
+ newRange.SetRange(newPos+1, m_selectionAnchor);
+ }
+
+ m_selection.SetRange(newRange);
+
+ RefreshForSelectionChange(oldSelection, m_selection);
+
+ if (newRange.GetStart() > newRange.GetEnd())
+ {
+ wxLogDebug(wxT("Strange selection range"));
+ }
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Scroll into view, returning true if we scrolled.
+/// This takes a _caret_ position.
+bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
+
+ if (!line)
+ return false;
+
+ int ppuX, ppuY;
+ GetScrollPixelsPerUnit(& ppuX, & ppuY);
+
+ int startXUnits, startYUnits;
+ GetViewStart(& startXUnits, & startYUnits);
+ int startY = startYUnits * ppuY;
+
+ int sx = 0, sy = 0;
+ GetVirtualSize(& sx, & sy);
+ int sxUnits = 0;
+ int syUnits = 0;
+ if (ppuY != 0)
+ syUnits = sy/ppuY;
+
+ wxRect rect = GetScaledRect(line->GetRect());
+
+ bool scrolled = false;
+
+ wxSize clientSize = GetClientSize();
+
+ int leftMargin, rightMargin, topMargin, bottomMargin;
+
+ {
+ wxClientDC dc(this);
+ wxRichTextObject::GetTotalMargin(dc, & GetBuffer(), GetBuffer().GetAttributes(), leftMargin, rightMargin,
+ topMargin, bottomMargin);
+ }
+ clientSize.y -= (int) (0.5 + bottomMargin * GetScale());
+
+ if (GetWindowStyle() & wxRE_CENTRE_CARET)
+ {
+ int y = rect.y - GetClientSize().y/2;
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
+ if (y >= 0 && (y + clientSize.y) < (int) (0.5 + GetBuffer().GetCachedSize().y * GetScale()))
+ {
+ if (startYUnits != yUnits)
+ {
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
+ scrolled = true;
+ }
+#if !wxRICHTEXT_USE_OWN_CARET
+ if (scrolled)
+#endif
+ PositionCaret();
+
+ return scrolled;
+ }
+ }
+
+ // Going down
+ if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN ||
+ keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT ||
+ keyCode == WXK_END || keyCode == WXK_NUMPAD_END ||
+ keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN)
+ {
+ if ((rect.y + rect.height) > (clientSize.y + startY))
+ {
+ // Make it scroll so this item is at the bottom
+ // of the window
+ int y = rect.y - (clientSize.y - rect.height);
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
+
+ // If we're still off the screen, scroll another line down
+ if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
+ yUnits ++;
+
+ if (startYUnits != yUnits)
+ {
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
+ scrolled = true;
+ }
+ }
+ else if (rect.y < (startY + (int) (0.5 + GetBuffer().GetTopMargin() * GetScale())))
+ {
+ // Make it scroll so this item is at the top
+ // of the window
+ int y = rect.y - (int) (0.5 + GetBuffer().GetTopMargin() * GetScale());
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
+
+ if (startYUnits != yUnits)
+ {
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
+ scrolled = true;
+ }
+ }
+ }
+ // Going up
+ else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP ||
+ keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT ||
+ keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME ||
+ keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP )
+ {
+ if (rect.y < (startY + (int) (0.5 + GetBuffer().GetBottomMargin() * GetScale())))
+ {
+ // Make it scroll so this item is at the top
+ // of the window
+ int y = rect.y - (int) (0.5 + GetBuffer().GetTopMargin() * GetScale());
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
+
+ if (startYUnits != yUnits)
+ {
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
+ scrolled = true;
+ }
+ }
+ else if ((rect.y + rect.height) > (clientSize.y + startY))
+ {
+ // Make it scroll so this item is at the bottom
+ // of the window
+ int y = rect.y - (clientSize.y - rect.height);
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
+
+ // If we're still off the screen, scroll another line down
+ if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
+ yUnits ++;
+
+ if (startYUnits != yUnits)
+ {
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
+ scrolled = true;
+ }
+ }
+ }
+
+#if !wxRICHTEXT_USE_OWN_CARET
+ if (scrolled)
+#endif
+ PositionCaret();
+
+ return scrolled;
+}
+
+/// Is the given position visible on the screen?
+bool wxRichTextCtrl::IsPositionVisible(long pos) const
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
+
+ if (!line)
+ return false;
+
+ int ppuX, ppuY;
+ GetScrollPixelsPerUnit(& ppuX, & ppuY);
+
+ int startX, startY;
+ GetViewStart(& startX, & startY);
+ startX = 0;
+ startY = startY * ppuY;
+
+ wxRect rect = GetScaledRect(line->GetRect());
+ wxSize clientSize = GetClientSize();
+ clientSize.y -= (int) (0.5 + GetBuffer().GetBottomMargin() * GetScale());
+
+ return (rect.GetTop() >= (startY + (int) (0.5 + GetBuffer().GetTopMargin() * GetScale()))) &&
+ (rect.GetBottom() <= (startY + clientSize.y));
+}
+
+void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
+{
+ m_caretPosition = position;
+ m_caretAtLineStart = showAtLineStart;
+}
+
+/// Move caret one visual step forward: this may mean setting a flag
+/// and keeping the same position if we're going from the end of one line
+/// to the start of the next, which may be the exact same caret position.
+void wxRichTextCtrl::MoveCaretForward(long oldPosition)
+{
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
+
+ // Only do the check if we're not at the end of the paragraph (where things work OK
+ // anyway)
+ if (para && (oldPosition != para->GetRange().GetEnd() - 1))
+ {
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
+
+ if (line)
+ {
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
+ // We're at the end of a line. See whether we need to
+ // stay at the same actual caret position but change visual
+ // position, or not.
+ if (oldPosition == lineRange.GetEnd())
+ {
+ if (m_caretAtLineStart)
+ {
+ // We're already at the start of the line, so actually move on now.
+ m_caretPosition = oldPosition + 1;
+ m_caretAtLineStart = false;
+ }
+ else
+ {
+ // We're showing at the end of the line, so keep to
+ // the same position but indicate that we're to show
+ // at the start of the next line.
+ m_caretPosition = oldPosition;
+ m_caretAtLineStart = true;
+ }
+ SetDefaultStyleToCursorStyle();
+ return;
+ }
+ }
+ }
+ m_caretPosition ++;
+ SetDefaultStyleToCursorStyle();
+}
+
+/// Move caret one visual step backward: this may mean setting a flag
+/// and keeping the same position if we're going from the end of one line
+/// to the start of the next, which may be the exact same caret position.
+void wxRichTextCtrl::MoveCaretBack(long oldPosition)
+{
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(oldPosition);
+
+ // Only do the check if we're not at the start of the paragraph (where things work OK
+ // anyway)
+ if (para && (oldPosition != para->GetRange().GetStart()))
+ {
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(oldPosition);
+
+ if (line)
+ {
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
+ // We're at the start of a line. See whether we need to
+ // stay at the same actual caret position but change visual
+ // position, or not.
+ if (oldPosition == lineRange.GetStart())
+ {
+ m_caretPosition = oldPosition-1;
+ m_caretAtLineStart = true;
+ return;
+ }
+ else if (oldPosition == lineRange.GetEnd())
+ {
+ if (m_caretAtLineStart)
+ {
+ // We're at the start of the line, so keep the same caret position
+ // but clear the start-of-line flag.
+ m_caretPosition = oldPosition;
+ m_caretAtLineStart = false;
+ }
+ else
+ {
+ // We're showing at the end of the line, so go back
+ // to the previous character position.
+ m_caretPosition = oldPosition - 1;
+ }
+ SetDefaultStyleToCursorStyle();
+ return;
+ }
+ }
+ }
+ m_caretPosition --;
+ SetDefaultStyleToCursorStyle();
+}
+
+/// Move right
+bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
+{
+ long endPos = GetFocusObject()->GetOwnRange().GetEnd();
+
+ if (m_caretPosition + noPositions < endPos)
+ {
+ long oldPos = m_caretPosition;
+ long newPos = m_caretPosition + noPositions;
+
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ // Determine by looking at oldPos and m_caretPosition whether
+ // we moved from the end of a line to the start of the next line, in which case
+ // we want to adjust the caret position such that it is positioned at the
+ // start of the next line, rather than jumping past the first character of the
+ // line.
+ if (noPositions == 1)
+ MoveCaretForward(oldPos);
+ else
+ SetCaretPosition(newPos);
+
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move left
+bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)