X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5d7836c47de679f03b1b7e738c77164c11bd3689..8573b14550f4e9023f0d4cec2ebd13f4d6801bdf:/src/richtext/richtextxml.cpp diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index 2e171c5d07..9a34fd8894 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -1,10 +1,10 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: richtextxml.cpp +// Name: src/richtext/richtextxml.cpp // Purpose: XML and HTML I/O for wxRichTextCtrl // Author: Julian Smart -// Modified by: +// Modified by: // Created: 2005-09-30 -// RCS-ID: +// RCS-ID: $Id$ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -13,42 +13,113 @@ #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop + #pragma hdrstop #endif -#ifndef WX_PRECOMP - #include "wx/wx.h" -#endif +#if wxUSE_RICHTEXT && wxUSE_XML -#include "wx/image.h" +#include "wx/richtext/richtextxml.h" -#if wxUSE_RICHTEXT +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/module.h" + #include "wx/log.h" +#endif #include "wx/filename.h" #include "wx/clipbrd.h" #include "wx/wfstream.h" #include "wx/sstream.h" -#include "wx/module.h" #include "wx/txtstrm.h" +#include "wx/mstream.h" +#include "wx/tokenzr.h" +#include "wx/stopwatch.h" #include "wx/xml/xml.h" -#include "wx/richtext/richtextxml.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::LoadFile(wxRichTextBuffer *buffer, wxInputStream& stream) +bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream) { if (!stream.IsOk()) return false; + buffer->ResetAndClearCommands(); buffer->Clear(); wxXmlDocument* xmlDoc = new wxXmlDocument; bool success = true; - if (!xmlDoc->Load(stream, wxT("ISO-8859-1"))) + // This is the encoding to convert to (memory encoding rather than file encoding) + wxString encoding(wxT("UTF-8")); + +#if !wxUSE_UNICODE && wxUSE_INTL + encoding = wxLocale::GetSystemEncodingName(); +#endif + + if (!xmlDoc->Load(stream, encoding)) { + buffer->ResetAndClearCommands(); success = false; } else @@ -65,9 +136,9 @@ bool wxRichTextXMLHandler::LoadFile(wxRichTextBuffer *buffer, wxInputStream& str { } else - ImportXML(buffer, child); + ImportXML(buffer, buffer, child); } - + child = child->GetNext(); } } @@ -76,7 +147,7 @@ bool wxRichTextXMLHandler::LoadFile(wxRichTextBuffer *buffer, wxInputStream& str success = false; } } - + delete xmlDoc; buffer->UpdateRanges(); @@ -84,120 +155,196 @@ bool wxRichTextXMLHandler::LoadFile(wxRichTextBuffer *buffer, wxInputStream& str return success; } +/// Creates an object given an XML element name +wxRichTextObject* wxRichTextXMLHandler::CreateObjectForXMLName(wxRichTextObject* WXUNUSED(parent), const wxString& name) const +{ + if (name == wxT("text") || name == wxT("symbol")) + return new wxRichTextPlainText; + else if (name == wxT("image")) + return new wxRichTextImage; + else if (name == wxT("paragraph")) + return new wxRichTextParagraph; + else if (name == wxT("paragraphlayout")) + return new wxRichTextParagraphLayoutBox; + else if (name == wxT("textbox")) + return new wxRichTextBox; + else if (name == wxT("cell")) + return new wxRichTextCell; + else if (name == wxT("table")) + return new wxRichTextTable; + else + return NULL; +} + /// Recursively import an object -bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) +bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node) { - wxString name = node->GetName(); + bool recurse = false; + obj->ImportFromXML(buffer, node, this, & recurse); - bool doneChildren = false; + // TODO: how to control whether to import children. - if (name == wxT("paragraphlayout")) - { - } - else if (name == wxT("paragraph")) + wxRichTextCompositeObject* compositeParent = wxDynamicCast(obj, wxRichTextCompositeObject); + if (recurse && compositeParent) { - wxRichTextParagraph* para = new wxRichTextParagraph(buffer); - buffer->AppendChild(para); - - GetStyle(para->GetAttributes(), node, true); - wxXmlNode* child = node->GetChildren(); while (child) { - wxString childName = child->GetName(); - if (childName == wxT("text")) + if (child->GetName() != wxT("stylesheet")) { - wxString text; - wxXmlNode* textChild = child->GetChildren(); - while (textChild) + wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName()); + if (childObj) { - if (textChild->GetType() == wxXML_TEXT_NODE || - textChild->GetType() == wxXML_CDATA_SECTION_NODE) - { - wxString text2 = textChild->GetContent(); - - // Strip whitespace from end - if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('\n')) - text2 = text2.Mid(0, text2.Length()-1); + compositeParent->AppendChild(childObj); + ImportXML(buffer, childObj, child); + } + } + child = child->GetNext(); + } + } - if (text2.Length() > 0 && text2[0] == wxT('"')) - text2 = text2.Mid(1); - if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('"')) - text2 = text2.Mid(0, text2.Length() - 1); + return true; +} - // TODO: further entity translation - text2.Replace(wxT("<"), wxT("<")); - text2.Replace(wxT(">"), wxT(">")); - text2.Replace(wxT("&"), wxT("&")); - text2.Replace(wxT("""), wxT("\"")); +bool wxRichTextXMLHandler::ImportProperties(wxRichTextObject* obj, wxXmlNode* node) +{ + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("properties")) + { + wxXmlNode* propertyChild = child->GetChildren(); + while (propertyChild) + { + if (propertyChild->GetName() == wxT("property")) + { + wxString name = propertyChild->GetAttribute(wxT("name"), wxEmptyString); + wxString value = propertyChild->GetAttribute(wxT("value"), wxEmptyString); + wxString type = propertyChild->GetAttribute(wxT("type"), wxEmptyString); - text += text2; + wxVariant var = MakePropertyFromString(name, value, type); + if (!var.IsNull()) + { + obj->GetProperties().SetProperty(var); } - textChild = textChild->GetNext(); } + propertyChild = propertyChild->GetNext(); + } + } + child = child->GetNext(); + } + return true; +} - wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para); - GetStyle(textObject->GetAttributes(), child, false); +bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node) +{ + wxString styleType = node->GetName(); + wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString); + wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString); - para->AppendChild(textObject); - } - else if (childName == wxT("image")) - { - int imageType = wxBITMAP_TYPE_PNG; - wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString); - if (!value.IsEmpty()) - imageType = wxAtoi(value); + if (styleName.empty()) + return false; - wxString data; + if (styleType == wxT("characterstyle")) + { + wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName); + def->SetBaseStyle(baseStyleName); - 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(); - } + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, false); + def->SetStyle(attr); + } + child = child->GetNext(); + } - if (!data.IsEmpty()) - { - wxRichTextImage* imageObj = new wxRichTextImage(para); - para->AppendChild(imageObj); + sheet->AddCharacterStyle(def); + } + else if (styleType == wxT("paragraphstyle")) + { + wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName); - wxStringInputStream strStream(data); + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); + def->SetNextStyle(nextStyleName); + def->SetBaseStyle(baseStyleName); - imageObj->GetImageBlock().ReadHex(strStream, data.Length(), imageType); - } + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + def->SetStyle(attr); } child = child->GetNext(); } - doneChildren = true; + sheet->AddParagraphStyle(def); } + else if (styleType == wxT("boxstyle")) + { + wxRichTextBoxStyleDefinition* def = new wxRichTextBoxStyleDefinition(styleName); + + def->SetBaseStyle(baseStyleName); - if (!doneChildren) + 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->GetAttribute(wxT("nextstyle"), wxEmptyString); + def->SetNextStyle(nextStyleName); + def->SetBaseStyle(baseStyleName); + wxXmlNode* child = node->GetChildren(); while (child) { - ImportXML(buffer, child); + if (child->GetName() == wxT("style")) + { + wxRichTextAttr attr; + ImportStyle(attr, child, true); + + wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString); + if (styleLevel.empty()) + { + def->SetStyle(attr); + } + else + { + int level = wxAtoi(styleLevel); + if (level > 0 && level <= 10) + { + def->SetLevelAttributes(level-1, attr); + } + } + } child = child->GetNext(); } - } + + sheet->AddListStyle(def); + } return true; } - //----------------------------------------------------------------------------- // xml support routines //----------------------------------------------------------------------------- @@ -242,7 +389,7 @@ wxString wxRichTextXMLHandler::GetNodeContent(wxXmlNode *node) wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param) { - if (param.IsEmpty()) + if (param.empty()) return GetNodeContent(node); else return GetNodeContent(GetParamNode(node, param)); @@ -257,14 +404,46 @@ wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, b return str1; } -// write string to output: +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 +#define WXUNUSED_IN_UNICODE(x) WXUNUSED(x) +#else +#define WXUNUSED_IN_UNICODE(x) x +#endif +#endif + +// write string to output inline static void OutputString(wxOutputStream& stream, const wxString& str, - wxMBConv *convMem = NULL, wxMBConv *convFile = NULL) + wxMBConv *WXUNUSED_IN_UNICODE(convMem), wxMBConv *convFile) { - if (str.IsEmpty()) return; + if (str.empty()) return; #if wxUSE_UNICODE - const wxWX2MBbuf buf(str.mb_str(convFile ? *convFile : wxConvUTF8)); - stream.Write((const char*)buf, strlen((const char*)buf)); + if (convFile) + { + const wxWX2MBbuf buf(str.mb_str(*convFile)); + stream.Write((const char*)buf, strlen((const char*)buf)); + } + else + { + const wxWX2MBbuf buf(str.mb_str(wxConvUTF8)); + stream.Write((const char*)buf, strlen((const char*)buf)); + } #else if ( convFile == NULL ) stream.Write(str.mb_str(), str.Len()); @@ -276,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, @@ -284,14 +471,19 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, wxString buf; size_t i, last, len; wxChar c; - + len = str.Len(); last = 0; for (i = 0; i < len; i++) { c = str.GetChar(i); + + // Original code excluded "&" but we _do_ want to convert + // the ampersand beginning & because otherwise when read in, + // the original "&" becomes "&". + if (c == wxT('<') || c == wxT('>') || c == wxT('"') || - (c == wxT('&') && (str.Mid(i+1, 4) != wxT("amp;")))) + (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ )) { OutputString(stream, str.Mid(last, i - last), convMem, convFile); switch (c) @@ -312,497 +504,2169 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, } last = i + 1; } + else if (wxUChar(c) > 127) + { + OutputString(stream, str.Mid(last, i - last), convMem, convFile); + + wxString s(wxT("&#")); +#if wxUSE_UNICODE + s << (int) c; +#else + s << (int) wxUChar(c); +#endif + s << wxT(";"); + OutputString(stream, s, NULL, NULL); + last = i + 1; + } } OutputString(stream, str.Mid(last, i - last), convMem, convFile); } -inline static void OutputIndentation(wxOutputStream& stream, int indent) +void wxRichTextXMLHandler::OutputString(wxOutputStream& stream, const wxString& str) +{ + ::OutputString(stream, str, m_convMem, m_convFile); +} + +void wxRichTextXMLHandler::OutputStringEnt(wxOutputStream& stream, const wxString& str) +{ + ::OutputStringEnt(stream, str, m_convMem, m_convFile); +} + +void wxRichTextXMLHandler::OutputIndentation(wxOutputStream& stream, int indent) { wxString str = wxT("\n"); for (int i = 0; i < indent; i++) str << wxT(' ') << wxT(' '); - OutputString(stream, str, NULL, NULL); + ::OutputString(stream, str, NULL, NULL); } -static wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s) +wxString wxRichTextXMLHandler::AttributeToXML(const wxString& str) { - stream.Write(s, s.Length()); - return stream; + wxString str1; + size_t i, last, len; + wxChar c; + + len = str.Len(); + last = 0; + for (i = 0; i < len; i++) + { + c = str.GetChar(i); + + // Original code excluded "&" but we _do_ want to convert + // the ampersand beginning & because otherwise when read in, + // the original "&" becomes "&". + + 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; } -static wxOutputStream& operator <<(wxOutputStream& stream, long l) +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + +static inline void AddAttribute(wxString& str, const wxString& name, const int& v) { - wxString str; - str.Printf(wxT("%ld"), l); - return stream << str; + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%d"), v) << wxT("\""); } -static wxOutputStream& operator <<(wxOutputStream& stream, const char c) +static inline void AddAttribute(wxString& str, const wxString& name, const long& v) { - wxString str; - str.Printf(wxT("%c"), c); - return stream << str; + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%ld"), v) << wxT("\""); } -// Convert a colour to a 6-digit hex string -static wxString ColourToHexString(const wxColour& col) +static inline void AddAttribute(wxString& str, const wxString& name, const double& v) { - wxString hex; - - hex += wxDecToHex(col.Red()); - hex += wxDecToHex(col.Green()); - hex += wxDecToHex(col.Blue()); - - return hex; + str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%.2f"), (float) v) << wxT("\""); } -// Convert 6-digit hex string to a colour -wxColour HexStringToColour(const wxString& hex) +static inline void AddAttribute(wxString& str, const wxString& name, const wxChar* s) { - unsigned int r = 0; - unsigned int g = 0; - unsigned int b = 0; - r = wxHexToDec(hex.Mid(0, 2)); - g = wxHexToDec(hex.Mid(2, 2)); - b = wxHexToDec(hex.Mid(4, 2)); + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); +} - return wxColour(r, g, b); +static inline void AddAttribute(wxString& str, const wxString& name, const wxString& s) +{ + str << wxT(" ") << name << wxT("=\"") << s << wxT("\""); } -bool wxRichTextXMLHandler::SaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream) +static inline void AddAttribute(wxString& str, const wxString& name, const wxColour& col) { - if (!stream.IsOk()) - return false; + str << wxT(" ") << name << wxT("=\"") << wxT("#") << ColourToHexString(col) << wxT("\""); +} - wxString version(wxT("1.0") ) ; -#if wxUSE_UNICODE - wxString fileencoding(wxT("UTF-8")) ; - wxString memencoding(wxT("UTF-8")) ; -#else - wxString fileencoding(wxT("ISO-8859-1")) ; - wxString memencoding(wxT("ISO-8859-1")) ; -#endif - wxString s ; - - wxMBConv *convMem = NULL, *convFile = NULL; -#if wxUSE_UNICODE - convFile = new wxCSConv(fileencoding); -#else - if ( fileencoding != memencoding ) +static inline void AddAttribute(wxString& str, const wxString& name, const wxTextAttrDimension& dim) +{ + if (dim.IsValid()) { - convFile = new wxCSConv(fileencoding); - convMem = new wxCSConv(memencoding); + wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString((int) dim.GetFlags()); + str << wxT(" ") << name << wxT("=\""); + str << value; + str << wxT("\""); } -#endif - - s.Printf(wxT("\n"), - (const wxChar*) version, (const wxChar*) fileencoding ); - OutputString(stream, s, NULL, NULL); - OutputString(stream, wxT("") , NULL, NULL); +} - int level = 1; - ExportXML(stream, convMem, convFile, *buffer, level); +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()); +} - OutputString(stream, wxT("\n") , NULL, NULL); - OutputString(stream, wxT("\n"), NULL, NULL); - - delete convFile; - delete convMem; +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()); +} - return true; +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()); } -/// 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"); +#endif + // wxRICHTEXT_HAVE_DIRECT_OUTPUT - if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) - { - wxRichTextPlainText& text = (wxRichTextPlainText&) obj; +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT - OutputIndentation(stream, indent); - stream << wxT("<") << objectName; - - wxString style = CreateStyle(obj.GetAttributes(), false); - - stream << style << wxT(">"); - - wxString str = text.GetText(); - if (str.Length() > 0 && (str[0] == wxT(' ') || str[str.Length()-1] == wxT(' '))) - { - stream << wxT("\""); - OutputStringEnt(stream, str, convMem, convFile); - stream << wxT("\""); - } - else - OutputStringEnt(stream, str, convMem, convFile); - } - else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) - { - wxRichTextImage& imageObj = (wxRichTextImage&) obj; +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const int& v) +{ + node->AddAttribute(name, MakeString(v)); +} - if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok()) - imageObj.MakeBlock(); +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const long& v) +{ + node->AddAttribute(name, MakeString(v)); +} - OutputIndentation(stream, indent); - stream << wxT("<") << objectName; - if (!imageObj.GetImageBlock().Ok()) - { - // No data - stream << wxT(">"); - } - else - { - stream << wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) << wxT(">"); - } +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const double& v) +{ + node->AddAttribute(name, MakeString(v)); +} - OutputIndentation(stream, indent+1); - stream << wxT(""); +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const wxString& s) +{ + node->AddAttribute(name, s); +} - imageObj.GetImageBlock().WriteHex(stream); +static inline void AddAttribute(wxXmlNode* node, const wxString& name, const wxColour& col) +{ + node->AddAttribute(name, MakeString(col)); +} - stream << wxT(""); - } - else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject))) +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()) + return false; + + wxString version(wxT("1.0") ) ; + + bool deleteConvFile = false; + wxString fileEncoding; + //wxMBConv* convFile = NULL; + +#if wxUSE_UNICODE + fileEncoding = wxT("UTF-8"); + m_convFile = & wxConvUTF8; +#else + fileEncoding = wxT("ISO-8859-1"); + m_convFile = & wxConvISO8859_1; +#endif + + // If SetEncoding has been called, change the output encoding. + if (!m_encoding.empty() && m_encoding.Lower() != fileEncoding.Lower()) + { + if (m_encoding == wxT("")) + { +#if wxUSE_INTL + fileEncoding = wxLocale::GetSystemEncodingName(); + // if !wxUSE_INTL, we fall back to UTF-8 or ISO-8859-1 below +#endif + } + else + { + fileEncoding = m_encoding; + } + + // GetSystemEncodingName may not have returned a name + if (fileEncoding.empty()) +#if wxUSE_UNICODE + fileEncoding = wxT("UTF-8"); +#else + fileEncoding = wxT("ISO-8859-1"); +#endif + m_convFile = new wxCSConv(fileEncoding); + deleteConvFile = true; + } + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + wxStopWatch stopwatch; +#endif + wxXmlDocument* doc = new wxXmlDocument; + doc->SetFileEncoding(fileEncoding); + + wxXmlNode* rootNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("richtext")); + doc->SetRoot(rootNode); + rootNode->AddAttribute(wxT("version"), wxT("1.0.0.0")); + rootNode->AddAttribute(wxT("xmlns"), wxT("http://www.wxwidgets.org")); + + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) { - OutputIndentation(stream, indent); - stream << wxT("<") << objectName; - - bool isPara = false; - if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout")) - isPara = true; + wxXmlNode* styleSheetNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("stylesheet")); + rootNode->AddChild(styleSheetNode); + + wxString nameAndDescr; + + if (!buffer->GetStyleSheet()->GetName().empty()) + styleSheetNode->AddAttribute(wxT("name"), buffer->GetStyleSheet()->GetName()); + + if (!buffer->GetStyleSheet()->GetDescription().empty()) + styleSheetNode->AddAttribute(wxT("description"), buffer->GetStyleSheet()->GetDescription()); + + int i; + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) + { + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++) + { + wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i); + ExportStyleDefinition(styleSheetNode, def); + } + } + bool success = ExportXML(rootNode, *buffer); +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + long t = stopwatch.Time(); + wxLogDebug(wxT("Creating the document took %ldms"), t); + wxMessageBox(wxString::Format(wxT("Creating the document took %ldms"), t)); +#endif + if (success) + { +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + wxStopWatch s2; +#endif + success = doc->Save(stream); +#if wxRICHTEXT_USE_OUTPUT_TIMINGS + long t2 = s2.Time(); + wxLogDebug(wxT("Save() took %ldms"), t2); + wxMessageBox(wxString::Format(wxT("Save() took %ldms"), t2)); +#endif + } + delete doc; + doc = NULL; + +#else + // !(wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT) + +#if !wxUSE_UNICODE + m_convMem = wxConvCurrent; +#else + m_convMem = NULL; +#endif + + wxString s ; + s.Printf(wxT("\n"), + version, fileEncoding); + OutputString(stream, s); + OutputString(stream, wxT("")); + + int level = 1; + + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) + { + OutputIndentation(stream, level); + wxString nameAndDescr; + if (!buffer->GetStyleSheet()->GetName().empty()) + nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\""); + if (!buffer->GetStyleSheet()->GetDescription().empty()) + nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\""); + OutputString(stream, wxString(wxT("")); + + int i; + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) + { + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++) + { + wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i); + ExportStyleDefinition(stream, def, level + 1); + } + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + + + bool success = ExportXML(stream, *buffer, level); + + OutputString(stream, wxT("\n")); + OutputString(stream, wxT("\n")); + + if (deleteConvFile) + delete m_convFile; + m_convFile = NULL; + m_convMem = NULL; +#endif + + return success; +} + +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT + +/// Recursively export an object +bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int indent) +{ + obj.ExportXML(stream, indent, this); + + return true; +} + +bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level) +{ + wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); + wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); + wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition); + wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition); + + wxString name = def->GetName(); + wxString nameProp; + if (!name.empty()) + nameProp = wxT(" name=\"") + AttributeToXML(name) + wxT("\""); + + wxString baseStyle = def->GetBaseStyle(); + wxString baseStyleProp; + if (!baseStyle.empty()) + baseStyleProp = wxT(" basestyle=\"") + AttributeToXML(baseStyle) + wxT("\""); + + wxString descr = def->GetDescription(); + wxString descrProp; + if (!descr.empty()) + descrProp = wxT(" description=\"") + AttributeToXML(descr) + wxT("\""); + + if (charDef) + { + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), false); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (listDef) + { + OutputIndentation(stream, level); + + if (!listDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(listDef->GetNextStyle()) << wxT("\""); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + int i; + for (i = 0; i < 10; i ++) + { + wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); + if (levelAttr) + { + wxString style = AddAttributes(def->GetStyle(), true); + wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1)); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (paraDef) + { + OutputIndentation(stream, level); + + if (!paraDef->GetNextStyle().empty()) + baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(paraDef->GetNextStyle()) << wxT("\""); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + else if (boxDef) + { + OutputIndentation(stream, level); + + OutputString(stream, wxT("")); + + level ++; + + wxString style = AddAttributes(def->GetStyle(), true); + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("")); + } + + + return true; +} + +/// Create a string containing style attributes +wxString wxRichTextXMLHandler::AddAttributes(const wxRichTextAttr& attr, bool isPara) +{ + wxString str; + if (attr.HasTextColour() && attr.GetTextColour().IsOk()) + AddAttribute(str, wxT("textcolor"), attr.GetTextColour()); + + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk()) + AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour()); + + if (attr.HasFontSize()) + AddAttribute(str, wxT("fontsize"), attr.GetFontSize()); + + if (attr.HasFontFamily()) + AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily()); + + if (attr.HasFontItalic()) + AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle()); + + if (attr.HasFontWeight()) + AddAttribute(str, wxT("fontweight"), attr.GetFontWeight()); + + if (attr.HasFontUnderlined()) + AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined()); + + if (attr.HasFontFaceName()) + AddAttribute(str, wxT("fontface"), AttributeToXML(attr.GetFontFaceName())); + + if (attr.HasTextEffects()) + { + AddAttribute(str, wxT("texteffects"), attr.GetTextEffects()); + AddAttribute(str, wxT("texteffectflags"), attr.GetTextEffectFlags()); + } + + if (!attr.GetCharacterStyleName().empty()) + AddAttribute(str, wxT("characterstyle"), AttributeToXML(attr.GetCharacterStyleName())); + + if (attr.HasURL()) + AddAttribute(str, wxT("url"), AttributeToXML(attr.GetURL())); + + if (isPara) + { + if (attr.HasAlignment()) + AddAttribute(str, wxT("alignment"), (int) attr.GetAlignment()); + + if (attr.HasLeftIndent()) + { + AddAttribute(str, wxT("leftindent"), (int) attr.GetLeftIndent()); + AddAttribute(str, wxT("leftsubindent"), (int) attr.GetLeftSubIndent()); + } + + if (attr.HasRightIndent()) + AddAttribute(str, wxT("rightindent"), (int) attr.GetRightIndent()); + + if (attr.HasParagraphSpacingAfter()) + AddAttribute(str, wxT("parspacingafter"), (int) attr.GetParagraphSpacingAfter()); + + if (attr.HasParagraphSpacingBefore()) + AddAttribute(str, wxT("parspacingbefore"), (int) attr.GetParagraphSpacingBefore()); + + if (attr.HasLineSpacing()) + AddAttribute(str, wxT("linespacing"), (int) attr.GetLineSpacing()); + + if (attr.HasBulletStyle()) + AddAttribute(str, wxT("bulletstyle"), (int) attr.GetBulletStyle()); + + if (attr.HasBulletNumber()) + AddAttribute(str, wxT("bulletnumber"), (int) attr.GetBulletNumber()); + + if (attr.HasBulletText()) + { + // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character. + // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1 + if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) + AddAttribute(str, wxT("bulletsymbol"), (int) (attr.GetBulletText()[0])); + else + AddAttribute(str, wxT("bullettext"), AttributeToXML(attr.GetBulletText())); + + AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont()); + } + + if (attr.HasBulletName()) + AddAttribute(str, wxT("bulletname"), AttributeToXML(attr.GetBulletName())); + + if (!attr.GetParagraphStyleName().empty()) + AddAttribute(str, wxT("parstyle"), AttributeToXML(attr.GetParagraphStyleName())); + + if (!attr.GetListStyleName().empty()) + AddAttribute(str, wxT("liststyle"), AttributeToXML(attr.GetListStyleName())); + + if (attr.HasTabs()) + { + wxString strTabs; + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) strTabs << wxT(","); + strTabs << attr.GetTabs()[i]; + } + AddAttribute(str, wxT("tabs"), strTabs); + } + + if (attr.HasPageBreak()) + { + AddAttribute(str, wxT("pagebreak"), 1); + } + + if (attr.HasOutlineLevel()) + AddAttribute(str, wxT("outlinelevel"), (int) attr.GetOutlineLevel()); + } + + AddAttribute(str, wxT("margin"), attr.GetTextBoxAttr().GetMargins()); + AddAttribute(str, wxT("padding"), attr.GetTextBoxAttr().GetPadding()); + AddAttribute(str, wxT("position"), attr.GetTextBoxAttr().GetPosition()); + AddAttribute(str, wxT("border"), attr.GetTextBoxAttr().GetBorder()); + AddAttribute(str, wxT("outline"), attr.GetTextBoxAttr().GetOutline()); + AddAttribute(str, wxT("width"), attr.GetTextBoxAttr().GetWidth()); + AddAttribute(str, wxT("height"), attr.GetTextBoxAttr().GetHeight()); + + if (attr.GetTextBoxAttr().HasVerticalAlignment()) + { + wxString value; + if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP) + value = wxT("top"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE) + value = wxT("centre"); + else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM) + value = wxT("bottom"); + else + value = wxT("none"); + AddAttribute(str, wxT("verticalalignment"), value); + } + + if (attr.GetTextBoxAttr().HasFloatMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT) + value = wxT("right"); + else + value = wxT("none"); + AddAttribute(str, wxT("float"), value); + } + + if (attr.GetTextBoxAttr().HasClearMode()) + { + wxString value; + if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT) + value = wxT("left"); + else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT) + value = wxT("right"); + else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH) + value = wxT("both"); + else + value = wxT("none"); + AddAttribute(str, wxT("clear"), value); + } + + if (attr.GetTextBoxAttr().HasCollapseBorders()) + AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders()); + + return str; +} + +// Make a string from the given property. This can be overridden for custom variants. +wxString wxRichTextXMLHandler::MakeStringFromProperty(const wxVariant& var) +{ + return var.MakeString(); +} + +// Create a proprty from the string read from the XML file. +wxVariant wxRichTextXMLHandler::MakePropertyFromString(const wxString& name, const wxString& value, const wxString& WXUNUSED(type)) +{ + wxVariant var(value, name); + // TODO: use type to create using common types + return var; +} + +// Write the properties +bool wxRichTextXMLHandler::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level) +{ + if (properties.GetCount() > 0) + { + level ++; + + OutputIndentation(stream, level); + OutputString(stream, wxT("\n")); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT("\n")); + + level --; + } + + return true; +} + + +#endif + // wxRICHTEXT_HAVE_DIRECT_OUTPUT + +#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT +bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj) +{ + obj.ExportXML(parent, this); + + return true; +} + +bool wxRichTextXMLHandler::ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def) +{ + wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); + wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); + wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition); + wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition); + + wxString baseStyle = def->GetBaseStyle(); + wxString descr = def->GetDescription(); + + wxXmlNode* defNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxEmptyString); + parent->AddChild(defNode); + if (!baseStyle.empty()) + defNode->AddAttribute(wxT("basestyle"), baseStyle); + if (!descr.empty()) + defNode->AddAttribute(wxT("description"), descr); + + wxXmlNode* styleNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style")); + defNode->AddChild(styleNode); + + if (charDef) + { + defNode->SetName(wxT("characterstyle")); + AddAttributes(styleNode, def->GetStyle(), false); + } + else if (listDef) + { + defNode->SetName(wxT("liststyle")); + + if (!listDef->GetNextStyle().empty()) + defNode->AddAttribute(wxT("nextstyle"), listDef->GetNextStyle()); + + AddAttributes(styleNode, def->GetStyle(), true); + + int i; + for (i = 0; i < 10; i ++) + { + wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); + if (levelAttr) + { + wxXmlNode* levelNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style")); + defNode->AddChild(levelNode); + levelNode->AddAttribute(wxT("level"), MakeString(i+1)); + AddAttributes(levelNode, * levelAttr, true); + } + } + } + else if (boxDef) + { + defNode->SetName(wxT("boxstyle")); + + AddAttributes(styleNode, def->GetStyle(), true); + } + else if (paraDef) + { + defNode->SetName(wxT("paragraphstyle")); + + if (!paraDef->GetNextStyle().empty()) + defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle()); + + AddAttributes(styleNode, def->GetStyle(), true); + } + + return true; +} + +bool wxRichTextXMLHandler::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara) +{ + if (attr.HasTextColour() && attr.GetTextColour().IsOk()) + node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour())); + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk()) + node->AddAttribute(wxT("bgcolor"), MakeString(attr.GetBackgroundColour())); + + if (attr.HasFontSize()) + node->AddAttribute(wxT("fontsize"), MakeString(attr.GetFontSize())); + if (attr.HasFontFamily()) + node->AddAttribute(wxT("fontfamily"), MakeString(attr.GetFontFamily())); + if (attr.HasFontItalic()) + node->AddAttribute(wxT("fontstyle"), MakeString(attr.GetFontStyle())); + if (attr.HasFontWeight()) + node->AddAttribute(wxT("fontweight"), MakeString(attr.GetFontWeight())); + if (attr.HasFontUnderlined()) + node->AddAttribute(wxT("fontunderlined"), MakeString((int) attr.GetFontUnderlined())); + if (attr.HasFontFaceName()) + node->AddAttribute(wxT("fontface"), attr.GetFontFaceName()); + + if (attr.HasTextEffects()) + { + node->AddAttribute(wxT("texteffects"), MakeString(attr.GetTextEffects())); + node->AddAttribute(wxT("texteffectflags"), MakeString(attr.GetTextEffectFlags())); + } + if (attr.HasCharacterStyleName() && !attr.GetCharacterStyleName().empty()) + node->AddAttribute(wxT("characterstyle"), attr.GetCharacterStyleName()); + + if (attr.HasURL()) + node->AddAttribute(wxT("url"), attr.GetURL()); // TODO: do we need to wrap this in AttributeToXML? + + if (isPara) + { + if (attr.HasAlignment()) + node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment())); + + if (attr.HasLeftIndent()) + { + node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent())); + node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent())); + } + + if (attr.HasRightIndent()) + node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent())); + + if (attr.HasParagraphSpacingAfter()) + node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter())); + + if (attr.HasParagraphSpacingBefore()) + node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore())); + + if (attr.HasLineSpacing()) + node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing())); + + if (attr.HasBulletStyle()) + node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle())); + + if (attr.HasBulletNumber()) + node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber())); + + if (attr.HasBulletText()) + { + // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character. + // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1 + if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)) + node->AddAttribute(wxT("bulletsymbol"), MakeString((int) (attr.GetBulletText()[0]))); + else + node->AddAttribute(wxT("bullettext"), attr.GetBulletText()); + + if (!attr.GetBulletFont().empty()) + node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont()); + } + + if (attr.HasBulletName()) + node->AddAttribute(wxT("bulletname"), attr.GetBulletName()); + + if (!attr.GetParagraphStyleName().empty()) + node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName()); + + if (!attr.GetListStyleName().empty()) + node->AddAttribute(wxT("liststyle"), attr.GetListStyleName()); + + if (attr.HasTabs()) + { + wxString tabs; + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) + tabs << wxT(","); + tabs << attr.GetTabs()[i]; + } + node->AddAttribute(wxT("tabs"), tabs); + } + + if (attr.HasPageBreak()) + node->AddAttribute(wxT("pagebreak"), wxT("1")); + + if (attr.HasOutlineLevel()) + node->AddAttribute(wxT("outlinelevel"), MakeString((int) attr.GetOutlineLevel())); + } + + AddAttribute(node, wxT("margin"), attr.GetTextBoxAttr().GetMargins()); + AddAttribute(node, wxT("padding"), attr.GetTextBoxAttr().GetPadding()); + AddAttribute(node, wxT("position"), attr.GetTextBoxAttr().GetPosition()); + AddAttribute(node, wxT("border"), attr.GetTextBoxAttr().GetBorder()); + AddAttribute(node, wxT("outline"), attr.GetTextBoxAttr().GetOutline()); + AddAttribute(node, wxT("width"), attr.GetTextBoxAttr().GetWidth()); + AddAttribute(node, wxT("height"), attr.GetTextBoxAttr().GetHeight()); + + 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("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; - wxString style = CreateStyle(obj.GetAttributes(), isPara); - - stream << style << wxT(">"); - - wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj; - size_t i; - for (i = 0; i < composite.GetChildCount(); i++) + if (!found) { - wxRichTextObject* child = composite.GetChild(i); - ExportXML(stream, convMem, convFile, *child, indent+1); + // 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("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(); } - if (objectName != wxT("text")) - OutputIndentation(stream, indent); + return true; +} + +#endif + // wxUSE_STREAMS + +// Import this object from XML +bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) +{ + handler->ImportProperties(this, node); + handler->ImportStyle(GetAttributes(), node, UsesParagraphAttributes()); - stream << wxT(""); + *recurse = true; return true; } -/// Create style parameters -wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara) +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextObject::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) { - wxString str; - if (attr.GetTextColour().Ok()) + ::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) { - str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\""); + handler->WriteProperties(stream, GetProperties(), indent); } - if (attr.GetBackgroundColour().Ok()) + + wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject); + if (composite) { - str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); + size_t i; + for (i = 0; i < composite->GetChildCount(); i++) + { + wxRichTextObject* child = composite->GetChild(i); + child->ExportXML(stream, indent+1, handler); + } } - if (attr.GetFont().Ok()) + ::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) { - str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); - str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); - str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); - str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); - str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); - str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); + size_t i; + for (i = 0; i < composite->GetChildCount(); i++) + { + wxRichTextObject* child = composite->GetChild(i); + child->ExportXML(elementNode, handler); + } } + return true; +} +#endif - if (!attr.GetCharacterStyleName().IsEmpty()) - str << wxT(" charactertyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\""); - if (isPara) +// 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")) { - str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); - str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); - str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); - str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); - str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); - str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); - str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); - str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); - str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); - str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\""); + // 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(); + } - if (!attr.GetParagraphStyleName().IsEmpty()) - str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + wxString actualText; + actualText << (wxChar) wxAtoi(text); + SetText(actualText); } + else + return false; - return str; + return true; } -/// Get style parameters -bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara) +#if wxRICHTEXT_HAVE_DIRECT_OUTPUT +// Export this object directly to the given stream. +bool wxRichTextPlainText::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler) { - wxString fontFacename; - int fontSize = 12; - int fontFamily = wxDEFAULT; - int fontWeight = wxNORMAL; - int fontStyle = wxNORMAL; - bool fontUnderlined = false; + 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()); - fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); + ::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()); - wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); - if (!value.IsEmpty()) - fontFamily = wxAtoi(value); + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + } - value = node->GetPropVal(wxT("fontstyle"), wxEmptyString); - if (!value.IsEmpty()) - fontStyle = wxAtoi(value); - value = node->GetPropVal(wxT("fontsize"), wxEmptyString); - if (!value.IsEmpty()) - fontSize = wxAtoi(value); + // 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()); - value = node->GetPropVal(wxT("fontweight"), wxEmptyString); - if (!value.IsEmpty()) - fontWeight = wxAtoi(value); + ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile()); + ::OutputString(stream, wxString::Format(wxT("%d"), c), handler->GetConvMem(), handler->GetConvFile()); - value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString); - if (!value.IsEmpty()) - fontUnderlined = wxAtoi(value) != 0; + if (GetProperties().GetCount() > 0) + { + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + } + } - attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); + wxString fragment; + if (last == 0) + fragment = text; + else + fragment = text.Mid(last, i-last); - value = node->GetPropVal(wxT("textcolor"), wxEmptyString); - if (!value.IsEmpty()) + if (last < len) { - if (value[0] == wxT('#')) - attr.SetTextColour(HexStringToColour(value.Mid(1))); + ::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 - attr.SetTextColour(value); + ::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(); - value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString); - if (!value.IsEmpty()) + if (len == 0) { - if (value[0] == wxT('#')) - attr.SetBackgroundColour(HexStringToColour(value.Mid(1))); - else - attr.SetBackgroundColour(value); - } + i = 0; - value = node->GetPropVal(wxT("characterstyle"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetCharacterStyleName(value); + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text")); + parent->AddChild(elementNode); - // Set paragraph attributes - if (isPara) + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); + } + else for (i = 0; i < len; i++) { - value = node->GetPropVal(wxT("alignment"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); +#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()); - int leftSubIndent = 0; - int leftIndent = 0; - value = node->GetPropVal(wxT("leftindent"), wxEmptyString); - if (!value.IsEmpty()) - leftIndent = wxAtoi(value); - value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString); - if (!value.IsEmpty()) - leftSubIndent = wxAtoi(value); - attr.SetLeftIndent(leftIndent, leftSubIndent); + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + elementNode->AddChild(textNode); - value = node->GetPropVal(wxT("rightindent"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetRightIndent(wxAtoi(value)); + if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')) + fragment = wxT("\"") + fragment + wxT("\""); - value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetParagraphSpacingBefore(wxAtoi(value)); + textNode->SetContent(fragment); + } + } - value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetParagraphSpacingAfter(wxAtoi(value)); - value = node->GetPropVal(wxT("linespacing"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetLineSpacing(wxAtoi(value)); + // Output this character as a number in a separate tag, because XML can't cope + // with entities below 32 except for 10 and 13 - value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetBulletStyle(wxAtoi(value)); + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("symbol")); + parent->AddChild(elementNode); - value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetBulletNumber(wxAtoi(value)); + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); - value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetBulletSymbol(value[0]); + wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text")); + elementNode->AddChild(textNode); + textNode->SetContent(wxString::Format(wxT("%d"), c)); - value = node->GetPropVal(wxT("parstyle"), wxEmptyString); - if (!value.IsEmpty()) - attr.SetParagraphStyleName(value); + 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 -IMPLEMENT_DYNAMIC_CLASS(wxRichTextHTMLHandler, wxRichTextFileHandler) -/// Can we handle this filename (if using files)? By default, checks the extension. -bool wxRichTextHTMLHandler::CanHandle(const wxString& filename) const +// Import this object from XML +bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) { - wxString path, file, ext; - wxSplitPath(filename, & path, & file, & ext); + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); - return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm")); -} + 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; -#if wxUSE_STREAMS -bool wxRichTextHTMLHandler::LoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream)) -{ - return false; -} + 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(); + } -/* - * We need to output only _changes_ in character formatting. - */ + } + imageChild = imageChild->GetNext(); + } -bool wxRichTextHTMLHandler::SaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream) -{ - buffer->Defragment(); + if (!data.empty()) + { + wxStringInputStream strStream(data); - wxTextOutputStream str(stream); + GetImageBlock().ReadHex(strStream, data.length(), imageType); - wxTextAttrEx currentParaStyle = buffer->GetAttributes(); - wxTextAttrEx currentCharStyle = buffer->GetAttributes(); + return true; + } + else + return false; +} - str << wxT("\n"); +#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); - wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst(); - while (node) + ::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) { - wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph); - wxASSERT (para != NULL); + handler->WriteProperties(stream, GetProperties(), indent); + ::OutputIndentation(stream, indent); + } - if (para) - { - OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, true); + ::OutputIndentation(stream, indent+1); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); - wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst(); - while (node2) - { - wxRichTextObject* obj = node2->GetData(); - wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText); - if (textObj && !textObj->IsEmpty()) - { - OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, true); + // wxStopWatch stopwatch; - str << textObj->GetText(); + GetImageBlock().WriteHex(stream); - OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, false); - } + // 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); - node2 = node2->GetNext(); - } + if (GetImageBlock().IsOk()) + elementNode->AddAttribute(wxT("imagetype"), MakeString((int) GetImageBlock().GetImageType())); - OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, false); + handler->AddAttributes(elementNode, GetAttributes(), false); + handler->WriteProperties(elementNode, GetProperties()); - str << wxT("

\n"); + 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; } - node = node->GetNext(); } +#else + { + wxStringOutputStream strStream(& strData); + GetImageBlock().WriteHex(strStream); + } +#endif - str << wxT("\n"); + textNode->SetContent(strData); + textNode->SetNoConversion(true); // optimize speed return true; } +#endif + -/// Output character formatting -void wxRichTextHTMLHandler::OutputCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start) +// Import this object from XML +bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse) { - wxTextOutputStream str(stream); + wxRichTextObject::ImportFromXML(buffer, node, handler, recurse); - bool isBold = false; - bool isItalic = false; - bool isUnderline = false; - wxString faceName; + *recurse = true; - if (thisStyle.GetFont().Ok()) + 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)) { - if (thisStyle.GetFont().GetWeight() == wxBOLD) - isBold = true; - if (thisStyle.GetFont().GetStyle() == wxITALIC) - isItalic = true; - if (thisStyle.GetFont().GetUnderlined()) - isUnderline = true; + 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(); + } - faceName = thisStyle.GetFont().GetFaceName(); + // 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); } - if (start) + 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) { - if (isBold) - str << wxT(""); - if (isItalic) - str << wxT(""); - if (isUnderline) - str << wxT(""); + handler->WriteProperties(stream, GetProperties(), indent); } - else + + size_t i; + for (i = 0; i < GetChildCount(); i++) { - if (isUnderline) - str << wxT(""); - if (isItalic) - str << wxT(""); - if (isBold) - str << wxT(""); + wxRichTextObject* child = GetChild(i); + child->ExportXML(stream, indent+1, handler); } + + ::OutputIndentation(stream, indent); + ::OutputString(stream, wxT(""), handler->GetConvMem(), handler->GetConvFile()); + return true; } +#endif -/// Output paragraph formatting -void wxRichTextHTMLHandler::OutputParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start) +#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) { - // TODO: lists, indentation (using tables), fonts, right-align, ... + wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName()); + parent->AddChild(elementNode); + handler->AddAttributes(elementNode, GetAttributes(), true); + handler->WriteProperties(elementNode, GetProperties()); - wxTextOutputStream str(stream); - bool isCentered = false; + if (GetPartialParagraph()) + elementNode->AddAttribute(wxT("partialparagraph"), wxT("true")); - if (thisStyle.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) + size_t i; + for (i = 0; i < GetChildCount(); i++) { - isCentered = true; + wxRichTextObject* child = GetChild(i); + child->ExportXML(elementNode, handler); } - if (start) + 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) { - if (isCentered) - str << wxT("

"); + wxRichTextObject* childObj = handler->CreateObjectForXMLName(this, child->GetName()); + if (childObj) + { + AppendChild(childObj); + handler->ImportXML(buffer, childObj, child); + } + child = child->GetNext(); } - else + + 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) { - if (isCentered) - str << wxT("
"); + 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_RICHTEXT && wxUSE_XML