]> git.saurik.com Git - wxWidgets.git/blobdiff - src/xrc/xmlres.cpp
No changes, synchronised source names that appear commented at the top of files with...
[wxWidgets.git] / src / xrc / xmlres.cpp
index 90aba8c06e5ee271395ba27d64f189f188ed096d..1b62dbc3dcfe6a78e7099f748c5309d2ff1ed48c 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        xmlres.cpp
+// Name:        src/xrc/xmlres.cpp
 // Purpose:     XRC resources
 // Author:      Vaclav Slavik
 // Created:     2000/03/05
@@ -8,10 +8,6 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#ifdef __GNUG__
-#pragma implementation "xmlres.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 
 #if wxUSE_XRC
 
-#include "wx/dialog.h"
-#include "wx/panel.h"
-#include "wx/frame.h"
+#include "wx/xrc/xmlres.h"
+
+#ifndef WX_PRECOMP
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/panel.h"
+    #include "wx/frame.h"
+    #include "wx/dialog.h"
+    #include "wx/settings.h"
+    #include "wx/bitmap.h"
+    #include "wx/image.h"
+    #include "wx/module.h"
+    #include "wx/wxcrtvararg.h"
+#endif
+
+#ifndef __WXWINCE__
+    #include <locale.h>
+#endif
+
+#include "wx/vector.h"
 #include "wx/wfstream.h"
 #include "wx/filesys.h"
 #include "wx/filename.h"
-#include "wx/log.h"
-#include "wx/intl.h"
 #include "wx/tokenzr.h"
 #include "wx/fontenum.h"
-#include "wx/module.h"
-#include "wx/bitmap.h"
-#include "wx/image.h"
 #include "wx/fontmap.h"
 #include "wx/artprov.h"
-
+#include "wx/imaglist.h"
+#include "wx/dir.h"
 #include "wx/xml/xml.h"
-#include "wx/xrc/xmlres.h"
+#include "wx/hashset.h"
+#include "wx/scopedptr.h"
+
+namespace
+{
+
+// Helper function to get modification time of either a wxFileSystem URI or
+// just a normal file name, depending on the build.
+#if wxUSE_DATETIME
+
+wxDateTime GetXRCFileModTime(const wxString& filename)
+{
+#if wxUSE_FILESYSTEM
+    wxFileSystem fsys;
+    wxScopedPtr<wxFSFile> file(fsys.OpenFile(filename));
+
+    return file ? file->GetModificationTime() : wxDateTime();
+#else // wxUSE_FILESYSTEM
+    return wxDateTime(wxFileModificationTime(filename));
+#endif // wxUSE_FILESYSTEM
+}
+
+#endif // wxUSE_DATETIME
+
+} // anonymous namespace
+
+class wxXmlResourceDataRecord
+{
+public:
+    // Ctor takes ownership of the document pointer.
+    wxXmlResourceDataRecord(const wxString& File_,
+                            wxXmlDocument *Doc_
+                           )
+        : File(File_), Doc(Doc_)
+    {
+#if wxUSE_DATETIME
+        Time = GetXRCFileModTime(File);
+#endif
+    }
+
+    ~wxXmlResourceDataRecord() {delete Doc;}
+
+    wxString File;
+    wxXmlDocument *Doc;
+#if wxUSE_DATETIME
+    wxDateTime Time;
+#endif
+
+    wxDECLARE_NO_COPY_CLASS(wxXmlResourceDataRecord);
+};
+
+class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
+{
+    // this is a class so that it can be forward-declared
+};
+
+WX_DECLARE_HASH_SET(int, wxIntegerHash, wxIntegerEqual, wxHashSetInt);
+
+class wxIdRange // Holds data for a particular rangename
+{
+protected:
+    wxIdRange(const wxXmlNode* node,
+              const wxString& rname,
+              const wxString& startno,
+              const wxString& rsize);
+
+    // Note the existence of an item within the range
+    void NoteItem(const wxXmlNode* node, const wxString& item);
+
+    // The manager is telling us that it's finished adding items
+    void Finalise(const wxXmlNode* node);
+
+    wxString GetName() const { return m_name; }
+    bool IsFinalised() const { return m_finalised; }
+
+    const wxString m_name;
+    int m_start;
+    int m_end;
+    unsigned int m_size;
+    bool m_item_end_found;
+    bool m_finalised;
+    wxHashSetInt m_indices;
+
+    friend class wxIdRangeManager;
+};
+
+class wxIdRangeManager
+{
+public:
+    ~wxIdRangeManager();
+    // Gets the global resources object or creates one if none exists.
+    static wxIdRangeManager *Get();
+
+    // Sets the global resources object and returns a pointer to the previous
+    // one (may be NULL).
+    static wxIdRangeManager *Set(wxIdRangeManager *res);
+
+    // Create a new IDrange from this node
+    void AddRange(const wxXmlNode* node);
+    // Tell the IdRange that this item exists, and should be pre-allocated an ID
+    void NotifyRangeOfItem(const wxXmlNode* node, const wxString& item) const;
+    // Tells all IDranges that they're now complete, and can create their IDs
+    void FinaliseRanges(const wxXmlNode* node) const;
+    // Searches for a known IdRange matching 'name', returning its index or -1
+    int Find(const wxString& rangename) const;
+    // Removes, if it exists, an entry from the XRCID table. Used in id-ranges
+    // to replace defunct or statically-initialised entries with current values
+    static void RemoveXRCIDEntry(const wxString& idstr);
+
+protected:
+    wxIdRange* FindRangeForItem(const wxXmlNode* node,
+                                const wxString& item,
+                                wxString& value) const;
+    wxVector<wxIdRange*> m_IdRanges;
+
+private:
+    static wxIdRangeManager *ms_instance;
+};
+
+namespace
+{
+
+// helper used by DoFindResource() and elsewhere: returns true if this is an
+// object or object_ref node
+//
+// node must be non-NULL
+inline bool IsObjectNode(wxXmlNode *node)
+{
+    return node->GetType() == wxXML_ELEMENT_NODE &&
+             (node->GetName() == wxS("object") ||
+                node->GetName() == wxS("object_ref"));
+}
+
+// special XML attribute with name of input file, see GetFileNameFromNode()
+const char *ATTR_INPUT_FILENAME = "__wx:filename";
+
+// helper to get filename corresponding to an XML node
+wxString
+GetFileNameFromNode(const wxXmlNode *node, const wxXmlResourceDataRecords& files)
+{
+    // this loop does two things: it looks for ATTR_INPUT_FILENAME among
+    // parents and if it isn't used, it finds the root of the XML tree 'node'
+    // is in
+    for ( ;; )
+    {
+        // in some rare cases (specifically, when an <object_ref> is used, see
+        // wxXmlResource::CreateResFromNode() and MergeNodesOver()), we work
+        // with XML nodes that are not rooted in any document from 'files'
+        // (because a new node was created by CreateResFromNode() to merge the
+        // content of <object_ref> and the referenced <object>); in that case,
+        // we hack around the problem by putting the information about input
+        // file into a custom attribute
+        if ( node->HasAttribute(ATTR_INPUT_FILENAME) )
+            return node->GetAttribute(ATTR_INPUT_FILENAME);
+
+        if ( !node->GetParent() )
+            break; // we found the root of this XML tree
+
+        node = node->GetParent();
+    }
+
+    // NB: 'node' now points to the root of XML document
+
+    for ( wxXmlResourceDataRecords::const_iterator i = files.begin();
+          i != files.end(); ++i )
+    {
+        if ( (*i)->Doc->GetRoot() == node )
+        {
+            return (*i)->File;
+        }
+    }
 
-#include "wx/arrimpl.cpp"
-WX_DEFINE_OBJARRAY(wxXmlResourceDataRecords);
+    return wxEmptyString; // not found
+}
+
+} // anonymous namespace
 
 
 wxXmlResource *wxXmlResource::ms_instance = NULL;
@@ -60,31 +241,111 @@ wxXmlResource *wxXmlResource::ms_instance = NULL;
     return old;
 }
 
-wxXmlResource::wxXmlResource(int flags)
+wxXmlResource::wxXmlResource(int flags, const wxString& domain)
 {
     m_flags = flags;
     m_version = -1;
+    m_data = new wxXmlResourceDataRecords;
+    SetDomain(domain);
 }
 
-wxXmlResource::wxXmlResource(const wxString& filemask, int flags)
+wxXmlResource::wxXmlResource(const wxString& filemask, int flags, const wxString& domain)
 {
     m_flags = flags;
     m_version = -1;
+    m_data = new wxXmlResourceDataRecords;
+    SetDomain(domain);
     Load(filemask);
 }
 
 wxXmlResource::~wxXmlResource()
 {
     ClearHandlers();
+
+    for ( wxXmlResourceDataRecords::iterator i = m_data->begin();
+          i != m_data->end(); ++i )
+    {
+        delete *i;
+    }
+    delete m_data;
+}
+
+void wxXmlResource::SetDomain(const wxString& domain)
+{
+    m_domain = domain;
 }
 
 
-bool wxXmlResource::Load(const wxString& filemask)
+/* static */
+wxString wxXmlResource::ConvertFileNameToURL(const wxString& filename)
 {
-    wxString fnd;
-    wxXmlResourceDataRecord *drec;
-    bool iswild = wxIsWild(filemask);
-    bool rt = true;
+    wxString fnd(filename);
+
+    // NB: as Load() and Unload() accept both filenames and URLs (should
+    //     probably be changed to filenames only, but embedded resources
+    //     currently rely on its ability to handle URLs - FIXME) we need to
+    //     determine whether found name is filename and not URL and this is the
+    //     fastest/simplest way to do it
+    if (wxFileName::FileExists(fnd))
+    {
+        // Make the name absolute filename, because the app may
+        // change working directory later:
+        wxFileName fn(fnd);
+        if (fn.IsRelative())
+        {
+            fn.MakeAbsolute();
+            fnd = fn.GetFullPath();
+        }
+#if wxUSE_FILESYSTEM
+        fnd = wxFileSystem::FileNameToURL(fnd);
+#endif
+    }
+
+    return fnd;
+}
+
+#if wxUSE_FILESYSTEM
+
+/* static */
+bool wxXmlResource::IsArchive(const wxString& filename)
+{
+    const wxString fnd = filename.Lower();
+
+    return fnd.Matches(wxT("*.zip")) || fnd.Matches(wxT("*.xrs"));
+}
+
+#endif // wxUSE_FILESYSTEM
+
+bool wxXmlResource::LoadFile(const wxFileName& file)
+{
+#if wxUSE_FILESYSTEM
+    return Load(wxFileSystem::FileNameToURL(file));
+#else
+    return Load(file.GetFullPath());
+#endif
+}
+
+bool wxXmlResource::LoadAllFiles(const wxString& dirname)
+{
+    bool ok = true;
+    wxArrayString files;
+
+    wxDir::GetAllFiles(dirname, &files, "*.xrc");
+
+    for ( wxArrayString::const_iterator i = files.begin(); i != files.end(); ++i )
+    {
+        if ( !LoadFile(*i) )
+            ok = false;
+    }
+
+    return ok;
+}
+
+bool wxXmlResource::Load(const wxString& filemask_)
+{
+    wxString filemask = ConvertFileNameToURL(filemask_);
+
+    bool allOK = true;
 
 #if wxUSE_FILESYSTEM
     wxFileSystem fsys;
@@ -94,54 +355,78 @@ bool wxXmlResource::Load(const wxString& filemask)
 #   define wxXmlFindFirst  wxFindFirstFile(filemask, wxFILE)
 #   define wxXmlFindNext   wxFindNextFile()
 #endif
-    if (iswild)
-        fnd = wxXmlFindFirst;
-    else
-        fnd = filemask;
-    while (!!fnd)
-    {
-        // NB: Load() accepts both filenames and URLs (should probably be
-        //     changed to filenames only, but embedded resources currently
-        //     rely on its ability to handle URLs - FIXME). This check
-        //     serves as a quick way to determine whether found name is
-        //     filename and not URL:
-        if (wxFileName::FileExists(fnd))
-        {
-            // Make the name absolute filename, because the app may
-            // change working directory later:
-            wxFileName fn(fnd);
-            if (fn.IsRelative())
-            {
-                fn.MakeAbsolute();
-                fnd = fn.GetFullPath();
-            }
-#if wxUSE_FILESYSTEM
-            fnd = wxFileSystem::FileNameToURL(fnd);
-#endif
-        }
+    wxString fnd = wxXmlFindFirst;
+    if ( fnd.empty() )
+    {
+        wxLogError(_("Cannot load resources from '%s'."), filemask);
+        return false;
+    }
 
+    while (!fnd.empty())
+    {
 #if wxUSE_FILESYSTEM
-        if (fnd.Lower().Matches(wxT("*.zip")) ||
-            fnd.Lower().Matches(wxT("*.xrs")))
+        if ( IsArchive(fnd) )
         {
-            rt = rt && Load(fnd + wxT("#zip:*.xrc"));
+            if ( !Load(fnd + wxT("#zip:*.xrc")) )
+                allOK = false;
         }
-        else
-#endif
+        else // a single resource URL
+#endif // wxUSE_FILESYSTEM
         {
-            drec = new wxXmlResourceDataRecord;
-            drec->File = fnd;
-            m_data.Add(drec);
+            wxXmlDocument * const doc = DoLoadFile(fnd);
+            if ( !doc )
+                allOK = false;
+            else
+                Data().push_back(new wxXmlResourceDataRecord(fnd, doc));
         }
 
-        if (iswild)
-            fnd = wxXmlFindNext;
-        else
-            fnd = wxEmptyString;
+        fnd = wxXmlFindNext;
     }
 #   undef wxXmlFindFirst
 #   undef wxXmlFindNext
-    return rt && UpdateResources();
+
+    return allOK;
+}
+
+bool wxXmlResource::Unload(const wxString& filename)
+{
+    wxASSERT_MSG( !wxIsWild(filename),
+                    wxT("wildcards not supported by wxXmlResource::Unload()") );
+
+    wxString fnd = ConvertFileNameToURL(filename);
+#if wxUSE_FILESYSTEM
+    const bool isArchive = IsArchive(fnd);
+    if ( isArchive )
+        fnd += wxT("#zip:");
+#endif // wxUSE_FILESYSTEM
+
+    bool unloaded = false;
+    for ( wxXmlResourceDataRecords::iterator i = Data().begin();
+          i != Data().end(); ++i )
+    {
+#if wxUSE_FILESYSTEM
+        if ( isArchive )
+        {
+            if ( (*i)->File.StartsWith(fnd) )
+                unloaded = true;
+            // don't break from the loop, we can have other matching files
+        }
+        else // a single resource URL
+#endif // wxUSE_FILESYSTEM
+        {
+            if ( (*i)->File == fnd )
+            {
+                delete *i;
+                Data().erase(i);
+                unloaded = true;
+
+                // no sense in continuing, there is only one file with this URL
+                break;
+            }
+        }
+    }
+
+    return unloaded;
 }
 
 
@@ -149,13 +434,13 @@ IMPLEMENT_ABSTRACT_CLASS(wxXmlResourceHandler, wxObject)
 
 void wxXmlResource::AddHandler(wxXmlResourceHandler *handler)
 {
-    m_handlers.Append(handler);
+    m_handlers.push_back(handler);
     handler->SetParentResource(this);
 }
 
 void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler)
 {
-    m_handlers.Insert(handler);
+    m_handlers.insert(m_handlers.begin(), handler);
     handler->SetParentResource(this);
 }
 
@@ -163,7 +448,10 @@ void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler)
 
 void wxXmlResource::ClearHandlers()
 {
-    WX_CLEAR_LIST(wxList, m_handlers);
+    for ( wxVector<wxXmlResourceHandler*>::iterator i = m_handlers.begin();
+          i != m_handlers.end(); ++i )
+        delete *i;
+    m_handlers.clear();
 }
 
 
@@ -242,14 +530,27 @@ wxIcon wxXmlResource::LoadIcon(const wxString& name)
 }
 
 
-wxObject *wxXmlResource::LoadObject(wxWindow *parent, const wxString& name, const wxString& classname)
+wxObject *
+wxXmlResource::DoLoadObject(wxWindow *parent,
+                            const wxString& name,
+                            const wxString& classname,
+                            bool recursive)
 {
-    return CreateResFromNode(FindResource(name, classname), parent, NULL);
+    wxXmlNode * const node = FindResource(name, classname, recursive);
+
+    return node ? DoCreateResFromNode(*node, parent, NULL) : NULL;
 }
 
-bool wxXmlResource::LoadObject(wxObject *instance, wxWindow *parent, const wxString& name, const wxString& classname)
+bool
+wxXmlResource::DoLoadObject(wxObject *instance,
+                            wxWindow *parent,
+                            const wxString& name,
+                            const wxString& classname,
+                            bool recursive)
 {
-    return CreateResFromNode(FindResource(name, classname), parent, instance) != NULL;
+    wxXmlNode * const node = FindResource(name, classname, recursive);
+
+    return node && DoCreateResFromNode(*node, parent, instance) != NULL;
 }
 
 
@@ -261,7 +562,7 @@ bool wxXmlResource::AttachUnknownControl(const wxString& name,
     wxWindow *container = parent->FindWindow(name + wxT("_container"));
     if (!container)
     {
-        wxLogError(_("Cannot find container for unknown control '%s'."), name.c_str());
+        wxLogError("Cannot find container for unknown control '%s'.", name);
         return false;
     }
     return control->Reparent(container);
@@ -277,7 +578,7 @@ static void ProcessPlatformProperty(wxXmlNode *node)
     while (c)
     {
         isok = false;
-        if (!c->GetPropVal(wxT("platform"), &s))
+        if (!c->GetAttribute(wxT("platform"), &s))
             isok = true;
         else
         {
@@ -318,17 +619,113 @@ static void ProcessPlatformProperty(wxXmlNode *node)
     }
 }
 
+static void PreprocessForIdRanges(wxXmlNode *rootnode)
+{
+    // First go through the top level, looking for the names of ID ranges
+    // as processing items is a lot easier if names are already known
+    wxXmlNode *c = rootnode->GetChildren();
+    while (c)
+    {
+        if (c->GetName() == wxT("ids-range"))
+            wxIdRangeManager::Get()->AddRange(c);
+        c = c->GetNext();
+    }
 
+    // Next, examine every 'name' for the '[' that denotes an ID in a range
+    c = rootnode->GetChildren();
+    while (c)
+    {
+        wxString name = c->GetAttribute(wxT("name"));
+        if (name.find('[') != wxString::npos)
+            wxIdRangeManager::Get()->NotifyRangeOfItem(rootnode, name);
+
+        // Do any children by recursion, then proceed to the next sibling
+        PreprocessForIdRanges(c);
+        c = c->GetNext();
+    }
+}
 
 bool wxXmlResource::UpdateResources()
 {
     bool rt = true;
-    bool modif;
-#   if wxUSE_FILESYSTEM
-    wxFSFile *file = NULL;
-    wxUnusedVar(file);
+
+    for ( wxXmlResourceDataRecords::iterator i = Data().begin();
+          i != Data().end(); ++i )
+    {
+        wxXmlResourceDataRecord* const rec = *i;
+
+        // Check if we need to reload this one.
+
+        // We never do it if this flag is specified.
+        if ( m_flags & wxXRC_NO_RELOADING )
+            continue;
+
+        // Otherwise check its modification time if we can.
+#if wxUSE_DATETIME
+        const wxDateTime lastModTime = GetXRCFileModTime(rec->File);
+
+        if ( lastModTime.IsValid() && lastModTime <= rec->Time )
+#else // !wxUSE_DATETIME
+        // Never reload the file contents: we can't know whether it changed or
+        // not in this build configuration and it would be unexpected and
+        // counter-productive to get a performance hit (due to constant
+        // reloading of XRC files) in a minimal wx build which is presumably
+        // used because of resource constraints of the current platform.
+#endif // wxUSE_DATETIME/!wxUSE_DATETIME
+        {
+            // No need to reload, the file wasn't modified since we did it
+            // last.
+            continue;
+        }
+
+        wxXmlDocument * const doc = DoLoadFile(rec->File);
+        if ( !doc )
+        {
+            // Notice that we keep the old XML document: it seems better to
+            // preserve it instead of throwing it away if we have nothing to
+            // replace it with.
+            rt = false;
+            continue;
+        }
+
+        // Replace the old resource contents with the new one.
+        delete rec->Doc;
+        rec->Doc = doc;
+
+        // And, now that we loaded it successfully, update the last load time.
+#if wxUSE_DATETIME
+        rec->Time = lastModTime.IsValid() ? lastModTime : wxDateTime::Now();
+#endif // wxUSE_DATETIME
+    }
+
+    return rt;
+}
+
+wxXmlDocument *wxXmlResource::DoLoadFile(const wxString& filename)
+{
+    wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), filename);
+
+    wxInputStream *stream = NULL;
+
+#if wxUSE_FILESYSTEM
     wxFileSystem fsys;
-#   endif
+    wxScopedPtr<wxFSFile> file(fsys.OpenFile(filename));
+    if (file)
+    {
+        // Notice that we don't have ownership of the stream in this case, it
+        // remains owned by wxFSFile.
+        stream = file->GetStream();
+    }
+#else // !wxUSE_FILESYSTEM
+    wxFileInputStream fstream(filename);
+    stream = &fstream;
+#endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM
+
+    if ( !stream || !stream->IsOk() )
+    {
+        wxLogError(_("Cannot open resources file '%s'."), filename);
+        return NULL;
+    }
 
     wxString encoding(wxT("UTF-8"));
 #if !wxUSE_UNICODE && wxUSE_INTL
@@ -341,293 +738,649 @@ bool wxXmlResource::UpdateResources()
     }
 #endif
 
-    for (size_t i = 0; i < m_data.GetCount(); i++)
+    wxScopedPtr<wxXmlDocument> doc(new wxXmlDocument);
+    if (!doc->Load(*stream, encoding))
     {
-        modif = (m_data[i].Doc == NULL);
-
-        if (!modif && !(m_flags & wxXRC_NO_RELOADING))
-        {
-#           if wxUSE_FILESYSTEM
-            file = fsys.OpenFile(m_data[i].File);
-            modif = file && file->GetModificationTime() > m_data[i].Time;
-            if (!file)
-            {
-                wxLogError(_("Cannot open file '%s'."), m_data[i].File.c_str());
-                rt = false;
-            }
-            wxDELETE(file);
-            wxUnusedVar(file);
-#           else
-            modif = wxDateTime(wxFileModificationTime(m_data[i].File)) > m_data[i].Time;
-#           endif
-        }
-
-        if (modif)
-        {
-            wxLogTrace(_T("xrc"),
-                       _T("opening file '%s'"), m_data[i].File.c_str());
-
-            wxInputStream *stream = NULL;
-
-#           if wxUSE_FILESYSTEM
-            file = fsys.OpenFile(m_data[i].File);
-            if (file)
-                stream = file->GetStream();
-#           else
-            stream = new wxFileInputStream(m_data[i].File);
-#           endif
-
-            if (stream)
-            {
-                delete m_data[i].Doc;
-                m_data[i].Doc = new wxXmlDocument;
-            }
-            if (!stream || !m_data[i].Doc->Load(*stream, encoding))
-            {
-                wxLogError(_("Cannot load resources from file '%s'."),
-                           m_data[i].File.c_str());
-                wxDELETE(m_data[i].Doc);
-                rt = false;
-            }
-            else if (m_data[i].Doc->GetRoot()->GetName() != wxT("resource"))
-            {
-                wxLogError(_("Invalid XRC resource '%s': doesn't have root node 'resource'."), m_data[i].File.c_str());
-                wxDELETE(m_data[i].Doc);
-                rt = false;
-            }
-            else
-            {
-                long version;
-                int v1, v2, v3, v4;
-                wxString verstr = m_data[i].Doc->GetRoot()->GetPropVal(
-                                      wxT("version"), wxT("0.0.0.0"));
-                if (wxSscanf(verstr.c_str(), wxT("%i.%i.%i.%i"),
-                    &v1, &v2, &v3, &v4) == 4)
-                    version = v1*256*256*256+v2*256*256+v3*256+v4;
-                else
-                    version = 0;
-                if (m_version == -1)
-                    m_version = version;
-                if (m_version != version)
-                {
-                    wxLogError(_("Resource files must have same version number!"));
-                    rt = false;
-                }
+        wxLogError(_("Cannot load resources from file '%s'."), filename);
+        return NULL;
+    }
 
-                ProcessPlatformProperty(m_data[i].Doc->GetRoot());
-#if wxUSE_FILESYSTEM
-                m_data[i].Time = file->GetModificationTime();
-#else
-                m_data[i].Time = wxDateTime(wxFileModificationTime(m_data[i].File));
-#endif
-            }
+    wxXmlNode * const root = doc->GetRoot();
+    if (root->GetName() != wxT("resource"))
+    {
+        ReportError
+        (
+            root,
+            "invalid XRC resource, doesn't have root node <resource>"
+        );
+        return NULL;
+    }
 
-#           if wxUSE_FILESYSTEM
-                wxDELETE(file);
-                wxUnusedVar(file);
-#           else
-                wxDELETE(stream);
-#           endif
-        }
+    long version;
+    int v1, v2, v3, v4;
+    wxString verstr = root->GetAttribute(wxT("version"), wxT("0.0.0.0"));
+    if (wxSscanf(verstr, wxT("%i.%i.%i.%i"), &v1, &v2, &v3, &v4) == 4)
+        version = v1*256*256*256+v2*256*256+v3*256+v4;
+    else
+        version = 0;
+    if (m_version == -1)
+        m_version = version;
+    if (m_version != version)
+    {
+        wxLogWarning("Resource files must have same version number.");
     }
 
-    return rt;
-}
+    ProcessPlatformProperty(root);
+    PreprocessForIdRanges(root);
+    wxIdRangeManager::Get()->FinaliseRanges(root);
 
+    return doc.release();
+}
 
 wxXmlNode *wxXmlResource::DoFindResource(wxXmlNode *parent,
                                          const wxString& name,
                                          const wxString& classname,
-                                         bool recursive)
+                                         bool recursive) const
 {
-    wxString dummy;
     wxXmlNode *node;
 
     // first search for match at the top-level nodes (as this is
     // where the resource is most commonly looked for):
     for (node = parent->GetChildren(); node; node = node->GetNext())
     {
-        if ( node->GetType() == wxXML_ELEMENT_NODE &&
-                 (node->GetName() == wxT("object") ||
-                  node->GetName() == wxT("object_ref")) &&
-             node->GetPropVal(wxT("name"), &dummy) && dummy == name )
+        if ( IsObjectNode(node) && node->GetAttribute(wxS("name")) == name )
         {
-            wxString cls(node->GetPropVal(wxT("class"), wxEmptyString));
-            if (!classname || cls == classname)
+            // empty class name matches everything
+            if ( classname.empty() )
                 return node;
-            // object_ref may not have 'class' property:
-            if (cls.empty() && node->GetName() == wxT("object_ref"))
+
+            wxString cls(node->GetAttribute(wxS("class")));
+
+            // object_ref may not have 'class' attribute:
+            if (cls.empty() && node->GetName() == wxS("object_ref"))
             {
-                wxString refName = node->GetPropVal(wxT("ref"), wxEmptyString);
+                wxString refName = node->GetAttribute(wxS("ref"));
                 if (refName.empty())
                     continue;
-                wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
-                if (refNode &&
-                    refNode->GetPropVal(wxT("class"), wxEmptyString) == classname)
-                {
-                    return node;
-                }
+
+                const wxXmlNode * const refNode = GetResourceNode(refName);
+                if ( refNode )
+                    cls = refNode->GetAttribute(wxS("class"));
             }
+
+            if ( cls == classname )
+                return node;
         }
     }
 
+    // then recurse in child nodes
     if ( recursive )
+    {
         for (node = parent->GetChildren(); node; node = node->GetNext())
         {
-            if ( node->GetType() == wxXML_ELEMENT_NODE &&
-                 (node->GetName() == wxT("object") ||
-                  node->GetName() == wxT("object_ref")) )
+            if ( IsObjectNode(node) )
             {
                 wxXmlNode* found = DoFindResource(node, name, classname, true);
                 if ( found )
                     return found;
             }
         }
+    }
 
-   return NULL;
+    return NULL;
 }
 
 wxXmlNode *wxXmlResource::FindResource(const wxString& name,
                                        const wxString& classname,
                                        bool recursive)
 {
-    UpdateResources(); //ensure everything is up-to-date
+    wxString path;
+    wxXmlNode * const
+        node = GetResourceNodeAndLocation(name, classname, recursive, &path);
 
-    wxString dummy;
-    for (size_t f = 0; f < m_data.GetCount(); f++)
+    if ( !node )
+    {
+        ReportError
+        (
+            NULL,
+            wxString::Format
+            (
+                "XRC resource \"%s\" (class \"%s\") not found",
+                name, classname
+            )
+        );
+    }
+#if wxUSE_FILESYSTEM
+    else // node was found
     {
-        if ( m_data[f].Doc == NULL || m_data[f].Doc->GetRoot() == NULL )
+        // ensure that relative paths work correctly when loading this node
+        // (which should happen as soon as we return as FindResource() result
+        // is always passed to CreateResFromNode())
+        m_curFileSystem.ChangePathTo(path);
+    }
+#endif // wxUSE_FILESYSTEM
+
+    return node;
+}
+
+wxXmlNode *
+wxXmlResource::GetResourceNodeAndLocation(const wxString& name,
+                                          const wxString& classname,
+                                          bool recursive,
+                                          wxString *path) const
+{
+    // ensure everything is up-to-date: this is needed to support on-demand
+    // reloading of XRC files
+    const_cast<wxXmlResource *>(this)->UpdateResources();
+
+    for ( wxXmlResourceDataRecords::const_iterator f = Data().begin();
+          f != Data().end(); ++f )
+    {
+        wxXmlResourceDataRecord *const rec = *f;
+        wxXmlDocument * const doc = rec->Doc;
+        if ( !doc || !doc->GetRoot() )
             continue;
 
-        wxXmlNode* found = DoFindResource(m_data[f].Doc->GetRoot(),
-                                          name, classname, recursive);
+        wxXmlNode * const
+            found = DoFindResource(doc->GetRoot(), name, classname, recursive);
         if ( found )
         {
-#if wxUSE_FILESYSTEM
-            m_curFileSystem.ChangePathTo(m_data[f].File);
-#endif
+            if ( path )
+                *path = rec->File;
+
             return found;
         }
     }
 
-    wxLogError(_("XRC resource '%s' (class '%s') not found!"),
-               name.c_str(), classname.c_str());
     return NULL;
 }
 
-static void MergeNodes(wxXmlNode& dest, wxXmlNode& with)
+static void MergeNodesOver(wxXmlNode& dest, wxXmlNode& overwriteWith,
+                           const wxString& overwriteFilename)
 {
-    // Merge properties:
-    for (wxXmlProperty *prop = with.GetProperties(); prop; prop = prop->GetNext())
+    // Merge attributes:
+    for ( wxXmlAttribute *attr = overwriteWith.GetAttributes();
+          attr; attr = attr->GetNext() )
     {
-        wxXmlProperty *dprop;
-        for (dprop = dest.GetProperties(); dprop; dprop = dprop->GetNext())
+        wxXmlAttribute *dattr;
+        for (dattr = dest.GetAttributes(); dattr; dattr = dattr->GetNext())
         {
 
-            if ( dprop->GetName() == prop->GetName() )
+            if ( dattr->GetName() == attr->GetName() )
             {
-                dprop->SetValue(prop->GetValue());
+                dattr->SetValue(attr->GetValue());
                 break;
             }
         }
 
-        if ( !dprop )
-            dest.AddProperty(prop->GetName(), prop->GetValue());
+        if ( !dattr )
+            dest.AddAttribute(attr->GetName(), attr->GetValue());
    }
 
     // Merge child nodes:
-    for (wxXmlNode* node = with.GetChildren(); node; node = node->GetNext())
+    for (wxXmlNode* node = overwriteWith.GetChildren(); node; node = node->GetNext())
     {
-        wxString name = node->GetPropVal(wxT("name"), wxEmptyString);
+        wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
         wxXmlNode *dnode;
 
         for (dnode = dest.GetChildren(); dnode; dnode = dnode->GetNext() )
         {
             if ( dnode->GetName() == node->GetName() &&
-                 dnode->GetPropVal(wxT("name"), wxEmptyString) == name &&
+                 dnode->GetAttribute(wxT("name"), wxEmptyString) == name &&
                  dnode->GetType() == node->GetType() )
             {
-                MergeNodes(*dnode, *node);
+                MergeNodesOver(*dnode, *node, overwriteFilename);
                 break;
             }
         }
 
         if ( !dnode )
-            dest.AddChild(new wxXmlNode(*node));
+        {
+            wxXmlNode *copyOfNode = new wxXmlNode(*node);
+            // remember referenced object's file, see GetFileNameFromNode()
+            copyOfNode->AddAttribute(ATTR_INPUT_FILENAME, overwriteFilename);
+
+            static const wxChar *AT_END = wxT("end");
+            wxString insert_pos = node->GetAttribute(wxT("insert_at"), AT_END);
+            if ( insert_pos == AT_END )
+            {
+                dest.AddChild(copyOfNode);
+            }
+            else if ( insert_pos == wxT("begin") )
+            {
+                dest.InsertChild(copyOfNode, dest.GetChildren());
+            }
+        }
     }
 
-    if ( dest.GetType() == wxXML_TEXT_NODE && with.GetContent().Length() )
-         dest.SetContent(with.GetContent());
+    if ( dest.GetType() == wxXML_TEXT_NODE && overwriteWith.GetContent().length() )
+         dest.SetContent(overwriteWith.GetContent());
 }
 
-wxObject *wxXmlResource::CreateResFromNode(wxXmlNode *node, wxObject *parent,
-                                           wxObject *instance,
-                                           wxXmlResourceHandler *handlerToUse)
+wxObject *
+wxXmlResource::DoCreateResFromNode(wxXmlNode& node,
+                                   wxObject *parent,
+                                   wxObject *instance,
+                                   wxXmlResourceHandler *handlerToUse)
 {
-    if (node == NULL) return NULL;
-
     // handling of referenced resource
-    if ( node->GetName() == wxT("object_ref") )
+    if ( node.GetName() == wxT("object_ref") )
     {
-        wxString refName = node->GetPropVal(wxT("ref"), wxEmptyString);
+        wxString refName = node.GetAttribute(wxT("ref"), wxEmptyString);
         wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
 
         if ( !refNode )
         {
-            wxLogError(_("Referenced object node with ref=\"%s\" not found!"),
-                       refName.c_str());
+            ReportError
+            (
+                &node,
+                wxString::Format
+                (
+                    "referenced object node with ref=\"%s\" not found",
+                    refName
+                )
+            );
             return NULL;
         }
 
-        wxXmlNode copy(*refNode);
-        MergeNodes(copy, *node);
+        if ( !node.GetChildren() )
+        {
+            // In the typical, simple case, <object_ref> is used to link
+            // to another node and doesn't have any content of its own that
+            // would overwrite linked object's properties. In this case,
+            // we can simply create the resource from linked node.
 
-        return CreateResFromNode(&copy, parent, instance);
-    }
+            return DoCreateResFromNode(*refNode, parent, instance);
+        }
+        else
+        {
+            // In the more complicated (but rare) case, <object_ref> has
+            // subnodes that partially overwrite content of the referenced
+            // object. In this case, we need to merge both XML trees and
+            // load the resource from result of the merge.
+
+            wxXmlNode copy(*refNode);
+            MergeNodesOver(copy, node, GetFileNameFromNode(&node, Data()));
 
-    wxXmlResourceHandler *handler;
+            // remember referenced object's file, see GetFileNameFromNode()
+            copy.AddAttribute(ATTR_INPUT_FILENAME,
+                              GetFileNameFromNode(refNode, Data()));
+
+            return DoCreateResFromNode(copy, parent, instance);
+        }
+    }
 
     if (handlerToUse)
     {
-        if (handlerToUse->CanHandle(node))
+        if (handlerToUse->CanHandle(&node))
+        {
+            return handlerToUse->CreateResource(&node, parent, instance);
+        }
+    }
+    else if (node.GetName() == wxT("object"))
+    {
+        for ( wxVector<wxXmlResourceHandler*>::iterator h = m_handlers.begin();
+              h != m_handlers.end(); ++h )
+        {
+            wxXmlResourceHandler *handler = *h;
+            if (handler->CanHandle(&node))
+                return handler->CreateResource(&node, parent, instance);
+        }
+    }
+
+    ReportError
+    (
+        &node,
+        wxString::Format
+        (
+            "no handler found for XML node \"%s\" (class \"%s\")",
+            node.GetName(),
+            node.GetAttribute("class", wxEmptyString)
+        )
+    );
+    return NULL;
+}
+
+wxIdRange::wxIdRange(const wxXmlNode* node,
+                     const wxString& rname,
+                     const wxString& startno,
+                     const wxString& rsize)
+    : m_name(rname),
+      m_start(0),
+      m_size(0),
+      m_item_end_found(0),
+      m_finalised(0)
+{
+    long l;
+    if ( startno.ToLong(&l) )
+    {
+        if ( l >= 0 )
+        {
+            m_start = l;
+        }
+        else
+        {
+            wxXmlResource::Get()->ReportError
+            (
+                node,
+                "a negative id-range start parameter was given"
+            );
+        }
+    }
+    else
+    {
+        wxXmlResource::Get()->ReportError
+        (
+            node,
+            "the id-range start parameter was malformed"
+        );
+    }
+
+    unsigned long ul;
+    if ( rsize.ToULong(&ul) )
+    {
+        m_size = ul;
+    }
+    else
+    {
+        wxXmlResource::Get()->ReportError
+        (
+            node,
+            "the id-range size parameter was malformed"
+        );
+    }
+}
+
+void wxIdRange::NoteItem(const wxXmlNode* node, const wxString& item)
+{
+    // Nothing gets added here, but the existence of each item is noted
+    // thus getting an accurate count. 'item' will be either an integer e.g.
+    // [0] [123]: will eventually create an XRCID as start+integer or [start]
+    // or [end] which are synonyms for [0] or [range_size-1] respectively.
+    wxString content(item.Mid(1, item.length()-2));
+
+    // Check that basename+item wasn't foo[]
+    if (content.empty())
+    {
+        wxXmlResource::Get()->ReportError(node, "an empty id-range item found");
+        return;
+    }
+
+    if (content=="start")
+    {
+        // "start" means [0], so store that in the set
+        if (m_indices.count(0) == 0)
+        {
+            m_indices.insert(0);
+        }
+        else
         {
-            return handlerToUse->CreateResource(node, parent, instance);
+            wxXmlResource::Get()->ReportError
+            (
+                node,
+                "duplicate id-range item found"
+            );
         }
     }
-    else if (node->GetName() == wxT("object"))
+    else if (content=="end")
+    {
+        // We can't yet be certain which XRCID this will be equivalent to, so
+        // just note that there's an item with this name, in case we need to
+        // inc the range size
+        m_item_end_found = true;
+    }
+    else
     {
-        wxList::compatibility_iterator ND = m_handlers.GetFirst();
-        while (ND)
+        // Anything else will be an integer, or rubbish
+        unsigned long l;
+        if ( content.ToULong(&l) )
         {
-            handler = (wxXmlResourceHandler*)ND->GetData();
-            if (handler->CanHandle(node))
+            if (m_indices.count(l) == 0)
+            {
+                m_indices.insert(l);
+                // Check that this item wouldn't fall outside the current range
+                // extent
+                if (l >= m_size)
+                {
+                    m_size = l + 1;
+                }
+            }
+            else
             {
-                return handler->CreateResource(node, parent, instance);
+                wxXmlResource::Get()->ReportError
+                (
+                    node,
+                    "duplicate id-range item found"
+                );
             }
-            ND = ND->GetNext();
+
+        }
+        else
+        {
+            wxXmlResource::Get()->ReportError
+            (
+                node,
+                "an id-range item had a malformed index"
+            );
         }
     }
+}
+
+void wxIdRange::Finalise(const wxXmlNode* node)
+{
+    wxCHECK_RET( !IsFinalised(),
+                 "Trying to finalise an already-finalised range" );
+
+    // Now we know about all the items, we can get an accurate range size
+    // Expand any requested range-size if there were more items than would fit
+    m_size = wxMax(m_size, m_indices.size());
+
+    // If an item is explicitly called foo[end], ensure it won't clash with
+    // another item
+    if ( m_item_end_found && m_indices.count(m_size-1) )
+        ++m_size;
+    if (m_size == 0)
+    {
+        // This will happen if someone creates a range but no items in this xrc
+        // file Report the error and abort, but don't finalise, in case items
+        // appear later
+        wxXmlResource::Get()->ReportError
+        (
+            node,
+            "trying to create an empty id-range"
+        );
+        return;
+    }
 
-    wxLogError(_("No handler found for XML node '%s', class '%s'!"),
-               node->GetName().c_str(),
-               node->GetPropVal(wxT("class"), wxEmptyString).c_str());
+    if (m_start==0)
+    {
+        // This is the usual case, where the user didn't specify a start ID
+        // So get the range using NewControlId().
+        //
+        // NB: negative numbers, but NewControlId already returns the most
+        //     negative
+        m_start = wxWindow::NewControlId(m_size);
+        wxCHECK_RET( m_start != wxID_NONE,
+                     "insufficient IDs available to create range" );
+        m_end = m_start + m_size - 1;
+    }
+    else
+    {
+        // The user already specified a start value, which must be positive
+        m_end = m_start + m_size - 1;
+    }
+
+    // Create the XRCIDs
+    for (int i=m_start; i <= m_end; ++i)
+    {
+        // First clear any pre-existing XRCID
+        // Necessary for wxXmlResource::Unload() followed by Load()
+        wxIdRangeManager::RemoveXRCIDEntry(
+                m_name + wxString::Format("[%i]", i-m_start));
+
+        // Use the second parameter of GetXRCID to force it to take the value i
+        wxXmlResource::GetXRCID(m_name + wxString::Format("[%i]", i-m_start), i);
+        wxLogTrace("xrcrange",
+                   "integer = %i %s now returns %i",
+                   i,
+                   m_name + wxString::Format("[%i]", i-m_start),
+                   XRCID((m_name + wxString::Format("[%i]", i-m_start)).mb_str()));
+    }
+    // and these special ones
+    wxIdRangeManager::RemoveXRCIDEntry(m_name + "[start]");
+    wxXmlResource::GetXRCID(m_name + "[start]", m_start);
+    wxIdRangeManager::RemoveXRCIDEntry(m_name + "[end]");
+    wxXmlResource::GetXRCID(m_name + "[end]", m_end);
+    wxLogTrace("xrcrange","%s[start] = %i  %s[end] = %i",
+            m_name.mb_str(),XRCID(wxString(m_name+"[start]").mb_str()),
+                m_name.mb_str(),XRCID(wxString(m_name+"[end]").mb_str()));
+
+    m_finalised = true;
+}
+
+wxIdRangeManager *wxIdRangeManager::ms_instance = NULL;
+
+/*static*/ wxIdRangeManager *wxIdRangeManager::Get()
+{
+    if ( !ms_instance )
+        ms_instance = new wxIdRangeManager;
+    return ms_instance;
+}
+
+/*static*/ wxIdRangeManager *wxIdRangeManager::Set(wxIdRangeManager *res)
+{
+    wxIdRangeManager *old = ms_instance;
+    ms_instance = res;
+    return old;
+}
+
+wxIdRangeManager::~wxIdRangeManager()
+{
+    for ( wxVector<wxIdRange*>::iterator i = m_IdRanges.begin();
+          i != m_IdRanges.end(); ++i )
+    {
+        delete *i;
+    }
+    m_IdRanges.clear();
+
+    delete ms_instance;
+}
+
+void wxIdRangeManager::AddRange(const wxXmlNode* node)
+{
+    wxString name = node->GetAttribute("name");
+    wxString start = node->GetAttribute("start", "0");
+    wxString size = node->GetAttribute("size", "0");
+    if (name.empty())
+    {
+        wxXmlResource::Get()->ReportError
+        (
+            node,
+            "xrc file contains an id-range without a name"
+        );
+        return;
+    }
+
+    int index = Find(name);
+    if (index == wxNOT_FOUND)
+    {
+        wxLogTrace("xrcrange",
+                   "Adding ID range, name=%s start=%s size=%s",
+                   name, start, size);
+
+        m_IdRanges.push_back(new wxIdRange(node, name, start, size));
+    }
+    else
+    {
+        // There was already a range with this name. Let's hope this is
+        // from an Unload()/(re)Load(), not an unintentional duplication
+        wxLogTrace("xrcrange",
+                   "Replacing ID range, name=%s start=%s size=%s",
+                   name, start, size);
+
+        wxIdRange* oldrange = m_IdRanges.at(index);
+        m_IdRanges.at(index) = new wxIdRange(node, name, start, size);
+        delete oldrange;
+    }
+}
+
+wxIdRange *
+wxIdRangeManager::FindRangeForItem(const wxXmlNode* node,
+                                   const wxString& item,
+                                   wxString& value) const
+{
+    wxString basename = item.BeforeFirst('[');
+    wxCHECK_MSG( !basename.empty(), NULL,
+                 "an id-range item without a range name" );
+
+    int index = Find(basename);
+    if (index == wxNOT_FOUND)
+    {
+        // Don't assert just because we've found an unexpected foo[123]
+        // Someone might just want such a name, nothing to do with ranges
+        return NULL;
+    }
+
+    value = item.Mid(basename.Len());
+    if (value.at(value.length()-1)==']')
+    {
+        return m_IdRanges.at(index);
+    }
+    wxXmlResource::Get()->ReportError(node, "a malformed id-range item");
     return NULL;
 }
 
+void
+wxIdRangeManager::NotifyRangeOfItem(const wxXmlNode* node,
+                                    const wxString& item) const
+{
+    wxString value;
+    wxIdRange* range = FindRangeForItem(node, item, value);
+    if (range)
+        range->NoteItem(node, value);
+}
+
+int wxIdRangeManager::Find(const wxString& rangename) const
+{
+    for ( int i=0; i < (int)m_IdRanges.size(); ++i )
+    {
+        if (m_IdRanges.at(i)->GetName() == rangename)
+            return i;
+    }
 
-#include "wx/listimpl.cpp"
-WX_DECLARE_LIST(wxXmlSubclassFactory, wxXmlSubclassFactoriesList);
-WX_DEFINE_LIST(wxXmlSubclassFactoriesList);
+    return wxNOT_FOUND;
+}
+
+void wxIdRangeManager::FinaliseRanges(const wxXmlNode* node) const
+{
+    for ( wxVector<wxIdRange*>::const_iterator i = m_IdRanges.begin();
+          i != m_IdRanges.end(); ++i )
+    {
+        // Check if this range has already been finalised. Quite possible,
+        // as  FinaliseRanges() gets called for each .xrc file loaded
+        if (!(*i)->IsFinalised())
+        {
+            wxLogTrace("xrcrange", "Finalising ID range %s", (*i)->GetName());
+            (*i)->Finalise(node);
+        }
+    }
+}
 
-wxXmlSubclassFactoriesList *wxXmlResource::ms_subclassFactories = NULL;
+
+class wxXmlSubclassFactories : public wxVector<wxXmlSubclassFactory*>
+{
+    // this is a class so that it can be forward-declared
+};
+
+wxXmlSubclassFactories *wxXmlResource::ms_subclassFactories = NULL;
 
 /*static*/ void wxXmlResource::AddSubclassFactory(wxXmlSubclassFactory *factory)
 {
     if (!ms_subclassFactories)
     {
-        ms_subclassFactories = new wxXmlSubclassFactoriesList;
+        ms_subclassFactories = new wxXmlSubclassFactories;
     }
-    ms_subclassFactories->Append(factory);
+    ms_subclassFactories->push_back(factory);
 }
 
 class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory
@@ -649,7 +1402,6 @@ public:
 
 
 
-
 wxXmlResourceHandler::wxXmlResourceHandler()
         : m_node(NULL), m_parent(NULL), m_instance(NULL),
           m_parentAsWindow(NULL)
@@ -665,31 +1417,38 @@ wxObject *wxXmlResourceHandler::CreateResource(wxXmlNode *node, wxObject *parent
     wxWindow *myParentAW = m_parentAsWindow;
 
     m_instance = instance;
-    if (!m_instance && node->HasProp(wxT("subclass")) &&
+    if (!m_instance && node->HasAttribute(wxT("subclass")) &&
         !(m_resource->GetFlags() & wxXRC_NO_SUBCLASSING))
     {
-        wxString subclass = node->GetPropVal(wxT("subclass"), wxEmptyString);
+        wxString subclass = node->GetAttribute(wxT("subclass"), wxEmptyString);
         if (!subclass.empty())
         {
-            for (wxXmlSubclassFactoriesList::compatibility_iterator i = wxXmlResource::ms_subclassFactories->GetFirst();
-                 i; i = i->GetNext())
+            for (wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
+                 i != wxXmlResource::ms_subclassFactories->end(); ++i)
             {
-                m_instance = i->GetData()->Create(subclass);
+                m_instance = (*i)->Create(subclass);
                 if (m_instance)
                     break;
             }
 
             if (!m_instance)
             {
-                wxString name = node->GetPropVal(wxT("name"), wxEmptyString);
-                wxLogError(_("Subclass '%s' not found for resource '%s', not subclassing!"),
-                           subclass.c_str(), name.c_str());
+                wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
+                ReportError
+                (
+                    node,
+                    wxString::Format
+                    (
+                        "subclass \"%s\" not found for resource \"%s\", not subclassing",
+                        subclass, name
+                    )
+                );
             }
         }
     }
 
     m_node = node;
-    m_class = node->GetPropVal(wxT("class"), wxEmptyString);
+    m_class = node->GetAttribute(wxT("class"), wxEmptyString);
     m_parent = parent;
     m_parentAsWindow = wxDynamicCast(m_parent, wxWindow);
 
@@ -715,17 +1474,24 @@ void wxXmlResourceHandler::AddStyle(const wxString& name, int value)
 void wxXmlResourceHandler::AddWindowStyles()
 {
     XRC_ADD_STYLE(wxCLIP_CHILDREN);
-    XRC_ADD_STYLE(wxSIMPLE_BORDER);
-    XRC_ADD_STYLE(wxSUNKEN_BORDER);
-    XRC_ADD_STYLE(wxDOUBLE_BORDER);
-    XRC_ADD_STYLE(wxRAISED_BORDER);
-    XRC_ADD_STYLE(wxSTATIC_BORDER);
-    XRC_ADD_STYLE(wxNO_BORDER);
+
+    // the border styles all have the old and new names, recognize both for now
+    XRC_ADD_STYLE(wxSIMPLE_BORDER); XRC_ADD_STYLE(wxBORDER_SIMPLE);
+    XRC_ADD_STYLE(wxSUNKEN_BORDER); XRC_ADD_STYLE(wxBORDER_SUNKEN);
+    XRC_ADD_STYLE(wxDOUBLE_BORDER); XRC_ADD_STYLE(wxBORDER_DOUBLE); // deprecated
+    XRC_ADD_STYLE(wxBORDER_THEME);
+    XRC_ADD_STYLE(wxRAISED_BORDER); XRC_ADD_STYLE(wxBORDER_RAISED);
+    XRC_ADD_STYLE(wxSTATIC_BORDER); XRC_ADD_STYLE(wxBORDER_STATIC);
+    XRC_ADD_STYLE(wxNO_BORDER);     XRC_ADD_STYLE(wxBORDER_NONE);
+
     XRC_ADD_STYLE(wxTRANSPARENT_WINDOW);
     XRC_ADD_STYLE(wxWANTS_CHARS);
+    XRC_ADD_STYLE(wxTAB_TRAVERSAL);
     XRC_ADD_STYLE(wxNO_FULL_REPAINT_ON_RESIZE);
     XRC_ADD_STYLE(wxFULL_REPAINT_ON_RESIZE);
+    XRC_ADD_STYLE(wxALWAYS_SHOW_SB);
     XRC_ADD_STYLE(wxWS_EX_BLOCK_EVENTS);
+    XRC_ADD_STYLE(wxWS_EX_VALIDATE_RECURSIVELY);
 }
 
 
@@ -751,9 +1517,17 @@ int wxXmlResourceHandler::GetStyle(const wxString& param, int defaults)
         fl = tkn.GetNextToken();
         index = m_styleNames.Index(fl);
         if (index != wxNOT_FOUND)
+        {
             style |= m_styleValues[index];
+        }
         else
-            wxLogError(_("Unknown style flag ") + fl);
+        {
+            ReportParamError
+            (
+                param,
+                wxString::Format("unknown style flag \"%s\"", fl)
+            );
+        }
     }
     return style;
 }
@@ -765,22 +1539,21 @@ wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
     wxXmlNode *parNode = GetParamNode(param);
     wxString str1(GetNodeContent(parNode));
     wxString str2;
-    const wxChar *dt;
-    wxChar amp_char;
+
+    // "\\" wasn't translated to "\" prior to 2.5.3.0:
+    const bool escapeBackslash = (m_resource->CompareVersion(2,5,3,0) >= 0);
 
     // VS: First version of XRC resources used $ instead of & (which is
     //     illegal in XML), but later I realized that '_' fits this purpose
     //     much better (because &File means "File with F underlined").
-    if (m_resource->CompareVersion(2,3,0,1) < 0)
-        amp_char = wxT('$');
-    else
-        amp_char = wxT('_');
+    const wxChar amp_char = (m_resource->CompareVersion(2,3,0,1) < 0)
+                            ? '$' : '_';
 
-    for (dt = str1.c_str(); *dt; dt++)
+    for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
     {
         // Remap amp_char to &, map double amp_char to amp_char (for things
         // like "&File..." -- this is illegal in XML, so we use "_File..."):
-        if (*dt == amp_char)
+        if ( *dt == amp_char )
         {
             if ( *(++dt) == amp_char )
                 str2 << amp_char;
@@ -788,43 +1561,48 @@ wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
                 str2 << wxT('&') << *dt;
         }
         // Remap \n to CR, \r to LF, \t to TAB, \\ to \:
-        else if (*dt == wxT('\\'))
-            switch (*(++dt))
+        else if ( *dt == wxT('\\') )
+        {
+            switch ( (*(++dt)).GetValue() )
             {
                 case wxT('n'):
                     str2 << wxT('\n');
                     break;
-                    
+
                 case wxT('t'):
                     str2 << wxT('\t');
                     break;
-                    
+
                 case wxT('r'):
                     str2 << wxT('\r');
                     break;
 
                 case wxT('\\') :
                     // "\\" wasn't translated to "\" prior to 2.5.3.0:
-                    if (m_resource->CompareVersion(2,5,3,0) >= 0)
+                    if ( escapeBackslash )
                     {
                         str2 << wxT('\\');
                         break;
                     }
                     // else fall-through to default: branch below
-    
+
                 default:
                     str2 << wxT('\\') << *dt;
                     break;
             }
-        else str2 << *dt;
+        }
+        else
+        {
+            str2 << *dt;
+        }
     }
 
     if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
     {
         if (translate && parNode &&
-            parNode->GetPropVal(wxT("translate"), wxEmptyString) != wxT("0"))
+            parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
         {
-            return wxGetTranslation(str2);
+            return wxGetTranslation(str2, m_resource->GetDomain());
         }
         else
         {
@@ -833,17 +1611,15 @@ wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
 #else
             // The string is internally stored as UTF-8, we have to convert
             // it into system's default encoding so that it can be displayed:
-            return wxString(str2.mb_str(wxConvUTF8), wxConvLocal);
+            return wxString(str2.wc_str(wxConvUTF8), wxConvLocal);
 #endif
         }
     }
-    else
-    {
-        // If wxXRC_USE_LOCALE is not set, then the string is already in
-        // system's default encoding in ANSI build, so we don't have to
-        // do anything special here.
-        return str2;
-    }
+
+    // If wxXRC_USE_LOCALE is not set, then the string is already in
+    // system's default encoding in ANSI build, so we don't have to
+    // do anything special here.
+    return str2;
 }
 
 
@@ -859,6 +1635,19 @@ long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv)
     return value;
 }
 
+float wxXmlResourceHandler::GetFloat(const wxString& param, float defaultv)
+{
+    wxString str = GetParamValue(param);
+
+    // strings in XRC always use C locale so make sure to use the
+    // locale-independent wxString::ToCDouble() and not ToDouble() which uses
+    // the current locale with a potentially different decimal point character
+    double value;
+    if (!str.ToCDouble(&value))
+        value = defaultv;
+
+    return wx_truncate_cast(float, value);
+}
 
 
 int wxXmlResourceHandler::GetID()
@@ -870,105 +1659,328 @@ int wxXmlResourceHandler::GetID()
 
 wxString wxXmlResourceHandler::GetName()
 {
-    return m_node->GetPropVal(wxT("name"), wxT("-1"));
+    return m_node->GetAttribute(wxT("name"), wxT("-1"));
 }
 
 
 
+bool wxXmlResourceHandler::GetBoolAttr(const wxString& attr, bool defaultv)
+{
+    wxString v;
+    return m_node->GetAttribute(attr, &v) ? v == '1' : defaultv;
+}
+
 bool wxXmlResourceHandler::GetBool(const wxString& param, bool defaultv)
 {
-    wxString v = GetParamValue(param);
-    v.MakeLower();
-    if (!v) return defaultv;
-    else return (v == wxT("1"));
+    const wxString v = GetParamValue(param);
+
+    return v.empty() ? defaultv : (v == '1');
 }
 
 
+static wxColour GetSystemColour(const wxString& name)
+{
+    if (!name.empty())
+    {
+        #define SYSCLR(clr) \
+            if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr);
+        SYSCLR(wxSYS_COLOUR_SCROLLBAR)
+        SYSCLR(wxSYS_COLOUR_BACKGROUND)
+        SYSCLR(wxSYS_COLOUR_DESKTOP)
+        SYSCLR(wxSYS_COLOUR_ACTIVECAPTION)
+        SYSCLR(wxSYS_COLOUR_INACTIVECAPTION)
+        SYSCLR(wxSYS_COLOUR_MENU)
+        SYSCLR(wxSYS_COLOUR_WINDOW)
+        SYSCLR(wxSYS_COLOUR_WINDOWFRAME)
+        SYSCLR(wxSYS_COLOUR_MENUTEXT)
+        SYSCLR(wxSYS_COLOUR_WINDOWTEXT)
+        SYSCLR(wxSYS_COLOUR_CAPTIONTEXT)
+        SYSCLR(wxSYS_COLOUR_ACTIVEBORDER)
+        SYSCLR(wxSYS_COLOUR_INACTIVEBORDER)
+        SYSCLR(wxSYS_COLOUR_APPWORKSPACE)
+        SYSCLR(wxSYS_COLOUR_HIGHLIGHT)
+        SYSCLR(wxSYS_COLOUR_HIGHLIGHTTEXT)
+        SYSCLR(wxSYS_COLOUR_BTNFACE)
+        SYSCLR(wxSYS_COLOUR_3DFACE)
+        SYSCLR(wxSYS_COLOUR_BTNSHADOW)
+        SYSCLR(wxSYS_COLOUR_3DSHADOW)
+        SYSCLR(wxSYS_COLOUR_GRAYTEXT)
+        SYSCLR(wxSYS_COLOUR_BTNTEXT)
+        SYSCLR(wxSYS_COLOUR_INACTIVECAPTIONTEXT)
+        SYSCLR(wxSYS_COLOUR_BTNHIGHLIGHT)
+        SYSCLR(wxSYS_COLOUR_BTNHILIGHT)
+        SYSCLR(wxSYS_COLOUR_3DHIGHLIGHT)
+        SYSCLR(wxSYS_COLOUR_3DHILIGHT)
+        SYSCLR(wxSYS_COLOUR_3DDKSHADOW)
+        SYSCLR(wxSYS_COLOUR_3DLIGHT)
+        SYSCLR(wxSYS_COLOUR_INFOTEXT)
+        SYSCLR(wxSYS_COLOUR_INFOBK)
+        SYSCLR(wxSYS_COLOUR_LISTBOX)
+        SYSCLR(wxSYS_COLOUR_HOTLIGHT)
+        SYSCLR(wxSYS_COLOUR_GRADIENTACTIVECAPTION)
+        SYSCLR(wxSYS_COLOUR_GRADIENTINACTIVECAPTION)
+        SYSCLR(wxSYS_COLOUR_MENUHILIGHT)
+        SYSCLR(wxSYS_COLOUR_MENUBAR)
+        #undef SYSCLR
+    }
+
+    return wxNullColour;
+}
 
-wxColour wxXmlResourceHandler::GetColour(const wxString& param)
+wxColour wxXmlResourceHandler::GetColour(const wxString& param, const wxColour& defaultv)
 {
     wxString v = GetParamValue(param);
-    unsigned long tmp = 0;
 
-    if (v.Length() != 7 || v[0u] != wxT('#') ||
-        wxSscanf(v.c_str(), wxT("#%lX"), &tmp) != 1)
+    if ( v.empty() )
+        return defaultv;
+
+    wxColour clr;
+
+    // wxString -> wxColour conversion
+    if (!clr.Set(v))
     {
-        wxLogError(_("XRC resource: Incorrect colour specification '%s' for property '%s'."),
-                   v.c_str(), param.c_str());
+        // the colour doesn't use #RRGGBB format, check if it is symbolic
+        // colour name:
+        clr = GetSystemColour(v);
+        if (clr.Ok())
+            return clr;
+
+        ReportParamError
+        (
+            param,
+            wxString::Format("incorrect colour specification \"%s\"", v)
+        );
         return wxNullColour;
     }
 
-    return wxColour((unsigned char) ((tmp & 0xFF0000) >> 16) ,
-                    (unsigned char) ((tmp & 0x00FF00) >> 8),
-                    (unsigned char) ((tmp & 0x0000FF)));
+    return clr;
 }
 
+namespace
+{
+
+// if 'param' has stock_id/stock_client, extracts them and returns true
+bool GetStockArtAttrs(const wxXmlNode *paramNode,
+                      const wxString& defaultArtClient,
+                      wxString& art_id, wxString& art_client)
+{
+    if ( paramNode )
+    {
+        art_id = paramNode->GetAttribute("stock_id", "");
+
+        if ( !art_id.empty() )
+        {
+            art_id = wxART_MAKE_ART_ID_FROM_STR(art_id);
+
+            art_client = paramNode->GetAttribute("stock_client", "");
+            if ( art_client.empty() )
+                art_client = defaultArtClient;
+            else
+                art_client = wxART_MAKE_CLIENT_ID_FROM_STR(art_client);
+
+            return true;
+        }
+    }
+
+    return false;
+}
 
+} // anonymous namespace
 
 wxBitmap wxXmlResourceHandler::GetBitmap(const wxString& param,
                                          const wxArtClient& defaultArtClient,
                                          wxSize size)
 {
+    // it used to be possible to pass an empty string here to indicate that the
+    // bitmap name should be read from this node itself but this is not
+    // supported any more because GetBitmap(m_node) can be used directly
+    // instead
+    wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" );
+
+    const wxXmlNode* const node = GetParamNode(param);
+
+    if ( !node )
+    {
+        // this is not an error as bitmap parameter could be optional
+        return wxNullBitmap;
+    }
+
+    return GetBitmap(node, defaultArtClient, size);
+}
+
+wxBitmap wxXmlResourceHandler::GetBitmap(const wxXmlNode* node,
+                                         const wxArtClient& defaultArtClient,
+                                         wxSize size)
+{
+    wxCHECK_MSG( node, wxNullBitmap, "bitmap node can't be NULL" );
+
     /* If the bitmap is specified as stock item, query wxArtProvider for it: */
-    wxXmlNode *bmpNode = GetParamNode(param);
-    if ( bmpNode )
+    wxString art_id, art_client;
+    if ( GetStockArtAttrs(node, defaultArtClient,
+                          art_id, art_client) )
     {
-        wxString sid = bmpNode->GetPropVal(wxT("stock_id"), wxEmptyString);
-        if ( !sid.empty() )
-        {
-            wxString scl = bmpNode->GetPropVal(wxT("stock_client"), wxEmptyString);
-            if (scl.empty())
-                scl = defaultArtClient;
-            else
-                scl = wxART_MAKE_CLIENT_ID_FROM_STR(scl);
-            
-            wxBitmap stockArt =
-                wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(sid),
-                                         scl, size);
-            if ( stockArt.Ok() )
-                return stockArt;
-        }
+        wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size));
+        if ( stockArt.Ok() )
+            return stockArt;
     }
 
     /* ...or load the bitmap from file: */
-    wxString name = GetParamValue(param);
-    if (name.IsEmpty()) return wxNullBitmap;
+    wxString name = GetParamValue(node);
+    if (name.empty()) return wxNullBitmap;
 #if wxUSE_FILESYSTEM
-    wxFSFile *fsfile = GetCurFileSystem().OpenFile(name);
+    wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
     if (fsfile == NULL)
     {
-        wxLogError(_("XRC resource: Cannot create bitmap from '%s'."),
-                   name.c_str());
+        ReportParamError
+        (
+            node->GetName(),
+            wxString::Format("cannot open bitmap resource \"%s\"", name)
+        );
         return wxNullBitmap;
     }
     wxImage img(*(fsfile->GetStream()));
     delete fsfile;
 #else
-    wxImage img(GetParamValue(wxT("bitmap")));
+    wxImage img(name);
 #endif
 
     if (!img.Ok())
     {
-        wxLogError(_("XRC resource: Cannot create bitmap from '%s'."), param.c_str());
+        ReportParamError
+        (
+            node->GetName(),
+            wxString::Format("cannot create bitmap from \"%s\"", name)
+        );
         return wxNullBitmap;
     }
     if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
     return wxBitmap(img);
-
 }
 
 
-
 wxIcon wxXmlResourceHandler::GetIcon(const wxString& param,
                                      const wxArtClient& defaultArtClient,
                                      wxSize size)
+{
+    // see comment in GetBitmap(wxString) overload
+    wxASSERT_MSG( !param.empty(), "icon parameter name can't be empty" );
+
+    const wxXmlNode* const node = GetParamNode(param);
+
+    if ( !node )
+    {
+        // this is not an error as icon parameter could be optional
+        return wxIcon();
+    }
+
+    return GetIcon(node, defaultArtClient, size);
+}
+
+wxIcon wxXmlResourceHandler::GetIcon(const wxXmlNode* node,
+                                     const wxArtClient& defaultArtClient,
+                                     wxSize size)
 {
     wxIcon icon;
-    icon.CopyFromBitmap(GetBitmap(param, defaultArtClient, size));
+    icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
     return icon;
 }
 
 
+wxIconBundle wxXmlResourceHandler::GetIconBundle(const wxString& param,
+                                                 const wxArtClient& defaultArtClient)
+{
+    wxString art_id, art_client;
+    if ( GetStockArtAttrs(GetParamNode(param), defaultArtClient,
+                          art_id, art_client) )
+    {
+        wxIconBundle stockArt(wxArtProvider::GetIconBundle(art_id, art_client));
+        if ( stockArt.IsOk() )
+            return stockArt;
+    }
+
+    const wxString name = GetParamValue(param);
+    if ( name.empty() )
+        return wxNullIconBundle;
+
+#if wxUSE_FILESYSTEM
+    wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
+    if ( fsfile == NULL )
+    {
+        ReportParamError
+        (
+            param,
+            wxString::Format("cannot open icon resource \"%s\"", name)
+        );
+        return wxNullIconBundle;
+    }
+
+    wxIconBundle bundle(*(fsfile->GetStream()));
+    delete fsfile;
+#else
+    wxIconBundle bundle(name);
+#endif
+
+    if ( !bundle.IsOk() )
+    {
+        ReportParamError
+        (
+            param,
+            wxString::Format("cannot create icon from \"%s\"", name)
+        );
+        return wxNullIconBundle;
+    }
+
+    return bundle;
+}
+
+
+wxImageList *wxXmlResourceHandler::GetImageList(const wxString& param)
+{
+    wxXmlNode * const imagelist_node = GetParamNode(param);
+    if ( !imagelist_node )
+        return NULL;
+
+    wxXmlNode * const oldnode = m_node;
+    m_node = imagelist_node;
+
+    // Get the size if we have it, otherwise we will use the size of the first
+    // list element.
+    wxSize size = GetSize();
+
+    // Start adding images, we'll create the image list when adding the first
+    // one.
+    wxImageList * imagelist = NULL;
+    wxString parambitmap = wxT("bitmap");
+    if ( HasParam(parambitmap) )
+    {
+        wxXmlNode *n = m_node->GetChildren();
+        while (n)
+        {
+            if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
+            {
+                wxIcon icon = GetIcon(n);
+                if ( !imagelist )
+                {
+                    // We need the real image list size to create it.
+                    if ( size == wxDefaultSize )
+                        size = icon.GetSize();
+
+                    // We use the mask by default.
+                    bool mask = !HasParam(wxS("mask")) || GetBool(wxS("mask"));
+
+                    imagelist = new wxImageList(size.x, size.y, mask);
+                }
+
+                // add icon instead of bitmap to keep the bitmap mask
+                imagelist->Add(icon);
+            }
+            n = n->GetNext();
+        }
+    }
+
+    m_node = oldnode;
+    return imagelist;
+}
 
 wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
 {
@@ -979,16 +1991,31 @@ wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
     while (n)
     {
         if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
+        {
+            // TODO: check that there are no other properties/parameters with
+            //       the same name and log an error if there are (can't do this
+            //       right now as I'm not sure if it's not going to break code
+            //       using this function in unintentional way (i.e. for
+            //       accessing other things than properties), for example
+            //       wxBitmapComboBoxXmlHandler almost surely does
             return n;
+        }
         n = n->GetNext();
     }
     return NULL;
 }
 
+/* static */
+bool wxXmlResourceHandler::IsOfClass(wxXmlNode *node, const wxString& classname)
+{
+    return node->GetAttribute(wxT("class")) == classname;
+}
+
 
-wxString wxXmlResourceHandler::GetNodeContent(wxXmlNode *node)
+
+wxString wxXmlResourceHandler::GetNodeContent(const wxXmlNode *node)
 {
-    wxXmlNode *n = node;
+    const wxXmlNode *n = node;
     if (n == NULL) return wxEmptyString;
     n = n->GetChildren();
 
@@ -1006,42 +2033,62 @@ wxString wxXmlResourceHandler::GetNodeContent(wxXmlNode *node)
 
 wxString wxXmlResourceHandler::GetParamValue(const wxString& param)
 {
-    if (param.IsEmpty())
+    if (param.empty())
         return GetNodeContent(m_node);
     else
         return GetNodeContent(GetParamNode(param));
 }
 
+wxString wxXmlResourceHandler::GetParamValue(const wxXmlNode* node)
+{
+    return GetNodeContent(node);
+}
 
 
-wxSize wxXmlResourceHandler::GetSize(const wxString& param)
+wxSize wxXmlResourceHandler::GetSize(const wxString& param,
+                                     wxWindow *windowToUse)
 {
     wxString s = GetParamValue(param);
-    if (s.IsEmpty()) s = wxT("-1,-1");
+    if (s.empty()) s = wxT("-1,-1");
     bool is_dlg;
     long sx, sy = 0;
 
-    is_dlg = s[s.Length()-1] == wxT('d');
+    is_dlg = s[s.length()-1] == wxT('d');
     if (is_dlg) s.RemoveLast();
 
     if (!s.BeforeFirst(wxT(',')).ToLong(&sx) ||
         !s.AfterLast(wxT(',')).ToLong(&sy))
     {
-        wxLogError(_("Cannot parse coordinates from '%s'."), s.c_str());
+        ReportParamError
+        (
+            param,
+            wxString::Format("cannot parse coordinates value \"%s\"", s)
+        );
         return wxDefaultSize;
     }
 
     if (is_dlg)
     {
-        if (m_parentAsWindow)
+        if (windowToUse)
+        {
+            return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
+        }
+        else if (m_parentAsWindow)
+        {
             return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, sy));
+        }
         else
         {
-            wxLogError(_("Cannot convert dialog units: dialog unknown."));
+            ReportParamError
+            (
+                param,
+                "cannot convert dialog units: dialog unknown"
+            );
             return wxDefaultSize;
         }
     }
-    else return wxSize(sx, sy);
+
+    return wxSize(sx, sy);
 }
 
 
@@ -1054,95 +2101,208 @@ wxPoint wxXmlResourceHandler::GetPosition(const wxString& param)
 
 
 
-wxCoord wxXmlResourceHandler::GetDimension(const wxString& param, wxCoord defaultv)
+wxCoord wxXmlResourceHandler::GetDimension(const wxString& param,
+                                           wxCoord defaultv,
+                                           wxWindow *windowToUse)
 {
     wxString s = GetParamValue(param);
-    if (s.IsEmpty()) return defaultv;
+    if (s.empty()) return defaultv;
     bool is_dlg;
     long sx;
 
-    is_dlg = s[s.Length()-1] == wxT('d');
+    is_dlg = s[s.length()-1] == wxT('d');
     if (is_dlg) s.RemoveLast();
 
     if (!s.ToLong(&sx))
     {
-        wxLogError(_("Cannot parse dimension from '%s'."), s.c_str());
+        ReportParamError
+        (
+            param,
+            wxString::Format("cannot parse dimension value \"%s\"", s)
+        );
         return defaultv;
     }
 
     if (is_dlg)
     {
-        if (m_parentAsWindow)
+        if (windowToUse)
+        {
+            return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
+        }
+        else if (m_parentAsWindow)
+        {
             return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, 0)).x;
+        }
         else
         {
-            wxLogError(_("Cannot convert dialog units: dialog unknown."));
+            ReportParamError
+            (
+                param,
+                "cannot convert dialog units: dialog unknown"
+            );
             return defaultv;
         }
     }
-    else return sx;
+
+    return sx;
 }
 
 
+// Get system font index using indexname
+static wxFont GetSystemFont(const wxString& name)
+{
+    if (!name.empty())
+    {
+        #define SYSFNT(fnt) \
+            if (name == wxT(#fnt)) return wxSystemSettings::GetFont(fnt);
+        SYSFNT(wxSYS_OEM_FIXED_FONT)
+        SYSFNT(wxSYS_ANSI_FIXED_FONT)
+        SYSFNT(wxSYS_ANSI_VAR_FONT)
+        SYSFNT(wxSYS_SYSTEM_FONT)
+        SYSFNT(wxSYS_DEVICE_DEFAULT_FONT)
+        SYSFNT(wxSYS_SYSTEM_FIXED_FONT)
+        SYSFNT(wxSYS_DEFAULT_GUI_FONT)
+        #undef SYSFNT
+    }
+
+    return wxNullFont;
+}
 
 wxFont wxXmlResourceHandler::GetFont(const wxString& param)
 {
     wxXmlNode *font_node = GetParamNode(param);
     if (font_node == NULL)
     {
-        wxLogError(_("Cannot find font node '%s'."), param.c_str());
+        ReportError(
+            wxString::Format("cannot find font node \"%s\"", param));
         return wxNullFont;
     }
 
     wxXmlNode *oldnode = m_node;
     m_node = font_node;
 
-    long size = GetLong(wxT("size"), 12);
+    // font attributes:
 
-    wxString style = GetParamValue(wxT("style"));
-    wxString weight = GetParamValue(wxT("weight"));
-    int istyle = wxNORMAL, iweight = wxNORMAL;
-    if (style == wxT("italic")) istyle = wxITALIC;
-    else if (style == wxT("slant")) istyle = wxSLANT;
-    if (weight == wxT("bold")) iweight = wxBOLD;
-    else if (weight == wxT("light")) iweight = wxLIGHT;
+    // size
+    int isize = -1;
+    bool hasSize = HasParam(wxT("size"));
+    if (hasSize)
+        isize = GetLong(wxT("size"), -1);
 
-    wxString family = GetParamValue(wxT("family"));
+    // style
+    int istyle = wxNORMAL;
+    bool hasStyle = HasParam(wxT("style"));
+    if (hasStyle)
+    {
+        wxString style = GetParamValue(wxT("style"));
+        if (style == wxT("italic"))
+            istyle = wxITALIC;
+        else if (style == wxT("slant"))
+            istyle = wxSLANT;
+    }
+
+    // weight
+    int iweight = wxNORMAL;
+    bool hasWeight = HasParam(wxT("weight"));
+    if (hasWeight)
+    {
+        wxString weight = GetParamValue(wxT("weight"));
+        if (weight == wxT("bold"))
+            iweight = wxBOLD;
+        else if (weight == wxT("light"))
+            iweight = wxLIGHT;
+    }
+
+    // underline
+    bool hasUnderlined = HasParam(wxT("underlined"));
+    bool underlined = hasUnderlined ? GetBool(wxT("underlined"), false) : false;
+
+    // family and facename
     int ifamily = wxDEFAULT;
-         if (family == wxT("decorative")) ifamily = wxDECORATIVE;
-    else if (family == wxT("roman")) ifamily = wxROMAN;
-    else if (family == wxT("script")) ifamily = wxSCRIPT;
-    else if (family == wxT("swiss")) ifamily = wxSWISS;
-    else if (family == wxT("modern")) ifamily = wxMODERN;
+    bool hasFamily = HasParam(wxT("family"));
+    if (hasFamily)
+    {
+        wxString family = GetParamValue(wxT("family"));
+             if (family == wxT("decorative")) ifamily = wxDECORATIVE;
+        else if (family == wxT("roman")) ifamily = wxROMAN;
+        else if (family == wxT("script")) ifamily = wxSCRIPT;
+        else if (family == wxT("swiss")) ifamily = wxSWISS;
+        else if (family == wxT("modern")) ifamily = wxMODERN;
+        else if (family == wxT("teletype")) ifamily = wxTELETYPE;
+    }
 
-    bool underlined = GetBool(wxT("underlined"), false);
 
-    wxString encoding = GetParamValue(wxT("encoding"));
-    wxFontMapper mapper;
-    wxFontEncoding enc = wxFONTENCODING_DEFAULT;
-    if (!encoding.IsEmpty())
-        enc = mapper.CharsetToEncoding(encoding);
-    if (enc == wxFONTENCODING_SYSTEM)
-        enc = wxFONTENCODING_DEFAULT;
-
-    wxString faces = GetParamValue(wxT("face"));
-    wxString facename = wxEmptyString;
-    wxFontEnumerator enu;
-    enu.EnumerateFacenames();
-    wxStringTokenizer tk(faces, wxT(","));
-    while (tk.HasMoreTokens())
-    {
-        int index = enu.GetFacenames()->Index(tk.GetNextToken(), false);
-        if (index != wxNOT_FOUND)
+    wxString facename;
+    bool hasFacename = HasParam(wxT("face"));
+    if (hasFacename)
+    {
+        wxString faces = GetParamValue(wxT("face"));
+        wxStringTokenizer tk(faces, wxT(","));
+#if wxUSE_FONTENUM
+        wxArrayString facenames(wxFontEnumerator::GetFacenames());
+        while (tk.HasMoreTokens())
         {
-            facename = (*enu.GetFacenames())[index];
-            break;
+            int index = facenames.Index(tk.GetNextToken(), false);
+            if (index != wxNOT_FOUND)
+            {
+                facename = facenames[index];
+                break;
+            }
         }
+#else // !wxUSE_FONTENUM
+        // just use the first face name if we can't check its availability:
+        if (tk.HasMoreTokens())
+            facename = tk.GetNextToken();
+#endif // wxUSE_FONTENUM/!wxUSE_FONTENUM
     }
 
-    m_node = oldnode;
+    // encoding
+    wxFontEncoding enc = wxFONTENCODING_DEFAULT;
+    bool hasEncoding = HasParam(wxT("encoding"));
+#if wxUSE_FONTMAP
+    if (hasEncoding)
+    {
+        wxString encoding = GetParamValue(wxT("encoding"));
+        wxFontMapper mapper;
+        if (!encoding.empty())
+            enc = mapper.CharsetToEncoding(encoding);
+        if (enc == wxFONTENCODING_SYSTEM)
+            enc = wxFONTENCODING_DEFAULT;
+    }
+#endif // wxUSE_FONTMAP
+
+    // is this font based on a system font?
+    wxFont font = GetSystemFont(GetParamValue(wxT("sysfont")));
+
+    if (font.Ok())
+    {
+        if (hasSize && isize != -1)
+            font.SetPointSize(isize);
+        else if (HasParam(wxT("relativesize")))
+            font.SetPointSize(int(font.GetPointSize() *
+                                     GetFloat(wxT("relativesize"))));
+
+        if (hasStyle)
+            font.SetStyle(istyle);
+        if (hasWeight)
+            font.SetWeight(iweight);
+        if (hasUnderlined)
+            font.SetUnderlined(underlined);
+        if (hasFamily)
+            font.SetFamily(ifamily);
+        if (hasFacename)
+            font.SetFaceName(facename);
+        if (hasEncoding)
+            font.SetDefaultEncoding(enc);
+    }
+    else // not based on system font
+    {
+        font = wxFont(isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize,
+                      ifamily, istyle, iweight,
+                      underlined, facename, enc);
+    }
 
-    wxFont font(size, ifamily, istyle, iweight, underlined, facename, enc);
+    m_node = oldnode;
     return font;
 }
 
@@ -1158,8 +2318,12 @@ void wxXmlResourceHandler::SetupWindow(wxWindow *wnd)
         wnd->SetExtraStyle(wnd->GetExtraStyle() | GetStyle(wxT("exstyle")));
     if (HasParam(wxT("bg")))
         wnd->SetBackgroundColour(GetColour(wxT("bg")));
+    if (HasParam(wxT("ownbg")))
+        wnd->SetOwnBackgroundColour(GetColour(wxT("ownbg")));
     if (HasParam(wxT("fg")))
         wnd->SetForegroundColour(GetColour(wxT("fg")));
+    if (HasParam(wxT("ownfg")))
+        wnd->SetOwnForegroundColour(GetColour(wxT("ownfg")));
     if (GetBool(wxT("enabled"), 1) == 0)
         wnd->Enable(false);
     if (GetBool(wxT("focused"), 0) == 1)
@@ -1171,23 +2335,23 @@ void wxXmlResourceHandler::SetupWindow(wxWindow *wnd)
         wnd->SetToolTip(GetText(wxT("tooltip")));
 #endif
     if (HasParam(wxT("font")))
-        wnd->SetFont(GetFont());
+        wnd->SetFont(GetFont(wxT("font")));
+    if (HasParam(wxT("ownfont")))
+        wnd->SetOwnFont(GetFont(wxT("ownfont")));
+    if (HasParam(wxT("help")))
+        wnd->SetHelpText(GetText(wxT("help")));
 }
 
 
 void wxXmlResourceHandler::CreateChildren(wxObject *parent, bool this_hnd_only)
 {
-    wxXmlNode *n = m_node->GetChildren();
-
-    while (n)
+    for ( wxXmlNode *n = m_node->GetChildren(); n; n = n->GetNext() )
     {
-        if (n->GetType() == wxXML_ELEMENT_NODE &&
-           (n->GetName() == wxT("object") || n->GetName() == wxT("object_ref")))
+        if ( IsObjectNode(n) )
         {
-            m_resource->CreateResFromNode(n, parent, NULL,
-                                          this_hnd_only ? this : NULL);
+            m_resource->DoCreateResFromNode(*n, parent, NULL,
+                                            this_hnd_only ? this : NULL);
         }
-        n = n->GetNext();
     }
 }
 
@@ -1209,32 +2373,96 @@ void wxXmlResourceHandler::CreateChildrenPrivately(wxObject *parent, wxXmlNode *
 }
 
 
+//-----------------------------------------------------------------------------
+// errors reporting
+//-----------------------------------------------------------------------------
+
+void wxXmlResourceHandler::ReportError(const wxString& message)
+{
+    m_resource->ReportError(m_node, message);
+}
+
+void wxXmlResourceHandler::ReportError(wxXmlNode *context,
+                                       const wxString& message)
+{
+    m_resource->ReportError(context ? context : m_node, message);
+}
+
+void wxXmlResourceHandler::ReportParamError(const wxString& param,
+                                            const wxString& message)
+{
+    m_resource->ReportError(GetParamNode(param), message);
+}
+
+void wxXmlResource::ReportError(const wxXmlNode *context, const wxString& message)
+{
+    if ( !context )
+    {
+        DoReportError("", NULL, message);
+        return;
+    }
+
+    // We need to find out the file that 'context' is part of. Performance of
+    // this code is not critical, so we simply find the root XML node and
+    // compare it with all loaded XRC files.
+    const wxString filename = GetFileNameFromNode(context, Data());
+
+    DoReportError(filename, context, message);
+}
 
+void wxXmlResource::DoReportError(const wxString& xrcFile, const wxXmlNode *position,
+                                  const wxString& message)
+{
+    const int line = position ? position->GetLineNumber() : -1;
 
+    wxString loc;
+    if ( !xrcFile.empty() )
+        loc = xrcFile + ':';
+    if ( line != -1 )
+        loc += wxString::Format("%d:", line);
+    if ( !loc.empty() )
+        loc += ' ';
 
+    wxLogError("XRC error: %s%s", loc, message);
+}
 
 
-// --------------- XRCID implementation -----------------------------
+//-----------------------------------------------------------------------------
+// XRCID implementation
+//-----------------------------------------------------------------------------
 
 #define XRCID_TABLE_SIZE     1024
 
 
 struct XRCID_record
 {
-    int id;
-    wxChar *key;
+    /* Hold the id so that once an id is allocated for a name, it
+       does not get created again by NewControlId at least
+       until we are done with it */
+    wxWindowIDRef id;
+    char *key;
     XRCID_record *next;
 };
 
 static XRCID_record *XRCID_Records[XRCID_TABLE_SIZE] = {NULL};
 
-static int XRCID_Lookup(const wxChar *str_id, int value_if_not_found = -2)
+// Extremely simplistic hash function which probably ought to be replaced with
+// wxStringHash::stringHash().
+static inline unsigned XRCIdHash(const char *str_id)
 {
-    int index = 0;
+    unsigned index = 0;
 
-    for (const wxChar *c = str_id; *c != wxT('\0'); c++) index += (int)*c;
+    for (const char *c = str_id; *c != '\0'; c++) index += (unsigned int)*c;
     index %= XRCID_TABLE_SIZE;
 
+    return index;
+}
+
+static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE)
+{
+    const unsigned index = XRCIdHash(str_id);
+
+
     XRCID_record *oldrec = NULL;
     for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
     {
@@ -1251,8 +2479,8 @@ static int XRCID_Lookup(const wxChar *str_id, int value_if_not_found = -2)
     (*rec_var)->key = wxStrdup(str_id);
     (*rec_var)->next = NULL;
 
-    wxChar *end;
-    if (value_if_not_found != -2)
+    char *end;
+    if (value_if_not_found != wxID_NONE)
         (*rec_var)->id = value_if_not_found;
     else
     {
@@ -1264,46 +2492,27 @@ static int XRCID_Lookup(const wxChar *str_id, int value_if_not_found = -2)
         }
         else
         {
-            (*rec_var)->id = wxNewId();
+            (*rec_var)->id = wxWindowBase::NewControlId();
         }
     }
 
     return (*rec_var)->id;
 }
 
-/*static*/ int wxXmlResource::GetXRCID(const wxChar *str_id)
-{
-    return XRCID_Lookup(str_id);
-}
-
-
-static void CleanXRCID_Record(XRCID_record *rec)
+namespace
 {
-    if (rec)
-    {
-        CleanXRCID_Record(rec->next);
-        free(rec->key);
-        delete rec;
-    }
-}
 
-static void CleanXRCID_Records()
-{
-    for (int i = 0; i < XRCID_TABLE_SIZE; i++)
-    {
-        CleanXRCID_Record(XRCID_Records[i]);
-        XRCID_Records[i] = NULL;
-    }
-}
+// flag indicating whether standard XRC ids were already initialized
+static bool gs_stdIDsAdded = false;
 
-static void AddStdXRCID_Records()
+void AddStdXRCID_Records()
 {
-#define stdID(id) XRCID_Lookup(wxT(#id), id)
+#define stdID(id) XRCID_Lookup(#id, id)
     stdID(-1);
 
     stdID(wxID_ANY);
     stdID(wxID_SEPARATOR);
-    
+
     stdID(wxID_OPEN);
     stdID(wxID_CLOSE);
     stdID(wxID_NEW);
@@ -1316,6 +2525,7 @@ static void AddStdXRCID_Records()
     stdID(wxID_HELP);
     stdID(wxID_PRINT);
     stdID(wxID_PRINT_SETUP);
+    stdID(wxID_PAGE_SETUP);
     stdID(wxID_PREVIEW);
     stdID(wxID_ABOUT);
     stdID(wxID_HELP_CONTENTS);
@@ -1324,6 +2534,7 @@ static void AddStdXRCID_Records()
     stdID(wxID_HELP_CONTEXT);
     stdID(wxID_CLOSE_ALL);
     stdID(wxID_PREFERENCES);
+    stdID(wxID_EDIT);
     stdID(wxID_CUT);
     stdID(wxID_COPY);
     stdID(wxID_PASTE);
@@ -1400,15 +2611,119 @@ static void AddStdXRCID_Records()
     stdID(wxID_MAXIMIZE_FRAME);
     stdID(wxID_ICONIZE_FRAME);
     stdID(wxID_RESTORE_FRAME);
+    stdID(wxID_CDROM);
+    stdID(wxID_CONVERT);
+    stdID(wxID_EXECUTE);
+    stdID(wxID_FLOPPY);
+    stdID(wxID_HARDDISK);
+    stdID(wxID_BOTTOM);
+    stdID(wxID_FIRST);
+    stdID(wxID_LAST);
+    stdID(wxID_TOP);
+    stdID(wxID_INFO);
+    stdID(wxID_JUMP_TO);
+    stdID(wxID_NETWORK);
+    stdID(wxID_SELECT_COLOR);
+    stdID(wxID_SELECT_FONT);
+    stdID(wxID_SORT_ASCENDING);
+    stdID(wxID_SORT_DESCENDING);
+    stdID(wxID_SPELL_CHECK);
+    stdID(wxID_STRIKETHROUGH);
 
 #undef stdID
 }
 
+} // anonymous namespace
+
+
+/*static*/
+int wxXmlResource::DoGetXRCID(const char *str_id, int value_if_not_found)
+{
+    if ( !gs_stdIDsAdded )
+    {
+        gs_stdIDsAdded = true;
+        AddStdXRCID_Records();
+    }
+
+    return XRCID_Lookup(str_id, value_if_not_found);
+}
+
+/* static */
+wxString wxXmlResource::FindXRCIDById(int numId)
+{
+    for ( int i = 0; i < XRCID_TABLE_SIZE; i++ )
+    {
+        for ( XRCID_record *rec = XRCID_Records[i]; rec; rec = rec->next )
+        {
+            if ( rec->id == numId )
+                return wxString(rec->key);
+        }
+    }
+
+    return wxString();
+}
+
+/* static */
+void wxIdRangeManager::RemoveXRCIDEntry(const wxString& idstr)
+{
+    const char *str_id = idstr.mb_str();
+
+    const unsigned index = XRCIdHash(str_id);
+
+    XRCID_record **p_previousrec = &XRCID_Records[index];
+    for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
+    {
+        if (wxStrcmp(rec->key, str_id) == 0)
+        {
+            // Found the item to be removed so delete its record; but first
+            // remove it from the linked list.
+            *p_previousrec = rec->next;
+            free(rec->key);
+            delete rec;
+            return;
+        }
+
+        p_previousrec = &rec->next;
+    }
+}
+
+static void CleanXRCID_Record(XRCID_record *rec)
+{
+    if (rec)
+    {
+        CleanXRCID_Record(rec->next);
+
+        free(rec->key);
+        delete rec;
+    }
+}
+
+static void CleanXRCID_Records()
+{
+    for (int i = 0; i < XRCID_TABLE_SIZE; i++)
+    {
+        CleanXRCID_Record(XRCID_Records[i]);
+        XRCID_Records[i] = NULL;
+    }
 
+    gs_stdIDsAdded = false;
+}
 
 
+//-----------------------------------------------------------------------------
+// module and globals
+//-----------------------------------------------------------------------------
 
-// --------------- module and globals -----------------------------
+// normally we would do the cleanup from wxXmlResourceModule::OnExit() but it
+// can happen that some XRC records have been created because of the use of
+// XRCID() in event tables, which happens during static objects initialization,
+// but then the application initialization failed and so the wx modules were
+// neither initialized nor cleaned up -- this static object does the cleanup in
+// this case
+static struct wxXRCStaticCleanup
+{
+    ~wxXRCStaticCleanup() { CleanXRCID_Records(); }
+} s_staticCleanup;
 
 class wxXmlResourceModule: public wxModule
 {
@@ -1417,16 +2732,22 @@ public:
     wxXmlResourceModule() {}
     bool OnInit()
     {
-        AddStdXRCID_Records();
         wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX);
         return true;
     }
     void OnExit()
     {
         delete wxXmlResource::Set(NULL);
+        delete wxIdRangeManager::Set(NULL);
         if(wxXmlResource::ms_subclassFactories)
-            WX_CLEAR_LIST(wxXmlSubclassFactoriesList, *wxXmlResource::ms_subclassFactories);
-        wxDELETE(wxXmlResource::ms_subclassFactories);
+        {
+            for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
+                  i != wxXmlResource::ms_subclassFactories->end(); ++i )
+            {
+                delete *i;
+            }
+            wxDELETE(wxXmlResource::ms_subclassFactories);
+        }
         CleanXRCID_Records();
     }
 };