1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/richtext/richtextxml.cpp
3 // Purpose:     XML and HTML I/O for wxRichTextCtrl
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     2005-09-30
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 
15 #if wxUSE_RICHTEXT && wxUSE_XML
16 
17 #include "wx/richtext/richtextxml.h"
18 
19 #ifndef WX_PRECOMP
20     #include "wx/intl.h"
21     #include "wx/module.h"
22     #include "wx/log.h"
23 #endif
24 
25 #include "wx/filename.h"
26 #include "wx/clipbrd.h"
27 #include "wx/wfstream.h"
28 #include "wx/sstream.h"
29 #include "wx/txtstrm.h"
30 #include "wx/mstream.h"
31 #include "wx/tokenzr.h"
32 #include "wx/stopwatch.h"
33 #include "wx/xml/xml.h"
34 
35 // For use with earlier versions of wxWidgets
36 #ifndef WXUNUSED_IN_UNICODE
37 #if wxUSE_UNICODE
38 #define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
39 #else
40 #define WXUNUSED_IN_UNICODE(x) x
41 #endif
42 #endif
43 
44 // Set to 1 for slower wxXmlDocument method, 0 for faster direct method.
45 // If we make wxXmlDocument::Save more efficient, we might switch to this
46 // method.
47 #define wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT 0
48 
49 #if wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
50 #   error Must define wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT in richtextxml.h to use this method.
51 #endif
52 
53 #if !wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_DIRECT_OUTPUT
54 #   error Must define wxRICHTEXT_HAVE_DIRECT_OUTPUT in richtextxml.h to use this method.
55 #endif
56 
57 // Set to 1 to time file saving
58 #define wxRICHTEXT_USE_OUTPUT_TIMINGS 0
59 
60 wxIMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler);
61 
62 wxStringToStringHashMap wxRichTextXMLHandler::sm_nodeNameToClassMap;
63 
Init()64 void wxRichTextXMLHandler::Init()
65 {
66 }
67 
68 #if wxUSE_STREAMS
DoLoadFile(wxRichTextBuffer * buffer,wxInputStream & stream)69 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
70 {
71     if (!stream.IsOk())
72         return false;
73 
74     m_helper.SetFlags(GetFlags());
75 
76     buffer->ResetAndClearCommands();
77     buffer->Clear();
78 
79     wxXmlDocument* xmlDoc = new wxXmlDocument;
80     bool success = true;
81 
82     // This is the encoding to convert to (memory encoding rather than file encoding)
83     wxString encoding(wxT("UTF-8"));
84 
85 #if !wxUSE_UNICODE && wxUSE_INTL
86     encoding = wxLocale::GetSystemEncodingName();
87 #endif
88 
89     if (!xmlDoc->Load(stream, encoding))
90     {
91         buffer->ResetAndClearCommands();
92         success = false;
93     }
94     else
95     {
96         if (xmlDoc->GetRoot() && xmlDoc->GetRoot()->GetType() == wxXML_ELEMENT_NODE && xmlDoc->GetRoot()->GetName() == wxT("richtext"))
97         {
98             wxXmlNode* child = xmlDoc->GetRoot()->GetChildren();
99             while (child)
100             {
101                 if (child->GetType() == wxXML_ELEMENT_NODE)
102                 {
103                     wxString name = child->GetName();
104                     if (name == wxT("richtext-version"))
105                     {
106                     }
107                     else
108                         ImportXML(buffer, buffer, child);
109                 }
110 
111                 child = child->GetNext();
112             }
113         }
114         else
115         {
116             success = false;
117         }
118     }
119 
120     delete xmlDoc;
121 
122     buffer->UpdateRanges();
123 
124     return success;
125 }
126 
127 /// Creates an object given an XML element name
CreateObjectForXMLName(wxRichTextObject * WXUNUSED (parent),const wxString & name) const128 wxRichTextObject* wxRichTextXMLHandler::CreateObjectForXMLName(wxRichTextObject* WXUNUSED(parent), const wxString& name) const
129 {
130     // The standard node to class mappings are added in wxRichTextModule::OnInit in richtextbuffer.cpp
131     wxStringToStringHashMap::const_iterator it = sm_nodeNameToClassMap.find(name);
132     if (it == sm_nodeNameToClassMap.end())
133         return NULL;
134     else
135         return wxDynamicCast(wxCreateDynamicObject(it->second), wxRichTextObject);
136 }
137 
138 /// Recursively import an object
ImportXML(wxRichTextBuffer * buffer,wxRichTextObject * obj,wxXmlNode * node)139 bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node)
140 {
141     bool recurse = false;
142     obj->ImportFromXML(buffer, node, this, & recurse);
143 
144     // TODO: how to control whether to import children.
145 
146     wxRichTextCompositeObject* compositeParent = wxDynamicCast(obj, wxRichTextCompositeObject);
147     if (recurse && compositeParent)
148     {
149         wxXmlNode* child = node->GetChildren();
150         while (child)
151         {
152             if (child->GetName() != wxT("stylesheet"))
153             {
154                 wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName());
155                 if (childObj)
156                 {
157                     compositeParent->AppendChild(childObj);
158                     ImportXML(buffer, childObj, child);
159                 }
160             }
161             child = child->GetNext();
162         }
163     }
164 
165     return true;
166 }
167 
DoSaveFile(wxRichTextBuffer * buffer,wxOutputStream & stream)168 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
169 {
170     if (!stream.IsOk())
171         return false;
172 
173     m_helper.SetupForSaving(m_encoding);
174     m_helper.SetFlags(GetFlags());
175 
176     wxString version(wxT("1.0") ) ;
177 
178     wxString fileEncoding = m_helper.GetFileEncoding();
179 
180 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT
181 #if wxRICHTEXT_USE_OUTPUT_TIMINGS
182     wxStopWatch stopwatch;
183 #endif
184     wxXmlDocument* doc = new wxXmlDocument;
185     doc->SetFileEncoding(fileEncoding);
186 
187     wxXmlNode* rootNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("richtext"));
188     doc->SetRoot(rootNode);
189     rootNode->AddAttribute(wxT("version"), wxT("1.0.0.0"));
190     rootNode->AddAttribute(wxT("xmlns"), wxT("http://www.wxwidgets.org"));
191 
192     if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
193     {
194         wxXmlNode* styleSheetNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("stylesheet"));
195         rootNode->AddChild(styleSheetNode);
196 
197         wxString nameAndDescr;
198 
199         if (!buffer->GetStyleSheet()->GetName().empty())
200             styleSheetNode->AddAttribute(wxT("name"), buffer->GetStyleSheet()->GetName());
201 
202         if (!buffer->GetStyleSheet()->GetDescription().empty())
203             styleSheetNode->AddAttribute(wxT("description"), buffer->GetStyleSheet()->GetDescription());
204 
205         int i;
206         for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
207         {
208             wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
209             m_helper.ExportStyleDefinition(styleSheetNode, def);
210         }
211 
212         for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
213         {
214             wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
215             m_helper.ExportStyleDefinition(styleSheetNode, def);
216         }
217 
218         for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
219         {
220             wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
221             m_helper.ExportStyleDefinition(styleSheetNode, def);
222         }
223 
224         for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++)
225         {
226             wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i);
227             m_helper.ExportStyleDefinition(styleSheetNode, def);
228         }
229 
230         m_helper.WriteProperties(styleSheetNode, buffer->GetStyleSheet()->GetProperties());
231     }
232     bool success = ExportXML(rootNode, *buffer);
233 #if wxRICHTEXT_USE_OUTPUT_TIMINGS
234     long t = stopwatch.Time();
235     wxLogDebug(wxT("Creating the document took %ldms"), t);
236     wxMessageBox(wxString::Format(wxT("Creating the document took %ldms"), t));
237 #endif
238     if (success)
239     {
240 #if wxRICHTEXT_USE_OUTPUT_TIMINGS
241         wxStopWatch s2;
242 #endif
243         success = doc->Save(stream);
244 #if wxRICHTEXT_USE_OUTPUT_TIMINGS
245         long t2 = s2.Time();
246         wxLogDebug(wxT("Save() took %ldms"), t2);
247         wxMessageBox(wxString::Format(wxT("Save() took %ldms"), t2));
248 #endif
249     }
250     delete doc;
251     doc = NULL;
252 
253 #else
254     // !(wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT)
255 
256     wxString s ;
257     s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
258              version.c_str(), fileEncoding.c_str());
259     m_helper.OutputString(stream, s);
260     m_helper.OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">"));
261 
262     int level = 1;
263 
264     if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
265     {
266         m_helper.OutputIndentation(stream, level);
267         wxString nameAndDescr;
268         if (!buffer->GetStyleSheet()->GetName().empty())
269             nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\"");
270         if (!buffer->GetStyleSheet()->GetDescription().empty())
271             nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\"");
272         m_helper.OutputString(stream, wxString(wxT("<stylesheet")) + nameAndDescr + wxT(">"));
273 
274         int i;
275 
276         for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
277         {
278             wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
279             m_helper.ExportStyleDefinition(stream, def, level + 1);
280         }
281 
282         for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
283         {
284             wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
285             m_helper.ExportStyleDefinition(stream, def, level + 1);
286         }
287 
288         for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
289         {
290             wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
291             m_helper.ExportStyleDefinition(stream, def, level + 1);
292         }
293 
294         for (i = 0; i < (int) buffer->GetStyleSheet()->GetBoxStyleCount(); i++)
295         {
296             wxRichTextBoxStyleDefinition* def = buffer->GetStyleSheet()->GetBoxStyle(i);
297             m_helper.ExportStyleDefinition(stream, def, level + 1);
298         }
299 
300         m_helper.WriteProperties(stream, buffer->GetStyleSheet()->GetProperties(), level);
301 
302         m_helper.OutputIndentation(stream, level);
303         m_helper.OutputString(stream, wxT("</stylesheet>"));
304     }
305 
306 
307     bool success = ExportXML(stream, *buffer, level);
308 
309     m_helper.OutputString(stream, wxT("\n</richtext>"));
310     m_helper.OutputString(stream, wxT("\n"));
311 #endif
312 
313     return success;
314 }
315 
316 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
317 
318 /// Recursively export an object
ExportXML(wxOutputStream & stream,wxRichTextObject & obj,int indent)319 bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int indent)
320 {
321     obj.ExportXML(stream, indent, this);
322 
323     return true;
324 }
325 
326 #endif
327     // wxRICHTEXT_HAVE_DIRECT_OUTPUT
328 
329 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
ExportXML(wxXmlNode * parent,wxRichTextObject & obj)330 bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj)
331 {
332     obj.ExportXML(parent, this);
333 
334     return true;
335 }
336 
337 #endif
338     // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
339 
340 #endif
341     // wxUSE_STREAMS
342 
343 // Import this object from XML
ImportFromXML(wxRichTextBuffer * WXUNUSED (buffer),wxXmlNode * node,wxRichTextXMLHandler * handler,bool * recurse)344 bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
345 {
346     handler->GetHelper().ImportProperties(GetProperties(), node);
347     handler->GetHelper().ImportStyle(GetAttributes(), node, UsesParagraphAttributes());
348 
349     wxString value = node->GetAttribute(wxT("show"), wxEmptyString);
350     if (!value.IsEmpty())
351         Show(value == wxT("1"));
352 
353     *recurse = true;
354 
355     return true;
356 }
357 
358 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
359 // Export this object directly to the given stream.
ExportXML(wxOutputStream & stream,int indent,wxRichTextXMLHandler * handler)360 bool wxRichTextObject::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
361 {
362     handler->GetHelper().OutputIndentation(stream, indent);
363     handler->GetHelper().OutputString(stream, wxT("<") + GetXMLNodeName());
364 
365     wxString style = handler->GetHelper().AddAttributes(this, true);
366 
367     handler->GetHelper().OutputString(stream, style + wxT(">"));
368 
369     if (GetProperties().GetCount() > 0)
370     {
371         handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
372     }
373 
374     wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
375     if (composite)
376     {
377         size_t i;
378         for (i = 0; i < composite->GetChildCount(); i++)
379         {
380             wxRichTextObject* child = composite->GetChild(i);
381             child->ExportXML(stream, indent+1, handler);
382         }
383     }
384 
385     handler->GetHelper().OutputIndentation(stream, indent);
386     handler->GetHelper().OutputString(stream, wxT("</") + GetXMLNodeName() + wxT(">"));
387     return true;
388 }
389 #endif
390 
391 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
392 // Export this object to the given parent node, usually creating at least one child node.
ExportXML(wxXmlNode * parent,wxRichTextXMLHandler * handler)393 bool wxRichTextObject::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
394 {
395     wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
396     parent->AddChild(elementNode);
397     handler->GetHelper().AddAttributes(elementNode, this, true);
398     handler->GetHelper().WriteProperties(elementNode, GetProperties());
399 
400     wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
401     if (composite)
402     {
403         size_t i;
404         for (i = 0; i < composite->GetChildCount(); i++)
405         {
406             wxRichTextObject* child = composite->GetChild(i);
407             child->ExportXML(elementNode, handler);
408         }
409     }
410     return true;
411 }
412 #endif
413 
414 // Import this object from XML
ImportFromXML(wxRichTextBuffer * buffer,wxXmlNode * node,wxRichTextXMLHandler * handler,bool * recurse)415 bool wxRichTextPlainText::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
416 {
417     wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
418 
419     if (node->GetName() == wxT("text"))
420     {
421         wxString text;
422         wxXmlNode* textChild = node->GetChildren();
423 
424         // First skip past properties, if any.
425         wxXmlNode* n = textChild;
426         while (n)
427         {
428             // Skip past properties
429             if ((n->GetType() == wxXML_ELEMENT_NODE) && n->GetName() == wxT("properties"))
430             {
431                 textChild = n->GetNext();
432                 n = NULL;
433 
434                 // Skip past the whitespace after the properties
435                 while (textChild && (textChild->GetType() == wxXML_TEXT_NODE))
436                 {
437                     wxString cText = textChild->GetContent();
438                     cText.Trim(true);
439                     cText.Trim(false);
440                     if (!cText.IsEmpty())
441                     {
442                         textChild->SetContent(cText);
443                         break;
444                     }
445                     else
446                         textChild = textChild->GetNext();
447                 }
448 
449                 break;
450             }
451             if (n)
452                 n = n->GetNext();
453         }
454 
455         while (textChild)
456         {
457             if (textChild->GetType() == wxXML_TEXT_NODE ||
458                 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
459             {
460                 wxString text2 = textChild->GetContent();
461 
462                 // Strip whitespace from end
463                 if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
464                     text2 = text2.Mid(0, text2.length()-1);
465 
466                 if (!text2.empty() && text2[0] == wxT('"'))
467                     text2 = text2.Mid(1);
468                 if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
469                     text2 = text2.Mid(0, text2.length() - 1);
470 
471                 text += text2;
472             }
473             textChild = textChild->GetNext();
474         }
475 
476         SetText(text);
477     }
478     else if (node->GetName() == wxT("symbol"))
479     {
480         // This is a symbol that XML can't read in the normal way
481         wxString text;
482         wxXmlNode* textChild = node->GetChildren();
483         while (textChild)
484         {
485             if (textChild->GetType() == wxXML_TEXT_NODE ||
486                 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
487             {
488                 wxString text2 = textChild->GetContent();
489                 text += text2;
490             }
491             textChild = textChild->GetNext();
492         }
493 
494         wxString actualText;
495         actualText << (wxChar) wxAtoi(text);
496         SetText(actualText);
497     }
498     else
499         return false;
500 
501     return true;
502 }
503 
504 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
505 // Export this object directly to the given stream.
ExportXML(wxOutputStream & stream,int indent,wxRichTextXMLHandler * handler)506 bool wxRichTextPlainText::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
507 {
508     wxString style = handler->GetHelper().AddAttributes(this, false);
509 
510     int i;
511     int last = 0;
512     const wxString& text = GetText();
513     int len = (int) text.Length();
514 
515     if (len == 0)
516     {
517         i = 0;
518         handler->GetHelper().OutputIndentation(stream, indent);
519         handler->GetHelper().OutputString(stream, wxT("<text"));
520         handler->GetHelper().OutputString(stream, style + wxT(">"));
521         if (GetProperties().GetCount() > 0)
522         {
523             handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
524             handler->GetHelper().OutputIndentation(stream, indent);
525         }
526         handler->GetHelper().OutputString(stream, wxT("</text>"));
527     }
528     else for (i = 0; i < len; i++)
529     {
530 #if wxUSE_UNICODE
531         int c = (int) text[i];
532 #else
533         int c = (int) wxUChar(text[i]);
534 #endif
535         if (((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13)
536             // XML ranges
537             || (!(c >= 32 && c <= 55295) && !(c >= 57344 && c <= 65533))
538             )
539         {
540             if (i > 0)
541             {
542                 wxString fragment(text.Mid(last, i-last));
543                 if (!fragment.empty())
544                 {
545                     handler->GetHelper().OutputIndentation(stream, indent);
546                     handler->GetHelper().OutputString(stream, wxT("<text"));
547 
548                     handler->GetHelper().OutputString(stream, style + wxT(">"));
549 
550                     if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
551                     {
552                         handler->GetHelper().OutputString(stream, wxT("\""));
553                         handler->GetHelper().OutputStringEnt(stream, fragment);
554                         handler->GetHelper().OutputString(stream, wxT("\""));
555                     }
556                     else
557                         handler->GetHelper().OutputStringEnt(stream, fragment);
558 
559                     if (GetProperties().GetCount() > 0)
560                     {
561                         handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
562                         handler->GetHelper().OutputIndentation(stream, indent);
563                     }
564                     handler->GetHelper().OutputString(stream, wxT("</text>"));
565                 }
566             }
567 
568             // Output this character as a number in a separate tag, because XML can't cope
569             // with entities below 32 except for 10 and 13
570             last = i + 1;
571             handler->GetHelper().OutputIndentation(stream, indent);
572             handler->GetHelper().OutputString(stream, wxT("<symbol"));
573 
574             handler->GetHelper().OutputString(stream, style + wxT(">"));
575             handler->GetHelper().OutputString(stream, wxString::Format(wxT("%d"), c));
576 
577             if (GetProperties().GetCount() > 0)
578             {
579                 handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
580                 handler->GetHelper().OutputIndentation(stream, indent);
581             }
582             handler->GetHelper().OutputString(stream, wxT("</symbol>"));
583         }
584     }
585 
586     wxString fragment;
587     if (last == 0)
588         fragment = text;
589     else
590         fragment = text.Mid(last, i-last);
591 
592     if (last < len)
593     {
594         handler->GetHelper().OutputIndentation(stream, indent);
595         handler->GetHelper().OutputString(stream, wxT("<text"));
596 
597         handler->GetHelper().OutputString(stream, style + wxT(">"));
598 
599         if (GetProperties().GetCount() > 0)
600         {
601             handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
602             handler->GetHelper().OutputIndentation(stream, indent);
603         }
604 
605         if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
606         {
607             handler->GetHelper().OutputString(stream, wxT("\""));
608             handler->GetHelper().OutputStringEnt(stream, fragment);
609             handler->GetHelper().OutputString(stream, wxT("\""));
610         }
611         else
612             handler->GetHelper().OutputStringEnt(stream, fragment);
613 
614         handler->GetHelper().OutputString(stream, wxT("</text>"));
615     }
616     return true;
617 }
618 #endif
619 
620 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
621 // Export this object to the given parent node, usually creating at least one child node.
ExportXML(wxXmlNode * parent,wxRichTextXMLHandler * handler)622 bool wxRichTextPlainText::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
623 {
624     int i;
625     int last = 0;
626     const wxString& text = GetText();
627     int len = (int) text.Length();
628 
629     if (len == 0)
630     {
631         i = 0;
632 
633         wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
634         parent->AddChild(elementNode);
635 
636         handler->GetHelper().AddAttributes(elementNode, GetAttributes(), false);
637         handler->GetHelper().WriteProperties(elementNode, GetProperties());
638     }
639     else for (i = 0; i < len; i++)
640     {
641 #if wxUSE_UNICODE
642         int c = (int) text[i];
643 #else
644         int c = (int) wxUChar(text[i]);
645 #endif
646         if ((c < 32 || c == 34) && c != 10 && c != 13)
647         {
648             if (i > 0)
649             {
650                 wxString fragment(text.Mid(last, i-last));
651                 if (!fragment.empty())
652                 {
653                     // TODO: I'm assuming wxXmlDocument will output quotes if necessary
654                     wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
655                     parent->AddChild(elementNode);
656                     handler->GetHelper().AddAttributes(elementNode, GetAttributes(), false);
657                     handler->GetHelper().WriteProperties(elementNode, GetProperties());
658 
659                     wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
660                     elementNode->AddChild(textNode);
661 
662                     if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
663                         fragment = wxT("\"") + fragment + wxT("\"");
664 
665                     textNode->SetContent(fragment);
666                 }
667             }
668 
669             // Output this character as a number in a separate tag, because XML can't cope
670             // with entities below 32 except for 10 and 13
671 
672             wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("symbol"));
673             parent->AddChild(elementNode);
674 
675             handler->GetHelper().AddAttributes(elementNode, GetAttributes(), false);
676             handler->GetHelper().WriteProperties(elementNode, GetProperties());
677 
678             wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
679             elementNode->AddChild(textNode);
680             textNode->SetContent(wxString::Format(wxT("%d"), c));
681 
682             last = i + 1;
683         }
684     }
685 
686     wxString fragment;
687     if (last == 0)
688         fragment = text;
689     else
690         fragment = text.Mid(last, i-last);
691 
692     if (last < len)
693     {
694         wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
695         parent->AddChild(elementNode);
696         handler->GetHelper().AddAttributes(elementNode, GetAttributes(), false);
697 
698         wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
699         elementNode->AddChild(textNode);
700 
701         if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
702             fragment = wxT("\"") + fragment + wxT("\"");
703 
704         textNode->SetContent(fragment);
705     }
706     return true;
707 }
708 #endif
709 
710 // Import this object from XML
ImportFromXML(wxRichTextBuffer * buffer,wxXmlNode * node,wxRichTextXMLHandler * handler,bool * recurse)711 bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
712 {
713     wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
714 
715     wxBitmapType imageType = wxBITMAP_TYPE_PNG;
716     wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString);
717     if (!value.empty())
718     {
719         int type = wxAtoi(value);
720 
721         // note: 0 == wxBITMAP_TYPE_INVALID
722         if (type <= 0 || type >= wxBITMAP_TYPE_MAX)
723         {
724             wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type);
725         }
726         else
727         {
728             imageType = (wxBitmapType)type;
729         }
730     }
731 
732     wxString data;
733 
734     wxXmlNode* imageChild = node->GetChildren();
735     while (imageChild)
736     {
737         wxString childName = imageChild->GetName();
738         if (childName == wxT("data"))
739         {
740             wxXmlNode* dataChild = imageChild->GetChildren();
741             while (dataChild)
742             {
743                 data = dataChild->GetContent();
744                 // wxLogDebug(data);
745                 dataChild = dataChild->GetNext();
746             }
747 
748         }
749         imageChild = imageChild->GetNext();
750     }
751 
752     if (!data.empty())
753     {
754         wxStringInputStream strStream(data);
755 
756         GetImageBlock().ReadHex(strStream, data.length(), imageType);
757 
758         return true;
759     }
760     else
761         return false;
762 }
763 
764 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
765 // Export this object directly to the given stream.
ExportXML(wxOutputStream & stream,int indent,wxRichTextXMLHandler * handler)766 bool wxRichTextImage::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
767 {
768     wxString style = handler->GetHelper().AddAttributes(this, false);
769 
770     handler->GetHelper().OutputIndentation(stream, indent);
771     handler->GetHelper().OutputString(stream, wxT("<image"));
772     if (!GetImageBlock().IsOk())
773     {
774         // No data
775         handler->GetHelper().OutputString(stream, style + wxT(">"));
776     }
777     else
778     {
779         handler->GetHelper().OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) GetImageBlock().GetImageType()) + style + wxT(">"));
780     }
781     if (GetProperties().GetCount() > 0)
782     {
783         handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
784         handler->GetHelper().OutputIndentation(stream, indent);
785     }
786 
787     handler->GetHelper().OutputIndentation(stream, indent+1);
788     handler->GetHelper().OutputString(stream, wxT("<data>"));
789 
790     // wxStopWatch stopwatch;
791 
792     GetImageBlock().WriteHex(stream);
793 
794     // wxLogDebug(wxT("Image conversion to hex took %ldms"), stopwatch.Time());
795 
796     handler->GetHelper().OutputString(stream, wxT("</data>\n"));
797     handler->GetHelper().OutputIndentation(stream, indent);
798     handler->GetHelper().OutputString(stream, wxT("</image>"));
799     return true;
800 }
801 #endif
802 
803 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
804 // Export this object to the given parent node, usually creating at least one child node.
ExportXML(wxXmlNode * parent,wxRichTextXMLHandler * handler)805 bool wxRichTextImage::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
806 {
807     wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("image"));
808     parent->AddChild(elementNode);
809 
810     if (GetImageBlock().IsOk())
811         elementNode->AddAttribute(wxT("imagetype"), wxRichTextXMLHelper::MakeString((int) GetImageBlock().GetImageType()));
812 
813     handler->GetHelper().AddAttributes(elementNode, this, false);
814     handler->GetHelper().WriteProperties(elementNode, GetProperties());
815 
816     wxXmlNode* dataNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("data"));
817     elementNode->AddChild(dataNode);
818     wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
819     dataNode->AddChild(textNode);
820 
821     wxString strData;
822 #if 1
823     {
824         wxMemoryOutputStream stream;
825         if (GetImageBlock().WriteHex(stream))
826         {
827             if (stream.GetSize() > 0)
828             {
829                 int size = stream.GetSize();
830 #ifdef __WXDEBUG__
831                 int size2 = stream.GetOutputStreamBuffer()->GetIntPosition();
832                 wxASSERT(size == size2);
833 #endif
834                 unsigned char* data = new unsigned char[size];
835                 stream.CopyTo(data, size);
836                 strData = wxString((const char*) data, wxConvUTF8, size);
837                 delete[] data;
838             }
839         }
840 
841     }
842 #else
843     {
844         wxStringOutputStream strStream(& strData);
845         GetImageBlock().WriteHex(strStream);
846     }
847 #endif
848 
849     textNode->SetContent(strData);
850 #if wxCHECK_VERSION(2,9,0)
851     textNode->SetNoConversion(true); // optimize speed
852 #endif
853 
854     return true;
855 }
856 #endif
857 
858 // Import this object from XML
ImportFromXML(wxRichTextBuffer * buffer,wxXmlNode * node,wxRichTextXMLHandler * handler,bool * recurse)859 bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
860 {
861     wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
862 
863     *recurse = true;
864 
865     wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString);
866     if (partial == wxT("true"))
867         SetPartialParagraph(true);
868 
869     wxXmlNode* child = handler->GetHelper().FindNode(node, wxT("stylesheet"));
870     if (child && (handler->GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
871     {
872         wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
873         wxString sheetName = child->GetAttribute(wxT("name"), wxEmptyString);
874         wxString sheetDescription = child->GetAttribute(wxT("description"), wxEmptyString);
875         sheet->SetName(sheetName);
876         sheet->SetDescription(sheetDescription);
877 
878         wxXmlNode* child2 = child->GetChildren();
879         while (child2)
880         {
881             handler->GetHelper().ImportStyleDefinition(sheet, child2);
882 
883             child2 = child2->GetNext();
884         }
885         handler->GetHelper().ImportProperties(sheet->GetProperties(), child);
886 
887         // Notify that styles have changed. If this is vetoed by the app,
888         // the new sheet will be deleted. If it is not vetoed, the
889         // old sheet will be deleted and replaced with the new one.
890         buffer->SetStyleSheetAndNotify(sheet);
891     }
892 
893     return true;
894 }
895 
896 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
897 // Export this object directly to the given stream.
ExportXML(wxOutputStream & stream,int indent,wxRichTextXMLHandler * handler)898 bool wxRichTextParagraphLayoutBox::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
899 {
900     handler->GetHelper().OutputIndentation(stream, indent);
901     wxString nodeName = GetXMLNodeName();
902     handler->GetHelper().OutputString(stream, wxT("<") + nodeName);
903 
904     wxString style = handler->GetHelper().AddAttributes(this, true);
905 
906     if (GetPartialParagraph())
907         style << wxT(" partialparagraph=\"true\"");
908 
909     handler->GetHelper().OutputString(stream, style + wxT(">"));
910 
911     if (GetProperties().GetCount() > 0)
912     {
913         handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
914     }
915 
916     size_t i;
917     for (i = 0; i < GetChildCount(); i++)
918     {
919         wxRichTextObject* child = GetChild(i);
920         child->ExportXML(stream, indent+1, handler);
921     }
922 
923     handler->GetHelper().OutputIndentation(stream, indent);
924     handler->GetHelper().OutputString(stream, wxT("</") + nodeName + wxT(">"));
925     return true;
926 }
927 #endif
928 
929 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
930 // Export this object to the given parent node, usually creating at least one child node.
ExportXML(wxXmlNode * parent,wxRichTextXMLHandler * handler)931 bool wxRichTextParagraphLayoutBox::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
932 {
933     wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
934     parent->AddChild(elementNode);
935     handler->GetHelper().AddAttributes(elementNode, this, true);
936     handler->GetHelper().WriteProperties(elementNode, GetProperties());
937 
938     if (GetPartialParagraph())
939         elementNode->AddAttribute(wxT("partialparagraph"), wxT("true"));
940 
941     size_t i;
942     for (i = 0; i < GetChildCount(); i++)
943     {
944         wxRichTextObject* child = GetChild(i);
945         child->ExportXML(elementNode, handler);
946     }
947 
948     return true;
949 }
950 #endif
951 
952 // Import this object from XML
ImportFromXML(wxRichTextBuffer * buffer,wxXmlNode * node,wxRichTextXMLHandler * handler,bool * recurse)953 bool wxRichTextTable::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
954 {
955     wxRichTextBox::ImportFromXML(buffer, node, handler, recurse);
956 
957     *recurse = false;
958 
959     m_rowCount = wxAtoi(node->GetAttribute(wxT("rows"), wxEmptyString));
960     m_colCount = wxAtoi(node->GetAttribute(wxT("cols"), wxEmptyString));
961 
962     wxXmlNode* child = node->GetChildren();
963     while (child)
964     {
965         wxRichTextObject* childObj = handler->CreateObjectForXMLName(this, child->GetName());
966         if (childObj)
967         {
968             AppendChild(childObj);
969             handler->ImportXML(buffer, childObj, child);
970         }
971         child = child->GetNext();
972     }
973 
974     m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
975     int i, j;
976     for (i = 0; i < m_rowCount; i++)
977     {
978         wxRichTextObjectPtrArray& colArray = m_cells[i];
979         for (j = 0; j < m_colCount; j++)
980         {
981             int idx = i * m_colCount + j;
982             if (idx < (int) GetChildren().GetCount())
983             {
984                 wxRichTextCell* cell = wxDynamicCast(GetChildren().Item(idx)->GetData(), wxRichTextCell);
985                 if (cell)
986                     colArray.Add(cell);
987             }
988         }
989     }
990 
991     return true;
992 }
993 
994 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
995 // Export this object directly to the given stream.
ExportXML(wxOutputStream & stream,int indent,wxRichTextXMLHandler * handler)996 bool wxRichTextTable::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
997 {
998     handler->GetHelper().OutputIndentation(stream, indent);
999     wxString nodeName = GetXMLNodeName();
1000     handler->GetHelper().OutputString(stream, wxT("<") + nodeName);
1001 
1002     wxString style = handler->GetHelper().AddAttributes(this, true);
1003 
1004     style << wxT(" rows=\"") << m_rowCount << wxT("\"");
1005     style << wxT(" cols=\"") << m_colCount << wxT("\"");
1006 
1007     handler->GetHelper().OutputString(stream, style + wxT(">"));
1008 
1009     if (GetProperties().GetCount() > 0)
1010     {
1011         handler->GetHelper().WriteProperties(stream, GetProperties(), indent);
1012     }
1013 
1014     int i, j;
1015     for (i = 0; i < m_rowCount; i++)
1016     {
1017         for (j = 0; j < m_colCount; j ++)
1018         {
1019             wxRichTextCell* cell = GetCell(i, j);
1020             cell->ExportXML(stream, indent+1, handler);
1021         }
1022     }
1023 
1024     handler->GetHelper().OutputIndentation(stream, indent);
1025     handler->GetHelper().OutputString(stream, wxT("</") + nodeName + wxT(">"));
1026 
1027     return true;
1028 }
1029 #endif
1030 
1031 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
1032 // Export this object to the given parent node, usually creating at least one child node.
ExportXML(wxXmlNode * parent,wxRichTextXMLHandler * handler)1033 bool wxRichTextTable::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
1034 {
1035     wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
1036     parent->AddChild(elementNode);
1037     handler->GetHelper().AddAttributes(elementNode, this, true);
1038     handler->GetHelper().WriteProperties(elementNode, GetProperties());
1039 
1040     elementNode->AddAttribute(wxT("rows"), wxString::Format(wxT("%d"), m_rowCount));
1041     elementNode->AddAttribute(wxT("cols"), wxString::Format(wxT("%d"), m_colCount));
1042 
1043     int i, j;
1044     for (i = 0; i < m_rowCount; i++)
1045     {
1046         for (j = 0; j < m_colCount; j ++)
1047         {
1048             wxRichTextCell* cell = GetCell(i, j);
1049             cell->ExportXML(elementNode, handler);
1050         }
1051     }
1052 
1053     return true;
1054 }
1055 #endif
1056 
~wxRichTextXMLHelper()1057 wxRichTextXMLHelper::~wxRichTextXMLHelper()
1058 {
1059     Clear();
1060 }
1061 
Init()1062 void wxRichTextXMLHelper::Init()
1063 {
1064 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1065     m_deleteConvFile = false;
1066     m_convMem = NULL;
1067     m_convFile = NULL;
1068 #endif
1069     m_flags = 0;
1070 }
1071 
Clear()1072 void wxRichTextXMLHelper::Clear()
1073 {
1074 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1075     if (m_deleteConvFile)
1076         delete m_convFile;
1077     m_convFile = NULL;
1078     m_convMem = NULL;
1079     m_deleteConvFile = false;
1080 #endif
1081     m_fileEncoding.clear();
1082 }
1083 
SetupForSaving(const wxString & enc)1084 void wxRichTextXMLHelper::SetupForSaving(const wxString& enc)
1085 {
1086     Clear();
1087 
1088 #if wxUSE_UNICODE
1089     m_fileEncoding = wxT("UTF-8");
1090 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1091     m_convFile = & wxConvUTF8;
1092 #endif
1093 #else
1094     m_fileEncoding = wxT("ISO-8859-1");
1095 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1096     m_convFile = & wxConvISO8859_1;
1097 #endif
1098 #endif
1099 
1100     // If we pass an explicit encoding, change the output encoding.
1101     if (!enc.empty() && enc.Lower() != m_fileEncoding.Lower())
1102     {
1103         if (enc == wxT("<System>"))
1104         {
1105 #if wxUSE_INTL
1106             m_fileEncoding = wxLocale::GetSystemEncodingName();
1107             // if !wxUSE_INTL, we fall back to UTF-8 or ISO-8859-1 below
1108 #endif
1109         }
1110         else
1111         {
1112             m_fileEncoding = enc;
1113         }
1114 
1115         // GetSystemEncodingName may not have returned a name
1116         if (m_fileEncoding.empty())
1117 #if wxUSE_UNICODE
1118             m_fileEncoding = wxT("UTF-8");
1119 #else
1120             m_fileEncoding = wxT("ISO-8859-1");
1121 #endif
1122 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1123         m_convFile = new wxCSConv(m_fileEncoding);
1124         m_deleteConvFile = true;
1125 #endif
1126     }
1127 
1128 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1129 #if !wxUSE_UNICODE
1130     m_convMem = wxConvCurrent;
1131 #else
1132     m_convMem = NULL;
1133 #endif
1134 #endif
1135 }
1136 
1137 // Convert a colour to a 6-digit hex string
ColourToHexString(const wxColour & col)1138 wxString wxRichTextXMLHelper::ColourToHexString(const wxColour& col)
1139 {
1140     wxString hex;
1141 
1142     hex += wxDecToHex(col.Red());
1143     hex += wxDecToHex(col.Green());
1144     hex += wxDecToHex(col.Blue());
1145 
1146     return hex;
1147 }
1148 
1149 // Convert 6-digit hex string to a colour
HexStringToColour(const wxString & hex)1150 wxColour wxRichTextXMLHelper::HexStringToColour(const wxString& hex)
1151 {
1152     unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
1153     unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
1154     unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
1155 
1156     return wxColour(r, g, b);
1157 }
1158 
1159 //-----------------------------------------------------------------------------
1160 //  xml support routines
1161 //-----------------------------------------------------------------------------
1162 
HasParam(wxXmlNode * node,const wxString & param)1163 bool wxRichTextXMLHelper::HasParam(wxXmlNode* node, const wxString& param)
1164 {
1165     return (GetParamNode(node, param) != NULL);
1166 }
1167 
GetParamNode(wxXmlNode * node,const wxString & param)1168 wxXmlNode *wxRichTextXMLHelper::GetParamNode(wxXmlNode* node, const wxString& param)
1169 {
1170     wxCHECK_MSG(node, NULL, wxT("You can't access node data before it was initialized!"));
1171 
1172     wxXmlNode *n = node->GetChildren();
1173 
1174     while (n)
1175     {
1176         if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
1177             return n;
1178         n = n->GetNext();
1179     }
1180     return NULL;
1181 }
1182 
GetNodeContent(wxXmlNode * node)1183 wxString wxRichTextXMLHelper::GetNodeContent(wxXmlNode *node)
1184 {
1185     wxXmlNode *n = node;
1186     if (n == NULL) return wxEmptyString;
1187     n = n->GetChildren();
1188 
1189     while (n)
1190     {
1191         if (n->GetType() == wxXML_TEXT_NODE ||
1192             n->GetType() == wxXML_CDATA_SECTION_NODE)
1193             return n->GetContent();
1194         n = n->GetNext();
1195     }
1196     return wxEmptyString;
1197 }
1198 
GetParamValue(wxXmlNode * node,const wxString & param)1199 wxString wxRichTextXMLHelper::GetParamValue(wxXmlNode *node, const wxString& param)
1200 {
1201     if (param.empty())
1202         return GetNodeContent(node);
1203     else
1204         return GetNodeContent(GetParamNode(node, param));
1205 }
1206 
GetText(wxXmlNode * node,const wxString & param)1207 wxString wxRichTextXMLHelper::GetText(wxXmlNode *node, const wxString& param)
1208 {
1209     wxXmlNode *parNode = GetParamNode(node, param);
1210     if (!parNode)
1211         parNode = node;
1212     wxString str1(GetNodeContent(parNode));
1213     return str1;
1214 }
1215 
FindNode(wxXmlNode * node,const wxString & name)1216 wxXmlNode* wxRichTextXMLHelper::FindNode(wxXmlNode* node, const wxString& name)
1217 {
1218     if (node->GetName() == name && name == wxT("stylesheet"))
1219         return node;
1220 
1221     wxXmlNode* child = node->GetChildren();
1222     while (child)
1223     {
1224         if (child->GetName() == name)
1225             return child;
1226         child = child->GetNext();
1227     }
1228     return NULL;
1229 }
1230 
AttributeToXML(const wxString & str)1231 wxString wxRichTextXMLHelper::AttributeToXML(const wxString& str)
1232 {
1233     wxString str1;
1234     size_t i, last, len;
1235     wxChar c;
1236 
1237     len = str.Len();
1238     last = 0;
1239     for (i = 0; i < len; i++)
1240     {
1241         c = str.GetChar(i);
1242 
1243         // Original code excluded "&amp;" but we _do_ want to convert
1244         // the ampersand beginning &amp; because otherwise when read in,
1245         // the original "&amp;" becomes "&".
1246 
1247         if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
1248             (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
1249         {
1250             str1 += str.Mid(last, i - last);
1251             switch (c)
1252             {
1253             case wxT('<'):
1254                 str1 += wxT("&lt;");
1255                 break;
1256             case wxT('>'):
1257                 str1 += wxT("&gt;");
1258                 break;
1259             case wxT('&'):
1260                 str1 += wxT("&amp;");
1261                 break;
1262             case wxT('"'):
1263                 str1 += wxT("&quot;");
1264                 break;
1265             default: break;
1266             }
1267             last = i + 1;
1268         }
1269         else if (wxUChar(c) > 127)
1270         {
1271             str1 += str.Mid(last, i - last);
1272 
1273             wxString s(wxT("&#"));
1274 #if wxUSE_UNICODE
1275             s << (int) c;
1276 #else
1277             s << (int) wxUChar(c);
1278 #endif
1279             s << wxT(";");
1280             str1 += s;
1281             last = i + 1;
1282         }
1283     }
1284     str1 += str.Mid(last, i - last);
1285     return str1;
1286 }
1287 
1288 // Make a string from the given property. This can be overridden for custom variants.
MakeStringFromProperty(const wxVariant & var)1289 wxString wxRichTextXMLHelper::MakeStringFromProperty(const wxVariant& var)
1290 {
1291     return var.MakeString();
1292 }
1293 
1294 // Create a proprty from the string read from the XML file.
MakePropertyFromString(const wxString & name,const wxString & value,const wxString & WXUNUSED (type))1295 wxVariant wxRichTextXMLHelper::MakePropertyFromString(const wxString& name, const wxString& value, const wxString& WXUNUSED(type))
1296 {
1297     wxVariant var(value, name);
1298     // TODO: use type to create using common types
1299     return var;
1300 }
1301 
1302 /// Replace face name with current name for platform.
1303 /// TODO: introduce a virtual function or settable table to
1304 /// do this comprehensively.
RichTextFixFaceName(wxString & facename)1305 bool wxRichTextXMLHelper::RichTextFixFaceName(wxString& facename)
1306 {
1307     if (facename.empty())
1308         return false;
1309 
1310 #ifdef __WXMSW__
1311     if (facename == wxT("Times"))
1312     {
1313         facename = wxT("Times New Roman");
1314         return true;
1315     }
1316     else if (facename == wxT("Helvetica"))
1317     {
1318         facename = wxT("Arial");
1319         return true;
1320     }
1321     else if (facename == wxT("Courier"))
1322     {
1323         facename = wxT("Courier New");
1324         return true;
1325     }
1326     else
1327         return false;
1328 #else
1329     if (facename == wxT("Times New Roman"))
1330     {
1331         facename = wxT("Times");
1332         return true;
1333     }
1334     else if (facename == wxT("Arial"))
1335     {
1336         facename = wxT("Helvetica");
1337         return true;
1338     }
1339     else if (facename == wxT("Courier New"))
1340     {
1341         facename = wxT("Courier");
1342         return true;
1343     }
1344     else
1345         return false;
1346 #endif
1347 }
1348 
ColourStringToLong(const wxString & colStr)1349 long wxRichTextXMLHelper::ColourStringToLong(const wxString& colStr)
1350 {
1351     if (!colStr.IsEmpty())
1352     {
1353         wxColour col(colStr);
1354 #if wxCHECK_VERSION(2,9,0)
1355         return col.GetRGB();
1356 #else
1357         return (col.Red() | (col.Green() << 8) | (col.Blue() << 16));
1358 #endif
1359     }
1360     else
1361         return 0;
1362 }
1363 
ParseDimension(const wxString & dimStr)1364 wxTextAttrDimension wxRichTextXMLHelper::ParseDimension(const wxString& dimStr)
1365 {
1366     wxString valuePart = dimStr.BeforeFirst(wxT(','));
1367     wxString flagsPart;
1368     if (dimStr.Contains(wxT(",")))
1369         flagsPart = dimStr.AfterFirst(wxT(','));
1370     wxTextAttrDimension dim;
1371     dim.SetValue(wxAtoi(valuePart));
1372     dim.SetFlags(wxAtoi(flagsPart));
1373 
1374     return dim;
1375 }
1376 
1377 /// Import style parameters
ImportStyle(wxRichTextAttr & attr,wxXmlNode * node,bool isPara)1378 bool wxRichTextXMLHelper::ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara)
1379 {
1380     wxXmlAttribute* xmlAttr = node->GetAttributes();
1381     bool found;
1382     while (xmlAttr)
1383     {
1384         const wxString& name = xmlAttr->GetName();
1385         const wxString& value = xmlAttr->GetValue();
1386         found = true;
1387 
1388         if (name == wxT("fontface"))
1389         {
1390             if (!value.empty())
1391             {
1392                 wxString v = value;
1393                 if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES)
1394                     RichTextFixFaceName(v);
1395                 attr.SetFontFaceName(v);
1396             }
1397         }
1398         else if (name == wxT("fontfamily"))
1399         {
1400             if (!value.empty())
1401                 attr.SetFontFamily((wxFontFamily)wxAtoi(value));
1402         }
1403         else if (name == wxT("fontstyle"))
1404         {
1405             if (!value.empty())
1406                 attr.SetFontStyle((wxFontStyle)wxAtoi(value));
1407         }
1408         else if (name == wxT("fontsize") || name == wxT("fontpointsize"))
1409         {
1410             if (!value.empty())
1411                 attr.SetFontPointSize(wxAtoi(value));
1412         }
1413         else if (name == wxT("fontpixelsize"))
1414         {
1415             if (!value.empty())
1416                 attr.SetFontPixelSize(wxAtoi(value));
1417         }
1418         else if (name == wxT("fontweight"))
1419         {
1420             if (!value.empty())
1421                 attr.SetFontWeight((wxFontWeight) wxAtoi(value));
1422         }
1423         else if (name == wxT("fontunderlined"))
1424         {
1425             if (!value.empty())
1426                 attr.SetFontUnderlined(wxAtoi(value) != 0);
1427         }
1428         else if (name == wxT("textcolor"))
1429         {
1430             if (!value.empty())
1431             {
1432                 if (value[0] == wxT('#'))
1433                     attr.SetTextColour(HexStringToColour(value.Mid(1)));
1434                 else
1435                     attr.SetTextColour(value);
1436             }
1437         }
1438         else if (name == wxT("bgcolor"))
1439         {
1440             if (!value.empty())
1441             {
1442                 if (value[0] == wxT('#'))
1443                     attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
1444                 else
1445                     attr.SetBackgroundColour(value);
1446             }
1447         }
1448         else if (name == wxT("characterstyle"))
1449         {
1450             if (!value.empty())
1451                 attr.SetCharacterStyleName(value);
1452         }
1453         else if (name == wxT("texteffects"))
1454         {
1455             if (!value.empty())
1456                 attr.SetTextEffects(wxAtoi(value));
1457         }
1458         else if (name == wxT("texteffectflags"))
1459         {
1460             if (!value.empty())
1461                 attr.SetTextEffectFlags(wxAtoi(value));
1462         }
1463         else if (name == wxT("url"))
1464         {
1465             if (!value.empty())
1466                 attr.SetURL(value);
1467         }
1468         else if (isPara)
1469         {
1470             if (name == wxT("alignment"))
1471             {
1472                 if (!value.empty())
1473                     attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
1474             }
1475             else if (name == wxT("leftindent"))
1476             {
1477                 if (!value.empty())
1478                     attr.SetLeftIndent(wxAtoi(value), attr.GetLeftSubIndent());
1479             }
1480             else if (name == wxT("leftsubindent"))
1481             {
1482                 if (!value.empty())
1483                     attr.SetLeftIndent(attr.GetLeftIndent(), wxAtoi(value));
1484             }
1485             else if (name == wxT("rightindent"))
1486             {
1487                 if (!value.empty())
1488                     attr.SetRightIndent(wxAtoi(value));
1489             }
1490             else if (name == wxT("parspacingbefore"))
1491             {
1492                 if (!value.empty())
1493                     attr.SetParagraphSpacingBefore(wxAtoi(value));
1494             }
1495             else if (name == wxT("parspacingafter"))
1496             {
1497                 if (!value.empty())
1498                     attr.SetParagraphSpacingAfter(wxAtoi(value));
1499             }
1500             else if (name == wxT("linespacing"))
1501             {
1502                 if (!value.empty())
1503                     attr.SetLineSpacing(wxAtoi(value));
1504             }
1505             else if (name == wxT("bulletstyle"))
1506             {
1507                 if (!value.empty())
1508                     attr.SetBulletStyle(wxAtoi(value));
1509             }
1510             else if (name == wxT("bulletnumber"))
1511             {
1512                 if (!value.empty())
1513                     attr.SetBulletNumber(wxAtoi(value));
1514             }
1515             else if (name == wxT("bulletsymbol"))
1516             {
1517                 if (!value.empty())
1518                 {
1519                     wxChar ch = wxAtoi(value);
1520                     wxString s;
1521                     s << ch;
1522                     attr.SetBulletText(s);
1523                 }
1524             }
1525             else if (name == wxT("bullettext"))
1526             {
1527                 if (!value.empty())
1528                 {
1529                     attr.SetBulletText(value);
1530                 }
1531             }
1532             else if (name == wxT("bulletfont"))
1533             {
1534                 if (!value.empty())
1535                 {
1536                     attr.SetBulletFont(value);
1537                 }
1538             }
1539             else if (name == wxT("bulletname"))
1540             {
1541                 if (!value.empty())
1542                 {
1543                     attr.SetBulletName(value);
1544                 }
1545             }
1546             else if (name == wxT("parstyle"))
1547             {
1548                 if (!value.empty())
1549                 {
1550                     attr.SetParagraphStyleName(value);
1551                 }
1552             }
1553             else if (name == wxT("liststyle"))
1554             {
1555                 if (!value.empty())
1556                 {
1557                     attr.SetListStyleName(value);
1558                 }
1559             }
1560             else if (name == wxT("boxstyle"))
1561             {
1562                 if (!value.empty())
1563                 {
1564                     attr.GetTextBoxAttr().SetBoxStyleName(value);
1565                 }
1566             }
1567             else if (name == wxT("tabs"))
1568             {
1569                 if (!value.empty())
1570                 {
1571                     wxArrayInt tabs;
1572                     wxStringTokenizer tkz(value, wxT(","));
1573                     while (tkz.HasMoreTokens())
1574                     {
1575                         wxString token = tkz.GetNextToken();
1576                         tabs.Add(wxAtoi(token));
1577                     }
1578                     attr.SetTabs(tabs);
1579                 }
1580             }
1581             else if (name == wxT("pagebreak"))
1582             {
1583                 if (!value.empty())
1584                 {
1585                     attr.SetPageBreak(wxAtoi(value) != 0);
1586                 }
1587             }
1588             else if (name == wxT("outlinelevel"))
1589             {
1590                 if (!value.empty())
1591                 {
1592                     attr.SetOutlineLevel(wxAtoi(value));
1593                 }
1594             }
1595             else
1596                 found = false;
1597         }
1598         else
1599             found = false;
1600 
1601         if (!found)
1602         {
1603             // Box attributes
1604 
1605             if (name == wxT("width"))
1606             {
1607                 attr.GetTextBoxAttr().GetWidth().SetValue(ParseDimension(value));
1608             }
1609             else if (name == wxT("height"))
1610             {
1611                 attr.GetTextBoxAttr().GetHeight().SetValue(ParseDimension(value));
1612             }
1613             else if (name == wxT("minwidth"))
1614             {
1615                 attr.GetTextBoxAttr().GetMinSize().GetWidth().SetValue(ParseDimension(value));
1616             }
1617             else if (name == wxT("minheight"))
1618             {
1619                 attr.GetTextBoxAttr().GetMinSize().GetHeight().SetValue(ParseDimension(value));
1620             }
1621             else if (name == wxT("maxwidth"))
1622             {
1623                 attr.GetTextBoxAttr().GetMaxSize().GetWidth().SetValue(ParseDimension(value));
1624             }
1625             else if (name == wxT("maxheight"))
1626             {
1627                 attr.GetTextBoxAttr().GetMaxSize().GetHeight().SetValue(ParseDimension(value));
1628             }
1629             else if (name == wxT("corner-radius"))
1630             {
1631                 attr.GetTextBoxAttr().SetCornerRadius(ParseDimension(value));
1632             }
1633 
1634             else if (name == wxT("verticalalignment"))
1635             {
1636                 if (value == wxT("top"))
1637                     attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP);
1638                 else if (value == wxT("centre"))
1639                     attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE);
1640                 else if (value == wxT("bottom"))
1641                     attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM);
1642                 else if (value == wxT("none"))
1643                     attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE);
1644             }
1645             else if (name == wxT("float"))
1646             {
1647                 if (value == wxT("left"))
1648                     attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_LEFT);
1649                 else if (value == wxT("right"))
1650                     attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_RIGHT);
1651                 else if (value == wxT("none"))
1652                     attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_NONE);
1653             }
1654             else if (name == wxT("clear"))
1655             {
1656                 if (value == wxT("left"))
1657                     attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_LEFT);
1658                 else if (value == wxT("right"))
1659                     attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_RIGHT);
1660                 else if (value == wxT("both"))
1661                     attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_BOTH);
1662                 else if (value == wxT("none"))
1663                     attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_NONE);
1664             }
1665             else if (name == wxT("collapse-borders"))
1666                 attr.GetTextBoxAttr().SetCollapseBorders((wxTextBoxAttrCollapseMode) wxAtoi(value));
1667             else if (name == wxT("whitespace-mode"))
1668                 attr.GetTextBoxAttr().SetWhitespaceMode((wxTextBoxAttrWhitespaceMode) wxAtoi(value));
1669 
1670             else if (name.Contains(wxT("border-")))
1671             {
1672                 if (name == wxT("border-left-style"))
1673                     attr.GetTextBoxAttr().GetBorder().GetLeft().SetStyle(wxAtoi(value));
1674                 else if (name == wxT("border-right-style"))
1675                     attr.GetTextBoxAttr().GetBorder().GetRight().SetStyle(wxAtoi(value));
1676                 else if (name == wxT("border-top-style"))
1677                     attr.GetTextBoxAttr().GetBorder().GetTop().SetStyle(wxAtoi(value));
1678                 else if (name == wxT("border-bottom-style"))
1679                     attr.GetTextBoxAttr().GetBorder().GetBottom().SetStyle(wxAtoi(value));
1680 
1681                 else if (name == wxT("border-left-color"))
1682                     attr.GetTextBoxAttr().GetBorder().GetLeft().SetColour(ColourStringToLong(value));
1683                 else if (name == wxT("border-right-color"))
1684                     attr.GetTextBoxAttr().GetBorder().GetRight().SetColour(ColourStringToLong(value));
1685                 else if (name == wxT("border-top-color"))
1686                     attr.GetTextBoxAttr().GetBorder().GetTop().SetColour(ColourStringToLong(value));
1687                 else if (name == wxT("border-bottom-color"))
1688                     attr.GetTextBoxAttr().GetBorder().GetBottom().SetColour(ColourStringToLong(value));
1689 
1690                 else if (name == wxT("border-left-width"))
1691                     attr.GetTextBoxAttr().GetBorder().GetLeft().SetWidth(ParseDimension(value));
1692                 else if (name == wxT("border-right-width"))
1693                     attr.GetTextBoxAttr().GetBorder().GetRight().SetWidth(ParseDimension(value));
1694                 else if (name == wxT("border-top-width"))
1695                     attr.GetTextBoxAttr().GetBorder().GetTop().SetWidth(ParseDimension(value));
1696                 else if (name == wxT("border-bottom-width"))
1697                     attr.GetTextBoxAttr().GetBorder().GetBottom().SetWidth(ParseDimension(value));
1698             }
1699             else if (name.Contains(wxT("outline-")))
1700             {
1701                 if (name == wxT("outline-left-style"))
1702                     attr.GetTextBoxAttr().GetOutline().GetLeft().SetStyle(wxAtoi(value));
1703                 else if (name == wxT("outline-right-style"))
1704                     attr.GetTextBoxAttr().GetOutline().GetRight().SetStyle(wxAtoi(value));
1705                 else if (name == wxT("outline-top-style"))
1706                     attr.GetTextBoxAttr().GetOutline().GetTop().SetStyle(wxAtoi(value));
1707                 else if (name == wxT("outline-bottom-style"))
1708                     attr.GetTextBoxAttr().GetOutline().GetBottom().SetStyle(wxAtoi(value));
1709 
1710                 else if (name == wxT("outline-left-color"))
1711                     attr.GetTextBoxAttr().GetOutline().GetLeft().SetColour(ColourStringToLong(value));
1712                 else if (name == wxT("outline-right-color"))
1713                     attr.GetTextBoxAttr().GetOutline().GetRight().SetColour(ColourStringToLong(value));
1714                 else if (name == wxT("outline-top-color"))
1715                     attr.GetTextBoxAttr().GetOutline().GetTop().SetColour(ColourStringToLong(value));
1716                 else if (name == wxT("outline-bottom-color"))
1717                     attr.GetTextBoxAttr().GetOutline().GetBottom().SetColour(ColourStringToLong(value));
1718 
1719                 else if (name == wxT("outline-left-width"))
1720                     attr.GetTextBoxAttr().GetOutline().GetLeft().SetWidth(ParseDimension(value));
1721                 else if (name == wxT("outline-right-width"))
1722                     attr.GetTextBoxAttr().GetOutline().GetRight().SetWidth(ParseDimension(value));
1723                 else if (name == wxT("outline-top-width"))
1724                     attr.GetTextBoxAttr().GetOutline().GetTop().SetWidth(ParseDimension(value));
1725                 else if (name == wxT("outline-bottom-width"))
1726                     attr.GetTextBoxAttr().GetOutline().GetBottom().SetWidth(ParseDimension(value));
1727             }
1728             else if (name.Contains(wxT("margin-")))
1729             {
1730                 if (name == wxT("margin-left"))
1731                     attr.GetTextBoxAttr().GetMargins().GetLeft().SetValue(ParseDimension(value));
1732                 else if (name == wxT("margin-right"))
1733                     attr.GetTextBoxAttr().GetMargins().GetRight().SetValue(ParseDimension(value));
1734                 else if (name == wxT("margin-top"))
1735                     attr.GetTextBoxAttr().GetMargins().GetTop().SetValue(ParseDimension(value));
1736                 else if (name == wxT("margin-bottom"))
1737                     attr.GetTextBoxAttr().GetMargins().GetBottom().SetValue(ParseDimension(value));
1738             }
1739             else if (name.Contains(wxT("padding-")))
1740             {
1741                 if (name == wxT("padding-left"))
1742                     attr.GetTextBoxAttr().GetPadding().GetLeft().SetValue(ParseDimension(value));
1743                 else if (name == wxT("padding-right"))
1744                     attr.GetTextBoxAttr().GetPadding().GetRight().SetValue(ParseDimension(value));
1745                 else if (name == wxT("padding-top"))
1746                     attr.GetTextBoxAttr().GetPadding().GetTop().SetValue(ParseDimension(value));
1747                 else if (name == wxT("padding-bottom"))
1748                     attr.GetTextBoxAttr().GetPadding().GetBottom().SetValue(ParseDimension(value));
1749             }
1750             else if (name.Contains(wxT("position-")))
1751             {
1752                 if (name == wxT("position-left"))
1753                     attr.GetTextBoxAttr().GetPosition().GetLeft().SetValue(ParseDimension(value));
1754                 else if (name == wxT("position-right"))
1755                     attr.GetTextBoxAttr().GetPosition().GetRight().SetValue(ParseDimension(value));
1756                 else if (name == wxT("position-top"))
1757                     attr.GetTextBoxAttr().GetPosition().GetTop().SetValue(ParseDimension(value));
1758                 else if (name == wxT("position-bottom"))
1759                     attr.GetTextBoxAttr().GetPosition().GetBottom().SetValue(ParseDimension(value));
1760             }
1761         }
1762 
1763         xmlAttr = xmlAttr->GetNext();
1764     }
1765 
1766     return true;
1767 }
1768 
ImportStyleDefinition(wxRichTextStyleSheet * sheet,wxXmlNode * node)1769 bool wxRichTextXMLHelper::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node)
1770 {
1771     wxString styleType = node->GetName();
1772     wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString);
1773     wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString);
1774 
1775     if (styleName.empty())
1776         return false;
1777 
1778     if (styleType == wxT("characterstyle"))
1779     {
1780         wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName);
1781         def->SetBaseStyle(baseStyleName);
1782 
1783         wxXmlNode* child = node->GetChildren();
1784         while (child)
1785         {
1786             if (child->GetName() == wxT("style"))
1787             {
1788                 wxRichTextAttr attr;
1789                 ImportStyle(attr, child, false);
1790                 def->SetStyle(attr);
1791             }
1792             child = child->GetNext();
1793         }
1794 
1795         ImportProperties(def->GetProperties(), node);
1796 
1797         sheet->AddCharacterStyle(def);
1798     }
1799     else if (styleType == wxT("paragraphstyle"))
1800     {
1801         wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName);
1802 
1803         wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString);
1804         def->SetNextStyle(nextStyleName);
1805         def->SetBaseStyle(baseStyleName);
1806 
1807         wxXmlNode* child = node->GetChildren();
1808         while (child)
1809         {
1810             if (child->GetName() == wxT("style"))
1811             {
1812                 wxRichTextAttr attr;
1813                 ImportStyle(attr, child, true);
1814                 def->SetStyle(attr);
1815             }
1816             child = child->GetNext();
1817         }
1818 
1819         ImportProperties(def->GetProperties(), node);
1820 
1821         sheet->AddParagraphStyle(def);
1822     }
1823     else if (styleType == wxT("boxstyle"))
1824     {
1825         wxRichTextBoxStyleDefinition* def = new wxRichTextBoxStyleDefinition(styleName);
1826 
1827         def->SetBaseStyle(baseStyleName);
1828 
1829         wxXmlNode* child = node->GetChildren();
1830         while (child)
1831         {
1832             if (child->GetName() == wxT("style"))
1833             {
1834                 wxRichTextAttr attr;
1835                 ImportStyle(attr, child, true);
1836                 def->SetStyle(attr);
1837             }
1838             child = child->GetNext();
1839         }
1840 
1841         ImportProperties(def->GetProperties(), node);
1842 
1843         sheet->AddBoxStyle(def);
1844     }
1845     else if (styleType == wxT("liststyle"))
1846     {
1847         wxRichTextListStyleDefinition* def = new wxRichTextListStyleDefinition(styleName);
1848 
1849         wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString);
1850         def->SetNextStyle(nextStyleName);
1851         def->SetBaseStyle(baseStyleName);
1852 
1853         wxXmlNode* child = node->GetChildren();
1854         while (child)
1855         {
1856             if (child->GetName() == wxT("style"))
1857             {
1858                 wxRichTextAttr attr;
1859                 ImportStyle(attr, child, true);
1860 
1861                 wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString);
1862                 if (styleLevel.empty())
1863                 {
1864                     def->SetStyle(attr);
1865                 }
1866                 else
1867                 {
1868                     int level = wxAtoi(styleLevel);
1869                     if (level > 0 && level <= 10)
1870                     {
1871                         def->SetLevelAttributes(level-1, attr);
1872                     }
1873                 }
1874             }
1875             child = child->GetNext();
1876         }
1877 
1878         ImportProperties(def->GetProperties(), node);
1879 
1880         sheet->AddListStyle(def);
1881     }
1882 
1883     return true;
1884 }
1885 
ImportProperties(wxRichTextProperties & properties,wxXmlNode * node)1886 bool wxRichTextXMLHelper::ImportProperties(wxRichTextProperties& properties, wxXmlNode* node)
1887 {
1888     wxXmlNode* child = node->GetChildren();
1889     while (child)
1890     {
1891         if (child->GetName() == wxT("properties"))
1892         {
1893             wxXmlNode* propertyChild = child->GetChildren();
1894             while (propertyChild)
1895             {
1896                 if (propertyChild->GetName() == wxT("property"))
1897                 {
1898                     wxString name = propertyChild->GetAttribute(wxT("name"), wxEmptyString);
1899                     wxString value = propertyChild->GetAttribute(wxT("value"), wxEmptyString);
1900                     wxString type = propertyChild->GetAttribute(wxT("type"), wxEmptyString);
1901 
1902                     wxVariant var = MakePropertyFromString(name, value, type);
1903                     if (!var.IsNull())
1904                     {
1905                         properties.SetProperty(var);
1906                     }
1907                 }
1908                 propertyChild = propertyChild->GetNext();
1909             }
1910         }
1911         child = child->GetNext();
1912     }
1913     return true;
1914 }
1915 
1916 #if wxRICHTEXT_HAVE_DIRECT_OUTPUT
1917 // write string to output
OutputString(wxOutputStream & stream,const wxString & str,wxMBConv * WXUNUSED_IN_UNICODE (convMem),wxMBConv * convFile)1918 void wxRichTextXMLHelper::OutputString(wxOutputStream& stream, const wxString& str,
1919                                 wxMBConv *WXUNUSED_IN_UNICODE(convMem), wxMBConv *convFile)
1920 {
1921     if (str.empty()) return;
1922 #if wxUSE_UNICODE
1923     if (convFile)
1924     {
1925         const wxWX2MBbuf buf(str.mb_str(*convFile));
1926         stream.Write((const char*)buf, strlen((const char*)buf));
1927     }
1928     else
1929     {
1930         const wxWX2MBbuf buf(str.mb_str(wxConvUTF8));
1931         stream.Write((const char*)buf, strlen((const char*)buf));
1932     }
1933 #else
1934     if ( convFile == NULL )
1935         stream.Write(str.mb_str(), str.Len());
1936     else
1937     {
1938         wxString str2(str.wc_str(*convMem), *convFile);
1939         stream.Write(str2.mb_str(), str2.Len());
1940     }
1941 #endif
1942 }
1943 
OutputIndentation(wxOutputStream & stream,int indent)1944 void wxRichTextXMLHelper::OutputIndentation(wxOutputStream& stream, int indent)
1945 {
1946     wxString str = wxT("\n");
1947     for (int i = 0; i < indent; i++)
1948         str << wxT(' ') << wxT(' ');
1949     OutputString(stream, str, NULL, NULL);
1950 }
1951 
1952 // Same as above, but create entities first.
1953 // Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
OutputStringEnt(wxOutputStream & stream,const wxString & str,wxMBConv * convMem,wxMBConv * convFile)1954 void wxRichTextXMLHelper::OutputStringEnt(wxOutputStream& stream, const wxString& str,
1955                             wxMBConv *convMem, wxMBConv *convFile)
1956 {
1957     wxString buf;
1958     size_t i, last, len;
1959     wxChar c;
1960 
1961     len = str.Len();
1962     last = 0;
1963     for (i = 0; i < len; i++)
1964     {
1965         c = str.GetChar(i);
1966 
1967         // Original code excluded "&amp;" but we _do_ want to convert
1968         // the ampersand beginning &amp; because otherwise when read in,
1969         // the original "&amp;" becomes "&".
1970 
1971         if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
1972             (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
1973         {
1974             OutputString(stream, str.Mid(last, i - last), convMem, convFile);
1975             switch (c)
1976             {
1977             case wxT('<'):
1978                 OutputString(stream, wxT("&lt;"), NULL, NULL);
1979                 break;
1980             case wxT('>'):
1981                 OutputString(stream, wxT("&gt;"), NULL, NULL);
1982                 break;
1983             case wxT('&'):
1984                 OutputString(stream, wxT("&amp;"), NULL, NULL);
1985                 break;
1986             case wxT('"'):
1987                 OutputString(stream, wxT("&quot;"), NULL, NULL);
1988                 break;
1989             default: break;
1990             }
1991             last = i + 1;
1992         }
1993         else if (wxUChar(c) > 127)
1994         {
1995             OutputString(stream, str.Mid(last, i - last), convMem, convFile);
1996 
1997             wxString s(wxT("&#"));
1998 #if wxUSE_UNICODE
1999             s << (int) c;
2000 #else
2001             s << (int) wxUChar(c);
2002 #endif
2003             s << wxT(";");
2004             OutputString(stream, s, NULL, NULL);
2005             last = i + 1;
2006         }
2007     }
2008     OutputString(stream, str.Mid(last, i - last), convMem, convFile);
2009 }
2010 
OutputString(wxOutputStream & stream,const wxString & str)2011 void wxRichTextXMLHelper::OutputString(wxOutputStream& stream, const wxString& str)
2012 {
2013     OutputString(stream, str, m_convMem, m_convFile);
2014 }
2015 
OutputStringEnt(wxOutputStream & stream,const wxString & str)2016 void wxRichTextXMLHelper::OutputStringEnt(wxOutputStream& stream, const wxString& str)
2017 {
2018     OutputStringEnt(stream, str, m_convMem, m_convFile);
2019 }
2020 
AddAttribute(wxString & str,const wxString & name,const int & v)2021 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const int& v)
2022 {
2023     str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%d"), v) << wxT("\"");
2024 }
2025 
AddAttribute(wxString & str,const wxString & name,const long & v)2026 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const long& v)
2027 {
2028     str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%ld"), v) << wxT("\"");
2029 }
2030 
AddAttribute(wxString & str,const wxString & name,const double & v)2031 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const double& v)
2032 {
2033     str << wxS(" ") << name << wxS("=\"") << wxString::Format(wxS("%.2f"), v) << wxS("\"");
2034 }
2035 
AddAttribute(wxString & str,const wxString & name,const wxChar * s)2036 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const wxChar* s)
2037 {
2038     str << wxT(" ") << name << wxT("=\"") << s << wxT("\"");
2039 }
2040 
AddAttribute(wxString & str,const wxString & name,const wxString & s)2041 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const wxString& s)
2042 {
2043     str << wxT(" ") << name << wxT("=\"") << s << wxT("\"");
2044 }
2045 
AddAttribute(wxString & str,const wxString & name,const wxColour & col)2046 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const wxColour& col)
2047 {
2048     str << wxT(" ") << name << wxT("=\"") << wxT("#") << ColourToHexString(col) << wxT("\"");
2049 }
2050 
AddAttribute(wxString & str,const wxString & name,const wxTextAttrDimension & dim)2051 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& name, const wxTextAttrDimension& dim)
2052 {
2053     if (dim.IsValid())
2054     {
2055         wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString((int) dim.GetFlags());
2056         str << wxT(" ") << name << wxT("=\"");
2057         str << value;
2058         str << wxT("\"");
2059     }
2060 }
2061 
AddAttribute(wxString & str,const wxString & rootName,const wxTextAttrDimensions & dims)2062 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& rootName, const wxTextAttrDimensions& dims)
2063 {
2064     if (dims.GetLeft().IsValid())
2065         AddAttribute(str, rootName + wxString(wxT("-left")), dims.GetLeft());
2066     if (dims.GetRight().IsValid())
2067         AddAttribute(str, rootName + wxString(wxT("-right")), dims.GetRight());
2068     if (dims.GetTop().IsValid())
2069         AddAttribute(str, rootName + wxString(wxT("-top")), dims.GetTop());
2070     if (dims.GetBottom().IsValid())
2071         AddAttribute(str, rootName + wxString(wxT("-bottom")), dims.GetBottom());
2072 }
2073 
AddAttribute(wxString & str,const wxString & rootName,const wxTextAttrBorder & border)2074 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& rootName, const wxTextAttrBorder& border)
2075 {
2076     if (border.HasStyle())
2077         AddAttribute(str, rootName + wxString(wxT("-style")), border.GetStyle());
2078     if (border.HasColour())
2079         AddAttribute(str, rootName + wxString(wxT("-color")), border.GetColour());
2080     if (border.HasWidth())
2081         AddAttribute(str, rootName + wxString(wxT("-width")), border.GetWidth());
2082 }
2083 
AddAttribute(wxString & str,const wxString & rootName,const wxTextAttrBorders & borders)2084 void wxRichTextXMLHelper::AddAttribute(wxString& str, const wxString& rootName, const wxTextAttrBorders& borders)
2085 {
2086     AddAttribute(str, rootName + wxString(wxT("-left")), borders.GetLeft());
2087     AddAttribute(str, rootName + wxString(wxT("-right")), borders.GetRight());
2088     AddAttribute(str, rootName + wxString(wxT("-top")), borders.GetTop());
2089     AddAttribute(str, rootName + wxString(wxT("-bottom")), borders.GetBottom());
2090 }
2091 
2092 /// Create a string containing style attributes
AddAttributes(const wxRichTextAttr & attr,bool isPara)2093 wxString wxRichTextXMLHelper::AddAttributes(const wxRichTextAttr& attr, bool isPara)
2094 {
2095     wxString str;
2096     if (attr.HasTextColour() && attr.GetTextColour().IsOk())
2097         AddAttribute(str, wxT("textcolor"), attr.GetTextColour());
2098 
2099     if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk())
2100         AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour());
2101 
2102     if (attr.HasFontPointSize())
2103         AddAttribute(str, wxT("fontpointsize"), attr.GetFontSize());
2104     else if (attr.HasFontPixelSize())
2105         AddAttribute(str, wxT("fontpixelsize"), attr.GetFontSize());
2106 
2107     if (attr.HasFontFamily())
2108         AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily());
2109 
2110     if (attr.HasFontItalic())
2111         AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle());
2112 
2113     if (attr.HasFontWeight())
2114         AddAttribute(str, wxT("fontweight"), attr.GetFontWeight());
2115 
2116     if (attr.HasFontUnderlined())
2117         AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined());
2118 
2119     if (attr.HasFontFaceName())
2120         AddAttribute(str, wxT("fontface"), AttributeToXML(attr.GetFontFaceName()));
2121 
2122     if (attr.HasTextEffects())
2123     {
2124         AddAttribute(str, wxT("texteffects"), attr.GetTextEffects());
2125         AddAttribute(str, wxT("texteffectflags"), attr.GetTextEffectFlags());
2126     }
2127 
2128     if (!attr.GetCharacterStyleName().empty())
2129         AddAttribute(str, wxT("characterstyle"), AttributeToXML(attr.GetCharacterStyleName()));
2130 
2131     if (attr.HasURL())
2132         AddAttribute(str, wxT("url"), AttributeToXML(attr.GetURL()));
2133 
2134     if (isPara)
2135     {
2136         if (attr.HasAlignment())
2137             AddAttribute(str, wxT("alignment"), (int) attr.GetAlignment());
2138 
2139         if (attr.HasLeftIndent())
2140         {
2141             AddAttribute(str, wxT("leftindent"), (int) attr.GetLeftIndent());
2142             AddAttribute(str, wxT("leftsubindent"), (int) attr.GetLeftSubIndent());
2143         }
2144 
2145         if (attr.HasRightIndent())
2146             AddAttribute(str, wxT("rightindent"), (int) attr.GetRightIndent());
2147 
2148         if (attr.HasParagraphSpacingAfter())
2149             AddAttribute(str, wxT("parspacingafter"), (int) attr.GetParagraphSpacingAfter());
2150 
2151         if (attr.HasParagraphSpacingBefore())
2152             AddAttribute(str, wxT("parspacingbefore"), (int) attr.GetParagraphSpacingBefore());
2153 
2154         if (attr.HasLineSpacing())
2155             AddAttribute(str, wxT("linespacing"), (int) attr.GetLineSpacing());
2156 
2157         if (attr.HasBulletStyle())
2158             AddAttribute(str, wxT("bulletstyle"), (int) attr.GetBulletStyle());
2159 
2160         if (attr.HasBulletNumber())
2161             AddAttribute(str, wxT("bulletnumber"), (int) attr.GetBulletNumber());
2162 
2163         if (attr.HasBulletText())
2164         {
2165             // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
2166             // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
2167             if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
2168                 AddAttribute(str, wxT("bulletsymbol"), (int) (attr.GetBulletText()[0]));
2169             else
2170                 AddAttribute(str, wxT("bullettext"), AttributeToXML(attr.GetBulletText()));
2171 
2172             AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont());
2173         }
2174 
2175         if (attr.HasBulletName())
2176             AddAttribute(str, wxT("bulletname"), AttributeToXML(attr.GetBulletName()));
2177 
2178         if (!attr.GetParagraphStyleName().empty())
2179             AddAttribute(str, wxT("parstyle"), AttributeToXML(attr.GetParagraphStyleName()));
2180 
2181         if (!attr.GetListStyleName().empty())
2182             AddAttribute(str, wxT("liststyle"), AttributeToXML(attr.GetListStyleName()));
2183 
2184         if (!attr.GetTextBoxAttr().GetBoxStyleName().empty())
2185             AddAttribute(str, wxT("boxstyle"), AttributeToXML(attr.GetTextBoxAttr().GetBoxStyleName()));
2186 
2187         if (attr.HasTabs())
2188         {
2189             wxString strTabs;
2190             size_t i;
2191             for (i = 0; i < attr.GetTabs().GetCount(); i++)
2192             {
2193                 if (i > 0) strTabs << wxT(",");
2194                 strTabs << attr.GetTabs()[i];
2195             }
2196             AddAttribute(str, wxT("tabs"), strTabs);
2197         }
2198 
2199         if (attr.HasPageBreak())
2200         {
2201             AddAttribute(str, wxT("pagebreak"), 1);
2202         }
2203 
2204         if (attr.HasOutlineLevel())
2205             AddAttribute(str, wxT("outlinelevel"), (int) attr.GetOutlineLevel());
2206     }
2207 
2208     AddAttribute(str, wxT("margin"), attr.GetTextBoxAttr().GetMargins());
2209     AddAttribute(str, wxT("padding"), attr.GetTextBoxAttr().GetPadding());
2210     AddAttribute(str, wxT("position"), attr.GetTextBoxAttr().GetPosition());
2211     AddAttribute(str, wxT("border"), attr.GetTextBoxAttr().GetBorder());
2212     AddAttribute(str, wxT("outline"), attr.GetTextBoxAttr().GetOutline());
2213     AddAttribute(str, wxT("width"), attr.GetTextBoxAttr().GetWidth());
2214     AddAttribute(str, wxT("height"), attr.GetTextBoxAttr().GetHeight());
2215     AddAttribute(str, wxT("minwidth"), attr.GetTextBoxAttr().GetMinSize().GetWidth());
2216     AddAttribute(str, wxT("minheight"), attr.GetTextBoxAttr().GetMinSize().GetHeight());
2217     AddAttribute(str, wxT("maxwidth"), attr.GetTextBoxAttr().GetMaxSize().GetWidth());
2218     AddAttribute(str, wxT("maxheight"), attr.GetTextBoxAttr().GetMaxSize().GetHeight());
2219     AddAttribute(str, wxT("corner-radius"), attr.GetTextBoxAttr().GetCornerRadius());
2220 
2221     if (attr.GetTextBoxAttr().HasVerticalAlignment())
2222     {
2223         wxString value;
2224         if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)
2225             value = wxT("top");
2226         else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2227             value = wxT("centre");
2228         else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2229             value = wxT("bottom");
2230         else
2231             value = wxT("none");
2232         AddAttribute(str, wxT("verticalalignment"), value);
2233     }
2234 
2235     if (attr.GetTextBoxAttr().HasFloatMode())
2236     {
2237         wxString value;
2238         if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
2239             value = wxT("left");
2240         else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
2241             value = wxT("right");
2242         else
2243             value = wxT("none");
2244         AddAttribute(str, wxT("float"), value);
2245     }
2246 
2247     if (attr.GetTextBoxAttr().HasClearMode())
2248     {
2249         wxString value;
2250         if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
2251             value = wxT("left");
2252         else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
2253             value = wxT("right");
2254         else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
2255             value = wxT("both");
2256         else
2257             value = wxT("none");
2258         AddAttribute(str, wxT("clear"), value);
2259     }
2260 
2261     if (attr.GetTextBoxAttr().HasCollapseBorders())
2262         AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
2263 
2264     if (attr.GetTextBoxAttr().HasWhitespaceMode())
2265         AddAttribute(str, wxT("whitespace-mode"), (int) attr.GetTextBoxAttr().GetWhitespaceMode());
2266     return str;
2267 }
2268 
2269 // Create a string containing style attributes, plus further object 'attributes' (shown, id)
AddAttributes(wxRichTextObject * obj,bool isPara)2270 wxString wxRichTextXMLHelper::AddAttributes(wxRichTextObject* obj, bool isPara)
2271 {
2272     wxString style = AddAttributes(obj->GetAttributes(), isPara);
2273     if (!obj->IsShown())
2274         style << wxT(" show=\"0\"");
2275     return style;
2276 }
2277 
2278 // Write the properties
WriteProperties(wxOutputStream & stream,const wxRichTextProperties & properties,int level)2279 bool wxRichTextXMLHelper::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level)
2280 {
2281     if (properties.GetCount() > 0)
2282     {
2283         level ++;
2284 
2285         OutputIndentation(stream, level);
2286         OutputString(stream, wxT("<properties>"));
2287 
2288         level ++;
2289 
2290         size_t i;
2291         for (i = 0; i < properties.GetCount(); i++)
2292         {
2293             const wxVariant& var = properties[i];
2294             if (!var.IsNull())
2295             {
2296                 const wxString& name = var.GetName();
2297                 wxString value = MakeStringFromProperty(var);
2298 
2299                 OutputIndentation(stream, level);
2300                 OutputString(stream, wxT("<property name=\"") + name +
2301                     wxT("\" type=\"") + var.GetType() + wxT("\" value=\""));
2302                 OutputStringEnt(stream, value);
2303                 OutputString(stream, wxT("\"/>"));
2304             }
2305         }
2306 
2307         level --;
2308 
2309         OutputIndentation(stream, level);
2310         OutputString(stream, wxT("</properties>"));
2311 
2312         level --;
2313     }
2314 
2315     return true;
2316 }
2317 
ExportStyleDefinition(wxOutputStream & stream,wxRichTextStyleDefinition * def,int level)2318 bool wxRichTextXMLHelper::ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level)
2319 {
2320     wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
2321     wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
2322     wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
2323     wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition);
2324 
2325     wxString name = def->GetName();
2326     wxString nameProp;
2327     if (!name.empty())
2328         nameProp = wxT(" name=\"") + AttributeToXML(name) + wxT("\"");
2329 
2330     wxString baseStyle = def->GetBaseStyle();
2331     wxString baseStyleProp;
2332     if (!baseStyle.empty())
2333         baseStyleProp = wxT(" basestyle=\"") + AttributeToXML(baseStyle) + wxT("\"");
2334 
2335     wxString descr = def->GetDescription();
2336     wxString descrProp;
2337     if (!descr.empty())
2338         descrProp = wxT(" description=\"") + AttributeToXML(descr) + wxT("\"");
2339 
2340     if (charDef)
2341     {
2342         OutputIndentation(stream, level);
2343         OutputString(stream, wxT("<characterstyle") + nameProp + baseStyleProp + descrProp + wxT(">"));
2344 
2345         level ++;
2346 
2347         wxString style = AddAttributes(def->GetStyle(), false);
2348 
2349         OutputIndentation(stream, level);
2350         OutputString(stream, wxT("<style ") + style + wxT(">"));
2351 
2352         OutputIndentation(stream, level);
2353         OutputString(stream, wxT("</style>"));
2354 
2355         level --;
2356 
2357         OutputIndentation(stream, level);
2358         OutputString(stream, wxT("</characterstyle>"));
2359     }
2360     else if (listDef)
2361     {
2362         OutputIndentation(stream, level);
2363 
2364         if (!listDef->GetNextStyle().empty())
2365             baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(listDef->GetNextStyle()) << wxT("\"");
2366 
2367         OutputString(stream, wxT("<liststyle") + nameProp + baseStyleProp + descrProp + wxT(">"));
2368 
2369         level ++;
2370 
2371         wxString style = AddAttributes(def->GetStyle(), true);
2372 
2373         OutputIndentation(stream, level);
2374         OutputString(stream, wxT("<style ") + style + wxT(">"));
2375 
2376         OutputIndentation(stream, level);
2377         OutputString(stream, wxT("</style>"));
2378 
2379         int i;
2380         for (i = 0; i < 10; i ++)
2381         {
2382             wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
2383             if (levelAttr)
2384             {
2385                 wxString levelStyle = AddAttributes(def->GetStyle(), true);
2386                 wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1));
2387 
2388                 OutputIndentation(stream, level);
2389                 OutputString(stream, wxT("<style ") + levelStr + levelStyle + wxT(">"));
2390 
2391                 OutputIndentation(stream, level);
2392                 OutputString(stream, wxT("</style>"));
2393             }
2394         }
2395 
2396         level --;
2397 
2398         OutputIndentation(stream, level);
2399         OutputString(stream, wxT("</liststyle>"));
2400     }
2401     else if (paraDef)
2402     {
2403         OutputIndentation(stream, level);
2404 
2405         if (!paraDef->GetNextStyle().empty())
2406             baseStyleProp << wxT(" nextstyle=\"") << AttributeToXML(paraDef->GetNextStyle()) << wxT("\"");
2407 
2408         OutputString(stream, wxT("<paragraphstyle") + nameProp + baseStyleProp + descrProp + wxT(">"));
2409 
2410         level ++;
2411 
2412         wxString style = AddAttributes(def->GetStyle(), true);
2413 
2414         OutputIndentation(stream, level);
2415         OutputString(stream, wxT("<style ") + style + wxT(">"));
2416 
2417         OutputIndentation(stream, level);
2418         OutputString(stream, wxT("</style>"));
2419 
2420         level --;
2421 
2422         OutputIndentation(stream, level);
2423         OutputString(stream, wxT("</paragraphstyle>"));
2424     }
2425     else if (boxDef)
2426     {
2427         OutputIndentation(stream, level);
2428 
2429         OutputString(stream, wxT("<boxstyle") + nameProp + baseStyleProp + descrProp + wxT(">"));
2430 
2431         level ++;
2432 
2433         wxString style = AddAttributes(def->GetStyle(), true);
2434 
2435         OutputIndentation(stream, level);
2436         OutputString(stream, wxT("<style ") + style + wxT(">"));
2437 
2438         OutputIndentation(stream, level);
2439         OutputString(stream, wxT("</style>"));
2440 
2441         level --;
2442 
2443         OutputIndentation(stream, level);
2444         OutputString(stream, wxT("</boxstyle>"));
2445     }
2446 
2447     WriteProperties(stream, def->GetProperties(), level);
2448 
2449     return true;
2450 }
2451 
2452 #endif
2453     // wxRICHTEXT_HAVE_DIRECT_OUTPUT
2454 
2455 
2456 #if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2457 
AddAttribute(wxXmlNode * node,const wxString & name,const int & v)2458 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const int& v)
2459 {
2460     node->AddAttribute(name, MakeString(v));
2461 }
2462 
AddAttribute(wxXmlNode * node,const wxString & name,const long & v)2463 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const long& v)
2464 {
2465     node->AddAttribute(name, MakeString(v));
2466 }
2467 
AddAttribute(wxXmlNode * node,const wxString & name,const double & v)2468 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const double& v)
2469 {
2470     node->AddAttribute(name, MakeString(v));
2471 }
2472 
AddAttribute(wxXmlNode * node,const wxString & name,const wxString & s)2473 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const wxString& s)
2474 {
2475     node->AddAttribute(name, s);
2476 }
2477 
AddAttribute(wxXmlNode * node,const wxString & name,const wxColour & col)2478 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const wxColour& col)
2479 {
2480     node->AddAttribute(name, MakeString(col));
2481 }
2482 
AddAttribute(wxXmlNode * node,const wxString & name,const wxTextAttrDimension & dim)2483 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& name, const wxTextAttrDimension& dim)
2484 {
2485     if (dim.IsValid())
2486     {
2487         wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString(dim.GetFlags());
2488         AddAttribute(node, name, value);
2489     }
2490 }
2491 
AddAttribute(wxXmlNode * node,const wxString & rootName,const wxTextAttrDimensions & dims)2492 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& rootName, const wxTextAttrDimensions& dims)
2493 {
2494     if (dims.GetLeft().IsValid())
2495         AddAttribute(node, rootName + wxString(wxT("-left")), dims.GetLeft());
2496     if (dims.GetRight().IsValid())
2497         AddAttribute(node, rootName + wxString(wxT("-right")), dims.GetRight());
2498     if (dims.GetTop().IsValid())
2499         AddAttribute(node, rootName + wxString(wxT("-top")), dims.GetTop());
2500     if (dims.GetBottom().IsValid())
2501         AddAttribute(node, rootName + wxString(wxT("-bottom")), dims.GetBottom());
2502 }
2503 
AddAttribute(wxXmlNode * node,const wxString & rootName,const wxTextAttrBorder & border)2504 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& rootName, const wxTextAttrBorder& border)
2505 {
2506     if (border.HasStyle())
2507         AddAttribute(node, rootName + wxString(wxT("-style")), border.GetStyle());
2508     if (border.HasColour())
2509         AddAttribute(node, rootName + wxString(wxT("-color")), border.GetColour());
2510     if (border.HasWidth())
2511         AddAttribute(node, rootName + wxString(wxT("-width")), border.GetWidth());
2512 }
2513 
AddAttribute(wxXmlNode * node,const wxString & rootName,const wxTextAttrBorders & borders)2514 void wxRichTextXMLHelper::AddAttribute(wxXmlNode* node, const wxString& rootName, const wxTextAttrBorders& borders)
2515 {
2516     AddAttribute(node, rootName + wxString(wxT("-left")), borders.GetLeft());
2517     AddAttribute(node, rootName + wxString(wxT("-right")), borders.GetRight());
2518     AddAttribute(node, rootName + wxString(wxT("-top")), borders.GetTop());
2519     AddAttribute(node, rootName + wxString(wxT("-bottom")), borders.GetBottom());
2520 }
2521 
ExportStyleDefinition(wxXmlNode * parent,wxRichTextStyleDefinition * def)2522 bool wxRichTextXMLHelper::ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def)
2523 {
2524     wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
2525     wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
2526     wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition);
2527     wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
2528 
2529     wxString baseStyle = def->GetBaseStyle();
2530     wxString descr = def->GetDescription();
2531 
2532     wxXmlNode* defNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxEmptyString);
2533     parent->AddChild(defNode);
2534     if (!baseStyle.empty())
2535         defNode->AddAttribute(wxT("basestyle"), baseStyle);
2536     if (!descr.empty())
2537         defNode->AddAttribute(wxT("description"), descr);
2538 
2539     wxXmlNode* styleNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
2540     defNode->AddChild(styleNode);
2541 
2542     if (charDef)
2543     {
2544         defNode->SetName(wxT("characterstyle"));
2545         AddAttributes(styleNode, def->GetStyle(), false);
2546     }
2547     else if (listDef)
2548     {
2549         defNode->SetName(wxT("liststyle"));
2550 
2551         if (!listDef->GetNextStyle().empty())
2552             defNode->AddAttribute(wxT("nextstyle"), listDef->GetNextStyle());
2553 
2554         AddAttributes(styleNode, def->GetStyle(), true);
2555 
2556         int i;
2557         for (i = 0; i < 10; i ++)
2558         {
2559             wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
2560             if (levelAttr)
2561             {
2562                 wxXmlNode* levelNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
2563                 defNode->AddChild(levelNode);
2564                 levelNode->AddAttribute(wxT("level"), MakeString(i+1));
2565                 AddAttributes(levelNode, * levelAttr, true);
2566             }
2567         }
2568     }
2569     else if (boxDef)
2570     {
2571         defNode->SetName(wxT("boxstyle"));
2572 
2573         AddAttributes(styleNode, def->GetStyle(), true);
2574     }
2575     else if (paraDef)
2576     {
2577         defNode->SetName(wxT("paragraphstyle"));
2578 
2579         if (!paraDef->GetNextStyle().empty())
2580             defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle());
2581 
2582         AddAttributes(styleNode, def->GetStyle(), true);
2583     }
2584 
2585     WriteProperties(defNode, def->GetProperties());
2586 
2587     return true;
2588 }
2589 
AddAttributes(wxXmlNode * node,wxRichTextAttr & attr,bool isPara)2590 bool wxRichTextXMLHelper::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara)
2591 {
2592     if (attr.HasTextColour() && attr.GetTextColour().IsOk())
2593         node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour()));
2594     if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk())
2595         node->AddAttribute(wxT("bgcolor"), MakeString(attr.GetBackgroundColour()));
2596 
2597     if (attr.HasFontPointSize())
2598         node->AddAttribute(wxT("fontpointsize"), MakeString(attr.GetFontSize()));
2599     else if (attr.HasFontPixelSize())
2600         node->AddAttribute(wxT("fontpixelsize"), MakeString(attr.GetFontSize()));
2601     if (attr.HasFontFamily())
2602         node->AddAttribute(wxT("fontfamily"), MakeString(attr.GetFontFamily()));
2603     if (attr.HasFontItalic())
2604         node->AddAttribute(wxT("fontstyle"), MakeString(attr.GetFontStyle()));
2605     if (attr.HasFontWeight())
2606         node->AddAttribute(wxT("fontweight"), MakeString(attr.GetFontWeight()));
2607     if (attr.HasFontUnderlined())
2608         node->AddAttribute(wxT("fontunderlined"), MakeString((int) attr.GetFontUnderlined()));
2609     if (attr.HasFontFaceName())
2610         node->AddAttribute(wxT("fontface"), attr.GetFontFaceName());
2611 
2612     if (attr.HasTextEffects())
2613     {
2614         node->AddAttribute(wxT("texteffects"), MakeString(attr.GetTextEffects()));
2615         node->AddAttribute(wxT("texteffectflags"), MakeString(attr.GetTextEffectFlags()));
2616     }
2617     if (attr.HasCharacterStyleName() && !attr.GetCharacterStyleName().empty())
2618         node->AddAttribute(wxT("characterstyle"), attr.GetCharacterStyleName());
2619 
2620     if (attr.HasURL())
2621         node->AddAttribute(wxT("url"), attr.GetURL()); // TODO: do we need to wrap this in AttributeToXML?
2622 
2623     if (isPara)
2624     {
2625         if (attr.HasAlignment())
2626             node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment()));
2627 
2628         if (attr.HasLeftIndent())
2629         {
2630             node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent()));
2631             node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent()));
2632         }
2633 
2634         if (attr.HasRightIndent())
2635             node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent()));
2636 
2637         if (attr.HasParagraphSpacingAfter())
2638             node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter()));
2639 
2640         if (attr.HasParagraphSpacingBefore())
2641             node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore()));
2642 
2643         if (attr.HasLineSpacing())
2644             node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing()));
2645 
2646         if (attr.HasBulletStyle())
2647             node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle()));
2648 
2649         if (attr.HasBulletNumber())
2650             node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber()));
2651 
2652         if (attr.HasBulletText())
2653         {
2654             // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
2655             // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
2656             if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
2657                 node->AddAttribute(wxT("bulletsymbol"), MakeString((int) (attr.GetBulletText()[0])));
2658             else
2659                 node->AddAttribute(wxT("bullettext"), attr.GetBulletText());
2660 
2661             if (!attr.GetBulletFont().empty())
2662                 node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont());
2663         }
2664 
2665         if (attr.HasBulletName())
2666             node->AddAttribute(wxT("bulletname"), attr.GetBulletName());
2667 
2668         if (!attr.GetParagraphStyleName().empty())
2669             node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName());
2670 
2671         if (!attr.GetListStyleName().empty())
2672             node->AddAttribute(wxT("liststyle"), attr.GetListStyleName());
2673 
2674         if (!attr.GetTextBoxAttr().GetBoxStyleName().empty())
2675             node->AddAttribute(wxT("boxstyle"), attr.GetTextBoxAttr().GetBoxStyleName());
2676 
2677         if (attr.HasTabs())
2678         {
2679             wxString tabs;
2680             size_t i;
2681             for (i = 0; i < attr.GetTabs().GetCount(); i++)
2682             {
2683                 if (i > 0)
2684                     tabs << wxT(",");
2685                 tabs << attr.GetTabs()[i];
2686             }
2687             node->AddAttribute(wxT("tabs"), tabs);
2688         }
2689 
2690         if (attr.HasPageBreak())
2691             node->AddAttribute(wxT("pagebreak"), wxT("1"));
2692 
2693         if (attr.HasOutlineLevel())
2694             node->AddAttribute(wxT("outlinelevel"), MakeString((int) attr.GetOutlineLevel()));
2695     }
2696 
2697     AddAttribute(node, wxT("margin"), attr.GetTextBoxAttr().GetMargins());
2698     AddAttribute(node, wxT("padding"), attr.GetTextBoxAttr().GetPadding());
2699     AddAttribute(node, wxT("position"), attr.GetTextBoxAttr().GetPosition());
2700     AddAttribute(node, wxT("border"), attr.GetTextBoxAttr().GetBorder());
2701     AddAttribute(node, wxT("outline"), attr.GetTextBoxAttr().GetOutline());
2702     AddAttribute(node, wxT("width"), attr.GetTextBoxAttr().GetWidth());
2703     AddAttribute(node, wxT("height"), attr.GetTextBoxAttr().GetHeight());
2704     AddAttribute(node, wxT("minwidth"), attr.GetTextBoxAttr().GetMinSize().GetWidth());
2705     AddAttribute(node, wxT("minheight"), attr.GetTextBoxAttr().GetMinSize().GetHeight());
2706     AddAttribute(node, wxT("maxwidth"), attr.GetTextBoxAttr().GetMaxSize().GetWidth());
2707     AddAttribute(node, wxT("maxheight"), attr.GetTextBoxAttr().GetMaxSize().GetHeight());
2708     AddAttribute(node, wxT("corner-radius"), attr.GetTextBoxAttr().GetCornerRadius());
2709 
2710     if (attr.GetTextBoxAttr().HasVerticalAlignment())
2711     {
2712         wxString value;
2713         if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)
2714             value = wxT("top");
2715         else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2716             value = wxT("centre");
2717         else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2718             value = wxT("bottom");
2719         else
2720             value = wxT("none");
2721         AddAttribute(node, wxT("verticalalignment"), value);
2722     }
2723 
2724     if (attr.GetTextBoxAttr().HasFloatMode())
2725     {
2726         wxString value;
2727         if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
2728             value = wxT("left");
2729         else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
2730             value = wxT("right");
2731         else
2732             value = wxT("none");
2733         AddAttribute(node, wxT("float"), value);
2734     }
2735 
2736     if (attr.GetTextBoxAttr().HasClearMode())
2737     {
2738         wxString value;
2739         if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
2740             value = wxT("left");
2741         else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
2742             value = wxT("right");
2743         else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
2744             value = wxT("both");
2745         else
2746             value = wxT("none");
2747         AddAttribute(node, wxT("clear"), value);
2748     }
2749 
2750     if (attr.GetTextBoxAttr().HasCollapseBorders())
2751         AddAttribute(node, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
2752 
2753     if (attr.GetTextBoxAttr().HasWhitespaceMode())
2754         AddAttribute(node, wxT("whitespace-mode"), (int) attr.GetTextBoxAttr().GetWhitespaceMode());
2755 
2756     return true;
2757 }
2758 
AddAttributes(wxXmlNode * node,wxRichTextObject * obj,bool isPara)2759 bool wxRichTextXMLHelper::AddAttributes(wxXmlNode* node, wxRichTextObject* obj, bool isPara)
2760 {
2761     if (obj)
2762     {
2763         if (!obj->IsShown())
2764             node->AddAttribute(wxT("show"), wxT("0"));
2765     }
2766 
2767     return AddAttributes(node, obj->GetAttributes(), isPara);
2768 }
2769 
WriteProperties(wxXmlNode * node,const wxRichTextProperties & properties)2770 bool wxRichTextXMLHelper::WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties)
2771 {
2772     if (properties.GetCount() > 0)
2773     {
2774         wxXmlNode* propertiesNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("properties"));
2775         node->AddChild(propertiesNode);
2776         size_t i;
2777         for (i = 0; i < properties.GetCount(); i++)
2778         {
2779             const wxVariant& var = properties[i];
2780             if (!var.IsNull())
2781             {
2782                 wxXmlNode* propertyNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property"));
2783                 propertiesNode->AddChild(propertyNode);
2784 
2785                 const wxString& name = var.GetName();
2786                 wxString value = MakeStringFromProperty(var);
2787 
2788                 AddAttribute(propertyNode, wxT("name"), name);
2789                 AddAttribute(propertyNode, wxT("type"), var.GetType());
2790                 AddAttribute(propertyNode, wxT("value"), value);
2791             }
2792         }
2793     }
2794     return true;
2795 }
2796 
2797 #endif
2798     // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2799 
2800 #endif
2801     // wxUSE_RICHTEXT && wxUSE_XML
2802 
2803