X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/87eaa6f666513f6209b52ba765b48900fb3ed49f..abe0903cd4a16c6d8f50a3263d601fc718038c6d:/src/richtext/richtextxml.cpp diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index bcff1aa074..f78883c8be 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -23,6 +23,7 @@ #ifndef WX_PRECOMP #include "wx/intl.h" #include "wx/module.h" + #include "wx/log.h" #endif #include "wx/filename.h" @@ -30,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) { @@ -73,7 +136,7 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s { } else - ImportXML(buffer, child); + ImportXML(buffer, buffer, child); } child = child->GetNext(); @@ -92,172 +155,94 @@ 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")) - { - wxString partial = node->GetPropVal(wxT("partialparagraph"), wxEmptyString); - if (partial == wxT("true")) - buffer->SetPartialParagraph(true); - } - 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")) - { - 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")) + if (child->GetName() != wxT("stylesheet")) { - int imageType = wxBITMAP_TYPE_PNG; - wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString); - if (!value.empty()) - imageType = wxAtoi(value); - - 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()) + wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName()); + if (childObj) { - wxRichTextImage* imageObj = new wxRichTextImage(para); - para->AppendChild(imageObj); - - wxStringInputStream strStream(data); - - imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType); + compositeParent->AppendChild(childObj); + ImportXML(buffer, childObj, child); } } child = child->GetNext(); } - - doneChildren = true; } - else if (name == wxT("stylesheet")) + + return true; +} + +bool wxRichTextXMLHandler::ImportProperties(wxRichTextObject* obj, wxXmlNode* node) +{ + wxXmlNode* child = node->GetChildren(); + while (child) { - if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET) + if (child->GetName() == wxT("properties")) { - wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet; - wxString sheetName = node->GetPropVal(wxT("name"), wxEmptyString); - wxString sheetDescription = node->GetPropVal(wxT("description"), wxEmptyString); - sheet->SetName(sheetName); - sheet->SetDescription(sheetDescription); - - wxXmlNode* child = node->GetChildren(); - while (child) + wxXmlNode* propertyChild = child->GetChildren(); + while (propertyChild) { - ImportStyleDefinition(sheet, child); + 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); - child = child->GetNext(); + wxVariant var = MakePropertyFromString(name, value, type); + if (!var.IsNull()) + { + obj->GetProperties().SetProperty(var); + } + } + propertyChild = propertyChild->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) - { - wxXmlNode* child = node->GetChildren(); - while (child) - { - ImportXML(buffer, child); - child = child->GetNext(); } + child = child->GetNext(); } - return true; } bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node) { wxString styleType = node->GetName(); - wxString styleName = node->GetPropVal(wxT("name"), wxEmptyString); - wxString baseStyleName = node->GetPropVal(wxT("basestyle"), wxEmptyString); + 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")) @@ -270,8 +255,8 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx { if (child->GetName() == wxT("style")) { - wxTextAttrEx attr; - GetStyle(attr, child, false); + wxRichTextAttr attr; + ImportStyle(attr, child, false); def->SetStyle(attr); } child = child->GetNext(); @@ -283,7 +268,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx { wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName); - wxString nextStyleName = node->GetPropVal(wxT("nextstyle"), wxEmptyString); + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); def->SetNextStyle(nextStyleName); def->SetBaseStyle(baseStyleName); @@ -292,8 +277,8 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx { if (child->GetName() == wxT("style")) { - wxTextAttrEx attr; - GetStyle(attr, child, false); + wxRichTextAttr attr; + ImportStyle(attr, child, true); def->SetStyle(attr); } child = child->GetNext(); @@ -301,11 +286,31 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx sheet->AddParagraphStyle(def); } + else if (styleType == wxT("boxstyle")) + { + wxRichTextBoxStyleDefinition* def = new wxRichTextBoxStyleDefinition(styleName); + + def->SetBaseStyle(baseStyleName); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + def->SetStyle(attr); + } + child = child->GetNext(); + } + + sheet->AddBoxStyle(def); + } else if (styleType == wxT("liststyle")) { wxRichTextListStyleDefinition* def = new wxRichTextListStyleDefinition(styleName); - wxString nextStyleName = node->GetPropVal(wxT("nextstyle"), wxEmptyString); + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); def->SetNextStyle(nextStyleName); def->SetBaseStyle(baseStyleName); @@ -314,11 +319,11 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx { if (child->GetName() == wxT("style")) { - wxTextAttrEx attr; - GetStyle(attr, child, false); + wxRichTextAttr attr; + ImportStyle(attr, child, true); - wxString styleLevel = child->GetPropVal(wxT("level"), wxEmptyString); - if (styleLevel.IsEmpty()) + wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString); + if (styleLevel.empty()) { def->SetStyle(attr); } @@ -399,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 @@ -408,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 @@ -435,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, @@ -481,7 +509,11 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, 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; @@ -490,36 +522,225 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, 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()) @@ -529,14 +750,14 @@ 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. @@ -544,7 +765,10 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& { 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 { @@ -558,275 +782,223 @@ 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; -#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); - - int level = 1; + 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)) { - OutputIndentation(stream, level); + wxXmlNode* styleSheetNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("stylesheet")); + rootNode->AddChild(styleSheetNode); + wxString nameAndDescr; - if (!buffer->GetStyleSheet()->GetName().IsEmpty()) - nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\""); - if (!buffer->GetStyleSheet()->GetDescription().IsEmpty()) - nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\""); - OutputString(stream, wxString(wxT(""), convMem, convFile); - int i; + 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(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(styleSheetNode, def); } for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) { wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); - ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(styleSheetNode, def); } for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) { wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); - ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + ExportStyleDefinition(styleSheetNode, def); } - OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + 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) - bool success = ExportXML(stream, convMem, convFile, *buffer, level); - - OutputString(stream, wxT("\n") , NULL, NULL); - OutputString(stream, wxT("\n"), NULL, NULL); - - if (deleteConvFile) - delete convFile; - - return success; -} +#if !wxUSE_UNICODE + m_convMem = wxConvCurrent; +#else + m_convMem = NULL; +#endif -/// 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"); + wxString s ; + s.Printf(wxT("\n"), + version, fileEncoding); + OutputString(stream, s); + OutputString(stream, wxT("")); - bool terminateTag = true; + int level = 1; - if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) { - wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj; - - wxString style = CreateStyle(obj.GetAttributes(), false); + 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; - int last = 0; - const wxString& text = textObj.GetText(); - int len = (int) text.Length(); - for (i = 0; i < len; i++) - { - int c = (int) text[i]; - if (c < 32 && c != 9 && c != 10 && c != 13) - { - if (i > 0) - { - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - - OutputString(stream, style + wxT(">"), convMem, convFile); - - wxString fragment(text.Mid(last, i-last)); - 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 9, 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); - } + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(stream, def, level + 1); } - wxString fragment; - if (last == 0) - fragment = text; - else - fragment = text.Mid(last, i-last); - - if (last < len) + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) { - 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); + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(stream, def, level + 1); } - else - terminateTag = false; - } - else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) - { - wxRichTextImage& imageObj = (wxRichTextImage&) obj; - if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok()) - imageObj.MakeBlock(); - - OutputIndentation(stream, indent); - OutputString(stream, wxT("<") + objectName, convMem, convFile); - if (!imageObj.GetImageBlock().Ok()) + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) { - // No data - OutputString(stream, wxT(">"), convMem, convFile); + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(stream, def, level + 1); } - else + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++) { - OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj.GetImageBlock().GetImageType())); + wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i); + ExportStyleDefinition(stream, def, level + 1); } - OutputIndentation(stream, indent+1); - OutputString(stream, wxT(""), convMem, convFile); - - imageObj.GetImageBlock().WriteHex(stream); - - OutputString(stream, wxT(""), convMem, convFile); + OutputIndentation(stream, level); + OutputString(stream, wxT("")); } - 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); + bool success = ExportXML(stream, *buffer, level); - if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph()) - style << wxT(" partialparagraph=\"true\""); + OutputString(stream, wxT("\n")); + OutputString(stream, wxT("\n")); - OutputString(stream, style + wxT(">"), convMem, convFile); + if (deleteConvFile) + delete m_convFile; + m_convFile = NULL; + m_convMem = NULL; +#endif - 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); - } - } + return success; +} - if (objectName != wxT("text")) - OutputIndentation(stream, indent); +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT - if (terminateTag) - OutputString(stream, wxT(""), convMem, convFile); +/// 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, 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); 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.IsEmpty()) - baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\""); + if (!baseStyle.empty()) + baseStyleProp = wxT(" basestyle=\"") + AttributeToXML(baseStyle) + wxT("\""); wxString descr = def->GetDescription(); wxString descrProp; - if (!descr.IsEmpty()) - descrProp = wxT(" description=\"") + descr + wxT("\""); + if (!descr.empty()) + descrProp = wxT(" description=\"") + AttributeToXML(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=\"") << AttributeToXML(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 ++) @@ -834,377 +1006,1016 @@ 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=\"") << 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(""), 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("")); level --; OutputIndentation(stream, level); - OutputString(stream, wxT(""), convMem, convFile); + OutputString(stream, wxT("")); } + return true; } -/// Create style parameters -wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& 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("\""); - } - if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok()) - { - str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); - } + if (attr.HasTextColour() && attr.GetTextColour().IsOk()) + AddAttribute(str, wxT("textcolor"), attr.GetTextColour()); - if (attr.GetFont().Ok()) - { - if (attr.HasFontSize()) - str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk()) + AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour()); - //if (attr.HasFontFamily()) - // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); + if (attr.HasFontSize()) + AddAttribute(str, wxT("fontsize"), attr.GetFontSize()); - if (attr.HasFontItalic()) - str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); + if (attr.HasFontFamily()) + AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily()); - if (attr.HasFontWeight()) - str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); + if (attr.HasFontItalic()) + AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle()); - if (attr.HasFontUnderlined()) - str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); + if (attr.HasFontWeight()) + AddAttribute(str, wxT("fontweight"), attr.GetFontWeight()); - if (attr.HasFontFaceName()) - str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); - } + if (attr.HasFontUnderlined()) + AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined()); + + if (attr.HasFontFaceName()) + AddAttribute(str, wxT("fontface"), AttributeToXML(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"), AttributeToXML(attr.GetCharacterStyleName())); + + if (attr.HasURL()) + 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"), AttributeToXML(attr.GetBulletText())); - str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\""); + AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont()); } if (attr.HasBulletName()) - str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\""); - - if (attr.HasURL()) - str << wxT(" url=\"") << attr.GetURL() << wxT("\""); + AddAttribute(str, wxT("bulletname"), AttributeToXML(attr.GetBulletName())); if (!attr.GetParagraphStyleName().empty()) - str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + AddAttribute(str, wxT("parstyle"), AttributeToXML(attr.GetParagraphStyleName())); if (!attr.GetListStyleName().empty()) - str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\""); + AddAttribute(str, wxT("liststyle"), AttributeToXML(attr.GetListStyleName())); + + if (!attr.GetTextBoxAttr().GetBoxStyleName().empty()) + AddAttribute(str, wxT("boxstyle"), AttributeToXML(attr.GetTextBoxAttr().GetBoxStyleName())); 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().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; } -/// Get style parameters -bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara) +// Make a string from the given property. This can be overridden for custom variants. +wxString wxRichTextXMLHandler::MakeStringFromProperty(const wxVariant& var) { - wxString fontFacename; - int fontSize = 12; - int fontFamily = wxDEFAULT; - int fontWeight = wxNORMAL; - int fontStyle = wxNORMAL; - bool fontUnderlined = false; + return var.MakeString(); +} - int fontFlags = 0; +// 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; +} - fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); - if (!fontFacename.IsEmpty()) - fontFlags |= wxTEXT_ATTR_FONT_FACE; +// Write the properties +bool wxRichTextXMLHandler::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level) +{ + if (properties.GetCount() > 0) + { + level ++; - wxString value; - //value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); - //if (!value.empty()) - // fontFamily = wxAtoi(value); + OutputIndentation(stream, level); + OutputString(stream, wxT("")); - value = node->GetPropVal(wxT("fontstyle"), wxEmptyString); - if (!value.empty()) - { - fontStyle = wxAtoi(value); - fontFlags |= wxTEXT_ATTR_FONT_ITALIC; - } + level ++; - value = node->GetPropVal(wxT("fontsize"), wxEmptyString); - if (!value.empty()) - { - fontSize = wxAtoi(value); - fontFlags |= wxTEXT_ATTR_FONT_SIZE; - } + 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); - value = node->GetPropVal(wxT("fontweight"), wxEmptyString); - if (!value.empty()) - { - fontWeight = wxAtoi(value); - fontFlags |= wxTEXT_ATTR_FONT_WEIGHT; - } + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + } - value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString); - if (!value.empty()) - { - fontUnderlined = wxAtoi(value) != 0; - fontFlags |= wxTEXT_ATTR_FONT_UNDERLINE; + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; } - attr.SetFlags(fontFlags); + return true; +} + - if (attr.HasFlag(wxTEXT_ATTR_FONT)) - attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); +#endif + // wxRICHTEXT_HAVE_DIRECT_OUTPUT - // Restore correct font flags - attr.SetFlags(fontFlags); +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj) +{ + obj.ExportXML(parent, this); - value = node->GetPropVal(wxT("textcolor"), wxEmptyString); - if (!value.empty()) + 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) { - if (value[0] == wxT('#')) - attr.SetTextColour(HexStringToColour(value.Mid(1))); - else - attr.SetTextColour(value); + defNode->SetName(wxT("characterstyle")); + AddAttributes(styleNode, def->GetStyle(), false); } + else if (listDef) + { + defNode->SetName(wxT("liststyle")); - value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString); - if (!value.empty()) + 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) { - if (value[0] == wxT('#')) - attr.SetBackgroundColour(HexStringToColour(value.Mid(1))); - else - attr.SetBackgroundColour(value); + defNode->SetName(wxT("boxstyle")); + + AddAttributes(styleNode, def->GetStyle(), true); } + else if (paraDef) + { + defNode->SetName(wxT("paragraphstyle")); - value = node->GetPropVal(wxT("characterstyle"), wxEmptyString); - if (!value.empty()) - attr.SetCharacterStyleName(value); + if (!paraDef->GetNextStyle().empty()) + defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle()); - value = node->GetPropVal(wxT("texteffects"), wxEmptyString); - if (!value.IsEmpty()) - { - attr.SetTextEffects(wxAtoi(value)); + AddAttributes(styleNode, def->GetStyle(), true); } - value = node->GetPropVal(wxT("texteffectflags"), wxEmptyString); - if (!value.IsEmpty()) + 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()) { - attr.SetTextEffectFlags(wxAtoi(value)); + 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? - // Set paragraph attributes if (isPara) { - value = node->GetPropVal(wxT("alignment"), wxEmptyString); - if (!value.empty()) - attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); - - int leftSubIndent = 0; - int leftIndent = 0; - bool hasLeftIndent = false; - - value = node->GetPropVal(wxT("leftindent"), wxEmptyString); - if (!value.empty()) - { - leftIndent = wxAtoi(value); - hasLeftIndent = true; - } + if (attr.HasAlignment()) + node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment())); - value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString); - if (!value.empty()) + if (attr.HasLeftIndent()) { - leftSubIndent = wxAtoi(value); - hasLeftIndent = true; + node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent())); + node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent())); } - if (hasLeftIndent) - attr.SetLeftIndent(leftIndent, leftSubIndent); - - value = node->GetPropVal(wxT("rightindent"), wxEmptyString); - if (!value.empty()) - attr.SetRightIndent(wxAtoi(value)); + if (attr.HasRightIndent()) + node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent())); - value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphSpacingBefore(wxAtoi(value)); + if (attr.HasParagraphSpacingAfter()) + node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter())); - value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphSpacingAfter(wxAtoi(value)); + if (attr.HasParagraphSpacingBefore()) + node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore())); - value = node->GetPropVal(wxT("linespacing"), wxEmptyString); - if (!value.empty()) - attr.SetLineSpacing(wxAtoi(value)); + if (attr.HasLineSpacing()) + node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing())); - value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString); - if (!value.empty()) - attr.SetBulletStyle(wxAtoi(value)); + if (attr.HasBulletStyle()) + node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle())); - value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString); - if (!value.empty()) - attr.SetBulletNumber(wxAtoi(value)); + if (attr.HasBulletNumber()) + node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber())); - value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString); - if (!value.empty()) + if (attr.HasBulletText()) { - wxChar ch = wxAtoi(value); - wxString s; - s << ch; - attr.SetBulletText(s); - } - - value = node->GetPropVal(wxT("bullettext"), wxEmptyString); - if (!value.empty()) - attr.SetBulletText(value); + // 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()); - value = node->GetPropVal(wxT("bulletfont"), wxEmptyString); - if (!value.empty()) - attr.SetBulletFont(value); + if (!attr.GetBulletFont().empty()) + node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont()); + } - value = node->GetPropVal(wxT("bulletname"), wxEmptyString); - if (!value.empty()) - attr.SetBulletName(value); + if (attr.HasBulletName()) + node->AddAttribute(wxT("bulletname"), attr.GetBulletName()); - value = node->GetPropVal(wxT("url"), wxEmptyString); - if (!value.empty()) - attr.SetURL(value); + if (!attr.GetParagraphStyleName().empty()) + node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName()); - value = node->GetPropVal(wxT("parstyle"), wxEmptyString); - if (!value.empty()) - attr.SetParagraphStyleName(value); + if (!attr.GetListStyleName().empty()) + node->AddAttribute(wxT("liststyle"), attr.GetListStyleName()); - value = node->GetPropVal(wxT("liststyle"), wxEmptyString); - if (!value.empty()) - attr.SetListStyleName(value); + if (!attr.GetTextBoxAttr().GetBoxStyleName().empty()) + node->AddAttribute(wxT("boxstyle"), attr.GetTextBoxAttr().GetBoxStyleName()); - value = node->GetPropVal(wxT("tabs"), wxEmptyString); - if (!value.empty()) + if (attr.HasTabs()) { - wxArrayInt tabs; - wxStringTokenizer tkz(value, wxT(",")); - while (tkz.HasMoreTokens()) + wxString tabs; + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) { - wxString token = tkz.GetNextToken(); - tabs.Add(wxAtoi(token)); + if (i > 0) + tabs << wxT(","); + tabs << attr.GetTabs()[i]; } - attr.SetTabs(tabs); + node->AddAttribute(wxT("tabs"), tabs); } - value = node->GetPropVal(wxT("pagebreak"), wxEmptyString); - if (!value.IsEmpty()) - { - attr.SetPageBreak(wxAtoi(value) != 0); - } + if (attr.HasPageBreak()) + node->AddAttribute(wxT("pagebreak"), wxT("1")); - value = node->GetPropVal(wxT("outlinelevel"), wxEmptyString); - if (!value.IsEmpty()) - { - attr.SetOutlineLevel(wxAtoi(value) != 0); - } + 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; @@ -1213,6 +2024,686 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is #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()); + + *recurse = true; + + 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, bool* recurse) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); + + 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, bool* recurse) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); + + 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().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) + { + 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().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 + + 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, bool* recurse) +{ + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); + + *recurse = true; + + wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString); + if (partial == wxT("true")) + SetPartialParagraph(true); + + 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); + + 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); + } + + 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()); + + 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, 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++) + { + 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); + + *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(); + } + + 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); + } + } + } + + return true; +} + +#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()); + + wxString style = handler->AddAttributes(GetAttributes(), true); + + style << wxT(" rows=\"") << m_rowCount << wxT("\""); + style << wxT(" cols=\"") << m_colCount << wxT("\""); + + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + } + + 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 + + #endif // wxUSE_RICHTEXT && wxUSE_XML