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