#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
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*>
// this is a class so that it can be forward-declared
};
+WX_DECLARE_HASH_SET(int, wxIntegerHash, wxIntegerEqual, wxHashSetInt);
+
+class wxIdRange // Holds data for a particular rangename
+{
+protected:
+ wxIdRange(const wxXmlNode* node,
+ const wxString& rname,
+ const wxString& startno,
+ const wxString& rsize);
+
+ // Note the existence of an item within the range
+ void NoteItem(const wxXmlNode* node, const wxString& item);
+
+ // The manager is telling us that it's finished adding items
+ void Finalise(const wxXmlNode* node);
+
+ wxString GetName() const { return m_name; }
+ bool IsFinalised() const { return m_finalised; }
+
+ const wxString m_name;
+ int m_start;
+ int m_end;
+ unsigned int m_size;
+ bool m_item_end_found;
+ bool m_finalised;
+ wxHashSetInt m_indices;
+
+ friend class wxIdRangeManager;
+};
+
+class wxIdRangeManager
+{
+public:
+ ~wxIdRangeManager();
+ // Gets the global resources object or creates one if none exists.
+ static wxIdRangeManager *Get();
+
+ // Sets the global resources object and returns a pointer to the previous
+ // one (may be NULL).
+ static wxIdRangeManager *Set(wxIdRangeManager *res);
+
+ // Create a new IDrange from this node
+ void AddRange(const wxXmlNode* node);
+ // Tell the IdRange that this item exists, and should be pre-allocated an ID
+ void NotifyRangeOfItem(const wxXmlNode* node, const wxString& item) const;
+ // Tells all IDranges that they're now complete, and can create their IDs
+ void FinaliseRanges(const wxXmlNode* node) const;
+ // Searches for a known IdRange matching 'name', returning its index or -1
+ int Find(const wxString& rangename) const;
+ // Removes, if it exists, an entry from the XRCID table. Used in id-ranges
+ // to replace defunct or statically-initialised entries with current values
+ static void RemoveXRCIDEntry(const wxString& idstr);
+
+protected:
+ wxIdRange* FindRangeForItem(const wxXmlNode* node,
+ const wxString& item,
+ wxString& value) const;
+ wxVector<wxIdRange*> m_IdRanges;
+
+private:
+ static wxIdRangeManager *ms_instance;
+};
+
namespace
{
{
wxString filemask = ConvertFileNameToURL(filemask_);
+ bool allOK = true;
+
#if wxUSE_FILESYSTEM
wxFileSystem fsys;
# define wxXmlFindFirst fsys.FindFirst(filemask, wxFILE)
if ( IsArchive(fnd) )
{
if ( !Load(fnd + wxT("#zip:*.xrc")) )
- return false;
+ allOK = false;
}
else // a single resource URL
#endif // wxUSE_FILESYSTEM
{
- wxXmlResourceDataRecord *drec = new wxXmlResourceDataRecord;
- drec->File = fnd;
- Data().push_back(drec);
+ wxXmlDocument * const doc = DoLoadFile(fnd);
+ if ( !doc )
+ allOK = false;
+ else
+ Data().push_back(new wxXmlResourceDataRecord(fnd, doc));
}
fnd = wxXmlFindNext;
# undef wxXmlFindFirst
# undef wxXmlFindNext
- return UpdateResources();
+ return allOK;
}
bool wxXmlResource::Unload(const wxString& filename)
}
}
+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);
- wxFileSystem fsys;
-# endif
-
- wxString encoding(wxT("UTF-8"));
-#if !wxUSE_UNICODE && wxUSE_INTL
- if ( (GetFlags() & wxXRC_USE_LOCALE) == 0 )
- {
- // In case we are not using wxLocale to translate strings, convert the
- // strings GUI's charset. This must not be done when wxXRC_USE_LOCALE
- // is on, because it could break wxGetTranslation lookup.
- encoding = wxLocale::GetSystemEncodingName();
- }
-#endif
for ( wxXmlResourceDataRecords::iterator i = Data().begin();
i != Data().end(); ++i )
{
wxXmlResourceDataRecord* const rec = *i;
- modif = (rec->Doc == NULL);
+ // Check if we need to reload this one.
- if (!modif && !(m_flags & wxXRC_NO_RELOADING))
+ // 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
{
-# if wxUSE_FILESYSTEM
- 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'."), rec->File);
- rt = false;
- }
- wxDELETE(file);
- wxUnusedVar(file);
-# else // wxUSE_FILESYSTEM
-# if wxUSE_DATETIME
- modif = wxDateTime(wxFileModificationTime(rec->File)) > rec->Time;
-# else // wxUSE_DATETIME
- modif = true;
-# endif // wxUSE_DATETIME
-# endif // wxUSE_FILESYSTEM
+ // No need to reload, the file wasn't modified since we did it
+ // last.
+ continue;
}
- if (modif)
+ wxXmlDocument * const doc = DoLoadFile(rec->File);
+ if ( !doc )
{
- wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), rec->File);
+ // 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;
+ }
- wxInputStream *stream = NULL;
+ // Replace the old resource contents with the new one.
+ delete rec->Doc;
+ rec->Doc = doc;
-# if wxUSE_FILESYSTEM
- file = fsys.OpenFile(rec->File);
- if (file)
- stream = file->GetStream();
-# else
- stream = new wxFileInputStream(rec->File);
-# endif
+ // 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
+ }
- if (stream)
- {
- delete rec->Doc;
- rec->Doc = new wxXmlDocument;
- }
- if (!stream || !stream->IsOk() || !rec->Doc->Load(*stream, encoding))
- {
- wxLogError(_("Cannot load resources from file '%s'."),
- rec->File);
- wxDELETE(rec->Doc);
- rt = false;
- }
- else if (rec->Doc->GetRoot()->GetName() != wxT("resource"))
- {
- ReportError
- (
- rec->Doc->GetRoot(),
- "invalid XRC resource, doesn't have root node <resource>"
- );
- wxDELETE(rec->Doc);
- rt = false;
- }
- else
- {
- long version;
- int v1, v2, v3, v4;
- 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)
- 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;
- }
+ return rt;
+}
+
+wxXmlDocument *wxXmlResource::DoLoadFile(const wxString& filename)
+{
+ wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), filename);
+
+ wxInputStream *stream = NULL;
- ProcessPlatformProperty(rec->Doc->GetRoot());
-#if wxUSE_DATETIME
#if wxUSE_FILESYSTEM
- rec->Time = file->GetModificationTime();
-#else // wxUSE_FILESYSTEM
- rec->Time = wxDateTime(wxFileModificationTime(rec->File));
-#endif // wxUSE_FILESYSTEM
-#endif // wxUSE_DATETIME
- }
+ wxFileSystem fsys;
+ 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 wxUSE_FILESYSTEM
- wxDELETE(file);
- wxUnusedVar(file);
-# else
- wxDELETE(stream);
-# endif
- }
+ if ( !stream || !stream->IsOk() )
+ {
+ wxLogError(_("Cannot open resources file '%s'."), filename);
+ return NULL;
}
- return rt;
+ wxString encoding(wxT("UTF-8"));
+#if !wxUSE_UNICODE && wxUSE_INTL
+ if ( (GetFlags() & wxXRC_USE_LOCALE) == 0 )
+ {
+ // In case we are not using wxLocale to translate strings, convert the
+ // strings GUI's charset. This must not be done when wxXRC_USE_LOCALE
+ // is on, because it could break wxGetTranslation lookup.
+ encoding = wxLocale::GetSystemEncodingName();
+ }
+#endif
+
+ wxScopedPtr<wxXmlDocument> doc(new wxXmlDocument);
+ if (!doc->Load(*stream, encoding))
+ {
+ wxLogError(_("Cannot load resources from file '%s'."), filename);
+ return NULL;
+ }
+
+ wxXmlNode * const root = doc->GetRoot();
+ if (root->GetName() != wxT("resource"))
+ {
+ ReportError
+ (
+ root,
+ "invalid XRC resource, doesn't have root node <resource>"
+ );
+ return NULL;
+ }
+
+ 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.");
+ }
+
+ ProcessPlatformProperty(root);
+ PreprocessForIdRanges(root);
+ wxIdRangeManager::Get()->FinaliseRanges(root);
+
+ return doc.release();
}
wxXmlNode *wxXmlResource::DoFindResource(wxXmlNode *parent,
bool recursive,
wxString *path) const
{
- // ensure everything is up-to-date: this is needed to support on-remand
+ // ensure everything is up-to-date: this is needed to support on-demand
// reloading of XRC files
const_cast<wxXmlResource *>(this)->UpdateResources();
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;
+ }
+
+ // Create the XRCIDs
+ for (int i=m_start; i <= m_end; ++i)
+ {
+ // First clear any pre-existing XRCID
+ // Necessary for wxXmlResource::Unload() followed by Load()
+ wxIdRangeManager::RemoveXRCIDEntry(
+ m_name + wxString::Format("[%i]", i-m_start));
+
+ // Use the second parameter of GetXRCID to force it to take the value i
+ wxXmlResource::GetXRCID(m_name + wxString::Format("[%i]", i-m_start), i);
+ wxLogTrace("xrcrange",
+ "integer = %i %s now returns %i",
+ i,
+ m_name + wxString::Format("[%i]", i-m_start),
+ XRCID((m_name + wxString::Format("[%i]", i-m_start)).mb_str()));
+ }
+ // and these special ones
+ wxIdRangeManager::RemoveXRCIDEntry(m_name + "[start]");
+ wxXmlResource::GetXRCID(m_name + "[start]", m_start);
+ wxIdRangeManager::RemoveXRCIDEntry(m_name + "[end]");
+ wxXmlResource::GetXRCID(m_name + "[end]", m_end);
+ wxLogTrace("xrcrange","%s[start] = %i %s[end] = %i",
+ m_name.mb_str(),XRCID(wxString(m_name+"[start]").mb_str()),
+ m_name.mb_str(),XRCID(wxString(m_name+"[end]").mb_str()));
+
+ m_finalised = true;
+}
+
+wxIdRangeManager *wxIdRangeManager::ms_instance = NULL;
+
+/*static*/ wxIdRangeManager *wxIdRangeManager::Get()
+{
+ if ( !ms_instance )
+ ms_instance = new wxIdRangeManager;
+ return ms_instance;
+}
+
+/*static*/ wxIdRangeManager *wxIdRangeManager::Set(wxIdRangeManager *res)
+{
+ wxIdRangeManager *old = ms_instance;
+ ms_instance = res;
+ return old;
+}
+
+wxIdRangeManager::~wxIdRangeManager()
+{
+ for ( wxVector<wxIdRange*>::iterator i = m_IdRanges.begin();
+ i != m_IdRanges.end(); ++i )
+ {
+ delete *i;
+ }
+ m_IdRanges.clear();
+
+ delete ms_instance;
+}
+
+void wxIdRangeManager::AddRange(const wxXmlNode* node)
+{
+ wxString name = node->GetAttribute("name");
+ wxString start = node->GetAttribute("start", "0");
+ wxString size = node->GetAttribute("size", "0");
+ if (name.empty())
+ {
+ wxXmlResource::Get()->ReportError
+ (
+ node,
+ "xrc file contains an id-range without a name"
+ );
+ return;
+ }
+
+ int index = Find(name);
+ if (index == wxNOT_FOUND)
+ {
+ wxLogTrace("xrcrange",
+ "Adding ID range, name=%s start=%s size=%s",
+ name, start, size);
+
+ m_IdRanges.push_back(new wxIdRange(node, name, start, size));
+ }
+ else
+ {
+ // There was already a range with this name. Let's hope this is
+ // from an Unload()/(re)Load(), not an unintentional duplication
+ wxLogTrace("xrcrange",
+ "Replacing ID range, name=%s start=%s size=%s",
+ name, start, size);
+
+ wxIdRange* oldrange = m_IdRanges.at(index);
+ m_IdRanges.at(index) = new wxIdRange(node, name, start, size);
+ delete oldrange;
+ }
+}
+
+wxIdRange *
+wxIdRangeManager::FindRangeForItem(const wxXmlNode* node,
+ const wxString& item,
+ wxString& value) const
+{
+ wxString basename = item.BeforeFirst('[');
+ wxCHECK_MSG( !basename.empty(), NULL,
+ "an id-range item without a range name" );
+
+ int index = Find(basename);
+ if (index == wxNOT_FOUND)
+ {
+ // Don't assert just because we've found an unexpected foo[123]
+ // Someone might just want such a name, nothing to do with ranges
+ return NULL;
+ }
+
+ value = item.Mid(basename.Len());
+ if (value.at(value.length()-1)==']')
+ {
+ return m_IdRanges.at(index);
+ }
+ wxXmlResource::Get()->ReportError(node, "a malformed id-range item");
+ return NULL;
+}
+
+void
+wxIdRangeManager::NotifyRangeOfItem(const wxXmlNode* node,
+ const wxString& item) const
+{
+ wxString value;
+ wxIdRange* range = FindRangeForItem(node, item, value);
+ if (range)
+ range->NoteItem(node, value);
+}
+
+int wxIdRangeManager::Find(const wxString& rangename) const
+{
+ for ( int i=0; i < (int)m_IdRanges.size(); ++i )
+ {
+ if (m_IdRanges.at(i)->GetName() == rangename)
+ return i;
+ }
+
+ 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*>
{
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)
{
- unsigned int index = 0;
+ unsigned index = 0;
for (const char *c = str_id; *c != '\0'; c++) index += (unsigned int)*c;
index %= XRCID_TABLE_SIZE;
+ return index;
+}
+
+static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE)
+{
+ const unsigned index = XRCIdHash(str_id);
+
+
XRCID_record *oldrec = NULL;
for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
{
return wxString();
}
+/* static */
+void wxIdRangeManager::RemoveXRCIDEntry(const wxString& idstr)
+{
+ const char *str_id = idstr.mb_str();
+
+ const unsigned index = XRCIdHash(str_id);
+
+ XRCID_record **p_previousrec = &XRCID_Records[index];
+ for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
+ {
+ if (wxStrcmp(rec->key, str_id) == 0)
+ {
+ // Found the item to be removed so delete its record; but first
+ // remove it from the linked list.
+ *p_previousrec = rec->next;
+ free(rec->key);
+ delete rec;
+ return;
+ }
+
+ p_previousrec = &rec->next;
+ }
+}
+
static void CleanXRCID_Record(XRCID_record *rec)
{
if (rec)
void OnExit()
{
delete wxXmlResource::Set(NULL);
+ delete wxIdRangeManager::Set(NULL);
if(wxXmlResource::ms_subclassFactories)
{
for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();