+ 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)
+{
+ long startPos = -1;
+
+ if (m_caretPosition > startPos - noPositions + 1)
+ {
+ long oldPos = m_caretPosition;
+ long newPos = m_caretPosition - noPositions;
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ if (noPositions == 1)
+ MoveCaretBack(oldPos);
+ else
+ SetCaretPosition(newPos);
+
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+// Find the caret position for the combination of hit-test flags and character position.
+// Returns the caret position and also an indication of where to place the caret (caretLineStart)
+// since this is ambiguous (same position used for end of line and start of next).
+long wxRichTextCtrl::FindCaretPositionForCharacterPosition(long position, int hitTestFlags, wxRichTextParagraphLayoutBox* container,
+ bool& caretLineStart)
+{
+ // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
+ // we want to be at the end of the last line but with m_caretAtLineStart set to true,
+ // so we view the caret at the start of the line.
+ caretLineStart = false;
+ long caretPosition = position;
+
+ if (hitTestFlags & wxRICHTEXT_HITTEST_BEFORE)
+ {
+ wxRichTextLine* thisLine = container->GetLineAtPosition(position-1);
+ wxRichTextRange lineRange;
+ if (thisLine)
+ lineRange = thisLine->GetAbsoluteRange();
+
+ if (thisLine && (position-1) == lineRange.GetEnd())
+ {
+ caretPosition --;
+ caretLineStart = true;
+ }
+ else
+ {
+ wxRichTextParagraph* para = container->GetParagraphAtPosition(position);
+ if (para && para->GetRange().GetStart() == position)
+ caretPosition --;
+ }
+ }
+ return caretPosition;
+}
+
+/// Move up
+bool wxRichTextCtrl::MoveUp(int noLines, int flags)
+{
+ return MoveDown(- noLines, flags);
+}
+
+/// Move up
+bool wxRichTextCtrl::MoveDown(int noLines, int flags)
+{
+ if (!GetCaret())
+ return false;
+
+ long lineNumber = GetFocusObject()->GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
+ wxPoint pt = GetCaret()->GetPosition();
+ long newLine = lineNumber + noLines;
+ bool notInThisObject = false;
+
+ if (lineNumber != -1)
+ {
+ if (noLines > 0)
+ {
+ long lastLine = GetFocusObject()->GetVisibleLineNumber(GetFocusObject()->GetOwnRange().GetEnd());
+ if (newLine > lastLine)
+ notInThisObject = true;
+ }
+ else
+ {
+ if (newLine < 0)
+ notInThisObject = true;
+ }
+ }
+
+ wxRichTextParagraphLayoutBox* container = GetFocusObject();
+ int hitTestFlags = wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS|wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS|wxRICHTEXT_HITTEST_HONOUR_ATOMIC;
+
+ bool lineIsEmpty = false;
+ if (notInThisObject)
+ {
+ // If we know we're navigating out of the current object,
+ // try to find an object anywhere in the buffer at the new position (up or down a bit)
+ container = & GetBuffer();
+ hitTestFlags &= ~wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS;
+
+ if (noLines > 0) // going down
+ {
+ pt.y = GetFocusObject()->GetPosition().y + GetFocusObject()->GetCachedSize().y + 2;
+ }
+ else // going up
+ {
+ pt.y = GetFocusObject()->GetPosition().y - 2;
+ }
+ }
+ else
+ {
+ wxRichTextLine* lineObj = GetFocusObject()->GetLineForVisibleLineNumber(newLine);
+ if (lineObj)
+ {
+ pt.y = lineObj->GetAbsolutePosition().y + 2;
+ if (lineObj->GetRange().GetStart() == lineObj->GetRange().GetEnd())
+ lineIsEmpty = true;
+ }
+ else
+ return false;
+ }
+
+ long newPos = 0;
+ wxClientDC dc(this);
+ PrepareDC(dc);
+ dc.SetFont(GetFont());
+
+ wxRichTextObject* hitObj = NULL;
+ wxRichTextObject* contextObj = NULL;
+ wxRichTextDrawingContext context(& GetBuffer());
+ int hitTest = container->HitTest(dc, context, pt, newPos, & hitObj, & contextObj, hitTestFlags);
+
+ if (hitObj &&
+ ((hitTest & wxRICHTEXT_HITTEST_NONE) == 0) &&
+ (! (hitObj == (& m_buffer) && ((hitTest & wxRICHTEXT_HITTEST_OUTSIDE) != 0))) // outside the buffer counts as 'do nothing'
+ )
+ {
+ if (notInThisObject)
+ {
+ wxRichTextParagraphLayoutBox* actualContainer = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
+ if (actualContainer && actualContainer != GetFocusObject() && actualContainer->AcceptsFocus())
+ {
+ SetFocusObject(actualContainer, false /* don't set caret position yet */);
+
+ container = actualContainer;
+ }
+ }
+
+ bool caretLineStart = true;
+
+ // If the line is empty, there is only one possible position for the caret,
+ // so force the 'before' state so FindCaretPositionForCharacterPosition doesn't
+ // just return the same position.
+ if (lineIsEmpty)
+ {
+ hitTest &= ~wxRICHTEXT_HITTEST_AFTER;
+ hitTest |= wxRICHTEXT_HITTEST_BEFORE;
+ }
+ long caretPosition = FindCaretPositionForCharacterPosition(newPos, hitTest, container, caretLineStart);
+ long newSelEnd = caretPosition;
+ bool extendSel;
+
+ if (notInThisObject)
+ extendSel = false;
+ else
+ extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
+
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(caretPosition, caretLineStart);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the end of the paragraph
+bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
+{
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
+ if (para)
+ {
+ long newPos = para->GetRange().GetEnd() - 1;
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(newPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the paragraph
+bool wxRichTextCtrl::MoveToParagraphStart(int flags)
+{
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
+ if (para)
+ {
+ long newPos = para->GetRange().GetStart() - 1;
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(newPos, true);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the end of the line
+bool wxRichTextCtrl::MoveToLineEnd(int flags)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+
+ if (line)
+ {
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ long newPos = lineRange.GetEnd();
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(newPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the line
+bool wxRichTextCtrl::MoveToLineStart(int flags)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+ if (line)
+ {
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ long newPos = lineRange.GetStart()-1;
+
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(line);
+
+ SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the buffer
+bool wxRichTextCtrl::MoveHome(int flags)
+{
+ if (m_caretPosition != -1)
+ {
+ bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(-1);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move to the end of the buffer
+bool wxRichTextCtrl::MoveEnd(int flags)
+{
+ long endPos = GetFocusObject()->GetOwnRange().GetEnd()-1;
+
+ if (m_caretPosition != endPos)
+ {
+ bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(endPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move noPages pages up
+bool wxRichTextCtrl::PageUp(int noPages, int flags)
+{
+ return PageDown(- noPages, flags);
+}
+
+/// Move noPages pages down
+bool wxRichTextCtrl::PageDown(int noPages, int flags)
+{
+ // Calculate which line occurs noPages * screen height further down.
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+ if (line)
+ {
+ wxSize clientSize = GetClientSize();
+ int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
+
+ wxRichTextLine* newLine = GetFocusObject()->GetLineAtYPosition(newY);
+ if (newLine)
+ {
+ wxRichTextRange lineRange = newLine->GetAbsoluteRange();
+ long pos = lineRange.GetStart()-1;
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphForLine(newLine);
+
+ bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool wxRichTextCtrlIsWhitespace(const wxString& str)
+{
+ return str == wxT(" ") || str == wxT("\t") || (!str.empty() && (str[0] == (wxChar) 160));
+}
+
+// Finds the caret position for the next word
+long wxRichTextCtrl::FindNextWordPosition(int direction) const
+{
+ long endPos = GetFocusObject()->GetOwnRange().GetEnd();
+
+ if (direction > 0)
+ {
+ long i = m_caretPosition+1+direction; // +1 for conversion to character pos
+
+ // First skip current text to space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
+ if (line && (i == line->GetAbsoluteRange().GetEnd()))
+ {
+ break;
+ }
+ else if (!wxRichTextCtrlIsWhitespace(text) && !text.empty())
+ i += direction;
+ else
+ {
+ break;
+ }
+ }
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
+ if (line && (i == line->GetAbsoluteRange().GetEnd()))
+ return wxMax(-1, i);
+
+ if (text.empty()) // End of paragraph, or maybe an image
+ return wxMax(-1, i - 1);
+ else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
+ i += direction;
+ else
+ {
+ // Convert to caret position
+ return wxMax(-1, i - 1);
+ }
+ }
+ if (i >= endPos)
+ return endPos-1;
+ return i-1;
+ }
+ else
+ {
+ long i = m_caretPosition;
+
+ // First skip white space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
+
+ if (text.empty() || (line && (i == line->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image
+ break;
+ else if (wxRichTextCtrlIsWhitespace(text) || text.empty())
+ i += direction;
+ else
+ break;
+ }
+ // Next skip current text to space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(i, i));
+ wxRichTextLine* line = GetFocusObject()->GetLineAtPosition(i, false);
+ if (line && line->GetAbsoluteRange().GetStart() == i)
+ return i-1;
+
+ if (!wxRichTextCtrlIsWhitespace(text) /* && !text.empty() */)
+ i += direction;
+ else
+ {
+ return i;
+ }
+ }
+ if (i < -1)
+ return -1;
+ return i;
+ }
+}
+
+/// Move n words left
+bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
+{
+ long pos = FindNextWordPosition(-1);
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
+
+ bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != pos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Move n words right
+bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
+{
+ long pos = FindNextWordPosition(1);
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(pos, true);
+
+ bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != pos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Sizing
+void wxRichTextCtrl::OnSize(wxSizeEvent& event)
+{
+ // Only do sizing optimization for large buffers
+ if (GetBuffer().GetOwnRange().GetEnd() > m_delayedLayoutThreshold)
+ {
+ m_fullLayoutRequired = true;
+ m_fullLayoutTime = wxGetLocalTimeMillis();
+ m_fullLayoutSavedPosition = GetFirstVisiblePosition();
+ LayoutContent(true /* onlyVisibleRect */);
+ }
+ else
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
+
+#if wxRICHTEXT_BUFFERED_PAINTING
+ RecreateBuffer();
+#endif
+
+ event.Skip();
+}
+
+// Force any pending layout due to large buffer
+void wxRichTextCtrl::ForceDelayedLayout()
+{
+ if (m_fullLayoutRequired)
+ {
+ m_fullLayoutRequired = false;
+ m_fullLayoutTime = 0;
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
+ ShowPosition(m_fullLayoutSavedPosition);
+ Refresh(false);
+ Update();
+ }
+}
+
+/// Idle-time processing
+void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
+{
+#if wxRICHTEXT_USE_OWN_CARET
+ if (((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
+ {
+ ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(false);
+ PositionCaret();
+ GetCaret()->Show();
+ }
+#endif
+
+ const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
+
+ if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
+ {
+ m_fullLayoutRequired = false;
+ m_fullLayoutTime = 0;
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
+ ShowPosition(m_fullLayoutSavedPosition);
+ Refresh(false);
+ }
+
+ if (m_caretPositionForDefaultStyle != -2)