diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index c4bbb91781..cd131df4e9 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -65,12 +65,14 @@ #include "wx/image.h" #include "wx/cmdproc.h" #include "wx/txtstrm.h" +#include "wx/variant.h" #if wxUSE_DATAOBJ #include "wx/dataobj.h" #endif // Compatibility +//#define wxRichTextAttr wxTextAttr #define wxTextAttrEx wxTextAttr // Setting wxRICHTEXT_USE_OWN_CARET to 1 implements a @@ -87,6 +89,16 @@ // don't use for now #define wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING 0 +// The following two symbols determine whether an output implementation +// is present. To switch the relevant one on, set wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT in +// richtextxml.cpp. By default, the faster direct output implementation is used. + +// Include the wxXmlDocument implementation for output +#define wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT 1 + +// Include the faster, direct implementation for output +#define wxRICHTEXT_HAVE_DIRECT_OUTPUT 1 + /*! * Special characters */ @@ -123,8 +135,9 @@ class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextListStyleDefinition; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextEvent; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextRenderer; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextBuffer; -class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextAnchoredObject; -class wxRichTextFloatCollector; +class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextXMLHandler; +class WXDLLIMPEXP_FWD_XML wxXmlNode; +class wxRichTextFloatCollector; /*! * Flags determining the available space, passed to Layout @@ -298,6 +311,7 @@ public: void SetValueMM(float value) { m_value = (int) ((value * 10.0) + 0.5); m_flags |= wxTEXT_ATTR_VALUE_PRESENT; } void SetValue(int value) { m_value = value; m_flags |= wxTEXT_ATTR_VALUE_PRESENT; } void SetValue(int value, wxTextAttrDimensionFlags flags) { m_value = value; m_flags = flags; } + void SetValue(const wxTextAttrDimension& dim) { (*this) = dim; } wxTextAttrUnits GetUnits() const { return (wxTextAttrUnits) (m_flags & wxTEXT_ATTR_UNITS_MASK); } void SetUnits(wxTextAttrUnits units) { m_flags &= ~wxTEXT_ATTR_UNITS_MASK; m_flags |= units; } @@ -308,29 +322,45 @@ public: bool IsPresent() const { return (m_flags & wxTEXT_ATTR_VALUE_PRESENT) != 0; } void SetPresent(bool b) { m_flags &= ~wxTEXT_ATTR_VALUE_PRESENT_MASK; m_flags |= (b ? wxTEXT_ATTR_VALUE_PRESENT : 0); } + wxTextAttrDimensionFlags GetFlags() const { return m_flags; } + void SetFlags(wxTextAttrDimensionFlags flags) { m_flags = flags; } + int m_value; wxTextAttrDimensionFlags m_flags; }; -class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrDimensions +// A class for left, right, top and bottom dimensions +class WXDLLIMPEXP_RICHTEXT wxTextAttrDimensions { public: void Reset() { m_left.Reset(); m_top.Reset(); m_right.Reset(); m_bottom.Reset(); } - bool operator==(const wxTextBoxAttrDimensions& dims) const { return m_left == dims.m_left && m_top == dims.m_top && m_right == dims.m_right && m_bottom == dims.m_bottom; } + bool operator==(const wxTextAttrDimensions& dims) const { return m_left == dims.m_left && m_top == dims.m_top && m_right == dims.m_right && m_bottom == dims.m_bottom; } // Partial equality test - bool EqPartial(const wxTextBoxAttrDimensions& dims) const; + bool EqPartial(const wxTextAttrDimensions& dims) const; // Apply border to 'this', but not if the same as compareWith - bool Apply(const wxTextBoxAttrDimensions& dims, const wxTextBoxAttrDimensions* compareWith = NULL); + bool Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith = NULL); // 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 CollectCommonAttributes(const wxTextBoxAttrDimensions& attr, wxTextBoxAttrDimensions& clashingAttr, wxTextBoxAttrDimensions& absentAttr); + void CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr); // Remove specified attributes from this object - bool RemoveStyle(const wxTextBoxAttrDimensions& attr); + bool RemoveStyle(const wxTextAttrDimensions& attr); + + const wxTextAttrDimension& GetLeft() const { return m_left; } + wxTextAttrDimension& GetLeft() { return m_left; } + + const wxTextAttrDimension& GetRight() const { return m_right; } + wxTextAttrDimension& GetRight() { return m_right; } + + const wxTextAttrDimension& GetTop() const { return m_top; } + wxTextAttrDimension& GetTop() { return m_top; } + + const wxTextAttrDimension& GetBottom() const { return m_bottom; } + wxTextAttrDimension& GetBottom() { return m_bottom; } wxTextAttrDimension m_left; wxTextAttrDimension m_top; @@ -338,8 +368,29 @@ public: wxTextAttrDimension m_bottom; }; +// A class to make it easier to convert dimensions +class WXDLLIMPEXP_RICHTEXT wxTextAttrDimensionConverter +{ +public: + wxTextAttrDimensionConverter(wxDC& dc, double scale = 1.0, const wxSize& parentSize = wxDefaultSize) + { m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize; } + + wxTextAttrDimensionConverter(int ppi, double scale = 1.0, const wxSize& parentSize = wxDefaultSize) + { m_ppi = ppi; m_scale = scale; m_parentSize = parentSize; } + + int GetPixels(const wxTextAttrDimension& dim, int direction = wxHORIZONTAL) const; + int GetTenthsMM(const wxTextAttrDimension& dim) const; + + int ConvertTenthsMMToPixels(int units) const; + int ConvertPixelsToTenthsMM(int pixels) const; + + int m_ppi; + double m_scale; + wxSize m_parentSize; +}; + // Border styles -enum wxTextBoxAttrBorderStyle +enum wxTextAttrBorderStyle { wxTEXT_BOX_ATTR_BORDER_NONE = 0, wxTEXT_BOX_ATTR_BORDER_SOLID = 1, @@ -353,12 +404,20 @@ enum wxTextBoxAttrBorderStyle }; // Border style presence flags -enum wxTextBoxAttrBorderFlags +enum wxTextAttrBorderFlags { wxTEXT_BOX_ATTR_BORDER_STYLE = 0x0001, wxTEXT_BOX_ATTR_BORDER_COLOUR = 0x0002 }; +// Border width symbols for qualitative widths +enum wxTextAttrBorderWidth +{ + wxTEXT_BOX_ATTR_BORDER_THIN = -1, + wxTEXT_BOX_ATTR_BORDER_MEDIUM = -2, + wxTEXT_BOX_ATTR_BORDER_THICK = -3 +}; + // Float styles enum wxTextBoxAttrFloatStyle { @@ -384,12 +443,12 @@ enum wxTextBoxAttrCollapseMode }; // Border -class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrBorder +class WXDLLIMPEXP_RICHTEXT wxTextAttrBorder { public: - wxTextBoxAttrBorder() { Reset(); } + wxTextAttrBorder() { Reset(); } - bool operator==(const wxTextBoxAttrBorder& border) const + bool operator==(const wxTextAttrBorder& border) const { return m_flags == border.m_flags && m_borderStyle == border.m_borderStyle && m_borderColour == border.m_borderColour && m_borderWidth == border.m_borderWidth; @@ -398,17 +457,17 @@ public: void Reset() { m_borderStyle = 0; m_borderColour = 0; m_flags = 0; m_borderWidth.Reset(); } // Partial equality test - bool EqPartial(const wxTextBoxAttrBorder& border) const; + bool EqPartial(const wxTextAttrBorder& border) const; // Apply border to 'this', but not if the same as compareWith - bool Apply(const wxTextBoxAttrBorder& border, const wxTextBoxAttrBorder* compareWith = NULL); + bool Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith = NULL); // Remove specified attributes from this object - bool RemoveStyle(const wxTextBoxAttrBorder& attr); + bool RemoveStyle(const wxTextAttrBorder& 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 CollectCommonAttributes(const wxTextBoxAttrBorder& attr, wxTextBoxAttrBorder& clashingAttr, wxTextBoxAttrBorder& absentAttr); + void CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr); void SetStyle(int style) { m_borderStyle = style; m_flags |= wxTEXT_BOX_ATTR_BORDER_STYLE; } int GetStyle() const { return m_borderStyle; } @@ -422,10 +481,12 @@ public: const wxTextAttrDimension& GetWidth() const { return m_borderWidth; } void SetWidth(const wxTextAttrDimension& width) { m_borderWidth = width; } - bool HasStyle() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_STYLE) == 0; } - bool HasColour() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_COLOUR) == 0; } + bool HasStyle() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_STYLE) != 0; } + bool HasColour() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_COLOUR) != 0; } bool HasWidth() const { return m_borderWidth.IsPresent(); } + bool IsValid() const { return HasStyle() && HasColour() && HasWidth(); } + int GetFlags() const { return m_flags; } void SetFlags(int flags) { m_flags = flags; } void AddFlag(int flag) { m_flags |= flag; } @@ -438,12 +499,12 @@ public: }; // Borders -class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrBorders +class WXDLLIMPEXP_RICHTEXT wxTextAttrBorders { public: - wxTextBoxAttrBorders() { } + wxTextAttrBorders() { } - bool operator==(const wxTextBoxAttrBorders& borders) const + bool operator==(const wxTextAttrBorders& borders) const { return m_left == borders.m_left && m_right == borders.m_right && m_top == borders.m_top && m_bottom == borders.m_bottom; @@ -463,19 +524,33 @@ public: void Reset() { m_left.Reset(); m_right.Reset(); m_top.Reset(); m_bottom.Reset(); } // Partial equality test - bool EqPartial(const wxTextBoxAttrBorders& borders) const; + bool EqPartial(const wxTextAttrBorders& borders) const; // Apply border to 'this', but not if the same as compareWith - bool Apply(const wxTextBoxAttrBorders& borders, const wxTextBoxAttrBorders* compareWith = NULL); + bool Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith = NULL); // Remove specified attributes from this object - bool RemoveStyle(const wxTextBoxAttrBorders& attr); + bool RemoveStyle(const wxTextAttrBorders& 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 CollectCommonAttributes(const wxTextBoxAttrBorders& attr, wxTextBoxAttrBorders& clashingAttr, wxTextBoxAttrBorders& absentAttr); + void CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr); + + bool HasBorder() const { return m_left.IsValid() || m_right.IsValid() || m_top.IsValid() || m_bottom.IsValid(); } - wxTextBoxAttrBorder m_left, m_right, m_top, m_bottom; + const wxTextAttrBorder& GetLeft() const { return m_left; } + wxTextAttrBorder& GetLeft() { return m_left; } + + const wxTextAttrBorder& GetRight() const { return m_right; } + wxTextAttrBorder& GetRight() { return m_right; } + + const wxTextAttrBorder& GetTop() const { return m_top; } + wxTextAttrBorder& GetTop() { return m_top; } + + const wxTextAttrBorder& GetBottom() const { return m_bottom; } + wxTextAttrBorder& GetBottom() { return m_bottom; } + + wxTextAttrBorder m_left, m_right, m_top, m_bottom; }; @@ -496,7 +571,7 @@ public: // Reset this object. void Reset(); - // Copy. Unecessary since we let it do a binary copy + // Copy. Unnecessary since we let it do a binary copy //void Copy(const wxTextBoxAttr& attr); // Assignment @@ -556,6 +631,9 @@ public: // Margins + wxTextAttrDimensions& GetMargins() { return m_margins; } + const wxTextAttrDimensions& GetMargins() const { return m_margins; } + wxTextAttrDimension& GetLeftMargin() { return m_margins.m_left; } const wxTextAttrDimension& GetLeftMargin() const { return m_margins.m_left; } @@ -570,6 +648,9 @@ public: // Position + wxTextAttrDimensions& GetPosition() { return m_position; } + const wxTextAttrDimensions& GetPosition() const { return m_position; } + wxTextAttrDimension& GetLeft() { return m_position.m_left; } const wxTextAttrDimension& GetLeft() const { return m_position.m_left; } @@ -584,6 +665,9 @@ public: // Padding + wxTextAttrDimensions& GetPadding() { return m_padding; } + const wxTextAttrDimensions& GetPadding() const { return m_padding; } + wxTextAttrDimension& GetLeftPadding() { return m_padding.m_left; } const wxTextAttrDimension& GetLeftPadding() const { return m_padding.m_left; } @@ -598,37 +682,37 @@ public: // Border - wxTextBoxAttrBorders& GetBorder() { return m_border; } - const wxTextBoxAttrBorders& GetBorder() const { return m_border; } + wxTextAttrBorders& GetBorder() { return m_border; } + const wxTextAttrBorders& GetBorder() const { return m_border; } - wxTextBoxAttrBorder& GetLeftBorder() { return m_border.m_left; } - const wxTextBoxAttrBorder& GetLeftBorder() const { return m_border.m_left; } + wxTextAttrBorder& GetLeftBorder() { return m_border.m_left; } + const wxTextAttrBorder& GetLeftBorder() const { return m_border.m_left; } - wxTextBoxAttrBorder& GetTopBorder() { return m_border.m_top; } - const wxTextBoxAttrBorder& GetTopBorder() const { return m_border.m_top; } + wxTextAttrBorder& GetTopBorder() { return m_border.m_top; } + const wxTextAttrBorder& GetTopBorder() const { return m_border.m_top; } - wxTextBoxAttrBorder& GetRightBorder() { return m_border.m_right; } - const wxTextBoxAttrBorder& GetRightBorder() const { return m_border.m_right; } + wxTextAttrBorder& GetRightBorder() { return m_border.m_right; } + const wxTextAttrBorder& GetRightBorder() const { return m_border.m_right; } - wxTextBoxAttrBorder& GetBottomBorder() { return m_border.m_bottom; } - const wxTextBoxAttrBorder& GetBottomBorder() const { return m_border.m_bottom; } + wxTextAttrBorder& GetBottomBorder() { return m_border.m_bottom; } + const wxTextAttrBorder& GetBottomBorder() const { return m_border.m_bottom; } // Outline - wxTextBoxAttrBorders& GetOutline() { return m_outline; } - const wxTextBoxAttrBorders& GetOutline() const { return m_outline; } + wxTextAttrBorders& GetOutline() { return m_outline; } + const wxTextAttrBorders& GetOutline() const { return m_outline; } - wxTextBoxAttrBorder& GetLeftOutline() { return m_outline.m_left; } - const wxTextBoxAttrBorder& GetLeftOutline() const { return m_outline.m_left; } + wxTextAttrBorder& GetLeftOutline() { return m_outline.m_left; } + const wxTextAttrBorder& GetLeftOutline() const { return m_outline.m_left; } - wxTextBoxAttrBorder& GetTopOutline() { return m_outline.m_top; } - const wxTextBoxAttrBorder& GetTopOutline() const { return m_outline.m_top; } + wxTextAttrBorder& GetTopOutline() { return m_outline.m_top; } + const wxTextAttrBorder& GetTopOutline() const { return m_outline.m_top; } - wxTextBoxAttrBorder& GetRightOutline() { return m_outline.m_right; } - const wxTextBoxAttrBorder& GetRightOutline() const { return m_outline.m_right; } + wxTextAttrBorder& GetRightOutline() { return m_outline.m_right; } + const wxTextAttrBorder& GetRightOutline() const { return m_outline.m_right; } - wxTextBoxAttrBorder& GetBottomOutline() { return m_outline.m_bottom; } - const wxTextBoxAttrBorder& GetBottomOutline() const { return m_outline.m_bottom; } + wxTextAttrBorder& GetBottomOutline() { return m_outline.m_bottom; } + const wxTextAttrBorder& GetBottomOutline() const { return m_outline.m_bottom; } // Width and height @@ -643,15 +727,15 @@ public: int m_flags; - wxTextBoxAttrDimensions m_margins; - wxTextBoxAttrDimensions m_padding; - wxTextBoxAttrDimensions m_position; + wxTextAttrDimensions m_margins; + wxTextAttrDimensions m_padding; + wxTextAttrDimensions m_position; wxTextAttrDimension m_width; wxTextAttrDimension m_height; - wxTextBoxAttrBorders m_border; - wxTextBoxAttrBorders m_outline; + wxTextAttrBorders m_border; + wxTextAttrBorders m_outline; short int m_floatMode; short int m_clearMode; @@ -701,6 +785,57 @@ public: wxTextBoxAttr m_textBoxAttr; }; +WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT); + +// ---------------------------------------------------------------------------- +// wxRichTextProperties - A simple property class using wxVariants +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_RICHTEXT wxRichTextProperties: public wxObject +{ +DECLARE_DYNAMIC_CLASS(wxRichTextProperties) +public: + wxRichTextProperties() {} + wxRichTextProperties(const wxRichTextProperties& props) { Copy(props); } + + void operator=(const wxRichTextProperties& props) { Copy(props); } + bool operator==(const wxRichTextProperties& props) const; + void Copy(const wxRichTextProperties& props) { m_properties = props.m_properties; } + const wxVariant& operator[](size_t idx) const { return m_properties[idx]; } + wxVariant& operator[](size_t idx) { return m_properties[idx]; } + void Clear() { m_properties.Clear(); } + + const wxRichTextVariantArray& GetProperties() const { return m_properties; } + wxRichTextVariantArray& GetProperties() { return m_properties; } + void SetProperties(const wxRichTextVariantArray& props) { m_properties = props; } + + wxArrayString GetPropertyNames() const; + + size_t GetCount() const { return m_properties.GetCount(); } + + int HasProperty(const wxString& name) const { return Find(name) != -1; } + + int Find(const wxString& name) const; + const wxVariant& GetProperty(const wxString& name) const; + wxVariant* FindOrCreateProperty(const wxString& name); + + wxString GetPropertyString(const wxString& name) const; + long GetPropertyLong(const wxString& name) const; + bool GetPropertyBool(const wxString& name) const; + double GetPropertyDouble(const wxString& name) const; + + void SetProperty(const wxVariant& variant); + void SetProperty(const wxString& name, const wxVariant& variant); + void SetProperty(const wxString& name, const wxString& value); + void SetProperty(const wxString& name, long value); + void SetProperty(const wxString& name, double value); + void SetProperty(const wxString& name, bool value); + +protected: + wxRichTextVariantArray m_properties; +}; + + /*! * wxRichTextFontTable * Manages quick access to a pool of fonts for rendering rich text @@ -850,13 +985,13 @@ public: virtual bool IsFloatable() const { return false; } /// Whether this object is currently floating - virtual bool IsFloating() const { return false; } + virtual bool IsFloating() const { return GetAttributes().GetTextBoxAttr().IsFloating(); } /// Whether this object is a place holding one // virtual bool IsPlaceHolding() const { return false; } - /// Floating direction - virtual int GetFloatDirection() const { return wxTEXT_BOX_ATTR_FLOAT_NONE; } + /// The floating direction + virtual int GetFloatDirection() const { return GetAttributes().GetTextBoxAttr().GetFloatMode(); } /// Get any text in this object for the given range virtual wxString GetTextForRange(const wxRichTextRange& WXUNUSED(range)) const { return wxEmptyString; } @@ -877,6 +1012,27 @@ public: /// Edit properties via a GUI virtual bool EditProperties(wxWindow* WXUNUSED(parent), wxRichTextBuffer* WXUNUSED(buffer)) { return false; } +#if wxUSE_XML + /// Import this object from XML + virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + /// Export this object directly to the given stream. + virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + /// Export this object to the given parent node, usually creating at least one child node. + virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler); +#endif + + /// Does this object take note of paragraph attributes? Text and image objects don't. + virtual bool UsesParagraphAttributes() const { return true; } + + /// What is the XML node name of this object? + virtual wxString GetXMLNodeName() const { return wxT("unknown"); } + // Accessors /// Get/set the cached object size as calculated by Layout. @@ -916,10 +1072,15 @@ public: virtual int GetTopMargin() const { return m_topMargin; } virtual int GetBottomMargin() const { return m_bottomMargin; } - /// Set attributes object + /// Set/get attributes object void SetAttributes(const wxRichTextAttr& attr) { m_attributes = attr; } const wxRichTextAttr& GetAttributes() const { return m_attributes; } wxRichTextAttr& GetAttributes() { return m_attributes; } + + /// Set/get properties + wxRichTextProperties& GetProperties() { return m_properties; } + const wxRichTextProperties& GetProperties() const { return m_properties; } + void SetProperties(const wxRichTextProperties& props) { m_properties = props; } /// Set/get stored descent void SetDescent(int descent) { m_descent = descent; } @@ -943,11 +1104,25 @@ public: /// Convert units in tenths of a millimetre to device units int ConvertTenthsMMToPixels(wxDC& dc, int units) const; - static int ConvertTenthsMMToPixels(int ppi, int units); + static int ConvertTenthsMMToPixels(int ppi, int units, double scale = 1.0); /// Convert units in pixels to tenths of a millimetre int ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const; - static int ConvertPixelsToTenthsMM(int ppi, int pixels); + static int ConvertPixelsToTenthsMM(int ppi, int pixels, double scale = 1.0); + + /// 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. + static bool DrawBoxAttributes(wxDC& dc, const wxRichTextAttr& attr, const wxRect& boxRect); + + /// Draw a border + static bool DrawBorder(wxDC& dc, const wxTextAttrBorders& attr, const wxRect& rect); + + /// 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. + static bool GetBoxRects(wxDC& dc, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect); protected: wxSize m_size; @@ -968,6 +1143,9 @@ protected: /// Attributes wxRichTextAttr m_attributes; + + /// Properties + wxRichTextProperties m_properties; }; WX_DECLARE_LIST_WITH_DECL( wxRichTextObject, wxRichTextObjectList, class WXDLLIMPEXP_RICHTEXT ); @@ -1056,58 +1234,19 @@ protected: wxRichTextObjectList m_children; }; -/*! - * wxRichTextBox class declaration - * This defines a 2D space to lay out objects - */ - -class WXDLLIMPEXP_RICHTEXT wxRichTextBox: public wxRichTextCompositeObject -{ - DECLARE_DYNAMIC_CLASS(wxRichTextBox) -public: -// Constructors - - wxRichTextBox(wxRichTextObject* parent = NULL); - wxRichTextBox(const wxRichTextBox& obj): wxRichTextCompositeObject() { Copy(obj); } - -// Overrideables - - /// Draw the item - virtual bool Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style); - - /// Lay the item out - virtual bool Layout(wxDC& dc, const wxRect& rect, int style); - - /// Get/set the object size for the given range. Returns false if the range - /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; - -// Accessors - -// Operations - - /// Clone - virtual wxRichTextObject* Clone() const { return new wxRichTextBox(*this); } - - /// Copy - void Copy(const wxRichTextBox& obj); - -protected: -}; - /*! * wxRichTextParagraphBox class declaration * This box knows how to lay out paragraphs. */ -class WXDLLIMPEXP_RICHTEXT wxRichTextParagraphLayoutBox: public wxRichTextBox +class WXDLLIMPEXP_RICHTEXT wxRichTextParagraphLayoutBox: public wxRichTextCompositeObject { DECLARE_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox) public: // Constructors wxRichTextParagraphLayoutBox(wxRichTextObject* parent = NULL); - wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj): wxRichTextBox() { Init(); Copy(obj); } + wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj): wxRichTextCompositeObject() { Init(); Copy(obj); } ~wxRichTextParagraphLayoutBox(); // Overrideables @@ -1132,6 +1271,24 @@ public: /// Get any text in this object for the given range virtual wxString GetTextForRange(const wxRichTextRange& range) const; +#if wxUSE_XML + /// Import this object from XML + virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + /// Export this object directly to the given stream. + virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + /// Export this object to the given parent node, usually creating at least one child node. + virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler); +#endif + + /// What is the XML node name of this object? + virtual wxString GetXMLNodeName() const { return wxT("paragraphlayout"); } + // Accessors /// Associate a control with the buffer, for operations that for example require refreshing the window. @@ -1153,7 +1310,7 @@ public: void DrawFloats(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style); /// Move an anchored object to another paragraph - void MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextAnchoredObject* obj); + void MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj); /// Initialize the object. void Init(); @@ -1351,6 +1508,41 @@ protected: wxRichTextFloatCollector* m_floatCollector; }; +/*! + * wxRichTextBox class declaration + * TODO: a floating text box + */ + +class WXDLLIMPEXP_RICHTEXT wxRichTextBox: public wxRichTextParagraphLayoutBox +{ + DECLARE_DYNAMIC_CLASS(wxRichTextBox) +public: +// Constructors + + wxRichTextBox(wxRichTextObject* parent = NULL); + wxRichTextBox(const wxRichTextBox& obj): wxRichTextParagraphLayoutBox() { Copy(obj); } + +// Overrideables + + /// Draw the item + virtual bool Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style); + + /// Lay the item out + virtual bool Layout(wxDC& dc, const wxRect& rect, int style); + +// Accessors + +// Operations + + /// Clone + virtual wxRichTextObject* Clone() const { return new wxRichTextBox(*this); } + + /// Copy + void Copy(const wxRichTextBox& obj); + +protected: +}; + /*! * wxRichTextLine class declaration * This object represents a line in a paragraph, and stores @@ -1480,6 +1672,9 @@ public: /// Calculate range virtual void CalculateRange(long start, long& end); + /// What is the XML node name of this object? + virtual wxString GetXMLNodeName() const { return wxT("paragraph"); } + // Accessors /// Get the cached lines @@ -1621,6 +1816,27 @@ public: /// Get the first position from pos that has a line break character. long GetFirstLineBreakPosition(long pos); + /// Does this object take note of paragraph attributes? Text and image objects don't. + virtual bool UsesParagraphAttributes() const { return false; } + +#if wxUSE_XML + /// Import this object from XML + virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + /// Export this object directly to the given stream. + virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + /// Export this object to the given parent node, usually creating at least one child node. + virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler); +#endif + + /// What is the XML node name of this object? + virtual wxString GetXMLNodeName() const { return wxT("text"); } + // Accessors /// Get the text @@ -1732,56 +1948,21 @@ protected: wxBitmapType m_imageType; }; -/*! - * wxRichTextAnchoredObject class declaration - * This object is an abstract one that represent some objects which can floats - */ -class WXDLLIMPEXP_RICHTEXT wxRichTextAnchoredObject: public wxRichTextObject -{ - DECLARE_CLASS(wxRichTextAnchoredObject) -public: -// Constructors - wxRichTextAnchoredObject(wxRichTextObject* parent = NULL, const wxRichTextAttr& attr = wxRichTextAttr()); - wxRichTextAnchoredObject(const wxRichTextAnchoredObject& obj) : wxRichTextObject(obj) /* , m_ph(NULL) */ { Copy(obj); } - ~wxRichTextAnchoredObject(); - -// Virtuals - virtual bool IsFloatable() const { return true; } - - /// Whether this object is currently floating - virtual bool IsFloating() const { return GetAttributes().GetTextBoxAttr().IsFloating(); } - - virtual void SetParent(wxRichTextObject* parent); - -// Accessors - - /// The floating direction - virtual int GetFloatDirection() const { return GetAttributes().GetTextBoxAttr().GetFloatMode(); } - - void operator=(const wxRichTextAnchoredObject&) { wxASSERT("Nobody can reset this object using ="); } - -// Functions - void Copy(const wxRichTextAnchoredObject& obj); - -protected: - -}; - /*! * wxRichTextImage class declaration * This object represents an image. */ -class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextAnchoredObject +class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextObject { DECLARE_DYNAMIC_CLASS(wxRichTextImage) public: // Constructors - wxRichTextImage(wxRichTextObject* parent = NULL): wxRichTextAnchoredObject(parent) { } + wxRichTextImage(wxRichTextObject* parent = NULL): wxRichTextObject(parent) { } wxRichTextImage(const wxImage& image, wxRichTextObject* parent = NULL, wxRichTextAttr* charStyle = NULL); wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent = NULL, wxRichTextAttr* charStyle = NULL); - wxRichTextImage(const wxRichTextImage& obj): wxRichTextAnchoredObject(obj) { Copy(obj); } + wxRichTextImage(const wxRichTextImage& obj): wxRichTextObject(obj) { Copy(obj); } // Overrideables @@ -1795,8 +1976,8 @@ public: /// is invalid for this object. virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; - /// Returns true if the object is empty - virtual bool IsEmpty() const { return !m_imageBlock.Ok(); } + /// Returns true if the object is empty. An image is never empty; if the image is broken, that's not the same thing as empty. + virtual bool IsEmpty() const { return false; /* !m_imageBlock.Ok(); */ } /// Can we edit properties via a GUI? virtual bool CanEditProperties() const { return true; } @@ -1804,6 +1985,30 @@ public: /// Edit properties via a GUI virtual bool EditProperties(wxWindow* parent, wxRichTextBuffer* buffer); + /// Does this object take note of paragraph attributes? Text and image objects don't. + virtual bool UsesParagraphAttributes() const { return false; } + +#if wxUSE_XML + /// Import this object from XML + virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + /// Export this object directly to the given stream. + virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler); +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + /// Export this object to the given parent node, usually creating at least one child node. + virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler); +#endif + + // Images can be floatable (optionally). + virtual bool IsFloatable() const { return true; } + + /// What is the XML node name of this object? + virtual wxString GetXMLNodeName() const { return wxT("image"); } + // Accessors /// Get the image cache (scaled bitmap) @@ -2086,6 +2291,9 @@ public: /// Copy void Copy(const wxRichTextBuffer& obj); + /// Assignment + void operator= (const wxRichTextBuffer& obj) { Copy(obj); } + /// Clone virtual wxRichTextObject* Clone() const { return new wxRichTextBuffer(*this); } diff --git a/include/wx/richtext/richtextxml.h b/include/wx/richtext/richtextxml.h index ca114c748c..a14e397bde 100644 --- a/include/wx/richtext/richtextxml.h +++ b/include/wx/richtext/richtextxml.h @@ -26,6 +26,7 @@ */ class WXDLLIMPEXP_FWD_XML wxXmlNode; +class WXDLLIMPEXP_FWD_XML wxXmlDocument; class WXDLLIMPEXP_RICHTEXT wxRichTextXMLHandler: public wxRichTextFileHandler { @@ -33,30 +34,59 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextXMLHandler: public wxRichTextFileHandler public: wxRichTextXMLHandler(const wxString& name = wxT("XML"), const wxString& ext = wxT("xml"), int type = wxRICHTEXT_TYPE_XML) : wxRichTextFileHandler(name, ext, type) - { } + { Init(); } + + void Init(); #if wxUSE_STREAMS + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT /// Recursively export an object - bool ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int level); - bool ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level); - - /// Recursively import an object - bool ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node); - bool ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node); - - /// Create style parameters - wxString CreateStyle(const wxRichTextAttr& attr, bool isPara = false); - - /// Get style parameters - bool GetStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara = false); + bool ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int level); + bool ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level); + wxString AddAttributes(const wxRichTextAttr& attr, bool isPara = false); + bool WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level); + void OutputString(wxOutputStream& stream, const wxString& str); + void OutputStringEnt(wxOutputStream& stream, const wxString& str); + void OutputIndentation(wxOutputStream& stream, int indent); + static wxString AttributeToXML(const wxString& str); #endif +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + bool ExportXML(wxXmlNode* parent, wxRichTextObject& obj); + bool ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def); + bool AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara = false); + bool WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties); +#endif + + /// Make a string from the given property. This can be overridden for custom variants. + virtual wxString MakeStringFromProperty(const wxVariant& var); + + /// Create a proprty from the string read from the XML file. + virtual wxVariant MakePropertyFromString(const wxString& name, const wxString& value, const wxString& type); + + /// Recursively import an object + bool ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node); + bool ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node); + bool ImportProperties(wxRichTextObject* obj, wxXmlNode* node); + + /// Import style parameters + bool ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara = false); +#endif + + /// Creates an object given an XML element name + virtual wxRichTextObject* CreateObjectForXMLName(wxRichTextObject* parent, const wxString& name) const; + /// Can we save using this handler? virtual bool CanSave() const { return true; } /// Can we load using this handler? virtual bool CanLoad() const { return true; } + // Used during saving + wxMBConv* GetConvMem() const { return m_convMem; } + wxMBConv* GetConvFile() const { return m_convFile; } + // Implementation bool HasParam(wxXmlNode* node, const wxString& param); @@ -70,6 +100,12 @@ protected: virtual bool DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream); virtual bool DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream); #endif + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + // Used during saving + wxMBConv* m_convMem; + wxMBConv* m_convFile; +#endif }; #endif diff --git a/samples/richtext/richtext.cpp b/samples/richtext/richtext.cpp index e45a90b288..1c5b0b97b7 100644 --- a/samples/richtext/richtext.cpp +++ b/samples/richtext/richtext.cpp @@ -34,6 +34,7 @@ #include "wx/splitter.h" #include "wx/sstream.h" #include "wx/html/htmlwin.h" +#include "wx/stopwatch.h" #if wxUSE_FILESYSTEM #include "wx/filesys.h" @@ -1071,7 +1072,14 @@ void MyFrame::OnSaveAs(wxCommandEvent& WXUNUSED(event)) if (!path.empty()) { + wxBusyCursor busy; + wxStopWatch stopwatch; + m_richTextCtrl->SaveFile(path); + + long t = stopwatch.Time(); + wxLogDebug(wxT("Saving took %ldms"), t); + wxMessageBox(wxString::Format(wxT("Saving took %ldms"), t)); } } } diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index af17e75462..86be3e8394 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -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,6 +1061,28 @@ bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range) node = node->GetNext(); } + // Delete any remaining empty objects, but leave at least one empty object per composite object. + if (GetChildCount() > 1) + { + 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(); + } + } + return true; } @@ -823,79 +1098,15 @@ void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream) } } - -/*! - * 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) -{ - 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; -} - -/// 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(); @@ -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; @@ -5335,8 +5550,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 +5761,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(); @@ -6643,6 +6859,35 @@ bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNa return true; } +/*! + * wxRichTextBox + */ + +IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject) + +wxRichTextBox::wxRichTextBox(wxRichTextObject* parent): + wxRichTextParagraphLayoutBox(parent) +{ +} + +/// Draw the item +bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style) +{ + return wxRichTextParagraphLayoutBox::Draw(dc, range, selectionRange, rect, descent, style); +} + +/// Lay the item out +bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style) +{ + return wxRichTextParagraphLayoutBox::Layout(dc, rect, style); +} + +/// Copy +void wxRichTextBox::Copy(const wxRichTextBox& obj) +{ + wxRichTextParagraphLayoutBox::Copy(obj); +} + /* * Module to initialise and clean up handlers */ @@ -7075,40 +7320,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 +7336,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 +7358,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 +7463,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; } @@ -7964,7 +8184,7 @@ void wxTextBoxAttr::Reset() m_floatMode = 0; m_clearMode = 0; m_collapseMode = 0; - + m_margins.Reset(); m_padding.Reset(); m_position.Reset(); @@ -7984,13 +8204,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 +8279,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 +8342,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 +8380,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 +8396,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 +8405,7 @@ bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const { if (!(wxTextAttr::operator==(attr))) return false; - + return (m_textBoxAttr == attr.m_textBoxAttr); } @@ -8194,7 +8414,7 @@ bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const { if (!(wxTextAttr::EqPartial(attr))) return false; - + return m_textBoxAttr.EqPartial(attr.m_textBoxAttr); } @@ -8221,12 +8441,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 +8461,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 +8483,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 +8497,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 +8536,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 +8569,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 +8578,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 +8587,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 +8595,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 +8604,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 +8656,52 @@ void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& att absentAttr.SetPresent(true); } +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 +8719,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 +8730,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 +8746,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 +9176,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 diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index 4c1d11568c..3651d221a1 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -31,11 +31,73 @@ #include "wx/wfstream.h" #include "wx/sstream.h" #include "wx/txtstrm.h" +#include "wx/mstream.h" #include "wx/tokenzr.h" +#include "wx/stopwatch.h" #include "wx/xml/xml.h" +// Set to 1 for slower wxXmlDocument method, 0 for faster direct method. +// If we make wxXmlDocument::Save more efficient, we might switch to this +// method. +#define wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT 0 + +#if wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +# error Must define wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT in richtextxml.h to use this method. +#endif + +#if !wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_DIRECT_OUTPUT +# error Must define wxRICHTEXT_HAVE_DIRECT_OUTPUT in richtextxml.h to use this method. +#endif + +// Set to 1 to time file saving +#define wxRICHTEXT_USE_OUTPUT_TIMINGS 0 + +// Convert a colour to a 6-digit hex string +static wxString ColourToHexString(const wxColour& col) +{ + wxString hex; + + hex += wxDecToHex(col.Red()); + hex += wxDecToHex(col.Green()); + hex += wxDecToHex(col.Blue()); + + return hex; +} + +// Convert 6-digit hex string to a colour +static wxColour HexStringToColour(const wxString& hex) +{ + unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2)); + unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2)); + unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2)); + + return wxColour(r, g, b); +} + +static inline wxString MakeString(const int& v) { return wxString::Format(wxT("%d"), v); } +static inline wxString MakeString(const long& v) { return wxString::Format(wxT("%ld"), v); } +static inline wxString MakeString(const double& v) { return wxString::Format(wxT("%.2f"), (float) v); } +static inline wxString MakeString(const wxString& s) { return s; } +static inline wxString MakeString(const wxColour& col) { return wxT("#") + ColourToHexString(col); } + +static inline void AddString(wxString& str, const int& v) { str << wxString::Format(wxT("%d"), v); } +static inline void AddString(wxString& str, const long& v) { str << wxString::Format(wxT("%ld"), v); } +static inline void AddString(wxString& str, const double& v) { str << wxString::Format(wxT("%.2f"), (float) v); } +static inline void AddString(wxString& str, const wxChar* s) { str << s; } +static inline void AddString(wxString& str, const wxString& s) { str << s; } +static inline void AddString(wxString& str, const wxColour& col) { str << wxT("#") << ColourToHexString(col); } + IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler) +void wxRichTextXMLHandler::Init() +{ +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + // Used during saving + m_convMem = NULL; + m_convFile = NULL; +#endif +} + #if wxUSE_STREAMS bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream) { @@ -74,7 +136,7 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s { } else - ImportXML(buffer, child); + ImportXML(buffer, buffer, child); } child = child->GetNext(); @@ -93,171 +155,65 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s return success; } -/// Recursively import an object -bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) +/// Creates an object given an XML element name +wxRichTextObject* wxRichTextXMLHandler::CreateObjectForXMLName(wxRichTextObject* WXUNUSED(parent), const wxString& name) const { - wxString name = node->GetName(); - - bool doneChildren = false; - - if (name == wxT("paragraphlayout")) - { - wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString); - if (partial == wxT("true")) - buffer->SetPartialParagraph(true); - } + if (name == wxT("text") || name == wxT("symbol")) + return new wxRichTextPlainText; + else if (name == wxT("image")) + return new wxRichTextImage; else if (name == wxT("paragraph")) - { - wxRichTextParagraph* para = new wxRichTextParagraph(buffer); - buffer->AppendChild(para); + return new wxRichTextParagraph; + else if (name == wxT("paragraphlayout")) + return new wxRichTextParagraphLayoutBox; + else + return NULL; +} - GetStyle(para->GetAttributes(), node, true); - - wxXmlNode* child = node->GetChildren(); - while (child) - { - wxString childName = child->GetName(); - if (childName == wxT("text")) - { - wxString text; - wxXmlNode* textChild = child->GetChildren(); - while (textChild) - { - if (textChild->GetType() == wxXML_TEXT_NODE || - textChild->GetType() == wxXML_CDATA_SECTION_NODE) - { - wxString text2 = textChild->GetContent(); - - // Strip whitespace from end - if (!text2.empty() && text2[text2.length()-1] == wxT('\n')) - text2 = text2.Mid(0, text2.length()-1); - - if (!text2.empty() && text2[0] == wxT('"')) - text2 = text2.Mid(1); - if (!text2.empty() && text2[text2.length()-1] == wxT('"')) - text2 = text2.Mid(0, text2.length() - 1); - - text += text2; - } - textChild = textChild->GetNext(); - } - - wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para); - GetStyle(textObject->GetAttributes(), child, false); - - para->AppendChild(textObject); - } - else if (childName == wxT("symbol")) - { - // This is a symbol that XML can't read in the normal way - wxString text; - wxXmlNode* textChild = child->GetChildren(); - while (textChild) - { - if (textChild->GetType() == wxXML_TEXT_NODE || - textChild->GetType() == wxXML_CDATA_SECTION_NODE) - { - wxString text2 = textChild->GetContent(); - text += text2; - } - textChild = textChild->GetNext(); - } - - wxString actualText; - actualText << (wxChar) wxAtoi(text); - - wxRichTextPlainText* textObject = new wxRichTextPlainText(actualText, para); - GetStyle(textObject->GetAttributes(), child, false); - - para->AppendChild(textObject); - } - else if (childName == wxT("image")) - { - wxBitmapType imageType = wxBITMAP_TYPE_PNG; - wxString value = child->GetAttribute(wxT("imagetype"), wxEmptyString); - if (!value.empty()) - { - int type = wxAtoi(value); - - // note: 0 == wxBITMAP_TYPE_INVALID - if (type <= 0 || type >= wxBITMAP_TYPE_MAX) - { - wxLogWarning("Invalid bitmap type specified for tag: %d", type); - } - else - { - imageType = (wxBitmapType)type; - } - } - - wxString data; - - wxXmlNode* imageChild = child->GetChildren(); - while (imageChild) - { - wxString childName = imageChild->GetName(); - if (childName == wxT("data")) - { - wxXmlNode* dataChild = imageChild->GetChildren(); - while (dataChild) - { - data = dataChild->GetContent(); - // wxLogDebug(data); - dataChild = dataChild->GetNext(); - } - - } - imageChild = imageChild->GetNext(); - } - - if (!data.empty()) - { - wxRichTextImage* imageObj = new wxRichTextImage(para); - GetStyle(imageObj->GetAttributes(), child, false); - para->AppendChild(imageObj); - - wxStringInputStream strStream(data); - - imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType); - } - } - child = child->GetNext(); - } - - doneChildren = true; - } - else if (name == wxT("stylesheet")) - { - if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET) - { - wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet; - wxString sheetName = node->GetAttribute(wxT("name"), wxEmptyString); - wxString sheetDescription = node->GetAttribute(wxT("description"), wxEmptyString); - sheet->SetName(sheetName); - sheet->SetDescription(sheetDescription); - - wxXmlNode* child = node->GetChildren(); - while (child) - { - ImportStyleDefinition(sheet, child); - - child = child->GetNext(); - } - - // Notify that styles have changed. If this is vetoed by the app, - // the new sheet will be deleted. If it is not vetoed, the - // old sheet will be deleted and replaced with the new one. - buffer->SetStyleSheetAndNotify(sheet); - } - doneChildren = true; - } - - if (!doneChildren) +/// Recursively import an object +bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node) +{ + obj->ImportFromXML(buffer, node, this); + + wxRichTextCompositeObject* compositeParent = wxDynamicCast(obj, wxRichTextCompositeObject); + if (compositeParent) { wxXmlNode* child = node->GetChildren(); while (child) { - ImportXML(buffer, child); + if (child->GetName() == wxT("stylesheet")) + { + if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET) + { + wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet; + wxString sheetName = child->GetAttribute(wxT("name"), wxEmptyString); + wxString sheetDescription = child->GetAttribute(wxT("description"), wxEmptyString); + sheet->SetName(sheetName); + sheet->SetDescription(sheetDescription); + + wxXmlNode* child2 = child->GetChildren(); + while (child2) + { + ImportStyleDefinition(sheet, child2); + + child2 = child2->GetNext(); + } + + // Notify that styles have changed. If this is vetoed by the app, + // the new sheet will be deleted. If it is not vetoed, the + // old sheet will be deleted and replaced with the new one. + buffer->SetStyleSheetAndNotify(sheet); + } + } + else + { + wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName()); + if (childObj) + { + compositeParent->AppendChild(childObj); + ImportXML(buffer, childObj, child); + } + } child = child->GetNext(); } } @@ -265,13 +221,43 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) return true; } +bool wxRichTextXMLHandler::ImportProperties(wxRichTextObject* obj, wxXmlNode* node) +{ + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("properties")) + { + wxXmlNode* propertyChild = child->GetChildren(); + while (propertyChild) + { + if (propertyChild->GetName() == wxT("property")) + { + wxString name = propertyChild->GetAttribute(wxT("name"), wxEmptyString); + wxString value = propertyChild->GetAttribute(wxT("value"), wxEmptyString); + wxString type = propertyChild->GetAttribute(wxT("type"), wxEmptyString); + + wxVariant var = MakePropertyFromString(name, value, type); + if (!var.IsNull()) + { + obj->GetProperties().SetProperty(var); + } + } + propertyChild = propertyChild->GetNext(); + } + } + child = child->GetNext(); + } + return true; +} + bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node) { wxString styleType = node->GetName(); wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString); wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString); - if (styleName.IsEmpty()) + if (styleName.empty()) return false; if (styleType == wxT("characterstyle")) @@ -285,7 +271,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx if (child->GetName() == wxT("style")) { wxRichTextAttr attr; - GetStyle(attr, child, false); + ImportStyle(attr, child, false); def->SetStyle(attr); } child = child->GetNext(); @@ -307,7 +293,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx if (child->GetName() == wxT("style")) { wxRichTextAttr attr; - GetStyle(attr, child, false); + ImportStyle(attr, child, true); def->SetStyle(attr); } child = child->GetNext(); @@ -329,10 +315,10 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx if (child->GetName() == wxT("style")) { wxRichTextAttr attr; - GetStyle(attr, child, false); + ImportStyle(attr, child, true); wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString); - if (styleLevel.IsEmpty()) + if (styleLevel.empty()) { def->SetStyle(attr); } @@ -422,9 +408,9 @@ wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, b #endif #endif -// write string to output: +// write string to output inline static void OutputString(wxOutputStream& stream, const wxString& str, - wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL) + wxMBConv *WXUNUSED_IN_UNICODE(convMem), wxMBConv *convFile) { if (str.empty()) return; #if wxUSE_UNICODE @@ -449,6 +435,14 @@ inline static void OutputString(wxOutputStream& stream, const wxString& str, #endif } +static void OutputIndentation(wxOutputStream& stream, int indent) +{ + wxString str = wxT("\n"); + for (int i = 0; i < indent; i++) + str << wxT(' ') << wxT(' '); + ::OutputString(stream, str, NULL, NULL); +} + // Same as above, but create entities first. // Translates '<' to "<", '>' to ">" and '&' to "&" static void OutputStringEnt(wxOutputStream& stream, const wxString& str, @@ -508,7 +502,25 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, OutputString(stream, str.Mid(last, i - last), convMem, convFile); } -static wxString AttributeToXML(const wxString& str) +void wxRichTextXMLHandler::OutputString(wxOutputStream& stream, const wxString& str) +{ + ::OutputString(stream, str, m_convMem, m_convFile); +} + +void wxRichTextXMLHandler::OutputStringEnt(wxOutputStream& stream, const wxString& str) +{ + ::OutputStringEnt(stream, str, m_convMem, m_convFile); +} + +void wxRichTextXMLHandler::OutputIndentation(wxOutputStream& stream, int indent) +{ + wxString str = wxT("\n"); + for (int i = 0; i < indent; i++) + str << wxT(' ') << wxT(' '); + ::OutputString(stream, str, NULL, NULL); +} + +wxString wxRichTextXMLHandler::AttributeToXML(const wxString& str) { wxString str1; size_t i, last, len; @@ -565,53 +577,167 @@ static wxString AttributeToXML(const wxString& str) return str1; } -inline static void OutputIndentation(wxOutputStream& stream, int indent) +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + +static inline void AddAttribute(wxString& str, const wxChar* name, const int& v) { - wxString str = wxT("\n"); - for (int i = 0; i < indent; i++) - str << wxT(' ') << wxT(' '); - OutputString(stream, str, NULL, NULL); + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%d"), v) << wxT("\""); } -// Convert a colour to a 6-digit hex string -static wxString ColourToHexString(const wxColour& col) +static inline void AddAttribute(wxString& str, const wxChar* name, const long& v) { - wxString hex; - - hex += wxDecToHex(col.Red()); - hex += wxDecToHex(col.Green()); - hex += wxDecToHex(col.Blue()); - - return hex; + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%ld"), v) << wxT("\""); } -// Convert 6-digit hex string to a colour -static wxColour HexStringToColour(const wxString& hex) +static inline void AddAttribute(wxString& str, const wxChar* name, const double& v) { - unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2)); - unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2)); - unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2)); - - return wxColour(r, g, b); + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%.2f"), (float) v) << wxT("\""); } +static inline void AddAttribute(wxString& str, const wxChar* name, const wxChar* s) +{ + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxChar* name, const wxString& s) +{ + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxChar* name, const wxColour& col) +{ + str << wxT(" ") << name << wxT("=\"") << wxT("#") << ColourToHexString(col) << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxChar* name, const wxTextAttrDimension& dim) +{ + if (dim.IsPresent()) + { + wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString((int) dim.GetFlags()); + str << wxT(" ") << name << wxT("=\""); + str << value; + str << wxT("\""); + } +} + +static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrDimensions& dims) +{ + if (dims.GetLeft().IsPresent()) + AddAttribute(str, rootName + wxString(wxT("-left")), dims.GetLeft()); + if (dims.GetRight().IsPresent()) + AddAttribute(str, rootName + wxString(wxT("-right")), dims.GetRight()); + if (dims.GetTop().IsPresent()) + AddAttribute(str, rootName + wxString(wxT("-top")), dims.GetTop()); + if (dims.GetBottom().IsPresent()) + AddAttribute(str, rootName + wxString(wxT("-bottom")), dims.GetBottom()); +} + +static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrBorder& border) +{ + if (border.HasStyle()) + AddAttribute(str, rootName + wxString(wxT("-style")), border.GetStyle()); + if (border.HasColour()) + AddAttribute(str, rootName + wxString(wxT("-color")), border.GetColour()); + if (border.HasWidth()) + AddAttribute(str, rootName + wxString(wxT("-width")), border.GetWidth()); +} + +static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrBorders& borders) +{ + AddAttribute(str, rootName + wxString(wxT("-left")), borders.GetLeft()); + AddAttribute(str, rootName + wxString(wxT("-right")), borders.GetRight()); + AddAttribute(str, rootName + wxString(wxT("-top")), borders.GetTop()); + AddAttribute(str, rootName + wxString(wxT("-bottom")), borders.GetBottom()); +} + +#endif + // wxRICHTEXT_HAVE_DIRECT_OUTPUT + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const int& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const long& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const double& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxString& s) +{ + node->AddAttribute(name, s); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxColour& col) +{ + node->AddAttribute(name, MakeString(col)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxTextAttrDimension& dim) +{ + if (dim.IsPresent()) + { + wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString(dim.GetFlags()); + AddAttribute(node, name, value); + } +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrDimensions& dims) +{ + if (dims.GetLeft().IsPresent()) + AddAttribute(node, rootName + wxString(wxT("-left")), dims.GetLeft()); + if (dims.GetRight().IsPresent()) + AddAttribute(node, rootName + wxString(wxT("-right")), dims.GetRight()); + if (dims.GetTop().IsPresent()) + AddAttribute(node, rootName + wxString(wxT("-top")), dims.GetTop()); + if (dims.GetBottom().IsPresent()) + AddAttribute(node, rootName + wxString(wxT("-bottom")), dims.GetBottom()); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrBorder& border) +{ + if (border.HasStyle()) + AddAttribute(node, rootName + wxString(wxT("-style")), border.GetStyle()); + if (border.HasColour()) + AddAttribute(node, rootName + wxString(wxT("-color")), border.GetColour()); + if (border.HasWidth()) + AddAttribute(node, rootName + wxString(wxT("-width")), border.GetWidth()); +} + +static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrBorders& borders) +{ + AddAttribute(node, rootName + wxString(wxT("-left")), borders.GetLeft()); + AddAttribute(node, rootName + wxString(wxT("-right")), borders.GetRight()); + AddAttribute(node, rootName + wxString(wxT("-top")), borders.GetTop()); + AddAttribute(node, rootName + wxString(wxT("-bottom")), borders.GetBottom()); +} +#endif + // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream) { if (!stream.IsOk()) return false; wxString version(wxT("1.0") ) ; - + bool deleteConvFile = false; wxString fileEncoding; - wxMBConv* convFile = NULL; + //wxMBConv* convFile = NULL; #if wxUSE_UNICODE fileEncoding = wxT("UTF-8"); - convFile = & wxConvUTF8; + m_convFile = & wxConvUTF8; #else fileEncoding = wxT("ISO-8859-1"); - convFile = & wxConvISO8859_1; + m_convFile = & wxConvISO8859_1; #endif // If SetEncoding has been called, change the output encoding. @@ -636,21 +762,89 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& #else fileEncoding = wxT("ISO-8859-1"); #endif - convFile = new wxCSConv(fileEncoding); + m_convFile = new wxCSConv(fileEncoding); deleteConvFile = true; } -#if !wxUSE_UNICODE - wxMBConv* convMem = wxConvCurrent; +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + wxStopWatch stopwatch; +#endif + wxXmlDocument* doc = new wxXmlDocument; + doc->SetFileEncoding(fileEncoding); + + wxXmlNode* rootNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("richtext")); + doc->SetRoot(rootNode); + rootNode->AddAttribute(wxT("version"), wxT("1.0.0.0")); + rootNode->AddAttribute(wxT("xmlns"), wxT("http://www.wxwidgets.org")); + + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) + { + wxXmlNode* styleSheetNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("stylesheet")); + rootNode->AddChild(styleSheetNode); + + wxString nameAndDescr; + + if (!buffer->GetStyleSheet()->GetName().empty()) + styleSheetNode->AddAttribute(wxT("name"), buffer->GetStyleSheet()->GetName()); + + if (!buffer->GetStyleSheet()->GetDescription().empty()) + styleSheetNode->AddAttribute(wxT("description"), buffer->GetStyleSheet()->GetDescription()); + + int i; + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) + { + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + } + bool success = ExportXML(rootNode, *buffer); +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + long t = stopwatch.Time(); + wxLogDebug(wxT("Creating the document took %ldms"), t); + wxMessageBox(wxString::Format(wxT("Creating the document took %ldms"), t)); +#endif + if (success) + { +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + wxStopWatch s2; +#endif + success = doc->Save(stream); +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + long t2 = s2.Time(); + wxLogDebug(wxT("Save() took %ldms"), t2); + wxMessageBox(wxString::Format(wxT("Save() took %ldms"), t2)); +#endif + } + delete doc; + doc = NULL; + #else - wxMBConv* convMem = NULL; + // !(wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT) + +#if !wxUSE_UNICODE + m_convMem = wxConvCurrent; +#else + m_convMem = NULL; #endif wxString s ; s.Printf(wxT("\n"), version, fileEncoding); - OutputString(stream, s, NULL, NULL); - OutputString(stream, wxT("") , NULL, NULL); + OutputString(stream, s); + OutputString(stream, wxT("")); int level = 1; @@ -658,218 +852,62 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& { OutputIndentation(stream, level); wxString nameAndDescr; - if (!buffer->GetStyleSheet()->GetName().IsEmpty()) + if (!buffer->GetStyleSheet()->GetName().empty()) nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\""); - if (!buffer->GetStyleSheet()->GetDescription().IsEmpty()) + if (!buffer->GetStyleSheet()->GetDescription().empty()) nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\""); - OutputString(stream, wxString(wxT(""), convMem, convFile); + OutputString(stream, wxString(wxT("")); int i; for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) { wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); - ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(stream, def, level + 1); } for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) { wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); - ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(stream, def, level + 1); } for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) { wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); - ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(stream, def, level + 1); } OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } - bool success = ExportXML(stream, convMem, convFile, *buffer, level); + bool success = ExportXML(stream, *buffer, level); - OutputString(stream, wxT("\n") , NULL, NULL); - OutputString(stream, wxT("\n"), NULL, NULL); + OutputString(stream, wxT("\n")); + OutputString(stream, wxT("\n")); if (deleteConvFile) - delete convFile; + delete m_convFile; + m_convFile = NULL; + m_convMem = NULL; +#endif return success; } +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + /// Recursively export an object -bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent) +bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int indent) { - wxString objectName; - if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox))) - objectName = wxT("paragraphlayout"); - else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph))) - objectName = wxT("paragraph"); - else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) - objectName = wxT("text"); - else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) - objectName = wxT("image"); - else - objectName = wxT("object"); - - bool terminateTag = true; - - if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) - { - wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj; - - wxString style = CreateStyle(obj.GetAttributes(), false); - - int i; - int last = 0; - const wxString& text = textObj.GetText(); - int len = (int) text.Length(); - - if (len == 0) - { - i = 0; - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - OutputString(stream, style + wxT(">"), convMem, convFile); - OutputString(stream, wxT(""), convMem, convFile); - } - else for (i = 0; i < len; i++) - { -#if wxUSE_UNICODE - int c = (int) text[i]; -#else - int c = (int) wxUChar(text[i]); -#endif - if ((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13) - { - if (i > 0) - { - wxString fragment(text.Mid(last, i-last)); - if (!fragment.IsEmpty()) - { - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - - OutputString(stream, style + wxT(">"), convMem, convFile); - - if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))) - { - OutputString(stream, wxT("\""), convMem, convFile); - OutputStringEnt(stream, fragment, convMem, convFile); - OutputString(stream, wxT("\""), convMem, convFile); - } - else - OutputStringEnt(stream, fragment, convMem, convFile); - - OutputString(stream, wxT(""), convMem, convFile); - } - } - - - // Output this character as a number in a separate tag, because XML can't cope - // with entities below 32 except for 10 and 13 - last = i + 1; - OutputIndentation(stream, indent); - OutputString(stream, wxT(""), convMem, convFile); - OutputString(stream, wxString::Format(wxT("%d"), c), convMem, convFile); - - OutputString(stream, wxT(""), convMem, convFile); - } - } - - wxString fragment; - if (last == 0) - fragment = text; - else - fragment = text.Mid(last, i-last); - - if (last < len) - { - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - - OutputString(stream, style + wxT(">"), convMem, convFile); - - if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))) - { - OutputString(stream, wxT("\""), convMem, convFile); - OutputStringEnt(stream, fragment, convMem, convFile); - OutputString(stream, wxT("\""), convMem, convFile); - } - else - OutputStringEnt(stream, fragment, convMem, convFile); - } - else - terminateTag = false; - } - else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) - { - wxRichTextImage& imageObj = (wxRichTextImage&) obj; - - wxString style = CreateStyle(obj.GetAttributes(), false); - - //if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok()) - // imageObj.MakeBlock(); - - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - if (!imageObj.GetImageBlock().Ok()) - { - // No data - OutputString(stream, style + wxT(">"), convMem, convFile); - } - else - { - OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) + style + wxT(">")); - } - - OutputIndentation(stream, indent+1); - OutputString(stream, wxT(""), convMem, convFile); - - imageObj.GetImageBlock().WriteHex(stream); - - OutputString(stream, wxT(""), convMem, convFile); - } - else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject))) - { - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - - bool isPara = false; - if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout")) - isPara = true; - - wxString style = CreateStyle(obj.GetAttributes(), isPara); - - if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph()) - style << wxT(" partialparagraph=\"true\""); - - OutputString(stream, style + wxT(">"), convMem, convFile); - - wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj; - size_t i; - for (i = 0; i < composite.GetChildCount(); i++) - { - wxRichTextObject* child = composite.GetChild(i); - ExportXML(stream, convMem, convFile, *child, indent+1); - } - } - - if (objectName != wxT("text")) - OutputIndentation(stream, indent); - - if (terminateTag) - OutputString(stream, wxT(""), convMem, convFile); + obj.ExportXML(stream, indent, this); return true; } -bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level) +bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level) { wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); @@ -877,52 +915,52 @@ bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBCon wxString baseStyle = def->GetBaseStyle(); wxString baseStyleProp; - if (!baseStyle.IsEmpty()) + if (!baseStyle.empty()) baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\""); wxString descr = def->GetDescription(); wxString descrProp; - if (!descr.IsEmpty()) + if (!descr.empty()) descrProp = wxT(" description=\"") + descr + wxT("\""); if (charDef) { OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); level ++; - wxString style = CreateStyle(def->GetStyle(), false); + wxString style = AddAttributes(def->GetStyle(), false); OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); level --; OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } else if (listDef) { OutputIndentation(stream, level); - if (!listDef->GetNextStyle().IsEmpty()) - baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\""); + if (!listDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << listDef->GetNextStyle() << wxT("\""); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); level ++; - wxString style = CreateStyle(def->GetStyle(), false); + wxString style = AddAttributes(def->GetStyle(), true); OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); int i; for (i = 0; i < 10; i ++) @@ -930,180 +968,504 @@ bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBCon wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); if (levelAttr) { - wxString style = CreateStyle(def->GetStyle(), false); + wxString style = AddAttributes(def->GetStyle(), true); wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1)); OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } } level --; OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } else if (paraDef) { OutputIndentation(stream, level); - if (!paraDef->GetNextStyle().IsEmpty()) - baseStyleProp << wxT(" basestyle=\"") << paraDef->GetNextStyle() << wxT("\""); + if (!paraDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << paraDef->GetNextStyle() << wxT("\""); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); level ++; - wxString style = CreateStyle(def->GetStyle(), false); + wxString style = AddAttributes(def->GetStyle(), false); OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); level --; OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } return true; } -/// Create style parameters -wxString wxRichTextXMLHandler::CreateStyle(const wxRichTextAttr& attr, bool isPara) +/// Create a string containing style attributes +wxString wxRichTextXMLHandler::AddAttributes(const wxRichTextAttr& attr, bool isPara) { wxString str; if (attr.HasTextColour() && attr.GetTextColour().Ok()) - { - str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\""); - } + AddAttribute(str, wxT("textcolor"), attr.GetTextColour()); + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok()) - { - str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); - } + AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour()); if (attr.HasFontSize()) - str << wxT(" fontsize=\"") << attr.GetFontSize() << wxT("\""); + AddAttribute(str, wxT("fontsize"), attr.GetFontSize()); if (attr.HasFontFamily()) - str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); + AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily()); if (attr.HasFontItalic()) - str << wxT(" fontstyle=\"") << attr.GetFontStyle() << wxT("\""); + AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle()); if (attr.HasFontWeight()) - str << wxT(" fontweight=\"") << attr.GetFontWeight() << wxT("\""); + AddAttribute(str, wxT("fontweight"), attr.GetFontWeight()); if (attr.HasFontUnderlined()) - str << wxT(" fontunderlined=\"") << (int) attr.GetFontUnderlined() << wxT("\""); + AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined()); if (attr.HasFontFaceName()) - str << wxT(" fontface=\"") << attr.GetFontFaceName() << wxT("\""); + AddAttribute(str, wxT("fontface"), attr.GetFontFaceName()); if (attr.HasTextEffects()) { - str << wxT(" texteffects=\""); - str << attr.GetTextEffects(); - str << wxT("\""); - - str << wxT(" texteffectflags=\""); - str << attr.GetTextEffectFlags(); - str << wxT("\""); + AddAttribute(str, wxT("texteffects"), attr.GetTextEffects()); + AddAttribute(str, wxT("texteffectflags"), attr.GetTextEffectFlags()); } if (!attr.GetCharacterStyleName().empty()) - str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\""); + AddAttribute(str, wxT("characterstyle"), attr.GetCharacterStyleName()); if (attr.HasURL()) - str << wxT(" url=\"") << AttributeToXML(attr.GetURL()) << wxT("\""); + AddAttribute(str, wxT("url"), AttributeToXML(attr.GetURL())); if (isPara) { if (attr.HasAlignment()) - str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); + AddAttribute(str, wxT("alignment"), (int) attr.GetAlignment()); if (attr.HasLeftIndent()) { - str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); - str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); + AddAttribute(str, wxT("leftindent"), (int) attr.GetLeftIndent()); + AddAttribute(str, wxT("leftsubindent"), (int) attr.GetLeftSubIndent()); } if (attr.HasRightIndent()) - str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); + AddAttribute(str, wxT("rightindent"), (int) attr.GetRightIndent()); if (attr.HasParagraphSpacingAfter()) - str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); + AddAttribute(str, wxT("parspacingafter"), (int) attr.GetParagraphSpacingAfter()); if (attr.HasParagraphSpacingBefore()) - str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); + AddAttribute(str, wxT("parspacingbefore"), (int) attr.GetParagraphSpacingBefore()); if (attr.HasLineSpacing()) - str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); + AddAttribute(str, wxT("linespacing"), (int) attr.GetLineSpacing()); if (attr.HasBulletStyle()) - str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); + AddAttribute(str, wxT("bulletstyle"), (int) attr.GetBulletStyle()); if (attr.HasBulletNumber()) - str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); + AddAttribute(str, wxT("bulletnumber"), (int) attr.GetBulletNumber()); if (attr.HasBulletText()) { // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character. // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1 - if (!attr.GetBulletText().IsEmpty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) - str << wxT(" bulletsymbol=\"") << (int) (attr.GetBulletText()[0]) << wxT("\""); + if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) + AddAttribute(str, wxT("bulletsymbol"), (int) (attr.GetBulletText()[0])); else - str << wxT(" bullettext=\"") << attr.GetBulletText() << wxT("\""); + AddAttribute(str, wxT("bullettext"), attr.GetBulletText()); - str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\""); + AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont()); } if (attr.HasBulletName()) - str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\""); + AddAttribute(str, wxT("bulletname"), attr.GetBulletName()); if (!attr.GetParagraphStyleName().empty()) - str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + AddAttribute(str, wxT("parstyle"), attr.GetParagraphStyleName()); if (!attr.GetListStyleName().empty()) - str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\""); + AddAttribute(str, wxT("liststyle"), attr.GetListStyleName()); if (attr.HasTabs()) { - str << wxT(" tabs=\""); + wxString strTabs; size_t i; for (i = 0; i < attr.GetTabs().GetCount(); i++) { - if (i > 0) - str << wxT(","); - str << attr.GetTabs()[i]; + if (i > 0) strTabs << wxT(","); + strTabs << attr.GetTabs()[i]; } - str << wxT("\""); + AddAttribute(str, wxT("tabs"), strTabs); } if (attr.HasPageBreak()) { - str << wxT(" pagebreak=\"1\""); + AddAttribute(str, wxT("pagebreak"), 1); } if (attr.HasOutlineLevel()) - str << wxT(" outlinelevel=\"") << (int) attr.GetOutlineLevel() << wxT("\""); - + AddAttribute(str, wxT("outlinelevel"), (int) attr.GetOutlineLevel()); } + + AddAttribute(str, wxT("margin"), attr.GetTextBoxAttr().GetMargins()); + AddAttribute(str, wxT("padding"), attr.GetTextBoxAttr().GetPadding()); + AddAttribute(str, wxT("position"), attr.GetTextBoxAttr().GetPosition()); + AddAttribute(str, wxT("border"), attr.GetTextBoxAttr().GetBorder()); + AddAttribute(str, wxT("outline"), attr.GetTextBoxAttr().GetOutline()); + AddAttribute(str, wxT("width"), attr.GetTextBoxAttr().GetWidth()); + AddAttribute(str, wxT("height"), attr.GetTextBoxAttr().GetWidth()); + + if (attr.GetTextBoxAttr().HasFloatMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT) + value = wxT("right"); + else + value = wxT("none"); + AddAttribute(str, wxT("float"), value); + } + + if (attr.GetTextBoxAttr().HasClearMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT) + value = wxT("right"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH) + value = wxT("both"); + else + value = wxT("none"); + AddAttribute(str, wxT("clear"), value); + } + + if (attr.GetTextBoxAttr().HasCollapseBorders()) + AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders()); return str; } +// Make a string from the given property. This can be overridden for custom variants. +wxString wxRichTextXMLHandler::MakeStringFromProperty(const wxVariant& var) +{ + return var.MakeString(); +} + +// Create a proprty from the string read from the XML file. +wxVariant wxRichTextXMLHandler::MakePropertyFromString(const wxString& name, const wxString& value, const wxString& WXUNUSED(type)) +{ + wxVariant var(value, name); + // TODO: use type to create using common types + return var; +} + +// Write the properties +bool wxRichTextXMLHandler::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level) +{ + if (properties.GetCount() > 0) + { + level ++; + + OutputIndentation(stream, level); + OutputString(stream, wxT("\n")); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("\n")); + + level --; + } + + return true; +} + + +#endif + // wxRICHTEXT_HAVE_DIRECT_OUTPUT + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj) +{ + obj.ExportXML(parent, this); + + return true; +} + +bool wxRichTextXMLHandler::ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def) +{ + wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); + wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); + wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition); + + wxString baseStyle = def->GetBaseStyle(); + wxString descr = def->GetDescription(); + + wxXmlNode* defNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxEmptyString); + parent->AddChild(defNode); + if (!baseStyle.empty()) + defNode->AddAttribute(wxT("basestyle"), baseStyle); + if (!descr.empty()) + defNode->AddAttribute(wxT("description"), descr); + + wxXmlNode* styleNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style")); + defNode->AddChild(styleNode); + + if (charDef) + { + defNode->SetName(wxT("characterstyle")); + AddAttributes(styleNode, def->GetStyle(), false); + } + else if (listDef) + { + defNode->SetName(wxT("liststyle")); + + if (!listDef->GetNextStyle().empty()) + defNode->AddAttribute(wxT("nextstyle"), listDef->GetNextStyle()); + + AddAttributes(styleNode, def->GetStyle(), true); + + int i; + for (i = 0; i < 10; i ++) + { + wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); + if (levelAttr) + { + wxXmlNode* levelNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style")); + defNode->AddChild(levelNode); + levelNode->AddAttribute(wxT("level"), MakeString(i+1)); + AddAttributes(levelNode, * levelAttr, true); + } + } + } + else if (paraDef) + { + defNode->SetName(wxT("paragraphstyle")); + + if (!paraDef->GetNextStyle().empty()) + defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle()); + + AddAttributes(styleNode, def->GetStyle(), true); + } + + return true; +} + +bool wxRichTextXMLHandler::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara) +{ + if (attr.HasTextColour() && attr.GetTextColour().Ok()) + node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour())); + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok()) + node->AddAttribute(wxT("bgcolor"), MakeString(attr.GetBackgroundColour())); + + if (attr.HasFontSize()) + node->AddAttribute(wxT("fontsize"), MakeString(attr.GetFontSize())); + if (attr.HasFontFamily()) + node->AddAttribute(wxT("fontfamily"), MakeString(attr.GetFontFamily())); + if (attr.HasFontItalic()) + node->AddAttribute(wxT("fontstyle"), MakeString(attr.GetFontStyle())); + if (attr.HasFontWeight()) + node->AddAttribute(wxT("fontweight"), MakeString(attr.GetFontWeight())); + if (attr.HasFontUnderlined()) + node->AddAttribute(wxT("fontunderlined"), MakeString((int) attr.GetFontUnderlined())); + if (attr.HasFontFaceName()) + node->AddAttribute(wxT("fontface"), attr.GetFontFaceName()); + + if (attr.HasTextEffects()) + { + node->AddAttribute(wxT("texteffects"), MakeString(attr.GetTextEffects())); + node->AddAttribute(wxT("texteffectflags"), MakeString(attr.GetTextEffectFlags())); + } + if (attr.HasCharacterStyleName() && !attr.GetCharacterStyleName().empty()) + node->AddAttribute(wxT("characterstyle"), attr.GetCharacterStyleName()); + + if (attr.HasURL()) + node->AddAttribute(wxT("url"), attr.GetURL()); // TODO: do we need to wrap this in AttributeToXML? + + if (isPara) + { + if (attr.HasAlignment()) + node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment())); + + if (attr.HasLeftIndent()) + { + node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent())); + node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent())); + } + + if (attr.HasRightIndent()) + node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent())); + + if (attr.HasParagraphSpacingAfter()) + node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter())); + + if (attr.HasParagraphSpacingBefore()) + node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore())); + + if (attr.HasLineSpacing()) + node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing())); + + if (attr.HasBulletStyle()) + node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle())); + + if (attr.HasBulletNumber()) + node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber())); + + if (attr.HasBulletText()) + { + // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character. + // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1 + if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) + node->AddAttribute(wxT("bulletsymbol"), MakeString((int) (attr.GetBulletText()[0]))); + else + node->AddAttribute(wxT("bullettext"), attr.GetBulletText()); + + if (!attr.GetBulletFont().empty()) + node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont()); + } + + if (attr.HasBulletName()) + node->AddAttribute(wxT("bulletname"), attr.GetBulletName()); + + if (!attr.GetParagraphStyleName().empty()) + node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName()); + + if (!attr.GetListStyleName().empty()) + node->AddAttribute(wxT("liststyle"), attr.GetListStyleName()); + + if (attr.HasTabs()) + { + wxString tabs; + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) + tabs << wxT(","); + tabs << attr.GetTabs()[i]; + } + node->AddAttribute(wxT("tabs"), tabs); + } + + if (attr.HasPageBreak()) + node->AddAttribute(wxT("pagebreak"), wxT("1")); + + if (attr.HasOutlineLevel()) + node->AddAttribute(wxT("outlinelevel"), MakeString((int) attr.GetOutlineLevel())); + } + + AddAttribute(node, wxT("margin"), attr.GetTextBoxAttr().GetMargins()); + AddAttribute(node, wxT("padding"), attr.GetTextBoxAttr().GetPadding()); + AddAttribute(node, wxT("position"), attr.GetTextBoxAttr().GetPosition()); + AddAttribute(node, wxT("border"), attr.GetTextBoxAttr().GetBorder()); + AddAttribute(node, wxT("outline"), attr.GetTextBoxAttr().GetOutline()); + AddAttribute(node, wxT("width"), attr.GetTextBoxAttr().GetWidth()); + AddAttribute(node, wxT("height"), attr.GetTextBoxAttr().GetWidth()); + + if (attr.GetTextBoxAttr().HasFloatMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT) + value = wxT("right"); + else + value = wxT("none"); + AddAttribute(node, wxT("float"), value); + } + + if (attr.GetTextBoxAttr().HasClearMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT) + value = wxT("right"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH) + value = wxT("both"); + else + value = wxT("none"); + AddAttribute(node, wxT("clear"), value); + } + + if (attr.GetTextBoxAttr().HasCollapseBorders()) + AddAttribute(node, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders()); + + return true; +} + +bool wxRichTextXMLHandler::WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties) +{ + if (properties.GetCount() > 0) + { + wxXmlNode* propertiesNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("properties")); + node->AddChild(propertiesNode); + size_t i; + for (i = 0; i < properties.GetCount(); i++) + { + const wxVariant& var = properties[i]; + if (!var.IsNull()) + { + wxXmlNode* propertyNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + propertiesNode->AddChild(propertyNode); + + const wxString& name = var.GetName(); + wxString value = MakeStringFromProperty(var); + + AddAttribute(propertyNode, wxT("name"), name); + AddAttribute(propertyNode, wxT("type"), var.GetType()); + AddAttribute(propertyNode, wxT("value"), value); + } + } + } + return true; +} + +#endif + // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT + /// Replace face name with current name for platform. /// TODO: introduce a virtual function or settable table to /// do this comprehensively. bool wxRichTextFixFaceName(wxString& facename) { - if (facename.IsEmpty()) + if (facename.empty()) return false; #ifdef __WXMSW__ @@ -1145,206 +1507,372 @@ bool wxRichTextFixFaceName(wxString& facename) #endif } -/// Get style parameters -bool wxRichTextXMLHandler::GetStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara) +static inline long wxRichTextColourStringToLong(const wxString& colStr) { - wxString fontFacename; - int fontSize = 12; - wxFontFamily fontFamily = wxFONTFAMILY_DEFAULT; - wxFontWeight fontWeight = wxFONTWEIGHT_NORMAL; - wxFontStyle fontStyle = wxFONTSTYLE_NORMAL; - bool fontUnderlined = false; - const wxString emptyString; // save a temporary string construction in GetPropVal - - // int fontFlags = 0; - - fontFacename = node->GetAttribute(wxT("fontface"), emptyString); - if (!fontFacename.IsEmpty()) + if (!colStr.IsEmpty()) { - attr.SetFontFaceName(fontFacename); - if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES) - wxRichTextFixFaceName(fontFacename); + wxColour col(colStr); + return col.GetRGB(); } + else + return 0; +} - wxString value; - value = node->GetAttribute(wxT("fontfamily"), emptyString); - if (!value.empty()) +static inline wxTextAttrDimension wxRichTextParseDimension(const wxString& dimStr) +{ + wxString valuePart = dimStr.BeforeFirst(wxT(',')); + wxString flagsPart; + if (dimStr.Contains(wxT(","))) + flagsPart = dimStr.AfterFirst(wxT(',')); + wxTextAttrDimension dim; + dim.SetValue(wxAtoi(valuePart)); + dim.SetFlags(wxAtoi(flagsPart)); + + return dim; +} + +/// Import style parameters +bool wxRichTextXMLHandler::ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara) +{ + wxXmlAttribute* xmlAttr = node->GetAttributes(); + bool found; + while (xmlAttr) { - fontFamily = (wxFontFamily)wxAtoi(value); - attr.SetFontFamily(fontFamily); - } + const wxString& name = xmlAttr->GetName(); + const wxString& value = xmlAttr->GetValue(); + found = true; - value = node->GetAttribute(wxT("fontstyle"), emptyString); - if (!value.empty()) - { - fontStyle = (wxFontStyle)wxAtoi(value); - attr.SetFontStyle(fontStyle); - } - - value = node->GetAttribute(wxT("fontsize"), emptyString); - if (!value.empty()) - { - fontSize = wxAtoi(value); - attr.SetFontSize(fontSize); - } - - value = node->GetAttribute(wxT("fontweight"), emptyString); - if (!value.empty()) - { - fontWeight = (wxFontWeight)wxAtoi(value); - attr.SetFontWeight(fontWeight); - } - - value = node->GetAttribute(wxT("fontunderlined"), emptyString); - if (!value.empty()) - { - fontUnderlined = wxAtoi(value) != 0; - attr.SetFontUnderlined(fontUnderlined); - } - - value = node->GetAttribute(wxT("textcolor"), emptyString); - if (!value.empty()) - { - if (value[0] == wxT('#')) - attr.SetTextColour(HexStringToColour(value.Mid(1))); - else - attr.SetTextColour(value); - } - - value = node->GetAttribute(wxT("bgcolor"), emptyString); - if (!value.empty()) - { - if (value[0] == wxT('#')) - attr.SetBackgroundColour(HexStringToColour(value.Mid(1))); - else - attr.SetBackgroundColour(value); - } - - value = node->GetAttribute(wxT("characterstyle"), emptyString); - if (!value.empty()) - attr.SetCharacterStyleName(value); - - value = node->GetAttribute(wxT("texteffects"), emptyString); - if (!value.IsEmpty()) - { - attr.SetTextEffects(wxAtoi(value)); - } - - value = node->GetAttribute(wxT("texteffectflags"), emptyString); - if (!value.IsEmpty()) - { - attr.SetTextEffectFlags(wxAtoi(value)); - } - - value = node->GetAttribute(wxT("url"), emptyString); - if (!value.empty()) - attr.SetURL(value); - - // Set paragraph attributes - if (isPara) - { - value = node->GetAttribute(wxT("alignment"), emptyString); - if (!value.empty()) - attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); - - int leftSubIndent = 0; - int leftIndent = 0; - bool hasLeftIndent = false; - - value = node->GetAttribute(wxT("leftindent"), emptyString); - if (!value.empty()) + if (name == wxT("fontface")) { - leftIndent = wxAtoi(value); - hasLeftIndent = true; - } - - value = node->GetAttribute(wxT("leftsubindent"), emptyString); - if (!value.empty()) - { - leftSubIndent = wxAtoi(value); - hasLeftIndent = true; - } - - if (hasLeftIndent) - attr.SetLeftIndent(leftIndent, leftSubIndent); - - value = node->GetAttribute(wxT("rightindent"), emptyString); - if (!value.empty()) - attr.SetRightIndent(wxAtoi(value)); - - value = node->GetAttribute(wxT("parspacingbefore"), emptyString); - if (!value.empty()) - attr.SetParagraphSpacingBefore(wxAtoi(value)); - - value = node->GetAttribute(wxT("parspacingafter"), emptyString); - if (!value.empty()) - attr.SetParagraphSpacingAfter(wxAtoi(value)); - - value = node->GetAttribute(wxT("linespacing"), emptyString); - if (!value.empty()) - attr.SetLineSpacing(wxAtoi(value)); - - value = node->GetAttribute(wxT("bulletstyle"), emptyString); - if (!value.empty()) - attr.SetBulletStyle(wxAtoi(value)); - - value = node->GetAttribute(wxT("bulletnumber"), emptyString); - if (!value.empty()) - attr.SetBulletNumber(wxAtoi(value)); - - value = node->GetAttribute(wxT("bulletsymbol"), emptyString); - if (!value.empty()) - { - wxChar ch = wxAtoi(value); - wxString s; - s << ch; - attr.SetBulletText(s); - } - - value = node->GetAttribute(wxT("bullettext"), emptyString); - if (!value.empty()) - attr.SetBulletText(value); - - value = node->GetAttribute(wxT("bulletfont"), emptyString); - if (!value.empty()) - attr.SetBulletFont(value); - - value = node->GetAttribute(wxT("bulletname"), emptyString); - if (!value.empty()) - attr.SetBulletName(value); - - value = node->GetAttribute(wxT("parstyle"), emptyString); - if (!value.empty()) - attr.SetParagraphStyleName(value); - - value = node->GetAttribute(wxT("liststyle"), emptyString); - if (!value.empty()) - attr.SetListStyleName(value); - - value = node->GetAttribute(wxT("tabs"), emptyString); - if (!value.empty()) - { - wxArrayInt tabs; - wxStringTokenizer tkz(value, wxT(",")); - while (tkz.HasMoreTokens()) + if (!value.empty()) { - wxString token = tkz.GetNextToken(); - tabs.Add(wxAtoi(token)); + wxString v = value; + if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES) + wxRichTextFixFaceName(v); + attr.SetFontFaceName(v); } - attr.SetTabs(tabs); } - - value = node->GetAttribute(wxT("pagebreak"), emptyString); - if (!value.IsEmpty()) + else if (name == wxT("fontfamily")) { - attr.SetPageBreak(wxAtoi(value) != 0); + if (!value.empty()) + attr.SetFontFamily((wxFontFamily)wxAtoi(value)); } - - value = node->GetAttribute(wxT("outlinelevel"), emptyString); - if (!value.IsEmpty()) + else if (name == wxT("fontstyle")) { - attr.SetOutlineLevel(wxAtoi(value)); + if (!value.empty()) + attr.SetFontStyle((wxFontStyle)wxAtoi(value)); } + else if (name == wxT("fontsize")) + { + if (!value.empty()) + attr.SetFontSize(wxAtoi(value)); + } + else if (name == wxT("fontweight")) + { + if (!value.empty()) + attr.SetFontWeight((wxFontWeight) wxAtoi(value)); + } + else if (name == wxT("fontunderlined")) + { + if (!value.empty()) + attr.SetFontUnderlined(wxAtoi(value) != 0); + } + else if (name == wxT("textcolor")) + { + if (!value.empty()) + { + if (value[0] == wxT('#')) + attr.SetTextColour(HexStringToColour(value.Mid(1))); + else + attr.SetTextColour(value); + } + } + else if (name == wxT("bgcolor")) + { + if (!value.empty()) + { + if (value[0] == wxT('#')) + attr.SetBackgroundColour(HexStringToColour(value.Mid(1))); + else + attr.SetBackgroundColour(value); + } + } + else if (name == wxT("characterstyle")) + { + if (!value.empty()) + attr.SetCharacterStyleName(value); + } + else if (name == wxT("texteffects")) + { + if (!value.empty()) + attr.SetTextEffects(wxAtoi(value)); + } + else if (name == wxT("texteffectflags")) + { + if (!value.empty()) + attr.SetTextEffectFlags(wxAtoi(value)); + } + else if (name == wxT("url")) + { + if (!value.empty()) + attr.SetURL(value); + } + else if (isPara) + { + if (name == wxT("alignment")) + { + if (!value.empty()) + attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); + } + else if (name == wxT("leftindent")) + { + if (!value.empty()) + attr.SetLeftIndent(wxAtoi(value), attr.GetLeftSubIndent()); + } + else if (name == wxT("leftsubindent")) + { + if (!value.empty()) + attr.SetLeftIndent(attr.GetLeftIndent(), wxAtoi(value)); + } + else if (name == wxT("rightindent")) + { + if (!value.empty()) + attr.SetRightIndent(wxAtoi(value)); + } + else if (name == wxT("parspacingbefore")) + { + if (!value.empty()) + attr.SetParagraphSpacingBefore(wxAtoi(value)); + } + else if (name == wxT("parspacingafter")) + { + if (!value.empty()) + attr.SetParagraphSpacingAfter(wxAtoi(value)); + } + else if (name == wxT("linespacing")) + { + if (!value.empty()) + attr.SetLineSpacing(wxAtoi(value)); + } + else if (name == wxT("bulletstyle")) + { + if (!value.empty()) + attr.SetBulletStyle(wxAtoi(value)); + } + else if (name == wxT("bulletnumber")) + { + if (!value.empty()) + attr.SetBulletNumber(wxAtoi(value)); + } + else if (name == wxT("bulletsymbol")) + { + if (!value.empty()) + { + wxChar ch = wxAtoi(value); + wxString s; + s << ch; + attr.SetBulletText(s); + } + } + else if (name == wxT("bullettext")) + { + if (!value.empty()) + { + attr.SetBulletText(value); + } + } + else if (name == wxT("bulletfont")) + { + if (!value.empty()) + { + attr.SetBulletFont(value); + } + } + else if (name == wxT("bulletname")) + { + if (!value.empty()) + { + attr.SetBulletName(value); + } + } + else if (name == wxT("parstyle")) + { + if (!value.empty()) + { + attr.SetParagraphStyleName(value); + } + } + else if (name == wxT("liststyle")) + { + if (!value.empty()) + { + attr.SetListStyleName(value); + } + } + else if (name == wxT("tabs")) + { + if (!value.empty()) + { + wxArrayInt tabs; + wxStringTokenizer tkz(value, wxT(",")); + while (tkz.HasMoreTokens()) + { + wxString token = tkz.GetNextToken(); + tabs.Add(wxAtoi(token)); + } + attr.SetTabs(tabs); + } + } + else if (name == wxT("pagebreak")) + { + if (!value.empty()) + { + attr.SetPageBreak(wxAtoi(value) != 0); + } + } + else if (name == wxT("outlinelevel")) + { + if (!value.empty()) + { + attr.SetOutlineLevel(wxAtoi(value)); + } + } + else + found = false; + } + else + found = false; + + if (!found) + { + // Box attributes + + if (name == wxT("width")) + { + attr.GetTextBoxAttr().GetWidth().SetValue(wxRichTextParseDimension(value)); + } + else if (name == wxT("height")) + { + attr.GetTextBoxAttr().GetHeight().SetValue(wxRichTextParseDimension(value)); + } + + else if (name == wxT("float")) + { + if (value == wxT("left")) + attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_LEFT); + else if (value == wxT("right")) + attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_RIGHT); + else if (value == wxT("none")) + attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_NONE); + } + else if (name == wxT("clear")) + { + if (value == wxT("left")) + attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_LEFT); + else if (value == wxT("right")) + attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_RIGHT); + else if (value == wxT("both")) + attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_BOTH); + else if (value == wxT("none")) + attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_NONE); + } + else if (name == wxT("collapse-borders")) + attr.GetTextBoxAttr().SetCollapseBorders(value == wxT("1")); + + else if (name.Contains(wxT("border-"))) + { + if (name == wxT("border-left-style")) + attr.GetTextBoxAttr().GetBorder().GetLeft().SetStyle(wxAtoi(value)); + else if (name == wxT("border-right-style")) + attr.GetTextBoxAttr().GetBorder().GetRight().SetStyle(wxAtoi(value)); + else if (name == wxT("border-top-style")) + attr.GetTextBoxAttr().GetBorder().GetTop().SetStyle(wxAtoi(value)); + else if (name == wxT("border-bottom-style")) + attr.GetTextBoxAttr().GetBorder().GetBottom().SetStyle(wxAtoi(value)); + + else if (name == wxT("border-left-colour")) + attr.GetTextBoxAttr().GetBorder().GetLeft().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("border-right-colour")) + attr.GetTextBoxAttr().GetBorder().GetRight().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("border-top-colour")) + attr.GetTextBoxAttr().GetBorder().GetTop().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("border-bottom-colour")) + attr.GetTextBoxAttr().GetBorder().GetBottom().SetColour(wxRichTextColourStringToLong(value)); + + else if (name == wxT("border-left-width")) + attr.GetTextBoxAttr().GetBorder().GetLeft().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("border-right-width")) + attr.GetTextBoxAttr().GetBorder().GetRight().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("border-top-width")) + attr.GetTextBoxAttr().GetBorder().GetTop().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("border-bottom-width")) + attr.GetTextBoxAttr().GetBorder().GetBottom().SetWidth(wxRichTextParseDimension(value)); + } + else if (name.Contains(wxT("outline-"))) + { + if (name == wxT("outline-left-style")) + attr.GetTextBoxAttr().GetOutline().GetLeft().SetStyle(wxAtoi(value)); + else if (name == wxT("outline-right-style")) + attr.GetTextBoxAttr().GetOutline().GetRight().SetStyle(wxAtoi(value)); + else if (name == wxT("outline-top-style")) + attr.GetTextBoxAttr().GetOutline().GetTop().SetStyle(wxAtoi(value)); + else if (name == wxT("outline-bottom-style")) + attr.GetTextBoxAttr().GetOutline().GetBottom().SetStyle(wxAtoi(value)); + + else if (name == wxT("outline-left-colour")) + attr.GetTextBoxAttr().GetOutline().GetLeft().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("outline-right-colour")) + attr.GetTextBoxAttr().GetOutline().GetRight().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("outline-top-colour")) + attr.GetTextBoxAttr().GetOutline().GetTop().SetColour(wxRichTextColourStringToLong(value)); + else if (name == wxT("outline-bottom-colour")) + attr.GetTextBoxAttr().GetOutline().GetBottom().SetColour(wxRichTextColourStringToLong(value)); + + else if (name == wxT("outline-left-width")) + attr.GetTextBoxAttr().GetOutline().GetLeft().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("outline-right-width")) + attr.GetTextBoxAttr().GetOutline().GetRight().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("outline-top-width")) + attr.GetTextBoxAttr().GetOutline().GetTop().SetWidth(wxRichTextParseDimension(value)); + else if (name == wxT("outline-bottom-width")) + attr.GetTextBoxAttr().GetOutline().GetBottom().SetWidth(wxRichTextParseDimension(value)); + } + else if (name.Contains(wxT("margin-"))) + { + if (name == wxT("margin-left")) + attr.GetTextBoxAttr().GetMargins().GetLeft().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("margin-right")) + attr.GetTextBoxAttr().GetMargins().GetRight().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("margin-top")) + attr.GetTextBoxAttr().GetMargins().GetTop().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("margin-bottom")) + attr.GetTextBoxAttr().GetMargins().GetBottom().SetValue(wxRichTextParseDimension(value)); + } + else if (name.Contains(wxT("padding-"))) + { + if (name == wxT("padding-left")) + attr.GetTextBoxAttr().GetPadding().GetLeft().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("padding-right")) + attr.GetTextBoxAttr().GetPadding().GetRight().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("padding-top")) + attr.GetTextBoxAttr().GetPadding().GetTop().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("padding-bottom")) + attr.GetTextBoxAttr().GetPadding().GetBottom().SetValue(wxRichTextParseDimension(value)); + } + else if (name.Contains(wxT("position-"))) + { + if (name == wxT("position-left")) + attr.GetTextBoxAttr().GetPosition().GetLeft().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("position-right")) + attr.GetTextBoxAttr().GetPosition().GetRight().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("position-top")) + attr.GetTextBoxAttr().GetPosition().GetTop().SetValue(wxRichTextParseDimension(value)); + else if (name == wxT("position-bottom")) + attr.GetTextBoxAttr().GetPosition().GetBottom().SetValue(wxRichTextParseDimension(value)); + } + } + + xmlAttr = xmlAttr->GetNext(); } return true; @@ -1353,6 +1881,551 @@ bool wxRichTextXMLHandler::GetStyle(wxRichTextAttr& attr, wxXmlNode* node, bool #endif // wxUSE_STREAMS +// Import this object from XML +bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler) +{ + handler->ImportProperties(this, node); + handler->ImportStyle(GetAttributes(), node, UsesParagraphAttributes()); + + return true; +} + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextObject::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) +{ + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("<") + GetXMLNodeName(), handler->GetConvMem(), handler->GetConvFile()); + + wxString style = handler->AddAttributes(GetAttributes(), true); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + } + + wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject); + if (composite) + { + size_t i; + for (i = 0; i < composite->GetChildCount(); i++) + { + wxRichTextObject* child = composite->GetChild(i); + child->ExportXML(stream, indent+1, handler); + } + } + + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; +} +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +// Export this object to the given parent node, usually creating at least one child node. +bool wxRichTextObject::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler) +{ + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName()); + parent->AddChild(elementNode); + handler->AddAttributes(elementNode, GetAttributes(), true); + handler->WriteProperties(elementNode, GetProperties()); + + wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject); + if (composite) + { + size_t i; + for (i = 0; i < composite->GetChildCount(); i++) + { + wxRichTextObject* child = composite->GetChild(i); + child->ExportXML(elementNode, handler); + } + } + return true; +} +#endif + + +// Import this object from XML +bool wxRichTextPlainText::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler); + + if (node->GetName() == wxT("text")) + { + wxString text; + wxXmlNode* textChild = node->GetChildren(); + while (textChild) + { + if (textChild->GetType() == wxXML_TEXT_NODE || + textChild->GetType() == wxXML_CDATA_SECTION_NODE) + { + wxString text2 = textChild->GetContent(); + + // Strip whitespace from end + if (!text2.empty() && text2[text2.length()-1] == wxT('\n')) + text2 = text2.Mid(0, text2.length()-1); + + if (!text2.empty() && text2[0] == wxT('"')) + text2 = text2.Mid(1); + if (!text2.empty() && text2[text2.length()-1] == wxT('"')) + text2 = text2.Mid(0, text2.length() - 1); + + text += text2; + } + textChild = textChild->GetNext(); + } + + SetText(text); + } + else if (node->GetName() == wxT("symbol")) + { + // This is a symbol that XML can't read in the normal way + wxString text; + wxXmlNode* textChild = node->GetChildren(); + while (textChild) + { + if (textChild->GetType() == wxXML_TEXT_NODE || + textChild->GetType() == wxXML_CDATA_SECTION_NODE) + { + wxString text2 = textChild->GetContent(); + text += text2; + } + textChild = textChild->GetNext(); + } + + wxString actualText; + actualText << (wxChar) wxAtoi(text); + SetText(actualText); + } + else + return false; + + return true; +} + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextPlainText::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) +{ + wxString style = handler->AddAttributes(GetAttributes(), false); + + int i; + int last = 0; + const wxString& text = GetText(); + int len = (int) text.Length(); + + if (len == 0) + { + i = 0; + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + else for (i = 0; i < len; i++) + { +#if wxUSE_UNICODE + int c = (int) text[i]; +#else + int c = (int) wxUChar(text[i]); +#endif + if ((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13) + { + if (i > 0) + { + wxString fragment(text.Mid(last, i-last)); + if (!fragment.empty()) + { + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))) + { + ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile()); + ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile()); + ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile()); + } + else + ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + } + + + // Output this character as a number in a separate tag, because XML can't cope + // with entities below 32 except for 10 and 13 + last = i + 1; + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + ::OutputString(stream, wxString::Format(wxT("%d"), c), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + } + + wxString fragment; + if (last == 0) + fragment = text; + else + fragment = text.Mid(last, i-last); + + if (last < len) + { + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + + if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))) + { + ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile()); + ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile()); + ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile()); + } + else + ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile()); + + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + return true; +} +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +// Export this object to the given parent node, usually creating at least one child node. +bool wxRichTextPlainText::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler) +{ + int i; + int last = 0; + const wxString& text = GetText(); + int len = (int) text.Length(); + + if (len == 0) + { + i = 0; + + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text")); + parent->AddChild(elementNode); + + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); + } + else for (i = 0; i < len; i++) + { +#if wxUSE_UNICODE + int c = (int) text[i]; +#else + int c = (int) wxUChar(text[i]); +#endif + if ((c < 32 || c == 34) && c != 10 && c != 13) + { + if (i > 0) + { + wxString fragment(text.Mid(last, i-last)); + if (!fragment.empty()) + { + // TODO: I'm assuming wxXmlDocument will output quotes if necessary + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text")); + parent->AddChild(elementNode); + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); + + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + elementNode->AddChild(textNode); + + if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')) + fragment = wxT("\"") + fragment + wxT("\""); + + textNode->SetContent(fragment); + } + } + + + // Output this character as a number in a separate tag, because XML can't cope + // with entities below 32 except for 10 and 13 + + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("symbol")); + parent->AddChild(elementNode); + + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); + + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + elementNode->AddChild(textNode); + textNode->SetContent(wxString::Format(wxT("%d"), c)); + + last = i + 1; + } + } + + wxString fragment; + if (last == 0) + fragment = text; + else + fragment = text.Mid(last, i-last); + + if (last < len) + { + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text")); + parent->AddChild(elementNode); + handler->AddAttributes(elementNode, GetAttributes(), false); + + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + elementNode->AddChild(textNode); + + if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')) + fragment = wxT("\"") + fragment + wxT("\""); + + textNode->SetContent(fragment); + } + return true; +} +#endif + + +// Import this object from XML +bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler); + + wxBitmapType imageType = wxBITMAP_TYPE_PNG; + wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString); + if (!value.empty()) + { + int type = wxAtoi(value); + + // note: 0 == wxBITMAP_TYPE_INVALID + if (type <= 0 || type >= wxBITMAP_TYPE_MAX) + { + wxLogWarning("Invalid bitmap type specified for tag: %d", type); + } + else + { + imageType = (wxBitmapType)type; + } + } + + wxString data; + + wxXmlNode* imageChild = node->GetChildren(); + while (imageChild) + { + wxString childName = imageChild->GetName(); + if (childName == wxT("data")) + { + wxXmlNode* dataChild = imageChild->GetChildren(); + while (dataChild) + { + data = dataChild->GetContent(); + // wxLogDebug(data); + dataChild = dataChild->GetNext(); + } + + } + imageChild = imageChild->GetNext(); + } + + if (!data.empty()) + { + wxStringInputStream strStream(data); + + GetImageBlock().ReadHex(strStream, data.length(), imageType); + + return true; + } + else + return false; +} + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextImage::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) +{ + wxString style = handler->AddAttributes(GetAttributes(), false); + + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + if (!GetImageBlock().Ok()) + { + // No data + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + } + else + { + ::OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) GetImageBlock().GetImageType()) + style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + } + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + + ::OutputIndentation(stream, indent+1); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + + // wxStopWatch stopwatch; + + GetImageBlock().WriteHex(stream); + + // wxLogDebug(wxT("Image conversion to hex took %ldms"), stopwatch.Time()); + + ::OutputString(stream, wxT("\n"), handler->GetConvMem(), handler->GetConvFile()); + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; +} +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +// Export this object to the given parent node, usually creating at least one child node. +bool wxRichTextImage::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler) +{ + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("image")); + parent->AddChild(elementNode); + + if (GetImageBlock().Ok()) + elementNode->AddAttribute(wxT("imagetype"), MakeString((int) GetImageBlock().GetImageType())); + + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); + + wxXmlNode* dataNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("data")); + elementNode->AddChild(dataNode); + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + dataNode->AddChild(textNode); + + wxString strData; +#if 1 + { + wxMemoryOutputStream stream; + if (GetImageBlock().WriteHex(stream)) + { + if (stream.GetSize() > 0) + { + int size = stream.GetSize(); + int size2 = stream.GetOutputStreamBuffer()->GetIntPosition(); + wxASSERT(size == size2); + + unsigned char* data = new unsigned char[size]; + stream.CopyTo(data, size); + strData = wxString((const char*) data, wxConvUTF8, size); + delete[] data; + } + else + strData = wxEmptyString; + } + + } +#else + { + wxStringOutputStream strStream(& strData); + GetImageBlock().WriteHex(strStream); + } +#endif + + textNode->SetContent(strData); + textNode->SetNoConversion(true); // optimize speed + + return true; +} +#endif + + +// Import this object from XML +bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler); + + wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString); + if (partial == wxT("true")) + SetPartialParagraph(true); + + return true; +} + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextParagraphLayoutBox::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) +{ + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + + wxString style = handler->AddAttributes(GetAttributes(), true); + + if (GetPartialParagraph()) + style << wxT(" partialparagraph=\"true\""); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + } + + size_t i; + for (i = 0; i < GetChildCount(); i++) + { + wxRichTextObject* child = GetChild(i); + child->ExportXML(stream, indent+1, handler); + } + + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; +} +#endif + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +// Export this object to the given parent node, usually creating at least one child node. +bool wxRichTextParagraphLayoutBox::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler) +{ + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("paragraphlayout")); + parent->AddChild(elementNode); + handler->AddAttributes(elementNode, GetAttributes(), true); + handler->WriteProperties(elementNode, GetProperties()); + + if (GetPartialParagraph()) + elementNode->AddAttribute(wxT("partialparagraph"), wxT("true")); + + size_t i; + for (i = 0; i < GetChildCount(); i++) + { + wxRichTextObject* child = GetChild(i); + child->ExportXML(elementNode, handler); + } + + return true; +} +#endif + #endif // wxUSE_RICHTEXT && wxUSE_XML