Further wxRichTextCtrl performance improvements

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@53381 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2008-04-27 13:26:57 +00:00
parent 70f90022b7
commit ff004763b2
4 changed files with 303 additions and 159 deletions

View File

@@ -2074,6 +2074,11 @@ public:
/// Replace the buffer paragraphs with the given fragment. /// Replace the buffer paragraphs with the given fragment.
void ApplyParagraphs(const wxRichTextParagraphLayoutBox& 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 /// Get the fragments
wxRichTextParagraphLayoutBox& GetNewParagraphs() { return m_newParagraphs; } wxRichTextParagraphLayoutBox& GetNewParagraphs() { return m_newParagraphs; }
wxRichTextParagraphLayoutBox& GetOldParagraphs() { return m_oldParagraphs; } wxRichTextParagraphLayoutBox& GetOldParagraphs() { return m_oldParagraphs; }

View File

@@ -500,7 +500,9 @@ bool wxRichTextCompositeObject::Defragment()
// child. // child.
} }
else else
{
node = node->GetNext(); node = node->GetNext();
}
} }
else else
node = node->GetNext(); node = node->GetNext();
@@ -637,7 +639,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range,
// Skip // Skip
} }
else else
child->Draw(dc, range, selectionRange, childRect, descent, style); child->Draw(dc, range, selectionRange, rect, descent, style);
} }
node = node->GetNext(); node = node->GetNext();
@@ -905,25 +907,29 @@ wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool c
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node) while (node)
{ {
// child is a paragraph wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph); if (obj->GetRange().Contains(pos))
wxASSERT (child != NULL);
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
while (node2)
{ {
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 if (range.Contains(pos) ||
// of the paragraph.
(range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
return line;
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(); node = node->GetNext();
@@ -3223,7 +3229,7 @@ wxRichTextParagraph::~wxRichTextParagraph()
} }
/// Draw the item /// 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(); wxTextAttrEx attr = GetCombinedAttributes();
@@ -3302,53 +3308,57 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxR
wxRichTextLine* line = node->GetData(); wxRichTextLine* line = node->GetData();
wxRichTextRange lineRange = line->GetAbsoluteRange(); wxRichTextRange lineRange = line->GetAbsoluteRange();
int maxDescent = line->GetDescent();
// Lines are specified relative to the paragraph // Lines are specified relative to the paragraph
wxPoint linePosition = line->GetPosition() + GetPosition(); wxPoint linePosition = line->GetPosition() + GetPosition();
wxPoint objectPosition = linePosition;
// Loop through objects until we get to the one within range // Don't draw if off the screen
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst(); if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
int i = 0;
while (node2)
{ {
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 wxRichTextObject* child = node2->GetData();
wxRichTextRange objectRange(child->GetRange());
objectRange.LimitTo(lineRange);
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 wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
if (i < (int) line->GetObjectSizes().GetCount()) if (i < (int) line->GetObjectSizes().GetCount())
{ {
objectSize.x = line->GetObjectSizes()[(size_t) i]; objectSize.x = line->GetObjectSizes()[(size_t) i];
} }
else else
#endif #endif
{ {
int descent = 0; int descent = 0;
child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition); 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 node2 = node2->GetNext();
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;
node2 = node2->GetNext();
} }
node = node->GetNext(); node = node->GetNext();
@@ -5186,7 +5196,7 @@ wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) cons
bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
{ {
return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) && 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. /// Returns true if this object merged itself with the given one.
@@ -5672,8 +5682,6 @@ bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx& style)
SetDefaultStyle(newStyle); SetDefaultStyle(newStyle);
// wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
return true; 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() bool wxRichTextAction::Do()
{ {
m_buffer->Modify(true); m_buffer->Modify(true);
@@ -6663,49 +6726,7 @@ bool wxRichTextAction::Do()
wxArrayInt optimizationLineYPositions; wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// NOTE: we're assuming that the buffer is laid out correctly at this point. CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
// 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 #endif
m_buffer->InsertFragment(GetRange().GetStart(), m_newParagraphs); m_buffer->InsertFragment(GetRange().GetStart(), m_newParagraphs);
@@ -6730,10 +6751,8 @@ bool wxRichTextAction::Do()
newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1)); newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1));
if (optimizationLineCharPositions.GetCount() > 0) g_ActionInDo = true;
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
else
UpdateAppearance(newCaretPosition, true /* send update event */);
wxRichTextEvent cmdEvent( wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
@@ -6748,6 +6767,15 @@ bool wxRichTextAction::Do()
} }
case wxRICHTEXT_DELETE: 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->DeleteRange(GetRange());
m_buffer->UpdateRanges(); m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
@@ -6756,7 +6784,8 @@ bool wxRichTextAction::Do()
if (caretPos >= m_buffer->GetRange().GetEnd()) if (caretPos >= m_buffer->GetRange().GetEnd())
caretPos --; caretPos --;
UpdateAppearance(caretPos, true /* send update event */); g_ActionInDo = true;
UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
wxRichTextEvent cmdEvent( wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
@@ -6802,13 +6831,23 @@ bool wxRichTextAction::Undo()
{ {
case wxRICHTEXT_INSERT: 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->DeleteRange(GetRange());
m_buffer->UpdateRanges(); m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
long newCaretPosition = GetPosition() - 1; long newCaretPosition = GetPosition() - 1;
UpdateAppearance(newCaretPosition, true /* send update event */); g_ActionInDo = false;
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
wxRichTextEvent cmdEvent( wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED, wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
@@ -6823,11 +6862,21 @@ bool wxRichTextAction::Undo()
} }
case wxRICHTEXT_DELETE: 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->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
m_buffer->UpdateRanges(); m_buffer->UpdateRanges();
m_buffer->Invalidate(GetRange()); m_buffer->Invalidate(GetRange());
UpdateAppearance(GetPosition(), true /* send update event */); g_ActionInDo = false;
UpdateAppearance(GetPosition(), true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
wxRichTextEvent cmdEvent( wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED, wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
@@ -6877,7 +6926,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// Find refresh rectangle if we are in a position to optimise refresh // 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; size_t i;
@@ -6888,17 +6937,61 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
int firstY = 0; int firstY = 0;
int lastY = firstVisiblePt.y + clientSize.y; int lastY = firstVisiblePt.y + clientSize.y;
bool foundStart = false;
bool foundEnd = false; bool foundEnd = false;
// position offset - how many characters were inserted // position offset - how many characters were inserted or deleted
int positionOffset = GetRange().GetLength(); 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 // 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 // before. Since we're talking about a simple insertion, we can assume
// that the rest of the window does not need to be redrawn. // that the rest of the window does not need to be redrawn.
wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition()); 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); wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
while (node) while (node)
{ {
@@ -6918,14 +7011,19 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
node2 = wxRichTextLineList::compatibility_iterator(); node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::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 else
{ {
if (!foundStart)
{
firstY = pt.y - firstVisiblePt.y;
foundStart = true;
}
// search for this line being at the same position as before // search for this line being at the same position as before
for (i = 0; i < optimizationLineCharPositions->GetCount(); i++) 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 // Stop, we're now the same as we were
foundEnd = true; foundEnd = true;
lastY = pt.y - firstVisiblePt.y; lastY = pt.y;
node2 = wxRichTextLineList::compatibility_iterator(); node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator(); node = wxRichTextObjectList::compatibility_iterator();
@@ -6952,21 +7050,19 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
node = node->GetNext(); node = node->GetNext();
} }
if (!foundStart) firstY = wxMax(firstVisiblePt.y, firstY);
firstY = firstVisiblePt.y;
if (!foundEnd) if (!foundEnd)
lastY = firstVisiblePt.y + clientSize.y; lastY = firstVisiblePt.y + clientSize.y;
wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + clientSize.x, lastY - firstY); // Convert to device coordinates
m_ctrl->RefreshRect(rect); wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
m_ctrl->RefreshRect(rect, false);
// 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).
} }
else else
#endif #endif
{
m_ctrl->Refresh(false); m_ctrl->Refresh(false);
}
if (sendUpdateEvent) if (sendUpdateEvent)
m_ctrl->SendTextUpdatedEvent(); m_ctrl->SendTextUpdatedEvent();

View File

@@ -35,6 +35,10 @@
// DLL options compatibility check: // DLL options compatibility check:
#include "wx/app.h" #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") WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
@@ -382,14 +386,10 @@ void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
if (event.ShiftDown()) if (event.ShiftDown())
{ {
bool extendSel = false;
if (m_selectionRange.GetStart() == -2) if (m_selectionRange.GetStart() == -2)
extendSel = ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
else else
extendSel = ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN);
if (extendSel)
Refresh(false);
} }
else else
SelectNone(); SelectNone();
@@ -509,13 +509,10 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
if (m_caretPosition != position) if (m_caretPosition != position)
{ {
bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN); ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
MoveCaret(position, caretAtLineStart); MoveCaret(position, caretAtLineStart);
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
} }
} }
} }
@@ -992,6 +989,8 @@ bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
{ {
if (flags & wxRICHTEXT_SHIFT_DOWN) if (flags & wxRICHTEXT_SHIFT_DOWN)
{ {
wxRichTextRange oldSelection = m_selectionRange;
// If not currently selecting, start selecting // If not currently selecting, start selecting
if (m_selectionRange.GetStart() == -2) 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); m_selectionRange.SetRange(newPos+1, m_selectionAnchor);
} }
wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
if (m_selectionRange.GetStart() > m_selectionRange.GetEnd()) if (m_selectionRange.GetStart() > m_selectionRange.GetEnd())
{ {
wxLogDebug(wxT("Strange selection range")); wxLogDebug(wxT("Strange selection range"));
@@ -1283,8 +1284,6 @@ bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
else else
@@ -1312,8 +1311,6 @@ bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
else else
@@ -1403,8 +1400,6 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1426,8 +1421,6 @@ bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1449,8 +1442,6 @@ bool wxRichTextCtrl::MoveToParagraphStart(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1474,8 +1465,6 @@ bool wxRichTextCtrl::MoveToLineEnd(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1501,8 +1490,6 @@ bool wxRichTextCtrl::MoveToLineStart(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1522,8 +1509,6 @@ bool wxRichTextCtrl::MoveHome(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
else else
@@ -1545,8 +1530,6 @@ bool wxRichTextCtrl::MoveEnd(int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
else else
@@ -1586,8 +1569,6 @@ bool wxRichTextCtrl::PageDown(int noPages, int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
} }
@@ -1705,8 +1686,6 @@ bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1729,8 +1708,6 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
PositionCaret(); PositionCaret();
SetDefaultStyleToCursorStyle(); SetDefaultStyleToCursorStyle();
if (extendSel)
Refresh(false);
return true; return true;
} }
@@ -1820,11 +1797,24 @@ void wxRichTextCtrl::SetupScrollbars(bool atTop)
int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0); int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5); 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 // Move to previous scroll position if
// possible // possible
SetScrollbars(0, pixelsPerUnit, SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY);
0, unitsY,
wxMin(maxPositionX, startX), wxMin(maxPositionY, startY));
} }
/// Paint the background /// Paint the background
@@ -1943,8 +1933,11 @@ void wxRichTextCtrl::SelectNone()
{ {
if (!(GetSelectionRange() == wxRichTextRange(-2, -2))) if (!(GetSelectionRange() == wxRichTextRange(-2, -2)))
{ {
Refresh(false); wxRichTextRange oldSelection = m_selectionRange;
m_selectionRange = wxRichTextRange(-2, -2); m_selectionRange = wxRichTextRange(-2, -2);
wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
} }
m_selectionAnchor = -2; m_selectionAnchor = -2;
} }
@@ -2343,12 +2336,13 @@ void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCare
} }
else else
{ {
wxRichTextRange oldSelection = m_selectionRange;
m_selectionAnchor = from; m_selectionAnchor = from;
m_selectionRange.SetRange(from, to-1); m_selectionRange.SetRange(from, to-1);
if (from > -2) if (from > -2)
m_caretPosition = from-1; m_caretPosition = from-1;
Refresh(false); wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange);
PositionCaret(); PositionCaret();
} }
} }
@@ -3240,5 +3234,53 @@ void wxRichTextCtrl::ClearAvailableFontNames()
sm_availableFontNames.Clear(); 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 #endif
// wxUSE_RICHTEXT // wxUSE_RICHTEXT

View File

@@ -33,6 +33,7 @@
*wxGridBagSizer*AdjustForOverflow*; *wxGridBagSizer*AdjustForOverflow*;
*wxRemotelyScrolledTreeCtrl*DoCalcScrolledPosition*; *wxRemotelyScrolledTreeCtrl*DoCalcScrolledPosition*;
*wxRemotelyScrolledTreeCtrl*SetScrollbar*; *wxRemotelyScrolledTreeCtrl*SetScrollbar*;
*wxRichTextAction*CalculateRefreshOptimizations*;
*wxRichTextCtrl*GetTextCursor*; *wxRichTextCtrl*GetTextCursor*;
*wxRichTextCtrl*GetURLCursor*; *wxRichTextCtrl*GetURLCursor*;
*wxRichTextCtrl*SetTextCursor*; *wxRichTextCtrl*SetTextCursor*;