X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b71e9aa4e2187a7f6469f68812467f2ecb6a3836..abe0903cd4a16c6d8f50a3263d601fc718038c6d:/src/richtext/richtextxml.cpp diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index 84dbc72288..f78883c8be 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: richtext/richtextxml.cpp +// Name: src/richtext/richtextxml.cpp // Purpose: XML and HTML I/O for wxRichTextCtrl // Author: Julian Smart // Modified by: @@ -13,7 +13,7 @@ #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop + #pragma hdrstop #endif #if wxUSE_RICHTEXT && wxUSE_XML @@ -21,26 +21,90 @@ #include "wx/richtext/richtextxml.h" #ifndef WX_PRECOMP - #include "wx/wx.h" + #include "wx/intl.h" + #include "wx/module.h" + #include "wx/log.h" #endif #include "wx/filename.h" #include "wx/clipbrd.h" #include "wx/wfstream.h" #include "wx/sstream.h" -#include "wx/module.h" #include "wx/txtstrm.h" +#include "wx/mstream.h" +#include "wx/tokenzr.h" +#include "wx/stopwatch.h" #include "wx/xml/xml.h" -#include "wx/intl.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) { if (!stream.IsOk()) return false; + buffer->ResetAndClearCommands(); buffer->Clear(); wxXmlDocument* xmlDoc = new wxXmlDocument; @@ -55,6 +119,7 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s if (!xmlDoc->Load(stream, encoding)) { + buffer->ResetAndClearCommands(); success = false; } else @@ -71,7 +136,7 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s { } else - ImportXML(buffer, child); + ImportXML(buffer, buffer, child); } child = child->GetNext(); @@ -90,114 +155,196 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s return success; } +/// Creates an object given an XML element name +wxRichTextObject* wxRichTextXMLHandler::CreateObjectForXMLName(wxRichTextObject* WXUNUSED(parent), const wxString& name) const +{ + if (name == wxT("text") || name == wxT("symbol")) + return new wxRichTextPlainText; + else if (name == wxT("image")) + return new wxRichTextImage; + else if (name == wxT("paragraph")) + return new wxRichTextParagraph; + else if (name == wxT("paragraphlayout")) + return new wxRichTextParagraphLayoutBox; + else if (name == wxT("textbox")) + return new wxRichTextBox; + else if (name == wxT("cell")) + return new wxRichTextCell; + else if (name == wxT("table")) + return new wxRichTextTable; + else + return NULL; +} + /// Recursively import an object -bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) +bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node) { - wxString name = node->GetName(); + bool recurse = false; + obj->ImportFromXML(buffer, node, this, & recurse); - bool doneChildren = false; + // TODO: how to control whether to import children. - if (name == wxT("paragraphlayout")) - { - } - else if (name == wxT("paragraph")) + wxRichTextCompositeObject* compositeParent = wxDynamicCast(obj, wxRichTextCompositeObject); + if (recurse && compositeParent) { - wxRichTextParagraph* para = new wxRichTextParagraph(buffer); - buffer->AppendChild(para); - - GetStyle(para->GetAttributes(), node, true); - wxXmlNode* child = node->GetChildren(); while (child) { - wxString childName = child->GetName(); - if (childName == wxT("text")) + if (child->GetName() != wxT("stylesheet")) { - wxString text; - wxXmlNode* textChild = child->GetChildren(); - while (textChild) + wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName()); + if (childObj) { - if (textChild->GetType() == wxXML_TEXT_NODE || - textChild->GetType() == wxXML_CDATA_SECTION_NODE) - { - wxString text2 = textChild->GetContent(); + compositeParent->AppendChild(childObj); + ImportXML(buffer, childObj, child); + } + } + child = child->GetNext(); + } + } - // Strip whitespace from end - if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('\n')) - text2 = text2.Mid(0, text2.Length()-1); + return true; +} - if (text2.Length() > 0 && text2[0] == wxT('"')) - text2 = text2.Mid(1); - if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('"')) - text2 = text2.Mid(0, text2.Length() - 1); +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); - text += text2; + wxVariant var = MakePropertyFromString(name, value, type); + if (!var.IsNull()) + { + obj->GetProperties().SetProperty(var); } - textChild = textChild->GetNext(); } + 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); - wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para); - GetStyle(textObject->GetAttributes(), child, false); + if (styleName.empty()) + return false; - para->AppendChild(textObject); - } - else if (childName == wxT("image")) + if (styleType == wxT("characterstyle")) + { + wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName); + def->SetBaseStyle(baseStyleName); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) { - int imageType = wxBITMAP_TYPE_PNG; - wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString); - if (!value.empty()) - imageType = wxAtoi(value); + wxRichTextAttr attr; + ImportStyle(attr, child, false); + def->SetStyle(attr); + } + child = child->GetNext(); + } - wxString data; + sheet->AddCharacterStyle(def); + } + else if (styleType == wxT("paragraphstyle")) + { + wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName); - 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(); - } + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); + def->SetNextStyle(nextStyleName); + def->SetBaseStyle(baseStyleName); - } - imageChild = imageChild->GetNext(); - } + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + def->SetStyle(attr); + } + child = child->GetNext(); + } - if (!data.empty()) - { - wxRichTextImage* imageObj = new wxRichTextImage(para); - para->AppendChild(imageObj); + sheet->AddParagraphStyle(def); + } + else if (styleType == wxT("boxstyle")) + { + wxRichTextBoxStyleDefinition* def = new wxRichTextBoxStyleDefinition(styleName); - wxStringInputStream strStream(data); + def->SetBaseStyle(baseStyleName); - imageObj->GetImageBlock().ReadHex(strStream, data.Length(), imageType); - } + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + def->SetStyle(attr); } child = child->GetNext(); } - doneChildren = true; + sheet->AddBoxStyle(def); } - - if (!doneChildren) + else if (styleType == wxT("liststyle")) { + wxRichTextListStyleDefinition* def = new wxRichTextListStyleDefinition(styleName); + + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); + def->SetNextStyle(nextStyleName); + def->SetBaseStyle(baseStyleName); + wxXmlNode* child = node->GetChildren(); while (child) { - ImportXML(buffer, child); + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + + wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString); + if (styleLevel.empty()) + { + def->SetStyle(attr); + } + else + { + int level = wxAtoi(styleLevel); + if (level > 0 && level <= 10) + { + def->SetLevelAttributes(level-1, attr); + } + } + } child = child->GetNext(); } + + sheet->AddListStyle(def); } return true; } - //----------------------------------------------------------------------------- // xml support routines //----------------------------------------------------------------------------- @@ -257,6 +404,21 @@ wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, b return str1; } +wxXmlNode* wxRichTextXMLHandler::FindNode(wxXmlNode* node, const wxString& name) +{ + if (node->GetName() == name && name == wxT("stylesheet")) + return node; + + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == name) + return child; + child = child->GetNext(); + } + return NULL; +} + // For use with earlier versions of wxWidgets #ifndef WXUNUSED_IN_UNICODE #if wxUSE_UNICODE @@ -266,9 +428,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 @@ -293,6 +455,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, @@ -307,7 +477,7 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, for (i = 0; i < len; i++) { c = str.GetChar(i); - + // Original code excluded "&" but we _do_ want to convert // the ampersand beginning & because otherwise when read in, // the original "&" becomes "&". @@ -334,40 +504,243 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, } last = i + 1; } + else if (wxUChar(c) > 127) + { + OutputString(stream, str.Mid(last, i - last), convMem, convFile); + + wxString s(wxT("&#")); +#if wxUSE_UNICODE + s << (int) c; +#else + s << (int) wxUChar(c); +#endif + s << wxT(";"); + OutputString(stream, s, NULL, NULL); + last = i + 1; + } } OutputString(stream, str.Mid(last, i - last), convMem, convFile); } -inline static void OutputIndentation(wxOutputStream& stream, int indent) +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); + ::OutputString(stream, str, NULL, NULL); } -// Convert a colour to a 6-digit hex string -static wxString ColourToHexString(const wxColour& col) +wxString wxRichTextXMLHandler::AttributeToXML(const wxString& str) { - wxString hex; + wxString str1; + size_t i, last, len; + wxChar c; - hex += wxDecToHex(col.Red()); - hex += wxDecToHex(col.Green()); - hex += wxDecToHex(col.Blue()); + len = str.Len(); + last = 0; + for (i = 0; i < len; i++) + { + c = str.GetChar(i); - return hex; + // Original code excluded "&" but we _do_ want to convert + // the ampersand beginning & because otherwise when read in, + // the original "&" becomes "&". + + if (c == wxT('<') || c == wxT('>') || c == wxT('"') || + (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ )) + { + str1 += str.Mid(last, i - last); + switch (c) + { + case wxT('<'): + str1 += wxT("<"); + break; + case wxT('>'): + str1 += wxT(">"); + break; + case wxT('&'): + str1 += wxT("&"); + break; + case wxT('"'): + str1 += wxT("""); + break; + default: break; + } + last = i + 1; + } + else if (wxUChar(c) > 127) + { + str1 += str.Mid(last, i - last); + + wxString s(wxT("&#")); +#if wxUSE_UNICODE + s << (int) c; +#else + s << (int) wxUChar(c); +#endif + s << wxT(";"); + str1 += s; + last = i + 1; + } + } + str1 += str.Mid(last, i - last); + return str1; } -// Convert 6-digit hex string to a colour -static wxColour HexStringToColour(const wxString& hex) +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + +static inline void AddAttribute(wxString& str, const wxString& name, const int& 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)); + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%d"), v) << wxT("\""); +} - return wxColour(r, g, b); +static inline void AddAttribute(wxString& str, const wxString& name, const long& v) +{ + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%ld"), v) << wxT("\""); } +static inline void AddAttribute(wxString& str, const wxString& name, const double& v) +{ + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%.2f"), (float) v) << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxString& name, const wxChar* s) +{ + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxString& name, const wxString& s) +{ + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxString& name, const wxColour& col) +{ + str << wxT(" ") << name << wxT("=\"") << wxT("#") << ColourToHexString(col) << wxT("\""); +} + +static inline void AddAttribute(wxString& str, const wxString& name, const wxTextAttrDimension& dim) +{ + if (dim.IsValid()) + { + 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 wxString& rootName, const wxTextAttrDimensions& dims) +{ + if (dims.GetLeft().IsValid()) + AddAttribute(str, rootName + wxString(wxT("-left")), dims.GetLeft()); + if (dims.GetRight().IsValid()) + AddAttribute(str, rootName + wxString(wxT("-right")), dims.GetRight()); + if (dims.GetTop().IsValid()) + AddAttribute(str, rootName + wxString(wxT("-top")), dims.GetTop()); + if (dims.GetBottom().IsValid()) + AddAttribute(str, rootName + wxString(wxT("-bottom")), dims.GetBottom()); +} + +static inline void AddAttribute(wxString& str, const wxString& 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 wxString& 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 wxString& name, const int& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const long& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const double& v) +{ + node->AddAttribute(name, MakeString(v)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const wxString& s) +{ + node->AddAttribute(name, s); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const wxColour& col) +{ + node->AddAttribute(name, MakeString(col)); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const wxTextAttrDimension& dim) +{ + if (dim.IsValid()) + { + wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString(dim.GetFlags()); + AddAttribute(node, name, value); + } +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& rootName, const wxTextAttrDimensions& dims) +{ + if (dims.GetLeft().IsValid()) + AddAttribute(node, rootName + wxString(wxT("-left")), dims.GetLeft()); + if (dims.GetRight().IsValid()) + AddAttribute(node, rootName + wxString(wxT("-right")), dims.GetRight()); + if (dims.GetTop().IsValid()) + AddAttribute(node, rootName + wxString(wxT("-top")), dims.GetTop()); + if (dims.GetBottom().IsValid()) + AddAttribute(node, rootName + wxString(wxT("-bottom")), dims.GetBottom()); +} + +static inline void AddAttribute(wxXmlNode* node, const wxString& 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 wxString& 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()) @@ -377,22 +750,25 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& 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. - if (!m_encoding.IsEmpty() && m_encoding.Lower() != fileEncoding.Lower()) + if (!m_encoding.empty() && m_encoding.Lower() != fileEncoding.Lower()) { if (m_encoding == wxT("")) { +#if wxUSE_INTL fileEncoding = wxLocale::GetSystemEncodingName(); + // if !wxUSE_INTL, we fall back to UTF-8 or ISO-8859-1 below +#endif } else { @@ -400,289 +776,1934 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& } // GetSystemEncodingName may not have returned a name - if (fileEncoding.IsEmpty()) + if (fileEncoding.empty()) #if wxUSE_UNICODE fileEncoding = wxT("UTF-8"); #else fileEncoding = wxT("ISO-8859-1"); #endif - convFile = new wxCSConv(fileEncoding); + m_convFile = new wxCSConv(fileEncoding); deleteConvFile = true; } -#if !wxUSE_UNICODE - wxMBConv* convMem = wxConvCurrent; -#else - wxMBConv* convMem = NULL; +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + wxStopWatch stopwatch; #endif + wxXmlDocument* doc = new wxXmlDocument; + doc->SetFileEncoding(fileEncoding); - wxString s ; - s.Printf(wxT("\n"), - (const wxChar*) version, (const wxChar*) fileEncoding ); - OutputString(stream, s, NULL, NULL); - OutputString(stream, wxT("") , NULL, NULL); + 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); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++) + { + wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(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 + // !(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); + OutputString(stream, wxT("")); int level = 1; - bool success = ExportXML(stream, convMem, convFile, *buffer, level); - OutputString(stream, wxT("\n") , NULL, NULL); - OutputString(stream, wxT("\n"), NULL, NULL); + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) + { + OutputIndentation(stream, level); + wxString nameAndDescr; + if (!buffer->GetStyleSheet()->GetName().empty()) + nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\""); + if (!buffer->GetStyleSheet()->GetDescription().empty()) + nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\""); + OutputString(stream, wxString(wxT("")); + + int i; + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) + { + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++) + { + wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + + + bool success = ExportXML(stream, *buffer, level); + + OutputString(stream, wxT("\n")); + OutputString(stream, wxT("\n")); + + if (deleteConvFile) + 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, wxRichTextObject& obj, int indent) +{ + obj.ExportXML(stream, indent, this); + + return true; +} + +bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level) +{ + wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); + wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); + wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition); + wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition); + + wxString name = def->GetName(); + wxString nameProp; + if (!name.empty()) + nameProp = wxT(" name=\"") + AttributeToXML(name) + wxT("\""); + + wxString baseStyle = def->GetBaseStyle(); + wxString baseStyleProp; + if (!baseStyle.empty()) + baseStyleProp = wxT(" basestyle=\"") + AttributeToXML(baseStyle) + wxT("\""); + + wxString descr = def->GetDescription(); + wxString descrProp; + if (!descr.empty()) + descrProp = wxT(" description=\"") + AttributeToXML(descr) + wxT("\""); + + if (charDef) + { + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), false); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (listDef) + { + OutputIndentation(stream, level); + + if (!listDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(listDef->GetNextStyle()) << wxT("\""); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + int i; + for (i = 0; i < 10; i ++) + { + wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); + if (levelAttr) + { + wxString style = AddAttributes(def->GetStyle(), true); + wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1)); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (paraDef) + { + OutputIndentation(stream, level); + + if (!paraDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(paraDef->GetNextStyle()) << wxT("\""); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (boxDef) + { + OutputIndentation(stream, level); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + + + return true; +} + +/// Create a string containing style attributes +wxString wxRichTextXMLHandler::AddAttributes(const wxRichTextAttr& attr, bool isPara) +{ + wxString str; + if (attr.HasTextColour() && attr.GetTextColour().IsOk()) + AddAttribute(str, wxT("textcolor"), attr.GetTextColour()); + + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk()) + AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour()); + + if (attr.HasFontSize()) + AddAttribute(str, wxT("fontsize"), attr.GetFontSize()); + + if (attr.HasFontFamily()) + AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily()); + + if (attr.HasFontItalic()) + AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle()); + + if (attr.HasFontWeight()) + AddAttribute(str, wxT("fontweight"), attr.GetFontWeight()); + + if (attr.HasFontUnderlined()) + AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined()); + + if (attr.HasFontFaceName()) + AddAttribute(str, wxT("fontface"), AttributeToXML(attr.GetFontFaceName())); + + if (attr.HasTextEffects()) + { + AddAttribute(str, wxT("texteffects"), attr.GetTextEffects()); + AddAttribute(str, wxT("texteffectflags"), attr.GetTextEffectFlags()); + } + + if (!attr.GetCharacterStyleName().empty()) + AddAttribute(str, wxT("characterstyle"), AttributeToXML(attr.GetCharacterStyleName())); + + if (attr.HasURL()) + AddAttribute(str, wxT("url"), AttributeToXML(attr.GetURL())); + + if (isPara) + { + if (attr.HasAlignment()) + AddAttribute(str, wxT("alignment"), (int) attr.GetAlignment()); + + if (attr.HasLeftIndent()) + { + AddAttribute(str, wxT("leftindent"), (int) attr.GetLeftIndent()); + AddAttribute(str, wxT("leftsubindent"), (int) attr.GetLeftSubIndent()); + } + + if (attr.HasRightIndent()) + AddAttribute(str, wxT("rightindent"), (int) attr.GetRightIndent()); + + if (attr.HasParagraphSpacingAfter()) + AddAttribute(str, wxT("parspacingafter"), (int) attr.GetParagraphSpacingAfter()); + + if (attr.HasParagraphSpacingBefore()) + AddAttribute(str, wxT("parspacingbefore"), (int) attr.GetParagraphSpacingBefore()); + + if (attr.HasLineSpacing()) + AddAttribute(str, wxT("linespacing"), (int) attr.GetLineSpacing()); + + if (attr.HasBulletStyle()) + AddAttribute(str, wxT("bulletstyle"), (int) attr.GetBulletStyle()); + + if (attr.HasBulletNumber()) + 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().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) + AddAttribute(str, wxT("bulletsymbol"), (int) (attr.GetBulletText()[0])); + else + AddAttribute(str, wxT("bullettext"), AttributeToXML(attr.GetBulletText())); + + AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont()); + } + + if (attr.HasBulletName()) + AddAttribute(str, wxT("bulletname"), AttributeToXML(attr.GetBulletName())); + + if (!attr.GetParagraphStyleName().empty()) + AddAttribute(str, wxT("parstyle"), AttributeToXML(attr.GetParagraphStyleName())); + + if (!attr.GetListStyleName().empty()) + AddAttribute(str, wxT("liststyle"), AttributeToXML(attr.GetListStyleName())); + + if (!attr.GetTextBoxAttr().GetBoxStyleName().empty()) + AddAttribute(str, wxT("boxstyle"), AttributeToXML(attr.GetTextBoxAttr().GetBoxStyleName())); + + if (attr.HasTabs()) + { + wxString strTabs; + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) strTabs << wxT(","); + strTabs << attr.GetTabs()[i]; + } + AddAttribute(str, wxT("tabs"), strTabs); + } + + if (attr.HasPageBreak()) + { + AddAttribute(str, wxT("pagebreak"), 1); + } + + if (attr.HasOutlineLevel()) + 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().GetHeight()); + AddAttribute(str, wxT("minwidth"), attr.GetTextBoxAttr().GetMinSize().GetWidth()); + AddAttribute(str, wxT("minheight"), attr.GetTextBoxAttr().GetMinSize().GetHeight()); + AddAttribute(str, wxT("maxwidth"), attr.GetTextBoxAttr().GetMaxSize().GetWidth()); + AddAttribute(str, wxT("maxheight"), attr.GetTextBoxAttr().GetMaxSize().GetHeight()); + + if (attr.GetTextBoxAttr().HasVerticalAlignment()) + { + wxString value; + if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP) + value = wxT("top"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE) + value = wxT("centre"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM) + value = wxT("bottom"); + else + value = wxT("none"); + AddAttribute(str, wxT("verticalalignment"), value); + } + + 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().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT) + value = wxT("right"); + else if (attr.GetTextBoxAttr().GetClearMode() == 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("")); + + level ++; + + size_t i; + for (i = 0; i < properties.GetCount(); i++) + { + const wxVariant& var = properties[i]; + if (!var.IsNull()) + { + const wxString& name = var.GetName(); + wxString value = MakeStringFromProperty(var); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + 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); + wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition); + 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 (boxDef) + { + defNode->SetName(wxT("boxstyle")); + + AddAttributes(styleNode, def->GetStyle(), 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().IsOk()) + node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour())); + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk()) + 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.GetTextBoxAttr().GetBoxStyleName().empty()) + node->AddAttribute(wxT("boxstyle"), attr.GetTextBoxAttr().GetBoxStyleName()); + + 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().GetHeight()); + AddAttribute(node, wxT("minwidth"), attr.GetTextBoxAttr().GetMinSize().GetWidth()); + AddAttribute(node, wxT("minheight"), attr.GetTextBoxAttr().GetMinSize().GetHeight()); + AddAttribute(node, wxT("maxwidth"), attr.GetTextBoxAttr().GetMaxSize().GetWidth()); + AddAttribute(node, wxT("maxheight"), attr.GetTextBoxAttr().GetMaxSize().GetHeight()); + + if (attr.GetTextBoxAttr().HasVerticalAlignment()) + { + wxString value; + if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP) + value = wxT("top"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE) + value = wxT("centre"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM) + value = wxT("bottom"); + else + value = wxT("none"); + AddAttribute(node, wxT("verticalalignment"), value); + } + + 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().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT) + value = wxT("right"); + else if (attr.GetTextBoxAttr().GetClearMode() == 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.empty()) + return false; + +#ifdef __WXMSW__ + if (facename == wxT("Times")) + { + facename = wxT("Times New Roman"); + return true; + } + else if (facename == wxT("Helvetica")) + { + facename = wxT("Arial"); + return true; + } + else if (facename == wxT("Courier")) + { + facename = wxT("Courier New"); + return true; + } + else + return false; +#else + if (facename == wxT("Times New Roman")) + { + facename = wxT("Times"); + return true; + } + else if (facename == wxT("Arial")) + { + facename = wxT("Helvetica"); + return true; + } + else if (facename == wxT("Courier New")) + { + facename = wxT("Courier"); + return true; + } + else + return false; +#endif +} + +static inline long wxRichTextColourStringToLong(const wxString& colStr) +{ + if (!colStr.IsEmpty()) + { + wxColour col(colStr); + return col.GetRGB(); + } + else + return 0; +} + +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) + { + const wxString& name = xmlAttr->GetName(); + const wxString& value = xmlAttr->GetValue(); + found = true; + + if (name == wxT("fontface")) + { + if (!value.empty()) + { + wxString v = value; + if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES) + wxRichTextFixFaceName(v); + attr.SetFontFaceName(v); + } + } + else if (name == wxT("fontfamily")) + { + if (!value.empty()) + attr.SetFontFamily((wxFontFamily)wxAtoi(value)); + } + else if (name == wxT("fontstyle")) + { + 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("boxstyle")) + { + if (!value.empty()) + { + attr.GetTextBoxAttr().SetBoxStyleName(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("minwidth")) + { + attr.GetTextBoxAttr().GetMinSize().GetWidth().SetValue(wxRichTextParseDimension(value)); + } + else if (name == wxT("minheight")) + { + attr.GetTextBoxAttr().GetMinSize().GetHeight().SetValue(wxRichTextParseDimension(value)); + } + else if (name == wxT("maxwidth")) + { + attr.GetTextBoxAttr().GetMaxSize().GetWidth().SetValue(wxRichTextParseDimension(value)); + } + else if (name == wxT("maxheight")) + { + attr.GetTextBoxAttr().GetMaxSize().GetHeight().SetValue(wxRichTextParseDimension(value)); + } + + else if (name == wxT("verticalalignment")) + { + if (value == wxT("top")) + attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP); + else if (value == wxT("centre")) + attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE); + else if (value == wxT("bottom")) + attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM); + else if (value == wxT("none")) + attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE); + } + 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((wxTextBoxAttrCollapseMode) wxAtoi(value)); + + 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; +} + +#endif + // wxUSE_STREAMS + +// Import this object from XML +bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) +{ + handler->ImportProperties(this, node); + handler->ImportStyle(GetAttributes(), node, UsesParagraphAttributes()); - if (deleteConvFile) - delete convFile; - - return success; + *recurse = true; + + return true; } -/// Recursively export an object -bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, 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"); +#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); - if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) { - wxRichTextPlainText& text = (wxRichTextPlainText&) obj; + handler->WriteProperties(stream, GetProperties(), indent); + } - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); + 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); + } + } - wxString style = CreateStyle(obj.GetAttributes(), false); + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; +} +#endif - OutputString(stream, style + wxT(">"), convMem, convFile); +#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()); - wxString str = text.GetText(); - if (str.Length() > 0 && (str[0] == wxT(' ') || str[str.Length()-1] == wxT(' '))) + wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject); + if (composite) + { + size_t i; + for (i = 0; i < composite->GetChildCount(); i++) { - OutputString(stream, wxT("\""), convMem, convFile); - OutputStringEnt(stream, str, convMem, convFile); - OutputString(stream, wxT("\""), convMem, convFile); + wxRichTextObject* child = composite->GetChild(i); + child->ExportXML(elementNode, handler); } - else - OutputStringEnt(stream, str, convMem, convFile); } - else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) + return true; +} +#endif + + +// Import this object from XML +bool wxRichTextPlainText::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); + + if (node->GetName() == wxT("text")) { - wxRichTextImage& imageObj = (wxRichTextImage&) obj; + 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 (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok()) - imageObj.MakeBlock(); + 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(); + } - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - if (!imageObj.GetImageBlock().Ok()) + 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) { - // No data - OutputString(stream, wxT(">"), convMem, convFile); + if (textChild->GetType() == wxXML_TEXT_NODE || + textChild->GetType() == wxXML_CDATA_SECTION_NODE) + { + wxString text2 = textChild->GetContent(); + text += text2; + } + textChild = textChild->GetNext(); } - else + + 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) { - OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj.GetImageBlock().GetImageType())); + 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()); + } + } - OutputIndentation(stream, indent+1); - OutputString(stream, wxT(""), convMem, convFile); - imageObj.GetImageBlock().WriteHex(stream); + // 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()); - OutputString(stream, wxT(""), convMem, convFile); + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } } - else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject))) + + 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); + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); - bool isPara = false; - if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout")) - isPara = true; + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); - wxString style = CreateStyle(obj.GetAttributes(), isPara); + 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, style + wxT(">"), convMem, convFile); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + return true; +} +#endif - wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj; - size_t i; - for (i = 0; i < composite.GetChildCount(); i++) +#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) { - wxRichTextObject* child = composite.GetChild(i); - ExportXML(stream, convMem, convFile, *child, indent+1); + 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; } } - if (objectName != wxT("text")) - OutputIndentation(stream, indent); + 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); - OutputString(stream, wxT(""), convMem, convFile); + if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')) + fragment = wxT("\"") + fragment + wxT("\""); + textNode->SetContent(fragment); + } return true; } +#endif + -/// Create style parameters -wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara) +// Import this object from XML +bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) { - wxString str; - if (attr.GetTextColour().Ok()) + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); + + wxBitmapType imageType = wxBITMAP_TYPE_PNG; + wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString); + if (!value.empty()) { - str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\""); + 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; + } } - if (attr.GetBackgroundColour().Ok()) + + wxString data; + + wxXmlNode* imageChild = node->GetChildren(); + while (imageChild) { - str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); + 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 (attr.GetFont().Ok()) + if (!data.empty()) { - str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); - str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); - str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); - str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); - str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); - str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); + wxStringInputStream strStream(data); + + GetImageBlock().ReadHex(strStream, data.length(), imageType); + + return true; } + else + return false; +} - if (!attr.GetCharacterStyleName().empty()) - str << wxT(" charactertyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\""); +#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); - if (isPara) + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT("GetConvMem(), handler->GetConvFile()); + if (!GetImageBlock().IsOk()) + { + // 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) { - str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); - str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); - str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); - str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); - str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); - str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); - str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); - str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); - str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); - str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\""); + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } - if (!attr.GetParagraphStyleName().empty()) - str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + ::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().IsOk()) + 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(); +#ifdef __WXDEBUG__ + int size2 = stream.GetOutputStreamBuffer()->GetIntPosition(); + wxASSERT(size == size2); +#endif + 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 - return str; + textNode->SetContent(strData); + textNode->SetNoConversion(true); // optimize speed + + return true; } +#endif + -/// Get style parameters -bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara) +// Import this object from XML +bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) { - wxString fontFacename; - int fontSize = 12; - int fontFamily = wxDEFAULT; - int fontWeight = wxNORMAL; - int fontStyle = wxNORMAL; - bool fontUnderlined = false; + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); - fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); + *recurse = true; - wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); - if (!value.empty()) - fontFamily = wxAtoi(value); + wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString); + if (partial == wxT("true")) + SetPartialParagraph(true); - value = node->GetPropVal(wxT("fontstyle"), wxEmptyString); - if (!value.empty()) - fontStyle = wxAtoi(value); + wxXmlNode* child = wxRichTextXMLHandler::FindNode(node, wxT("stylesheet")); + if (child && (handler->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) + { + handler->ImportStyleDefinition(sheet, child2); - value = node->GetPropVal(wxT("fontsize"), wxEmptyString); - if (!value.empty()) - fontSize = wxAtoi(value); + child2 = child2->GetNext(); + } - value = node->GetPropVal(wxT("fontweight"), wxEmptyString); - if (!value.empty()) - fontWeight = wxAtoi(value); + // 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); + } - value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString); - if (!value.empty()) - fontUnderlined = wxAtoi(value) != 0; + 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); + wxString nodeName = GetXMLNodeName(); + ::OutputString(stream, wxT("<") + nodeName, handler->GetConvMem(), handler->GetConvFile()); - attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); + wxString style = handler->AddAttributes(GetAttributes(), true); - value = node->GetPropVal(wxT("textcolor"), wxEmptyString); - if (!value.empty()) + if (GetPartialParagraph()) + style << wxT(" partialparagraph=\"true\""); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) { - if (value[0] == wxT('#')) - attr.SetTextColour(HexStringToColour(value.Mid(1))); - else - attr.SetTextColour(value); + handler->WriteProperties(stream, GetProperties(), indent); } - value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString); - if (!value.empty()) + size_t i; + for (i = 0; i < GetChildCount(); i++) { - if (value[0] == wxT('#')) - attr.SetBackgroundColour(HexStringToColour(value.Mid(1))); - else - attr.SetBackgroundColour(value); + wxRichTextObject* child = GetChild(i); + child->ExportXML(stream, indent+1, handler); } - value = node->GetPropVal(wxT("characterstyle"), wxEmptyString); - if (!value.empty()) - attr.SetCharacterStyleName(value); + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; +} +#endif - // Set paragraph attributes - if (isPara) +#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, GetXMLNodeName()); + 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++) { - value = node->GetPropVal(wxT("alignment"), wxEmptyString); - if (!value.empty()) - attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); + wxRichTextObject* child = GetChild(i); + child->ExportXML(elementNode, handler); + } + + return true; +} +#endif + +// Import this object from XML +bool wxRichTextTable::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) +{ + wxRichTextBox::ImportFromXML(buffer, node, handler, recurse); - int leftSubIndent = 0; - int leftIndent = 0; - value = node->GetPropVal(wxT("leftindent"), wxEmptyString); - if (!value.empty()) - leftIndent = wxAtoi(value); - value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString); - if (!value.empty()) - leftSubIndent = wxAtoi(value); - attr.SetLeftIndent(leftIndent, leftSubIndent); + *recurse = false; + + m_rowCount = wxAtoi(node->GetAttribute(wxT("rows"), wxEmptyString)); + m_colCount = wxAtoi(node->GetAttribute(wxT("cols"), wxEmptyString)); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + wxRichTextObject* childObj = handler->CreateObjectForXMLName(this, child->GetName()); + if (childObj) + { + AppendChild(childObj); + handler->ImportXML(buffer, childObj, child); + } + child = child->GetNext(); + } - value = node->GetPropVal(wxT("rightindent"), wxEmptyString); - if (!value.empty()) - attr.SetRightIndent(wxAtoi(value)); + m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount); + int i, j; + for (i = 0; i < m_rowCount; i++) + { + wxRichTextObjectPtrArray& colArray = m_cells[i]; + for (j = 0; j < m_colCount; j++) + { + int idx = i * m_colCount + j; + if (idx < (int) GetChildren().GetCount()) + { + wxRichTextCell* cell = wxDynamicCast(GetChildren().Item(idx)->GetData(), wxRichTextCell); + if (cell) + colArray.Add(cell); + } + } + } - value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphSpacingBefore(wxAtoi(value)); + return true; +} - value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphSpacingAfter(wxAtoi(value)); +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextTable::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) +{ + ::OutputIndentation(stream, indent); + wxString nodeName = GetXMLNodeName(); + ::OutputString(stream, wxT("<") + nodeName, handler->GetConvMem(), handler->GetConvFile()); - value = node->GetPropVal(wxT("linespacing"), wxEmptyString); - if (!value.empty()) - attr.SetLineSpacing(wxAtoi(value)); + wxString style = handler->AddAttributes(GetAttributes(), true); - value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString); - if (!value.empty()) - attr.SetBulletStyle(wxAtoi(value)); + style << wxT(" rows=\"") << m_rowCount << wxT("\""); + style << wxT(" cols=\"") << m_colCount << wxT("\""); - value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString); - if (!value.empty()) - attr.SetBulletNumber(wxAtoi(value)); + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); - value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString); - if (!value.empty()) - attr.SetBulletSymbol(value[0]); + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + } - value = node->GetPropVal(wxT("parstyle"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphStyleName(value); + int i, j; + for (i = 0; i < m_rowCount; i++) + { + for (j = 0; j < m_colCount; j ++) + { + wxRichTextCell* cell = GetCell(i, j); + cell->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 wxRichTextTable::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()); + + elementNode->AddAttribute(wxT("rows"), wxString::Format(wxT("%d"), m_rowCount)); + elementNode->AddAttribute(wxT("cols"), wxString::Format(wxT("%d"), m_colCount)); + + int i, j; + for (i = 0; i < m_rowCount; i++) + { + for (j = 0; j < m_colCount; j ++) + { + wxRichTextCell* cell = GetCell(i, j); + cell->ExportXML(elementNode, handler); + } + } + return true; +} #endif - // wxUSE_STREAMS - + + #endif // wxUSE_RICHTEXT && wxUSE_XML +