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