]> git.saurik.com Git - wxWidgets.git/blobdiff - src/xml/xml.cpp
Better place for coordinate mirroring and removal
[wxWidgets.git] / src / xml / xml.cpp
index ab40593bede751e0241b07634062c65743a82d35..6a76beb7c62e81220aa8a6cebd50983d25363caf 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        xml.cpp
+// Name:        src/xml/xml.cpp
 // Purpose:     wxXmlDocument - XML parser & data holder class
 // Author:      Vaclav Slavik
 // Created:     2000/03/05
@@ -8,10 +8,6 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "xml.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
     #pragma hdrstop
 #endif
 
+#if wxUSE_XML
+
 #include "wx/xml/xml.h"
 
-#if wxUSE_XML
+#ifndef WX_PRECOMP
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/app.h"
+#endif
 
 #include "wx/wfstream.h"
 #include "wx/datstrm.h"
 #include "wx/zstream.h"
-#include "wx/log.h"
-#include "wx/intl.h"
 #include "wx/strconv.h"
 
 #include "expat.h" // from Expat
 
 // DLL options compatibility check:
-#include "wx/app.h"
 WX_CHECK_BUILD_OPTIONS("wxXML")
 
+
+IMPLEMENT_CLASS(wxXmlDocument, wxObject)
+
+
+// a private utility used by wxXML
+static bool wxIsWhiteOnly(const wxChar *buf);
+
+
 //-----------------------------------------------------------------------------
 //  wxXmlNode
 //-----------------------------------------------------------------------------
@@ -127,11 +134,11 @@ bool wxXmlNode::HasProp(const wxString& propName) const
 
     while (prop)
     {
-        if (prop->GetName() == propName) return TRUE;
+        if (prop->GetName() == propName) return true;
         prop = prop->GetNext();
     }
 
-    return FALSE;
+    return false;
 }
 
 bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
@@ -143,12 +150,12 @@ bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
         if (prop->GetName() == propName)
         {
             *value = prop->GetValue();
-            return TRUE;
+            return true;
         }
         prop = prop->GetNext();
     }
 
-    return FALSE;
+    return false;
 }
 
 wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
@@ -156,8 +163,8 @@ wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& default
     wxString tmp;
     if (GetPropVal(propName, &tmp))
         return tmp;
-    else
-        return defaultVal;
+
+    return defaultVal;
 }
 
 void wxXmlNode::AddChild(wxXmlNode *child)
@@ -174,33 +181,52 @@ void wxXmlNode::AddChild(wxXmlNode *child)
     child->m_parent = this;
 }
 
-void wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
+bool wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
 {
-    wxASSERT_MSG(before_node->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
+    wxCHECK_MSG(before_node == NULL || before_node->GetParent() == this, false,
+                 wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
+    wxCHECK_MSG(child, false, wxT("Cannot insert a NULL pointer!"));
 
     if (m_children == before_node)
        m_children = child;
+    else if (m_children == NULL)
+    {
+        if (before_node != NULL)
+            return false;       // we have no children so we don't need to search
+        m_children = child;
+    }
+    else if (before_node == NULL)
+    {
+        // prepend child
+        child->m_parent = this;
+        child->m_next = m_children;
+        m_children = child;
+        return true;
+    }
     else
     {
         wxXmlNode *ch = m_children;
-        while (ch->m_next != before_node) ch = ch->m_next;
+        while (ch && ch->m_next != before_node) ch = ch->m_next;
+        if (!ch)
+            return false;       // before_node not found
         ch->m_next = child;
     }
 
     child->m_parent = this;
     child->m_next = before_node;
+    return true;
 }
 
 bool wxXmlNode::RemoveChild(wxXmlNode *child)
 {
     if (m_children == NULL)
-        return FALSE;
+        return false;
     else if (m_children == child)
     {
         m_children = child->m_next;
         child->m_parent = NULL;
         child->m_next = NULL;
-        return TRUE;
+        return true;
     }
     else
     {
@@ -212,11 +238,11 @@ bool wxXmlNode::RemoveChild(wxXmlNode *child)
                 ch->m_next = child->m_next;
                 child->m_parent = NULL;
                 child->m_next = NULL;
-                return TRUE;
+                return true;
             }
             ch = ch->m_next;
         }
-        return FALSE;
+        return false;
     }
 }
 
@@ -242,7 +268,7 @@ bool wxXmlNode::DeleteProperty(const wxString& name)
     wxXmlProperty *prop;
 
     if (m_properties == NULL)
-        return FALSE;
+        return false;
 
     else if (m_properties->GetName() == name)
     {
@@ -250,7 +276,7 @@ bool wxXmlNode::DeleteProperty(const wxString& name)
         m_properties = prop->GetNext();
         prop->SetNext(NULL);
         delete prop;
-        return TRUE;
+        return true;
     }
 
     else
@@ -264,14 +290,50 @@ bool wxXmlNode::DeleteProperty(const wxString& name)
                 p->SetNext(prop->GetNext());
                 prop->SetNext(NULL);
                 delete prop;
-                return TRUE;
+                return true;
             }
             p = p->GetNext();
         }
-        return FALSE;
+        return false;
     }
 }
 
+wxString wxXmlNode::GetNodeContent() const
+{
+    wxXmlNode *n = GetChildren();
+
+    while (n)
+    {
+        if (n->GetType() == wxXML_TEXT_NODE ||
+            n->GetType() == wxXML_CDATA_SECTION_NODE)
+            return n->GetContent();
+        n = n->GetNext();
+    }
+    return wxEmptyString;
+}
+
+int wxXmlNode::GetDepth(wxXmlNode *grandparent) const
+{
+    const wxXmlNode *n = this;
+    int ret = -1;
+
+    do
+    {
+        ret++;
+        n = n->GetParent();
+        if (n == grandparent)
+            return ret;
+
+    } while (n);
+
+    return wxNOT_FOUND;
+}
+
+bool wxXmlNode::IsWhitespaceOnly() const
+{
+    return wxIsWhiteOnly(m_content);
+}
+
 
 
 //-----------------------------------------------------------------------------
@@ -287,7 +349,7 @@ wxXmlDocument::wxXmlDocument()
 }
 
 wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
-                          : wxObject(), m_root(NULL)
+              :wxObject(), m_root(NULL)
 {
     if ( !Load(filename, encoding) )
     {
@@ -296,7 +358,7 @@ wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
 }
 
 wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
-                          : wxObject(), m_root(NULL)
+              :wxObject(), m_root(NULL)
 {
     if ( !Load(stream, encoding) )
     {
@@ -305,6 +367,7 @@ wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
 }
 
 wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc)
+              :wxObject()
 {
     DoCopy(doc);
 }
@@ -326,16 +389,20 @@ void wxXmlDocument::DoCopy(const wxXmlDocument& doc)
     m_root = new wxXmlNode(*doc.m_root);
 }
 
-bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
+bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding, int flags)
 {
     wxFileInputStream stream(filename);
-    return Load(stream, encoding);
+    if (!stream.Ok())
+        return false;
+    return Load(stream, encoding, flags);
 }
 
-bool wxXmlDocument::Save(const wxString& filename) const
+bool wxXmlDocument::Save(const wxString& filename, int indentstep) const
 {
     wxFileOutputStream stream(filename);
-    return Save(stream);
+    if (!stream.Ok())
+        return false;
+    return Save(stream, indentstep);
 }
 
 
@@ -344,36 +411,42 @@ bool wxXmlDocument::Save(const wxString& filename) const
 //  wxXmlDocument loading routines
 //-----------------------------------------------------------------------------
 
-/*
-    FIXME:
-       - process all elements, including CDATA
- */
-
-// converts Expat-produced string in UTF-8 into wxString.
-inline static wxString CharToString(wxMBConv *conv,
+// converts Expat-produced string in UTF-8 into wxString using the specified
+// conv or keep in UTF-8 if conv is NULL
+static wxString CharToString(wxMBConv *conv,
                                     const char *s, size_t len = wxSTRING_MAXLEN)
 {
 #if wxUSE_UNICODE
-    (void)conv;
+    wxUnusedVar(conv);
+
     return wxString(s, wxConvUTF8, len);
-#else
+#else // !wxUSE_UNICODE
     if ( conv )
     {
-        size_t nLen = (len != wxSTRING_MAXLEN) ? len :
-                          wxConvUTF8.MB2WC((wchar_t*) NULL, s, 0);
+        // there can be no embedded NULs in this string so we don't need the
+        // output length, it will be NUL-terminated
+        const wxWCharBuffer wbuf(
+            wxConvUTF8.cMB2WC(s, len == wxSTRING_MAXLEN ? wxNO_LEN : len, NULL));
 
-        wchar_t *buf = new wchar_t[nLen+1];
-        wxConvUTF8.MB2WC(buf, s, nLen);
-        buf[nLen] = 0;
-        wxString str(buf, *conv, len);
-        delete[] buf;
-        return str;
+        return wxString(wbuf, *conv);
     }
-    else
-        return wxString(s, len);
-#endif
+    else // already in UTF-8, no conversion needed
+    {
+        return wxString(s, len != wxSTRING_MAXLEN ? len : strlen(s));
+    }
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+}
+
+// returns true if the given string contains only whitespaces
+bool wxIsWhiteOnly(const wxChar *buf)
+{
+    for (const wxChar *c = buf; *c != wxT('\0'); c++)
+        if (*c != wxT(' ') && *c != wxT('\t') && *c != wxT('\n') && *c != wxT('\r'))
+            return false;
+    return true;
 }
 
+
 struct wxXmlParsingContext
 {
     wxMBConv  *conv;
@@ -382,8 +455,11 @@ struct wxXmlParsingContext
     wxXmlNode *lastAsText;
     wxString   encoding;
     wxString   version;
+    bool       bLastCdata;
+    bool       removeWhiteOnlyNodes;
 };
 
+extern "C" {
 static void StartElementHnd(void *userData, const char *name, const char **atts)
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
@@ -401,7 +477,9 @@ static void StartElementHnd(void *userData, const char *name, const char **atts)
     ctx->node = node;
     ctx->lastAsText = NULL;
 }
+}
 
+extern "C" {
 static void EndElementHnd(void *userData, const char* WXUNUSED(name))
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
@@ -409,40 +487,63 @@ static void EndElementHnd(void *userData, const char* WXUNUSED(name))
     ctx->node = ctx->node->GetParent();
     ctx->lastAsText = NULL;
 }
+}
 
+extern "C" {
 static void TextHnd(void *userData, const char *s, int len)
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
-    char *buf = new char[len + 1];
-
-    buf[len] = '\0';
-    memcpy(buf, s, (size_t)len);
+    wxString str = CharToString(ctx->conv, s, len);
 
     if (ctx->lastAsText)
     {
-        ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
-                                    CharToString(ctx->conv, buf));
+        if ( ctx->bLastCdata )
+        {
+            ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
+                                        CharToString(NULL, s, len));
+        }
+        else
+        {
+            ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + str);
+        }
     }
     else
     {
-        bool whiteOnly = TRUE;
-        for (char *c = buf; *c != '\0'; c++)
-            if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
-            {
-                whiteOnly = FALSE;
-                break;
-            }
+        bool whiteOnly = false;
+        if (ctx->removeWhiteOnlyNodes)
+            whiteOnly = wxIsWhiteOnly(str);
+
         if (!whiteOnly)
         {
-            ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
-                                            CharToString(ctx->conv, buf));
+            ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str);
             ctx->node->AddChild(ctx->lastAsText);
         }
     }
+}
+}
+
+extern "C" {
+static void StartCdataHnd(void *userData)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
 
-    delete[] buf;
+    ctx->bLastCdata = true;
+
+    ctx->lastAsText = new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"),wxT(""));
+    ctx->node->AddChild(ctx->lastAsText);
+}
 }
 
+extern "C" {
+static void EndCdataHnd(void *userData)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+    ctx->bLastCdata = false;
+}
+}
+
+extern "C" {
 static void CommentHnd(void *userData, const char *data)
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
@@ -457,7 +558,9 @@ static void CommentHnd(void *userData, const char *data)
     }
     ctx->lastAsText = NULL;
 }
+}
 
+extern "C" {
 static void DefaultHnd(void *userData, const char *s, int len)
 {
     // XML header:
@@ -475,14 +578,17 @@ static void DefaultHnd(void *userData, const char *s, int len)
             ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
     }
 }
+}
 
+extern "C" {
 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
                               const XML_Char *name, XML_Encoding *info)
 {
     // We must build conversion table for expat. The easiest way to do so
     // is to let wxCSConv convert as string containing all characters to
     // wide character representation:
-    wxCSConv conv(wxString(name, wxConvLibc));
+    wxString str(name, wxConvLibc);
+    wxCSConv conv(str);
     char mbBuf[2];
     wchar_t wcBuf[10];
     size_t i;
@@ -499,15 +605,16 @@ static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
         }
         info->map[i+1] = (int)wcBuf[0];
     }
-    
+
     info->data = NULL;
     info->convert = NULL;
     info->release = NULL;
 
     return 1;
 }
+}
 
-bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
+bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int flags)
 {
 #if wxUSE_UNICODE
     (void)encoding;
@@ -528,10 +635,13 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
     if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") )
         ctx.conv = new wxCSConv(encoding);
 #endif
+    ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0;
+    ctx.bLastCdata = false;
 
     XML_SetUserData(parser, (void*)&ctx);
     XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
     XML_SetCharacterDataHandler(parser, TextHnd);
+    XML_SetCdataSectionHandler(parser, StartCdataHnd, EndCdataHnd );
     XML_SetCommentHandler(parser, CommentHnd);
     XML_SetDefaultHandler(parser, DefaultHnd);
     XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
@@ -555,9 +665,9 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
 
     if (ok)
     {
-        if (!ctx.version.IsEmpty())
+        if (!ctx.version.empty())
             SetVersion(ctx.version);
-        if (!ctx.encoding.IsEmpty())
+        if (!ctx.encoding.empty())
             SetFileEncoding(ctx.encoding);
         SetRoot(ctx.root);
     }
@@ -584,27 +694,42 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
 
 // write string to output:
 inline static void OutputString(wxOutputStream& stream, const wxString& str,
-                                wxMBConv *convMem, wxMBConv *convFile)
+                                wxMBConv *convMem = NULL,
+                                wxMBConv *convFile = NULL)
 {
-    if (str.IsEmpty()) return;
+    if (str.empty())
+        return;
+
 #if wxUSE_UNICODE
+    wxUnusedVar(convMem);
+
     const wxWX2MBbuf buf(str.mb_str(*(convFile ? convFile : &wxConvUTF8)));
     stream.Write((const char*)buf, strlen((const char*)buf));
-#else
-    if ( convFile == NULL )
-        stream.Write(str.mb_str(), str.Len());
-    else
+#else // !wxUSE_UNICODE
+    if ( convFile && convMem )
     {
         wxString str2(str.wc_str(*convMem), *convFile);
         stream.Write(str2.mb_str(), str2.Len());
     }
-#endif
+    else // no conversions to do
+    {
+        stream.Write(str.mb_str(), str.Len());
+    }
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
 }
 
+// flags for OutputStringEnt()
+enum
+{
+    XML_ESCAPE_QUOTES = 1
+};
+
 // Same as above, but create entities first.
 // Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
 static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
-                            wxMBConv *convMem, wxMBConv *convFile)
+                            wxMBConv *convMem = NULL,
+                            wxMBConv *convFile = NULL,
+                            int flags = 0)
 {
     wxString buf;
     size_t i, last, len;
@@ -616,21 +741,26 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
     {
         c = str.GetChar(i);
         if (c == wxT('<') || c == wxT('>') ||
-            (c == wxT('&') && str.Mid(i+1, 4) != wxT("amp;")))
+            (c == wxT('&') && str.Mid(i+1, 4) != wxT("amp;")) ||
+            ((flags & XML_ESCAPE_QUOTES) && c == wxT('"')))
         {
             OutputString(stream, str.Mid(last, i - last), convMem, convFile);
             switch (c)
             {
                 case wxT('<'):
-                    OutputString(stream, wxT("&lt;"), NULL, NULL);
+                    OutputString(stream, wxT("&lt;"));
                     break;
                 case wxT('>'):
-                    OutputString(stream, wxT("&gt;"), NULL, NULL);
+                    OutputString(stream, wxT("&gt;"));
                     break;
                 case wxT('&'):
-                    OutputString(stream, wxT("&amp;"), NULL, NULL);
+                    OutputString(stream, wxT("&amp;"));
+                    break;
+                case wxT('"'):
+                    OutputString(stream, wxT("&quot;"));
+                    break;
+                default:
                     break;
-                default: break;
             }
             last = i + 1;
         }
@@ -643,62 +773,68 @@ inline static void OutputIndentation(wxOutputStream& stream, int indent)
     wxString str = wxT("\n");
     for (int i = 0; i < indent; i++)
         str << wxT(' ') << wxT(' ');
-    OutputString(stream, str, NULL, NULL);
+    OutputString(stream, str);
 }
 
 static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
-                       wxMBConv *convMem, wxMBConv *convFile)
+                       wxMBConv *convMem, wxMBConv *convFile, int indentstep)
 {
     wxXmlNode *n, *prev;
     wxXmlProperty *prop;
 
     switch (node->GetType())
     {
+        case wxXML_CDATA_SECTION_NODE:
+            OutputString( stream, wxT("<![CDATA["));
+            OutputString( stream, node->GetContent() );
+            OutputString( stream, wxT("]]>") );
+            break;
+
         case wxXML_TEXT_NODE:
             OutputStringEnt(stream, node->GetContent(), convMem, convFile);
             break;
 
         case wxXML_ELEMENT_NODE:
-            OutputString(stream, wxT("<"), NULL, NULL);
-            OutputString(stream, node->GetName(), NULL, NULL);
+            OutputString(stream, wxT("<"));
+            OutputString(stream, node->GetName());
 
             prop = node->GetProperties();
             while (prop)
             {
-                OutputString(stream, wxT(" ") + prop->GetName() +
-                             wxT("=\"") + prop->GetValue() + wxT("\""),
-                             NULL, NULL);
-                // FIXME - what if prop contains '"'?
+                OutputString(stream, wxT(" ") + prop->GetName() +  wxT("=\""));
+                OutputStringEnt(stream, prop->GetValue(), convMem, convFile,
+                                XML_ESCAPE_QUOTES);
+                OutputString(stream, wxT("\""));
                 prop = prop->GetNext();
             }
 
             if (node->GetChildren())
             {
-                OutputString(stream, wxT(">"), NULL, NULL);
+                OutputString(stream, wxT(">"));
                 prev = NULL;
                 n = node->GetChildren();
                 while (n)
                 {
-                    if (n && n->GetType() != wxXML_TEXT_NODE)
-                        OutputIndentation(stream, indent + 1);
-                    OutputNode(stream, n, indent + 1, convMem, convFile);
+                    if (indentstep >= 0 && n && n->GetType() != wxXML_TEXT_NODE)
+                        OutputIndentation(stream, indent + indentstep);
+                    OutputNode(stream, n, indent + indentstep, convMem, convFile, indentstep);
                     prev = n;
                     n = n->GetNext();
                 }
-                if (prev && prev->GetType() != wxXML_TEXT_NODE)
+                if (indentstep >= 0 && prev && prev->GetType() != wxXML_TEXT_NODE)
                     OutputIndentation(stream, indent);
-                OutputString(stream, wxT("</"), NULL, NULL);
-                OutputString(stream, node->GetName(), NULL, NULL);
-                OutputString(stream, wxT(">"), NULL, NULL);
+                OutputString(stream, wxT("</"));
+                OutputString(stream, node->GetName());
+                OutputString(stream, wxT(">"));
             }
             else
-                OutputString(stream, wxT("/>"), NULL, NULL);
+                OutputString(stream, wxT("/>"));
             break;
 
         case wxXML_COMMENT_NODE:
-            OutputString(stream, wxT("<!--"), NULL, NULL);
+            OutputString(stream, wxT("<!--"));
             OutputString(stream, node->GetContent(), convMem, convFile);
-            OutputString(stream, wxT("-->"), NULL, NULL);
+            OutputString(stream, wxT("-->"));
             break;
 
         default:
@@ -706,17 +842,19 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
     }
 }
 
-bool wxXmlDocument::Save(wxOutputStream& stream) const
+bool wxXmlDocument::Save(wxOutputStream& stream, int indentstep) const
 {
     if ( !IsOk() )
-        return FALSE;
+        return false;
 
     wxString s;
 
-    wxMBConv *convMem = NULL, *convFile = NULL;
+    wxMBConv *convMem = NULL;
+
 #if wxUSE_UNICODE
-    convFile = new wxCSConv(GetFileEncoding());
+    wxMBConv *convFile = new wxCSConv(GetFileEncoding());
 #else
+    wxMBConv *convFile = NULL;
     if ( GetFileEncoding() != GetEncoding() )
     {
         convFile = new wxCSConv(GetFileEncoding());
@@ -726,17 +864,17 @@ bool wxXmlDocument::Save(wxOutputStream& stream) const
 
     s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
              GetVersion().c_str(), GetFileEncoding().c_str());
-    OutputString(stream, s, NULL, NULL);
+    OutputString(stream, s);
 
-    OutputNode(stream, GetRoot(), 0, convMem, convFile);
-    OutputString(stream, wxT("\n"), NULL, NULL);
+    OutputNode(stream, GetRoot(), 0, convMem, convFile, indentstep);
+    OutputString(stream, wxT("\n"));
 
     if ( convFile )
         delete convFile;
     if ( convMem )
         delete convMem;
 
-    return TRUE;
+    return true;
 }
 
 #endif // wxUSE_XML