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