#include "wx/fontenum.h"
#include "wx/fontmap.h"
#include "wx/artprov.h"
-
+#include "wx/imaglist.h"
+#include "wx/dir.h"
#include "wx/xml/xml.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
+// Assign the given value to the specified entry or add a new value with this
+// name.
+static void XRCID_Assign(const wxString& str_id, int value);
class wxXmlResourceDataRecord
{
public:
- wxXmlResourceDataRecord() : Doc(NULL) {
+ // Ctor takes ownership of the document pointer.
+ wxXmlResourceDataRecord(const wxString& File_,
+ wxXmlDocument *Doc_
+ )
+ : File(File_), Doc(Doc_)
+ {
#if wxUSE_DATETIME
- Time = wxDateTime::Now();
+ Time = GetXRCFileModTime(File);
#endif
}
+
~wxXmlResourceDataRecord() {delete Doc;}
wxString File;
#if wxUSE_DATETIME
wxDateTime Time;
#endif
+
+ wxDECLARE_NO_COPY_CLASS(wxXmlResourceDataRecord);
};
-class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord>
+class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
{
// this is a class so that it can be forward-declared
};
+WX_DECLARE_HASH_SET_PTR(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;
+
+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;
+ }
+ }
+
+ return wxEmptyString; // not found
+}
+
+} // anonymous namespace
+
wxXmlResource *wxXmlResource::ms_instance = NULL;
{
ClearHandlers();
+ for ( wxXmlResourceDataRecords::iterator i = m_data->begin();
+ i != m_data->end(); ++i )
+ {
+ delete *i;
+ }
delete m_data;
}
#endif // wxUSE_FILESYSTEM
-bool wxXmlResource::Load(const wxString& filemask)
+bool wxXmlResource::LoadFile(const wxFileName& file)
{
- wxString fnd;
- bool iswild = wxIsWild(filemask);
- bool rt = true;
+#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;
# 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() )
{
- fnd = ConvertFileNameToURL(fnd);
+ wxLogError(_("Cannot load resources from '%s'."), filemask);
+ return false;
+ }
+ while (!fnd.empty())
+ {
#if wxUSE_FILESYSTEM
if ( IsArchive(fnd) )
{
- rt = rt && Load(fnd + wxT("#zip:*.xrc"));
+ if ( !Load(fnd + wxT("#zip:*.xrc")) )
+ allOK = false;
}
else // a single resource URL
#endif // wxUSE_FILESYSTEM
{
- wxXmlResourceDataRecord drec;
- drec.File = fnd;
- Data().push_back(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),
- _T("wildcards not supported by wxXmlResource::Unload()") );
+ wxT("wildcards not supported by wxXmlResource::Unload()") );
wxString fnd = ConvertFileNameToURL(filename);
#if wxUSE_FILESYSTEM
const bool isArchive = IsArchive(fnd);
if ( isArchive )
- fnd += _T("#zip:");
+ fnd += wxT("#zip:");
#endif // wxUSE_FILESYSTEM
bool unloaded = false;
#if wxUSE_FILESYSTEM
if ( isArchive )
{
- if ( i->File.StartsWith(fnd) )
+ 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 )
+ if ( (*i)->File == fnd )
{
+ delete *i;
Data().erase(i);
unloaded = true;
}
-IMPLEMENT_ABSTRACT_CLASS(wxXmlResourceHandler, wxObject)
-
void wxXmlResource::AddHandler(wxXmlResourceHandler *handler)
{
+ wxXmlResourceHandlerImpl *impl = new wxXmlResourceHandlerImpl(handler);
+ handler->SetImpl(impl);
m_handlers.push_back(handler);
handler->SetParentResource(this);
}
void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler)
{
+ wxXmlResourceHandlerImpl *impl = new wxXmlResourceHandlerImpl(handler);
+ handler->SetImpl(impl);
m_handlers.insert(m_handlers.begin(), handler);
handler->SetParentResource(this);
}
}
-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;
}
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);
}
}
+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
}
#endif
- for ( wxXmlResourceDataRecords::iterator i = Data().begin();
- i != Data().end(); ++i )
+ wxScopedPtr<wxXmlDocument> doc(new wxXmlDocument);
+ if (!doc->Load(*stream, encoding))
{
- modif = (i->Doc == NULL);
-
- if (!modif && !(m_flags & wxXRC_NO_RELOADING))
- {
-# if wxUSE_FILESYSTEM
- file = fsys.OpenFile(i->File);
-# if wxUSE_DATETIME
- modif = file && file->GetModificationTime() > i->Time;
-# else // wxUSE_DATETIME
- modif = true;
-# endif // wxUSE_DATETIME
- if (!file)
- {
- wxLogError(_("Cannot open file '%s'."), i->File.c_str());
- rt = false;
- }
- wxDELETE(file);
- wxUnusedVar(file);
-# else // wxUSE_FILESYSTEM
-# if wxUSE_DATETIME
- modif = wxDateTime(wxFileModificationTime(i->File)) > i->Time;
-# else // wxUSE_DATETIME
- modif = true;
-# endif // wxUSE_DATETIME
-# endif // wxUSE_FILESYSTEM
- }
-
- if (modif)
- {
- wxLogTrace(_T("xrc"),
- _T("opening file '%s'"), i->File.c_str());
-
- wxInputStream *stream = NULL;
-
-# if wxUSE_FILESYSTEM
- file = fsys.OpenFile(i->File);
- if (file)
- stream = file->GetStream();
-# else
- stream = new wxFileInputStream(i->File);
-# endif
-
- if (stream)
- {
- delete i->Doc;
- i->Doc = new wxXmlDocument;
- }
- if (!stream || !i->Doc->Load(*stream, encoding))
- {
- wxLogError(_("Cannot load resources from file '%s'."),
- i->File.c_str());
- wxDELETE(i->Doc);
- rt = false;
- }
- else if (i->Doc->GetRoot()->GetName() != wxT("resource"))
- {
- wxLogError(_("Invalid XRC resource '%s': doesn't have root node 'resource'."), i->File.c_str());
- wxDELETE(i->Doc);
- rt = false;
- }
- else
- {
- long version;
- int v1, v2, v3, v4;
- wxString verstr = i->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)
- 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(i->Doc->GetRoot());
-#if wxUSE_DATETIME
-#if wxUSE_FILESYSTEM
- i->Time = file->GetModificationTime();
-#else // wxUSE_FILESYSTEM
- i->Time = wxDateTime(wxFileModificationTime(i->File));
-#endif // wxUSE_FILESYSTEM
-#endif // wxUSE_DATETIME
- }
+ 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->GetAttribute(wxT("name"), &dummy) && dummy == name )
+ if ( IsObjectNode(node) && node->GetAttribute(wxS("name")) == name )
{
- wxString cls(node->GetAttribute(wxT("class"), wxEmptyString));
- if (!classname || cls == classname)
+ // empty class name matches everything
+ if ( classname.empty() )
return node;
+
+ wxString cls(node->GetAttribute(wxS("class")));
+
// object_ref may not have 'class' attribute:
- if (cls.empty() && node->GetName() == wxT("object_ref"))
+ if (cls.empty() && node->GetName() == wxS("object_ref"))
{
- wxString refName = node->GetAttribute(wxT("ref"), wxEmptyString);
+ wxString refName = node->GetAttribute(wxS("ref"));
if (refName.empty())
continue;
- wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
- if (refNode &&
- refNode->GetAttribute(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-demand
+ // reloading of XRC files
+ const_cast<wxXmlResource *>(this)->UpdateResources();
- wxString dummy;
for ( wxXmlResourceDataRecords::const_iterator f = Data().begin();
f != Data().end(); ++f )
{
- if ( f->Doc == NULL || f->Doc->GetRoot() == NULL )
+ wxXmlResourceDataRecord *const rec = *f;
+ wxXmlDocument * const doc = rec->Doc;
+ if ( !doc || !doc->GetRoot() )
continue;
- wxXmlNode* found = DoFindResource(f->Doc->GetRoot(),
- name, classname, recursive);
+ wxXmlNode * const
+ found = DoFindResource(doc->GetRoot(), name, classname, recursive);
if ( found )
{
-#if wxUSE_FILESYSTEM
- m_curFileSystem.ChangePathTo(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 attributes:
- for ( wxXmlAttribute *attr = with.GetAttributes();
+ for ( wxXmlAttribute *attr = overwriteWith.GetAttributes();
attr; attr = attr->GetNext() )
{
wxXmlAttribute *dattr;
}
// Merge child nodes:
- for (wxXmlNode* node = with.GetChildren(); node; node = node->GetNext())
+ for (wxXmlNode* node = overwriteWith.GetChildren(); node; node = node->GetNext())
{
wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
wxXmlNode *dnode;
dnode->GetAttribute(wxT("name"), wxEmptyString) == name &&
dnode->GetType() == node->GetType() )
{
- MergeNodes(*dnode, *node);
+ MergeNodesOver(*dnode, *node, overwriteFilename);
break;
}
}
if ( !dnode )
{
+ 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(new wxXmlNode(*node));
+ dest.AddChild(copyOfNode);
}
else if ( insert_pos == wxT("begin") )
{
- dest.InsertChild(new wxXmlNode(*node), dest.GetChildren());
+ 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->GetAttribute(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);
+ const bool hasOnlyRefAttr = node.GetAttributes() != NULL &&
+ node.GetAttributes()->GetNext() == NULL;
+
+ if ( hasOnlyRefAttr && !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(©, 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()));
+
+ // 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"))
{
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);
+ 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
+ {
+ wxXmlResource::Get()->ReportError
+ (
+ node,
+ "duplicate id-range item found"
+ );
+ }
+ }
+ 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
+ {
+ // Anything else will be an integer, or rubbish
+ unsigned long l;
+ if ( content.ToULong(&l) )
+ {
+ 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
+ {
+ wxXmlResource::Get()->ReportError
+ (
+ node,
+ "duplicate id-range item found"
+ );
+ }
+
}
+ 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;
+ }
+
+ 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;
}
- wxLogError(_("No handler found for XML node '%s', class '%s'!"),
- node->GetName().c_str(),
- node->GetAttribute(wxT("class"), wxEmptyString).c_str());
+ // Create the XRCIDs
+ for (int i=m_start; i <= m_end; ++i)
+ {
+ // Ensure that we overwrite any existing value as otherwise
+ // wxXmlResource::Unload() followed by Load() wouldn't work correctly.
+ XRCID_Assign(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
+ XRCID_Assign(m_name + "[start]", m_start);
+ XRCID_Assign(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;
+ }
+
+ 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);
+ }
+ }
+}
+
class wxXmlSubclassFactories : public wxVector<wxXmlSubclassFactory*>
{
-wxXmlResourceHandler::wxXmlResourceHandler()
- : m_node(NULL), m_parent(NULL), m_instance(NULL),
- m_parentAsWindow(NULL)
-{}
+wxXmlResourceHandlerImpl::wxXmlResourceHandlerImpl(wxXmlResourceHandler *handler)
+ :wxXmlResourceHandlerImplBase(handler)
+{
+}
+wxObject *wxXmlResourceHandlerImpl::CreateResFromNode(wxXmlNode *node,
+ wxObject *parent, wxObject *instance)
+{
+ return m_handler->m_resource->CreateResFromNode(node, parent, instance);
+}
+
+#if wxUSE_FILESYSTEM
+wxFileSystem& wxXmlResourceHandlerImpl::GetCurFileSystem()
+{
+ return m_handler->m_resource->GetCurFileSystem();
+}
+#endif
-wxObject *wxXmlResourceHandler::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
+wxObject *wxXmlResourceHandlerImpl::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
{
- wxXmlNode *myNode = m_node;
- wxString myClass = m_class;
- wxObject *myParent = m_parent, *myInstance = m_instance;
- wxWindow *myParentAW = m_parentAsWindow;
+ wxXmlNode *myNode = m_handler->m_node;
+ wxString myClass = m_handler->m_class;
+ wxObject *myParent = m_handler->m_parent, *myInstance = m_handler->m_instance;
+ wxWindow *myParentAW = m_handler->m_parentAsWindow;
- m_instance = instance;
- if (!m_instance && node->HasAttribute(wxT("subclass")) &&
- !(m_resource->GetFlags() & wxXRC_NO_SUBCLASSING))
+ m_handler->m_instance = instance;
+ if (!m_handler->m_instance && node->HasAttribute(wxT("subclass")) &&
+ !(m_handler->m_resource->GetFlags() & wxXRC_NO_SUBCLASSING))
{
wxString subclass = node->GetAttribute(wxT("subclass"), wxEmptyString);
if (!subclass.empty())
for (wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
i != wxXmlResource::ms_subclassFactories->end(); ++i)
{
- m_instance = (*i)->Create(subclass);
- if (m_instance)
+ m_handler->m_instance = (*i)->Create(subclass);
+ if (m_handler->m_instance)
break;
}
- if (!m_instance)
+ if (!m_handler->m_instance)
{
wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
- wxLogError(_("Subclass '%s' not found for resource '%s', not subclassing!"),
- subclass.c_str(), name.c_str());
+ ReportError
+ (
+ node,
+ wxString::Format
+ (
+ "subclass \"%s\" not found for resource \"%s\", not subclassing",
+ subclass, name
+ )
+ );
}
}
}
- m_node = node;
- m_class = node->GetAttribute(wxT("class"), wxEmptyString);
- m_parent = parent;
- m_parentAsWindow = wxDynamicCast(m_parent, wxWindow);
+ m_handler->m_node = node;
+ m_handler->m_class = node->GetAttribute(wxT("class"), wxEmptyString);
+ m_handler->m_parent = parent;
+ m_handler->m_parentAsWindow = wxDynamicCast(m_handler->m_parent, wxWindow);
- wxObject *returned = DoCreateResource();
+ wxObject *returned = GetHandler()->DoCreateResource();
- m_node = myNode;
- m_class = myClass;
- m_parent = myParent; m_parentAsWindow = myParentAW;
- m_instance = myInstance;
+ m_handler->m_node = myNode;
+ m_handler->m_class = myClass;
+ m_handler->m_parent = myParent; m_handler->m_parentAsWindow = myParentAW;
+ m_handler->m_instance = myInstance;
return returned;
}
-
-void wxXmlResourceHandler::AddStyle(const wxString& name, int value)
-{
- m_styleNames.Add(name);
- m_styleValues.Add(value);
-}
-
-
-
-void wxXmlResourceHandler::AddWindowStyles()
-{
- XRC_ADD_STYLE(wxCLIP_CHILDREN);
-
- // 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);
- 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);
-}
-
-
-
-bool wxXmlResourceHandler::HasParam(const wxString& param)
+bool wxXmlResourceHandlerImpl::HasParam(const wxString& param)
{
return (GetParamNode(param) != NULL);
}
-int wxXmlResourceHandler::GetStyle(const wxString& param, int defaults)
+int wxXmlResourceHandlerImpl::GetStyle(const wxString& param, int defaults)
{
wxString s = GetParamValue(param);
while (tkn.HasMoreTokens())
{
fl = tkn.GetNextToken();
- index = m_styleNames.Index(fl);
+ index = m_handler->m_styleNames.Index(fl);
if (index != wxNOT_FOUND)
- style |= m_styleValues[index];
+ {
+ style |= m_handler->m_styleValues[index];
+ }
else
- wxLogError(_("Unknown style flag ") + fl);
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("unknown style flag \"%s\"", fl)
+ );
+ }
}
return style;
}
-wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
+wxString wxXmlResourceHandlerImpl::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_handler->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_handler->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 )
+ if ( dt+1 == str1.end() || *(++dt) == amp_char )
str2 << amp_char;
else
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');
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;
str2 << wxT('\\') << *dt;
break;
}
- else str2 << *dt;
+ }
+ else
+ {
+ str2 << *dt;
+ }
}
- if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
+ if (m_handler->m_resource->GetFlags() & wxXRC_USE_LOCALE)
{
if (translate && parNode &&
parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
{
- return wxGetTranslation(str2, m_resource->GetDomain());
+ return wxGetTranslation(str2, m_handler->m_resource->GetDomain());
}
else
{
#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
}
}
-long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv)
+long wxXmlResourceHandlerImpl::GetLong(const wxString& param, long defaultv)
{
- long value;
+ long value = defaultv;
wxString str1 = GetParamValue(param);
- if (!str1.ToLong(&value))
- value = defaultv;
+ if (!str1.empty())
+ {
+ if (!str1.ToLong(&value))
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("invalid long specification \"%s\"", str1)
+ );
+ }
+ }
return value;
}
-float wxXmlResourceHandler::GetFloat(const wxString& param, float defaultv)
+float wxXmlResourceHandlerImpl::GetFloat(const wxString& param, float defaultv)
{
wxString str = GetParamValue(param);
- // 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));
-
- double value;
- if (!str.ToDouble(&value))
- value = defaultv;
+ // 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 = defaultv;
+ if (!str.empty())
+ {
+ if (!str.ToCDouble(&value))
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("invalid float specification \"%s\"", str)
+ );
+ }
+ }
return wx_truncate_cast(float, value);
}
-int wxXmlResourceHandler::GetID()
+int wxXmlResourceHandlerImpl::GetID()
{
return wxXmlResource::GetXRCID(GetName());
}
-wxString wxXmlResourceHandler::GetName()
+wxString wxXmlResourceHandlerImpl::GetName()
{
- return m_node->GetAttribute(wxT("name"), wxT("-1"));
+ return m_handler->m_node->GetAttribute(wxT("name"), wxT("-1"));
}
-bool wxXmlResourceHandler::GetBool(const wxString& param, bool defaultv)
+bool wxXmlResourceHandlerImpl::GetBoolAttr(const wxString& attr, bool defaultv)
{
- wxString v = GetParamValue(param);
- v.MakeLower();
- if (!v) return defaultv;
+ wxString v;
+ return m_handler->m_node->GetAttribute(attr, &v) ? v == '1' : defaultv;
+}
+
+bool wxXmlResourceHandlerImpl::GetBool(const wxString& param, bool defaultv)
+{
+ const wxString v = GetParamValue(param);
- return (v == wxT("1"));
+ return v.empty() ? defaultv : (v == '1');
}
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)
return wxNullColour;
}
-wxColour wxXmlResourceHandler::GetColour(const wxString& param, const wxColour& defaultv)
+wxColour wxXmlResourceHandlerImpl::GetColour(const wxString& param, const wxColour& defaultv)
{
wxString v = GetParamValue(param);
// the colour doesn't use #RRGGBB format, check if it is symbolic
// colour name:
clr = GetSystemColour(v);
- if (clr.Ok())
+ if (clr.IsOk())
return clr;
- wxLogError(_("XRC resource: Incorrect colour specification '%s' for attribute '%s'."),
- v.c_str(), param.c_str());
+ ReportParamError
+ (
+ param,
+ wxString::Format("incorrect colour specification \"%s\"", v)
+ );
return wxNullColour;
}
return clr;
}
+namespace
+{
-
-wxBitmap wxXmlResourceHandler::GetBitmap(const wxString& param,
- const wxArtClient& defaultArtClient,
- wxSize size)
+// 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 the bitmap is specified as stock item, query wxArtProvider for it: */
- wxXmlNode *bmpNode = GetParamNode(param);
- if ( bmpNode )
+ if ( paramNode )
{
- wxString sid = bmpNode->GetAttribute(wxT("stock_id"), wxEmptyString);
- if ( !sid.empty() )
+ art_id = paramNode->GetAttribute("stock_id", "");
+
+ if ( !art_id.empty() )
{
- wxString scl = bmpNode->GetAttribute(wxT("stock_client"), wxEmptyString);
- if (scl.empty())
- scl = defaultArtClient;
+ art_id = wxART_MAKE_ART_ID_FROM_STR(art_id);
+
+ art_client = paramNode->GetAttribute("stock_client", "");
+ if ( art_client.empty() )
+ art_client = defaultArtClient;
else
- scl = wxART_MAKE_CLIENT_ID_FROM_STR(scl);
+ art_client = wxART_MAKE_CLIENT_ID_FROM_STR(art_client);
- wxBitmap stockArt =
- wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(sid),
- scl, size);
- if ( stockArt.Ok() )
- return stockArt;
+ return true;
}
}
+ return false;
+}
+
+} // anonymous namespace
+
+wxBitmap wxXmlResourceHandlerImpl::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 wxXmlResourceHandlerImpl::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.IsOk() )
+ 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, 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()));
wxImage img(name);
#endif
- if (!img.Ok())
+ if (!img.IsOk())
{
- wxLogError(_("XRC resource: Cannot create bitmap from '%s'."),
- name.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);
}
-#if wxUSE_ANIMATIONCTRL
-wxAnimation wxXmlResourceHandler::GetAnimation(const wxString& param)
+
+wxIcon wxXmlResourceHandlerImpl::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 wxXmlResourceHandlerImpl::GetIcon(const wxXmlNode* node,
+ const wxArtClient& defaultArtClient,
+ wxSize size)
+{
+ wxIcon icon;
+ icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
+ return icon;
+}
+
+
+wxIconBundle wxXmlResourceHandlerImpl::GetIconBundle(const wxString& param,
+ const wxArtClient& defaultArtClient)
{
- wxAnimation ani;
+ 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;
- /* load the animation from file: */
- wxString name = GetParamValue(param);
- if (name.empty()) return wxNullAnimation;
#if wxUSE_FILESYSTEM
wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
- if (fsfile == NULL)
+ if ( fsfile == NULL )
{
- wxLogError(_("XRC resource: Cannot create animation from '%s'."),
- name.c_str());
- return wxNullAnimation;
+ ReportParamError
+ (
+ param,
+ wxString::Format("cannot open icon resource \"%s\"", name)
+ );
+ return wxNullIconBundle;
}
- ani.Load(*(fsfile->GetStream()));
+
+ wxIconBundle bundle(*(fsfile->GetStream()));
delete fsfile;
#else
- ani.LoadFile(name);
+ wxIconBundle bundle(name);
#endif
- if (!ani.IsOk())
+ if ( !bundle.IsOk() )
{
- wxLogError(_("XRC resource: Cannot create animation from '%s'."),
- name.c_str());
- return wxNullAnimation;
+ ReportParamError
+ (
+ param,
+ wxString::Format("cannot create icon from \"%s\"", name)
+ );
+ return wxNullIconBundle;
}
- return ani;
+ return bundle;
}
-#endif // wxUSE_ANIMATIONCTRL
-
-wxIcon wxXmlResourceHandler::GetIcon(const wxString& param,
- const wxArtClient& defaultArtClient,
- wxSize size)
+wxImageList *wxXmlResourceHandlerImpl::GetImageList(const wxString& param)
{
- wxIcon icon;
- icon.CopyFromBitmap(GetBitmap(param, defaultArtClient, size));
- return icon;
-}
+ wxXmlNode * const imagelist_node = GetParamNode(param);
+ if ( !imagelist_node )
+ return NULL;
+
+ wxXmlNode * const oldnode = m_handler->m_node;
+ m_handler->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_handler->m_node->GetChildren();
+ while (n)
+ {
+ if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
+ {
+ wxIcon icon = GetIcon(n, wxART_OTHER, size);
+ 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_handler->m_node = oldnode;
+ return imagelist;
+}
-wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
+wxXmlNode *wxXmlResourceHandlerImpl::GetParamNode(const wxString& param)
{
- wxCHECK_MSG(m_node, NULL, wxT("You can't access handler data before it was initialized!"));
+ wxCHECK_MSG(m_handler->m_node, NULL, wxT("You can't access handler data before it was initialized!"));
- wxXmlNode *n = m_node->GetChildren();
+ wxXmlNode *n = m_handler->m_node->GetChildren();
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)
+bool wxXmlResourceHandlerImpl::IsOfClass(wxXmlNode *node, const wxString& classname) const
{
- return node->GetAttribute(wxT("class"), wxEmptyString) == classname;
+ return node->GetAttribute(wxT("class")) == classname;
}
-wxString wxXmlResourceHandler::GetNodeContent(wxXmlNode *node)
+wxString wxXmlResourceHandlerImpl::GetNodeContent(const wxXmlNode *node)
{
- wxXmlNode *n = node;
+ const wxXmlNode *n = node;
if (n == NULL) return wxEmptyString;
n = n->GetChildren();
-wxString wxXmlResourceHandler::GetParamValue(const wxString& param)
+wxString wxXmlResourceHandlerImpl::GetParamValue(const wxString& param)
{
if (param.empty())
- return GetNodeContent(m_node);
+ return GetNodeContent(m_handler->m_node);
else
return GetNodeContent(GetParamNode(param));
}
+wxString wxXmlResourceHandlerImpl::GetParamValue(const wxXmlNode* node)
+{
+ return GetNodeContent(node);
+}
-wxSize wxXmlResourceHandler::GetSize(const wxString& param,
+wxSize wxXmlResourceHandlerImpl::GetSize(const wxString& param,
wxWindow *windowToUse)
{
wxString s = GetParamValue(param);
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;
}
{
return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
}
- else if (m_parentAsWindow)
+ else if (m_handler->m_parentAsWindow)
{
- return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, sy));
+ return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, sy));
}
else
{
- wxLogError(_("Cannot convert dialog units: dialog unknown."));
+ ReportParamError
+ (
+ param,
+ "cannot convert dialog units: dialog unknown"
+ );
return wxDefaultSize;
}
}
-wxPoint wxXmlResourceHandler::GetPosition(const wxString& param)
+wxPoint wxXmlResourceHandlerImpl::GetPosition(const wxString& param)
{
wxSize sz = GetSize(param);
return wxPoint(sz.x, sz.y);
-wxCoord wxXmlResourceHandler::GetDimension(const wxString& param,
+wxCoord wxXmlResourceHandlerImpl::GetDimension(const wxString& param,
wxCoord defaultv,
wxWindow *windowToUse)
{
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;
}
{
return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
}
- else if (m_parentAsWindow)
+ else if (m_handler->m_parentAsWindow)
{
- return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, 0)).x;
+ return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, 0)).x;
}
else
{
- wxLogError(_("Cannot convert dialog units: dialog unknown."));
+ ReportParamError
+ (
+ param,
+ "cannot convert dialog units: dialog unknown"
+ );
return defaultv;
}
}
return sx;
}
+wxDirection
+wxXmlResourceHandlerImpl::GetDirection(const wxString& param, wxDirection dirDefault)
+{
+ wxDirection dir;
+
+ const wxString dirstr = GetParamValue(param);
+ if ( dirstr.empty() )
+ dir = dirDefault;
+ else if ( dirstr == "wxLEFT" )
+ dir = wxLEFT;
+ else if ( dirstr == "wxRIGHT" )
+ dir = wxRIGHT;
+ else if ( dirstr == "wxTOP" )
+ dir = wxTOP;
+ else if ( dirstr == "wxBOTTOM" )
+ dir = wxBOTTOM;
+ else
+ {
+ ReportError
+ (
+ GetParamNode(param),
+ wxString::Format
+ (
+ "Invalid direction \"%s\": must be one of "
+ "wxLEFT|wxRIGHT|wxTOP|wxBOTTOM.",
+ dirstr
+ )
+ );
+
+ dir = dirDefault;
+ }
+
+ return dir;
+}
// Get system font index using indexname
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
return wxNullFont;
}
-wxFont wxXmlResourceHandler::GetFont(const wxString& param)
+wxFont wxXmlResourceHandlerImpl::GetFont(const wxString& param, wxWindow* parent)
{
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;
+ wxXmlNode *oldnode = m_handler->m_node;
+ m_handler->m_node = font_node;
// font attributes:
istyle = wxITALIC;
else if (style == wxT("slant"))
istyle = wxSLANT;
+ else if (style != wxT("normal"))
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("unknown font style \"%s\"", style)
+ );
+ }
}
// weight
iweight = wxBOLD;
else if (weight == wxT("light"))
iweight = wxLIGHT;
+ else if (weight != wxT("normal"))
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("unknown font weight \"%s\"", weight)
+ );
+ }
}
// underline
else if (family == wxT("swiss")) ifamily = wxSWISS;
else if (family == wxT("modern")) ifamily = wxMODERN;
else if (family == wxT("teletype")) ifamily = wxTELETYPE;
+ else
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("unknown font family \"%s\"", family)
+ );
+ }
}
if (hasFacename)
{
wxString faces = GetParamValue(wxT("face"));
- wxArrayString facenames(wxFontEnumerator::GetFacenames());
wxStringTokenizer tk(faces, wxT(","));
+#if wxUSE_FONTENUM
+ wxArrayString facenames(wxFontEnumerator::GetFacenames());
while (tk.HasMoreTokens())
{
int index = facenames.Index(tk.GetNextToken(), false);
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"));
if (enc == wxFONTENCODING_SYSTEM)
enc = wxFONTENCODING_DEFAULT;
}
+#endif // wxUSE_FONTMAP
+
+ wxFont font;
// is this font based on a system font?
- wxFont font = GetSystemFont(GetParamValue(wxT("sysfont")));
+ if (HasParam(wxT("sysfont")))
+ {
+ font = GetSystemFont(GetParamValue(wxT("sysfont")));
+ if (HasParam(wxT("inherit")))
+ {
+ ReportParamError
+ (
+ param,
+ "double specification of \"sysfont\" and \"inherit\""
+ );
+ }
+ }
+ // or should the font of the widget be used?
+ else if (GetBool(wxT("inherit"), false))
+ {
+ if (parent)
+ font = parent->GetFont();
+ else
+ {
+ ReportParamError
+ (
+ param,
+ "no parent window specified to derive the font from"
+ );
+ }
+ }
- if (font.Ok())
+ if (font.IsOk())
{
if (hasSize && isize != -1)
+ {
font.SetPointSize(isize);
+ if (HasParam(wxT("relativesize")))
+ {
+ ReportParamError
+ (
+ param,
+ "double specification of \"size\" and \"relativesize\""
+ );
+ }
+ }
else if (HasParam(wxT("relativesize")))
font.SetPointSize(int(font.GetPointSize() *
GetFloat(wxT("relativesize"))));
underlined, facename, enc);
}
- m_node = oldnode;
+ m_handler->m_node = oldnode;
return font;
}
-void wxXmlResourceHandler::SetupWindow(wxWindow *wnd)
+void wxXmlResourceHandlerImpl::SetupWindow(wxWindow *wnd)
{
//FIXME : add cursor
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)
wnd->SetToolTip(GetText(wxT("tooltip")));
#endif
if (HasParam(wxT("font")))
- wnd->SetFont(GetFont());
+ wnd->SetFont(GetFont(wxT("font"), wnd));
+ if (HasParam(wxT("ownfont")))
+ wnd->SetOwnFont(GetFont(wxT("ownfont"), wnd));
if (HasParam(wxT("help")))
wnd->SetHelpText(GetText(wxT("help")));
}
-void wxXmlResourceHandler::CreateChildren(wxObject *parent, bool this_hnd_only)
+void wxXmlResourceHandlerImpl::CreateChildren(wxObject *parent, bool this_hnd_only)
{
- wxXmlNode *n = m_node->GetChildren();
-
- while (n)
+ for ( wxXmlNode *n = m_handler->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);
- }
- n = n->GetNext();
+ m_handler->m_resource->DoCreateResFromNode(*n, parent, NULL,
+ this_hnd_only ? this->GetHandler() : NULL);
+ }
}
}
-void wxXmlResourceHandler::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
+void wxXmlResourceHandlerImpl::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
{
wxXmlNode *root;
- if (rootnode == NULL) root = m_node; else root = rootnode;
+ if (rootnode == NULL) root = m_handler->m_node; else root = rootnode;
wxXmlNode *n = root->GetChildren();
while (n)
{
- if (n->GetType() == wxXML_ELEMENT_NODE && CanHandle(n))
+ if (n->GetType() == wxXML_ELEMENT_NODE && GetHandler()->CanHandle(n))
{
CreateResource(n, parent, NULL);
}
}
+//-----------------------------------------------------------------------------
+// errors reporting
+//-----------------------------------------------------------------------------
+
+void wxXmlResourceHandlerImpl::ReportError(const wxString& message)
+{
+ m_handler->m_resource->ReportError(m_handler->m_node, message);
+}
+
+void wxXmlResourceHandlerImpl::ReportError(wxXmlNode *context,
+ const wxString& message)
+{
+ m_handler->m_resource->ReportError(context ? context : m_handler->m_node, message);
+}
+
+void wxXmlResourceHandlerImpl::ReportParamError(const wxString& param,
+ const wxString& message)
+{
+ m_handler->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;
+ /* 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 char *str_id, int value_if_not_found = wxID_NONE)
+// 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 char *c = str_id; *c != '\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 void XRCID_Assign(const wxString& str_id, int value)
+{
+ const wxCharBuffer buf_id(str_id.mb_str());
+ const unsigned index = XRCIdHash(buf_id);
+
+
+ XRCID_record *oldrec = NULL;
+ for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
+ {
+ if (wxStrcmp(rec->key, buf_id) == 0)
+ {
+ rec->id = value;
+ return;
+ }
+ oldrec = rec;
+ }
+
+ XRCID_record **rec_var = (oldrec == NULL) ?
+ &XRCID_Records[index] : &oldrec->next;
+ *rec_var = new XRCID_record;
+ (*rec_var)->key = wxStrdup(str_id);
+ (*rec_var)->id = value;
+ (*rec_var)->next = NULL;
+}
+
+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)
{
return (*rec_var)->id;
}
-static void AddStdXRCID_Records();
-
-/*static*/
-int wxXmlResource::DoGetXRCID(const char *str_id, int value_if_not_found)
-{
- static bool s_stdIDsAdded = false;
-
- if ( !s_stdIDsAdded )
- {
- s_stdIDsAdded = true;
- AddStdXRCID_Records();
- }
-
- return XRCID_Lookup(str_id, value_if_not_found);
-}
-
-
-static void CleanXRCID_Record(XRCID_record *rec)
+namespace
{
- if (rec)
- {
- CleanXRCID_Record(rec->next);
-
- // if we had generated the value of this id automatically, release it
- // now that we don't need it any longer
- if ( wxWindow::IsAutoGeneratedId(rec->id) )
- wxWindow::ReleaseControlId(rec->id);
-
- 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(#id, id)
stdID(-1);
stdID(wxID_PREVIEW);
stdID(wxID_ABOUT);
stdID(wxID_HELP_CONTENTS);
+ stdID(wxID_HELP_INDEX),
+ stdID(wxID_HELP_SEARCH),
stdID(wxID_HELP_COMMANDS);
stdID(wxID_HELP_PROCEDURES);
stdID(wxID_HELP_CONTEXT);
stdID(wxID_CLOSE_ALL);
stdID(wxID_PREFERENCES);
+
stdID(wxID_EDIT);
stdID(wxID_CUT);
stdID(wxID_COPY);
stdID(wxID_REPLACE);
stdID(wxID_REPLACE_ALL);
stdID(wxID_PROPERTIES);
+
stdID(wxID_VIEW_DETAILS);
stdID(wxID_VIEW_LARGEICONS);
stdID(wxID_VIEW_SMALLICONS);
stdID(wxID_VIEW_SORTNAME);
stdID(wxID_VIEW_SORTSIZE);
stdID(wxID_VIEW_SORTTYPE);
+
+
stdID(wxID_FILE1);
stdID(wxID_FILE2);
stdID(wxID_FILE3);
stdID(wxID_FILE7);
stdID(wxID_FILE8);
stdID(wxID_FILE9);
+
+
stdID(wxID_OK);
stdID(wxID_CANCEL);
stdID(wxID_APPLY);
stdID(wxID_IGNORE);
stdID(wxID_ADD);
stdID(wxID_REMOVE);
+
stdID(wxID_UP);
stdID(wxID_DOWN);
stdID(wxID_HOME);
stdID(wxID_REFRESH);
stdID(wxID_STOP);
stdID(wxID_INDEX);
+
stdID(wxID_BOLD);
stdID(wxID_ITALIC);
stdID(wxID_JUSTIFY_CENTER);
stdID(wxID_ZOOM_OUT);
stdID(wxID_UNDELETE);
stdID(wxID_REVERT_TO_SAVED);
+ 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);
+
+
stdID(wxID_SYSTEM_MENU);
stdID(wxID_CLOSE_FRAME);
stdID(wxID_MOVE_FRAME);
stdID(wxID_ICONIZE_FRAME);
stdID(wxID_RESTORE_FRAME);
+
+
+ stdID(wxID_MDI_WINDOW_CASCADE);
+ stdID(wxID_MDI_WINDOW_TILE_HORZ);
+ stdID(wxID_MDI_WINDOW_TILE_VERT);
+ stdID(wxID_MDI_WINDOW_ARRANGE_ICONS);
+ stdID(wxID_MDI_WINDOW_PREV);
+ stdID(wxID_MDI_WINDOW_NEXT);
#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 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
{
void OnExit()
{
delete wxXmlResource::Set(NULL);
+ delete wxIdRangeManager::Set(NULL);
if(wxXmlResource::ms_subclassFactories)
{
for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
void wxXmlInitResourceModule()
{
wxModule* module = new wxXmlResourceModule;
- module->Init();
wxModule::RegisterModule(module);
+ wxModule::InitializeModules();
}
#endif // wxUSE_XRC