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