]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextxml.cpp
compilation fix for !wxUSE_MIMETYPE
[wxWidgets.git] / src / richtext / richtextxml.cpp
CommitLineData
5d7836c4 1/////////////////////////////////////////////////////////////////////////////
88a7a4e1 2// Name: src/richtext/richtextxml.cpp
5d7836c4
JS
3// Purpose: XML and HTML I/O for wxRichTextCtrl
4// Author: Julian Smart
7fe8059f 5// Modified by:
5d7836c4 6// Created: 2005-09-30
7fe8059f 7// RCS-ID: $Id$
5d7836c4
JS
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__
61399247 16 #pragma hdrstop
5d7836c4
JS
17#endif
18
fcdbeefa 19#if wxUSE_RICHTEXT && wxUSE_XML
b01ca8b6
JS
20
21#include "wx/richtext/richtextxml.h"
22
5d7836c4 23#ifndef WX_PRECOMP
88a7a4e1 24 #include "wx/intl.h"
02761f6c 25 #include "wx/module.h"
5d7836c4
JS
26#endif
27
5d7836c4
JS
28#include "wx/filename.h"
29#include "wx/clipbrd.h"
30#include "wx/wfstream.h"
31#include "wx/sstream.h"
5d7836c4 32#include "wx/txtstrm.h"
0ca07313 33#include "wx/tokenzr.h"
5d7836c4
JS
34#include "wx/xml/xml.h"
35
5d7836c4
JS
36IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
37
38#if wxUSE_STREAMS
7fe8059f 39bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
5d7836c4
JS
40{
41 if (!stream.IsOk())
42 return false;
43
85d8909b 44 buffer->ResetAndClearCommands();
5d7836c4
JS
45
46 wxXmlDocument* xmlDoc = new wxXmlDocument;
47 bool success = true;
48
b71e9aa4
JS
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))
5d7836c4
JS
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 }
7fe8059f 76
5d7836c4
JS
77 child = child->GetNext();
78 }
79 }
80 else
81 {
82 success = false;
83 }
84 }
7fe8059f 85
5d7836c4
JS
86 delete xmlDoc;
87
88 buffer->UpdateRanges();
89
90 return success;
91}
92
93/// Recursively import an object
94bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
95{
96 wxString name = node->GetName();
97
98 bool doneChildren = false;
99
100 if (name == wxT("paragraphlayout"))
101 {
0ca07313
JS
102 wxString partial = node->GetPropVal(wxT("partialparagraph"), wxEmptyString);
103 if (partial == wxT("true"))
104 buffer->SetPartialParagraph(true);
5d7836c4
JS
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
88a7a4e1
WS
129 if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
130 text2 = text2.Mid(0, text2.length()-1);
5d7836c4 131
88a7a4e1 132 if (!text2.empty() && text2[0] == wxT('"'))
5d7836c4 133 text2 = text2.Mid(1);
88a7a4e1
WS
134 if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
135 text2 = text2.Mid(0, text2.length() - 1);
5d7836c4 136
5d7836c4
JS
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 }
7b907278
JS
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 }
5d7836c4
JS
171 else if (childName == wxT("image"))
172 {
173 int imageType = wxBITMAP_TYPE_PNG;
174 wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString);
7fe8059f 175 if (!value.empty())
5d7836c4
JS
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 }
7fe8059f 193
5d7836c4
JS
194 }
195 imageChild = imageChild->GetNext();
196 }
197
7fe8059f 198 if (!data.empty())
5d7836c4
JS
199 {
200 wxRichTextImage* imageObj = new wxRichTextImage(para);
201 para->AppendChild(imageObj);
202
203 wxStringInputStream strStream(data);
204
88a7a4e1 205 imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType);
5d7836c4
JS
206 }
207 }
208 child = child->GetNext();
209 }
210
211 doneChildren = true;
212 }
d2d0adc7
JS
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 }
5d7836c4
JS
234
235 if (!doneChildren)
236 {
237 wxXmlNode* child = node->GetChildren();
238 while (child)
239 {
240 ImportXML(buffer, child);
241 child = child->GetNext();
242 }
7fe8059f 243 }
5d7836c4
JS
244
245 return true;
246}
247
d2d0adc7
JS
248bool 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}
5d7836c4
JS
336
337//-----------------------------------------------------------------------------
338// xml support routines
339//-----------------------------------------------------------------------------
340
341bool wxRichTextXMLHandler::HasParam(wxXmlNode* node, const wxString& param)
342{
343 return (GetParamNode(node, param) != NULL);
344}
345
346wxXmlNode *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
362wxString 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
379wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param)
380{
7fe8059f 381 if (param.empty())
5d7836c4
JS
382 return GetNodeContent(node);
383 else
384 return GetNodeContent(GetParamNode(node, param));
385}
386
387wxString 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
1e967276
JS
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
5d7836c4
JS
405// write string to output:
406inline static void OutputString(wxOutputStream& stream, const wxString& str,
7fe8059f 407 wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL)
5d7836c4 408{
7fe8059f 409 if (str.empty()) return;
5d7836c4 410#if wxUSE_UNICODE
0bab774b
JS
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 }
5d7836c4
JS
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;"
434static 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;
7fe8059f 440
5d7836c4
JS
441 len = str.Len();
442 last = 0;
443 for (i = 0; i < len; i++)
444 {
445 c = str.GetChar(i);
88a7a4e1 446
b71e9aa4
JS
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
5d7836c4 451 if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
b71e9aa4 452 (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
5d7836c4
JS
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 }
07854e5e 473 else if (wxUChar(c) > 127)
7b907278
JS
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 }
5d7836c4
JS
483 }
484 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
485}
486
487inline 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
5d7836c4
JS
495// Convert a colour to a 6-digit hex string
496static 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
b71e9aa4 508static wxColour HexStringToColour(const wxString& hex)
5d7836c4 509{
7fe8059f
WS
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));
5d7836c4
JS
513
514 return wxColour(r, g, b);
515}
516
7fe8059f 517bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
5d7836c4
JS
518{
519 if (!stream.IsOk())
520 return false;
521
522 wxString version(wxT("1.0") ) ;
b71e9aa4
JS
523
524 bool deleteConvFile = false;
525 wxString fileEncoding;
526 wxMBConv* convFile = NULL;
527
5d7836c4 528#if wxUSE_UNICODE
b71e9aa4
JS
529 fileEncoding = wxT("UTF-8");
530 convFile = & wxConvUTF8;
5d7836c4 531#else
b71e9aa4
JS
532 fileEncoding = wxT("ISO-8859-1");
533 convFile = & wxConvISO8859_1;
5d7836c4 534#endif
7fe8059f 535
b71e9aa4 536 // If SetEncoding has been called, change the output encoding.
88a7a4e1 537 if (!m_encoding.empty() && m_encoding.Lower() != fileEncoding.Lower())
b71e9aa4
JS
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
88a7a4e1 549 if (fileEncoding.empty())
5d7836c4 550#if wxUSE_UNICODE
b71e9aa4 551 fileEncoding = wxT("UTF-8");
5d7836c4 552#else
b71e9aa4
JS
553 fileEncoding = wxT("ISO-8859-1");
554#endif
555 convFile = new wxCSConv(fileEncoding);
556 deleteConvFile = true;
5d7836c4 557 }
b71e9aa4
JS
558
559#if !wxUSE_UNICODE
560 wxMBConv* convMem = wxConvCurrent;
561#else
562 wxMBConv* convMem = NULL;
5d7836c4 563#endif
7fe8059f 564
b71e9aa4 565 wxString s ;
5d7836c4 566 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
b71e9aa4 567 (const wxChar*) version, (const wxChar*) fileEncoding );
5d7836c4
JS
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;
5d7836c4 572
d2d0adc7
JS
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
5d7836c4
JS
605 OutputString(stream, wxT("\n</richtext>") , NULL, NULL);
606 OutputString(stream, wxT("\n"), NULL, NULL);
7fe8059f 607
b71e9aa4
JS
608 if (deleteConvFile)
609 delete convFile;
88a7a4e1 610
b71e9aa4 611 return success;
5d7836c4
JS
612}
613
614/// Recursively export an object
615bool 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");
7b907278
JS
628
629 bool terminateTag = true;
5d7836c4
JS
630
631 if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
632 {
7b907278
JS
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);
5d7836c4 650
7b907278 651 OutputString(stream, style + wxT(">"), convMem, convFile);
7fe8059f 652
7b907278
JS
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);
7fe8059f 662
7b907278
JS
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);
7fe8059f 672
7b907278
JS
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)
5d7836c4 687 {
7b907278
JS
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);
5d7836c4
JS
701 }
702 else
7b907278 703 terminateTag = false;
5d7836c4
JS
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);
b71e9aa4 713 OutputString(stream, wxT("<") + objectName, convMem, convFile);
5d7836c4
JS
714 if (!imageObj.GetImageBlock().Ok())
715 {
716 // No data
b71e9aa4 717 OutputString(stream, wxT(">"), convMem, convFile);
5d7836c4
JS
718 }
719 else
720 {
b71e9aa4 721 OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj.GetImageBlock().GetImageType()));
5d7836c4
JS
722 }
723
724 OutputIndentation(stream, indent+1);
b71e9aa4 725 OutputString(stream, wxT("<data>"), convMem, convFile);
5d7836c4
JS
726
727 imageObj.GetImageBlock().WriteHex(stream);
728
b71e9aa4 729 OutputString(stream, wxT("</data>"), convMem, convFile);
5d7836c4
JS
730 }
731 else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
732 {
733 OutputIndentation(stream, indent);
b71e9aa4 734 OutputString(stream, wxT("<") + objectName, convMem, convFile);
7fe8059f 735
5d7836c4
JS
736 bool isPara = false;
737 if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
738 isPara = true;
739
740 wxString style = CreateStyle(obj.GetAttributes(), isPara);
7b907278 741
0ca07313
JS
742 if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph())
743 style << wxT(" partialparagraph=\"true\"");
744
b71e9aa4 745 OutputString(stream, style + wxT(">"), convMem, convFile);
7fe8059f 746
5d7836c4
JS
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
7b907278
JS
759 if (terminateTag)
760 OutputString(stream, wxT("</") + objectName + wxT(">"), convMem, convFile);
5d7836c4
JS
761
762 return true;
763}
764
d2d0adc7
JS
765bool 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
5d7836c4
JS
865/// Create style parameters
866wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara)
867{
868 wxString str;
0ca07313 869 if (attr.HasTextColour() && attr.GetTextColour().Ok())
5d7836c4
JS
870 {
871 str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
872 }
0ca07313 873 if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok())
5d7836c4
JS
874 {
875 str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
876 }
877
878 if (attr.GetFont().Ok())
879 {
0ca07313
JS
880 if (attr.HasSize())
881 str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\"");
7b907278 882
0ca07313
JS
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("\"");
5d7836c4
JS
897 }
898
7fe8059f 899 if (!attr.GetCharacterStyleName().empty())
f089713f 900 str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
5d7836c4
JS
901
902 if (isPara)
903 {
0ca07313
JS
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
d2d0adc7 931 if (attr.HasBulletText())
7b907278 932 {
d2d0adc7
JS
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
7b907278
JS
940 str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\"");
941 }
5d7836c4 942
f089713f
JS
943 if (attr.HasBulletName())
944 str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\"");
945
d2d0adc7
JS
946 if (attr.HasURL())
947 str << wxT(" url=\"") << attr.GetURL() << wxT("\"");
948
7fe8059f 949 if (!attr.GetParagraphStyleName().empty())
5d7836c4 950 str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
7b907278 951
f089713f
JS
952 if (!attr.GetListStyleName().empty())
953 str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\"");
954
0ca07313
JS
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 }
7b907278 965 str << wxT("\"");
0ca07313 966 }
5d7836c4
JS
967 }
968
969 return str;
970}
971
972/// Get style parameters
973bool 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;
7b907278 981
0ca07313
JS
982 int fontFlags = 0;
983
5d7836c4 984 fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString);
0ca07313
JS
985 if (!fontFacename.IsEmpty())
986 fontFlags |= wxTEXT_ATTR_FONT_FACE;
5d7836c4 987
0ca07313
JS
988 wxString value;
989 //value = node->GetPropVal(wxT("fontfamily"), wxEmptyString);
990 //if (!value.empty())
991 // fontFamily = wxAtoi(value);
5d7836c4
JS
992
993 value = node->GetPropVal(wxT("fontstyle"), wxEmptyString);
7fe8059f 994 if (!value.empty())
0ca07313 995 {
5d7836c4 996 fontStyle = wxAtoi(value);
0ca07313
JS
997 fontFlags |= wxTEXT_ATTR_FONT_ITALIC;
998 }
5d7836c4
JS
999
1000 value = node->GetPropVal(wxT("fontsize"), wxEmptyString);
7fe8059f 1001 if (!value.empty())
0ca07313 1002 {
5d7836c4 1003 fontSize = wxAtoi(value);
0ca07313
JS
1004 fontFlags |= wxTEXT_ATTR_FONT_SIZE;
1005 }
5d7836c4
JS
1006
1007 value = node->GetPropVal(wxT("fontweight"), wxEmptyString);
7fe8059f 1008 if (!value.empty())
0ca07313 1009 {
5d7836c4 1010 fontWeight = wxAtoi(value);
0ca07313
JS
1011 fontFlags |= wxTEXT_ATTR_FONT_WEIGHT;
1012 }
5d7836c4
JS
1013
1014 value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString);
7fe8059f 1015 if (!value.empty())
0ca07313 1016 {
5d7836c4 1017 fontUnderlined = wxAtoi(value) != 0;
0ca07313
JS
1018 fontFlags |= wxTEXT_ATTR_FONT_UNDERLINE;
1019 }
7b907278 1020
0ca07313 1021 attr.SetFlags(fontFlags);
7b907278 1022
0ca07313
JS
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);
7b907278 1028
5d7836c4 1029 value = node->GetPropVal(wxT("textcolor"), wxEmptyString);
7fe8059f 1030 if (!value.empty())
5d7836c4
JS
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);
7fe8059f 1039 if (!value.empty())
5d7836c4
JS
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);
7fe8059f 1048 if (!value.empty())
5d7836c4
JS
1049 attr.SetCharacterStyleName(value);
1050
1051 // Set paragraph attributes
1052 if (isPara)
1053 {
1054 value = node->GetPropVal(wxT("alignment"), wxEmptyString);
7fe8059f 1055 if (!value.empty())
5d7836c4
JS
1056 attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
1057
1058 int leftSubIndent = 0;
1059 int leftIndent = 0;
0ca07313 1060 bool hasLeftIndent = false;
7b907278 1061
5d7836c4 1062 value = node->GetPropVal(wxT("leftindent"), wxEmptyString);
7fe8059f 1063 if (!value.empty())
0ca07313 1064 {
5d7836c4 1065 leftIndent = wxAtoi(value);
0ca07313
JS
1066 hasLeftIndent = true;
1067 }
1068
5d7836c4 1069 value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString);
7fe8059f 1070 if (!value.empty())
0ca07313 1071 {
5d7836c4 1072 leftSubIndent = wxAtoi(value);
0ca07313
JS
1073 hasLeftIndent = true;
1074 }
1075
1076 if (hasLeftIndent)
1077 attr.SetLeftIndent(leftIndent, leftSubIndent);
5d7836c4
JS
1078
1079 value = node->GetPropVal(wxT("rightindent"), wxEmptyString);
7fe8059f 1080 if (!value.empty())
5d7836c4
JS
1081 attr.SetRightIndent(wxAtoi(value));
1082
1083 value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString);
7fe8059f 1084 if (!value.empty())
5d7836c4
JS
1085 attr.SetParagraphSpacingBefore(wxAtoi(value));
1086
1087 value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString);
7fe8059f 1088 if (!value.empty())
5d7836c4
JS
1089 attr.SetParagraphSpacingAfter(wxAtoi(value));
1090
1091 value = node->GetPropVal(wxT("linespacing"), wxEmptyString);
7fe8059f 1092 if (!value.empty())
5d7836c4
JS
1093 attr.SetLineSpacing(wxAtoi(value));
1094
1095 value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString);
7fe8059f 1096 if (!value.empty())
5d7836c4
JS
1097 attr.SetBulletStyle(wxAtoi(value));
1098
1099 value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString);
7fe8059f 1100 if (!value.empty())
5d7836c4
JS
1101 attr.SetBulletNumber(wxAtoi(value));
1102
1103 value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString);
7fe8059f 1104 if (!value.empty())
d2d0adc7
JS
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);
7b907278
JS
1115
1116 value = node->GetPropVal(wxT("bulletfont"), wxEmptyString);
1117 if (!value.empty())
1118 attr.SetBulletFont(value);
5d7836c4 1119
f089713f
JS
1120 value = node->GetPropVal(wxT("bulletname"), wxEmptyString);
1121 if (!value.empty())
1122 attr.SetBulletName(value);
1123
d2d0adc7
JS
1124 value = node->GetPropVal(wxT("url"), wxEmptyString);
1125 if (!value.empty())
1126 attr.SetURL(value);
1127
5d7836c4 1128 value = node->GetPropVal(wxT("parstyle"), wxEmptyString);
7fe8059f 1129 if (!value.empty())
5d7836c4 1130 attr.SetParagraphStyleName(value);
7b907278 1131
f089713f
JS
1132 value = node->GetPropVal(wxT("liststyle"), wxEmptyString);
1133 if (!value.empty())
1134 attr.SetListStyleName(value);
1135
0ca07313
JS
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 }
5d7836c4
JS
1148 }
1149
1150 return true;
1151}
1152
1153#endif
b71e9aa4 1154 // wxUSE_STREAMS
88a7a4e1 1155
5d7836c4 1156#endif
fcdbeefa 1157 // wxUSE_RICHTEXT && wxUSE_XML
7b907278 1158