]> git.saurik.com Git - wxWidgets.git/blobdiff - src/richtext/richtextbuffer.cpp
Reverted patch 1476893 pending further investigation (broke Advise in IPC)
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
index d250a15a0eed6ecd52ad3881a757b5ad9a3ef2ee..83b2e6f44933f46363b8381cf7d63cfd84e748e4 100644 (file)
@@ -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);
 }