X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1e967276d8bce0ea59924f71ed00962ce19a742e..cf4a8b2698fb853ac4b638d08d95cc8e5794d1aa:/src/richtext/richtextxml.cpp diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index f8d267d2e4..c5759c78cc 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: richtext/richtextxml.cpp +// Name: src/richtext/richtextxml.cpp // Purpose: XML and HTML I/O for wxRichTextCtrl // Author: Julian Smart // Modified by: @@ -13,27 +13,26 @@ #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" +#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/tokenzr.h" #include "wx/xml/xml.h" -#include "wx/richtext/richtextxml.h" - IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler) #if wxUSE_STREAMS @@ -42,13 +41,22 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s 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 @@ -93,6 +101,9 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) if (name == wxT("paragraphlayout")) { + wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString); + if (partial == wxT("true")) + buffer->SetPartialParagraph(true); } else if (name == wxT("paragraph")) { @@ -117,19 +128,13 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* 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); + if (!text2.empty() && text2[text2.length()-1] == wxT('\n')) + text2 = text2.Mid(0, text2.length()-1); - if (text2.Length() > 0 && text2[0] == wxT('"')) + if (!text2.empty() && text2[0] == wxT('"')) text2 = text2.Mid(1); - if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('"')) - text2 = text2.Mid(0, text2.Length() - 1); - - // TODO: further entity translation - text2.Replace(wxT("<"), wxT("<")); - text2.Replace(wxT(">"), wxT(">")); - text2.Replace(wxT("&"), wxT("&")); - text2.Replace(wxT("""), wxT("\"")); + if (!text2.empty() && text2[text2.length()-1] == wxT('"')) + text2 = text2.Mid(0, text2.length() - 1); text += text2; } @@ -141,10 +146,34 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) 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")) { int imageType = wxBITMAP_TYPE_PNG; - wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString); + wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString); if (!value.empty()) imageType = wxAtoi(value); @@ -175,7 +204,7 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) wxStringInputStream strStream(data); - imageObj->GetImageBlock().ReadHex(strStream, data.Length(), imageType); + imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType); } } child = child->GetNext(); @@ -183,6 +212,31 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) doneChildren = true; } + else if (name == wxT("stylesheet")) + { + if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET) + { + wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet; + wxString sheetName = node->GetAttribute(wxT("name"), wxEmptyString); + wxString sheetDescription = node->GetAttribute(wxT("description"), wxEmptyString); + sheet->SetName(sheetName); + sheet->SetDescription(sheetDescription); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + ImportStyleDefinition(sheet, child); + + child = child->GetNext(); + } + + // Notify that styles have changed. If this is vetoed by the app, + // the new sheet will be deleted. If it is not vetoed, the + // old sheet will be deleted and replaced with the new one. + buffer->SetStyleSheetAndNotify(sheet); + } + doneChildren = true; + } if (!doneChildren) { @@ -197,6 +251,94 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) return true; } +bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node) +{ + wxString styleType = node->GetName(); + wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString); + wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString); + + if (styleName.IsEmpty()) + return false; + + if (styleType == wxT("characterstyle")) + { + wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName); + def->SetBaseStyle(baseStyleName); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxTextAttrEx attr; + GetStyle(attr, child, false); + def->SetStyle(attr); + } + child = child->GetNext(); + } + + sheet->AddCharacterStyle(def); + } + else if (styleType == wxT("paragraphstyle")) + { + wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName); + + wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString); + def->SetNextStyle(nextStyleName); + def->SetBaseStyle(baseStyleName); + + wxXmlNode* child = node->GetChildren(); + while (child) + { + if (child->GetName() == wxT("style")) + { + wxTextAttrEx attr; + GetStyle(attr, child, false); + def->SetStyle(attr); + } + child = child->GetNext(); + } + + sheet->AddParagraphStyle(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) + { + if (child->GetName() == wxT("style")) + { + wxTextAttrEx attr; + GetStyle(attr, child, false); + + wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString); + if (styleLevel.IsEmpty()) + { + 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 @@ -272,8 +414,16 @@ inline static void OutputString(wxOutputStream& stream, const wxString& str, { 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()); @@ -299,8 +449,13 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str, for (i = 0; i < len; i++) { c = str.GetChar(i); + + // Original code excluded "&" but we _do_ want to convert + // the ampersand beginning & because otherwise when read in, + // the original "&" becomes "&". + 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) @@ -321,6 +476,16 @@ 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("&#")); + s << (int) c; + s << wxT(";"); + OutputString(stream, s, NULL, NULL); + last = i + 1; + } } OutputString(stream, str.Mid(last, i - last), convMem, convFile); } @@ -333,26 +498,6 @@ inline static void OutputIndentation(wxOutputStream& stream, int indent) OutputString(stream, str, NULL, NULL); } -static wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s) -{ - stream.Write(s, s.Length()); - return stream; -} - -static wxOutputStream& operator <<(wxOutputStream& stream, long l) -{ - wxString str; - str.Printf(wxT("%ld"), l); - return stream << str; -} - -static wxOutputStream& operator <<(wxOutputStream& stream, const char c) -{ - wxString str; - str.Printf(wxT("%c"), c); - return stream << str; -} - // Convert a colour to a 6-digit hex string static wxString ColourToHexString(const wxColour& col) { @@ -366,7 +511,7 @@ static wxString ColourToHexString(const wxColour& col) } // Convert 6-digit hex string to a colour -wxColour HexStringToColour(const wxString& hex) +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)); @@ -381,41 +526,103 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& return false; wxString version(wxT("1.0") ) ; + + bool deleteConvFile = false; + wxString fileEncoding; + wxMBConv* convFile = NULL; + #if wxUSE_UNICODE - wxString fileencoding(wxT("UTF-8")) ; - wxString memencoding(wxT("UTF-8")) ; + fileEncoding = wxT("UTF-8"); + convFile = & wxConvUTF8; #else - wxString fileencoding(wxT("ISO-8859-1")) ; - wxString memencoding(wxT("ISO-8859-1")) ; + fileEncoding = wxT("ISO-8859-1"); + convFile = & wxConvISO8859_1; #endif - wxString s ; - wxMBConv *convMem = NULL, *convFile = NULL; + // 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 - convFile = new wxCSConv(fileencoding); + fileEncoding = wxT("UTF-8"); #else - if ( fileencoding != memencoding ) - { - convFile = new wxCSConv(fileencoding); - convMem = new wxCSConv(memencoding); + fileEncoding = wxT("ISO-8859-1"); +#endif + convFile = new wxCSConv(fileEncoding); + deleteConvFile = true; } + +#if !wxUSE_UNICODE + wxMBConv* convMem = wxConvCurrent; +#else + wxMBConv* convMem = NULL; #endif + wxString s ; s.Printf(wxT("\n"), - (const wxChar*) version, (const wxChar*) fileencoding ); + version, fileEncoding); OutputString(stream, s, NULL, NULL); OutputString(stream, wxT("") , NULL, NULL); int level = 1; - ExportXML(stream, convMem, convFile, *buffer, level); + + if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)) + { + OutputIndentation(stream, level); + 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; + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i); + ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i); + ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + } + + for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++) + { + wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i); + ExportStyleDefinition(stream, convMem, convFile, def, level + 1); + } + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + } + + + bool success = ExportXML(stream, convMem, convFile, *buffer, level); OutputString(stream, wxT("\n") , NULL, NULL); OutputString(stream, wxT("\n"), NULL, NULL); - delete convFile; - delete convMem; + if (deleteConvFile) + delete convFile; - return true; + return success; } /// Recursively export an object @@ -433,26 +640,81 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, else objectName = wxT("object"); + bool terminateTag = true; + if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText))) { - wxRichTextPlainText& text = (wxRichTextPlainText&) obj; - - OutputIndentation(stream, indent); - stream << wxT("<") << objectName; + wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj; wxString style = CreateStyle(obj.GetAttributes(), false); - stream << style << 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(" 0 && (str[0] == wxT(' ') || str[str.Length()-1] == wxT(' '))) + OutputString(stream, style + wxT(">"), convMem, convFile); + OutputString(stream, wxString::Format(wxT("%d"), c), convMem, convFile); + + OutputString(stream, wxT(""), convMem, convFile); + } + } + + wxString fragment; + if (last == 0) + fragment = text; + else + fragment = text.Mid(last, i-last); + + if (last < len) { - stream << wxT("\""); - OutputStringEnt(stream, str, convMem, convFile); - stream << wxT("\""); + OutputIndentation(stream, indent); + OutputString(stream, wxT("<") + objectName, convMem, convFile); + + OutputString(stream, style + wxT(">"), convMem, convFile); + + if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))) + { + OutputString(stream, wxT("\""), convMem, convFile); + OutputStringEnt(stream, fragment, convMem, convFile); + OutputString(stream, wxT("\""), convMem, convFile); + } + else + OutputStringEnt(stream, fragment, convMem, convFile); } else - OutputStringEnt(stream, str, convMem, convFile); + terminateTag = false; } else if (obj.IsKindOf(CLASSINFO(wxRichTextImage))) { @@ -462,28 +724,28 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, imageObj.MakeBlock(); OutputIndentation(stream, indent); - stream << wxT("<") << objectName; + OutputString(stream, wxT("<") + objectName, convMem, convFile); if (!imageObj.GetImageBlock().Ok()) { // No data - stream << wxT(">"); + OutputString(stream, wxT(">"), convMem, convFile); } else { - stream << wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) << wxT(">"); + OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj.GetImageBlock().GetImageType())); } OutputIndentation(stream, indent+1); - stream << wxT(""); + OutputString(stream, wxT(""), convMem, convFile); imageObj.GetImageBlock().WriteHex(stream); - stream << wxT(""); + OutputString(stream, wxT(""), convMem, convFile); } else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject))) { OutputIndentation(stream, indent); - stream << wxT("<") << objectName; + OutputString(stream, wxT("<") + objectName, convMem, convFile); bool isPara = false; if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout")) @@ -491,7 +753,10 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxString style = CreateStyle(obj.GetAttributes(), isPara); - stream << style << wxT(">"); + if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph()) + style << wxT(" partialparagraph=\"true\""); + + OutputString(stream, style + wxT(">"), convMem, convFile); wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj; size_t i; @@ -505,7 +770,113 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, if (objectName != wxT("text")) OutputIndentation(stream, indent); - stream << wxT(""); + if (terminateTag) + OutputString(stream, wxT(""), convMem, convFile); + + return true; +} + +bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level) +{ + wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition); + wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition); + wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition); + + wxString baseStyle = def->GetBaseStyle(); + wxString baseStyleProp; + if (!baseStyle.IsEmpty()) + baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\""); + + wxString descr = def->GetDescription(); + wxString descrProp; + if (!descr.IsEmpty()) + descrProp = wxT(" description=\"") + descr + wxT("\""); + + if (charDef) + { + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + + level ++; + + wxString style = CreateStyle(def->GetStyle(), false); + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + } + else if (listDef) + { + OutputIndentation(stream, level); + + if (!listDef->GetNextStyle().IsEmpty()) + baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\""); + + OutputString(stream, wxT(""), convMem, convFile); + + level ++; + + wxString style = CreateStyle(def->GetStyle(), false); + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + + int i; + for (i = 0; i < 10; i ++) + { + wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i); + if (levelAttr) + { + wxString style = CreateStyle(def->GetStyle(), false); + wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1)); + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + } + } + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + } + else if (paraDef) + { + OutputIndentation(stream, level); + + if (!paraDef->GetNextStyle().IsEmpty()) + baseStyleProp << wxT(" basestyle=\"") << paraDef->GetNextStyle() << wxT("\""); + + OutputString(stream, wxT(""), convMem, convFile); + + level ++; + + wxString style = CreateStyle(def->GetStyle(), false); + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + + level --; + + OutputIndentation(stream, level); + OutputString(stream, wxT(""), convMem, convFile); + } return true; } @@ -514,43 +885,124 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara) { wxString str; - if (attr.GetTextColour().Ok()) + if (attr.HasTextColour() && attr.GetTextColour().Ok()) { str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\""); } - if (attr.GetBackgroundColour().Ok()) + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok()) { str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); } if (attr.GetFont().Ok()) { - 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("\""); + if (attr.HasFontSize()) + str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); + + //if (attr.HasFontFamily()) + // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); + + if (attr.HasFontItalic()) + str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); + + if (attr.HasFontWeight()) + str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); + + if (attr.HasFontUnderlined()) + str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); + + if (attr.HasFontFaceName()) + str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); + } + + if (attr.HasTextEffects()) + { + str << wxT(" texteffects=\""); + str << attr.GetTextEffects(); + str << wxT("\""); + + str << wxT(" texteffectflags=\""); + str << attr.GetTextEffectFlags(); + str << wxT("\""); } if (!attr.GetCharacterStyleName().empty()) - str << wxT(" charactertyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\""); + str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\""); if (isPara) { - 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("\""); + if (attr.HasAlignment()) + str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); + + if (attr.HasLeftIndent()) + { + str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); + str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); + } + + if (attr.HasRightIndent()) + str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); + + if (attr.HasParagraphSpacingAfter()) + str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); + + if (attr.HasParagraphSpacingBefore()) + str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); + + if (attr.HasLineSpacing()) + str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); + + if (attr.HasBulletStyle()) + str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); + + if (attr.HasBulletNumber()) + str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); + + 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("\""); + else + str << wxT(" bullettext=\"") << attr.GetBulletText() << wxT("\""); + + str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\""); + } + + if (attr.HasBulletName()) + str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\""); + + if (attr.HasURL()) + str << wxT(" url=\"") << attr.GetURL() << wxT("\""); if (!attr.GetParagraphStyleName().empty()) str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + + if (!attr.GetListStyleName().empty()) + str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\""); + + if (attr.HasTabs()) + { + str << wxT(" tabs=\""); + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) + str << wxT(","); + str << attr.GetTabs()[i]; + } + str << wxT("\""); + } + + if (attr.HasPageBreak()) + { + str << wxT(" pagebreak=\"1\""); + } + + if (attr.HasOutlineLevel()) + str << wxT(" outlinelevel=\"") << (int) attr.GetOutlineLevel() << wxT("\""); + } return str; @@ -566,31 +1018,54 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is int fontStyle = wxNORMAL; bool fontUnderlined = false; - fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); + int fontFlags = 0; - wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); - if (!value.empty()) - fontFamily = wxAtoi(value); + fontFacename = node->GetAttribute(wxT("fontface"), wxEmptyString); + if (!fontFacename.IsEmpty()) + fontFlags |= wxTEXT_ATTR_FONT_FACE; - value = node->GetPropVal(wxT("fontstyle"), wxEmptyString); + wxString value; + //value = node->GetAttribute(wxT("fontfamily"), wxEmptyString); + //if (!value.empty()) + // fontFamily = wxAtoi(value); + + value = node->GetAttribute(wxT("fontstyle"), wxEmptyString); if (!value.empty()) + { fontStyle = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_ITALIC; + } - value = node->GetPropVal(wxT("fontsize"), wxEmptyString); + value = node->GetAttribute(wxT("fontsize"), wxEmptyString); if (!value.empty()) + { fontSize = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_SIZE; + } - value = node->GetPropVal(wxT("fontweight"), wxEmptyString); + value = node->GetAttribute(wxT("fontweight"), wxEmptyString); if (!value.empty()) + { fontWeight = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_WEIGHT; + } - value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString); + value = node->GetAttribute(wxT("fontunderlined"), wxEmptyString); if (!value.empty()) + { fontUnderlined = wxAtoi(value) != 0; + fontFlags |= wxTEXT_ATTR_FONT_UNDERLINE; + } + + attr.SetFlags(fontFlags); - attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); + if (attr.HasFlag(wxTEXT_ATTR_FONT)) + attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); - value = node->GetPropVal(wxT("textcolor"), wxEmptyString); + // Restore correct font flags + attr.SetFlags(fontFlags); + + value = node->GetAttribute(wxT("textcolor"), wxEmptyString); if (!value.empty()) { if (value[0] == wxT('#')) @@ -599,7 +1074,7 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is attr.SetTextColour(value); } - value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString); + value = node->GetAttribute(wxT("backgroundcolor"), wxEmptyString); if (!value.empty()) { if (value[0] == wxT('#')) @@ -608,206 +1083,139 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is attr.SetBackgroundColour(value); } - value = node->GetPropVal(wxT("characterstyle"), wxEmptyString); + value = node->GetAttribute(wxT("characterstyle"), wxEmptyString); if (!value.empty()) attr.SetCharacterStyleName(value); + value = node->GetAttribute(wxT("texteffects"), wxEmptyString); + if (!value.IsEmpty()) + { + attr.SetTextEffects(wxAtoi(value)); + } + + value = node->GetAttribute(wxT("texteffectflags"), wxEmptyString); + if (!value.IsEmpty()) + { + attr.SetTextEffectFlags(wxAtoi(value)); + } + // Set paragraph attributes if (isPara) { - value = node->GetPropVal(wxT("alignment"), wxEmptyString); + value = node->GetAttribute(wxT("alignment"), wxEmptyString); if (!value.empty()) attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value)); int leftSubIndent = 0; int leftIndent = 0; - value = node->GetPropVal(wxT("leftindent"), wxEmptyString); + bool hasLeftIndent = false; + + value = node->GetAttribute(wxT("leftindent"), wxEmptyString); if (!value.empty()) + { leftIndent = wxAtoi(value); - value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString); + hasLeftIndent = true; + } + + value = node->GetAttribute(wxT("leftsubindent"), wxEmptyString); if (!value.empty()) + { leftSubIndent = wxAtoi(value); - attr.SetLeftIndent(leftIndent, leftSubIndent); + hasLeftIndent = true; + } + + if (hasLeftIndent) + attr.SetLeftIndent(leftIndent, leftSubIndent); - value = node->GetPropVal(wxT("rightindent"), wxEmptyString); + value = node->GetAttribute(wxT("rightindent"), wxEmptyString); if (!value.empty()) attr.SetRightIndent(wxAtoi(value)); - value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString); + value = node->GetAttribute(wxT("parspacingbefore"), wxEmptyString); if (!value.empty()) attr.SetParagraphSpacingBefore(wxAtoi(value)); - value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString); + value = node->GetAttribute(wxT("parspacingafter"), wxEmptyString); if (!value.empty()) attr.SetParagraphSpacingAfter(wxAtoi(value)); - value = node->GetPropVal(wxT("linespacing"), wxEmptyString); + value = node->GetAttribute(wxT("linespacing"), wxEmptyString); if (!value.empty()) attr.SetLineSpacing(wxAtoi(value)); - value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString); + value = node->GetAttribute(wxT("bulletstyle"), wxEmptyString); if (!value.empty()) attr.SetBulletStyle(wxAtoi(value)); - value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString); + value = node->GetAttribute(wxT("bulletnumber"), wxEmptyString); if (!value.empty()) attr.SetBulletNumber(wxAtoi(value)); - value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString); + value = node->GetAttribute(wxT("bulletsymbol"), wxEmptyString); if (!value.empty()) - attr.SetBulletSymbol(value[0]); + { + wxChar ch = wxAtoi(value); + wxString s; + s << ch; + attr.SetBulletText(s); + } - value = node->GetPropVal(wxT("parstyle"), wxEmptyString); + value = node->GetAttribute(wxT("bullettext"), wxEmptyString); if (!value.empty()) - attr.SetParagraphStyleName(value); - } + attr.SetBulletText(value); - 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 -{ - wxString path, file, ext; - wxSplitPath(filename, & path, & file, & ext); - - return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm")); -} - - -#if wxUSE_STREAMS -bool wxRichTextHTMLHandler::DoLoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream)) -{ - return false; -} - -/* - * We need to output only _changes_ in character formatting. - */ - -bool wxRichTextHTMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream) -{ - buffer->Defragment(); + value = node->GetAttribute(wxT("bulletfont"), wxEmptyString); + if (!value.empty()) + attr.SetBulletFont(value); - wxTextOutputStream str(stream); + value = node->GetAttribute(wxT("bulletname"), wxEmptyString); + if (!value.empty()) + attr.SetBulletName(value); - wxTextAttrEx currentParaStyle = buffer->GetAttributes(); - wxTextAttrEx currentCharStyle = buffer->GetAttributes(); + value = node->GetAttribute(wxT("url"), wxEmptyString); + if (!value.empty()) + attr.SetURL(value); - str << wxT("\n"); + value = node->GetAttribute(wxT("parstyle"), wxEmptyString); + if (!value.empty()) + attr.SetParagraphStyleName(value); - wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst(); - while (node) - { - wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph); - wxASSERT (para != NULL); + value = node->GetAttribute(wxT("liststyle"), wxEmptyString); + if (!value.empty()) + attr.SetListStyleName(value); - if (para) + value = node->GetAttribute(wxT("tabs"), wxEmptyString); + if (!value.empty()) { - OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, true); - - wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst(); - while (node2) + wxArrayInt tabs; + wxStringTokenizer tkz(value, wxT(",")); + while (tkz.HasMoreTokens()) { - wxRichTextObject* obj = node2->GetData(); - wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText); - if (textObj && !textObj->IsEmpty()) - { - OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, true); - - str << textObj->GetText(); - - OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, false); - } - - node2 = node2->GetNext(); + wxString token = tkz.GetNextToken(); + tabs.Add(wxAtoi(token)); } + attr.SetTabs(tabs); + } - OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, false); - - str << wxT("

\n"); + value = node->GetAttribute(wxT("pagebreak"), wxEmptyString); + if (!value.IsEmpty()) + { + attr.SetPageBreak(wxAtoi(value) != 0); } - node = node->GetNext(); + value = node->GetAttribute(wxT("outlinelevel"), wxEmptyString); + if (!value.IsEmpty()) + { + attr.SetOutlineLevel(wxAtoi(value) != 0); + } } - str << wxT("\n"); - return true; } -/// Output character formatting -void wxRichTextHTMLHandler::OutputCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start) -{ - wxTextOutputStream str(stream); - - bool isBold = false; - bool isItalic = false; - bool isUnderline = false; - wxString faceName; - - if (thisStyle.GetFont().Ok()) - { - if (thisStyle.GetFont().GetWeight() == wxBOLD) - isBold = true; - if (thisStyle.GetFont().GetStyle() == wxITALIC) - isItalic = true; - if (thisStyle.GetFont().GetUnderlined()) - isUnderline = true; - - faceName = thisStyle.GetFont().GetFaceName(); - } - - if (start) - { - if (isBold) - str << wxT(""); - if (isItalic) - str << wxT(""); - if (isUnderline) - str << wxT(""); - } - else - { - if (isUnderline) - str << wxT(""); - if (isItalic) - str << wxT(""); - if (isBold) - str << wxT(""); - } -} - -/// Output paragraph formatting -void wxRichTextHTMLHandler::OutputParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start) -{ - // TODO: lists, indentation (using tables), fonts, right-align, ... - - wxTextOutputStream str(stream); - bool isCentered = false; - - if (thisStyle.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE) - { - isCentered = true; - } - - if (start) - { - if (isCentered) - str << wxT("

"); - } - else - { - if (isCentered) - str << wxT("
"); - } -} - #endif + // wxUSE_STREAMS #endif - // wxUSE_RICHTEXT + // wxUSE_RICHTEXT && wxUSE_XML +