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