]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextxml.cpp
Fixed #13833: wxRichTextCtrl: 'Copy' can grab an extra character
[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
JS
1195 AddAttribute(str, wxT("height"), attr.GetTextBoxAttr().GetHeight());
1196
1197 if (attr.GetTextBoxAttr().HasVerticalAlignment())
1198 {
1199 wxString value;
1200 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)
1201 value = wxT("top");
1202 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
1203 value = wxT("centre");
1204 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
1205 value = wxT("bottom");
1206 else
1207 value = wxT("none");
1208 AddAttribute(str, wxT("verticalalignment"), value);
1209 }
1210
bec80f4f
JS
1211 if (attr.GetTextBoxAttr().HasFloatMode())
1212 {
1213 wxString value;
1214 if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
1215 value = wxT("left");
1216 else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
1217 value = wxT("right");
1218 else
1219 value = wxT("none");
1220 AddAttribute(str, wxT("float"), value);
1221 }
4d6d8bf4 1222
bec80f4f
JS
1223 if (attr.GetTextBoxAttr().HasClearMode())
1224 {
1225 wxString value;
1226 if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
1227 value = wxT("left");
2be72ac2 1228 else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
bec80f4f 1229 value = wxT("right");
2be72ac2 1230 else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
bec80f4f
JS
1231 value = wxT("both");
1232 else
1233 value = wxT("none");
1234 AddAttribute(str, wxT("clear"), value);
5d7836c4
JS
1235 }
1236
bec80f4f
JS
1237 if (attr.GetTextBoxAttr().HasCollapseBorders())
1238 AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
1239
5d7836c4
JS
1240 return str;
1241}
1242
bec80f4f
JS
1243// Make a string from the given property. This can be overridden for custom variants.
1244wxString wxRichTextXMLHandler::MakeStringFromProperty(const wxVariant& var)
a2beab22 1245{
bec80f4f
JS
1246 return var.MakeString();
1247}
a2beab22 1248
bec80f4f
JS
1249// Create a proprty from the string read from the XML file.
1250wxVariant wxRichTextXMLHandler::MakePropertyFromString(const wxString& name, const wxString& value, const wxString& WXUNUSED(type))
1251{
1252 wxVariant var(value, name);
1253 // TODO: use type to create using common types
1254 return var;
1255}
1256
1257// Write the properties
1258bool wxRichTextXMLHandler::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level)
1259{
1260 if (properties.GetCount() > 0)
a2beab22 1261 {
bec80f4f
JS
1262 level ++;
1263
1264 OutputIndentation(stream, level);
1265 OutputString(stream, wxT("<properties"));
1266
1267 level ++;
1268
1269 size_t i;
1270 for (i = 0; i < properties.GetCount(); i++)
1271 {
1272 const wxVariant& var = properties[i];
1273 if (!var.IsNull())
706465df 1274 {
bec80f4f
JS
1275 const wxString& name = var.GetName();
1276 wxString value = MakeStringFromProperty(var);
1277
1278 OutputIndentation(stream, level);
1279 OutputString(stream, wxT("<property name=\"") + name +
1280 wxT("\" type=\"") + var.GetType() + wxT("\" value=\""));
1281 OutputStringEnt(stream, value);
1282 OutputString(stream, wxT("\"/>\n"));
1283 }
1284 }
1285
1286 level --;
1287
1288 OutputIndentation(stream, level);
1289 OutputString(stream, wxT("</properties>\n"));
1290
1291 level --;
a2beab22 1292 }
bec80f4f
JS
1293
1294 return true;
a2beab22
JS
1295}
1296
bec80f4f
JS
1297
1298#endif
1299 // wxRICHTEXT_HAVE_DIRECT_OUTPUT
1300
1301#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
1302bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj)
5d7836c4 1303{
bec80f4f 1304 obj.ExportXML(parent, this);
87eaa6f6 1305
bec80f4f
JS
1306 return true;
1307}
0ca07313 1308
bec80f4f
JS
1309bool wxRichTextXMLHandler::ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def)
1310{
1311 wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
1312 wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
603f702b 1313 wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition);
bec80f4f 1314 wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
a2beab22 1315
bec80f4f
JS
1316 wxString baseStyle = def->GetBaseStyle();
1317 wxString descr = def->GetDescription();
706465df 1318
bec80f4f
JS
1319 wxXmlNode* defNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxEmptyString);
1320 parent->AddChild(defNode);
1321 if (!baseStyle.empty())
1322 defNode->AddAttribute(wxT("basestyle"), baseStyle);
1323 if (!descr.empty())
1324 defNode->AddAttribute(wxT("description"), descr);
706465df 1325
bec80f4f
JS
1326 wxXmlNode* styleNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
1327 defNode->AddChild(styleNode);
706465df 1328
bec80f4f 1329 if (charDef)
9c4cb611 1330 {
bec80f4f
JS
1331 defNode->SetName(wxT("characterstyle"));
1332 AddAttributes(styleNode, def->GetStyle(), false);
9c4cb611 1333 }
bec80f4f 1334 else if (listDef)
0ca07313 1335 {
bec80f4f 1336 defNode->SetName(wxT("liststyle"));
5d7836c4 1337
bec80f4f
JS
1338 if (!listDef->GetNextStyle().empty())
1339 defNode->AddAttribute(wxT("nextstyle"), listDef->GetNextStyle());
5d7836c4 1340
bec80f4f 1341 AddAttributes(styleNode, def->GetStyle(), true);
5d7836c4 1342
bec80f4f
JS
1343 int i;
1344 for (i = 0; i < 10; i ++)
1345 {
1346 wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
1347 if (levelAttr)
1348 {
1349 wxXmlNode* levelNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
1350 defNode->AddChild(levelNode);
1351 levelNode->AddAttribute(wxT("level"), MakeString(i+1));
1352 AddAttributes(levelNode, * levelAttr, true);
1353 }
1354 }
0ca07313 1355 }
603f702b
JS
1356 else if (boxDef)
1357 {
1358 defNode->SetName(wxT("boxstyle"));
1359
1360 AddAttributes(styleNode, def->GetStyle(), true);
1361 }
bec80f4f 1362 else if (paraDef)
5d7836c4 1363 {
bec80f4f 1364 defNode->SetName(wxT("paragraphstyle"));
5d7836c4 1365
bec80f4f
JS
1366 if (!paraDef->GetNextStyle().empty())
1367 defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle());
1368
1369 AddAttributes(styleNode, def->GetStyle(), true);
5d7836c4
JS
1370 }
1371
bec80f4f
JS
1372 return true;
1373}
5d7836c4 1374
bec80f4f
JS
1375bool wxRichTextXMLHandler::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara)
1376{
a1b806b9 1377 if (attr.HasTextColour() && attr.GetTextColour().IsOk())
bec80f4f 1378 node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour()));
a1b806b9 1379 if (attr.HasBackgroundColour() && attr.GetBackgroundColour().IsOk())
bec80f4f 1380 node->AddAttribute(wxT("bgcolor"), MakeString(attr.GetBackgroundColour()));
42688aea 1381
bec80f4f
JS
1382 if (attr.HasFontSize())
1383 node->AddAttribute(wxT("fontsize"), MakeString(attr.GetFontSize()));
1384 if (attr.HasFontFamily())
1385 node->AddAttribute(wxT("fontfamily"), MakeString(attr.GetFontFamily()));
1386 if (attr.HasFontItalic())
1387 node->AddAttribute(wxT("fontstyle"), MakeString(attr.GetFontStyle()));
1388 if (attr.HasFontWeight())
1389 node->AddAttribute(wxT("fontweight"), MakeString(attr.GetFontWeight()));
1390 if (attr.HasFontUnderlined())
1391 node->AddAttribute(wxT("fontunderlined"), MakeString((int) attr.GetFontUnderlined()));
1392 if (attr.HasFontFaceName())
1393 node->AddAttribute(wxT("fontface"), attr.GetFontFaceName());
1394
1395 if (attr.HasTextEffects())
1f65137f 1396 {
bec80f4f
JS
1397 node->AddAttribute(wxT("texteffects"), MakeString(attr.GetTextEffects()));
1398 node->AddAttribute(wxT("texteffectflags"), MakeString(attr.GetTextEffectFlags()));
1f65137f 1399 }
bec80f4f
JS
1400 if (attr.HasCharacterStyleName() && !attr.GetCharacterStyleName().empty())
1401 node->AddAttribute(wxT("characterstyle"), attr.GetCharacterStyleName());
1f65137f 1402
bec80f4f
JS
1403 if (attr.HasURL())
1404 node->AddAttribute(wxT("url"), attr.GetURL()); // TODO: do we need to wrap this in AttributeToXML?
03cd2124 1405
5d7836c4
JS
1406 if (isPara)
1407 {
bec80f4f
JS
1408 if (attr.HasAlignment())
1409 node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment()));
0ca07313 1410
bec80f4f 1411 if (attr.HasLeftIndent())
0ca07313 1412 {
bec80f4f
JS
1413 node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent()));
1414 node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent()));
0ca07313
JS
1415 }
1416
bec80f4f
JS
1417 if (attr.HasRightIndent())
1418 node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent()));
5d7836c4 1419
bec80f4f
JS
1420 if (attr.HasParagraphSpacingAfter())
1421 node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter()));
5d7836c4 1422
bec80f4f
JS
1423 if (attr.HasParagraphSpacingBefore())
1424 node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore()));
5d7836c4 1425
bec80f4f
JS
1426 if (attr.HasLineSpacing())
1427 node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing()));
5d7836c4 1428
bec80f4f
JS
1429 if (attr.HasBulletStyle())
1430 node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle()));
5d7836c4 1431
bec80f4f
JS
1432 if (attr.HasBulletNumber())
1433 node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber()));
5d7836c4 1434
bec80f4f 1435 if (attr.HasBulletText())
d2d0adc7 1436 {
bec80f4f
JS
1437 // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
1438 // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
1439 if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
1440 node->AddAttribute(wxT("bulletsymbol"), MakeString((int) (attr.GetBulletText()[0])));
1441 else
1442 node->AddAttribute(wxT("bullettext"), attr.GetBulletText());
7b907278 1443
bec80f4f
JS
1444 if (!attr.GetBulletFont().empty())
1445 node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont());
1446 }
5d7836c4 1447
bec80f4f
JS
1448 if (attr.HasBulletName())
1449 node->AddAttribute(wxT("bulletname"), attr.GetBulletName());
f089713f 1450
bec80f4f
JS
1451 if (!attr.GetParagraphStyleName().empty())
1452 node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName());
87eaa6f6 1453
bec80f4f
JS
1454 if (!attr.GetListStyleName().empty())
1455 node->AddAttribute(wxT("liststyle"), attr.GetListStyleName());
87eaa6f6 1456
2f987d83
JS
1457 if (!attr.GetTextBoxAttr().GetBoxStyleName().empty())
1458 node->AddAttribute(wxT("boxstyle"), attr.GetTextBoxAttr().GetBoxStyleName());
1459
bec80f4f 1460 if (attr.HasTabs())
0ca07313 1461 {
bec80f4f
JS
1462 wxString tabs;
1463 size_t i;
1464 for (i = 0; i < attr.GetTabs().GetCount(); i++)
0ca07313 1465 {
bec80f4f
JS
1466 if (i > 0)
1467 tabs << wxT(",");
1468 tabs << attr.GetTabs()[i];
0ca07313 1469 }
bec80f4f 1470 node->AddAttribute(wxT("tabs"), tabs);
0ca07313 1471 }
87eaa6f6 1472
bec80f4f
JS
1473 if (attr.HasPageBreak())
1474 node->AddAttribute(wxT("pagebreak"), wxT("1"));
4d6d8bf4 1475
bec80f4f
JS
1476 if (attr.HasOutlineLevel())
1477 node->AddAttribute(wxT("outlinelevel"), MakeString((int) attr.GetOutlineLevel()));
1478 }
1479
1480 AddAttribute(node, wxT("margin"), attr.GetTextBoxAttr().GetMargins());
1481 AddAttribute(node, wxT("padding"), attr.GetTextBoxAttr().GetPadding());
1482 AddAttribute(node, wxT("position"), attr.GetTextBoxAttr().GetPosition());
1483 AddAttribute(node, wxT("border"), attr.GetTextBoxAttr().GetBorder());
1484 AddAttribute(node, wxT("outline"), attr.GetTextBoxAttr().GetOutline());
1485 AddAttribute(node, wxT("width"), attr.GetTextBoxAttr().GetWidth());
706465df
JS
1486 AddAttribute(node, wxT("height"), attr.GetTextBoxAttr().GetHeight());
1487
1488 if (attr.GetTextBoxAttr().HasVerticalAlignment())
1489 {
1490 wxString value;
1491 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP)
1492 value = wxT("top");
1493 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
1494 value = wxT("centre");
1495 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
1496 value = wxT("bottom");
1497 else
1498 value = wxT("none");
1499 AddAttribute(node, wxT("verticalalignment"), value);
1500 }
bec80f4f
JS
1501
1502 if (attr.GetTextBoxAttr().HasFloatMode())
1503 {
1504 wxString value;
1505 if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
1506 value = wxT("left");
1507 else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
1508 value = wxT("right");
1509 else
1510 value = wxT("none");
1511 AddAttribute(node, wxT("float"), value);
5d7836c4
JS
1512 }
1513
bec80f4f
JS
1514 if (attr.GetTextBoxAttr().HasClearMode())
1515 {
1516 wxString value;
1517 if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
1518 value = wxT("left");
2be72ac2 1519 else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
bec80f4f 1520 value = wxT("right");
2be72ac2 1521 else if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
bec80f4f
JS
1522 value = wxT("both");
1523 else
1524 value = wxT("none");
1525 AddAttribute(node, wxT("clear"), value);
1526 }
1527
1528 if (attr.GetTextBoxAttr().HasCollapseBorders())
1529 AddAttribute(node, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
1530
5d7836c4
JS
1531 return true;
1532}
1533
bec80f4f
JS
1534bool wxRichTextXMLHandler::WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties)
1535{
1536 if (properties.GetCount() > 0)
1537 {
1538 wxXmlNode* propertiesNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("properties"));
1539 node->AddChild(propertiesNode);
1540 size_t i;
1541 for (i = 0; i < properties.GetCount(); i++)
1542 {
1543 const wxVariant& var = properties[i];
1544 if (!var.IsNull())
1545 {
1546 wxXmlNode* propertyNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property"));
1547 propertiesNode->AddChild(propertyNode);
1548
1549 const wxString& name = var.GetName();
1550 wxString value = MakeStringFromProperty(var);
1551
1552 AddAttribute(propertyNode, wxT("name"), name);
1553 AddAttribute(propertyNode, wxT("type"), var.GetType());
1554 AddAttribute(propertyNode, wxT("value"), value);
1555 }
1556 }
1557 }
1558 return true;
1559}
1560
1561#endif
1562 // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
1563
1564/// Replace face name with current name for platform.
1565/// TODO: introduce a virtual function or settable table to
1566/// do this comprehensively.
1567bool wxRichTextFixFaceName(wxString& facename)
1568{
1569 if (facename.empty())
1570 return false;
1571
1572#ifdef __WXMSW__
1573 if (facename == wxT("Times"))
1574 {
1575 facename = wxT("Times New Roman");
1576 return true;
1577 }
1578 else if (facename == wxT("Helvetica"))
1579 {
1580 facename = wxT("Arial");
1581 return true;
1582 }
1583 else if (facename == wxT("Courier"))
1584 {
1585 facename = wxT("Courier New");
1586 return true;
1587 }
1588 else
1589 return false;
1590#else
1591 if (facename == wxT("Times New Roman"))
1592 {
1593 facename = wxT("Times");
1594 return true;
1595 }
1596 else if (facename == wxT("Arial"))
1597 {
1598 facename = wxT("Helvetica");
1599 return true;
1600 }
1601 else if (facename == wxT("Courier New"))
1602 {
1603 facename = wxT("Courier");
1604 return true;
1605 }
1606 else
1607 return false;
1608#endif
1609}
1610
1611static inline long wxRichTextColourStringToLong(const wxString& colStr)
1612{
1613 if (!colStr.IsEmpty())
1614 {
1615 wxColour col(colStr);
1616 return col.GetRGB();
1617 }
1618 else
1619 return 0;
1620}
1621
1622static inline wxTextAttrDimension wxRichTextParseDimension(const wxString& dimStr)
1623{
1624 wxString valuePart = dimStr.BeforeFirst(wxT(','));
1625 wxString flagsPart;
1626 if (dimStr.Contains(wxT(",")))
1627 flagsPart = dimStr.AfterFirst(wxT(','));
1628 wxTextAttrDimension dim;
1629 dim.SetValue(wxAtoi(valuePart));
1630 dim.SetFlags(wxAtoi(flagsPart));
1631
1632 return dim;
1633}
1634
1635/// Import style parameters
1636bool wxRichTextXMLHandler::ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara)
1637{
1638 wxXmlAttribute* xmlAttr = node->GetAttributes();
1639 bool found;
1640 while (xmlAttr)
1641 {
1642 const wxString& name = xmlAttr->GetName();
1643 const wxString& value = xmlAttr->GetValue();
1644 found = true;
1645
1646 if (name == wxT("fontface"))
1647 {
1648 if (!value.empty())
1649 {
1650 wxString v = value;
1651 if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES)
1652 wxRichTextFixFaceName(v);
1653 attr.SetFontFaceName(v);
1654 }
1655 }
1656 else if (name == wxT("fontfamily"))
1657 {
1658 if (!value.empty())
1659 attr.SetFontFamily((wxFontFamily)wxAtoi(value));
1660 }
1661 else if (name == wxT("fontstyle"))
1662 {
1663 if (!value.empty())
1664 attr.SetFontStyle((wxFontStyle)wxAtoi(value));
1665 }
1666 else if (name == wxT("fontsize"))
1667 {
1668 if (!value.empty())
1669 attr.SetFontSize(wxAtoi(value));
1670 }
1671 else if (name == wxT("fontweight"))
1672 {
1673 if (!value.empty())
1674 attr.SetFontWeight((wxFontWeight) wxAtoi(value));
1675 }
1676 else if (name == wxT("fontunderlined"))
1677 {
1678 if (!value.empty())
1679 attr.SetFontUnderlined(wxAtoi(value) != 0);
1680 }
1681 else if (name == wxT("textcolor"))
1682 {
1683 if (!value.empty())
1684 {
1685 if (value[0] == wxT('#'))
1686 attr.SetTextColour(HexStringToColour(value.Mid(1)));
1687 else
1688 attr.SetTextColour(value);
1689 }
1690 }
1691 else if (name == wxT("bgcolor"))
1692 {
1693 if (!value.empty())
1694 {
1695 if (value[0] == wxT('#'))
1696 attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
1697 else
1698 attr.SetBackgroundColour(value);
1699 }
1700 }
1701 else if (name == wxT("characterstyle"))
1702 {
1703 if (!value.empty())
1704 attr.SetCharacterStyleName(value);
1705 }
1706 else if (name == wxT("texteffects"))
1707 {
1708 if (!value.empty())
1709 attr.SetTextEffects(wxAtoi(value));
1710 }
1711 else if (name == wxT("texteffectflags"))
1712 {
1713 if (!value.empty())
1714 attr.SetTextEffectFlags(wxAtoi(value));
1715 }
1716 else if (name == wxT("url"))
1717 {
1718 if (!value.empty())
1719 attr.SetURL(value);
1720 }
1721 else if (isPara)
1722 {
1723 if (name == wxT("alignment"))
1724 {
1725 if (!value.empty())
1726 attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
1727 }
1728 else if (name == wxT("leftindent"))
1729 {
1730 if (!value.empty())
1731 attr.SetLeftIndent(wxAtoi(value), attr.GetLeftSubIndent());
1732 }
1733 else if (name == wxT("leftsubindent"))
1734 {
1735 if (!value.empty())
1736 attr.SetLeftIndent(attr.GetLeftIndent(), wxAtoi(value));
1737 }
1738 else if (name == wxT("rightindent"))
1739 {
1740 if (!value.empty())
1741 attr.SetRightIndent(wxAtoi(value));
1742 }
1743 else if (name == wxT("parspacingbefore"))
1744 {
1745 if (!value.empty())
1746 attr.SetParagraphSpacingBefore(wxAtoi(value));
1747 }
1748 else if (name == wxT("parspacingafter"))
1749 {
1750 if (!value.empty())
1751 attr.SetParagraphSpacingAfter(wxAtoi(value));
1752 }
1753 else if (name == wxT("linespacing"))
1754 {
1755 if (!value.empty())
1756 attr.SetLineSpacing(wxAtoi(value));
1757 }
1758 else if (name == wxT("bulletstyle"))
1759 {
1760 if (!value.empty())
1761 attr.SetBulletStyle(wxAtoi(value));
1762 }
1763 else if (name == wxT("bulletnumber"))
1764 {
1765 if (!value.empty())
1766 attr.SetBulletNumber(wxAtoi(value));
1767 }
1768 else if (name == wxT("bulletsymbol"))
1769 {
1770 if (!value.empty())
1771 {
1772 wxChar ch = wxAtoi(value);
1773 wxString s;
1774 s << ch;
1775 attr.SetBulletText(s);
1776 }
1777 }
1778 else if (name == wxT("bullettext"))
1779 {
1780 if (!value.empty())
1781 {
1782 attr.SetBulletText(value);
1783 }
1784 }
1785 else if (name == wxT("bulletfont"))
1786 {
1787 if (!value.empty())
1788 {
1789 attr.SetBulletFont(value);
1790 }
1791 }
1792 else if (name == wxT("bulletname"))
1793 {
1794 if (!value.empty())
1795 {
1796 attr.SetBulletName(value);
1797 }
1798 }
1799 else if (name == wxT("parstyle"))
1800 {
1801 if (!value.empty())
1802 {
1803 attr.SetParagraphStyleName(value);
1804 }
1805 }
1806 else if (name == wxT("liststyle"))
1807 {
1808 if (!value.empty())
1809 {
1810 attr.SetListStyleName(value);
2f987d83
JS
1811 }
1812 }
1813 else if (name == wxT("boxstyle"))
1814 {
1815 if (!value.empty())
1816 {
1817 attr.GetTextBoxAttr().SetBoxStyleName(value);
bec80f4f
JS
1818 }
1819 }
1820 else if (name == wxT("tabs"))
1821 {
1822 if (!value.empty())
1823 {
1824 wxArrayInt tabs;
1825 wxStringTokenizer tkz(value, wxT(","));
1826 while (tkz.HasMoreTokens())
1827 {
1828 wxString token = tkz.GetNextToken();
1829 tabs.Add(wxAtoi(token));
1830 }
1831 attr.SetTabs(tabs);
1832 }
1833 }
1834 else if (name == wxT("pagebreak"))
1835 {
1836 if (!value.empty())
1837 {
1838 attr.SetPageBreak(wxAtoi(value) != 0);
1839 }
1840 }
1841 else if (name == wxT("outlinelevel"))
1842 {
1843 if (!value.empty())
1844 {
1845 attr.SetOutlineLevel(wxAtoi(value));
1846 }
1847 }
1848 else
1849 found = false;
1850 }
1851 else
1852 found = false;
1853
1854 if (!found)
1855 {
1856 // Box attributes
1857
1858 if (name == wxT("width"))
1859 {
1860 attr.GetTextBoxAttr().GetWidth().SetValue(wxRichTextParseDimension(value));
1861 }
1862 else if (name == wxT("height"))
1863 {
1864 attr.GetTextBoxAttr().GetHeight().SetValue(wxRichTextParseDimension(value));
1865 }
1866
706465df
JS
1867 else if (name == wxT("verticalalignment"))
1868 {
1869 if (value == wxT("top"))
1870 attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP);
1871 else if (value == wxT("centre"))
1872 attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE);
1873 else if (value == wxT("bottom"))
1874 attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM);
1875 else if (value == wxT("none"))
1876 attr.GetTextBoxAttr().SetVerticalAlignment(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE);
1877 }
bec80f4f
JS
1878 else if (name == wxT("float"))
1879 {
1880 if (value == wxT("left"))
1881 attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_LEFT);
1882 else if (value == wxT("right"))
1883 attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_RIGHT);
1884 else if (value == wxT("none"))
1885 attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_NONE);
1886 }
1887 else if (name == wxT("clear"))
1888 {
1889 if (value == wxT("left"))
1890 attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_LEFT);
1891 else if (value == wxT("right"))
1892 attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_RIGHT);
1893 else if (value == wxT("both"))
1894 attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_BOTH);
1895 else if (value == wxT("none"))
1896 attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_NONE);
1897 }
1898 else if (name == wxT("collapse-borders"))
603f702b 1899 attr.GetTextBoxAttr().SetCollapseBorders((wxTextBoxAttrCollapseMode) wxAtoi(value));
bec80f4f
JS
1900
1901 else if (name.Contains(wxT("border-")))
1902 {
1903 if (name == wxT("border-left-style"))
1904 attr.GetTextBoxAttr().GetBorder().GetLeft().SetStyle(wxAtoi(value));
1905 else if (name == wxT("border-right-style"))
1906 attr.GetTextBoxAttr().GetBorder().GetRight().SetStyle(wxAtoi(value));
1907 else if (name == wxT("border-top-style"))
1908 attr.GetTextBoxAttr().GetBorder().GetTop().SetStyle(wxAtoi(value));
1909 else if (name == wxT("border-bottom-style"))
1910 attr.GetTextBoxAttr().GetBorder().GetBottom().SetStyle(wxAtoi(value));
1911
1912 else if (name == wxT("border-left-colour"))
1913 attr.GetTextBoxAttr().GetBorder().GetLeft().SetColour(wxRichTextColourStringToLong(value));
1914 else if (name == wxT("border-right-colour"))
1915 attr.GetTextBoxAttr().GetBorder().GetRight().SetColour(wxRichTextColourStringToLong(value));
1916 else if (name == wxT("border-top-colour"))
1917 attr.GetTextBoxAttr().GetBorder().GetTop().SetColour(wxRichTextColourStringToLong(value));
1918 else if (name == wxT("border-bottom-colour"))
1919 attr.GetTextBoxAttr().GetBorder().GetBottom().SetColour(wxRichTextColourStringToLong(value));
1920
1921 else if (name == wxT("border-left-width"))
1922 attr.GetTextBoxAttr().GetBorder().GetLeft().SetWidth(wxRichTextParseDimension(value));
1923 else if (name == wxT("border-right-width"))
1924 attr.GetTextBoxAttr().GetBorder().GetRight().SetWidth(wxRichTextParseDimension(value));
1925 else if (name == wxT("border-top-width"))
1926 attr.GetTextBoxAttr().GetBorder().GetTop().SetWidth(wxRichTextParseDimension(value));
1927 else if (name == wxT("border-bottom-width"))
1928 attr.GetTextBoxAttr().GetBorder().GetBottom().SetWidth(wxRichTextParseDimension(value));
1929 }
1930 else if (name.Contains(wxT("outline-")))
1931 {
1932 if (name == wxT("outline-left-style"))
1933 attr.GetTextBoxAttr().GetOutline().GetLeft().SetStyle(wxAtoi(value));
1934 else if (name == wxT("outline-right-style"))
1935 attr.GetTextBoxAttr().GetOutline().GetRight().SetStyle(wxAtoi(value));
1936 else if (name == wxT("outline-top-style"))
1937 attr.GetTextBoxAttr().GetOutline().GetTop().SetStyle(wxAtoi(value));
1938 else if (name == wxT("outline-bottom-style"))
1939 attr.GetTextBoxAttr().GetOutline().GetBottom().SetStyle(wxAtoi(value));
1940
1941 else if (name == wxT("outline-left-colour"))
1942 attr.GetTextBoxAttr().GetOutline().GetLeft().SetColour(wxRichTextColourStringToLong(value));
1943 else if (name == wxT("outline-right-colour"))
1944 attr.GetTextBoxAttr().GetOutline().GetRight().SetColour(wxRichTextColourStringToLong(value));
1945 else if (name == wxT("outline-top-colour"))
1946 attr.GetTextBoxAttr().GetOutline().GetTop().SetColour(wxRichTextColourStringToLong(value));
1947 else if (name == wxT("outline-bottom-colour"))
1948 attr.GetTextBoxAttr().GetOutline().GetBottom().SetColour(wxRichTextColourStringToLong(value));
1949
1950 else if (name == wxT("outline-left-width"))
1951 attr.GetTextBoxAttr().GetOutline().GetLeft().SetWidth(wxRichTextParseDimension(value));
1952 else if (name == wxT("outline-right-width"))
1953 attr.GetTextBoxAttr().GetOutline().GetRight().SetWidth(wxRichTextParseDimension(value));
1954 else if (name == wxT("outline-top-width"))
1955 attr.GetTextBoxAttr().GetOutline().GetTop().SetWidth(wxRichTextParseDimension(value));
1956 else if (name == wxT("outline-bottom-width"))
1957 attr.GetTextBoxAttr().GetOutline().GetBottom().SetWidth(wxRichTextParseDimension(value));
1958 }
1959 else if (name.Contains(wxT("margin-")))
1960 {
1961 if (name == wxT("margin-left"))
1962 attr.GetTextBoxAttr().GetMargins().GetLeft().SetValue(wxRichTextParseDimension(value));
1963 else if (name == wxT("margin-right"))
1964 attr.GetTextBoxAttr().GetMargins().GetRight().SetValue(wxRichTextParseDimension(value));
1965 else if (name == wxT("margin-top"))
1966 attr.GetTextBoxAttr().GetMargins().GetTop().SetValue(wxRichTextParseDimension(value));
1967 else if (name == wxT("margin-bottom"))
1968 attr.GetTextBoxAttr().GetMargins().GetBottom().SetValue(wxRichTextParseDimension(value));
1969 }
1970 else if (name.Contains(wxT("padding-")))
1971 {
1972 if (name == wxT("padding-left"))
1973 attr.GetTextBoxAttr().GetPadding().GetLeft().SetValue(wxRichTextParseDimension(value));
1974 else if (name == wxT("padding-right"))
1975 attr.GetTextBoxAttr().GetPadding().GetRight().SetValue(wxRichTextParseDimension(value));
1976 else if (name == wxT("padding-top"))
1977 attr.GetTextBoxAttr().GetPadding().GetTop().SetValue(wxRichTextParseDimension(value));
1978 else if (name == wxT("padding-bottom"))
1979 attr.GetTextBoxAttr().GetPadding().GetBottom().SetValue(wxRichTextParseDimension(value));
1980 }
1981 else if (name.Contains(wxT("position-")))
1982 {
1983 if (name == wxT("position-left"))
1984 attr.GetTextBoxAttr().GetPosition().GetLeft().SetValue(wxRichTextParseDimension(value));
1985 else if (name == wxT("position-right"))
1986 attr.GetTextBoxAttr().GetPosition().GetRight().SetValue(wxRichTextParseDimension(value));
1987 else if (name == wxT("position-top"))
1988 attr.GetTextBoxAttr().GetPosition().GetTop().SetValue(wxRichTextParseDimension(value));
1989 else if (name == wxT("position-bottom"))
1990 attr.GetTextBoxAttr().GetPosition().GetBottom().SetValue(wxRichTextParseDimension(value));
1991 }
1992 }
706465df 1993
bec80f4f
JS
1994 xmlAttr = xmlAttr->GetNext();
1995 }
1996
1997 return true;
1998}
1999
2000#endif
2001 // wxUSE_STREAMS
2002
2003// Import this object from XML
603f702b 2004bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
bec80f4f
JS
2005{
2006 handler->ImportProperties(this, node);
2007 handler->ImportStyle(GetAttributes(), node, UsesParagraphAttributes());
706465df 2008
2865d42d 2009 *recurse = true;
bec80f4f
JS
2010
2011 return true;
2012}
2013
2014#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
2015// Export this object directly to the given stream.
2016bool wxRichTextObject::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
2017{
2018 ::OutputIndentation(stream, indent);
2019 ::OutputString(stream, wxT("<") + GetXMLNodeName(), handler->GetConvMem(), handler->GetConvFile());
2020
2021 wxString style = handler->AddAttributes(GetAttributes(), true);
2022
2023 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2024
2025 if (GetProperties().GetCount() > 0)
2026 {
2027 handler->WriteProperties(stream, GetProperties(), indent);
2028 }
706465df 2029
bec80f4f
JS
2030 wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
2031 if (composite)
2032 {
2033 size_t i;
2034 for (i = 0; i < composite->GetChildCount(); i++)
2035 {
2036 wxRichTextObject* child = composite->GetChild(i);
2037 child->ExportXML(stream, indent+1, handler);
2038 }
2039 }
2040
2041 ::OutputIndentation(stream, indent);
2042 ::OutputString(stream, wxT("</") + GetXMLNodeName() + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2043 return true;
2044}
2045#endif
2046
2047#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2048// Export this object to the given parent node, usually creating at least one child node.
2049bool wxRichTextObject::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
2050{
2051 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
2052 parent->AddChild(elementNode);
2053 handler->AddAttributes(elementNode, GetAttributes(), true);
2054 handler->WriteProperties(elementNode, GetProperties());
2055
2056 wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
2057 if (composite)
2058 {
2059 size_t i;
2060 for (i = 0; i < composite->GetChildCount(); i++)
2061 {
2062 wxRichTextObject* child = composite->GetChild(i);
2063 child->ExportXML(elementNode, handler);
2064 }
2065 }
2066 return true;
2067}
2068#endif
2069
2070
2071// Import this object from XML
603f702b 2072bool wxRichTextPlainText::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
bec80f4f 2073{
603f702b 2074 wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
706465df 2075
bec80f4f
JS
2076 if (node->GetName() == wxT("text"))
2077 {
2078 wxString text;
2079 wxXmlNode* textChild = node->GetChildren();
2080 while (textChild)
2081 {
2082 if (textChild->GetType() == wxXML_TEXT_NODE ||
2083 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
2084 {
2085 wxString text2 = textChild->GetContent();
2086
2087 // Strip whitespace from end
2088 if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
2089 text2 = text2.Mid(0, text2.length()-1);
2090
2091 if (!text2.empty() && text2[0] == wxT('"'))
2092 text2 = text2.Mid(1);
2093 if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
2094 text2 = text2.Mid(0, text2.length() - 1);
2095
2096 text += text2;
2097 }
2098 textChild = textChild->GetNext();
2099 }
706465df 2100
bec80f4f
JS
2101 SetText(text);
2102 }
2103 else if (node->GetName() == wxT("symbol"))
2104 {
2105 // This is a symbol that XML can't read in the normal way
2106 wxString text;
2107 wxXmlNode* textChild = node->GetChildren();
2108 while (textChild)
2109 {
2110 if (textChild->GetType() == wxXML_TEXT_NODE ||
2111 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
2112 {
2113 wxString text2 = textChild->GetContent();
2114 text += text2;
2115 }
2116 textChild = textChild->GetNext();
2117 }
2118
2119 wxString actualText;
2120 actualText << (wxChar) wxAtoi(text);
2121 SetText(actualText);
2122 }
2123 else
2124 return false;
2125
2126 return true;
2127}
2128
2129#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
2130// Export this object directly to the given stream.
2131bool wxRichTextPlainText::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
2132{
2133 wxString style = handler->AddAttributes(GetAttributes(), false);
2134
2135 int i;
2136 int last = 0;
2137 const wxString& text = GetText();
2138 int len = (int) text.Length();
2139
2140 if (len == 0)
2141 {
2142 i = 0;
2143 ::OutputIndentation(stream, indent);
2144 ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
2145 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2146 if (GetProperties().GetCount() > 0)
2147 {
2148 handler->WriteProperties(stream, GetProperties(), indent);
2149 ::OutputIndentation(stream, indent);
2150 }
2151 ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
2152 }
2153 else for (i = 0; i < len; i++)
2154 {
2155#if wxUSE_UNICODE
2156 int c = (int) text[i];
2157#else
2158 int c = (int) wxUChar(text[i]);
2159#endif
2160 if ((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13)
2161 {
2162 if (i > 0)
2163 {
2164 wxString fragment(text.Mid(last, i-last));
2165 if (!fragment.empty())
2166 {
2167 ::OutputIndentation(stream, indent);
2168 ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
2169
2170 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2171
2172 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
2173 {
2174 ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
2175 ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
2176 ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
2177 }
2178 else
2179 ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
2180
2181 if (GetProperties().GetCount() > 0)
2182 {
2183 handler->WriteProperties(stream, GetProperties(), indent);
2184 ::OutputIndentation(stream, indent);
2185 }
2186 ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
2187 }
2188 }
2189
2190
2191 // Output this character as a number in a separate tag, because XML can't cope
2192 // with entities below 32 except for 10 and 13
2193 last = i + 1;
2194 ::OutputIndentation(stream, indent);
2195 ::OutputString(stream, wxT("<symbol"), handler->GetConvMem(), handler->GetConvFile());
2196
2197 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2198 ::OutputString(stream, wxString::Format(wxT("%d"), c), handler->GetConvMem(), handler->GetConvFile());
2199
2200 if (GetProperties().GetCount() > 0)
2201 {
2202 handler->WriteProperties(stream, GetProperties(), indent);
2203 ::OutputIndentation(stream, indent);
2204 }
2205 ::OutputString(stream, wxT("</symbol>"), handler->GetConvMem(), handler->GetConvFile());
2206 }
2207 }
2208
2209 wxString fragment;
2210 if (last == 0)
2211 fragment = text;
2212 else
2213 fragment = text.Mid(last, i-last);
2214
2215 if (last < len)
2216 {
2217 ::OutputIndentation(stream, indent);
2218 ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
2219
2220 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
706465df 2221
bec80f4f
JS
2222 if (GetProperties().GetCount() > 0)
2223 {
2224 handler->WriteProperties(stream, GetProperties(), indent);
2225 ::OutputIndentation(stream, indent);
2226 }
2227
2228 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
2229 {
2230 ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
2231 ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
2232 ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
2233 }
2234 else
2235 ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
2236
2237 ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
2238 }
2239 return true;
2240}
2241#endif
2242
2243#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2244// Export this object to the given parent node, usually creating at least one child node.
2245bool wxRichTextPlainText::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
2246{
2247 int i;
2248 int last = 0;
2249 const wxString& text = GetText();
2250 int len = (int) text.Length();
2251
2252 if (len == 0)
2253 {
2254 i = 0;
2255
2256 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
2257 parent->AddChild(elementNode);
2258
2259 handler->AddAttributes(elementNode, GetAttributes(), false);
2260 handler->WriteProperties(elementNode, GetProperties());
2261 }
2262 else for (i = 0; i < len; i++)
2263 {
2264#if wxUSE_UNICODE
2265 int c = (int) text[i];
2266#else
2267 int c = (int) wxUChar(text[i]);
2268#endif
2269 if ((c < 32 || c == 34) && c != 10 && c != 13)
2270 {
2271 if (i > 0)
2272 {
2273 wxString fragment(text.Mid(last, i-last));
2274 if (!fragment.empty())
2275 {
2276 // TODO: I'm assuming wxXmlDocument will output quotes if necessary
2277 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
2278 parent->AddChild(elementNode);
2279 handler->AddAttributes(elementNode, GetAttributes(), false);
2280 handler->WriteProperties(elementNode, GetProperties());
706465df 2281
bec80f4f
JS
2282 wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
2283 elementNode->AddChild(textNode);
2284
2285 if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
2286 fragment = wxT("\"") + fragment + wxT("\"");
2287
2288 textNode->SetContent(fragment);
2289 }
2290 }
2291
2292
2293 // Output this character as a number in a separate tag, because XML can't cope
2294 // with entities below 32 except for 10 and 13
706465df 2295
bec80f4f
JS
2296 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("symbol"));
2297 parent->AddChild(elementNode);
2298
2299 handler->AddAttributes(elementNode, GetAttributes(), false);
2300 handler->WriteProperties(elementNode, GetProperties());
2301
2302 wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
2303 elementNode->AddChild(textNode);
2304 textNode->SetContent(wxString::Format(wxT("%d"), c));
2305
2306 last = i + 1;
2307 }
2308 }
2309
2310 wxString fragment;
2311 if (last == 0)
2312 fragment = text;
2313 else
2314 fragment = text.Mid(last, i-last);
2315
2316 if (last < len)
2317 {
2318 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
2319 parent->AddChild(elementNode);
2320 handler->AddAttributes(elementNode, GetAttributes(), false);
706465df 2321
bec80f4f
JS
2322 wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
2323 elementNode->AddChild(textNode);
2324
2325 if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
2326 fragment = wxT("\"") + fragment + wxT("\"");
2327
706465df 2328 textNode->SetContent(fragment);
bec80f4f
JS
2329 }
2330 return true;
2331}
2332#endif
2333
2334
2335// Import this object from XML
603f702b 2336bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
bec80f4f 2337{
603f702b 2338 wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
bec80f4f
JS
2339
2340 wxBitmapType imageType = wxBITMAP_TYPE_PNG;
2341 wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString);
2342 if (!value.empty())
2343 {
2344 int type = wxAtoi(value);
2345
2346 // note: 0 == wxBITMAP_TYPE_INVALID
2347 if (type <= 0 || type >= wxBITMAP_TYPE_MAX)
2348 {
2349 wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type);
2350 }
2351 else
2352 {
2353 imageType = (wxBitmapType)type;
2354 }
2355 }
2356
2357 wxString data;
2358
2359 wxXmlNode* imageChild = node->GetChildren();
2360 while (imageChild)
2361 {
2362 wxString childName = imageChild->GetName();
2363 if (childName == wxT("data"))
2364 {
2365 wxXmlNode* dataChild = imageChild->GetChildren();
2366 while (dataChild)
2367 {
2368 data = dataChild->GetContent();
2369 // wxLogDebug(data);
2370 dataChild = dataChild->GetNext();
2371 }
2372
2373 }
2374 imageChild = imageChild->GetNext();
2375 }
2376
2377 if (!data.empty())
2378 {
2379 wxStringInputStream strStream(data);
2380
2381 GetImageBlock().ReadHex(strStream, data.length(), imageType);
706465df 2382
bec80f4f
JS
2383 return true;
2384 }
2385 else
2386 return false;
2387}
2388
2389#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
2390// Export this object directly to the given stream.
2391bool wxRichTextImage::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
2392{
2393 wxString style = handler->AddAttributes(GetAttributes(), false);
2394
2395 ::OutputIndentation(stream, indent);
2396 ::OutputString(stream, wxT("<image"), handler->GetConvMem(), handler->GetConvFile());
a1b806b9 2397 if (!GetImageBlock().IsOk())
bec80f4f
JS
2398 {
2399 // No data
2400 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2401 }
2402 else
2403 {
2404 ::OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) GetImageBlock().GetImageType()) + style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2405 }
2406 if (GetProperties().GetCount() > 0)
2407 {
2408 handler->WriteProperties(stream, GetProperties(), indent);
2409 ::OutputIndentation(stream, indent);
2410 }
2411
2412 ::OutputIndentation(stream, indent+1);
2413 ::OutputString(stream, wxT("<data>"), handler->GetConvMem(), handler->GetConvFile());
2414
2415 // wxStopWatch stopwatch;
2416
2417 GetImageBlock().WriteHex(stream);
2418
2419 // wxLogDebug(wxT("Image conversion to hex took %ldms"), stopwatch.Time());
2420
2421 ::OutputString(stream, wxT("</data>\n"), handler->GetConvMem(), handler->GetConvFile());
2422 ::OutputIndentation(stream, indent);
2423 ::OutputString(stream, wxT("</image>"), handler->GetConvMem(), handler->GetConvFile());
2424 return true;
2425}
2426#endif
2427
2428#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2429// Export this object to the given parent node, usually creating at least one child node.
2430bool wxRichTextImage::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
2431{
2432 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("image"));
2433 parent->AddChild(elementNode);
2434
a1b806b9 2435 if (GetImageBlock().IsOk())
bec80f4f
JS
2436 elementNode->AddAttribute(wxT("imagetype"), MakeString((int) GetImageBlock().GetImageType()));
2437
2438 handler->AddAttributes(elementNode, GetAttributes(), false);
2439 handler->WriteProperties(elementNode, GetProperties());
2440
2441 wxXmlNode* dataNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("data"));
2442 elementNode->AddChild(dataNode);
2443 wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
2444 dataNode->AddChild(textNode);
2445
2446 wxString strData;
706465df 2447#if 1
bec80f4f
JS
2448 {
2449 wxMemoryOutputStream stream;
2450 if (GetImageBlock().WriteHex(stream))
2451 {
2452 if (stream.GetSize() > 0)
2453 {
2454 int size = stream.GetSize();
32742d3d 2455#ifdef __WXDEBUG__
bec80f4f
JS
2456 int size2 = stream.GetOutputStreamBuffer()->GetIntPosition();
2457 wxASSERT(size == size2);
32742d3d 2458#endif
bec80f4f
JS
2459 unsigned char* data = new unsigned char[size];
2460 stream.CopyTo(data, size);
2461 strData = wxString((const char*) data, wxConvUTF8, size);
2462 delete[] data;
2463 }
2464 else
2465 strData = wxEmptyString;
2466 }
706465df 2467
bec80f4f
JS
2468 }
2469#else
2470 {
2471 wxStringOutputStream strStream(& strData);
706465df 2472 GetImageBlock().WriteHex(strStream);
bec80f4f
JS
2473 }
2474#endif
2475
2476 textNode->SetContent(strData);
2477 textNode->SetNoConversion(true); // optimize speed
2478
2479 return true;
2480}
2481#endif
2482
2483
2484// Import this object from XML
603f702b 2485bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
bec80f4f 2486{
603f702b 2487 wxRichTextObject::ImportFromXML(buffer, node, handler, recurse);
706465df 2488
603f702b 2489 *recurse = true;
bec80f4f
JS
2490
2491 wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString);
2492 if (partial == wxT("true"))
2493 SetPartialParagraph(true);
2494
603f702b
JS
2495 wxXmlNode* child = wxRichTextXMLHandler::FindNode(node, wxT("stylesheet"));
2496 if (child && (handler->GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
2497 {
2498 wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
2499 wxString sheetName = child->GetAttribute(wxT("name"), wxEmptyString);
2500 wxString sheetDescription = child->GetAttribute(wxT("description"), wxEmptyString);
2501 sheet->SetName(sheetName);
2502 sheet->SetDescription(sheetDescription);
2503
2504 wxXmlNode* child2 = child->GetChildren();
2505 while (child2)
2506 {
2507 handler->ImportStyleDefinition(sheet, child2);
2508
2509 child2 = child2->GetNext();
2510 }
2511
2512 // Notify that styles have changed. If this is vetoed by the app,
2513 // the new sheet will be deleted. If it is not vetoed, the
2514 // old sheet will be deleted and replaced with the new one.
2515 buffer->SetStyleSheetAndNotify(sheet);
2516 }
2517
bec80f4f
JS
2518 return true;
2519}
2520
2521#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
2522// Export this object directly to the given stream.
2523bool wxRichTextParagraphLayoutBox::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
2524{
2525 ::OutputIndentation(stream, indent);
603f702b
JS
2526 wxString nodeName = GetXMLNodeName();
2527 ::OutputString(stream, wxT("<") + nodeName, handler->GetConvMem(), handler->GetConvFile());
bec80f4f
JS
2528
2529 wxString style = handler->AddAttributes(GetAttributes(), true);
2530
2531 if (GetPartialParagraph())
2532 style << wxT(" partialparagraph=\"true\"");
2533
2534 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2535
2536 if (GetProperties().GetCount() > 0)
2537 {
2538 handler->WriteProperties(stream, GetProperties(), indent);
2539 }
706465df 2540
bec80f4f
JS
2541 size_t i;
2542 for (i = 0; i < GetChildCount(); i++)
2543 {
2544 wxRichTextObject* child = GetChild(i);
2545 child->ExportXML(stream, indent+1, handler);
2546 }
2547
2548 ::OutputIndentation(stream, indent);
603f702b 2549 ::OutputString(stream, wxT("</") + nodeName + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
bec80f4f
JS
2550 return true;
2551}
2552#endif
2553
2554#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2555// Export this object to the given parent node, usually creating at least one child node.
2556bool wxRichTextParagraphLayoutBox::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
2557{
603f702b 2558 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
bec80f4f
JS
2559 parent->AddChild(elementNode);
2560 handler->AddAttributes(elementNode, GetAttributes(), true);
2561 handler->WriteProperties(elementNode, GetProperties());
2562
2563 if (GetPartialParagraph())
2564 elementNode->AddAttribute(wxT("partialparagraph"), wxT("true"));
2565
2566 size_t i;
2567 for (i = 0; i < GetChildCount(); i++)
2568 {
2569 wxRichTextObject* child = GetChild(i);
2570 child->ExportXML(elementNode, handler);
2571 }
2572
2573 return true;
2574}
2575#endif
88a7a4e1 2576
603f702b
JS
2577// Import this object from XML
2578bool wxRichTextTable::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler, bool* recurse)
2579{
2580 wxRichTextBox::ImportFromXML(buffer, node, handler, recurse);
706465df 2581
603f702b
JS
2582 *recurse = false;
2583
2584 m_rowCount = wxAtoi(node->GetAttribute(wxT("rows"), wxEmptyString));
2585 m_colCount = wxAtoi(node->GetAttribute(wxT("cols"), wxEmptyString));
706465df 2586
603f702b
JS
2587 wxXmlNode* child = node->GetChildren();
2588 while (child)
2589 {
2590 wxRichTextObject* childObj = handler->CreateObjectForXMLName(this, child->GetName());
2591 if (childObj)
2592 {
2593 AppendChild(childObj);
2594 handler->ImportXML(buffer, childObj, child);
2595 }
2596 child = child->GetNext();
2597 }
2598
2599 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
2600 int i, j;
2601 for (i = 0; i < m_rowCount; i++)
2602 {
2603 wxRichTextObjectPtrArray& colArray = m_cells[i];
2604 for (j = 0; j < m_colCount; j++)
2605 {
2606 int idx = i * m_colCount + j;
2607 if (idx < (int) GetChildren().GetCount())
2608 {
2609 wxRichTextCell* cell = wxDynamicCast(GetChildren().Item(idx)->GetData(), wxRichTextCell);
2610 if (cell)
2611 colArray.Add(cell);
2612 }
2613 }
2614 }
2615
2616 return true;
2617}
2618
2619#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
2620// Export this object directly to the given stream.
2621bool wxRichTextTable::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
2622{
2623 ::OutputIndentation(stream, indent);
2624 wxString nodeName = GetXMLNodeName();
2625 ::OutputString(stream, wxT("<") + nodeName, handler->GetConvMem(), handler->GetConvFile());
2626
2627 wxString style = handler->AddAttributes(GetAttributes(), true);
2628
2629 style << wxT(" rows=\"") << m_rowCount << wxT("\"");
2630 style << wxT(" cols=\"") << m_colCount << wxT("\"");
2631
2632 ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2633
2634 if (GetProperties().GetCount() > 0)
2635 {
2636 handler->WriteProperties(stream, GetProperties(), indent);
2637 }
706465df 2638
603f702b
JS
2639 int i, j;
2640 for (i = 0; i < m_rowCount; i++)
2641 {
2642 for (j = 0; j < m_colCount; j ++)
2643 {
2644 wxRichTextCell* cell = GetCell(i, j);
2645 cell->ExportXML(stream, indent+1, handler);
2646 }
2647 }
2648
2649 ::OutputIndentation(stream, indent);
2650 ::OutputString(stream, wxT("</") + nodeName + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
2651
2652 return true;
2653}
2654#endif
2655
2656#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
2657// Export this object to the given parent node, usually creating at least one child node.
2658bool wxRichTextTable::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
2659{
2660 wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
2661 parent->AddChild(elementNode);
2662 handler->AddAttributes(elementNode, GetAttributes(), true);
2663 handler->WriteProperties(elementNode, GetProperties());
2664
2665 elementNode->AddAttribute(wxT("rows"), wxString::Format(wxT("%d"), m_rowCount));
2666 elementNode->AddAttribute(wxT("cols"), wxString::Format(wxT("%d"), m_colCount));
2667
2668 int i, j;
2669 for (i = 0; i < m_rowCount; i++)
2670 {
2671 for (j = 0; j < m_colCount; j ++)
2672 {
2673 wxRichTextCell* cell = GetCell(i, j);
2674 cell->ExportXML(elementNode, handler);
2675 }
2676 }
2677
2678 return true;
2679}
2680#endif
2681
2682
5d7836c4 2683#endif
fcdbeefa 2684 // wxUSE_RICHTEXT && wxUSE_XML
7b907278 2685