Added URL support to attribute objects and to wxRichTextCtrl,
[wxWidgets.git] / src / richtext / richtextxml.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextxml.cpp
3 // Purpose: XML and HTML I/O for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT && wxUSE_XML
20
21 #include "wx/richtext/richtextxml.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/intl.h"
25 #include "wx/module.h"
26 #endif
27
28 #include "wx/filename.h"
29 #include "wx/clipbrd.h"
30 #include "wx/wfstream.h"
31 #include "wx/sstream.h"
32 #include "wx/txtstrm.h"
33 #include "wx/tokenzr.h"
34 #include "wx/xml/xml.h"
35
36 IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
37
38 #if wxUSE_STREAMS
39 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
40 {
41 if (!stream.IsOk())
42 return false;
43
44 buffer->Clear();
45
46 wxXmlDocument* xmlDoc = new wxXmlDocument;
47 bool success = true;
48
49 // This is the encoding to convert to (memory encoding rather than file encoding)
50 wxString encoding(wxT("UTF-8"));
51
52 #if !wxUSE_UNICODE && wxUSE_INTL
53 encoding = wxLocale::GetSystemEncodingName();
54 #endif
55
56 if (!xmlDoc->Load(stream, encoding))
57 {
58 success = false;
59 }
60 else
61 {
62 if (xmlDoc->GetRoot() && xmlDoc->GetRoot()->GetType() == wxXML_ELEMENT_NODE && xmlDoc->GetRoot()->GetName() == wxT("richtext"))
63 {
64 wxXmlNode* child = xmlDoc->GetRoot()->GetChildren();
65 while (child)
66 {
67 if (child->GetType() == wxXML_ELEMENT_NODE)
68 {
69 wxString name = child->GetName();
70 if (name == wxT("richtext-version"))
71 {
72 }
73 else
74 ImportXML(buffer, child);
75 }
76
77 child = child->GetNext();
78 }
79 }
80 else
81 {
82 success = false;
83 }
84 }
85
86 delete xmlDoc;
87
88 buffer->UpdateRanges();
89
90 return success;
91 }
92
93 /// Recursively import an object
94 bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
95 {
96 wxString name = node->GetName();
97
98 bool doneChildren = false;
99
100 if (name == wxT("paragraphlayout"))
101 {
102 wxString partial = node->GetPropVal(wxT("partialparagraph"), wxEmptyString);
103 if (partial == wxT("true"))
104 buffer->SetPartialParagraph(true);
105 }
106 else if (name == wxT("paragraph"))
107 {
108 wxRichTextParagraph* para = new wxRichTextParagraph(buffer);
109 buffer->AppendChild(para);
110
111 GetStyle(para->GetAttributes(), node, true);
112
113 wxXmlNode* child = node->GetChildren();
114 while (child)
115 {
116 wxString childName = child->GetName();
117 if (childName == wxT("text"))
118 {
119 wxString text;
120 wxXmlNode* textChild = child->GetChildren();
121 while (textChild)
122 {
123 if (textChild->GetType() == wxXML_TEXT_NODE ||
124 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
125 {
126 wxString text2 = textChild->GetContent();
127
128 // Strip whitespace from end
129 if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
130 text2 = text2.Mid(0, text2.length()-1);
131
132 if (!text2.empty() && text2[0] == wxT('"'))
133 text2 = text2.Mid(1);
134 if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
135 text2 = text2.Mid(0, text2.length() - 1);
136
137 text += text2;
138 }
139 textChild = textChild->GetNext();
140 }
141
142 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para);
143 GetStyle(textObject->GetAttributes(), child, false);
144
145 para->AppendChild(textObject);
146 }
147 else if (childName == wxT("symbol"))
148 {
149 // This is a symbol that XML can't read in the normal way
150 wxString text;
151 wxXmlNode* textChild = child->GetChildren();
152 while (textChild)
153 {
154 if (textChild->GetType() == wxXML_TEXT_NODE ||
155 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
156 {
157 wxString text2 = textChild->GetContent();
158 text += text2;
159 }
160 textChild = textChild->GetNext();
161 }
162
163 wxString actualText;
164 actualText << (wxChar) wxAtoi(text);
165
166 wxRichTextPlainText* textObject = new wxRichTextPlainText(actualText, para);
167 GetStyle(textObject->GetAttributes(), child, false);
168
169 para->AppendChild(textObject);
170 }
171 else if (childName == wxT("image"))
172 {
173 int imageType = wxBITMAP_TYPE_PNG;
174 wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString);
175 if (!value.empty())
176 imageType = wxAtoi(value);
177
178 wxString data;
179
180 wxXmlNode* imageChild = child->GetChildren();
181 while (imageChild)
182 {
183 wxString childName = imageChild->GetName();
184 if (childName == wxT("data"))
185 {
186 wxXmlNode* dataChild = imageChild->GetChildren();
187 while (dataChild)
188 {
189 data = dataChild->GetContent();
190 // wxLogDebug(data);
191 dataChild = dataChild->GetNext();
192 }
193
194 }
195 imageChild = imageChild->GetNext();
196 }
197
198 if (!data.empty())
199 {
200 wxRichTextImage* imageObj = new wxRichTextImage(para);
201 para->AppendChild(imageObj);
202
203 wxStringInputStream strStream(data);
204
205 imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType);
206 }
207 }
208 child = child->GetNext();
209 }
210
211 doneChildren = true;
212 }
213 else if (name == wxT("stylesheet"))
214 {
215 if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)
216 {
217 wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
218
219 wxXmlNode* child = node->GetChildren();
220 while (child)
221 {
222 ImportStyleDefinition(sheet, child);
223
224 child = child->GetNext();
225 }
226
227 // Notify that styles have changed. If this is vetoed by the app,
228 // the new sheet will be deleted. If it is not vetoed, the
229 // old sheet will be deleted and replaced with the new one.
230 buffer->SetStyleSheetAndNotify(sheet);
231 }
232 doneChildren = true;
233 }
234
235 if (!doneChildren)
236 {
237 wxXmlNode* child = node->GetChildren();
238 while (child)
239 {
240 ImportXML(buffer, child);
241 child = child->GetNext();
242 }
243 }
244
245 return true;
246 }
247
248 bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node)
249 {
250 wxString styleType = node->GetName();
251 wxString styleName = node->GetPropVal(wxT("name"), wxEmptyString);
252 wxString baseStyleName = node->GetPropVal(wxT("basestyle"), wxEmptyString);
253
254 if (styleName.IsEmpty())
255 return false;
256
257 if (styleType == wxT("characterstyle"))
258 {
259 wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName);
260 def->SetBaseStyle(baseStyleName);
261
262 wxXmlNode* child = node->GetChildren();
263 while (child)
264 {
265 if (child->GetName() == wxT("style"))
266 {
267 wxTextAttrEx attr;
268 GetStyle(attr, child, false);
269 def->SetStyle(attr);
270 }
271 child = child->GetNext();
272 }
273
274 sheet->AddCharacterStyle(def);
275 }
276 else if (styleType == wxT("paragraphstyle"))
277 {
278 wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName);
279
280 wxString nextStyleName = node->GetPropVal(wxT("nextstyle"), wxEmptyString);
281 def->SetNextStyle(nextStyleName);
282 def->SetBaseStyle(baseStyleName);
283
284 wxXmlNode* child = node->GetChildren();
285 while (child)
286 {
287 if (child->GetName() == wxT("style"))
288 {
289 wxTextAttrEx attr;
290 GetStyle(attr, child, false);
291 def->SetStyle(attr);
292 }
293 child = child->GetNext();
294 }
295
296 sheet->AddParagraphStyle(def);
297 }
298 else if (styleType == wxT("liststyle"))
299 {
300 wxRichTextListStyleDefinition* def = new wxRichTextListStyleDefinition(styleName);
301
302 wxString nextStyleName = node->GetPropVal(wxT("nextstyle"), wxEmptyString);
303 def->SetNextStyle(nextStyleName);
304 def->SetBaseStyle(baseStyleName);
305
306 wxXmlNode* child = node->GetChildren();
307 while (child)
308 {
309 if (child->GetName() == wxT("style"))
310 {
311 wxTextAttrEx attr;
312 GetStyle(attr, child, false);
313
314 wxString styleLevel = child->GetPropVal(wxT("level"), wxEmptyString);
315 if (styleLevel.IsEmpty())
316 {
317 def->SetStyle(attr);
318 }
319 else
320 {
321 int level = wxAtoi(styleLevel);
322 if (level > 0 && level <= 10)
323 {
324 def->SetLevelAttributes(level-1, attr);
325 }
326 }
327 }
328 child = child->GetNext();
329 }
330
331 sheet->AddListStyle(def);
332 }
333
334 return true;
335 }
336
337 //-----------------------------------------------------------------------------
338 // xml support routines
339 //-----------------------------------------------------------------------------
340
341 bool wxRichTextXMLHandler::HasParam(wxXmlNode* node, const wxString& param)
342 {
343 return (GetParamNode(node, param) != NULL);
344 }
345
346 wxXmlNode *wxRichTextXMLHandler::GetParamNode(wxXmlNode* node, const wxString& param)
347 {
348 wxCHECK_MSG(node, NULL, wxT("You can't access node data before it was initialized!"));
349
350 wxXmlNode *n = node->GetChildren();
351
352 while (n)
353 {
354 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
355 return n;
356 n = n->GetNext();
357 }
358 return NULL;
359 }
360
361
362 wxString wxRichTextXMLHandler::GetNodeContent(wxXmlNode *node)
363 {
364 wxXmlNode *n = node;
365 if (n == NULL) return wxEmptyString;
366 n = n->GetChildren();
367
368 while (n)
369 {
370 if (n->GetType() == wxXML_TEXT_NODE ||
371 n->GetType() == wxXML_CDATA_SECTION_NODE)
372 return n->GetContent();
373 n = n->GetNext();
374 }
375 return wxEmptyString;
376 }
377
378
379 wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param)
380 {
381 if (param.empty())
382 return GetNodeContent(node);
383 else
384 return GetNodeContent(GetParamNode(node, param));
385 }
386
387 wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, bool WXUNUSED(translate))
388 {
389 wxXmlNode *parNode = GetParamNode(node, param);
390 if (!parNode)
391 parNode = node;
392 wxString str1(GetNodeContent(parNode));
393 return str1;
394 }
395
396 // For use with earlier versions of wxWidgets
397 #ifndef WXUNUSED_IN_UNICODE
398 #if wxUSE_UNICODE
399 #define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
400 #else
401 #define WXUNUSED_IN_UNICODE(x) x
402 #endif
403 #endif
404
405 // write string to output:
406 inline static void OutputString(wxOutputStream& stream, const wxString& str,
407 wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL)
408 {
409 if (str.empty()) return;
410 #if wxUSE_UNICODE
411 if (convFile)
412 {
413 const wxWX2MBbuf buf(str.mb_str(*convFile));
414 stream.Write((const char*)buf, strlen((const char*)buf));
415 }
416 else
417 {
418 const wxWX2MBbuf buf(str.mb_str(wxConvUTF8));
419 stream.Write((const char*)buf, strlen((const char*)buf));
420 }
421 #else
422 if ( convFile == NULL )
423 stream.Write(str.mb_str(), str.Len());
424 else
425 {
426 wxString str2(str.wc_str(*convMem), *convFile);
427 stream.Write(str2.mb_str(), str2.Len());
428 }
429 #endif
430 }
431
432 // Same as above, but create entities first.
433 // Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
434 static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
435 wxMBConv *convMem = NULL, wxMBConv *convFile = NULL)
436 {
437 wxString buf;
438 size_t i, last, len;
439 wxChar c;
440
441 len = str.Len();
442 last = 0;
443 for (i = 0; i < len; i++)
444 {
445 c = str.GetChar(i);
446
447 // Original code excluded "&amp;" but we _do_ want to convert
448 // the ampersand beginning &amp; because otherwise when read in,
449 // the original "&amp;" becomes "&".
450
451 if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
452 (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
453 {
454 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
455 switch (c)
456 {
457 case wxT('<'):
458 OutputString(stream, wxT("&lt;"), NULL, NULL);
459 break;
460 case wxT('>'):
461 OutputString(stream, wxT("&gt;"), NULL, NULL);
462 break;
463 case wxT('&'):
464 OutputString(stream, wxT("&amp;"), NULL, NULL);
465 break;
466 case wxT('"'):
467 OutputString(stream, wxT("&quot;"), NULL, NULL);
468 break;
469 default: break;
470 }
471 last = i + 1;
472 }
473 else if (wxUChar(c) > 127)
474 {
475 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
476
477 wxString s(wxT("&#"));
478 s << (int) c;
479 s << wxT(";");
480 OutputString(stream, s, NULL, NULL);
481 last = i + 1;
482 }
483 }
484 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
485 }
486
487 inline static void OutputIndentation(wxOutputStream& stream, int indent)
488 {
489 wxString str = wxT("\n");
490 for (int i = 0; i < indent; i++)
491 str << wxT(' ') << wxT(' ');
492 OutputString(stream, str, NULL, NULL);
493 }
494
495 // Convert a colour to a 6-digit hex string
496 static wxString ColourToHexString(const wxColour& col)
497 {
498 wxString hex;
499
500 hex += wxDecToHex(col.Red());
501 hex += wxDecToHex(col.Green());
502 hex += wxDecToHex(col.Blue());
503
504 return hex;
505 }
506
507 // Convert 6-digit hex string to a colour
508 static wxColour HexStringToColour(const wxString& hex)
509 {
510 unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
511 unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
512 unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
513
514 return wxColour(r, g, b);
515 }
516
517 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
518 {
519 if (!stream.IsOk())
520 return false;
521
522 wxString version(wxT("1.0") ) ;
523
524 bool deleteConvFile = false;
525 wxString fileEncoding;
526 wxMBConv* convFile = NULL;
527
528 #if wxUSE_UNICODE
529 fileEncoding = wxT("UTF-8");
530 convFile = & wxConvUTF8;
531 #else
532 fileEncoding = wxT("ISO-8859-1");
533 convFile = & wxConvISO8859_1;
534 #endif
535
536 // If SetEncoding has been called, change the output encoding.
537 if (!m_encoding.empty() && m_encoding.Lower() != fileEncoding.Lower())
538 {
539 if (m_encoding == wxT("<System>"))
540 {
541 fileEncoding = wxLocale::GetSystemEncodingName();
542 }
543 else
544 {
545 fileEncoding = m_encoding;
546 }
547
548 // GetSystemEncodingName may not have returned a name
549 if (fileEncoding.empty())
550 #if wxUSE_UNICODE
551 fileEncoding = wxT("UTF-8");
552 #else
553 fileEncoding = wxT("ISO-8859-1");
554 #endif
555 convFile = new wxCSConv(fileEncoding);
556 deleteConvFile = true;
557 }
558
559 #if !wxUSE_UNICODE
560 wxMBConv* convMem = wxConvCurrent;
561 #else
562 wxMBConv* convMem = NULL;
563 #endif
564
565 wxString s ;
566 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
567 (const wxChar*) version, (const wxChar*) fileEncoding );
568 OutputString(stream, s, NULL, NULL);
569 OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL, NULL);
570
571 int level = 1;
572
573 if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
574 {
575 OutputIndentation(stream, level);
576 OutputString(stream, wxT("<stylesheet>"), convMem, convFile);
577
578 int i;
579
580 for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
581 {
582 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
583 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
584 }
585
586 for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
587 {
588 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
589 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
590 }
591
592 for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
593 {
594 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
595 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
596 }
597
598 OutputIndentation(stream, level);
599 OutputString(stream, wxT("</stylesheet>"), convMem, convFile);
600 }
601
602
603 bool success = ExportXML(stream, convMem, convFile, *buffer, level);
604
605 OutputString(stream, wxT("\n</richtext>") , NULL, NULL);
606 OutputString(stream, wxT("\n"), NULL, NULL);
607
608 if (deleteConvFile)
609 delete convFile;
610
611 return success;
612 }
613
614 /// Recursively export an object
615 bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent)
616 {
617 wxString objectName;
618 if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox)))
619 objectName = wxT("paragraphlayout");
620 else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph)))
621 objectName = wxT("paragraph");
622 else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
623 objectName = wxT("text");
624 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
625 objectName = wxT("image");
626 else
627 objectName = wxT("object");
628
629 bool terminateTag = true;
630
631 if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
632 {
633 wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj;
634
635 wxString style = CreateStyle(obj.GetAttributes(), false);
636
637 int i;
638 int last = 0;
639 const wxString& text = textObj.GetText();
640 int len = (int) text.Length();
641 for (i = 0; i < len; i++)
642 {
643 int c = (int) text[i];
644 if (c < 32 && c != 9 && c != 10 && c != 13)
645 {
646 if (i > 0)
647 {
648 OutputIndentation(stream, indent);
649 OutputString(stream, wxT("<") + objectName, convMem, convFile);
650
651 OutputString(stream, style + wxT(">"), convMem, convFile);
652
653 wxString fragment(text.Mid(last, i-last));
654 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
655 {
656 OutputString(stream, wxT("\""), convMem, convFile);
657 OutputStringEnt(stream, fragment, convMem, convFile);
658 OutputString(stream, wxT("\""), convMem, convFile);
659 }
660 else
661 OutputStringEnt(stream, fragment, convMem, convFile);
662
663 OutputString(stream, wxT("</text>"), convMem, convFile);
664 }
665
666
667 // Output this character as a number in a separate tag, because XML can't cope
668 // with entities below 32 except for 9, 10 and 13
669 last = i + 1;
670 OutputIndentation(stream, indent);
671 OutputString(stream, wxT("<symbol"), convMem, convFile);
672
673 OutputString(stream, style + wxT(">"), convMem, convFile);
674 OutputString(stream, wxString::Format(wxT("%d"), c), convMem, convFile);
675
676 OutputString(stream, wxT("</symbol>"), convMem, convFile);
677 }
678 }
679
680 wxString fragment;
681 if (last == 0)
682 fragment = text;
683 else
684 fragment = text.Mid(last, i-last);
685
686 if (last < len)
687 {
688 OutputIndentation(stream, indent);
689 OutputString(stream, wxT("<") + objectName, convMem, convFile);
690
691 OutputString(stream, style + wxT(">"), convMem, convFile);
692
693 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
694 {
695 OutputString(stream, wxT("\""), convMem, convFile);
696 OutputStringEnt(stream, fragment, convMem, convFile);
697 OutputString(stream, wxT("\""), convMem, convFile);
698 }
699 else
700 OutputStringEnt(stream, fragment, convMem, convFile);
701 }
702 else
703 terminateTag = false;
704 }
705 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
706 {
707 wxRichTextImage& imageObj = (wxRichTextImage&) obj;
708
709 if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok())
710 imageObj.MakeBlock();
711
712 OutputIndentation(stream, indent);
713 OutputString(stream, wxT("<") + objectName, convMem, convFile);
714 if (!imageObj.GetImageBlock().Ok())
715 {
716 // No data
717 OutputString(stream, wxT(">"), convMem, convFile);
718 }
719 else
720 {
721 OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj.GetImageBlock().GetImageType()));
722 }
723
724 OutputIndentation(stream, indent+1);
725 OutputString(stream, wxT("<data>"), convMem, convFile);
726
727 imageObj.GetImageBlock().WriteHex(stream);
728
729 OutputString(stream, wxT("</data>"), convMem, convFile);
730 }
731 else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
732 {
733 OutputIndentation(stream, indent);
734 OutputString(stream, wxT("<") + objectName, convMem, convFile);
735
736 bool isPara = false;
737 if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
738 isPara = true;
739
740 wxString style = CreateStyle(obj.GetAttributes(), isPara);
741
742 if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph())
743 style << wxT(" partialparagraph=\"true\"");
744
745 OutputString(stream, style + wxT(">"), convMem, convFile);
746
747 wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj;
748 size_t i;
749 for (i = 0; i < composite.GetChildCount(); i++)
750 {
751 wxRichTextObject* child = composite.GetChild(i);
752 ExportXML(stream, convMem, convFile, *child, indent+1);
753 }
754 }
755
756 if (objectName != wxT("text"))
757 OutputIndentation(stream, indent);
758
759 if (terminateTag)
760 OutputString(stream, wxT("</") + objectName + wxT(">"), convMem, convFile);
761
762 return true;
763 }
764
765 bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level)
766 {
767 wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
768 wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
769 wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
770
771 wxString baseStyle = def->GetBaseStyle();
772 wxString baseStyleProp;
773 if (!baseStyle.IsEmpty())
774 baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\"");
775
776 if (charDef)
777 {
778 OutputIndentation(stream, level);
779 OutputString(stream, wxT("<characterstyle") + baseStyleProp + wxT(">"), convMem, convFile);
780
781 level ++;
782
783 wxString style = CreateStyle(def->GetStyle(), false);
784
785 OutputIndentation(stream, level);
786 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
787
788 OutputIndentation(stream, level);
789 OutputString(stream, wxT("</style>"), convMem, convFile);
790
791 level --;
792
793 OutputIndentation(stream, level);
794 OutputString(stream, wxT("</characterstyle>"), convMem, convFile);
795 }
796 else if (listDef)
797 {
798 OutputIndentation(stream, level);
799
800 if (!listDef->GetNextStyle().IsEmpty())
801 baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\"");
802
803 OutputString(stream, wxT("<liststyle") + baseStyleProp + wxT(">"), convMem, convFile);
804
805 level ++;
806
807 wxString style = CreateStyle(def->GetStyle(), false);
808
809 OutputIndentation(stream, level);
810 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
811
812 OutputIndentation(stream, level);
813 OutputString(stream, wxT("</style>"), convMem, convFile);
814
815 int i;
816 for (i = 0; i < 10; i ++)
817 {
818 wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
819 if (levelAttr)
820 {
821 wxString style = CreateStyle(def->GetStyle(), false);
822 wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1));
823
824 OutputIndentation(stream, level);
825 OutputString(stream, wxT("<style ") + levelStr + style + wxT(">"), convMem, convFile);
826
827 OutputIndentation(stream, level);
828 OutputString(stream, wxT("</style>"), convMem, convFile);
829 }
830 }
831
832 level --;
833
834 OutputIndentation(stream, level);
835 OutputString(stream, wxT("</liststyle>"), convMem, convFile);
836 }
837 else if (paraDef)
838 {
839 OutputIndentation(stream, level);
840
841 if (!listDef->GetNextStyle().IsEmpty())
842 baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\"");
843
844 OutputString(stream, wxT("<paragraphstyle") + baseStyleProp + wxT(">"), convMem, convFile);
845
846 level ++;
847
848 wxString style = CreateStyle(def->GetStyle(), false);
849
850 OutputIndentation(stream, level);
851 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
852
853 OutputIndentation(stream, level);
854 OutputString(stream, wxT("</style>"), convMem, convFile);
855
856 level --;
857
858 OutputIndentation(stream, level);
859 OutputString(stream, wxT("</paragraphstyle>"), convMem, convFile);
860 }
861
862 return true;
863 }
864
865 /// Create style parameters
866 wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara)
867 {
868 wxString str;
869 if (attr.HasTextColour() && attr.GetTextColour().Ok())
870 {
871 str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
872 }
873 if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok())
874 {
875 str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
876 }
877
878 if (attr.GetFont().Ok())
879 {
880 if (attr.HasSize())
881 str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\"");
882
883 //if (attr.HasFamily())
884 // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
885
886 if (attr.HasItalic())
887 str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\"");
888
889 if (attr.HasWeight())
890 str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\"");
891
892 if (attr.HasUnderlined())
893 str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\"");
894
895 if (attr.HasFaceName())
896 str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\"");
897 }
898
899 if (!attr.GetCharacterStyleName().empty())
900 str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
901
902 if (isPara)
903 {
904 if (attr.HasAlignment())
905 str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\"");
906
907 if (attr.HasLeftIndent())
908 {
909 str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\"");
910 str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\"");
911 }
912
913 if (attr.HasRightIndent())
914 str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\"");
915
916 if (attr.HasParagraphSpacingAfter())
917 str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\"");
918
919 if (attr.HasParagraphSpacingBefore())
920 str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\"");
921
922 if (attr.HasLineSpacing())
923 str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\"");
924
925 if (attr.HasBulletStyle())
926 str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\"");
927
928 if (attr.HasBulletNumber())
929 str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\"");
930
931 if (attr.HasBulletText())
932 {
933 // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
934 // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
935 if (!attr.GetBulletText().IsEmpty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
936 str << wxT(" bulletsymbol=\"") << (int) (attr.GetBulletText()[0]) << wxT("\"");
937 else
938 str << wxT(" bullettext=\"") << attr.GetBulletText() << wxT("\"");
939
940 str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\"");
941 }
942
943 if (attr.HasBulletName())
944 str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\"");
945
946 if (attr.HasURL())
947 str << wxT(" url=\"") << attr.GetURL() << wxT("\"");
948
949 if (!attr.GetParagraphStyleName().empty())
950 str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
951
952 if (!attr.GetListStyleName().empty())
953 str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\"");
954
955 if (attr.HasTabs())
956 {
957 str << wxT(" tabs=\"");
958 size_t i;
959 for (i = 0; i < attr.GetTabs().GetCount(); i++)
960 {
961 if (i > 0)
962 str << wxT(",");
963 str << attr.GetTabs()[i];
964 }
965 str << wxT("\"");
966 }
967 }
968
969 return str;
970 }
971
972 /// Get style parameters
973 bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara)
974 {
975 wxString fontFacename;
976 int fontSize = 12;
977 int fontFamily = wxDEFAULT;
978 int fontWeight = wxNORMAL;
979 int fontStyle = wxNORMAL;
980 bool fontUnderlined = false;
981
982 int fontFlags = 0;
983
984 fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString);
985 if (!fontFacename.IsEmpty())
986 fontFlags |= wxTEXT_ATTR_FONT_FACE;
987
988 wxString value;
989 //value = node->GetPropVal(wxT("fontfamily"), wxEmptyString);
990 //if (!value.empty())
991 // fontFamily = wxAtoi(value);
992
993 value = node->GetPropVal(wxT("fontstyle"), wxEmptyString);
994 if (!value.empty())
995 {
996 fontStyle = wxAtoi(value);
997 fontFlags |= wxTEXT_ATTR_FONT_ITALIC;
998 }
999
1000 value = node->GetPropVal(wxT("fontsize"), wxEmptyString);
1001 if (!value.empty())
1002 {
1003 fontSize = wxAtoi(value);
1004 fontFlags |= wxTEXT_ATTR_FONT_SIZE;
1005 }
1006
1007 value = node->GetPropVal(wxT("fontweight"), wxEmptyString);
1008 if (!value.empty())
1009 {
1010 fontWeight = wxAtoi(value);
1011 fontFlags |= wxTEXT_ATTR_FONT_WEIGHT;
1012 }
1013
1014 value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString);
1015 if (!value.empty())
1016 {
1017 fontUnderlined = wxAtoi(value) != 0;
1018 fontFlags |= wxTEXT_ATTR_FONT_UNDERLINE;
1019 }
1020
1021 attr.SetFlags(fontFlags);
1022
1023 if (attr.HasFlag(wxTEXT_ATTR_FONT))
1024 attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename));
1025
1026 // Restore correct font flags
1027 attr.SetFlags(fontFlags);
1028
1029 value = node->GetPropVal(wxT("textcolor"), wxEmptyString);
1030 if (!value.empty())
1031 {
1032 if (value[0] == wxT('#'))
1033 attr.SetTextColour(HexStringToColour(value.Mid(1)));
1034 else
1035 attr.SetTextColour(value);
1036 }
1037
1038 value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString);
1039 if (!value.empty())
1040 {
1041 if (value[0] == wxT('#'))
1042 attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
1043 else
1044 attr.SetBackgroundColour(value);
1045 }
1046
1047 value = node->GetPropVal(wxT("characterstyle"), wxEmptyString);
1048 if (!value.empty())
1049 attr.SetCharacterStyleName(value);
1050
1051 // Set paragraph attributes
1052 if (isPara)
1053 {
1054 value = node->GetPropVal(wxT("alignment"), wxEmptyString);
1055 if (!value.empty())
1056 attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
1057
1058 int leftSubIndent = 0;
1059 int leftIndent = 0;
1060 bool hasLeftIndent = false;
1061
1062 value = node->GetPropVal(wxT("leftindent"), wxEmptyString);
1063 if (!value.empty())
1064 {
1065 leftIndent = wxAtoi(value);
1066 hasLeftIndent = true;
1067 }
1068
1069 value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString);
1070 if (!value.empty())
1071 {
1072 leftSubIndent = wxAtoi(value);
1073 hasLeftIndent = true;
1074 }
1075
1076 if (hasLeftIndent)
1077 attr.SetLeftIndent(leftIndent, leftSubIndent);
1078
1079 value = node->GetPropVal(wxT("rightindent"), wxEmptyString);
1080 if (!value.empty())
1081 attr.SetRightIndent(wxAtoi(value));
1082
1083 value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString);
1084 if (!value.empty())
1085 attr.SetParagraphSpacingBefore(wxAtoi(value));
1086
1087 value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString);
1088 if (!value.empty())
1089 attr.SetParagraphSpacingAfter(wxAtoi(value));
1090
1091 value = node->GetPropVal(wxT("linespacing"), wxEmptyString);
1092 if (!value.empty())
1093 attr.SetLineSpacing(wxAtoi(value));
1094
1095 value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString);
1096 if (!value.empty())
1097 attr.SetBulletStyle(wxAtoi(value));
1098
1099 value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString);
1100 if (!value.empty())
1101 attr.SetBulletNumber(wxAtoi(value));
1102
1103 value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString);
1104 if (!value.empty())
1105 {
1106 wxChar ch = wxAtoi(value);
1107 wxString s;
1108 s << ch;
1109 attr.SetBulletText(s);
1110 }
1111
1112 value = node->GetPropVal(wxT("bullettext"), wxEmptyString);
1113 if (!value.empty())
1114 attr.SetBulletText(value);
1115
1116 value = node->GetPropVal(wxT("bulletfont"), wxEmptyString);
1117 if (!value.empty())
1118 attr.SetBulletFont(value);
1119
1120 value = node->GetPropVal(wxT("bulletname"), wxEmptyString);
1121 if (!value.empty())
1122 attr.SetBulletName(value);
1123
1124 value = node->GetPropVal(wxT("url"), wxEmptyString);
1125 if (!value.empty())
1126 attr.SetURL(value);
1127
1128 value = node->GetPropVal(wxT("parstyle"), wxEmptyString);
1129 if (!value.empty())
1130 attr.SetParagraphStyleName(value);
1131
1132 value = node->GetPropVal(wxT("liststyle"), wxEmptyString);
1133 if (!value.empty())
1134 attr.SetListStyleName(value);
1135
1136 value = node->GetPropVal(wxT("tabs"), wxEmptyString);
1137 if (!value.empty())
1138 {
1139 wxArrayInt tabs;
1140 wxStringTokenizer tkz(value, wxT(","));
1141 while (tkz.HasMoreTokens())
1142 {
1143 wxString token = tkz.GetNextToken();
1144 tabs.Add(wxAtoi(token));
1145 }
1146 attr.SetTabs(tabs);
1147 }
1148 }
1149
1150 return true;
1151 }
1152
1153 #endif
1154 // wxUSE_STREAMS
1155
1156 #endif
1157 // wxUSE_RICHTEXT && wxUSE_XML
1158