X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b0802e642190c07d819d1c01bd7c315453d091d0..81533a3af6ed598c32a35e1c1c2b60f4908f5541:/src/xrc/xmlres.cpp diff --git a/src/xrc/xmlres.cpp b/src/xrc/xmlres.cpp index 1434040d17..88fa86ab4a 100644 --- a/src/xrc/xmlres.cpp +++ b/src/xrc/xmlres.cpp @@ -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 ///////////////////////////////////////////////////////////////////////////// -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) -#pragma implementation "xmlres.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -21,32 +17,117 @@ #if wxUSE_XRC +#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 + #include #endif -#include "wx/dialog.h" -#include "wx/panel.h" -#include "wx/frame.h" +#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/settings.h" - +#include "wx/imaglist.h" +#include "wx/dir.h" #include "wx/xml/xml.h" -#include "wx/xrc/xmlres.h" -#include "wx/arrimpl.cpp" -WX_DEFINE_OBJARRAY(wxXmlResourceDataRecords); + +class wxXmlResourceDataRecord +{ +public: + wxXmlResourceDataRecord() : Doc(NULL) { +#if wxUSE_DATETIME + Time = wxDateTime::Now(); +#endif + } + ~wxXmlResourceDataRecord() {delete Doc;} + + wxString File; + wxXmlDocument *Doc; +#if wxUSE_DATETIME + wxDateTime Time; +#endif +}; + +class wxXmlResourceDataRecords : public wxVector +{ + // this is a class so that it can be forward-declared +}; + +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(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 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 and the referenced ); 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; + } + } + + return wxEmptyString; // not found +} + +} // anonymous namespace wxXmlResource *wxXmlResource::ms_instance = NULL; @@ -65,31 +146,109 @@ 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_); #if wxUSE_FILESYSTEM wxFileSystem fsys; @@ -99,54 +258,76 @@ bool wxXmlResource::Load(const wxString& filemask) # define wxXmlFindFirst wxFindFirstFile(filemask, wxFILE) # define wxXmlFindNext wxFindNextFile() #endif - if (iswild) - fnd = wxXmlFindFirst; - else - fnd = filemask; - while (!fnd.empty()) + wxString fnd = wxXmlFindFirst; + if ( fnd.empty() ) { - // 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 - } + 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")) ) + return false; } - else -#endif + else // a single resource URL +#endif // wxUSE_FILESYSTEM { - drec = new wxXmlResourceDataRecord; + wxXmlResourceDataRecord *drec = new wxXmlResourceDataRecord; drec->File = fnd; - m_data.Add(drec); + Data().push_back(drec); } - if (iswild) - fnd = wxXmlFindNext; - else - fnd = wxEmptyString; + fnd = wxXmlFindNext; } # undef wxXmlFindFirst # undef wxXmlFindNext - return rt && UpdateResources(); + + return UpdateResources(); +} + +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; } @@ -154,13 +335,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); } @@ -168,7 +349,10 @@ void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler) void wxXmlResource::ClearHandlers() { - WX_CLEAR_LIST(wxList, m_handlers); + for ( wxVector::iterator i = m_handlers.begin(); + i != m_handlers.end(); ++i ) + delete *i; + m_handlers.clear(); } @@ -247,14 +431,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; } @@ -266,7 +463,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); @@ -282,7 +479,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 { @@ -346,65 +543,79 @@ bool wxXmlResource::UpdateResources() } #endif - for (size_t i = 0; i < m_data.GetCount(); i++) + for ( wxXmlResourceDataRecords::iterator i = Data().begin(); + i != Data().end(); ++i ) { - modif = (m_data[i].Doc == NULL); + wxXmlResourceDataRecord* const rec = *i; + + modif = (rec->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; + file = fsys.OpenFile(rec->File); +# if wxUSE_DATETIME + modif = file && file->GetModificationTime() > rec->Time; +# else // wxUSE_DATETIME + modif = true; +# endif // wxUSE_DATETIME if (!file) { - wxLogError(_("Cannot open file '%s'."), m_data[i].File.c_str()); + wxLogError(_("Cannot open file '%s'."), rec->File); rt = false; } wxDELETE(file); wxUnusedVar(file); -# else - modif = wxDateTime(wxFileModificationTime(m_data[i].File)) > m_data[i].Time; -# endif +# else // wxUSE_FILESYSTEM +# if wxUSE_DATETIME + modif = wxDateTime(wxFileModificationTime(rec->File)) > rec->Time; +# else // wxUSE_DATETIME + modif = true; +# endif // wxUSE_DATETIME +# endif // wxUSE_FILESYSTEM } if (modif) { - wxLogTrace(_T("xrc"), - _T("opening file '%s'"), m_data[i].File.c_str()); + wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), rec->File); wxInputStream *stream = NULL; # if wxUSE_FILESYSTEM - file = fsys.OpenFile(m_data[i].File); + file = fsys.OpenFile(rec->File); if (file) stream = file->GetStream(); # else - stream = new wxFileInputStream(m_data[i].File); + stream = new wxFileInputStream(rec->File); # endif if (stream) { - delete m_data[i].Doc; - m_data[i].Doc = new wxXmlDocument; + delete rec->Doc; + rec->Doc = new wxXmlDocument; } - if (!stream || !m_data[i].Doc->Load(*stream, encoding)) + if (!stream || !stream->IsOk() || !rec->Doc->Load(*stream, encoding)) { wxLogError(_("Cannot load resources from file '%s'."), - m_data[i].File.c_str()); - wxDELETE(m_data[i].Doc); + rec->File); + wxDELETE(rec->Doc); rt = false; } - else if (m_data[i].Doc->GetRoot()->GetName() != wxT("resource")) + else if (rec->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); + ReportError + ( + rec->Doc->GetRoot(), + "invalid XRC resource, doesn't have root node " + ); + wxDELETE(rec->Doc); rt = false; } else { long version; int v1, v2, v3, v4; - wxString verstr = m_data[i].Doc->GetRoot()->GetPropVal( + wxString verstr = rec->Doc->GetRoot()->GetAttribute( wxT("version"), wxT("0.0.0.0")); if (wxSscanf(verstr.c_str(), wxT("%i.%i.%i.%i"), &v1, &v2, &v3, &v4) == 4) @@ -415,16 +626,18 @@ bool wxXmlResource::UpdateResources() m_version = version; if (m_version != version) { - wxLogError(_("Resource files must have same version number!")); + wxLogError("Resource files must have same version number."); rt = false; } - ProcessPlatformProperty(m_data[i].Doc->GetRoot()); + ProcessPlatformProperty(rec->Doc->GetRoot()); +#if wxUSE_DATETIME #if wxUSE_FILESYSTEM - m_data[i].Time = file->GetModificationTime(); -#else - m_data[i].Time = wxDateTime(wxFileModificationTime(m_data[i].File)); -#endif + rec->Time = file->GetModificationTime(); +#else // wxUSE_FILESYSTEM + rec->Time = wxDateTime(wxFileModificationTime(rec->File)); +#endif // wxUSE_FILESYSTEM +#endif // wxUSE_DATETIME } # if wxUSE_FILESYSTEM @@ -439,200 +652,285 @@ bool wxXmlResource::UpdateResources() return rt; } - 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); + + if ( !node ) + { + ReportError + ( + NULL, + wxString::Format + ( + "XRC resource \"%s\" (class \"%s\") not found", + name, classname + ) + ); + } +#if wxUSE_FILESYSTEM + else // node was found + { + // 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-remand + // reloading of XRC files + const_cast(this)->UpdateResources(); - wxString dummy; - for (size_t f = 0; f < m_data.GetCount(); f++) + for ( wxXmlResourceDataRecords::const_iterator f = Data().begin(); + f != Data().end(); ++f ) { - if ( m_data[f].Doc == NULL || m_data[f].Doc->GetRoot() == NULL ) + 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, 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(©, parent, instance); - } + return DoCreateResFromNode(*refNode, parent, instance); + } + else + { + // In the more complicated (but rare) case, 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. - wxXmlResourceHandler *handler; + wxXmlNode copy(*refNode); + MergeNodesOver(copy, node, GetFileNameFromNode(&node, Data())); + + // 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); + return handlerToUse->CreateResource(&node, parent, instance); } } - else if (node->GetName() == wxT("object")) + else if (node.GetName() == wxT("object")) { - wxList::compatibility_iterator ND = m_handlers.GetFirst(); - while (ND) + for ( wxVector::iterator h = m_handlers.begin(); + h != m_handlers.end(); ++h ) { - handler = (wxXmlResourceHandler*)ND->GetData(); - if (handler->CanHandle(node)) - { - return handler->CreateResource(node, parent, instance); - } - ND = ND->GetNext(); + wxXmlResourceHandler *handler = *h; + if (handler->CanHandle(&node)) + return handler->CreateResource(&node, parent, instance); } } - wxLogError(_("No handler found for XML node '%s', class '%s'!"), - node->GetName().c_str(), - node->GetPropVal(wxT("class"), wxEmptyString).c_str()); + ReportError + ( + &node, + wxString::Format + ( + "no handler found for XML node \"%s\" (class \"%s\")", + node.GetName(), + node.GetAttribute("class", wxEmptyString) + ) + ); return NULL; } -#include "wx/listimpl.cpp" -WX_DECLARE_LIST(wxXmlSubclassFactory, wxXmlSubclassFactoriesList); -WX_DEFINE_LIST(wxXmlSubclassFactoriesList); +class wxXmlSubclassFactories : public wxVector +{ + // this is a class so that it can be forward-declared +}; -wxXmlSubclassFactoriesList *wxXmlResource::ms_subclassFactories = NULL; +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 @@ -669,31 +967,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); @@ -719,16 +1024,22 @@ 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); } @@ -756,9 +1067,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; } @@ -770,22 +1089,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; @@ -793,8 +1111,9 @@ 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'); @@ -810,7 +1129,7 @@ wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate) 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; @@ -821,15 +1140,19 @@ wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate) 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 { @@ -838,7 +1161,7 @@ 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 } } @@ -864,21 +1187,23 @@ long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv) float wxXmlResourceHandler::GetFloat(const wxString& param, float defaultv) { - double value; - wxString str1 = GetParamValue(param); + wxString str = GetParamValue(param); -#ifndef __WXWINCE__ - const char *prevlocale = setlocale(LC_NUMERIC, "C"); -#endif +#if wxUSE_INTL + // strings in XRC always use C locale but wxString::ToDouble() uses the + // current one, so transform the string to it supposing that the only + // difference between them is the decimal separator + // + // TODO: use wxString::ToCDouble() when we have it + str.Replace(wxT("."), wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, + wxLOCALE_CAT_NUMBER)); +#endif // wxUSE_INTL - if (!str1.ToDouble(&value)) + double value; + if (!str.ToDouble(&value)) value = defaultv; -#ifndef __WXWINCE__ - setlocale(LC_NUMERIC, prevlocale); -#endif - - return value; + return wx_truncate_cast(float, value); } @@ -891,18 +1216,22 @@ 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; + const wxString v = GetParamValue(param); - return (v == wxT("1")); + return v.empty() ? defaultv : (v == '1'); } @@ -911,7 +1240,7 @@ static wxColour GetSystemColour(const wxString& name) if (!name.empty()) { #define SYSCLR(clr) \ - if (name == _T(#clr)) return wxSystemSettings::GetColour(clr); + if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr); SYSCLR(wxSYS_COLOUR_SCROLLBAR) SYSCLR(wxSYS_COLOUR_BACKGROUND) SYSCLR(wxSYS_COLOUR_DESKTOP) @@ -955,99 +1284,252 @@ static wxColour GetSystemColour(const wxString& name) return wxNullColour; } -wxColour wxXmlResourceHandler::GetColour(const wxString& param) +wxColour wxXmlResourceHandler::GetColour(const wxString& param, const wxColour& defaultv) { wxString v = GetParamValue(param); - // find colour using HTML syntax (#RRGGBB) - unsigned long tmp = 0; + if ( v.empty() ) + return defaultv; + + wxColour clr; - if (v.Length() != 7 || v[0u] != wxT('#') || - wxSscanf(v.c_str(), wxT("#%lX"), &tmp) != 1) + // wxString -> wxColour conversion + if (!clr.Set(v)) { // the colour doesn't use #RRGGBB format, check if it is symbolic // colour name: - wxColour clr = GetSystemColour(v); + clr = GetSystemColour(v); if (clr.Ok()) return clr; - wxLogError(_("XRC resource: Incorrect colour specification '%s' for property '%s'."), - v.c_str(), param.c_str()); + 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) { - /* If the bitmap is specified as stock item, query wxArtProvider for it: */ - wxXmlNode *bmpNode = GetParamNode(param); - if ( bmpNode ) + // 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 ) { - 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); + // this is not an error as bitmap parameter could be optional + return wxNullBitmap; + } - wxBitmap stockArt = - wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(sid), - scl, size); - if ( stockArt.Ok() ) - return stockArt; - } + 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: */ + wxString art_id, art_client; + if ( GetStockArtAttrs(node, defaultArtClient, + art_id, art_client) ) + { + wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size)); + if ( stockArt.Ok() ) + return stockArt; } /* ...or load the bitmap from file: */ - wxString name = GetParamValue(param); + 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; + + // size + wxSize size = GetSize(); + size.SetDefaults(wxSize(wxSystemSettings::GetMetric(wxSYS_ICON_X), + wxSystemSettings::GetMetric(wxSYS_ICON_Y))); + + // mask: true by default + bool mask = HasParam(wxT("mask")) ? GetBool(wxT("mask"), true) : true; + + // now we have everything we need to create the image list + wxImageList *imagelist = new wxImageList(size.x, size.y, mask); + + // add images + wxString parambitmap = wxT("bitmap"); + if ( HasParam(parambitmap) ) + { + wxXmlNode *n = m_node->GetChildren(); + while (n) + { + if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap) + { + // add icon instead of bitmap to keep the bitmap mask + imagelist->Add(GetIcon(n)); + } + n = n->GetNext(); + } + } + + m_node = oldnode; + return imagelist; +} wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param) { @@ -1058,16 +1540,30 @@ 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; } +bool wxXmlResourceHandler::IsOfClass(wxXmlNode *node, const wxString& classname) +{ + return node->GetAttribute(wxT("class"), wxEmptyString) == 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(); @@ -1091,6 +1587,10 @@ wxString wxXmlResourceHandler::GetParamValue(const wxString& param) return GetNodeContent(GetParamNode(param)); } +wxString wxXmlResourceHandler::GetParamValue(const wxXmlNode* node) +{ + return GetNodeContent(node); +} wxSize wxXmlResourceHandler::GetSize(const wxString& param, @@ -1101,13 +1601,17 @@ wxSize wxXmlResourceHandler::GetSize(const wxString& param, 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; } @@ -1123,7 +1627,11 @@ wxSize wxXmlResourceHandler::GetSize(const wxString& param, } else { - wxLogError(_("Cannot convert dialog units: dialog unknown.")); + ReportParamError + ( + param, + "cannot convert dialog units: dialog unknown" + ); return wxDefaultSize; } } @@ -1150,12 +1658,16 @@ wxCoord wxXmlResourceHandler::GetDimension(const wxString& param, 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; } @@ -1171,7 +1683,11 @@ wxCoord wxXmlResourceHandler::GetDimension(const wxString& param, } else { - wxLogError(_("Cannot convert dialog units: dialog unknown.")); + ReportParamError + ( + param, + "cannot convert dialog units: dialog unknown" + ); return defaultv; } } @@ -1186,13 +1702,12 @@ static wxFont GetSystemFont(const wxString& name) if (!name.empty()) { #define SYSFNT(fnt) \ - if (name == _T(#fnt)) return wxSystemSettings::GetFont(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_DEFAULT_PALETTE) SYSFNT(wxSYS_SYSTEM_FIXED_FONT) SYSFNT(wxSYS_DEFAULT_GUI_FONT) #undef SYSFNT @@ -1206,7 +1721,8 @@ 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; } @@ -1216,10 +1732,10 @@ wxFont wxXmlResourceHandler::GetFont(const wxString& param) // font attributes: // size - int isize = wxDEFAULT; + int isize = -1; bool hasSize = HasParam(wxT("size")); if (hasSize) - isize = GetLong(wxT("size"), wxDEFAULT); + isize = GetLong(wxT("size"), -1); // style int istyle = wxNORMAL; @@ -1269,23 +1785,29 @@ wxFont wxXmlResourceHandler::GetFont(const wxString& param) if (hasFacename) { wxString faces = GetParamValue(wxT("face")); - wxFontEnumerator enu; - enu.EnumerateFacenames(); wxStringTokenizer tk(faces, wxT(",")); +#if wxUSE_FONTENUM + wxArrayString facenames(wxFontEnumerator::GetFacenames()); while (tk.HasMoreTokens()) { - int index = enu.GetFacenames()->Index(tk.GetNextToken(), false); + int index = facenames.Index(tk.GetNextToken(), false); if (index != wxNOT_FOUND) { - facename = (*enu.GetFacenames())[index]; + 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 } // encoding wxFontEncoding enc = wxFONTENCODING_DEFAULT; bool hasEncoding = HasParam(wxT("encoding")); +#if wxUSE_FONTMAP if (hasEncoding) { wxString encoding = GetParamValue(wxT("encoding")); @@ -1295,38 +1817,41 @@ wxFont wxXmlResourceHandler::GetFont(const wxString& param) if (enc == wxFONTENCODING_SYSTEM) enc = wxFONTENCODING_DEFAULT; } +#endif // wxUSE_FONTMAP // is this font based on a system font? - wxFont sysfont = GetSystemFont(GetParamValue(wxT("sysfont"))); + wxFont font = GetSystemFont(GetParamValue(wxT("sysfont"))); - if (sysfont.Ok()) + if (font.Ok()) { - if (hasSize) - sysfont.SetPointSize(isize); + if (hasSize && isize != -1) + font.SetPointSize(isize); else if (HasParam(wxT("relativesize"))) - sysfont.SetPointSize(int(sysfont.GetPointSize() * + font.SetPointSize(int(font.GetPointSize() * GetFloat(wxT("relativesize")))); if (hasStyle) - sysfont.SetStyle(istyle); + font.SetStyle(istyle); if (hasWeight) - sysfont.SetWeight(iweight); + font.SetWeight(iweight); if (hasUnderlined) - sysfont.SetUnderlined(underlined); + font.SetUnderlined(underlined); if (hasFamily) - sysfont.SetFamily(ifamily); + font.SetFamily(ifamily); if (hasFacename) - sysfont.SetFaceName(facename); + font.SetFaceName(facename); if (hasEncoding) - sysfont.SetDefaultEncoding(enc); - - m_node = oldnode; - return sysfont; + font.SetDefaultEncoding(enc); + } + else // not based on system font + { + font = wxFont(isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize, + ifamily, istyle, iweight, + underlined, facename, enc); } m_node = oldnode; - return wxFont(isize, ifamily, istyle, iweight, - underlined, facename, enc); + return font; } @@ -1341,8 +1866,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) @@ -1354,23 +1883,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(); } } @@ -1392,30 +1921,84 @@ 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(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()); -// --------------- XRCID implementation ----------------------------- + DoReportError(filename, context, message); +} + +void wxXmlResource::DoReportError(const wxString& xrcFile, 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 +//----------------------------------------------------------------------------- #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) +static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE) { - int index = 0; + unsigned int 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; XRCID_record *oldrec = NULL; @@ -1434,8 +2017,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 { @@ -1447,41 +2030,22 @@ 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) +namespace { - return XRCID_Lookup(str_id); -} - - -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; - } -} +// 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); @@ -1499,6 +2063,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); @@ -1507,6 +2072,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); @@ -1583,15 +2149,95 @@ 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); +} -// --------------- module and globals ----------------------------- +/* 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 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 +//----------------------------------------------------------------------------- + +// 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 { @@ -1600,7 +2246,6 @@ public: wxXmlResourceModule() {} bool OnInit() { - AddStdXRCID_Records(); wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX); return true; } @@ -1608,8 +2253,14 @@ public: { delete wxXmlResource::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(); } };