]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextxml.cpp
reformat for readability; more prep. for patch 1376506
[wxWidgets.git] / src / richtext / richtextxml.cpp
CommitLineData
5d7836c4 1/////////////////////////////////////////////////////////////////////////////
7fe8059f 2// Name: 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__
16 #pragma hdrstop
17#endif
18
fcdbeefa 19#if wxUSE_RICHTEXT && wxUSE_XML
b01ca8b6
JS
20
21#include "wx/richtext/richtextxml.h"
22
5d7836c4
JS
23#ifndef WX_PRECOMP
24 #include "wx/wx.h"
25#endif
26
5d7836c4
JS
27#include "wx/filename.h"
28#include "wx/clipbrd.h"
29#include "wx/wfstream.h"
30#include "wx/sstream.h"
31#include "wx/module.h"
32#include "wx/txtstrm.h"
33#include "wx/xml/xml.h"
34
5d7836c4
JS
35IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
36
37#if wxUSE_STREAMS
7fe8059f 38bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
5d7836c4
JS
39{
40 if (!stream.IsOk())
41 return false;
42
43 buffer->Clear();
44
45 wxXmlDocument* xmlDoc = new wxXmlDocument;
46 bool success = true;
47
48 if (!xmlDoc->Load(stream, wxT("ISO-8859-1")))
49 {
50 success = false;
51 }
52 else
53 {
54 if (xmlDoc->GetRoot() && xmlDoc->GetRoot()->GetType() == wxXML_ELEMENT_NODE && xmlDoc->GetRoot()->GetName() == wxT("richtext"))
55 {
56 wxXmlNode* child = xmlDoc->GetRoot()->GetChildren();
57 while (child)
58 {
59 if (child->GetType() == wxXML_ELEMENT_NODE)
60 {
61 wxString name = child->GetName();
62 if (name == wxT("richtext-version"))
63 {
64 }
65 else
66 ImportXML(buffer, child);
67 }
7fe8059f 68
5d7836c4
JS
69 child = child->GetNext();
70 }
71 }
72 else
73 {
74 success = false;
75 }
76 }
7fe8059f 77
5d7836c4
JS
78 delete xmlDoc;
79
80 buffer->UpdateRanges();
81
82 return success;
83}
84
85/// Recursively import an object
86bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
87{
88 wxString name = node->GetName();
89
90 bool doneChildren = false;
91
92 if (name == wxT("paragraphlayout"))
93 {
94 }
95 else if (name == wxT("paragraph"))
96 {
97 wxRichTextParagraph* para = new wxRichTextParagraph(buffer);
98 buffer->AppendChild(para);
99
100 GetStyle(para->GetAttributes(), node, true);
101
102 wxXmlNode* child = node->GetChildren();
103 while (child)
104 {
105 wxString childName = child->GetName();
106 if (childName == wxT("text"))
107 {
108 wxString text;
109 wxXmlNode* textChild = child->GetChildren();
110 while (textChild)
111 {
112 if (textChild->GetType() == wxXML_TEXT_NODE ||
113 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
114 {
115 wxString text2 = textChild->GetContent();
116
117 // Strip whitespace from end
118 if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('\n'))
119 text2 = text2.Mid(0, text2.Length()-1);
120
121 if (text2.Length() > 0 && text2[0] == wxT('"'))
122 text2 = text2.Mid(1);
123 if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('"'))
124 text2 = text2.Mid(0, text2.Length() - 1);
125
126 // TODO: further entity translation
127 text2.Replace(wxT("&lt;"), wxT("<"));
128 text2.Replace(wxT("&gt;"), wxT(">"));
129 text2.Replace(wxT("&amp;"), wxT("&"));
130 text2.Replace(wxT("&quot;"), wxT("\""));
131
132 text += text2;
133 }
134 textChild = textChild->GetNext();
135 }
136
137 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para);
138 GetStyle(textObject->GetAttributes(), child, false);
139
140 para->AppendChild(textObject);
141 }
142 else if (childName == wxT("image"))
143 {
144 int imageType = wxBITMAP_TYPE_PNG;
145 wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString);
7fe8059f 146 if (!value.empty())
5d7836c4
JS
147 imageType = wxAtoi(value);
148
149 wxString data;
150
151 wxXmlNode* imageChild = child->GetChildren();
152 while (imageChild)
153 {
154 wxString childName = imageChild->GetName();
155 if (childName == wxT("data"))
156 {
157 wxXmlNode* dataChild = imageChild->GetChildren();
158 while (dataChild)
159 {
160 data = dataChild->GetContent();
161 // wxLogDebug(data);
162 dataChild = dataChild->GetNext();
163 }
7fe8059f 164
5d7836c4
JS
165 }
166 imageChild = imageChild->GetNext();
167 }
168
7fe8059f 169 if (!data.empty())
5d7836c4
JS
170 {
171 wxRichTextImage* imageObj = new wxRichTextImage(para);
172 para->AppendChild(imageObj);
173
174 wxStringInputStream strStream(data);
175
176 imageObj->GetImageBlock().ReadHex(strStream, data.Length(), imageType);
177 }
178 }
179 child = child->GetNext();
180 }
181
182 doneChildren = true;
183 }
184
185 if (!doneChildren)
186 {
187 wxXmlNode* child = node->GetChildren();
188 while (child)
189 {
190 ImportXML(buffer, child);
191 child = child->GetNext();
192 }
7fe8059f 193 }
5d7836c4
JS
194
195 return true;
196}
197
198
199//-----------------------------------------------------------------------------
200// xml support routines
201//-----------------------------------------------------------------------------
202
203bool wxRichTextXMLHandler::HasParam(wxXmlNode* node, const wxString& param)
204{
205 return (GetParamNode(node, param) != NULL);
206}
207
208wxXmlNode *wxRichTextXMLHandler::GetParamNode(wxXmlNode* node, const wxString& param)
209{
210 wxCHECK_MSG(node, NULL, wxT("You can't access node data before it was initialized!"));
211
212 wxXmlNode *n = node->GetChildren();
213
214 while (n)
215 {
216 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
217 return n;
218 n = n->GetNext();
219 }
220 return NULL;
221}
222
223
224wxString wxRichTextXMLHandler::GetNodeContent(wxXmlNode *node)
225{
226 wxXmlNode *n = node;
227 if (n == NULL) return wxEmptyString;
228 n = n->GetChildren();
229
230 while (n)
231 {
232 if (n->GetType() == wxXML_TEXT_NODE ||
233 n->GetType() == wxXML_CDATA_SECTION_NODE)
234 return n->GetContent();
235 n = n->GetNext();
236 }
237 return wxEmptyString;
238}
239
240
241wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param)
242{
7fe8059f 243 if (param.empty())
5d7836c4
JS
244 return GetNodeContent(node);
245 else
246 return GetNodeContent(GetParamNode(node, param));
247}
248
249wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, bool WXUNUSED(translate))
250{
251 wxXmlNode *parNode = GetParamNode(node, param);
252 if (!parNode)
253 parNode = node;
254 wxString str1(GetNodeContent(parNode));
255 return str1;
256}
257
1e967276
JS
258// For use with earlier versions of wxWidgets
259#ifndef WXUNUSED_IN_UNICODE
260#if wxUSE_UNICODE
261#define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
262#else
263#define WXUNUSED_IN_UNICODE(x) x
264#endif
265#endif
266
5d7836c4
JS
267// write string to output:
268inline static void OutputString(wxOutputStream& stream, const wxString& str,
7fe8059f 269 wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL)
5d7836c4 270{
7fe8059f 271 if (str.empty()) return;
5d7836c4 272#if wxUSE_UNICODE
0bab774b
JS
273 if (convFile)
274 {
275 const wxWX2MBbuf buf(str.mb_str(*convFile));
276 stream.Write((const char*)buf, strlen((const char*)buf));
277 }
278 else
279 {
280 const wxWX2MBbuf buf(str.mb_str(wxConvUTF8));
281 stream.Write((const char*)buf, strlen((const char*)buf));
282 }
5d7836c4
JS
283#else
284 if ( convFile == NULL )
285 stream.Write(str.mb_str(), str.Len());
286 else
287 {
288 wxString str2(str.wc_str(*convMem), *convFile);
289 stream.Write(str2.mb_str(), str2.Len());
290 }
291#endif
292}
293
294// Same as above, but create entities first.
295// Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
296static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
297 wxMBConv *convMem = NULL, wxMBConv *convFile = NULL)
298{
299 wxString buf;
300 size_t i, last, len;
301 wxChar c;
7fe8059f 302
5d7836c4
JS
303 len = str.Len();
304 last = 0;
305 for (i = 0; i < len; i++)
306 {
307 c = str.GetChar(i);
308 if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
309 (c == wxT('&') && (str.Mid(i+1, 4) != wxT("amp;"))))
310 {
311 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
312 switch (c)
313 {
314 case wxT('<'):
315 OutputString(stream, wxT("&lt;"), NULL, NULL);
316 break;
317 case wxT('>'):
318 OutputString(stream, wxT("&gt;"), NULL, NULL);
319 break;
320 case wxT('&'):
321 OutputString(stream, wxT("&amp;"), NULL, NULL);
322 break;
323 case wxT('"'):
324 OutputString(stream, wxT("&quot;"), NULL, NULL);
325 break;
326 default: break;
327 }
328 last = i + 1;
329 }
330 }
331 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
332}
333
334inline static void OutputIndentation(wxOutputStream& stream, int indent)
335{
336 wxString str = wxT("\n");
337 for (int i = 0; i < indent; i++)
338 str << wxT(' ') << wxT(' ');
339 OutputString(stream, str, NULL, NULL);
340}
341
342static wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s)
343{
344 stream.Write(s, s.Length());
345 return stream;
346}
347
7383cf9d 348#if 0
5d7836c4
JS
349static wxOutputStream& operator <<(wxOutputStream& stream, long l)
350{
351 wxString str;
352 str.Printf(wxT("%ld"), l);
353 return stream << str;
354}
355
356static wxOutputStream& operator <<(wxOutputStream& stream, const char c)
357{
358 wxString str;
359 str.Printf(wxT("%c"), c);
360 return stream << str;
361}
7383cf9d 362#endif
5d7836c4
JS
363
364// Convert a colour to a 6-digit hex string
365static wxString ColourToHexString(const wxColour& col)
366{
367 wxString hex;
368
369 hex += wxDecToHex(col.Red());
370 hex += wxDecToHex(col.Green());
371 hex += wxDecToHex(col.Blue());
372
373 return hex;
374}
375
376// Convert 6-digit hex string to a colour
377wxColour HexStringToColour(const wxString& hex)
378{
7fe8059f
WS
379 unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
380 unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
381 unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
5d7836c4
JS
382
383 return wxColour(r, g, b);
384}
385
7fe8059f 386bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
5d7836c4
JS
387{
388 if (!stream.IsOk())
389 return false;
390
391 wxString version(wxT("1.0") ) ;
392#if wxUSE_UNICODE
393 wxString fileencoding(wxT("UTF-8")) ;
394 wxString memencoding(wxT("UTF-8")) ;
395#else
396 wxString fileencoding(wxT("ISO-8859-1")) ;
397 wxString memencoding(wxT("ISO-8859-1")) ;
398#endif
399 wxString s ;
7fe8059f 400
5d7836c4
JS
401 wxMBConv *convMem = NULL, *convFile = NULL;
402#if wxUSE_UNICODE
403 convFile = new wxCSConv(fileencoding);
404#else
405 if ( fileencoding != memencoding )
406 {
407 convFile = new wxCSConv(fileencoding);
408 convMem = new wxCSConv(memencoding);
409 }
410#endif
7fe8059f 411
5d7836c4
JS
412 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
413 (const wxChar*) version, (const wxChar*) fileencoding );
414 OutputString(stream, s, NULL, NULL);
415 OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL, NULL);
416
417 int level = 1;
418 ExportXML(stream, convMem, convFile, *buffer, level);
419
420 OutputString(stream, wxT("\n</richtext>") , NULL, NULL);
421 OutputString(stream, wxT("\n"), NULL, NULL);
7fe8059f 422
5d7836c4
JS
423 delete convFile;
424 delete convMem;
425
426 return true;
427}
428
429/// Recursively export an object
430bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent)
431{
432 wxString objectName;
433 if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox)))
434 objectName = wxT("paragraphlayout");
435 else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph)))
436 objectName = wxT("paragraph");
437 else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
438 objectName = wxT("text");
439 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
440 objectName = wxT("image");
441 else
442 objectName = wxT("object");
443
444 if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
445 {
446 wxRichTextPlainText& text = (wxRichTextPlainText&) obj;
447
448 OutputIndentation(stream, indent);
449 stream << wxT("<") << objectName;
7fe8059f 450
5d7836c4 451 wxString style = CreateStyle(obj.GetAttributes(), false);
7fe8059f 452
5d7836c4 453 stream << style << wxT(">");
7fe8059f 454
5d7836c4
JS
455 wxString str = text.GetText();
456 if (str.Length() > 0 && (str[0] == wxT(' ') || str[str.Length()-1] == wxT(' ')))
457 {
458 stream << wxT("\"");
459 OutputStringEnt(stream, str, convMem, convFile);
460 stream << wxT("\"");
461 }
462 else
463 OutputStringEnt(stream, str, convMem, convFile);
464 }
465 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
466 {
467 wxRichTextImage& imageObj = (wxRichTextImage&) obj;
468
469 if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok())
470 imageObj.MakeBlock();
471
472 OutputIndentation(stream, indent);
473 stream << wxT("<") << objectName;
474 if (!imageObj.GetImageBlock().Ok())
475 {
476 // No data
477 stream << wxT(">");
478 }
479 else
480 {
481 stream << wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) << wxT(">");
482 }
483
484 OutputIndentation(stream, indent+1);
485 stream << wxT("<data>");
486
487 imageObj.GetImageBlock().WriteHex(stream);
488
489 stream << wxT("</data>");
490 }
491 else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
492 {
493 OutputIndentation(stream, indent);
494 stream << wxT("<") << objectName;
7fe8059f 495
5d7836c4
JS
496 bool isPara = false;
497 if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
498 isPara = true;
499
500 wxString style = CreateStyle(obj.GetAttributes(), isPara);
7fe8059f 501
5d7836c4 502 stream << style << wxT(">");
7fe8059f 503
5d7836c4
JS
504 wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj;
505 size_t i;
506 for (i = 0; i < composite.GetChildCount(); i++)
507 {
508 wxRichTextObject* child = composite.GetChild(i);
509 ExportXML(stream, convMem, convFile, *child, indent+1);
510 }
511 }
512
513 if (objectName != wxT("text"))
514 OutputIndentation(stream, indent);
515
516 stream << wxT("</") << objectName << wxT(">");
517
518 return true;
519}
520
521/// Create style parameters
522wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara)
523{
524 wxString str;
525 if (attr.GetTextColour().Ok())
526 {
527 str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
528 }
529 if (attr.GetBackgroundColour().Ok())
530 {
531 str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
532 }
533
534 if (attr.GetFont().Ok())
535 {
536 str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\"");
537 str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
538 str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\"");
539 str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\"");
540 str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\"");
541 str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\"");
542 }
543
7fe8059f 544 if (!attr.GetCharacterStyleName().empty())
5d7836c4
JS
545 str << wxT(" charactertyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
546
547 if (isPara)
548 {
549 str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\"");
550 str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\"");
551 str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\"");
552 str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\"");
553 str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\"");
554 str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\"");
555 str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\"");
556 str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\"");
557 str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\"");
558 str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\"");
559
7fe8059f 560 if (!attr.GetParagraphStyleName().empty())
5d7836c4
JS
561 str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
562 }
563
564 return str;
565}
566
567/// Get style parameters
568bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara)
569{
570 wxString fontFacename;
571 int fontSize = 12;
572 int fontFamily = wxDEFAULT;
573 int fontWeight = wxNORMAL;
574 int fontStyle = wxNORMAL;
575 bool fontUnderlined = false;
576
577 fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString);
578
579 wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString);
7fe8059f 580 if (!value.empty())
5d7836c4
JS
581 fontFamily = wxAtoi(value);
582
583 value = node->GetPropVal(wxT("fontstyle"), wxEmptyString);
7fe8059f 584 if (!value.empty())
5d7836c4
JS
585 fontStyle = wxAtoi(value);
586
587 value = node->GetPropVal(wxT("fontsize"), wxEmptyString);
7fe8059f 588 if (!value.empty())
5d7836c4
JS
589 fontSize = wxAtoi(value);
590
591 value = node->GetPropVal(wxT("fontweight"), wxEmptyString);
7fe8059f 592 if (!value.empty())
5d7836c4
JS
593 fontWeight = wxAtoi(value);
594
595 value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString);
7fe8059f 596 if (!value.empty())
5d7836c4
JS
597 fontUnderlined = wxAtoi(value) != 0;
598
599 attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename));
600
601 value = node->GetPropVal(wxT("textcolor"), wxEmptyString);
7fe8059f 602 if (!value.empty())
5d7836c4
JS
603 {
604 if (value[0] == wxT('#'))
605 attr.SetTextColour(HexStringToColour(value.Mid(1)));
606 else
607 attr.SetTextColour(value);
608 }
609
610 value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString);
7fe8059f 611 if (!value.empty())
5d7836c4
JS
612 {
613 if (value[0] == wxT('#'))
614 attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
615 else
616 attr.SetBackgroundColour(value);
617 }
618
619 value = node->GetPropVal(wxT("characterstyle"), wxEmptyString);
7fe8059f 620 if (!value.empty())
5d7836c4
JS
621 attr.SetCharacterStyleName(value);
622
623 // Set paragraph attributes
624 if (isPara)
625 {
626 value = node->GetPropVal(wxT("alignment"), wxEmptyString);
7fe8059f 627 if (!value.empty())
5d7836c4
JS
628 attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
629
630 int leftSubIndent = 0;
631 int leftIndent = 0;
632 value = node->GetPropVal(wxT("leftindent"), wxEmptyString);
7fe8059f 633 if (!value.empty())
5d7836c4
JS
634 leftIndent = wxAtoi(value);
635 value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString);
7fe8059f 636 if (!value.empty())
5d7836c4
JS
637 leftSubIndent = wxAtoi(value);
638 attr.SetLeftIndent(leftIndent, leftSubIndent);
639
640 value = node->GetPropVal(wxT("rightindent"), wxEmptyString);
7fe8059f 641 if (!value.empty())
5d7836c4
JS
642 attr.SetRightIndent(wxAtoi(value));
643
644 value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString);
7fe8059f 645 if (!value.empty())
5d7836c4
JS
646 attr.SetParagraphSpacingBefore(wxAtoi(value));
647
648 value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString);
7fe8059f 649 if (!value.empty())
5d7836c4
JS
650 attr.SetParagraphSpacingAfter(wxAtoi(value));
651
652 value = node->GetPropVal(wxT("linespacing"), wxEmptyString);
7fe8059f 653 if (!value.empty())
5d7836c4
JS
654 attr.SetLineSpacing(wxAtoi(value));
655
656 value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString);
7fe8059f 657 if (!value.empty())
5d7836c4
JS
658 attr.SetBulletStyle(wxAtoi(value));
659
660 value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString);
7fe8059f 661 if (!value.empty())
5d7836c4
JS
662 attr.SetBulletNumber(wxAtoi(value));
663
664 value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString);
7fe8059f 665 if (!value.empty())
5d7836c4
JS
666 attr.SetBulletSymbol(value[0]);
667
668 value = node->GetPropVal(wxT("parstyle"), wxEmptyString);
7fe8059f 669 if (!value.empty())
5d7836c4
JS
670 attr.SetParagraphStyleName(value);
671 }
672
673 return true;
674}
675
676#endif
677
678IMPLEMENT_DYNAMIC_CLASS(wxRichTextHTMLHandler, wxRichTextFileHandler)
679
680/// Can we handle this filename (if using files)? By default, checks the extension.
681bool wxRichTextHTMLHandler::CanHandle(const wxString& filename) const
682{
683 wxString path, file, ext;
684 wxSplitPath(filename, & path, & file, & ext);
685
686 return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm"));
687}
688
689
690#if wxUSE_STREAMS
7fe8059f 691bool wxRichTextHTMLHandler::DoLoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream))
5d7836c4
JS
692{
693 return false;
694}
695
696/*
697 * We need to output only _changes_ in character formatting.
698 */
699
7fe8059f 700bool wxRichTextHTMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
5d7836c4
JS
701{
702 buffer->Defragment();
703
704 wxTextOutputStream str(stream);
705
706 wxTextAttrEx currentParaStyle = buffer->GetAttributes();
707 wxTextAttrEx currentCharStyle = buffer->GetAttributes();
708
709 str << wxT("<html><head></head><body>\n");
710
711 wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst();
712 while (node)
713 {
714 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
715 wxASSERT (para != NULL);
716
717 if (para)
718 {
719 OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, true);
720
721 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
722 while (node2)
723 {
724 wxRichTextObject* obj = node2->GetData();
725 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
726 if (textObj && !textObj->IsEmpty())
727 {
728 OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, true);
729
730 str << textObj->GetText();
731
732 OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, false);
733 }
734
735 node2 = node2->GetNext();
7fe8059f 736 }
5d7836c4
JS
737
738 OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, false);
739
740 str << wxT("<P>\n");
741 }
742
743 node = node->GetNext();
744 }
745
746 str << wxT("</body></html>\n");
747
748 return true;
749}
750
751/// Output character formatting
752void wxRichTextHTMLHandler::OutputCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start)
753{
754 wxTextOutputStream str(stream);
755
756 bool isBold = false;
757 bool isItalic = false;
758 bool isUnderline = false;
759 wxString faceName;
760
761 if (thisStyle.GetFont().Ok())
762 {
763 if (thisStyle.GetFont().GetWeight() == wxBOLD)
764 isBold = true;
765 if (thisStyle.GetFont().GetStyle() == wxITALIC)
766 isItalic = true;
767 if (thisStyle.GetFont().GetUnderlined())
768 isUnderline = true;
769
770 faceName = thisStyle.GetFont().GetFaceName();
771 }
772
773 if (start)
774 {
775 if (isBold)
776 str << wxT("<b>");
777 if (isItalic)
778 str << wxT("<i>");
779 if (isUnderline)
780 str << wxT("<u>");
781 }
782 else
783 {
784 if (isUnderline)
785 str << wxT("</u>");
786 if (isItalic)
787 str << wxT("</i>");
788 if (isBold)
789 str << wxT("</b>");
790 }
791}
792
793/// Output paragraph formatting
794void wxRichTextHTMLHandler::OutputParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start)
795{
796 // TODO: lists, indentation (using tables), fonts, right-align, ...
797
798 wxTextOutputStream str(stream);
799 bool isCentered = false;
800
801 if (thisStyle.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
802 {
803 isCentered = true;
804 }
805
806 if (start)
807 {
808 if (isCentered)
809 str << wxT("<center>");
810 }
811 else
812 {
813 if (isCentered)
814 str << wxT("</center>");
815 }
816}
817
818#endif
819
820#endif
fcdbeefa 821 // wxUSE_RICHTEXT && wxUSE_XML