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