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