X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d2d0adc7765dfe904f0f7e97d85a2638c934a7d6..7e134dc10c5412505716891154c67df3ee229efe:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index d250a15a0e..83b2e6f449 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -43,6 +43,9 @@ WX_DEFINE_LIST(wxRichTextObjectList) WX_DEFINE_LIST(wxRichTextLineList) +// Switch off if the platform doesn't like it for some reason +#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1 + /*! * wxRichTextObject * This is the base for drawable objects. @@ -103,11 +106,21 @@ void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin m_bottomMargin = bottomMargin; } -// Convert units in tends of a millimetre to device units +// Convert units in tenths of a millimetre to device units int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) { - int ppi = dc.GetPPI().x; + int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units); + + // Unscale + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer) + p = (int) ((double)p / buffer->GetScale()); + return p; +} +// Convert units in tenths of a millimetre to device units +int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units) +{ // There are ppi pixels in 254.1 "1/10 mm" double pixels = ((double) units * (double)ppi) / 254.1; @@ -123,6 +136,14 @@ void wxRichTextObject::Dump(wxTextOutputStream& stream) stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n"); } +/// Gets the containing buffer +wxRichTextBuffer* wxRichTextObject::GetBuffer() const +{ + const wxRichTextObject* obj = this; + while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer))) + obj = obj->GetParent(); + return wxDynamicCast(obj, wxRichTextBuffer); +} /*! * wxRichTextCompositeObject @@ -508,12 +529,17 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, { wxRect childRect(child->GetPosition(), child->GetCachedSize()); - if (childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop()) + if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom()) + { + // Stop drawing + break; + } + else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop()) { // Skip } else - child->Draw(dc, child->GetRange(), selectionRange, childRect, descent, style); + child->Draw(dc, range, selectionRange, childRect, descent, style); } node = node->GetNext(); @@ -528,7 +554,10 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT; // If only laying out a specific area, the passed rect has a different meaning: - // the visible part of the buffer. + // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize, + // so that during a size, only the visible part will be relaid out, or + // it would take too long causing flicker. As an approximation, we assume that + // everything up to the start of the visible area is laid out correctly. if (formatRect) { availableSpace = wxRect(0 + m_leftMargin, @@ -2399,6 +2428,8 @@ void wxRichTextParagraphLayoutBox::Reset() Clear(); AddParagraph(wxEmptyString); + + Invalidate(wxRICHTEXT_ALL); } /// Invalidate the buffer. With no argument, invalidates whole buffer. @@ -2670,7 +2701,9 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co { bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0); // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0); +#ifdef __WXDEBUG__ bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0); +#endif bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0); @@ -2975,7 +3008,7 @@ wxRichTextParagraph::~wxRichTextParagraph() } /// Draw the item -bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(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& WXUNUSED(rect), int WXUNUSED(descent), int style) { #if wxRICHTEXT_USE_DYNAMIC_STYLES wxTextAttrEx attr = GetCombinedAttributes(); @@ -3060,7 +3093,8 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(range), while (node2) { wxRichTextObject* child = node2->GetData(); - if (!child->GetRange().IsOutside(lineRange)) + + if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range)) { // Draw this part of the line at the correct position wxRichTextRange objectRange(child->GetRange()); @@ -3328,7 +3362,7 @@ void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttrEx& attr, const wx } else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) { - pos.x = rect.GetRight() - size.x; + pos.x = pos.x + rect.GetWidth() - size.x; line->SetPosition(pos); } @@ -4510,6 +4544,7 @@ void wxRichTextBuffer::Init() m_batchedCommand = NULL; m_suppressUndo = 0; m_handlerFlags = 0; + m_scale = 1.0; } /// Initialisation @@ -4522,19 +4557,12 @@ wxRichTextBuffer::~wxRichTextBuffer() ClearEventHandlers(); } -void wxRichTextBuffer::Clear() +void wxRichTextBuffer::ResetAndClearCommands() { - DeleteChildren(); + Reset(); + GetCommandProcessor()->ClearCommands(); - Modify(false); - Invalidate(wxRICHTEXT_ALL); -} -void wxRichTextBuffer::Reset() -{ - DeleteChildren(); - AddParagraph(wxEmptyString); - GetCommandProcessor()->ClearCommands(); Modify(false); Invalidate(wxRICHTEXT_ALL); } @@ -4794,7 +4822,7 @@ wxRichTextAttr wxRichTextBuffer::GetStyleForNewParagraph(long pos, bool caretPos { wxRichTextAttr numberingAttr; if (FindNextParagraphNumber(para, numberingAttr)) - wxRichTextApplyStyle(attr, numberingAttr); + wxRichTextApplyStyle(attr, (const wxRichTextAttr&) numberingAttr); } return attr; @@ -5629,7 +5657,7 @@ void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer) sm_renderer = renderer; } -bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect) +bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect) { if (bulletAttr.GetTextColour().Ok()) { @@ -5664,7 +5692,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par y = y + (charHeight+1)/2 - (bulletHeight+1)/2; // The margin between a bullet and text. - int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); + int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT) x = rect.x + rect.width - bulletWidth - margin; @@ -5702,7 +5730,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par return true; } -bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text) +bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text) { if (!text.empty()) { @@ -5735,7 +5763,7 @@ bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragra int y = rect.y + (rect.height - charHeight); // The margin between a bullet and text. - int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); + int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT) x = (rect.x + rect.width) - tw - margin; @@ -5893,6 +5921,57 @@ bool wxRichTextAction::Do() { 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 + // 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(GetPosition()); + 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 + m_buffer->InsertFragment(GetPosition(), m_newParagraphs); m_buffer->UpdateRanges(); m_buffer->Invalidate(GetRange()); @@ -5907,8 +5986,12 @@ bool wxRichTextAction::Do() newCaretPosition --; newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1)); + - UpdateAppearance(newCaretPosition, true /* send update event */); + if (optimizationLineCharPositions.GetCount() > 0) + UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions); + else + UpdateAppearance(newCaretPosition, true /* send update event */); break; } @@ -5953,7 +6036,7 @@ bool wxRichTextAction::Undo() long newCaretPosition = GetPosition() - 1; // if (m_newParagraphs.GetPartialParagraph()) // newCaretPosition --; - + UpdateAppearance(newCaretPosition, true /* send update event */); break; @@ -5985,7 +6068,7 @@ bool wxRichTextAction::Undo() } /// Update the control appearance -void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent) +void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions) { if (m_ctrl) { @@ -5994,7 +6077,99 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent { m_ctrl->LayoutContent(); m_ctrl->PositionCaret(); - m_ctrl->Refresh(false); + +#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) + { + size_t i; + + wxSize clientSize = m_ctrl->GetClientSize(); + wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint(); + + // Start/end positions + int firstY = 0; + int lastY = firstVisiblePt.y + clientSize.y; + + bool foundStart = false; + bool foundEnd = false; + + // position offset - how many characters were inserted + int positionOffset = GetRange().GetLength(); + + // 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()); + 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(); + + // we want to find the first line that is in the same position + // as before. This will mean we're at the end of the changed text. + + if (pt.y > lastY) // going past the end of the window, no more info + { + node2 = wxRichTextLineList::compatibility_iterator(); + node = wxRichTextObjectList::compatibility_iterator(); + } + 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++) + { + if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) && + ((*optimizationLineYPositions)[i] == pt.y)) + { + // Stop, we're now the same as we were + foundEnd = true; + lastY = pt.y - firstVisiblePt.y; + + node2 = wxRichTextLineList::compatibility_iterator(); + node = wxRichTextObjectList::compatibility_iterator(); + + break; + } + } + } + + if (node2) + node2 = node2->GetNext(); + } + + if (node) + node = node->GetNext(); + } + + if (!foundStart) + firstY = firstVisiblePt.y; + 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). + } + else +#endif + m_ctrl->Refresh(false); if (sendUpdateEvent) m_ctrl->SendTextUpdatedEvent(); @@ -6195,7 +6370,8 @@ bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2) attr1.GetBulletFont() == attr2.GetBulletFont() && attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() && attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName() && - attr1.GetListStyleName() == attr2.GetListStyleName()); + attr1.GetListStyleName() == attr2.GetListStyleName() && + attr1.HasPageBreak() == attr2.HasPageBreak()); } /// Compare two attribute objects, but take into account the flags @@ -6284,6 +6460,10 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, i !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs())) return false; + if ((flags & wxTEXT_ATTR_PAGE_BREAK) && + (attr1.HasPageBreak() != attr2.HasPageBreak())) + return false; + return true; } @@ -6374,6 +6554,10 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs())) return false; + if ((flags & wxTEXT_ATTR_PAGE_BREAK) && + (attr1.HasPageBreak() != attr2.HasPageBreak())) + return false; + return true; } @@ -6497,6 +6681,9 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style) if (style.HasURL()) destStyle.SetURL(style.GetURL()); + if (style.HasPageBreak()) + destStyle.SetPageBreak(); + return true; } @@ -6509,6 +6696,14 @@ bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style) return true; } +bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith) +{ + wxTextAttrEx attr(destStyle); + wxRichTextApplyStyle(attr, style, compareWith); + destStyle = attr; + return true; +} + bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith) { // Whole font. Avoiding setting individual attributes if possible, since @@ -6703,6 +6898,12 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, destStyle.SetURL(style.GetURL()); } + if (style.HasPageBreak()) + { + if (!(compareWith && compareWith->HasPageBreak())) + destStyle.SetPageBreak(); + } + return true; } @@ -7080,6 +7281,9 @@ wxRichTextAttr wxRichTextAttr::Combine(const wxRichTextAttr& attr, if (attr.HasURL()) newAttr.SetURL(attr.GetURL()); + if (attr.HasPageBreak()) + newAttr.SetPageBreak(); + return newAttr; } @@ -7087,7 +7291,7 @@ wxRichTextAttr wxRichTextAttr::Combine(const wxRichTextAttr& attr, * wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes. */ -wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr) +wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr() { Copy(attr); }