diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index d4cd971fde..f970e83728 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -2074,6 +2074,11 @@ public: /// Replace the buffer paragraphs with the given fragment. void ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment); +#if wxABI_VERSION >= 20808 + // Create arrays to be used in refresh optimization + void CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions); +#endif + /// Get the fragments wxRichTextParagraphLayoutBox& GetNewParagraphs() { return m_newParagraphs; } wxRichTextParagraphLayoutBox& GetOldParagraphs() { return m_oldParagraphs; } diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 563de5cb6d..3c2bc94422 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -500,7 +500,9 @@ bool wxRichTextCompositeObject::Defragment() // child. } else + { node = node->GetNext(); + } } else node = node->GetNext(); @@ -637,7 +639,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, // Skip } else - child->Draw(dc, range, selectionRange, childRect, descent, style); + child->Draw(dc, range, selectionRange, rect, descent, style); } node = node->GetNext(); @@ -905,25 +907,29 @@ wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool c wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { - // child is a paragraph - wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph); - wxASSERT (child != NULL); - - wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst(); - while (node2) + wxRichTextObject* obj = (wxRichTextObject*) node->GetData(); + if (obj->GetRange().Contains(pos)) { - wxRichTextLine* line = node2->GetData(); + // child is a paragraph + wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph); + wxASSERT (child != NULL); - wxRichTextRange range = line->GetAbsoluteRange(); + wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst(); + while (node2) + { + wxRichTextLine* line = node2->GetData(); - if (range.Contains(pos) || + wxRichTextRange range = line->GetAbsoluteRange(); - // If the position is end-of-paragraph, then return the last line of - // of the paragraph. - (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())) - return line; + if (range.Contains(pos) || - node2 = node2->GetNext(); + // If the position is end-of-paragraph, then return the last line of + // of the paragraph. + (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())) + return line; + + node2 = node2->GetNext(); + } } node = node->GetNext(); @@ -3223,7 +3229,7 @@ wxRichTextParagraph::~wxRichTextParagraph() } /// Draw the item -bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style) +bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int style) { wxTextAttrEx attr = GetCombinedAttributes(); @@ -3302,53 +3308,57 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxR wxRichTextLine* line = node->GetData(); wxRichTextRange lineRange = line->GetAbsoluteRange(); - int maxDescent = line->GetDescent(); - // Lines are specified relative to the paragraph wxPoint linePosition = line->GetPosition() + GetPosition(); - wxPoint objectPosition = linePosition; - // Loop through objects until we get to the one within range - wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst(); - - int i = 0; - while (node2) + // Don't draw if off the screen + if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height)) { - wxRichTextObject* child = node2->GetData(); + wxPoint objectPosition = linePosition; + int maxDescent = line->GetDescent(); - if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range)) + // Loop through objects until we get to the one within range + wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst(); + + int i = 0; + while (node2) { - // Draw this part of the line at the correct position - wxRichTextRange objectRange(child->GetRange()); - objectRange.LimitTo(lineRange); + wxRichTextObject* child = node2->GetData(); - wxSize objectSize; + if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range)) + { + // Draw this part of the line at the correct position + wxRichTextRange objectRange(child->GetRange()); + objectRange.LimitTo(lineRange); + + wxSize objectSize; #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS - if (i < (int) line->GetObjectSizes().GetCount()) - { - objectSize.x = line->GetObjectSizes()[(size_t) i]; - } - else + if (i < (int) line->GetObjectSizes().GetCount()) + { + objectSize.x = line->GetObjectSizes()[(size_t) i]; + } + else #endif - { - int descent = 0; - child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition); + { + int descent = 0; + child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition); + } + + // Use the child object's width, but the whole line's height + wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y)); + child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style); + + objectPosition.x += objectSize.x; + i ++; } + else if (child->GetRange().GetStart() > lineRange.GetEnd()) + // Can break out of inner loop now since we've passed this line's range + break; - // Use the child object's width, but the whole line's height - wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y)); - child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style); - - objectPosition.x += objectSize.x; - i ++; + node2 = node2->GetNext(); } - else if (child->GetRange().GetStart() > lineRange.GetEnd()) - // Can break out of inner loop now since we've passed this line's range - break; - - node2 = node2->GetNext(); } node = node->GetNext(); @@ -5186,7 +5196,7 @@ wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) cons bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const { return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) && - (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes())); + (m_text.empty() || ((GetAttributes().GetFlags() == object->GetAttributes().GetFlags()) && wxTextAttrEqPartial(GetAttributes(), object->GetAttributes(), GetAttributes().GetFlags()))); } /// Returns true if this object merged itself with the given one. @@ -5672,8 +5682,6 @@ bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx& style) SetDefaultStyle(newStyle); - // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize()); - return true; } @@ -6649,6 +6657,61 @@ wxRichTextAction::~wxRichTextAction() { } +// Create arrays to be used in refresh optimization +void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions) +{ + // Store a list of line start character and y positions so we can figure out which area + // we need to refresh + +#if wxRICHTEXT_USE_OPTIMIZED_DRAWING + // NOTE: we're assuming that the buffer is laid out correctly at this point. + // If we had several actions, which only invalidate and leave layout until the + // paint handler is called, then this might not be true. So we may need to switch + // optimisation on only when we're simply adding text and not simultaneously + // deleting a selection, for example. Or, we make sure the buffer is laid out correctly + // first, but of course this means we'll be doing it twice. + if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly + { + wxSize clientSize = m_ctrl->GetClientSize(); + wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint(); + int lastY = firstVisiblePt.y + clientSize.y; + + wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart()); + wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para); + while (node) + { + wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData(); + wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst(); + while (node2) + { + wxRichTextLine* line = node2->GetData(); + wxPoint pt = line->GetAbsolutePosition(); + wxRichTextRange range = line->GetAbsoluteRange(); + + if (pt.y > lastY) + { + node2 = wxRichTextLineList::compatibility_iterator(); + node = wxRichTextObjectList::compatibility_iterator(); + } + else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y) + { + optimizationLineCharPositions.Add(range.GetStart()); + optimizationLineYPositions.Add(pt.y); + } + + if (node2) + node2 = node2->GetNext(); + } + + if (node) + node = node->GetNext(); + } + } +#endif +} + +bool g_ActionInDo = false; + bool wxRichTextAction::Do() { m_buffer->Modify(true); @@ -6663,49 +6726,7 @@ bool wxRichTextAction::Do() wxArrayInt optimizationLineYPositions; #if wxRICHTEXT_USE_OPTIMIZED_DRAWING - // NOTE: we're assuming that the buffer is laid out correctly at this point. - // If we had several actions, which only invalidate and leave layout until the - // paint handler is called, then this might not be true. So we may need to switch - // optimisation on only when we're simply adding text and not simultaneously - // deleting a selection, for example. Or, we make sure the buffer is laid out correctly - // first, but of course this means we'll be doing it twice. - if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly - { - wxSize clientSize = m_ctrl->GetClientSize(); - wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint(); - int lastY = firstVisiblePt.y + clientSize.y; - - wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart()); - wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para); - while (node) - { - wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData(); - wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst(); - while (node2) - { - wxRichTextLine* line = node2->GetData(); - wxPoint pt = line->GetAbsolutePosition(); - wxRichTextRange range = line->GetAbsoluteRange(); - - if (pt.y > lastY) - { - node2 = wxRichTextLineList::compatibility_iterator(); - node = wxRichTextObjectList::compatibility_iterator(); - } - else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y) - { - optimizationLineCharPositions.Add(range.GetStart()); - optimizationLineYPositions.Add(pt.y); - } - - if (node2) - node2 = node2->GetNext(); - } - - if (node) - node = node->GetNext(); - } - } + CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions); #endif m_buffer->InsertFragment(GetRange().GetStart(), m_newParagraphs); @@ -6730,10 +6751,8 @@ bool wxRichTextAction::Do() newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1)); - if (optimizationLineCharPositions.GetCount() > 0) - UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); - else - UpdateAppearance(newCaretPosition, true /* send update event */); + g_ActionInDo = true; + UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); wxRichTextEvent cmdEvent( wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, @@ -6748,6 +6767,15 @@ bool wxRichTextAction::Do() } case wxRICHTEXT_DELETE: { + // Store a list of line start character and y positions so we can figure out which area + // we need to refresh + wxArrayInt optimizationLineCharPositions; + wxArrayInt optimizationLineYPositions; + +#if wxRICHTEXT_USE_OPTIMIZED_DRAWING + CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions); +#endif + m_buffer->DeleteRange(GetRange()); m_buffer->UpdateRanges(); m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); @@ -6756,7 +6784,8 @@ bool wxRichTextAction::Do() if (caretPos >= m_buffer->GetRange().GetEnd()) caretPos --; - UpdateAppearance(caretPos, true /* send update event */); + g_ActionInDo = true; + UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); wxRichTextEvent cmdEvent( wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, @@ -6802,13 +6831,23 @@ bool wxRichTextAction::Undo() { case wxRICHTEXT_INSERT: { + // Store a list of line start character and y positions so we can figure out which area + // we need to refresh + wxArrayInt optimizationLineCharPositions; + wxArrayInt optimizationLineYPositions; + +#if wxRICHTEXT_USE_OPTIMIZED_DRAWING + CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions); +#endif + m_buffer->DeleteRange(GetRange()); m_buffer->UpdateRanges(); m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); long newCaretPosition = GetPosition() - 1; - UpdateAppearance(newCaretPosition, true /* send update event */); + g_ActionInDo = false; + UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); wxRichTextEvent cmdEvent( wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, @@ -6823,11 +6862,21 @@ bool wxRichTextAction::Undo() } case wxRICHTEXT_DELETE: { + // Store a list of line start character and y positions so we can figure out which area + // we need to refresh + wxArrayInt optimizationLineCharPositions; + wxArrayInt optimizationLineYPositions; + +#if wxRICHTEXT_USE_OPTIMIZED_DRAWING + CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions); +#endif + m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs); m_buffer->UpdateRanges(); m_buffer->Invalidate(GetRange()); - UpdateAppearance(GetPosition(), true /* send update event */); + g_ActionInDo = false; + UpdateAppearance(GetPosition(), true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); wxRichTextEvent cmdEvent( wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, @@ -6877,7 +6926,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent #if wxRICHTEXT_USE_OPTIMIZED_DRAWING // Find refresh rectangle if we are in a position to optimise refresh - if (m_cmdId == wxRICHTEXT_INSERT && optimizationLineCharPositions && optimizationLineCharPositions->GetCount() > 0) + if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions) { size_t i; @@ -6888,17 +6937,61 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent int firstY = 0; int lastY = firstVisiblePt.y + clientSize.y; - bool foundStart = false; bool foundEnd = false; - // position offset - how many characters were inserted + // position offset - how many characters were inserted or deleted int positionOffset = GetRange().GetLength(); + // Determine whether this is Do or Undo, and adjust positionOffset accordingly + if ((m_cmdId == wxRICHTEXT_DELETE && g_ActionInDo) || (m_cmdId == wxRICHTEXT_INSERT && !g_ActionInDo)) + positionOffset = - positionOffset; + // find the first line which is being drawn at the same position as it was // before. Since we're talking about a simple insertion, we can assume // that the rest of the window does not need to be redrawn. wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition()); + if (para) + { + // Find line containing GetPosition(). + wxRichTextLine* line = NULL; + wxRichTextLineList::compatibility_iterator node2 = para->GetLines().GetFirst(); + while (node2) + { + wxRichTextLine* l = node2->GetData(); + wxRichTextRange range = l->GetAbsoluteRange(); + if (range.Contains(GetRange().GetStart()-1)) + { + line = l; + break; + } + node2 = node2->GetNext(); + } + + if (line) + { + // Step back a couple of lines to where we can be sure of reformatting correctly + wxRichTextLineList::compatibility_iterator lineNode = para->GetLines().Find(line); + if (lineNode) + { + lineNode = lineNode->GetPrevious(); + if (lineNode) + { + line = (wxRichTextLine*) lineNode->GetData(); + lineNode = lineNode->GetPrevious(); + if (lineNode) + line = (wxRichTextLine*) lineNode->GetData(); + } + } + + firstY = line->GetAbsolutePosition().y; + } + } + + // find the first line which is being drawn at the same position as it was + // before. Since we're talking about a simple insertion, we can assume + // that the rest of the window does not need to be redrawn. + wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para); while (node) { @@ -6918,14 +7011,19 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent node2 = wxRichTextLineList::compatibility_iterator(); node = wxRichTextObjectList::compatibility_iterator(); } + // Detect last line in the buffer + else if (!node2->GetNext() && para->GetRange().Contains(m_buffer->GetRange().GetEnd())) + { + foundEnd = true; + lastY = pt.y + line->GetSize().y; + + node2 = wxRichTextLineList::compatibility_iterator(); + node = wxRichTextObjectList::compatibility_iterator(); + + break; + } else { - if (!foundStart) - { - firstY = pt.y - firstVisiblePt.y; - foundStart = true; - } - // search for this line being at the same position as before for (i = 0; i < optimizationLineCharPositions->GetCount(); i++) { @@ -6934,7 +7032,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent { // Stop, we're now the same as we were foundEnd = true; - lastY = pt.y - firstVisiblePt.y; + lastY = pt.y; node2 = wxRichTextLineList::compatibility_iterator(); node = wxRichTextObjectList::compatibility_iterator(); @@ -6952,21 +7050,19 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent node = node->GetNext(); } - if (!foundStart) - firstY = firstVisiblePt.y; + firstY = wxMax(firstVisiblePt.y, firstY); if (!foundEnd) lastY = firstVisiblePt.y + clientSize.y; - wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + clientSize.x, lastY - firstY); - m_ctrl->RefreshRect(rect); - - // TODO: we need to make sure that lines are only drawn if in the update region. The rect - // passed to Draw is currently used in different ways (to pass the position the content should - // be drawn at as well as the relevant region). + // Convert to device coordinates + wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY)); + m_ctrl->RefreshRect(rect, false); } else #endif + { m_ctrl->Refresh(false); + } if (sendUpdateEvent) m_ctrl->SendTextUpdatedEvent(); diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index 882112cd66..40be6f07cc 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -35,6 +35,10 @@ // DLL options compatibility check: #include "wx/app.h" + +// Refresh the area affected by a selection change +bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection); + WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl") DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK) @@ -382,14 +386,10 @@ void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event) if (event.ShiftDown()) { - bool extendSel = false; if (m_selectionRange.GetStart() == -2) - extendSel = ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); + ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); else - extendSel = ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); - - if (extendSel) - Refresh(false); + ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); } else SelectNone(); @@ -509,13 +509,10 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event) if (m_caretPosition != position) { - bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN); + ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN); MoveCaret(position, caretAtLineStart); SetDefaultStyleToCursorStyle(); - - if (extendSel) - Refresh(false); } } } @@ -992,6 +989,8 @@ bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags) { if (flags & wxRICHTEXT_SHIFT_DOWN) { + wxRichTextRange oldSelection = m_selectionRange; + // If not currently selecting, start selecting if (m_selectionRange.GetStart() == -2) { @@ -1012,6 +1011,8 @@ bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags) m_selectionRange.SetRange(newPos+1, m_selectionAnchor); } + wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); + if (m_selectionRange.GetStart() > m_selectionRange.GetEnd()) { wxLogDebug(wxT("Strange selection range")); @@ -1283,8 +1284,6 @@ bool wxRichTextCtrl::MoveRight(int noPositions, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } else @@ -1312,8 +1311,6 @@ bool wxRichTextCtrl::MoveLeft(int noPositions, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } else @@ -1403,8 +1400,6 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1426,8 +1421,6 @@ bool wxRichTextCtrl::MoveToParagraphEnd(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1449,8 +1442,6 @@ bool wxRichTextCtrl::MoveToParagraphStart(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1474,8 +1465,6 @@ bool wxRichTextCtrl::MoveToLineEnd(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1501,8 +1490,6 @@ bool wxRichTextCtrl::MoveToLineStart(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1522,8 +1509,6 @@ bool wxRichTextCtrl::MoveHome(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } else @@ -1545,8 +1530,6 @@ bool wxRichTextCtrl::MoveEnd(int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } else @@ -1586,8 +1569,6 @@ bool wxRichTextCtrl::PageDown(int noPages, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } } @@ -1705,8 +1686,6 @@ bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1729,8 +1708,6 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (extendSel) - Refresh(false); return true; } @@ -1820,11 +1797,24 @@ void wxRichTextCtrl::SetupScrollbars(bool atTop) int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 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; + // Move to previous scroll position if // possible - SetScrollbars(0, pixelsPerUnit, - 0, unitsY, - wxMin(maxPositionX, startX), wxMin(maxPositionY, startY)); + SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY); } /// Paint the background @@ -1943,8 +1933,11 @@ void wxRichTextCtrl::SelectNone() { if (!(GetSelectionRange() == wxRichTextRange(-2, -2))) { - Refresh(false); + wxRichTextRange oldSelection = m_selectionRange; + m_selectionRange = wxRichTextRange(-2, -2); + + wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); } m_selectionAnchor = -2; } @@ -2343,12 +2336,13 @@ void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCare } else { + wxRichTextRange oldSelection = m_selectionRange; m_selectionAnchor = from; m_selectionRange.SetRange(from, to-1); if (from > -2) m_caretPosition = from-1; - Refresh(false); + wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); PositionCaret(); } } @@ -3240,5 +3234,53 @@ void wxRichTextCtrl::ClearAvailableFontNames() sm_availableFontNames.Clear(); } +// Refresh the area affected by a selection change +bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection) +{ + // Calculate the refresh rectangle - just the affected lines + long firstPos, lastPos; + if (oldSelection.GetStart() == -2 && newSelection.GetStart() != -2) + { + firstPos = newSelection.GetStart(); + lastPos = newSelection.GetEnd(); + } + else if (oldSelection.GetStart() != -2 && newSelection.GetStart() == -2) + { + firstPos = oldSelection.GetStart(); + lastPos = oldSelection.GetEnd(); + } + else if (oldSelection.GetStart() == -2 && newSelection.GetStart() == -2) + { + return false; + } + else + { + firstPos = wxMin(oldSelection.GetStart(), newSelection.GetStart()); + lastPos = wxMax(oldSelection.GetEnd(), newSelection.GetEnd()); + } + + wxRichTextLine* firstLine = ctrl.GetBuffer().GetLineAtPosition(firstPos); + wxRichTextLine* lastLine = ctrl.GetBuffer().GetLineAtPosition(lastPos); + + if (firstLine && lastLine) + { + wxSize clientSize = ctrl.GetClientSize(); + wxPoint pt1 = ctrl.GetPhysicalPoint(firstLine->GetAbsolutePosition()); + wxPoint pt2 = ctrl.GetPhysicalPoint(lastLine->GetAbsolutePosition()) + wxPoint(0, lastLine->GetSize().y); + + pt1.x = 0; + pt1.y = wxMax(0, pt1.y); + pt2.x = 0; + pt2.y = wxMin(clientSize.y, pt2.y); + + wxRect rect(pt1, wxSize(clientSize.x, pt2.y - pt1.y)); + ctrl.RefreshRect(rect, false); + } + else + ctrl.Refresh(false); + + return true; +} + #endif // wxUSE_RICHTEXT diff --git a/version-script.in b/version-script.in index c2dd880d13..ea9e5df893 100644 --- a/version-script.in +++ b/version-script.in @@ -33,6 +33,7 @@ *wxGridBagSizer*AdjustForOverflow*; *wxRemotelyScrolledTreeCtrl*DoCalcScrolledPosition*; *wxRemotelyScrolledTreeCtrl*SetScrollbar*; + *wxRichTextAction*CalculateRefreshOptimizations*; *wxRichTextCtrl*GetTextCursor*; *wxRichTextCtrl*GetURLCursor*; *wxRichTextCtrl*SetTextCursor*;