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