]> git.saurik.com Git - wxWidgets.git/commitdiff
Reuse wxCaret object
authorJulian Smart <julian@anthemion.co.uk>
Mon, 30 Oct 2006 17:51:38 +0000 (17:51 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Mon, 30 Oct 2006 17:51:38 +0000 (17:51 +0000)
Add page break specification
Optimize drawing after character input

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42734 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/richtext/richtextbuffer.h
include/wx/richtext/richtextctrl.h
src/richtext/richtextbuffer.cpp
src/richtext/richtextctrl.cpp
src/richtext/richtextprint.cpp

index 7b49ad732db2f22bbe3b2776071416abcb839365..486d4a2607ca66b3b16bf171b29626d61a38fd1c 100644 (file)
@@ -198,6 +198,7 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextBuffer;
 #define wxTEXT_ATTR_BULLET_TEXT             0x00080000
 #define wxTEXT_ATTR_BULLET_NAME             0x00100000
 #define wxTEXT_ATTR_URL                     0x00200000
+#define wxTEXT_ATTR_PAGE_BREAK              0x00400000
 
 /*!
  * Styles for wxTextAttrEx::SetBulletStyle
@@ -346,6 +347,7 @@ public:
     void SetBulletName(const wxString& name) { m_bulletName = name; SetFlags(GetFlags() | wxTEXT_ATTR_BULLET_NAME); }
     void SetBulletFont(const wxString& bulletFont) { m_bulletFont = bulletFont; }
     void SetURL(const wxString& url) { m_urlTarget = url; SetFlags(GetFlags() | wxTEXT_ATTR_URL); }
+    void SetPageBreak(bool pageBreak = true) { SetFlags(pageBreak ? (GetFlags() | wxTEXT_ATTR_PAGE_BREAK) : (GetFlags() & ~wxTEXT_ATTR_PAGE_BREAK)); }
 
     const wxString& GetCharacterStyleName() const { return m_characterStyleName; }
     const wxString& GetParagraphStyleName() const { return m_paragraphStyleName; }
@@ -377,6 +379,7 @@ public:
     bool HasBulletText() const { return HasFlag(wxTEXT_ATTR_BULLET_TEXT); }
     bool HasBulletName() const { return HasFlag(wxTEXT_ATTR_BULLET_NAME); }
     bool HasURL() const { return HasFlag(wxTEXT_ATTR_URL); }
+    bool HasPageBreak() const { return HasFlag(wxTEXT_ATTR_PAGE_BREAK); }
 
     // Is this a character style?
     bool IsCharacterStyle() const { return (0 != (GetFlags() & wxTEXT_ATTR_CHARACTER)); }
@@ -492,6 +495,7 @@ public:
     void SetBulletFont(const wxString& bulletFont) { m_bulletFont = bulletFont; }
     void SetBulletName(const wxString& name) { m_bulletName = name; m_flags |= wxTEXT_ATTR_BULLET_NAME; }
     void SetURL(const wxString& url) { m_urlTarget = url; m_flags |= wxTEXT_ATTR_URL; }
+    void SetPageBreak(bool pageBreak = true) { SetFlags(pageBreak ? (GetFlags() | wxTEXT_ATTR_PAGE_BREAK) : (GetFlags() & ~wxTEXT_ATTR_PAGE_BREAK)); }
 
     const wxColour& GetTextColour() const { return m_colText; }
     const wxColour& GetBackgroundColour() const { return m_colBack; }
@@ -546,6 +550,7 @@ public:
     bool HasBulletText() const { return (m_flags & wxTEXT_ATTR_BULLET_TEXT) != 0; }
     bool HasBulletName() const { return (m_flags & wxTEXT_ATTR_BULLET_NAME) != 0; }
     bool HasURL() const { return HasFlag(wxTEXT_ATTR_URL); }
+    bool HasPageBreak() const { return HasFlag(wxTEXT_ATTR_PAGE_BREAK); }
 
     bool HasFlag(long flag) const { return (m_flags & flag) != 0; }
 
@@ -1985,7 +1990,8 @@ public:
     bool Undo();
 
     /// Update the control appearance
-    void UpdateAppearance(long caretPosition, bool sendUpdateEvent = false);
+    void UpdateAppearance(long caretPosition, bool sendUpdateEvent = false,
+                            wxArrayInt* optimizationLineCharPositions = NULL, wxArrayInt* optimizationLineYPositions = NULL);
 
     /// Replace the buffer paragraphs with the given fragment.
     void ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment);
index fa3a21753860bfb60463a482b6320be43146f557..7e17b06b43fcb23dc27eb04b65961d93ab1f4e7e 100644 (file)
@@ -760,6 +760,9 @@ public:
         SetCaretPositionForDefaultStyle(GetCaretPosition());
     }
 
+    /// Get the first visible point in the window
+    wxPoint GetFirstVisiblePoint() const;
+
 // Implementation
 
      /// Font names take a long time to retrieve, so cache them (on demand)
index 39129eb982fc68f8ef6e92458874950d0beb2f04..5730cbdeab09e1c717fdd14dc229a64282381630 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.
@@ -526,7 +529,12 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range,
         {
             wxRect childRect(child->GetPosition(), child->GetCachedSize());
 
-            if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && 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
             }
@@ -5911,6 +5919,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 = NULL;
+                            node = NULL;
+                        }
+                        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());
@@ -5925,8 +5984,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;
         }
@@ -5971,7 +6034,7 @@ bool wxRichTextAction::Undo()
             long newCaretPosition = GetPosition() - 1;
             // if (m_newParagraphs.GetPartialParagraph())
             //    newCaretPosition --;
-
+            
             UpdateAppearance(newCaretPosition, true /* send update event */);
 
             break;
@@ -6003,7 +6066,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)
     {
@@ -6012,7 +6075,98 @@ 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 = NULL;
+                            node = NULL;
+                        }
+                        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 = NULL;
+                                    node = NULL;
+                                    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();
@@ -6213,7 +6367,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
@@ -6302,6 +6457,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;
 }
 
@@ -6392,6 +6551,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;
 }
 
@@ -6515,6 +6678,9 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
     if (style.HasURL())
         destStyle.SetURL(style.GetURL());
 
+    if (style.HasPageBreak())
+        destStyle.SetPageBreak();
+
     return true;
 }
 
@@ -6729,6 +6895,12 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style,
             destStyle.SetURL(style.GetURL());
     }
 
+    if (style.HasPageBreak())
+    {
+        if (!(compareWith && compareWith->HasPageBreak()))
+            destStyle.SetPageBreak();
+    }
+
     return true;
 }
 
@@ -7106,6 +7278,9 @@ wxRichTextAttr wxRichTextAttr::Combine(const wxRichTextAttr& attr,
     if (attr.HasURL())
         newAttr.SetURL(attr.GetURL());
 
+    if (attr.HasPageBreak())
+        newAttr.SetPageBreak();
+
     return newAttr;
 }
 
index ae7de5772277316d46b13917d6f07560f1c65c08..ad6bccb6efeb920085b03ef258f857775bcbb996 100644 (file)
@@ -130,6 +130,9 @@ bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& va
 
     GetBuffer().Reset();
     GetBuffer().SetRichTextCtrl(this);
+    
+    SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16));
+    GetCaret()->Show();
 
     if (style & wxTE_READONLY)
         SetEditable(false);
@@ -241,7 +244,7 @@ void wxRichTextCtrl::Clear()
 /// Painting
 void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
 {
-    if (GetCaret())
+    if (GetCaret() && GetCaret()->IsVisible())
         GetCaret()->Hide();
 
     {
@@ -260,7 +263,11 @@ void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
         // Paint the background
         PaintBackground(dc);
 
-        wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
+        // wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
+
+        wxRect drawingArea(GetUpdateRegion().GetBox());
+        drawingArea.SetPosition(GetLogicalPoint(drawingArea.GetPosition()));
+
         wxRect availableSpace(GetClientSize());
         if (GetBuffer().GetDirty())
         {
@@ -272,7 +279,7 @@ void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
         GetBuffer().Draw(dc, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
     }
 
-    if (GetCaret())
+    if (GetCaret() && !GetCaret()->IsVisible())
         GetCaret()->Show();
 
     PositionCaret();
@@ -285,21 +292,24 @@ void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
 
 void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
 {
-    wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16);
-    SetCaret(caret);
-    caret->Show();
-    PositionCaret();
+    if (GetCaret())
+    {
+        if (!GetCaret()->IsVisible())
+            GetCaret()->Show();
+        PositionCaret();
+    }
 
-    if (!IsFrozen())
-        Refresh(false);
+    // if (!IsFrozen())
+    //    Refresh(false);
 }
 
 void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
 {
-    SetCaret(NULL);
+    if (GetCaret() && GetCaret()->IsVisible())
+        GetCaret()->Hide();
 
-    if (!IsFrozen())
-        Refresh(false);
+    // if (!IsFrozen())
+    //    Refresh(false);
 }
 
 /// Left-click
@@ -2945,6 +2955,18 @@ long wxRichTextCtrl::GetFirstVisiblePosition() const
         return 0;
 }
 
+/// Get the first visible point in the window
+wxPoint wxRichTextCtrl::GetFirstVisiblePoint() const
+{
+    int ppuX, ppuY;
+    int startXUnits, startYUnits;
+
+    GetScrollPixelsPerUnit(& ppuX, & ppuY);
+    GetViewStart(& startXUnits, & startYUnits);
+
+    return wxPoint(startXUnits * ppuX, startYUnits * ppuY);
+}
+
 /// The adjusted caret position is the character position adjusted to take
 /// into account whether we're at the start of a paragraph, in which case
 /// style information should be taken from the next position, not current one.
index 5319131f7452fa7252374e6e9b78e36c5bf36e85..8652db7c631609a86de6ccfee1503c76db10a8be 100644 (file)
@@ -80,18 +80,22 @@ void wxRichTextPrintout::OnPreparePrinting()
             // child is a paragraph
             wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
             wxASSERT (child != NULL);
-
+            
             wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
             while (node2)
             {
                 wxRichTextLine* line = node2->GetData();
-                
+
                 // Set the line to the page-adjusted position
                 line->SetPosition(wxPoint(line->GetPosition().x, line->GetPosition().y - yOffset));
 
                 int lineY = child->GetPosition().y + line->GetPosition().y;
                 
-                if ((lineY + line->GetSize().y) > rect.GetBottom())
+                // Break the page if either we're going off the bottom, or this paragraph specifies
+                // an explicit page break
+                
+                if (((lineY + line->GetSize().y) > rect.GetBottom()) ||
+                    ((node2 == child->GetLines().GetFirst()) && child->GetAttributes().HasPageBreak()))
                 {
                     // New page starting at this line
                     int newY = rect.y;