]> git.saurik.com Git - wxWidgets.git/blobdiff - src/richtext/richtextbuffer.cpp
Forward declare classes instead of including their declarations.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
index af17e754620a24f3f9b3ba47c6a6bc1bc6ce0546..924074a58673a9140098b572f6b2f53c108c2129 100644 (file)
@@ -43,6 +43,7 @@
 #include "wx/richtext/richtextimagedlg.h"
 
 #include "wx/listimpl.cpp"
+#include "wx/arrimpl.cpp"
 
 WX_DEFINE_LIST(wxRichTextObjectList)
 WX_DEFINE_LIST(wxRichTextLineList)
@@ -270,11 +271,7 @@ void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
 
         if (floating->IsFloating())
         {
-            wxRichTextAnchoredObject* anchor = wxDynamicCast(floating, wxRichTextAnchoredObject);
-            if (anchor)
-            {
-                CollectFloat(para, floating);
-            }
+            CollectFloat(para, floating);
         }
 
         node = node->GetNext();
@@ -467,6 +464,7 @@ void wxRichTextObject::Copy(const wxRichTextObject& obj)
     m_dirty = obj.m_dirty;
     m_range = obj.m_range;
     m_attributes = obj.m_attributes;
+    m_properties = obj.m_properties;
     m_descent = obj.m_descent;
 }
 
@@ -486,21 +484,23 @@ void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin
 // Convert units in tenths of a millimetre to device units
 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
 {
-    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units);
-
     // Unscale
-    wxRichTextBuffer* buffer = GetBuffer();
-    if (buffer)
-        p = (int) ((double)p / buffer->GetScale());
+    double scale = 1.0;
+    if (GetBuffer())
+        scale = GetBuffer()->GetScale();
+    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
+
     return p;
 }
 
 // Convert units in tenths of a millimetre to device units
-int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
+int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
 {
     // There are ppi pixels in 254.1 "1/10 mm"
 
     double pixels = ((double) units * (double)ppi) / 254.1;
+    if (scale != 1.0)
+        pixels /= scale;
 
     return (int) pixels;
 }
@@ -509,19 +509,272 @@ int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
 {
     int p = pixels;
-    if (GetBuffer() && GetBuffer()->GetScale() != 1.0)
-        p = (int) (double(p) * GetBuffer()->GetScale());
-    return ConvertPixelsToTenthsMM(dc.GetPPI().x, p);
+    double scale = 1.0;
+    if (GetBuffer())
+        scale = GetBuffer()->GetScale();
+
+    return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
 }
 
-int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels)
+int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
 {
     // There are ppi pixels in 254.1 "1/10 mm"
-    
-    int units = int( double(pixels) * 254.1 / (double) ppi );
+
+    double p = double(pixels);
+
+    if (scale != 1.0)
+        p *= scale;
+
+    int units = int( p * 254.1 / (double) ppi );
     return units;
 }
 
+// Draw the borders and background for the given rectangle and attributes.
+// Width and height are taken to be the content size, so excluding any
+// border, margin and padding.
+bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, const wxRichTextAttr& attr, const wxRect& boxRect)
+{
+    // Assume boxRect is the area around the content
+    wxRect contentRect = boxRect;
+    wxRect marginRect, borderRect, paddingRect, outlineRect;
+
+    GetBoxRects(dc, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
+
+    // Margin is transparent. Draw background from margin.
+    if (attr.HasBackgroundColour())
+    {
+        wxPen pen(attr.GetBackgroundColour());
+        wxBrush brush(attr.GetBackgroundColour());
+
+        dc.SetPen(pen);
+        dc.SetBrush(brush);
+        dc.DrawRectangle(marginRect);
+    }
+
+    if (attr.GetTextBoxAttr().GetBorder().HasBorder())
+        DrawBorder(dc, attr.GetTextBoxAttr().GetBorder(), borderRect);
+
+    if (attr.GetTextBoxAttr().GetOutline().HasBorder())
+        DrawBorder(dc, attr.GetTextBoxAttr().GetOutline(), outlineRect);
+
+    return true;
+}
+
+// Draw a border
+bool wxRichTextObject::DrawBorder(wxDC& dc, const wxTextAttrBorders& attr, const wxRect& rect)
+{
+    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
+    wxTextAttrDimensionConverter converter(dc);
+
+    if (attr.GetLeft().IsValid())
+    {
+        borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
+        wxColour col(attr.GetLeft().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderLeft == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
+
+        }
+        else if (borderLeft > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
+        }
+    }
+
+    if (attr.GetRight().IsValid())
+    {
+        borderRight = converter.GetPixels(attr.GetRight().GetWidth());
+
+        wxColour col(attr.GetRight().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderRight == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height);
+
+        }
+        else if (borderRight > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x - borderRight, rect.y, borderRight, rect.height);
+        }
+    }
+
+    if (attr.GetTop().IsValid())
+    {
+        borderTop = converter.GetPixels(attr.GetTop().GetWidth());
+
+        wxColour col(attr.GetTop().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderTop == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
+
+        }
+        else if (borderTop > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
+        }
+    }
+
+    if (attr.GetBottom().IsValid())
+    {
+        borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
+        wxColour col(attr.GetTop().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderBottom == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
+
+        }
+        else if (borderBottom > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y - rect.height - borderBottom, rect.width, borderBottom);
+        }
+    }
+
+    return true;
+}
+
+// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
+// or marginRect (outer), and the other must be the default rectangle (no width or height).
+// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
+// is available.
+//
+// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
+
+bool wxRichTextObject::GetBoxRects(wxDC& dc, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
+{
+    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
+    int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
+    int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
+    int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
+
+    wxTextAttrDimensionConverter converter(dc);
+
+    if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsPresent())
+        marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
+    if (attr.GetTextBoxAttr().GetMargins().GetRight().IsPresent())
+        marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
+    if (attr.GetTextBoxAttr().GetMargins().GetTop().IsPresent())
+        marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
+    if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsPresent())
+        marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
+
+    if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsPresent())
+        borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsPresent())
+        borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsPresent())
+        borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsPresent())
+        borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
+
+    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsPresent())
+        paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
+    if (attr.GetTextBoxAttr().GetPadding().GetRight().IsPresent())
+        paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
+    if (attr.GetTextBoxAttr().GetPadding().GetTop().IsPresent())
+        paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
+    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsPresent())
+        paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
+
+    if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsPresent())
+        outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsPresent())
+        outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsPresent())
+        outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsPresent())
+        outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
+
+    int leftTotal = marginLeft + borderLeft + paddingLeft;
+    int rightTotal = marginRight + borderRight + paddingRight;
+    int topTotal = marginTop + borderTop + paddingTop;
+    int bottomTotal = marginBottom + borderBottom + paddingBottom;
+
+    if (marginRect != wxRect())
+    {
+        contentRect.x = marginRect.x + leftTotal;
+        contentRect.y = marginRect.y + topTotal;
+        contentRect.width = marginRect.width - (leftTotal + rightTotal);
+        contentRect.height = marginRect.height - (topTotal + bottomTotal);
+    }
+    else
+    {
+        marginRect.x = contentRect.x - leftTotal;
+        marginRect.y = contentRect.y - topTotal;
+        marginRect.width = contentRect.width + (leftTotal + rightTotal);
+        marginRect.height = contentRect.height + (topTotal + bottomTotal);
+    }
+
+    borderRect.x = marginRect.x + marginLeft;
+    borderRect.y = marginRect.y + marginTop;
+    borderRect.width = marginRect.width - (marginLeft + marginRight);
+    borderRect.height = marginRect.height - (marginTop + marginBottom);
+
+    paddingRect.x = marginRect.x + marginLeft + borderLeft;
+    paddingRect.y = marginRect.y + marginTop + borderTop;
+    paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
+    paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
+
+    // The outline is outside the margin and doesn't influence the overall box position or content size.
+    outlineRect.x = marginRect.x - outlineLeft;
+    outlineRect.y = marginRect.y - outlineTop;
+    outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
+    outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
+
+    return true;
+}
+
+
 /// Dump to output stream for debugging
 void wxRichTextObject::Dump(wxTextOutputStream& stream)
 {
@@ -808,94 +1061,52 @@ bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
             node = node->GetNext();
     }
 
-    return true;
-}
-
-/// Dump to output stream for debugging
-void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    while (node)
+    // Delete any remaining empty objects, but leave at least one empty object per composite object.
+    if (GetChildCount() > 1)
     {
-        wxRichTextObject* child = node->GetData();
-        child->Dump(stream);
-        node = node->GetNext();
+        node = m_children.GetFirst();
+        while (node)
+        {
+            wxRichTextObjectList::compatibility_iterator next = node->GetNext();
+            wxRichTextObject* child = node->GetData();
+            if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
+            {
+                if (child->IsEmpty())
+                {
+                    child->Dereference();
+                    m_children.Erase(node);
+                }
+                node = next;
+            }
+            else
+                node = node->GetNext();
+        }
     }
-}
 
-
-/*!
- * wxRichTextBox
- * This defines a 2D space to lay out objects
- */
-
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
-
-wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
-    wxRichTextCompositeObject(parent)
-{
-}
-
-/// Draw the item
-bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    while (node)
-    {
-        wxRichTextObject* child = node->GetData();
-
-        wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
-        child->Draw(dc, range, selectionRange, childRect, descent, style);
-
-        node = node->GetNext();
-    }
     return true;
 }
 
-/// Lay the item out
-bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
+/// Dump to output stream for debugging
+void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
 {
     wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
     while (node)
     {
         wxRichTextObject* child = node->GetData();
-        child->Layout(dc, rect, style);
-
+        child->Dump(stream);
         node = node->GetNext();
     }
-    m_dirty = false;
-    return true;
-}
-
-/// Get/set the size for the given range. Assume only has one child.
-bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    if (node)
-    {
-        wxRichTextObject* child = node->GetData();
-        return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
-    }
-    else
-        return false;
-}
-
-/// Copy
-void wxRichTextBox::Copy(const wxRichTextBox& obj)
-{
-    wxRichTextCompositeObject::Copy(obj);
 }
 
-
 /*!
  * wxRichTextParagraphLayoutBox
  * This box knows how to lay out paragraphs.
  */
 
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextBox)
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
 
 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
-    wxRichTextBox(parent)
+    wxRichTextCompositeObject(parent)
 {
     Init();
 }
@@ -926,6 +1137,27 @@ void wxRichTextParagraphLayoutBox::Init()
     m_floatCollector = NULL;
 }
 
+void wxRichTextParagraphLayoutBox::Clear()
+{
+    DeleteChildren();
+
+    if (m_floatCollector)
+        delete m_floatCollector;
+    m_floatCollector = NULL;
+    m_partialParagraph = false;
+}
+
+/// Copy
+void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
+{
+    Clear();
+
+    wxRichTextCompositeObject::Copy(obj);
+
+    m_partialParagraph = obj.m_partialParagraph;
+    m_defaultAttributes = obj.m_defaultAttributes;
+}
+
 // Gather information about floating objects
 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(int width, wxRichTextObject* untilObj)
 {
@@ -965,7 +1197,7 @@ void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, const wxRichTextRange& r
         m_floatCollector->Draw(dc, range, selectionRange, rect, descent, style);
 }
 
-void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextAnchoredObject* obj)
+void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
 {
     if (from == to)
         return;
@@ -1150,15 +1382,6 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl
     return true;
 }
 
-/// Copy
-void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
-{
-    wxRichTextBox::Copy(obj);
-
-    m_partialParagraph = obj.m_partialParagraph;
-    m_defaultAttributes = obj.m_defaultAttributes;
-}
-
 /// Get/set the size for the given range.
 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
 {
@@ -2309,7 +2532,7 @@ void wxRichTextParagraphLayoutBox::SetImageStyle(wxRichTextImage *image, const w
         action->SetRange(image->GetRange().FromInternal());
         action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
         image->SetAttributes(textAttr);
-        
+
         // Set the new attribute
         newPara = new wxRichTextParagraph(*para);
         action->GetNewParagraphs().AppendChild(newPara);
@@ -2548,11 +2771,6 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange&
     return foundCount == matchingCount && foundCount != 0;
 }
 
-void wxRichTextParagraphLayoutBox::Clear()
-{
-    DeleteChildren();
-}
-
 void wxRichTextParagraphLayoutBox::Reset()
 {
     Clear();
@@ -3389,7 +3607,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
     wxArrayInt partialExtents;
 
     wxSize paraSize;
-    int paraDescent;
+    int paraDescent = 0;
 
     // This calculates the partial text extents
     GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
@@ -3766,7 +3984,7 @@ bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
 
 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
 {
-    wxRichTextBox::Copy(obj);
+    wxRichTextCompositeObject::Copy(obj);
 }
 
 /// Clear the cached lines
@@ -4553,13 +4771,13 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
     wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
     while (node)
     {
-        wxRichTextAnchoredObject* anchored = wxDynamicCast(node->GetData(), wxRichTextAnchoredObject);
+        wxRichTextObject* anchored = node->GetData();
         if (anchored && anchored->IsFloating())
         {
             wxSize size;
             int descent, x = 0;
             anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, style);
-            
+
             int offsetY = 0;
             if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsPresent())
             {
@@ -4569,7 +4787,7 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
                     offsetY = ConvertTenthsMMToPixels(dc, offsetY);
                 }
             }
-            
+
             int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
 
             /* Update the offset */
@@ -4580,9 +4798,6 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
                     newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
                 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
             }
-            
-            // attr.m_offset = pos - rect.y;
-            //anchored->SetAnchoredAttr(attr);
 
             if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
                 x = 0;
@@ -4902,7 +5117,7 @@ bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr,
             dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
     }
 
-    wxCoord x_orig = x;
+    wxCoord x_orig = GetParent()->GetPosition().x;
     while (hasTabs)
     {
         // the string has a tab
@@ -5001,6 +5216,7 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
 
     wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
     wxASSERT (para != NULL);
+    int relativeX = position.x - GetParent()->GetPosition().x;
 
     wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
 
@@ -5087,9 +5303,9 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
                     partialExtents->Add(oldWidth + p[j]);
 
                 if (partialExtents->GetCount() > 0)
-                    absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + position.x;
+                    absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
                 else
-                    absoluteWidth = position.x;
+                    absoluteWidth = relativeX;
             }
             else
             {
@@ -5116,7 +5332,7 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
                     }
 
                     notFound = false;
-                    width = nextTabPos - position.x;
+                    width = nextTabPos - relativeX;
 
                     if (partialExtents)
                         partialExtents->Add(width);
@@ -5335,8 +5551,10 @@ void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
 
     m_styleSheet = obj.m_styleSheet;
     m_modified = obj.m_modified;
-    m_batchedCommandDepth = obj.m_batchedCommandDepth;
-    m_batchedCommand = obj.m_batchedCommand;
+    m_batchedCommandDepth = 0;
+    if (m_batchedCommand)
+        delete m_batchedCommand;
+    m_batchedCommand = NULL;
     m_suppressUndo = obj.m_suppressUndo;
 }
 
@@ -5544,7 +5762,6 @@ bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock&
     wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
     newPara->AppendChild(imageObject);
     imageObject->SetAttributes(textAttr);
-    //imageObject->SetAnchoredAttr(floatAttr);
     action->GetNewParagraphs().AppendChild(newPara);
     action->GetNewParagraphs().UpdateRanges();
 
@@ -6319,7 +6536,11 @@ bool wxRichTextBuffer::PasteFromClipboard(long position)
                     delete richTextBuffer;
                 }
             }
-            else if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT))
+            else if (wxTheClipboard->IsSupported(wxDF_TEXT)
+#if wxUSE_UNICODE
+                        || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
+#endif // wxUSE_UNICODE
+                    )
             {
                 wxTextDataObject data;
                 wxTheClipboard->GetData(data);
@@ -6383,9 +6604,12 @@ bool wxRichTextBuffer::CanPasteFromClipboard() const
 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
     if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
     {
-        if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT) ||
-            wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
-            wxTheClipboard->IsSupported(wxDF_BITMAP))
+        if (wxTheClipboard->IsSupported(wxDF_TEXT)
+#if wxUSE_UNICODE
+                || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
+#endif // wxUSE_UNICODE
+                || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId()))
+                || wxTheClipboard->IsSupported(wxDF_BITMAP))
         {
             canPaste = true;
         }
@@ -6643,6 +6867,68 @@ bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNa
     return true;
 }
 
+/*!
+ * wxRichTextBox
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
+
+wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
+    wxRichTextCompositeObject(parent)
+{
+}
+
+/// Draw the item
+bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
+{
+    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+    while (node)
+    {
+        wxRichTextObject* child = node->GetData();
+
+        wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
+        child->Draw(dc, range, selectionRange, childRect, descent, style);
+
+        node = node->GetNext();
+    }
+    return true;
+}
+
+/// Lay the item out
+bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+    while (node)
+    {
+        wxRichTextObject* child = node->GetData();
+        child->Layout(dc, rect, style);
+
+        node = node->GetNext();
+    }
+    m_dirty = false;
+    return true;
+
+}
+
+/// Copy
+void wxRichTextBox::Copy(const wxRichTextBox& obj)
+{
+    wxRichTextCompositeObject::Copy(obj);
+}
+
+/// Get/set the size for the given range. Assume only has one child.
+bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
+{
+    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+    if (node)
+    {
+        wxRichTextObject* child = node->GetData();
+        return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
+    }
+    else
+        return false;
+}
+
 /*
  * Module to initialise and clean up handlers
  */
@@ -7075,40 +7361,15 @@ bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
     return true;
 }
 
-/*!
- * wxRichTextAnchoredObject implementation
- */
-IMPLEMENT_CLASS(wxRichTextAnchoredObject, wxRichTextObject)
-
-wxRichTextAnchoredObject::wxRichTextAnchoredObject(wxRichTextObject* parent, const wxRichTextAttr& attr):
-    wxRichTextObject(parent)
-{
-    SetAttributes(attr);
-}
-
-wxRichTextAnchoredObject::~wxRichTextAnchoredObject()
-{
-}
-
-void wxRichTextAnchoredObject::Copy(const wxRichTextAnchoredObject& obj)
-{
-    wxRichTextObject::Copy(obj);
-}
-
-void wxRichTextAnchoredObject::SetParent(wxRichTextObject* parent)
-{
-    wxRichTextObject::SetParent(parent);
-}
-
 /*!
  * wxRichTextImage implementation
  * This object represents an image.
  */
 
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextAnchoredObject)
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
 
 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
-    wxRichTextAnchoredObject(parent)
+    wxRichTextObject(parent)
 {
     m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
     if (charStyle)
@@ -7116,7 +7377,7 @@ wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent,
 }
 
 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
-    wxRichTextAnchoredObject(parent)
+    wxRichTextObject(parent)
 {
     m_imageBlock = imageBlock;
     if (charStyle)
@@ -7138,7 +7399,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
 
         int width = image.GetWidth();
         int height = image.GetHeight();
-        
+
         if (GetAttributes().GetTextBoxAttr().GetWidth().IsPresent() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
         {
             if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
@@ -7243,7 +7504,7 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i
 /// Copy
 void wxRichTextImage::Copy(const wxRichTextImage& obj)
 {
-    wxRichTextAnchoredObject::Copy(obj);
+    wxRichTextObject::Copy(obj);
 
     m_imageBlock = obj.m_imageBlock;
 }
@@ -7584,7 +7845,7 @@ bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType i
 
     unsigned char* block = new unsigned char[memStream.GetSize()];
     if (!block)
-        return NULL;
+        return false;
 
     if (m_data)
         delete[] m_data;
@@ -7964,7 +8225,7 @@ void wxTextBoxAttr::Reset()
     m_floatMode = 0;
     m_clearMode = 0;
     m_collapseMode = 0;
-    
+
     m_margins.Reset();
     m_padding.Reset();
     m_position.Reset();
@@ -7984,13 +8245,13 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
         m_floatMode == attr.m_floatMode &&
         m_clearMode == attr.m_clearMode &&
         m_collapseMode == attr.m_collapseMode &&
-        
+
         m_margins == attr.m_margins &&
         m_padding == attr.m_padding &&
         m_position == attr.m_position &&
 
         m_width == attr.m_width &&
-        m_height == attr.m_height &&   
+        m_height == attr.m_height &&
 
         m_border == attr.m_border &&
         m_outline == attr.m_outline
@@ -8059,16 +8320,16 @@ bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compar
         if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
             SetCollapseBorders(true);
     }
-    
-    m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextBoxAttrDimensions*) NULL);
-    m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextBoxAttrDimensions*) NULL);
-    m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextBoxAttrDimensions*) NULL);
+
+    m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
+    m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
+    m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
 
     m_width.Apply(attr.m_width, compareWith ? (& attr.m_width) : (const wxTextAttrDimension*) NULL);
     m_height.Apply(attr.m_height, compareWith ? (& attr.m_height) : (const wxTextAttrDimension*) NULL);
 
-    m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextBoxAttrBorders*) NULL);
-    m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextBoxAttrBorders*) NULL);
+    m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
+    m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
 
     return true;
 }
@@ -8122,7 +8383,7 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
-        
+
     if (attr.HasClearMode())
     {
         if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
@@ -8160,7 +8421,7 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
-        
+
     m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
     m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
     m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
@@ -8176,8 +8437,8 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
 
 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
 {
-    wxTextAttr::Copy(attr); 
-    
+    wxTextAttr::Copy(attr);
+
     m_textBoxAttr = attr.m_textBoxAttr;
 }
 
@@ -8185,7 +8446,7 @@ bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
 {
     if (!(wxTextAttr::operator==(attr)))
         return false;
-        
+
     return (m_textBoxAttr == attr.m_textBoxAttr);
 }
 
@@ -8194,7 +8455,7 @@ bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
 {
     if (!(wxTextAttr::EqPartial(attr)))
         return false;
-        
+
     return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
 }
 
@@ -8221,12 +8482,12 @@ bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
 {
     wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
-    
+
     m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
 }
 
 // Partial equality test
-bool wxTextBoxAttrBorder::EqPartial(const wxTextBoxAttrBorder& border) const
+bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
 {
     if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
         return false;
@@ -8241,7 +8502,7 @@ bool wxTextBoxAttrBorder::EqPartial(const wxTextBoxAttrBorder& border) const
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrBorder::Apply(const wxTextBoxAttrBorder& border, const wxTextBoxAttrBorder* compareWith)
+bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
 {
     if (border.HasStyle())
     {
@@ -8263,7 +8524,7 @@ bool wxTextBoxAttrBorder::Apply(const wxTextBoxAttrBorder& border, const wxTextB
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrBorder::RemoveStyle(const wxTextBoxAttrBorder& attr)
+bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
 {
     if (attr.HasStyle() && HasStyle())
         SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
@@ -8277,7 +8538,7 @@ bool wxTextBoxAttrBorder::RemoveStyle(const wxTextBoxAttrBorder& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrBorder::CollectCommonAttributes(const wxTextBoxAttrBorder& attr, wxTextBoxAttrBorder& clashingAttr, wxTextBoxAttrBorder& absentAttr)
+void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
 {
     if (attr.HasStyle())
     {
@@ -8316,29 +8577,29 @@ void wxTextBoxAttrBorder::CollectCommonAttributes(const wxTextBoxAttrBorder& att
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
-        
+
     m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
 }
 
 // Partial equality test
-bool wxTextBoxAttrBorders::EqPartial(const wxTextBoxAttrBorders& borders) const
+bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
 {
     return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
             m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrBorders::Apply(const wxTextBoxAttrBorders& borders, const wxTextBoxAttrBorders* compareWith)
+bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
 {
-    m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextBoxAttrBorder*) NULL);
-    m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextBoxAttrBorder*) NULL);
-    m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextBoxAttrBorder*) NULL);
-    m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextBoxAttrBorder*) NULL);
+    m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
+    m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
+    m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
+    m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
     return true;
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrBorders::RemoveStyle(const wxTextBoxAttrBorders& attr)
+bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
 {
     m_left.RemoveStyle(attr.m_left);
     m_right.RemoveStyle(attr.m_right);
@@ -8349,7 +8610,7 @@ bool wxTextBoxAttrBorders::RemoveStyle(const wxTextBoxAttrBorders& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrBorders::CollectCommonAttributes(const wxTextBoxAttrBorders& attr, wxTextBoxAttrBorders& clashingAttr, wxTextBoxAttrBorders& absentAttr)
+void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
 {
     m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
     m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
@@ -8358,7 +8619,7 @@ void wxTextBoxAttrBorders::CollectCommonAttributes(const wxTextBoxAttrBorders& a
 }
 
 // Set style of all borders
-void wxTextBoxAttrBorders::SetStyle(int style)
+void wxTextAttrBorders::SetStyle(int style)
 {
     m_left.SetStyle(style);
     m_right.SetStyle(style);
@@ -8367,7 +8628,7 @@ void wxTextBoxAttrBorders::SetStyle(int style)
 }
 
 // Set colour of all borders
-void wxTextBoxAttrBorders::SetColour(unsigned long colour)
+void wxTextAttrBorders::SetColour(unsigned long colour)
 {
     m_left.SetColour(colour);
     m_right.SetColour(colour);
@@ -8375,7 +8636,7 @@ void wxTextBoxAttrBorders::SetColour(unsigned long colour)
     m_bottom.SetColour(colour);
 }
 
-void wxTextBoxAttrBorders::SetColour(const wxColour& colour)
+void wxTextAttrBorders::SetColour(const wxColour& colour)
 {
     m_left.SetColour(colour);
     m_right.SetColour(colour);
@@ -8384,7 +8645,7 @@ void wxTextBoxAttrBorders::SetColour(const wxColour& colour)
 }
 
 // Set width of all borders
-void wxTextBoxAttrBorders::SetWidth(const wxTextAttrDimension& width)
+void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
 {
     m_left.SetWidth(width);
     m_right.SetWidth(width);
@@ -8436,8 +8697,62 @@ void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& att
         absentAttr.SetPresent(true);
 }
 
+wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
+{
+    m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
+}
+
+wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
+{
+    m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
+}
+
+int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
+{
+    return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
+}
+
+int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
+{
+    return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
+}
+
+int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
+{
+    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+        return ConvertTenthsMMToPixels(dim.GetValue());
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+        return dim.GetValue();
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+    {
+        wxASSERT(m_parentSize != wxDefaultSize);
+        if (direction == wxHORIZONTAL)
+            return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
+        else
+            return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
+    }
+    else
+    {
+        wxASSERT(false);
+        return 0;
+    }
+}
+
+int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
+{
+    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+        return dim.GetValue();
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+        return ConvertPixelsToTenthsMM(dim.GetValue());
+    else
+    {
+        wxASSERT(false);
+        return 0;
+    }
+}
+
 // Partial equality test
-bool wxTextBoxAttrDimensions::EqPartial(const wxTextBoxAttrDimensions& dims) const
+bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
 {
     if (!m_left.EqPartial(dims.m_left))
         return false;
@@ -8455,7 +8770,7 @@ bool wxTextBoxAttrDimensions::EqPartial(const wxTextBoxAttrDimensions& dims) con
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrDimensions::Apply(const wxTextBoxAttrDimensions& dims, const wxTextBoxAttrDimensions* compareWith)
+bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
 {
     m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
     m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
@@ -8466,7 +8781,7 @@ bool wxTextBoxAttrDimensions::Apply(const wxTextBoxAttrDimensions& dims, const w
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrDimensions::RemoveStyle(const wxTextBoxAttrDimensions& attr)
+bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
 {
     if (attr.m_left.IsPresent())
         m_left.Reset();
@@ -8482,7 +8797,7 @@ bool wxTextBoxAttrDimensions::RemoveStyle(const wxTextBoxAttrDimensions& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrDimensions::CollectCommonAttributes(const wxTextBoxAttrDimensions& attr, wxTextBoxAttrDimensions& clashingAttr, wxTextBoxAttrDimensions& absentAttr)
+void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
 {
     m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
     m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
@@ -8912,6 +9227,139 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt
     }
 }
 
+WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
+
+bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
+{
+    if (m_properties.GetCount() != props.GetCount())
+        return false;
+
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        const wxVariant& var1 = m_properties[i];
+        int idx = props.Find(var1.GetName());
+        if (idx == -1)
+            return false;
+        const wxVariant& var2 = props.m_properties[idx];
+        if (!(var1 == var2))
+            return false;
+    }
+
+    return true;
+}
+
+wxArrayString wxRichTextProperties::GetPropertyNames() const
+{
+    wxArrayString arr;
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        arr.Add(m_properties[i].GetName());
+    }
+    return arr;
+}
+
+int wxRichTextProperties::Find(const wxString& name) const
+{
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        if (m_properties[i].GetName() == name)
+            return (int) i;
+    }
+    return -1;
+}
+
+wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
+{
+    int idx = Find(name);
+    if (idx == wxNOT_FOUND)
+        SetProperty(name, wxString());
+    idx = Find(name);
+    if (idx != wxNOT_FOUND)
+    {
+        return & (*this)[idx];
+    }
+    else
+        return NULL;
+}
+
+const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
+{
+    static const wxVariant nullVariant;
+    int idx = Find(name);
+    if (idx != -1)
+        return m_properties[idx];
+    else
+        return nullVariant;
+}
+
+wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
+{
+    return GetProperty(name).GetString();
+}
+
+long wxRichTextProperties::GetPropertyLong(const wxString& name) const
+{
+    return GetProperty(name).GetLong();
+}
+
+bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
+{
+    return GetProperty(name).GetBool();
+}
+
+double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
+{
+    return GetProperty(name).GetDouble();
+}
+
+void wxRichTextProperties::SetProperty(const wxVariant& variant)
+{
+    wxASSERT(!variant.GetName().IsEmpty());
+
+    int idx = Find(variant.GetName());
+
+    if (idx == -1)
+        m_properties.Add(variant);
+    else
+        m_properties[idx] = variant;
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
+{
+    int idx = Find(name);
+    wxVariant var(variant);
+    var.SetName(name);
+
+    if (idx == -1)
+        m_properties.Add(var);
+    else
+        m_properties[idx] = var;
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, long value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, double value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, bool value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
 
 #endif
     // wxUSE_RICHTEXT