+ m_caretPosition = -1;
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+ }
+
+ ScrollIntoView(m_caretPosition, WXK_LEFT);
+
+ // Always send this event; wxEVT_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
+ {
+ wxRichTextEvent cmdEvent(
+ wxEVT_RICHTEXT_DELETE,
+ GetId());
+ cmdEvent.SetEventObject(this);
+ cmdEvent.SetFlags(flags);
+ cmdEvent.SetPosition(m_caretPosition+1);
+ cmdEvent.SetContainer(GetFocusObject());
+ GetEventHandler()->ProcessEvent(cmdEvent);
+ }
+
+ Update();
+ }
+
+ return true;
+}
+
+/// 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)
+{
+ 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)
+ {
+ // If the caret position has changed, no longer reflect the default style
+ // in the UI.
+ if (GetCaretPosition() != m_caretPositionForDefaultStyle)
+ m_caretPositionForDefaultStyle = -2;
+ }
+
+ event.Skip();
+}
+
+/// Scrolling
+void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
+{
+#if wxRICHTEXT_USE_OWN_CARET
+ if (!((wxRichTextCaret*) GetCaret())->GetNeedsUpdate())
+ {
+ GetCaret()->Hide();
+ ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate();
+ }
+#endif
+
+ event.Skip();
+}
+
+/// Set up scrollbars, e.g. after a resize
+void wxRichTextCtrl::SetupScrollbars(bool atTop)
+{
+ if (IsFrozen())
+ return;
+
+ if (GetBuffer().IsEmpty() || !m_verticalScrollbarEnabled)
+ {
+ SetScrollbars(0, 0, 0, 0, 0, 0);
+ return;
+ }
+
+ // TODO: reimplement scrolling so we scroll by line, not by fixed number
+ // of pixels. See e.g. wxVScrolledWindow for ideas.
+ int pixelsPerUnit = 5;
+ wxSize clientSize = GetClientSize();
+
+ int maxHeight = (int) (0.5 + GetScale() * (GetBuffer().GetCachedSize().y + GetBuffer().GetTopMargin()));
+
+ // Round up so we have at least maxHeight pixels
+ int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5);
+
+ int startX = 0, startY = 0;
+ if (!atTop)
+ GetViewStart(& startX, & startY);
+
+ int maxPositionX = 0;
+ int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5);
+
+ int newStartX = wxMin(maxPositionX, startX);
+ int newStartY = wxMin(maxPositionY, startY);
+
+ int oldPPUX, oldPPUY;
+ int oldStartX, oldStartY;
+ int oldVirtualSizeX = 0, oldVirtualSizeY = 0;
+ GetScrollPixelsPerUnit(& oldPPUX, & oldPPUY);
+ GetViewStart(& oldStartX, & oldStartY);
+ GetVirtualSize(& oldVirtualSizeX, & oldVirtualSizeY);
+ if (oldPPUY > 0)
+ oldVirtualSizeY /= oldPPUY;
+
+ if (oldPPUX == 0 && oldPPUY == pixelsPerUnit && oldVirtualSizeY == unitsY && oldStartX == newStartX && oldStartY == newStartY)
+ return;
+
+ // Don't set scrollbars if there were none before, and there will be none now.
+ if (oldPPUY != 0 && (oldVirtualSizeY*oldPPUY < clientSize.y) && (unitsY*pixelsPerUnit < clientSize.y))
+ return;
+
+ // Move to previous scroll position if
+ // possible
+ SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY);
+}
+
+/// Paint the background
+void wxRichTextCtrl::PaintBackground(wxDC& dc)
+{
+ wxColour backgroundColour = GetBackgroundColour();
+ if (!backgroundColour.IsOk())
+ backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
+
+ // Clear the background
+ dc.SetBrush(wxBrush(backgroundColour));
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ wxRect windowRect(GetClientSize());
+ windowRect.x -= 2; windowRect.y -= 2;
+ windowRect.width += 4; windowRect.height += 4;
+
+ // We need to shift the rectangle to take into account
+ // scrolling. Converting device to logical coordinates.
+ CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
+ dc.DrawRectangle(windowRect);
+}
+
+#if wxRICHTEXT_BUFFERED_PAINTING
+/// Recreate buffer bitmap if necessary
+bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
+{
+ wxSize sz = size;
+ if (sz == wxDefaultSize)
+ sz = GetClientSize();
+
+ if (sz.x < 1 || sz.y < 1)
+ return false;
+
+ if (!m_bufferBitmap.IsOk() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
+ m_bufferBitmap = wxBitmap(sz.x, sz.y);
+ return m_bufferBitmap.IsOk();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// file IO functions
+// ----------------------------------------------------------------------------
+
+bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType)
+{
+ SetFocusObject(& GetBuffer(), true);
+
+ bool success = GetBuffer().LoadFile(filename, (wxRichTextFileType)fileType);
+ if (success)
+ m_filename = filename;
+
+ DiscardEdits();
+ SetInsertionPoint(0);
+ LayoutContent();
+ PositionCaret();
+ SetupScrollbars(true);
+ Refresh(false);
+ wxTextCtrl::SendTextUpdatedEvent(this);
+
+ if (success)
+ return true;
+ else
+ {
+ wxLogError(_("File couldn't be loaded."));
+
+ return false;
+ }
+}
+
+bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
+{
+ if (GetBuffer().SaveFile(filename, (wxRichTextFileType)fileType))
+ {
+ m_filename = filename;
+
+ DiscardEdits();
+
+ return true;
+ }
+
+ wxLogError(_("The text couldn't be saved."));
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// wxRichTextCtrl specific functionality
+// ----------------------------------------------------------------------------
+
+/// Add a new paragraph of text to the end of the buffer
+wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
+{
+ wxRichTextRange range = GetFocusObject()->AddParagraph(text);
+ GetBuffer().Invalidate();
+ LayoutContent();
+ return range;
+}
+
+/// Add an image
+wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
+{
+ wxRichTextRange range = GetFocusObject()->AddImage(image);
+ GetBuffer().Invalidate();
+ LayoutContent();
+ return range;
+}
+
+// ----------------------------------------------------------------------------
+// selection and ranges
+// ----------------------------------------------------------------------------
+
+/// Select none
+void wxRichTextCtrl::SelectNone()
+{
+ if (m_selection.IsValid())
+ {
+ wxRichTextSelection oldSelection = m_selection;
+
+ m_selection.Reset();
+
+ RefreshForSelectionChange(oldSelection, m_selection);
+ }
+ m_selectionAnchor = -2;
+ m_selectionAnchorObject = NULL;
+ m_selectionState = wxRichTextCtrlSelectionState_Normal;
+}
+
+static bool wxIsWordDelimiter(const wxString& text)
+{
+ return !text.IsEmpty() && !wxIsalnum(text[0]);
+}
+
+/// Select the word at the given character position
+bool wxRichTextCtrl::SelectWord(long position)
+{
+ if (position < 0 || position > GetFocusObject()->GetOwnRange().GetEnd())
+ return false;
+
+ wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(position);
+ if (!para)
+ return false;
+
+ if (position == para->GetRange().GetEnd())
+ position --;
+
+ long positionStart = position;
+ long positionEnd = position;
+
+ for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --)
+ {
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionStart, positionStart));
+ if (wxIsWordDelimiter(text))
+ {
+ positionStart ++;
+ break;
+ }
+ }
+ if (positionStart < para->GetRange().GetStart())
+ positionStart = para->GetRange().GetStart();
+
+ for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++)
+ {
+ wxString text = GetFocusObject()->GetTextForRange(wxRichTextRange(positionEnd, positionEnd));
+ if (wxIsWordDelimiter(text))
+ {
+ positionEnd --;
+ break;
+ }
+ }
+ if (positionEnd >= para->GetRange().GetEnd())
+ positionEnd = para->GetRange().GetEnd();
+
+ if (positionEnd < positionStart)
+ return false;
+
+ SetSelection(positionStart, positionEnd+1);
+
+ if (positionStart >= 0)
+ {
+ MoveCaret(positionStart-1, true);
+ SetDefaultStyleToCursorStyle();
+ }
+
+ return true;
+}
+
+wxString wxRichTextCtrl::GetStringSelection() const
+{
+ long from, to;
+ GetSelection(&from, &to);
+
+ return GetRange(from, to);
+}
+
+// ----------------------------------------------------------------------------
+// hit testing
+// ----------------------------------------------------------------------------
+
+wxTextCtrlHitTestResult
+wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
+{
+ // implement in terms of the other overload as the native ports typically
+ // can get the position and not (x, y) pair directly (although wxUniv
+ // directly gets x and y -- and so overrides this method as well)
+ long pos;
+ wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
+
+ if ( rc != wxTE_HT_UNKNOWN )
+ {
+ PositionToXY(pos, x, y);
+ }
+
+ return rc;
+}
+
+wxTextCtrlHitTestResult
+wxRichTextCtrl::HitTest(const wxPoint& pt,
+ long * pos) const
+{
+ wxClientDC dc((wxRichTextCtrl*) this);
+ ((wxRichTextCtrl*)this)->PrepareDC(dc);
+
+ // Buffer uses logical position (relative to start of buffer)
+ // so convert
+ wxPoint pt2 = GetLogicalPoint(pt);
+
+ wxRichTextObject* hitObj = NULL;
+ wxRichTextObject* contextObj = NULL;
+ wxRichTextDrawingContext context((wxRichTextBuffer*) & GetBuffer());
+ int hit = ((wxRichTextCtrl*)this)->GetFocusObject()->HitTest(dc, context, pt2, *pos, & hitObj, & contextObj, wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS);
+
+ if ((hit & wxRICHTEXT_HITTEST_BEFORE) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
+ return wxTE_HT_BEFORE;
+ else if ((hit & wxRICHTEXT_HITTEST_AFTER) && (hit & wxRICHTEXT_HITTEST_OUTSIDE))
+ return wxTE_HT_BEYOND;
+ else if (hit & (wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_AFTER))
+ return wxTE_HT_ON_TEXT;
+
+ return wxTE_HT_UNKNOWN;
+}
+
+wxRichTextParagraphLayoutBox*
+wxRichTextCtrl::FindContainerAtPoint(const wxPoint pt, long& position, int& hit, wxRichTextObject* hitObj, int flags/* = 0*/)
+{
+ wxClientDC dc(this);
+ PrepareDC(dc);
+ dc.SetFont(GetFont());
+
+ wxPoint logicalPt = GetLogicalPoint(pt);
+
+ wxRichTextObject* contextObj = NULL;
+ wxRichTextDrawingContext context(& GetBuffer());
+ hit = GetBuffer().HitTest(dc, context, GetUnscaledPoint(logicalPt), position, &hitObj, &contextObj, flags);
+ wxRichTextParagraphLayoutBox* container = wxDynamicCast(contextObj, wxRichTextParagraphLayoutBox);
+
+ return container;
+}
+
+
+// ----------------------------------------------------------------------------
+// set/get the controls text
+// ----------------------------------------------------------------------------
+
+wxString wxRichTextCtrl::DoGetValue() const
+{
+ return GetBuffer().GetText();
+}
+
+wxString wxRichTextCtrl::GetRange(long from, long to) const
+{
+ // Public API for range is different from internals
+ return GetFocusObject()->GetTextForRange(wxRichTextRange(from, to-1));
+}
+
+void wxRichTextCtrl::DoSetValue(const wxString& value, int flags)
+{
+ // Don't call Clear here, since it always sends a text updated event
+ m_buffer.ResetAndClearCommands();
+ m_buffer.Invalidate(wxRICHTEXT_ALL);
+ m_caretPosition = -1;
+ m_caretPositionForDefaultStyle = -2;
+ m_caretAtLineStart = false;
+ m_selection.Reset();
+ m_selectionState = wxRichTextCtrlSelectionState_Normal;
+
+ Scroll(0,0);
+
+ if (!IsFrozen())
+ {
+ LayoutContent();
+ Refresh(false);
+ }
+
+ if (!value.IsEmpty())
+ {
+ // Remove empty paragraph
+ GetBuffer().Clear();
+ DoWriteText(value, flags);
+
+ // for compatibility, don't move the cursor when doing SetValue()
+ SetInsertionPoint(0);
+ }
+ else
+ {
+ // still send an event for consistency
+ if (flags & SetValue_SendEvent)
+ wxTextCtrl::SendTextUpdatedEvent(this);
+ }
+ DiscardEdits();
+}
+
+void wxRichTextCtrl::WriteText(const wxString& value)
+{
+ DoWriteText(value);
+}
+
+void wxRichTextCtrl::DoWriteText(const wxString& value, int flags)
+{
+ wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
+
+ GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
+ wxRichTextDrawingContext context(& GetBuffer());
+ GetBuffer().Defragment(context);
+
+ if ( flags & SetValue_SendEvent )
+ wxTextCtrl::SendTextUpdatedEvent(this);
+}
+
+void wxRichTextCtrl::AppendText(const wxString& text)
+{
+ SetInsertionPointEnd();
+
+ WriteText(text);
+}
+
+/// Write an image at the current insertion point
+bool wxRichTextCtrl::WriteImage(const wxImage& image, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
+{
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image2 = image;
+ if (imageBlock.MakeImageBlock(image2, bitmapType))
+ return WriteImage(imageBlock, textAttr);
+
+ return false;
+}
+
+bool wxRichTextCtrl::WriteImage(const wxString& filename, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
+{
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image;
+ if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
+ return WriteImage(imageBlock, textAttr);
+
+ return false;
+}
+
+bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock, const wxRichTextAttr& textAttr)
+{
+ return GetFocusObject()->InsertImageWithUndo(& GetBuffer(), m_caretPosition+1, imageBlock, this, 0, textAttr);
+}
+
+bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, wxBitmapType bitmapType, const wxRichTextAttr& textAttr)
+{
+ if (bitmap.IsOk())
+ {
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image = bitmap.ConvertToImage();
+ if (image.IsOk() && imageBlock.MakeImageBlock(image, bitmapType))
+ return WriteImage(imageBlock, textAttr);
+ }
+
+ return false;
+}
+
+// Write a text box at the current insertion point.
+wxRichTextBox* wxRichTextCtrl::WriteTextBox(const wxRichTextAttr& textAttr)
+{
+ wxRichTextBox* textBox = new wxRichTextBox;
+ textBox->SetAttributes(textAttr);
+ textBox->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
+ textBox->AddParagraph(wxEmptyString);
+ textBox->SetParent(NULL);
+
+ // The object returned is the one actually inserted into the buffer,
+ // while the original one is deleted.
+ wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(& GetBuffer(), m_caretPosition+1, textBox, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
+ wxRichTextBox* box = wxDynamicCast(obj, wxRichTextBox);
+ return box;
+}
+
+wxRichTextField* wxRichTextCtrl::WriteField(const wxString& fieldType, const wxRichTextProperties& properties,
+ const wxRichTextAttr& textAttr)
+{
+ return GetFocusObject()->InsertFieldWithUndo(& GetBuffer(), m_caretPosition+1, fieldType, properties,
+ this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE, textAttr);
+}
+
+// Write a table at the current insertion point, returning the table.
+wxRichTextTable* wxRichTextCtrl::WriteTable(int rows, int cols, const wxRichTextAttr& tableAttr, const wxRichTextAttr& cellAttr)
+{
+ wxASSERT(rows > 0 && cols > 0);
+
+ if (rows <= 0 || cols <= 0)
+ return NULL;
+
+ wxRichTextTable* table = new wxRichTextTable;
+ table->SetAttributes(tableAttr);
+ table->SetParent(& GetBuffer()); // set parent temporarily for AddParagraph to use correct style
+
+ table->CreateTable(rows, cols);
+
+ table->SetParent(NULL);
+
+ int i, j;
+ for (j = 0; j < rows; j++)
+ {
+ for (i = 0; i < cols; i++)
+ {
+ table->GetCell(j, i)->GetAttributes() = cellAttr;
+ }
+ }
+
+ // The object returned is the one actually inserted into the buffer,
+ // while the original one is deleted.
+ wxRichTextObject* obj = GetFocusObject()->InsertObjectWithUndo(& GetBuffer(), m_caretPosition+1, table, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
+ wxRichTextTable* tableResult = wxDynamicCast(obj, wxRichTextTable);
+ return tableResult;
+}
+
+
+/// Insert a newline (actually paragraph) at the current insertion point.
+bool wxRichTextCtrl::Newline()
+{
+ return GetFocusObject()->InsertNewlineWithUndo(& GetBuffer(), m_caretPosition+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
+}
+
+/// Insert a line break at the current insertion point.
+bool wxRichTextCtrl::LineBreak()
+{
+ wxString text;
+ text = wxRichTextLineBreakChar;
+ return GetFocusObject()->InsertTextWithUndo(& GetBuffer(), m_caretPosition+1, text, this);
+}
+
+// ----------------------------------------------------------------------------
+// Clipboard operations
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Copy()
+{
+ if (CanCopy())
+ {
+ wxRichTextRange range = GetInternalSelectionRange();
+ GetBuffer().CopyToClipboard(range);
+ }
+}
+
+void wxRichTextCtrl::Cut()
+{
+ if (CanCut())
+ {
+ wxRichTextRange range = GetInternalSelectionRange();
+ GetBuffer().CopyToClipboard(range);
+
+ DeleteSelectedContent();
+ LayoutContent();
+ Refresh(false);
+ }
+}
+
+void wxRichTextCtrl::Paste()
+{
+ if (CanPaste())
+ {
+ BeginBatchUndo(_("Paste"));
+
+ long newPos = m_caretPosition;
+ DeleteSelectedContent(& newPos);
+
+ GetBuffer().PasteFromClipboard(newPos);
+
+ EndBatchUndo();
+ }
+}
+
+void wxRichTextCtrl::DeleteSelection()
+{
+ if (CanDeleteSelection())
+ {
+ DeleteSelectedContent();
+ }
+}
+
+bool wxRichTextCtrl::HasSelection() const
+{
+ return (m_selection.IsValid() && m_selection.GetContainer() == GetFocusObject());
+}
+
+bool wxRichTextCtrl::HasUnfocusedSelection() const
+{
+ return m_selection.IsValid();
+}
+
+bool wxRichTextCtrl::CanCopy() const
+{
+ // Can copy if there's a selection
+ return HasSelection();
+}
+
+bool wxRichTextCtrl::CanCut() const
+{
+ return CanDeleteSelection();
+}
+
+bool wxRichTextCtrl::CanPaste() const
+{
+ if ( !IsEditable() || !GetFocusObject() || !CanInsertContent(* GetFocusObject(), m_caretPosition+1))
+ return false;
+
+ return GetBuffer().CanPasteFromClipboard();
+}
+
+bool wxRichTextCtrl::CanDeleteSelection() const
+{
+ return HasSelection() && IsEditable() && CanDeleteRange(* GetFocusObject(), GetSelectionRange());
+}
+
+
+// ----------------------------------------------------------------------------
+// Accessors
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::SetContextMenu(wxMenu* menu)
+{
+ if (m_contextMenu && m_contextMenu != menu)
+ delete m_contextMenu;
+ m_contextMenu = menu;
+}
+
+void wxRichTextCtrl::SetEditable(bool editable)
+{
+ m_editable = editable;
+}
+
+void wxRichTextCtrl::SetInsertionPoint(long pos)
+{
+ SelectNone();
+
+ m_caretPosition = pos - 1;
+ m_caretAtLineStart = true;
+
+ PositionCaret();
+
+ SetDefaultStyleToCursorStyle();
+}
+
+void wxRichTextCtrl::SetInsertionPointEnd()
+{
+ long pos = GetLastPosition();
+ SetInsertionPoint(pos);
+}
+
+long wxRichTextCtrl::GetInsertionPoint() const
+{
+ return m_caretPosition+1;
+}
+
+wxTextPos wxRichTextCtrl::GetLastPosition() const
+{
+ return GetFocusObject()->GetOwnRange().GetEnd();
+}
+
+// If the return values from and to are the same, there is no
+// selection.
+void wxRichTextCtrl::GetSelection(long* from, long* to) const
+{
+ if (m_selection.IsValid())
+ {
+ *from = m_selection.GetRange().GetStart();
+ *to = m_selection.GetRange().GetEnd();
+ (*to) ++;
+ }
+ else
+ {
+ *from = -2;
+ *to = -2;
+ }
+}
+
+bool wxRichTextCtrl::IsEditable() const
+{
+ return m_editable;
+}
+
+// ----------------------------------------------------------------------------
+// selection
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::SetSelection(long from, long to)
+{
+ // if from and to are both -1, it means (in wxWidgets) that all text should
+ // be selected.
+ if ( (from == -1) && (to == -1) )
+ {
+ from = 0;
+ to = GetLastPosition()+1;
+ }
+
+ if (from == to)
+ {
+ SelectNone();
+ }
+ else
+ {
+ wxRichTextSelection oldSelection = m_selection;
+
+ m_selectionAnchor = from-1;
+ m_selectionAnchorObject = NULL;
+ m_selection.Set(wxRichTextRange(from, to-1), GetFocusObject());
+
+ m_caretPosition = wxMax(-1, to-1);
+
+ RefreshForSelectionChange(oldSelection, m_selection);
+ PositionCaret();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Editing
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Replace(long from, long to,
+ const wxString& value)
+{
+ BeginBatchUndo(_("Replace"));
+
+ SetSelection(from, to);
+
+ wxRichTextAttr attr(GetDefaultStyle());
+
+ DeleteSelectedContent();
+
+ SetDefaultStyle(attr);
+
+ if (!value.IsEmpty())
+ DoWriteText(value, SetValue_SelectionOnly);
+
+ EndBatchUndo();
+}
+
+void wxRichTextCtrl::Remove(long from, long to)
+{
+ SelectNone();
+
+ GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(from, to-1), this, & GetBuffer());
+
+ LayoutContent();
+ if (!IsFrozen())
+ Refresh(false);
+}
+
+bool wxRichTextCtrl::IsModified() const
+{
+ return m_buffer.IsModified();
+}
+
+void wxRichTextCtrl::MarkDirty()
+{
+ m_buffer.Modify(true);
+}
+
+void wxRichTextCtrl::DiscardEdits()
+{
+ m_caretPositionForDefaultStyle = -2;
+ m_buffer.Modify(false);
+ m_buffer.GetCommandProcessor()->ClearCommands();
+}
+
+int wxRichTextCtrl::GetNumberOfLines() const
+{
+ return GetFocusObject()->GetParagraphCount();
+}
+
+// ----------------------------------------------------------------------------
+// Positions <-> coords
+// ----------------------------------------------------------------------------
+
+long wxRichTextCtrl::XYToPosition(long x, long y) const
+{
+ return GetFocusObject()->XYToPosition(x, y);
+}
+
+bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
+{
+ return GetFocusObject()->PositionToXY(pos, x, y);
+}
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::ShowPosition(long pos)
+{
+ if (!IsPositionVisible(pos))
+ ScrollIntoView(pos-1, WXK_DOWN);
+}