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