]> git.saurik.com Git - wxWidgets.git/blame - src/xrc/xml.cpp
Start() should resume the stopwatch if it is suspended
[wxWidgets.git] / src / xrc / xml.cpp
CommitLineData
78d14f80
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: xml.cpp
3// Purpose: wxXmlDocument - XML parser & data holder class
4// Author: Vaclav Slavik
5// Created: 2000/03/05
6// RCS-ID: $Id$
7// Copyright: (c) 2000 Vaclav Slavik
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11#ifdef __GNUG__
12#pragma implementation "xml.h"
78d14f80
VS
13#endif
14
15// For compilers that support precompilation, includes "wx.h".
16#include "wx/wxprec.h"
17
18#ifdef __BORLANDC__
19 #pragma hdrstop
20#endif
21
22
23#include "wx/wfstream.h"
24#include "wx/datstrm.h"
25#include "wx/zstream.h"
26#include "wx/log.h"
27#include "wx/intl.h"
4d876ee3 28#include "wx/strconv.h"
78d14f80
VS
29
30#include "wx/xrc/xml.h"
78d14f80 31
4d876ee3 32#include "xmlparse.h" // from Expat
78d14f80 33
4d876ee3
VS
34//-----------------------------------------------------------------------------
35// wxXmlNode
36//-----------------------------------------------------------------------------
78d14f80
VS
37
38wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
39 const wxString& name, const wxString& content,
40 wxXmlProperty *props, wxXmlNode *next)
41 : m_type(type), m_name(name), m_content(content),
42 m_properties(props), m_parent(parent),
43 m_children(NULL), m_next(next)
44{
45 if (m_parent)
46 {
47 if (m_parent->m_children)
48 {
49 m_next = m_parent->m_children;
50 m_parent->m_children = this;
51 }
52 else
53 m_parent->m_children = this;
54 }
55}
56
78d14f80
VS
57wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name,
58 const wxString& content)
59 : m_type(type), m_name(name), m_content(content),
60 m_properties(NULL), m_parent(NULL),
61 m_children(NULL), m_next(NULL)
62{}
63
78d14f80
VS
64wxXmlNode::wxXmlNode(const wxXmlNode& node)
65{
66 m_next = NULL;
67 m_parent = NULL;
68 DoCopy(node);
69}
70
93cd80d6
VS
71wxXmlNode::~wxXmlNode()
72{
73 wxXmlNode *c, *c2;
74 for (c = m_children; c; c = c2)
75 {
76 c2 = c->m_next;
77 delete c;
78 }
79
80 wxXmlProperty *p, *p2;
81 for (p = m_properties; p; p = p2)
82 {
83 p2 = p->GetNext();
84 delete p;
85 }
86}
87
78d14f80
VS
88wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node)
89{
3fb676f8
GT
90 wxDELETE(m_properties);
91 wxDELETE(m_children);
78d14f80
VS
92 DoCopy(node);
93 return *this;
94}
95
78d14f80
VS
96void wxXmlNode::DoCopy(const wxXmlNode& node)
97{
98 m_type = node.m_type;
99 m_name = node.m_name;
100 m_content = node.m_content;
101 m_children = NULL;
102
103 wxXmlNode *n = node.m_children;
104 while (n)
105 {
106 AddChild(new wxXmlNode(*n));
107 n = n->GetNext();
108 }
109
110 m_properties = NULL;
111 wxXmlProperty *p = node.m_properties;
112 while (p)
113 {
114 AddProperty(p->GetName(), p->GetValue());
115 p = p->GetNext();
116 }
117}
118
78d14f80
VS
119bool wxXmlNode::HasProp(const wxString& propName) const
120{
121 wxXmlProperty *prop = GetProperties();
122
123 while (prop)
124 {
125 if (prop->GetName() == propName) return TRUE;
126 prop = prop->GetNext();
127 }
128
129 return FALSE;
130}
131
78d14f80
VS
132bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
133{
134 wxXmlProperty *prop = GetProperties();
135
136 while (prop)
137 {
138 if (prop->GetName() == propName)
139 {
140 *value = prop->GetValue();
141 return TRUE;
142 }
143 prop = prop->GetNext();
144 }
145
146 return FALSE;
147}
148
78d14f80
VS
149wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
150{
151 wxString tmp;
152 if (GetPropVal(propName, &tmp))
153 return tmp;
154 else
155 return defaultVal;
156}
157
78d14f80
VS
158void wxXmlNode::AddChild(wxXmlNode *child)
159{
160 if (m_children == NULL)
161 m_children = child;
162 else
163 {
164 wxXmlNode *ch = m_children;
165 while (ch->m_next) ch = ch->m_next;
166 ch->m_next = child;
167 }
168 child->m_next = NULL;
169 child->m_parent = this;
170}
171
78d14f80
VS
172void wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
173{
174 wxASSERT_MSG(before_node->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
175
176 if (m_children == before_node)
177 m_children = child;
178 else
179 {
180 wxXmlNode *ch = m_children;
181 while (ch->m_next != before_node) ch = ch->m_next;
182 ch->m_next = child;
183 }
184
185 child->m_parent = this;
186 child->m_next = before_node;
187}
188
78d14f80
VS
189bool wxXmlNode::RemoveChild(wxXmlNode *child)
190{
191 if (m_children == NULL)
192 return FALSE;
193 else if (m_children == child)
194 {
195 m_children = child->m_next;
196 child->m_parent = NULL;
197 child->m_next = NULL;
198 return TRUE;
199 }
200 else
201 {
202 wxXmlNode *ch = m_children;
203 while (ch->m_next)
204 {
205 if (ch->m_next == child)
206 {
207 ch->m_next = child->m_next;
208 child->m_parent = NULL;
209 child->m_next = NULL;
210 return TRUE;
211 }
212 ch = ch->m_next;
213 }
214 return FALSE;
215 }
216}
217
78d14f80
VS
218void wxXmlNode::AddProperty(const wxString& name, const wxString& value)
219{
220 AddProperty(new wxXmlProperty(name, value, NULL));
221}
222
223void wxXmlNode::AddProperty(wxXmlProperty *prop)
224{
225 if (m_properties == NULL)
226 m_properties = prop;
227 else
228 {
229 wxXmlProperty *p = m_properties;
230 while (p->GetNext()) p = p->GetNext();
231 p->SetNext(prop);
232 }
233}
234
78d14f80
VS
235bool wxXmlNode::DeleteProperty(const wxString& name)
236{
46b8b00b
GT
237 wxXmlProperty *prop;
238
78d14f80
VS
239 if (m_properties == NULL)
240 return FALSE;
241
242 else if (m_properties->GetName() == name)
243 {
46b8b00b 244 prop = m_properties;
78d14f80
VS
245 m_properties = prop->GetNext();
246 prop->SetNext(NULL);
247 delete prop;
248 return TRUE;
249 }
250
251 else
252 {
253 wxXmlProperty *p = m_properties;
254 while (p->GetNext())
255 {
256 if (p->GetNext()->GetName() == name)
257 {
46b8b00b 258 prop = p->GetNext();
78d14f80
VS
259 p->SetNext(prop->GetNext());
260 prop->SetNext(NULL);
261 delete prop;
262 return TRUE;
263 }
264 p = p->GetNext();
265 }
266 return FALSE;
267 }
268}
269
270
271
4d876ee3
VS
272//-----------------------------------------------------------------------------
273// wxXmlDocument
274//-----------------------------------------------------------------------------
78d14f80 275
5dac8a3b
VS
276wxXmlDocument::wxXmlDocument()
277 : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL)
278{
279#if !wxUSE_UNICODE
280 m_encoding = wxT("UTF-8");
281#endif
282}
283
4d876ee3 284wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
78d14f80
VS
285 : wxObject(), m_root(NULL)
286{
4d876ee3 287 if ( !Load(filename, encoding) )
78d14f80 288 {
3fb676f8 289 wxDELETE(m_root);
78d14f80
VS
290 }
291}
292
4d876ee3 293wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
78d14f80
VS
294 : wxObject(), m_root(NULL)
295{
4d876ee3 296 if ( !Load(stream, encoding) )
78d14f80 297 {
3fb676f8 298 wxDELETE(m_root);
78d14f80
VS
299 }
300}
301
78d14f80
VS
302wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc)
303{
304 DoCopy(doc);
305}
306
78d14f80
VS
307wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc)
308{
3fb676f8 309 wxDELETE(m_root);
78d14f80
VS
310 DoCopy(doc);
311 return *this;
312}
313
78d14f80
VS
314void wxXmlDocument::DoCopy(const wxXmlDocument& doc)
315{
316 m_version = doc.m_version;
480505bc 317#if !wxUSE_UNICODE
78d14f80 318 m_encoding = doc.m_encoding;
480505bc
VS
319#endif
320 m_fileEncoding = doc.m_fileEncoding;
78d14f80
VS
321 m_root = new wxXmlNode(*doc.m_root);
322}
323
4d876ee3 324bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
78d14f80
VS
325{
326 wxFileInputStream stream(filename);
4d876ee3
VS
327 return Load(stream, encoding);
328}
329
330bool wxXmlDocument::Save(const wxString& filename) const
331{
332 wxFileOutputStream stream(filename);
333 return Save(stream);
78d14f80
VS
334}
335
336
337
4d876ee3
VS
338//-----------------------------------------------------------------------------
339// wxXmlDocument loading routines
340//-----------------------------------------------------------------------------
341
574c939e 342/*
4d876ee3
VS
343 FIXME:
344 - process all elements, including CDATA
345 */
346
347// converts Expat-produced string in UTF-8 into wxString.
348inline static wxString CharToString(wxMBConv *conv,
349 const char *s, size_t len = wxSTRING_MAXLEN)
78d14f80 350{
480505bc 351#if wxUSE_UNICODE
4d876ee3
VS
352 (void)conv;
353 return wxString(s, wxConvUTF8, len);
480505bc 354#else
4d876ee3 355 if ( conv )
78d14f80 356 {
4d876ee3
VS
357 size_t nLen = (len != wxSTRING_MAXLEN) ? len :
358 nLen = wxConvUTF8.MB2WC((wchar_t*) NULL, s, 0);
574c939e 359
4d876ee3
VS
360 wchar_t *buf = new wchar_t[nLen+1];
361 wxConvUTF8.MB2WC(buf, s, nLen);
362 buf[nLen] = 0;
5dac8a3b 363 wxString str(buf, *conv, len);
4d876ee3 364 delete[] buf;
5dac8a3b 365 return str;
78d14f80 366 }
4d876ee3
VS
367 else
368 return wxString(s, len);
369#endif
78d14f80
VS
370}
371
4d876ee3
VS
372struct wxXmlParsingContext
373{
374 wxMBConv *conv;
375 wxXmlNode *root;
376 wxXmlNode *node;
377 wxXmlNode *lastAsText;
378 wxString encoding;
379 wxString version;
380};
78d14f80 381
4d876ee3 382static void StartElementHnd(void *userData, const char *name, const char **atts)
78d14f80 383{
4d876ee3
VS
384 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
385 wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name));
386 const char **a = atts;
387 while (*a)
388 {
389 node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1]));
390 a += 2;
391 }
392 if (ctx->root == NULL)
393 ctx->root = node;
394 else
395 ctx->node->AddChild(node);
396 ctx->node = node;
397 ctx->lastAsText = NULL;
78d14f80
VS
398}
399
4d876ee3
VS
400static void EndElementHnd(void *userData, const char* WXUNUSED(name))
401{
402 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
78d14f80 403
4d876ee3
VS
404 ctx->node = ctx->node->GetParent();
405 ctx->lastAsText = NULL;
406}
78d14f80 407
4d876ee3 408static void TextHnd(void *userData, const char *s, int len)
78d14f80 409{
4d876ee3
VS
410 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
411 char *buf = new char[len + 1];
412
413 buf[len] = '\0';
414 memcpy(buf, s, (size_t)len);
415
416 if (ctx->lastAsText)
417 {
418 ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
419 CharToString(ctx->conv, buf));
420 }
421 else
78d14f80 422 {
4d876ee3
VS
423 bool whiteOnly = TRUE;
424 for (char *c = buf; *c != '\0'; c++)
425 if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
426 {
427 whiteOnly = FALSE;
428 break;
429 }
430 if (!whiteOnly)
78d14f80 431 {
4d876ee3
VS
432 ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
433 CharToString(ctx->conv, buf));
434 ctx->node->AddChild(ctx->lastAsText);
78d14f80 435 }
78d14f80 436 }
78d14f80 437
4d876ee3
VS
438 delete[] buf;
439}
78d14f80 440
4d876ee3
VS
441static void CommentHnd(void *userData, const char *data)
442{
443 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
78d14f80 444
4d876ee3
VS
445 if (ctx->node)
446 {
447 // VS: ctx->node == NULL happens if there is a comment before
448 // the root element (e.g. wxDesigner's output). We ignore such
449 // comments, no big deal...
450 ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE,
451 wxT("comment"), CharToString(ctx->conv, data)));
452 }
453 ctx->lastAsText = NULL;
454}
78d14f80 455
4d876ee3 456static void DefaultHnd(void *userData, const char *s, int len)
78d14f80 457{
4d876ee3
VS
458 // XML header:
459 if (len > 6 && memcmp(s, "<?xml ", 6) == 0)
78d14f80 460 {
4d876ee3
VS
461 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
462
463 wxString buf = CharToString(ctx->conv, s, (size_t)len);
464 int pos;
465 pos = buf.Find(wxT("encoding="));
466 if (pos != wxNOT_FOUND)
467 ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]);
468 pos = buf.Find(wxT("version="));
469 if (pos != wxNOT_FOUND)
470 ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
78d14f80 471 }
78d14f80
VS
472}
473
4d876ee3 474static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
5dac8a3b 475 const XML_Char *name, XML_Encoding *info)
78d14f80 476{
4d876ee3
VS
477 // We must build conversion table for expat. The easiest way to do so
478 // is to let wxCSConv convert as string containing all characters to
479 // wide character representation:
97ddad38 480 wxCSConv conv(wxString(name, wxConvLibc));
5dac8a3b
VS
481 char mbBuf[2];
482 wchar_t wcBuf[10];
4d876ee3 483 size_t i;
574c939e 484
5dac8a3b 485 mbBuf[1] = 0;
4d876ee3
VS
486 info->map[0] = 0;
487 for (i = 0; i < 255; i++)
5dac8a3b
VS
488 {
489 mbBuf[0] = (char)(i+1);
490 if (conv.MB2WC(wcBuf, mbBuf, 2) == (size_t)-1)
491 {
492 // invalid/undefined byte in the encoding:
493 info->map[i+1] = -1;
494 }
495 info->map[i+1] = (int)wcBuf[0];
496 }
497
4d876ee3
VS
498 info->data = NULL;
499 info->convert = NULL;
500 info->release = NULL;
574c939e 501
4d876ee3 502 return 1;
78d14f80
VS
503}
504
4d876ee3 505bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
78d14f80 506{
4d876ee3
VS
507#if wxUSE_UNICODE
508 (void)encoding;
509#else
510 m_encoding = encoding;
511#endif
512
513 const size_t BUFSIZE = 1024;
514 char buf[BUFSIZE];
515 wxXmlParsingContext ctx;
516 bool done;
517 XML_Parser parser = XML_ParserCreate(NULL);
518
519 ctx.root = ctx.node = NULL;
520 ctx.encoding = wxT("UTF-8"); // default in absence of encoding=""
521 ctx.conv = NULL;
522#if !wxUSE_UNICODE
523 if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") )
524 ctx.conv = new wxCSConv(encoding);
78d14f80 525#endif
574c939e 526
4d876ee3
VS
527 XML_SetUserData(parser, (void*)&ctx);
528 XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
529 XML_SetCharacterDataHandler(parser, TextHnd);
530 XML_SetCommentHandler(parser, CommentHnd);
531 XML_SetDefaultHandler(parser, DefaultHnd);
532 XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
533
534 do
535 {
536 size_t len = stream.Read(buf, BUFSIZE).LastRead();
537 done = (len < BUFSIZE);
538 if (!XML_Parse(parser, buf, len, done))
539 {
540 wxLogError(_("XML parsing error: '%s' at line %d"),
541 XML_ErrorString(XML_GetErrorCode(parser)),
542 XML_GetCurrentLineNumber(parser));
543 return FALSE;
544 }
545 } while (!done);
546
547 SetVersion(ctx.version);
548 SetFileEncoding(ctx.encoding);
549 SetRoot(ctx.root);
550
551 XML_ParserFree(parser);
552#if !wxUSE_UNICODE
553 if ( ctx.conv )
554 delete ctx.conv;
555#endif
574c939e 556
4d876ee3
VS
557 return TRUE;
558
78d14f80
VS
559}
560
561
78d14f80 562
4d876ee3
VS
563//-----------------------------------------------------------------------------
564// wxXmlDocument saving routines
565//-----------------------------------------------------------------------------
566
567// write string to output:
568inline static void OutputString(wxOutputStream& stream, const wxString& str,
569 wxMBConv *convMem, wxMBConv *convFile)
78d14f80 570{
4d876ee3
VS
571 if (str.IsEmpty()) return;
572#if wxUSE_UNICODE
97ddad38 573 const wxWX2MBbuf buf(str.mb_str(convFile ? *convFile : wxConvUTF8));
4d876ee3
VS
574 stream.Write((const char*)buf, strlen((const char*)buf));
575#else
576 if ( convFile == NULL )
577 stream.Write(str.mb_str(), str.Len());
578 else
579 {
580 wxString str2(str.wc_str(*convMem), *convFile);
581 stream.Write(str2.mb_str(), str2.Len());
582 }
583#endif
584}
78d14f80 585
574c939e 586// Same as above, but create entities first.
4d876ee3
VS
587// Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
588static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
589 wxMBConv *convMem, wxMBConv *convFile)
590{
591 wxString buf;
592 size_t i, last, len;
593 wxChar c;
574c939e 594
4d876ee3
VS
595 len = str.Len();
596 last = 0;
597 for (i = 0; i < len; i++)
598 {
599 c = str.GetChar(i);
574c939e 600 if (c == wxT('<') || c == wxT('>') ||
4d876ee3
VS
601 (c == wxT('&') && str.Mid(i+1, 4) != wxT("amp;")))
602 {
603 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
604 switch (c)
605 {
574c939e 606 case wxT('<'):
4d876ee3
VS
607 OutputString(stream, wxT("&lt;"), NULL, NULL);
608 break;
574c939e 609 case wxT('>'):
4d876ee3
VS
610 OutputString(stream, wxT("&gt;"), NULL, NULL);
611 break;
574c939e 612 case wxT('&'):
4d876ee3
VS
613 OutputString(stream, wxT("&amp;"), NULL, NULL);
614 break;
615 default: break;
616 }
617 last = i + 1;
618 }
619 }
620 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
621}
78d14f80 622
4d876ee3
VS
623inline static void OutputIndentation(wxOutputStream& stream, int indent)
624{
625 wxString str = wxT("\n");
626 for (int i = 0; i < indent; i++)
627 str << wxT(' ') << wxT(' ');
628 OutputString(stream, str, NULL, NULL);
629}
78d14f80 630
4d876ee3
VS
631static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
632 wxMBConv *convMem, wxMBConv *convFile)
633{
634 wxXmlNode *n, *prev;
635 wxXmlProperty *prop;
78d14f80 636
4d876ee3
VS
637 switch (node->GetType())
638 {
639 case wxXML_TEXT_NODE:
640 OutputStringEnt(stream, node->GetContent(), convMem, convFile);
641 break;
574c939e 642
4d876ee3
VS
643 case wxXML_ELEMENT_NODE:
644 OutputString(stream, wxT("<"), NULL, NULL);
645 OutputString(stream, node->GetName(), NULL, NULL);
574c939e 646
4d876ee3
VS
647 prop = node->GetProperties();
648 while (prop)
649 {
650 OutputString(stream, wxT(" ") + prop->GetName() +
651 wxT("=\"") + prop->GetValue() + wxT("\""),
652 NULL, NULL);
653 // FIXME - what if prop contains '"'?
654 prop = prop->GetNext();
655 }
574c939e 656
4d876ee3
VS
657 if (node->GetChildren())
658 {
659 OutputString(stream, wxT(">"), NULL, NULL);
660 prev = NULL;
661 n = node->GetChildren();
662 while (n)
663 {
664 if (n && n->GetType() != wxXML_TEXT_NODE)
665 OutputIndentation(stream, indent + 1);
666 OutputNode(stream, n, indent + 1, convMem, convFile);
667 prev = n;
668 n = n->GetNext();
669 }
670 if (prev && prev->GetType() != wxXML_TEXT_NODE)
671 OutputIndentation(stream, indent);
672 OutputString(stream, wxT("</"), NULL, NULL);
673 OutputString(stream, node->GetName(), NULL, NULL);
674 OutputString(stream, wxT(">"), NULL, NULL);
675 }
676 else
677 OutputString(stream, wxT("/>"), NULL, NULL);
678 break;
574c939e 679
4d876ee3
VS
680 case wxXML_COMMENT_NODE:
681 OutputString(stream, wxT("<!--"), NULL, NULL);
682 OutputString(stream, node->GetContent(), convMem, convFile);
683 OutputString(stream, wxT("-->"), NULL, NULL);
684 break;
574c939e 685
4d876ee3
VS
686 default:
687 wxFAIL_MSG(wxT("unsupported node type"));
688 }
689}
78d14f80 690
4d876ee3 691bool wxXmlDocument::Save(wxOutputStream& stream) const
78d14f80 692{
4d876ee3
VS
693 if ( !IsOk() )
694 return FALSE;
574c939e 695
4d876ee3 696 wxString s;
574c939e 697
4d876ee3
VS
698 wxMBConv *convMem = NULL, *convFile = NULL;
699#if wxUSE_UNICODE
700 convFile = new wxCSConv(GetFileEncoding());
701#else
702 if ( GetFileEncoding() != GetEncoding() )
703 {
704 convFile = new wxCSConv(GetFileEncoding());
705 convMem = new wxCSConv(GetEncoding());
706 }
707#endif
574c939e 708
4d876ee3
VS
709 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
710 GetVersion().c_str(), GetFileEncoding().c_str());
711 OutputString(stream, s, NULL, NULL);
574c939e 712
4d876ee3
VS
713 OutputNode(stream, GetRoot(), 0, convMem, convFile);
714 OutputString(stream, wxT("\n"), NULL, NULL);
574c939e 715
4d876ee3
VS
716 if ( convFile )
717 delete convFile;
718 if ( convMem )
719 delete convMem;
574c939e 720
4d876ee3 721 return TRUE;
78d14f80 722}