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