]> git.saurik.com Git - wxWidgets.git/commitdiff
Added paragraph symbol font (Get/SetSymbolFont in attribute)
authorJulian Smart <julian@anthemion.co.uk>
Sun, 8 Oct 2006 13:59:07 +0000 (13:59 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Sun, 8 Oct 2006 13:59:07 +0000 (13:59 +0000)
and use this font instead of paragraph font if specified.

wxRichTextBuffer::SetStyle extended to respond to flags for
specifying undo, optimization (only apply the style necessary
for display when content/base/paragraph attributes are combined),
and para/character styles only.

wxRichTextBuffer::GetStyleForRange gets the common attributes
for a range, e.g. for showing common attributes in a formatting
dialog.

Implemented Roman numeral bullet points.

Fixed wxRichTextImage copy bug.

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

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

index d8fa8a67f32df990997a166765c162d403b0881f..2ccc6ac95726d3a65d9892041088278729563d5e 100644 (file)
@@ -22,9 +22,9 @@
 
   The top of the hierarchy is the buffer, a kind of wxRichTextParagraphLayoutBox.
   These boxes will allow flexible placement of text boxes on a page, but
-  for now there will be a single box representing the document,
-  and this box will a wxRichTextParagraphLayoutBox which contains further
-  wxRichTextParagraph objects, each of which can include text and images.
+  for now there is a single box representing the document, and this box is
+  a wxRichTextParagraphLayoutBox which contains further wxRichTextParagraph
+  objects, each of which can include text and images.
 
   Each object maintains a range (start and end position) measured
   from the start of the main parent box.
   a position relative to that text box. For now, we will not be dealing with
   embedded objects but it's something to bear in mind for later.
 
+  Note that internally, a range (5,5) represents a range of one character.
+  In the public wx[Rich]TextCtrl API, this would be passed to e.g. SetSelection
+  as (5,6). A paragraph with one character might have an internal range of (0, 1)
+  since the end of the paragraph takes up one position.
+
   Layout
   ======
 
   in the vertical direction. The implementation of Layout can then
   cache the calculated size and position within the parent.
 
-  Note that position and size should be calculated separately, because
-  for example inserting a paragraph may result in the following paragraphs
-  moving down, but not changing in size.
-
-  Need to determine how objects specify their position. Absolute coordinates,
-  or relative to last object? May be hard to determine that. So should probably
-  be in absolute coordinates, in which case we'll need a Move virtual function
-  that allows quick movement of all elements without layout.
-
-  Let's work through a simple example of layout. Say we're laying out
-  a document with the buffer as the top box, with a wxRichTextParagraphLayoutBox
-  inside that that consists of wxRichTextParagraph objects.
-
-  We're in a mode whereby changes of window size change the width of the
-  page (useful for text editors, as opposed to word processors). The
-  window width is 600.
-
-  We pass (600, -1) to the top-level Layout (i.e. restrict size in horizontal
-  direction only). The wxRichTextBuffer box doesn't currently have
-  well-defined layout behaviour so we simply assume it has one child
-  that fills its parent (later we can define sizer-like box layout behaviour).
-  So it passes the same dimensions to the child, which is a wxRichTextParagraphLayoutBox.
-  This then looks at each child in turn (wxRichTextParagraph) and determines
-  the size the paragraph will take up, setting the cached size, and
-  splitting the paragraph into lines.
-
-  When the layout for one paragraph returns, the next paragraph is
-  fed the position of the previous, so it can position itself.
-
-  Each time Layout is called, the cached list of lines for each paragraph
-  is recreated, since it can change for example if the parent object width
-  changes.
-
-  Reporting size
-  ==============
-
-  Each object can report its size for a given range. It's important that
-  it can report a partial size, so that wrapping can be implemented,
-  hit test calculations performed, etc. So GetRangeSize must be implemented
-  for each object.
-
  */
 
 /*!
@@ -168,6 +132,29 @@ class WXDLLIMPEXP_RICHTEXT wxTextAttrEx;
 #define wxRICHTEXT_FORMATTED        0x01
 #define wxRICHTEXT_UNFORMATTED      0x02
 
+/*!
+ * Flags for SetStyle
+ */
+
+#define wxRICHTEXT_SETSTYLE_NONE            0x00
+
+// Specifies that this operation should be undoable
+#define wxRICHTEXT_SETSTYLE_WITH_UNDO       0x01
+
+// Specifies that the style should not be applied if the
+// combined style at this point is already the style in question.
+#define wxRICHTEXT_SETSTYLE_OPTIMIZE        0x02
+
+// Specifies that the style should only be applied to paragraphs,
+// and not the content. This allows content styling to be
+// preserved independently from that of e.g. a named paragraph style.
+#define wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY 0x04
+
+// Specifies that the style should only be applied to characters,
+// and not the paragraph. This allows content styling to be
+// preserved independently from that of e.g. a named paragraph style.
+#define wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY 0x08
+
 /*!
  * Flags for text insertion
  */
@@ -305,6 +292,7 @@ public:
     void SetBulletStyle(int style) { m_bulletStyle = style; SetFlags(GetFlags() | wxTEXT_ATTR_BULLET_STYLE); }
     void SetBulletNumber(int n) { m_bulletNumber = n; SetFlags(GetFlags() | wxTEXT_ATTR_BULLET_NUMBER); }
     void SetBulletSymbol(wxChar symbol) { m_bulletSymbol = symbol; SetFlags(GetFlags() | wxTEXT_ATTR_BULLET_SYMBOL); }
+    void SetBulletFont(const wxString& bulletFont) { m_bulletFont = bulletFont; }
 
     const wxString& GetCharacterStyleName() const { return m_characterStyleName; }
     const wxString& GetParagraphStyleName() const { return m_paragraphStyleName; }
@@ -314,6 +302,7 @@ public:
     int GetBulletStyle() const { return m_bulletStyle; }
     int GetBulletNumber() const { return m_bulletNumber; }
     wxChar GetBulletSymbol() const { return m_bulletSymbol; }
+    const wxString& GetBulletFont() const { return m_bulletFont; }
 
     bool HasWeight() const { return (GetFlags() & wxTEXT_ATTR_FONT_WEIGHT) != 0; }
     bool HasSize() const { return (GetFlags() & wxTEXT_ATTR_FONT_SIZE) != 0; }
@@ -360,6 +349,7 @@ private:
     int                 m_bulletStyle;
     int                 m_bulletNumber;
     wxChar              m_bulletSymbol;
+    wxString            m_bulletFont;
 
     // Character style
     wxString            m_characterStyleName;
@@ -431,6 +421,7 @@ public:
     void SetBulletStyle(int style) { m_bulletStyle = style; m_flags |= wxTEXT_ATTR_BULLET_STYLE; }
     void SetBulletNumber(int n) { m_bulletNumber = n; m_flags |= wxTEXT_ATTR_BULLET_NUMBER; }
     void SetBulletSymbol(wxChar symbol) { m_bulletSymbol = symbol; m_flags |= wxTEXT_ATTR_BULLET_NUMBER; }
+    void SetBulletFont(const wxString& bulletFont) { m_bulletFont = bulletFont; }
 
     const wxColour& GetTextColour() const { return m_colText; }
     const wxColour& GetBackgroundColour() const { return m_colBack; }
@@ -455,6 +446,7 @@ public:
     int GetBulletStyle() const { return m_bulletStyle; }
     int GetBulletNumber() const { return m_bulletNumber; }
     wxChar GetBulletSymbol() const { return m_bulletSymbol; }
+    const wxString& GetBulletFont() const { return m_bulletFont; }
 
     // accessors
     bool HasTextColour() const { return m_colText.Ok() && HasFlag(wxTEXT_ATTR_TEXT_COLOUR) ; }
@@ -520,6 +512,7 @@ private:
     int                 m_bulletStyle;
     int                 m_bulletNumber;
     wxChar              m_bulletSymbol;
+    wxString            m_bulletFont;
 
     // Character styles
     wxColour            m_colText,
@@ -537,11 +530,11 @@ private:
     wxString            m_paragraphStyleName;
 };
 
-#define wxTEXT_ATTR_CHARACTER (wxTEXT_ATTR_FONT | wxTEXT_ATTR_BACKGROUND_COLOUR | wxTEXT_ATTR_TEXT_COLOUR)
+#define wxTEXT_ATTR_CHARACTER (wxTEXT_ATTR_FONT | wxTEXT_ATTR_BACKGROUND_COLOUR | wxTEXT_ATTR_TEXT_COLOUR | wxTEXT_ATTR_CHARACTER_STYLE_NAME)
 
 #define wxTEXT_ATTR_PARAGRAPH (wxTEXT_ATTR_ALIGNMENT|wxTEXT_ATTR_LEFT_INDENT|wxTEXT_ATTR_RIGHT_INDENT|wxTEXT_ATTR_TABS|\
     wxTEXT_ATTR_PARA_SPACING_BEFORE|wxTEXT_ATTR_PARA_SPACING_AFTER|wxTEXT_ATTR_LINE_SPACING|\
-    wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_SYMBOL)
+    wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_SYMBOL|wxTEXT_ATTR_PARAGRAPH_STYLE_NAME)
 
 #define wxTEXT_ATTR_ALL (wxTEXT_ATTR_CHARACTER|wxTEXT_ATTR_PARAGRAPH)
 
@@ -937,8 +930,8 @@ public:
     virtual bool PositionToXY(long pos, long* x, long* y) const;
 
     /// Set text attributes: character and/or paragraph styles.
-    virtual bool SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, bool withUndo = true);
-    virtual bool SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, bool withUndo = true);
+    virtual bool SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO);
+    virtual bool SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO);
 
     /// Get the conbined text attributes for this position.
     virtual bool GetStyle(long position, wxTextAttrEx& style);
@@ -952,6 +945,14 @@ public:
     /// context attributes.
     virtual bool DoGetStyle(long position, wxTextAttrEx& style, bool combineStyles = true);
 
+    /// Get the combined style for a range - if any attribute is different within the range,
+    /// that attribute is not present within the flags
+    virtual bool GetStyleForRange(const wxRichTextRange& range, wxTextAttrEx& style);
+
+    /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
+    /// content.
+    bool CollectStyle(wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes);
+
     /// Test if this whole range has character attributes of the specified kind. If any
     /// of the attributes are different within the range, the test fails. You
     /// can use this to implement, for example, bold button updating. style must have
@@ -1199,7 +1200,7 @@ public:
 
     /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
     /// retrieve the actual style.
-    wxTextAttrEx GetCombinedAttributes(const wxTextAttr& contentStyle) const;
+    wxTextAttrEx GetCombinedAttributes(const wxTextAttrEx& contentStyle) const;
 
     /// Get combined attributes of the base style and paragraph style.
     wxTextAttrEx GetCombinedAttributes() const;
@@ -1990,10 +1991,20 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2,
 /// Apply one style to another
 bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style);
 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style);
-bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style);
+bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith = NULL);
+
+/// Compare tabs
+bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2);
+
+/// Set the font without changing the font attributes
+void wxSetFontPreservingStyles(wxTextAttr& attr, const wxFont& font);
+
+/// Convert a decimal to Roman numerals
+wxString wxRichTextDecimalToRoman(long n);
 
 #endif
     // wxUSE_RICHTEXT
 
 #endif
     // _WX_RICHTEXTBUFFER_H_
+
index d9d78dcb84fb58cd7ff150fcae6d6fd82050b942..78f53cf6ffe77854ac0c94ae009b2363c92941e0 100644 (file)
@@ -1556,7 +1556,7 @@ wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long pos
 }
 
 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
-bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, bool withUndo)
+bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
 {
     bool characterStyle = false;
     bool paragraphStyle = false;
@@ -1566,6 +1566,15 @@ bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const
     if (style.IsParagraphStyle())
         paragraphStyle = true;
 
+    bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
+    bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
+    bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
+    bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
+
+    // Limit the attributes to be set to the content to only character attributes.
+    wxRichTextAttr characterAttributes(style);
+    characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
+
     // If we are associated with a control, make undoable; otherwise, apply immediately
     // to the data.
 
@@ -1609,15 +1618,32 @@ bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const
                 else
                     newPara = para;
 
-                if (paragraphStyle)
-                    wxRichTextApplyStyle(newPara->GetAttributes(), style);
+                if (paragraphStyle && !charactersOnly)
+                {
+                    if (applyMinimal)
+                    {
+                        // Only apply attributes that will make a difference to the combined
+                        // style as seen on the display
+                        wxRichTextAttr combinedAttr(para->GetCombinedAttributes());
+                        wxRichTextApplyStyle(newPara->GetAttributes(), style, & combinedAttr);
+                    }
+                    else
+                        wxRichTextApplyStyle(newPara->GetAttributes(), style);
+                }
 
 #if wxRICHTEXT_USE_DYNAMIC_STYLES
                 // If applying paragraph styles dynamically, don't change the text objects' attributes
                 // since they will computed as needed. Only apply the character styling if it's _only_
                 // character styling. This policy is subject to change and might be put under user control.
 
-                if (!paragraphStyle && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
+                // Hm. we might well be applying a mix of paragraph and character styles, in which
+                // case we _do_ want to apply character styles regardless of what para styles are set.
+                // But if we're applying a paragraph style, which has some character attributes, but
+                // we only want the paragraphs to hold this character style, then we _don't_ want to
+                // apply the character style. So we need to be able to choose.
+
+                // if (!paragraphStyle && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
+                if (!parasOnly && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
 #else
                 if (characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
 #endif
@@ -1668,7 +1694,16 @@ bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const
                     {
                         wxRichTextObject* child = node2->GetData();
 
-                        wxRichTextApplyStyle(child->GetAttributes(), style);
+                        if (applyMinimal)
+                        {
+                            // Only apply attributes that will make a difference to the combined
+                            // style as seen on the display
+                            wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes()));
+                            wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
+                        }
+                        else
+                            wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
+
                         if (node2 == lastNode)
                             break;
 
@@ -1689,10 +1724,10 @@ bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const
 }
 
 /// Set text attributes
-bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, bool withUndo)
+bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, int flags)
 {
     wxRichTextAttr richStyle = style;
-    return SetStyle(range, richStyle, withUndo);
+    return SetStyle(range, richStyle, flags);
 }
 
 /// Get the text attributes for this position.
@@ -1782,6 +1817,405 @@ bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxTextAttrEx& style
     return false;
 }
 
+static bool wxHasStyle(long flags, long style)
+{
+    return (flags & style) != 0;
+}
+
+/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
+/// content.
+bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes)
+{
+    if (style.HasFont())
+    {
+        if (style.HasSize() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_SIZE))
+        {
+            if (currentStyle.GetFont().Ok() && currentStyle.HasSize())
+            {
+                if (currentStyle.GetFont().GetPointSize() != style.GetFont().GetPointSize())
+                {
+                    // Clash of style - mark as such
+                    multipleStyleAttributes |= wxTEXT_ATTR_FONT_SIZE;
+                    currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_SIZE);
+                }
+            }
+            else
+            {
+                if (!currentStyle.GetFont().Ok())
+                    wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
+                wxFont font(currentStyle.GetFont());
+                font.SetPointSize(style.GetFont().GetPointSize());
+
+                wxSetFontPreservingStyles(currentStyle, font);
+                currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
+            }
+        }
+
+        if (style.HasItalic() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_ITALIC))
+        {
+            if (currentStyle.GetFont().Ok() && currentStyle.HasItalic())
+            {
+                if (currentStyle.GetFont().GetStyle() != style.GetFont().GetStyle())
+                {
+                    // Clash of style - mark as such
+                    multipleStyleAttributes |= wxTEXT_ATTR_FONT_ITALIC;
+                    currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_ITALIC);
+                }
+            }
+            else
+            {
+                if (!currentStyle.GetFont().Ok())
+                    wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
+                wxFont font(currentStyle.GetFont());
+                font.SetStyle(style.GetFont().GetStyle());
+                wxSetFontPreservingStyles(currentStyle, font);
+                currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
+            }
+        }
+
+        if (style.HasWeight() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT))
+        {
+            if (currentStyle.GetFont().Ok() && currentStyle.HasWeight())
+            {
+                if (currentStyle.GetFont().GetWeight() != style.GetFont().GetWeight())
+                {
+                    // Clash of style - mark as such
+                    multipleStyleAttributes |= wxTEXT_ATTR_FONT_WEIGHT;
+                    currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_WEIGHT);
+                }
+            }
+            else
+            {
+                if (!currentStyle.GetFont().Ok())
+                    wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
+                wxFont font(currentStyle.GetFont());
+                font.SetWeight(style.GetFont().GetWeight());
+                wxSetFontPreservingStyles(currentStyle, font);
+                currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
+            }
+        }
+
+        if (style.HasFaceName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_FACE))
+        {
+            if (currentStyle.GetFont().Ok() && currentStyle.HasFaceName())
+            {
+                wxString faceName1(currentStyle.GetFont().GetFaceName());
+                wxString faceName2(style.GetFont().GetFaceName());
+
+                if (faceName1 != faceName2)
+                {
+                    // Clash of style - mark as such
+                    multipleStyleAttributes |= wxTEXT_ATTR_FONT_FACE;
+                    currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_FACE);
+                }
+            }
+            else
+            {
+                if (!currentStyle.GetFont().Ok())
+                    wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
+                wxFont font(currentStyle.GetFont());
+                font.SetFaceName(style.GetFont().GetFaceName());
+                wxSetFontPreservingStyles(currentStyle, font);
+                currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
+            }
+        }
+
+        if (style.HasUnderlined() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE))
+        {
+            if (currentStyle.GetFont().Ok() && currentStyle.HasUnderlined())
+            {
+                if (currentStyle.GetFont().GetUnderlined() != style.GetFont().GetUnderlined())
+                {
+                    // Clash of style - mark as such
+                    multipleStyleAttributes |= wxTEXT_ATTR_FONT_UNDERLINE;
+                    currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_UNDERLINE);
+                }
+            }
+            else
+            {
+                if (!currentStyle.GetFont().Ok())
+                    wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
+                wxFont font(currentStyle.GetFont());
+                font.SetUnderlined(style.GetFont().GetUnderlined());
+                wxSetFontPreservingStyles(currentStyle, font);
+                currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
+            }
+        }
+    }
+
+    if (style.HasTextColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR))
+    {
+        if (currentStyle.HasTextColour())
+        {
+            if (currentStyle.GetTextColour() != style.GetTextColour())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_TEXT_COLOUR;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_TEXT_COLOUR);
+            }
+        }
+        else
+            currentStyle.SetTextColour(style.GetTextColour());
+    }
+
+    if (style.HasBackgroundColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR))
+    {
+        if (currentStyle.HasBackgroundColour())
+        {
+            if (currentStyle.GetBackgroundColour() != style.GetBackgroundColour())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_BACKGROUND_COLOUR;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
+            }
+        }
+        else
+            currentStyle.SetBackgroundColour(style.GetBackgroundColour());
+    }
+
+    if (style.HasAlignment() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_ALIGNMENT))
+    {
+        if (currentStyle.HasAlignment())
+        {
+            if (currentStyle.GetAlignment() != style.GetAlignment())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_ALIGNMENT;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_ALIGNMENT);
+            }
+        }
+        else
+            currentStyle.SetAlignment(style.GetAlignment());
+    }
+
+    if (style.HasTabs() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TABS))
+    {
+        if (currentStyle.HasTabs())
+        {
+            if (!wxRichTextTabsEq(currentStyle.GetTabs(), style.GetTabs()))
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_TABS;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_TABS);
+            }
+        }
+        else
+            currentStyle.SetTabs(style.GetTabs());
+    }
+
+    if (style.HasLeftIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LEFT_INDENT))
+    {
+        if (currentStyle.HasLeftIndent())
+        {
+            if (currentStyle.GetLeftIndent() != style.GetLeftIndent() || currentStyle.GetLeftSubIndent() != style.GetLeftSubIndent())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_LEFT_INDENT;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT);
+            }
+        }
+        else
+            currentStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
+    }
+
+    if (style.HasRightIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT))
+    {
+        if (currentStyle.HasRightIndent())
+        {
+            if (currentStyle.GetRightIndent() != style.GetRightIndent())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_RIGHT_INDENT;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_RIGHT_INDENT);
+            }
+        }
+        else
+            currentStyle.SetRightIndent(style.GetRightIndent());
+    }
+
+    if (style.HasParagraphSpacingAfter() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER))
+    {
+        if (currentStyle.HasParagraphSpacingAfter())
+        {
+            if (currentStyle.HasParagraphSpacingAfter() != style.HasParagraphSpacingAfter())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_AFTER;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_AFTER);
+            }
+        }
+        else
+            currentStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
+    }
+
+    if (style.HasParagraphSpacingBefore() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE))
+    {
+        if (currentStyle.HasParagraphSpacingBefore())
+        {
+            if (currentStyle.HasParagraphSpacingBefore() != style.HasParagraphSpacingBefore())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_BEFORE);
+            }
+        }
+        else
+            currentStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
+    }
+
+    if (style.HasLineSpacing() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LINE_SPACING))
+    {
+        if (currentStyle.HasLineSpacing())
+        {
+            if (currentStyle.HasLineSpacing() != style.HasLineSpacing())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_LINE_SPACING;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_LINE_SPACING);
+            }
+        }
+        else
+            currentStyle.SetLineSpacing(style.GetLineSpacing());
+    }
+
+    if (style.HasCharacterStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
+    {
+        if (currentStyle.HasCharacterStyleName())
+        {
+            if (currentStyle.HasCharacterStyleName() != style.HasCharacterStyleName())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_CHARACTER_STYLE_NAME;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER_STYLE_NAME);
+            }
+        }
+        else
+            currentStyle.SetCharacterStyleName(style.GetCharacterStyleName());
+    }
+
+    if (style.HasParagraphStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
+    {
+        if (currentStyle.HasParagraphStyleName())
+        {
+            if (currentStyle.HasParagraphStyleName() != style.HasParagraphStyleName())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
+            }
+        }
+        else
+            currentStyle.SetParagraphStyleName(style.GetParagraphStyleName());
+    }
+
+    if (style.HasBulletStyle() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_STYLE))
+    {
+        if (currentStyle.HasBulletStyle())
+        {
+            if (currentStyle.HasBulletStyle() != style.HasBulletStyle())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_BULLET_STYLE;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_STYLE);
+            }
+        }
+        else
+            currentStyle.SetBulletStyle(style.GetBulletStyle());
+    }
+
+    if (style.HasBulletNumber() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER))
+    {
+        if (currentStyle.HasBulletNumber())
+        {
+            if (currentStyle.HasBulletNumber() != style.HasBulletNumber())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NUMBER;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NUMBER);
+            }
+        }
+        else
+            currentStyle.SetBulletNumber(style.GetBulletNumber());
+    }
+
+    if (style.HasBulletSymbol() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_SYMBOL))
+    {
+        if (currentStyle.HasBulletSymbol())
+        {
+            if (currentStyle.HasBulletSymbol() != style.HasBulletSymbol())
+            {
+                // Clash of style - mark as such
+                multipleStyleAttributes |= wxTEXT_ATTR_BULLET_SYMBOL;
+                currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_SYMBOL);
+            }
+        }
+        else
+        {
+            currentStyle.SetBulletSymbol(style.GetBulletSymbol());
+            currentStyle.SetBulletFont(style.GetBulletFont());
+        }
+    }
+
+    return true;
+}
+
+/// Get the combined style for a range - if any attribute is different within the range,
+/// that attribute is not present within the flags.
+/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
+/// nested.
+bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxTextAttrEx& style)
+{
+    style = wxTextAttrEx();
+
+    // The attributes that aren't valid because of multiple styles within the range
+    long multipleStyleAttributes = 0;
+
+    wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
+    while (node)
+    {
+        wxRichTextParagraph* para = (wxRichTextParagraph*) node->GetData();
+        if (!(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
+        {
+            if (para->GetChildren().GetCount() == 0)
+            {
+                wxTextAttrEx paraStyle = para->GetCombinedAttributes();
+
+                CollectStyle(style, paraStyle, multipleStyleAttributes);
+            }
+            else
+            {
+                wxRichTextRange paraRange(para->GetRange());
+                paraRange.LimitTo(range);
+
+                // First collect paragraph attributes only
+                wxTextAttrEx paraStyle = para->GetCombinedAttributes();
+                paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
+                CollectStyle(style, paraStyle, multipleStyleAttributes);
+
+                wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
+
+                while (childNode)
+                {
+                    wxRichTextObject* child = childNode->GetData();
+                    if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
+                    {
+                        wxTextAttrEx childStyle = para->GetCombinedAttributes(child->GetAttributes());
+
+                        // Now collect character attributes only
+                        childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
+
+                        CollectStyle(style, childStyle, multipleStyleAttributes);
+                    }
+
+                    childNode = childNode->GetNext();
+                }
+            }
+        }
+        node = node->GetNext();
+    }
+    return true;
+}
+
 /// Set default style
 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx& style)
 {
@@ -2039,10 +2473,7 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(range),
         if (attr.GetLeftSubIndent() != 0)
         {
             int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
-            // int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
             int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
-            // int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
-            // int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
 
             if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
             {
@@ -2053,11 +2484,26 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(range),
                 wxString bulletText = GetBulletText();
                 if (!bulletText.empty())
                 {
-                    if (attr.GetFont().Ok())
-                        dc.SetFont(attr.GetFont());
+                    // Get the combined font, or if a font is specified for a symbol bullet,
+                    // create the font
+
+                    wxTextAttrEx bulletAttr(GetCombinedAttributes());
+                    wxFont font;
+                    if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && bulletAttr.GetFont().Ok())
+                    {
+                        font = (*wxTheFontList->FindOrCreateFont(bulletAttr.GetFont().GetPointSize(), bulletAttr.GetFont().GetFamily(),
+                                                bulletAttr.GetFont().GetStyle(), bulletAttr.GetFont().GetWeight(), bulletAttr.GetFont().GetUnderlined(),
+                                                attr.GetBulletFont()));
+                    }
+                    else if (bulletAttr.GetFont().Ok())
+                        font = bulletAttr.GetFont();
+                    else
+                        font = (*wxNORMAL_FONT);
 
-                    if (attr.GetTextColour().Ok())
-                        dc.SetTextForeground(attr.GetTextColour());
+                    dc.SetFont(font);
+
+                    if (bulletAttr.GetTextColour().Ok())
+                        dc.SetTextForeground(bulletAttr.GetTextColour());
 
                     dc.SetBackgroundMode(wxTRANSPARENT);
 
@@ -2183,9 +2629,6 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
     if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
         startPositionFirstLine = startPositionSubsequentLines;
 
-    //bool restrictWidth = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_WIDTH);
-    //bool restrictHeight = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_HEIGHT);
-
     long lastEndPos = GetRange().GetStart()-1;
     long lastCompletedEndPos = lastEndPos;
 
@@ -2947,31 +3390,12 @@ wxString wxRichTextParagraph::GetBulletText()
     }
     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
     {
-        // TODO: convert from number to roman numeral
-        if (number == 1)
-            text = wxT("I");
-        else if (number == 2)
-            text = wxT("II");
-        else if (number == 3)
-            text = wxT("III");
-        else if (number == 4)
-            text = wxT("IV");
-        else
-            text = wxT("TODO");
+        text = wxRichTextDecimalToRoman(number);
     }
     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
     {
-        // TODO: convert from number to roman numeral
-        if (number == 1)
-            text = wxT("i");
-        else if (number == 2)
-            text = wxT("ii");
-        else if (number == 3)
-            text = wxT("iii");
-        else if (number == 4)
-            text = wxT("iv");
-        else
-            text = wxT("TODO");
+        text = wxRichTextDecimalToRoman(number);
+        text.MakeLower();
     }
     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
     {
@@ -3026,7 +3450,7 @@ bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
 
 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
 /// retrieve the actual style.
-wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes(const wxTextAttr& contentStyle) const
+wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes(const wxTextAttrEx& contentStyle) const
 {
     wxTextAttrEx attr;
     wxRichTextBuffer* buf = wxDynamicCast(GetParent(), wxRichTextBuffer);
@@ -3566,6 +3990,18 @@ bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagr
 #endif
 
     action->GetNewParagraphs() = paragraphs;
+
+    if (p)
+    {
+        wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
+        while (node)
+        {
+            wxRichTextParagraph* obj = (wxRichTextParagraph*) node->GetData();
+            obj->SetAttributes(*p);
+            node = node->GetPrevious();
+        }
+    }
+
     action->SetPosition(pos);
 
     // Set the range we'll need to delete in Undo
@@ -4423,7 +4859,7 @@ DECLARE_DYNAMIC_CLASS(wxRichTextModule)
 public:
     wxRichTextModule() {}
     bool OnInit() { wxRichTextBuffer::InitStandardHandlers(); return true; };
-    void OnExit() { wxRichTextBuffer::CleanUpHandlers(); };
+    void OnExit() { wxRichTextBuffer::CleanUpHandlers(); wxRichTextDecimalToRoman(-1); };
 };
 
 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
@@ -4774,6 +5210,8 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i
 /// Copy
 void wxRichTextImage::Copy(const wxRichTextImage& obj)
 {
+    wxRichTextObject::Copy(obj);
+
     m_image = obj.m_image;
     m_imageBlock = obj.m_imageBlock;
 }
@@ -4794,13 +5232,14 @@ bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2)
         attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
         attr1.GetRightIndent() == attr2.GetRightIndent() &&
         attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
-        attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
+        wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()) &&
         attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
         attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
         attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
         attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
         attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
         attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
+        attr1.GetBulletFont() == attr2.GetBulletFont() &&
         attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
         attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
 }
@@ -4819,13 +5258,14 @@ bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2)
         attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
         attr1.GetRightIndent() == attr2.GetRightIndent() &&
         attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
-        attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
+        wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()) &&
         attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
         attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
         attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
         attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
         attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
         attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
+        attr1.GetBulletFont() == attr2.GetBulletFont() &&
         attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
         attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
 }
@@ -4903,10 +5343,13 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, i
         (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
          return false;
 
-/* TODO
+    if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
+        (attr1.GetBulletFont() != attr2.GetBulletFont()))
+         return false;
+
     if ((flags & wxTEXT_ATTR_TABS) &&
+        !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
         return false;
-*/
 
     return true;
 }
@@ -4985,14 +5428,32 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2,
         (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
          return false;
 
-/* TODO
+    if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
+        (attr1.GetBulletFont() != attr2.GetBulletFont()))
+         return false;
+
     if ((flags & wxTEXT_ATTR_TABS) &&
+        !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
         return false;
-*/
 
     return true;
 }
 
+/// Compare tabs
+bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
+{
+    if (tabs1.GetCount() != tabs2.GetCount())
+        return false;
+
+    size_t i;
+    for (i = 0; i < tabs1.GetCount(); i++)
+    {
+        if (tabs1[i] != tabs2[i])
+            return false;
+    }
+    return true;
+}
+
 
 /// Apply one style to another
 bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
@@ -5081,6 +5542,7 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
     {
         destStyle.SetBulletStyle(style.GetBulletStyle());
         destStyle.SetBulletSymbol(style.GetBulletSymbol());
+        destStyle.SetBulletFont(style.GetBulletFont());
     }
 
     if (style.HasBulletNumber())
@@ -5098,11 +5560,11 @@ bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style)
     return true;
 }
 
-bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style)
+bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
 {
     // Whole font. Avoiding setting individual attributes if possible, since
     // it recreates the font each time.
-    if ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT))
+    if (((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT)) && !compareWith)
     {
         destStyle.SetFont(wxFont(style.GetFontSize(), destStyle.GetFont().Ok() ? destStyle.GetFont().GetFamily() : wxDEFAULT,
             style.GetFontStyle(), style.GetFontWeight(), style.GetFontUnderlined(), style.GetFontFaceName()));
@@ -5113,32 +5575,67 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style)
 
         if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
         {
-            destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
-            font.SetFaceName(style.GetFontFaceName());
+            if (compareWith && compareWith->HasFaceName() && compareWith->GetFontFaceName() == style.GetFontFaceName())
+            {
+                // The same as currently displayed, so don't set
+            }
+            else
+            {
+                destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
+                font.SetFaceName(style.GetFontFaceName());
+            }
         }
 
         if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
         {
-            destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
-            font.SetPointSize(style.GetFontSize());
+            if (compareWith && compareWith->HasSize() && compareWith->GetFontSize() == style.GetFontSize())
+            {
+                // The same as currently displayed, so don't set
+            }
+            else
+            {
+                destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
+                font.SetPointSize(style.GetFontSize());
+            }
         }
 
         if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
         {
-            destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
-            font.SetStyle(style.GetFontStyle());
+            if (compareWith && compareWith->HasItalic() && compareWith->GetFontStyle() == style.GetFontStyle())
+            {
+                // The same as currently displayed, so don't set
+            }
+            else
+            {
+                destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
+                font.SetStyle(style.GetFontStyle());
+            }
         }
 
         if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
         {
-            destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
-            font.SetWeight(style.GetFontWeight());
+            if (compareWith && compareWith->HasWeight() && compareWith->GetFontWeight() == style.GetFontWeight())
+            {
+                // The same as currently displayed, so don't set
+            }
+            else
+            {
+                destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
+                font.SetWeight(style.GetFontWeight());
+            }
         }
 
         if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
         {
-            destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
-            font.SetUnderlined(style.GetFontUnderlined());
+            if (compareWith && compareWith->HasUnderlined() && compareWith->GetFontUnderlined() == style.GetFontUnderlined())
+            {
+                // The same as currently displayed, so don't set
+            }
+            else
+            {
+                destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
+                font.SetUnderlined(style.GetFontUnderlined());
+            }
         }
 
         if (font != destStyle.GetFont())
@@ -5151,51 +5648,157 @@ bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style)
         }
     }
 
-    if ( style.GetTextColour().Ok() && style.HasTextColour())
-        destStyle.SetTextColour(style.GetTextColour());
+    if (style.GetTextColour().Ok() && style.HasTextColour())
+    {
+        if (!(compareWith && compareWith->HasTextColour() && compareWith->GetTextColour() == style.GetTextColour()))
+            destStyle.SetTextColour(style.GetTextColour());
+    }
 
-    if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
-        destStyle.SetBackgroundColour(style.GetBackgroundColour());
+    if (style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
+    {
+        if (!(compareWith && compareWith->HasBackgroundColour() && compareWith->GetBackgroundColour() == style.GetBackgroundColour()))
+            destStyle.SetBackgroundColour(style.GetBackgroundColour());
+    }
 
     if (style.HasAlignment())
-        destStyle.SetAlignment(style.GetAlignment());
+    {
+        if (!(compareWith && compareWith->HasAlignment() && compareWith->GetAlignment() == style.GetAlignment()))
+            destStyle.SetAlignment(style.GetAlignment());
+    }
 
     if (style.HasTabs())
-        destStyle.SetTabs(style.GetTabs());
+    {
+        if (!(compareWith && compareWith->HasTabs() && wxRichTextTabsEq(compareWith->GetTabs(), style.GetTabs())))
+            destStyle.SetTabs(style.GetTabs());
+    }
 
     if (style.HasLeftIndent())
-        destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
+    {
+        if (!(compareWith && compareWith->HasLeftIndent() && compareWith->GetLeftIndent() == style.GetLeftIndent()
+                          && compareWith->GetLeftSubIndent() == style.GetLeftSubIndent()))
+            destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
+    }
 
     if (style.HasRightIndent())
-        destStyle.SetRightIndent(style.GetRightIndent());
+    {
+        if (!(compareWith && compareWith->HasRightIndent() && compareWith->GetRightIndent() == style.GetRightIndent()))
+            destStyle.SetRightIndent(style.GetRightIndent());
+    }
 
     if (style.HasParagraphSpacingAfter())
-        destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
+    {
+        if (!(compareWith && compareWith->HasParagraphSpacingAfter() && compareWith->GetParagraphSpacingAfter() == style.GetParagraphSpacingAfter()))
+            destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
+    }
 
     if (style.HasParagraphSpacingBefore())
-        destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
+    {
+        if (!(compareWith && compareWith->HasParagraphSpacingBefore() && compareWith->GetParagraphSpacingBefore() == style.GetParagraphSpacingBefore()))
+            destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
+    }
 
     if (style.HasLineSpacing())
-        destStyle.SetLineSpacing(style.GetLineSpacing());
+    {
+        if (!(compareWith && compareWith->HasLineSpacing() && compareWith->GetLineSpacing() == style.GetLineSpacing()))
+            destStyle.SetLineSpacing(style.GetLineSpacing());
+    }
 
     if (style.HasCharacterStyleName())
-        destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
+    {
+        if (!(compareWith && compareWith->HasCharacterStyleName() && compareWith->GetCharacterStyleName() == style.GetCharacterStyleName()))
+            destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
+    }
 
     if (style.HasParagraphStyleName())
-        destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
+    {
+        if (!(compareWith && compareWith->HasParagraphStyleName() && compareWith->GetParagraphStyleName() == style.GetParagraphStyleName()))
+            destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
+    }
 
     if (style.HasBulletStyle())
     {
-        destStyle.SetBulletStyle(style.GetBulletStyle());
-        destStyle.SetBulletSymbol(style.GetBulletSymbol());
+        if (!(compareWith && compareWith->HasBulletStyle() && compareWith->GetBulletStyle() == style.GetBulletStyle()))
+            destStyle.SetBulletStyle(style.GetBulletStyle());
+    }
+
+    if (style.HasBulletSymbol())
+    {
+        if (!(compareWith && compareWith->HasBulletSymbol() && compareWith->GetBulletSymbol() == style.GetBulletSymbol()))
+        {
+            destStyle.SetBulletSymbol(style.GetBulletSymbol());
+            destStyle.SetBulletFont(style.GetBulletFont());
+        }
     }
 
     if (style.HasBulletNumber())
-        destStyle.SetBulletNumber(style.GetBulletNumber());
+    {
+        if (!(compareWith && compareWith->HasBulletNumber() && compareWith->GetBulletNumber() == style.GetBulletNumber()))
+            destStyle.SetBulletNumber(style.GetBulletNumber());
+    }
 
     return true;
 }
 
+void wxSetFontPreservingStyles(wxTextAttr& attr, const wxFont& font)
+{
+    long flags = attr.GetFlags();
+    attr.SetFont(font);
+    attr.SetFlags(flags);
+}
+
+/// Convert a decimal to Roman numerals
+wxString wxRichTextDecimalToRoman(long n)
+{
+    static wxArrayInt decimalNumbers;
+    static wxArrayString romanNumbers;
+
+    // Clean up arrays
+    if (n == -1)
+    {
+        decimalNumbers.Clear();
+        romanNumbers.Clear();
+        return wxEmptyString;
+    }
+
+    if (decimalNumbers.GetCount() == 0)
+    {
+        #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
+
+        wxRichTextAddDecRom(1000, wxT("M"));
+        wxRichTextAddDecRom(900, wxT("CM"));
+        wxRichTextAddDecRom(500, wxT("D"));
+        wxRichTextAddDecRom(400, wxT("CD"));
+        wxRichTextAddDecRom(100, wxT("C"));
+        wxRichTextAddDecRom(90, wxT("XC"));
+        wxRichTextAddDecRom(50, wxT("L"));
+        wxRichTextAddDecRom(40, wxT("XL"));
+        wxRichTextAddDecRom(10, wxT("X"));
+        wxRichTextAddDecRom(9, wxT("IX"));
+        wxRichTextAddDecRom(5, wxT("V"));
+        wxRichTextAddDecRom(4, wxT("IV"));
+        wxRichTextAddDecRom(1, wxT("I"));
+    }
+
+    int i = 0;
+    wxString roman;
+
+    while (n > 0 && i < 13)
+    {
+        if (n >= decimalNumbers[i])
+        {
+            n -= decimalNumbers[i];
+            roman += romanNumbers[i];
+        }
+        else
+        {
+            i ++;
+        }
+    }
+    if (roman.IsEmpty())
+        roman = wxT("0");
+    return roman;
+}
+
 
 /*!
  * wxRichTextAttr stores attributes without a wxFont object, so is a much more
@@ -5270,6 +5873,7 @@ void wxRichTextAttr::operator= (const wxRichTextAttr& attr)
     m_bulletStyle = attr.m_bulletStyle;
     m_bulletNumber = attr.m_bulletNumber;
     m_bulletSymbol = attr.m_bulletSymbol;
+    m_bulletFont = attr.m_bulletFont;
 }
 
 // operators
@@ -5289,6 +5893,10 @@ void wxRichTextAttr::operator= (const wxTextAttrEx& attr)
     m_lineSpacing = attr.GetLineSpacing();
     m_characterStyleName = attr.GetCharacterStyleName();
     m_paragraphStyleName = attr.GetParagraphStyleName();
+    m_bulletStyle = attr.GetBulletStyle();
+    m_bulletNumber = attr.GetBulletNumber();
+    m_bulletSymbol = attr.GetBulletSymbol();
+    m_bulletFont = attr.GetBulletFont();
 
     if (attr.GetFont().Ok())
         GetFontAttributes(attr.GetFont());
@@ -5314,7 +5922,7 @@ bool wxRichTextAttr::operator== (const wxRichTextAttr& attr) const
             GetLeftIndent() == attr.GetLeftIndent() &&
             GetLeftSubIndent() == attr.GetLeftSubIndent() &&
             GetRightIndent() == attr.GetRightIndent() &&
-            //GetTabs() == attr.GetTabs() &&
+            wxRichTextTabsEq(GetTabs(), attr.GetTabs()) &&
 
             GetParagraphSpacingAfter() == attr.GetParagraphSpacingAfter() &&
             GetParagraphSpacingBefore() == attr.GetParagraphSpacingBefore() &&
@@ -5322,6 +5930,11 @@ bool wxRichTextAttr::operator== (const wxRichTextAttr& attr) const
             GetCharacterStyleName() == attr.GetCharacterStyleName() &&
             GetParagraphStyleName() == attr.GetParagraphStyleName() &&
 
+            GetBulletStyle() == attr.GetBulletStyle() &&
+            GetBulletSymbol() == attr.GetBulletSymbol() &&
+            GetBulletNumber() == attr.GetBulletNumber() &&
+            GetBulletFont() == attr.GetBulletFont() &&
+
             m_fontSize == attr.m_fontSize &&
             m_fontStyle == attr.m_fontStyle &&
             m_fontWeight == attr.m_fontWeight &&
@@ -5346,6 +5959,7 @@ void wxRichTextAttr::CopyTo(wxTextAttrEx& attr) const
     attr.SetBulletStyle(m_bulletStyle);
     attr.SetBulletNumber(m_bulletNumber);
     attr.SetBulletSymbol(m_bulletSymbol);
+    attr.SetBulletFont(m_bulletFont);
     attr.SetCharacterStyleName(m_characterStyleName);
     attr.SetParagraphStyleName(m_paragraphStyleName);
 
@@ -5460,7 +6074,10 @@ wxRichTextAttr wxRichTextAttr::Combine(const wxRichTextAttr& attr,
         newAttr.SetBulletNumber(attr.GetBulletNumber());
 
     if (attr.HasBulletSymbol())
+    {
         newAttr.SetBulletSymbol(attr.GetBulletSymbol());
+        newAttr.SetBulletFont(attr.GetBulletFont());
+    }
 
     return newAttr;
 }
@@ -5479,6 +6096,7 @@ wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr(attr)
     m_bulletStyle = attr.m_bulletStyle;
     m_bulletNumber = attr.m_bulletNumber;
     m_bulletSymbol = attr.m_bulletSymbol;
+    m_bulletFont = attr.m_bulletFont;
 }
 
 // Initialise this object.
@@ -5506,6 +6124,7 @@ void wxTextAttrEx::operator= (const wxTextAttrEx& attr)
     m_bulletStyle = attr.m_bulletStyle;
     m_bulletNumber = attr.m_bulletNumber;
     m_bulletSymbol = attr.m_bulletSymbol;
+    m_bulletFont = attr.m_bulletFont;
 }
 
 // Assignment from a wxTextAttr object.
@@ -5546,7 +6165,7 @@ wxTextAttrEx wxTextAttrEx::CombineEx(const wxTextAttrEx& attr,
             font = *wxNORMAL_FONT;
 
         // Otherwise, if there are font attributes in attr, apply them
-        if (attr.HasFont())
+        if (attr.GetFlags() & wxTEXT_ATTR_FONT)
         {
             if (attr.HasSize())
             {
@@ -5648,7 +6267,10 @@ wxTextAttrEx wxTextAttrEx::CombineEx(const wxTextAttrEx& attr,
         newAttr.SetBulletNumber(attr.GetBulletNumber());
 
     if (attr.HasBulletSymbol())
+    {
         newAttr.SetBulletSymbol(attr.GetBulletSymbol());
+        newAttr.SetBulletFont(attr.GetBulletFont());
+    }
 
     return newAttr;
 }
@@ -6127,3 +6749,4 @@ bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
 
 #endif
     // wxUSE_RICHTEXT
+