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