wxWidgets/src/xrc/xmlres.cpp
Václav Slavík 964352934e Add window variant support to XRC.
Fixes #16247.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76996 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2014-08-03 17:10:24 +00:00

2919 lines
80 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/xrc/xmlres.cpp
// Purpose: XRC resources
// Author: Vaclav Slavik
// Created: 2000/03/05
// Copyright: (c) 2000 Vaclav Slavik
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#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 <locale.h>
#endif
#include "wx/vector.h"
#include "wx/wfstream.h"
#include "wx/filesys.h"
#include "wx/filename.h"
#include "wx/tokenzr.h"
#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:
// Ctor takes ownership of the document pointer.
wxXmlResourceDataRecord(const wxString& File_,
wxXmlDocument *Doc_
)
: File(File_), Doc(Doc_)
{
#if wxUSE_DATETIME
Time = GetXRCFileModTime(File);
#endif
}
~wxXmlResourceDataRecord() {delete Doc;}
wxString File;
wxXmlDocument *Doc;
#if wxUSE_DATETIME
wxDateTime Time;
#endif
wxDECLARE_NO_COPY_CLASS(wxXmlResourceDataRecord);
};
class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
{
// this is a class so that it can be forward-declared
};
WX_DECLARE_HASH_SET_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;
/*static*/ wxXmlResource *wxXmlResource::Get()
{
if ( !ms_instance )
ms_instance = new wxXmlResource;
return ms_instance;
}
/*static*/ wxXmlResource *wxXmlResource::Set(wxXmlResource *res)
{
wxXmlResource *old = ms_instance;
ms_instance = res;
return old;
}
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, 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;
}
/* static */
wxString wxXmlResource::ConvertFileNameToURL(const wxString& filename)
{
wxString fnd(filename);
// NB: as Load() and Unload() accept both filenames and URLs (should
// probably be changed to filenames only, but embedded resources
// currently rely on its ability to handle URLs - FIXME) we need to
// determine whether found name is filename and not URL and this is the
// fastest/simplest way to do it
if (wxFileName::FileExists(fnd))
{
// Make the name absolute filename, because the app may
// change working directory later:
wxFileName fn(fnd);
if (fn.IsRelative())
{
fn.MakeAbsolute();
fnd = fn.GetFullPath();
}
#if wxUSE_FILESYSTEM
fnd = wxFileSystem::FileNameToURL(fnd);
#endif
}
return fnd;
}
#if wxUSE_FILESYSTEM
/* static */
bool wxXmlResource::IsArchive(const wxString& filename)
{
const wxString fnd = filename.Lower();
return fnd.Matches(wxT("*.zip")) || fnd.Matches(wxT("*.xrs"));
}
#endif // wxUSE_FILESYSTEM
bool wxXmlResource::LoadFile(const wxFileName& file)
{
#if wxUSE_FILESYSTEM
return Load(wxFileSystem::FileNameToURL(file));
#else
return Load(file.GetFullPath());
#endif
}
bool wxXmlResource::LoadAllFiles(const wxString& dirname)
{
bool ok = true;
wxArrayString files;
wxDir::GetAllFiles(dirname, &files, "*.xrc");
for ( wxArrayString::const_iterator i = files.begin(); i != files.end(); ++i )
{
if ( !LoadFile(*i) )
ok = false;
}
return ok;
}
bool wxXmlResource::Load(const wxString& filemask_)
{
wxString filemask = ConvertFileNameToURL(filemask_);
bool allOK = true;
#if wxUSE_FILESYSTEM
wxFileSystem fsys;
# define wxXmlFindFirst fsys.FindFirst(filemask, wxFILE)
# define wxXmlFindNext fsys.FindNext()
#else
# define wxXmlFindFirst wxFindFirstFile(filemask, wxFILE)
# define wxXmlFindNext wxFindNextFile()
#endif
wxString fnd = wxXmlFindFirst;
if ( fnd.empty() )
{
wxLogError(_("Cannot load resources from '%s'."), filemask);
return false;
}
while (!fnd.empty())
{
#if wxUSE_FILESYSTEM
if ( IsArchive(fnd) )
{
if ( !Load(fnd + wxT("#zip:*.xrc")) )
allOK = false;
}
else // a single resource URL
#endif // wxUSE_FILESYSTEM
{
wxXmlDocument * const doc = DoLoadFile(fnd);
if ( !doc )
allOK = false;
else
Data().push_back(new wxXmlResourceDataRecord(fnd, doc));
}
fnd = wxXmlFindNext;
}
# undef wxXmlFindFirst
# undef wxXmlFindNext
return allOK;
}
bool wxXmlResource::Unload(const wxString& filename)
{
wxASSERT_MSG( !wxIsWild(filename),
wxT("wildcards not supported by wxXmlResource::Unload()") );
wxString fnd = ConvertFileNameToURL(filename);
#if wxUSE_FILESYSTEM
const bool isArchive = IsArchive(fnd);
if ( isArchive )
fnd += wxT("#zip:");
#endif // wxUSE_FILESYSTEM
bool unloaded = false;
for ( wxXmlResourceDataRecords::iterator i = Data().begin();
i != Data().end(); ++i )
{
#if wxUSE_FILESYSTEM
if ( isArchive )
{
if ( (*i)->File.StartsWith(fnd) )
unloaded = true;
// don't break from the loop, we can have other matching files
}
else // a single resource URL
#endif // wxUSE_FILESYSTEM
{
if ( (*i)->File == fnd )
{
delete *i;
Data().erase(i);
unloaded = true;
// no sense in continuing, there is only one file with this URL
break;
}
}
}
return unloaded;
}
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);
}
void wxXmlResource::ClearHandlers()
{
for ( wxVector<wxXmlResourceHandler*>::iterator i = m_handlers.begin();
i != m_handlers.end(); ++i )
delete *i;
m_handlers.clear();
}
wxMenu *wxXmlResource::LoadMenu(const wxString& name)
{
return (wxMenu*)CreateResFromNode(FindResource(name, wxT("wxMenu")), NULL, NULL);
}
wxMenuBar *wxXmlResource::LoadMenuBar(wxWindow *parent, const wxString& name)
{
return (wxMenuBar*)CreateResFromNode(FindResource(name, wxT("wxMenuBar")), parent, NULL);
}
#if wxUSE_TOOLBAR
wxToolBar *wxXmlResource::LoadToolBar(wxWindow *parent, const wxString& name)
{
return (wxToolBar*)CreateResFromNode(FindResource(name, wxT("wxToolBar")), parent, NULL);
}
#endif
wxDialog *wxXmlResource::LoadDialog(wxWindow *parent, const wxString& name)
{
return (wxDialog*)CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, NULL);
}
bool wxXmlResource::LoadDialog(wxDialog *dlg, wxWindow *parent, const wxString& name)
{
return CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, dlg) != NULL;
}
wxPanel *wxXmlResource::LoadPanel(wxWindow *parent, const wxString& name)
{
return (wxPanel*)CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, NULL);
}
bool wxXmlResource::LoadPanel(wxPanel *panel, wxWindow *parent, const wxString& name)
{
return CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, panel) != NULL;
}
wxFrame *wxXmlResource::LoadFrame(wxWindow* parent, const wxString& name)
{
return (wxFrame*)CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, NULL);
}
bool wxXmlResource::LoadFrame(wxFrame* frame, wxWindow *parent, const wxString& name)
{
return CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, frame) != NULL;
}
wxBitmap wxXmlResource::LoadBitmap(const wxString& name)
{
wxBitmap *bmp = (wxBitmap*)CreateResFromNode(
FindResource(name, wxT("wxBitmap")), NULL, NULL);
wxBitmap rt;
if (bmp) { rt = *bmp; delete bmp; }
return rt;
}
wxIcon wxXmlResource::LoadIcon(const wxString& name)
{
wxIcon *icon = (wxIcon*)CreateResFromNode(
FindResource(name, wxT("wxIcon")), NULL, NULL);
wxIcon rt;
if (icon) { rt = *icon; delete icon; }
return rt;
}
wxObject *
wxXmlResource::DoLoadObject(wxWindow *parent,
const wxString& name,
const wxString& classname,
bool recursive)
{
wxXmlNode * const node = FindResource(name, classname, recursive);
return node ? DoCreateResFromNode(*node, parent, NULL) : NULL;
}
bool
wxXmlResource::DoLoadObject(wxObject *instance,
wxWindow *parent,
const wxString& name,
const wxString& classname,
bool recursive)
{
wxXmlNode * const node = FindResource(name, classname, recursive);
return node && DoCreateResFromNode(*node, parent, instance) != NULL;
}
bool wxXmlResource::AttachUnknownControl(const wxString& name,
wxWindow *control, wxWindow *parent)
{
if (parent == NULL)
parent = control->GetParent();
wxWindow *container = parent->FindWindow(name + wxT("_container"));
if (!container)
{
wxLogError("Cannot find container for unknown control '%s'.", name);
return false;
}
return control->Reparent(container);
}
static void ProcessPlatformProperty(wxXmlNode *node)
{
wxString s;
bool isok;
wxXmlNode *c = node->GetChildren();
while (c)
{
isok = false;
if (!c->GetAttribute(wxT("platform"), &s))
isok = true;
else
{
wxStringTokenizer tkn(s, wxT(" |"));
while (tkn.HasMoreTokens())
{
s = tkn.GetNextToken();
#ifdef __WINDOWS__
if (s == wxT("win")) isok = true;
#endif
#if defined(__MAC__) || defined(__APPLE__)
if (s == wxT("mac")) isok = true;
#elif defined(__UNIX__)
if (s == wxT("unix")) isok = true;
#endif
if (isok)
break;
}
}
if (isok)
{
ProcessPlatformProperty(c);
c = c->GetNext();
}
else
{
wxXmlNode *c2 = c->GetNext();
node->RemoveChild(c);
delete c;
c = c2;
}
}
}
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;
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;
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
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,
const wxString& name,
const wxString& classname,
bool recursive) const
{
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 ( IsObjectNode(node) && node->GetAttribute(wxS("name")) == name )
{
// 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() == wxS("object_ref"))
{
wxString refName = node->GetAttribute(wxS("ref"));
if (refName.empty())
continue;
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 ( IsObjectNode(node) )
{
wxXmlNode* found = DoFindResource(node, name, classname, true);
if ( found )
return found;
}
}
}
return NULL;
}
wxXmlNode *wxXmlResource::FindResource(const wxString& name,
const wxString& classname,
bool recursive)
{
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();
for ( wxXmlResourceDataRecords::const_iterator f = Data().begin();
f != Data().end(); ++f )
{
wxXmlResourceDataRecord *const rec = *f;
wxXmlDocument * const doc = rec->Doc;
if ( !doc || !doc->GetRoot() )
continue;
wxXmlNode * const
found = DoFindResource(doc->GetRoot(), name, classname, recursive);
if ( found )
{
if ( path )
*path = rec->File;
return found;
}
}
return NULL;
}
static void MergeNodesOver(wxXmlNode& dest, wxXmlNode& overwriteWith,
const wxString& overwriteFilename)
{
// Merge attributes:
for ( wxXmlAttribute *attr = overwriteWith.GetAttributes();
attr; attr = attr->GetNext() )
{
wxXmlAttribute *dattr;
for (dattr = dest.GetAttributes(); dattr; dattr = dattr->GetNext())
{
if ( dattr->GetName() == attr->GetName() )
{
dattr->SetValue(attr->GetValue());
break;
}
}
if ( !dattr )
dest.AddAttribute(attr->GetName(), attr->GetValue());
}
// Merge child nodes:
for (wxXmlNode* node = overwriteWith.GetChildren(); node; node = node->GetNext())
{
wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
wxXmlNode *dnode;
for (dnode = dest.GetChildren(); dnode; dnode = dnode->GetNext() )
{
if ( dnode->GetName() == node->GetName() &&
dnode->GetAttribute(wxT("name"), wxEmptyString) == name &&
dnode->GetType() == node->GetType() )
{
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(copyOfNode);
}
else if ( insert_pos == wxT("begin") )
{
dest.InsertChild(copyOfNode, dest.GetChildren());
}
}
}
if ( dest.GetType() == wxXML_TEXT_NODE && overwriteWith.GetContent().length() )
dest.SetContent(overwriteWith.GetContent());
}
wxObject *
wxXmlResource::DoCreateResFromNode(wxXmlNode& node,
wxObject *parent,
wxObject *instance,
wxXmlResourceHandler *handlerToUse)
{
// handling of referenced resource
if ( node.GetName() == wxT("object_ref") )
{
wxString refName = node.GetAttribute(wxT("ref"), wxEmptyString);
wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
if ( !refNode )
{
ReportError
(
&node,
wxString::Format
(
"referenced object node with ref=\"%s\" not found",
refName
)
);
return NULL;
}
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 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))
{
return handlerToUse->CreateResource(&node, parent, instance);
}
}
else if (node.GetName() == wxT("object"))
{
for ( wxVector<wxXmlResourceHandler*>::iterator h = m_handlers.begin();
h != m_handlers.end(); ++h )
{
wxXmlResourceHandler *handler = *h;
if (handler->CanHandle(&node))
return handler->CreateResource(&node, parent, instance);
}
}
ReportError
(
&node,
wxString::Format
(
"no handler found for XML node \"%s\" (class \"%s\")",
node.GetName(),
node.GetAttribute("class", wxEmptyString)
)
);
return NULL;
}
wxIdRange::wxIdRange(const wxXmlNode* node,
const wxString& rname,
const wxString& startno,
const wxString& rsize)
: m_name(rname),
m_start(0),
m_size(0),
m_item_end_found(0),
m_finalised(0)
{
long l;
if ( startno.ToLong(&l) )
{
if ( l >= 0 )
{
m_start = l;
}
else
{
wxXmlResource::Get()->ReportError
(
node,
"a negative id-range start parameter was given"
);
}
}
else
{
wxXmlResource::Get()->ReportError
(
node,
"the id-range start parameter was malformed"
);
}
unsigned long ul;
if ( rsize.ToULong(&ul) )
{
m_size = ul;
}
else
{
wxXmlResource::Get()->ReportError
(
node,
"the id-range size parameter was malformed"
);
}
}
void wxIdRange::NoteItem(const wxXmlNode* node, const wxString& item)
{
// Nothing gets added here, but the existence of each item is noted
// thus getting an accurate count. 'item' will be either an integer e.g.
// [0] [123]: will eventually create an XRCID as start+integer or [start]
// or [end] which are synonyms for [0] or [range_size-1] respectively.
wxString content(item.Mid(1, item.length()-2));
// Check that basename+item wasn't foo[]
if (content.empty())
{
wxXmlResource::Get()->ReportError(node, "an empty id-range item found");
return;
}
if (content=="start")
{
// "start" means [0], so store that in the set
if (m_indices.count(0) == 0)
{
m_indices.insert(0);
}
else
{
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)
{
// 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();
}
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*>
{
// this is a class so that it can be forward-declared
};
wxXmlSubclassFactories *wxXmlResource::ms_subclassFactories = NULL;
/*static*/ void wxXmlResource::AddSubclassFactory(wxXmlSubclassFactory *factory)
{
if (!ms_subclassFactories)
{
ms_subclassFactories = new wxXmlSubclassFactories;
}
ms_subclassFactories->push_back(factory);
}
class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory
{
public:
~wxXmlSubclassFactoryCXX() {}
wxObject *Create(const wxString& className) wxOVERRIDE
{
wxClassInfo* classInfo = wxClassInfo::FindClass(className);
if (classInfo)
return classInfo->CreateObject();
else
return 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 *wxXmlResourceHandlerImpl::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
{
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_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_handler->m_instance = (*i)->Create(subclass);
if (m_handler->m_instance)
break;
}
if (!m_handler->m_instance)
{
wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
ReportError
(
node,
wxString::Format
(
"subclass \"%s\" not found for resource \"%s\", not subclassing",
subclass, name
)
);
}
}
}
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 = GetHandler()->DoCreateResource();
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;
}
bool wxXmlResourceHandlerImpl::HasParam(const wxString& param)
{
return (GetParamNode(param) != NULL);
}
int wxXmlResourceHandlerImpl::GetStyle(const wxString& param, int defaults)
{
wxString s = GetParamValue(param);
if (!s) return defaults;
wxStringTokenizer tkn(s, wxT("| \t\n"), wxTOKEN_STRTOK);
int style = 0;
int index;
wxString fl;
while (tkn.HasMoreTokens())
{
fl = tkn.GetNextToken();
index = m_handler->m_styleNames.Index(fl);
if (index != wxNOT_FOUND)
{
style |= m_handler->m_styleValues[index];
}
else
{
ReportParamError
(
param,
wxString::Format("unknown style flag \"%s\"", fl)
);
}
}
return style;
}
wxString wxXmlResourceHandlerImpl::GetText(const wxString& param, bool translate)
{
wxXmlNode *parNode = GetParamNode(param);
wxString str1(GetNodeContent(parNode));
wxString str2;
// "\\" 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").
const wxChar amp_char = (m_handler->m_resource->CompareVersion(2,3,0,1) < 0)
? '$' : '_';
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+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)).GetValue() )
{
case wxT('n'):
str2 << wxT('\n');
break;
case wxT('t'):
str2 << wxT('\t');
break;
case wxT('r'):
str2 << wxT('\r');
break;
case wxT('\\') :
// "\\" wasn't translated to "\" prior to 2.5.3.0:
if ( escapeBackslash )
{
str2 << wxT('\\');
break;
}
wxFALLTHROUGH;// else fall-through to default: branch below
default:
str2 << wxT('\\') << *dt;
break;
}
}
else
{
str2 << *dt;
}
}
if (m_handler->m_resource->GetFlags() & wxXRC_USE_LOCALE)
{
if (translate && parNode &&
parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
{
return wxGetTranslation(str2, m_handler->m_resource->GetDomain());
}
else
{
#if wxUSE_UNICODE
return str2;
#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.wc_str(wxConvUTF8), wxConvLocal);
#endif
}
}
// If wxXRC_USE_LOCALE is not set, then the string is already in
// system's default encoding in ANSI build, so we don't have to
// do anything special here.
return str2;
}
long wxXmlResourceHandlerImpl::GetLong(const wxString& param, long defaultv)
{
long value = defaultv;
wxString str1 = GetParamValue(param);
if (!str1.empty())
{
if (!str1.ToLong(&value))
{
ReportParamError
(
param,
wxString::Format("invalid long specification \"%s\"", str1)
);
}
}
return value;
}
float wxXmlResourceHandlerImpl::GetFloat(const wxString& param, float defaultv)
{
wxString str = GetParamValue(param);
// strings in XRC always use C locale so make sure to use the
// locale-independent wxString::ToCDouble() and not ToDouble() which uses
// the current locale with a potentially different decimal point character
double value = defaultv;
if (!str.empty())
{
if (!str.ToCDouble(&value))
{
ReportParamError
(
param,
wxString::Format("invalid float specification \"%s\"", str)
);
}
}
return wx_truncate_cast(float, value);
}
int wxXmlResourceHandlerImpl::GetID()
{
return wxXmlResource::GetXRCID(GetName());
}
wxString wxXmlResourceHandlerImpl::GetName()
{
return m_handler->m_node->GetAttribute(wxT("name"), wxT("-1"));
}
bool wxXmlResourceHandlerImpl::GetBoolAttr(const wxString& attr, bool 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.empty() ? defaultv : (v == '1');
}
static wxColour GetSystemColour(const wxString& name)
{
if (!name.empty())
{
#define SYSCLR(clr) \
if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr);
SYSCLR(wxSYS_COLOUR_SCROLLBAR)
SYSCLR(wxSYS_COLOUR_BACKGROUND)
SYSCLR(wxSYS_COLOUR_DESKTOP)
SYSCLR(wxSYS_COLOUR_ACTIVECAPTION)
SYSCLR(wxSYS_COLOUR_INACTIVECAPTION)
SYSCLR(wxSYS_COLOUR_MENU)
SYSCLR(wxSYS_COLOUR_WINDOW)
SYSCLR(wxSYS_COLOUR_WINDOWFRAME)
SYSCLR(wxSYS_COLOUR_MENUTEXT)
SYSCLR(wxSYS_COLOUR_WINDOWTEXT)
SYSCLR(wxSYS_COLOUR_CAPTIONTEXT)
SYSCLR(wxSYS_COLOUR_ACTIVEBORDER)
SYSCLR(wxSYS_COLOUR_INACTIVEBORDER)
SYSCLR(wxSYS_COLOUR_APPWORKSPACE)
SYSCLR(wxSYS_COLOUR_HIGHLIGHT)
SYSCLR(wxSYS_COLOUR_HIGHLIGHTTEXT)
SYSCLR(wxSYS_COLOUR_BTNFACE)
SYSCLR(wxSYS_COLOUR_3DFACE)
SYSCLR(wxSYS_COLOUR_BTNSHADOW)
SYSCLR(wxSYS_COLOUR_3DSHADOW)
SYSCLR(wxSYS_COLOUR_GRAYTEXT)
SYSCLR(wxSYS_COLOUR_BTNTEXT)
SYSCLR(wxSYS_COLOUR_INACTIVECAPTIONTEXT)
SYSCLR(wxSYS_COLOUR_BTNHIGHLIGHT)
SYSCLR(wxSYS_COLOUR_BTNHILIGHT)
SYSCLR(wxSYS_COLOUR_3DHIGHLIGHT)
SYSCLR(wxSYS_COLOUR_3DHILIGHT)
SYSCLR(wxSYS_COLOUR_3DDKSHADOW)
SYSCLR(wxSYS_COLOUR_3DLIGHT)
SYSCLR(wxSYS_COLOUR_INFOTEXT)
SYSCLR(wxSYS_COLOUR_INFOBK)
SYSCLR(wxSYS_COLOUR_LISTBOX)
SYSCLR(wxSYS_COLOUR_HOTLIGHT)
SYSCLR(wxSYS_COLOUR_GRADIENTACTIVECAPTION)
SYSCLR(wxSYS_COLOUR_GRADIENTINACTIVECAPTION)
SYSCLR(wxSYS_COLOUR_MENUHILIGHT)
SYSCLR(wxSYS_COLOUR_MENUBAR)
#undef SYSCLR
}
return wxNullColour;
}
wxColour wxXmlResourceHandlerImpl::GetColour(const wxString& param, const wxColour& defaultv)
{
wxString v = GetParamValue(param);
if ( v.empty() )
return defaultv;
wxColour clr;
// wxString -> wxColour conversion
if (!clr.Set(v))
{
// the colour doesn't use #RRGGBB format, check if it is symbolic
// colour name:
clr = GetSystemColour(v);
if (clr.IsOk())
return clr;
ReportParamError
(
param,
wxString::Format("incorrect colour specification \"%s\"", v)
);
return wxNullColour;
}
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 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(node);
if (name.empty()) return wxNullBitmap;
#if wxUSE_FILESYSTEM
wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
if (fsfile == NULL)
{
ReportParamError
(
node->GetName(),
wxString::Format("cannot open bitmap resource \"%s\"", name)
);
return wxNullBitmap;
}
wxImage img(*(fsfile->GetStream()));
delete fsfile;
#else
wxImage img(name);
#endif
if (!img.IsOk())
{
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 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)
{
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 *wxXmlResourceHandlerImpl::GetImageList(const wxString& param)
{
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 = GetBool(wxS("mask"), true);
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 *wxXmlResourceHandlerImpl::GetParamNode(const wxString& param)
{
wxCHECK_MSG(m_handler->m_node, NULL, wxT("You can't access handler data before it was initialized!"));
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 wxXmlResourceHandlerImpl::IsOfClass(wxXmlNode *node, const wxString& classname) const
{
return node->GetAttribute(wxT("class")) == classname;
}
bool wxXmlResourceHandlerImpl::IsObjectNode(const wxXmlNode *node) const
{
return node &&
node->GetType() == wxXML_ELEMENT_NODE &&
(node->GetName() == wxS("object") ||
node->GetName() == wxS("object_ref"));
}
wxString wxXmlResourceHandlerImpl::GetNodeContent(const wxXmlNode *node)
{
const wxXmlNode *n = node;
if (n == NULL) return wxEmptyString;
n = n->GetChildren();
while (n)
{
if (n->GetType() == wxXML_TEXT_NODE ||
n->GetType() == wxXML_CDATA_SECTION_NODE)
return n->GetContent();
n = n->GetNext();
}
return wxEmptyString;
}
wxXmlNode *wxXmlResourceHandlerImpl::GetNodeParent(const wxXmlNode *node) const
{
return node ? node->GetParent() : NULL;
}
wxXmlNode *wxXmlResourceHandlerImpl::GetNodeNext(const wxXmlNode *node) const
{
return node ? node->GetNext() : NULL;
}
wxXmlNode *wxXmlResourceHandlerImpl::GetNodeChildren(const wxXmlNode *node) const
{
return node ? node->GetChildren() : NULL;
}
wxString wxXmlResourceHandlerImpl::GetParamValue(const wxString& param)
{
if (param.empty())
return GetNodeContent(m_handler->m_node);
else
return GetNodeContent(GetParamNode(param));
}
wxString wxXmlResourceHandlerImpl::GetParamValue(const wxXmlNode* node)
{
return GetNodeContent(node);
}
wxSize wxXmlResourceHandlerImpl::GetSize(const wxString& param,
wxWindow *windowToUse)
{
wxString s = GetParamValue(param);
if (s.empty()) s = wxT("-1,-1");
bool is_dlg;
long sx, sy = 0;
is_dlg = s[s.length()-1] == wxT('d');
if (is_dlg) s.RemoveLast();
if (!s.BeforeFirst(wxT(',')).ToLong(&sx) ||
!s.AfterLast(wxT(',')).ToLong(&sy))
{
ReportParamError
(
param,
wxString::Format("cannot parse coordinates value \"%s\"", s)
);
return wxDefaultSize;
}
if (is_dlg)
{
if (windowToUse)
{
return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
}
else if (m_handler->m_parentAsWindow)
{
return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, sy));
}
else
{
ReportParamError
(
param,
"cannot convert dialog units: dialog unknown"
);
return wxDefaultSize;
}
}
return wxSize(sx, sy);
}
wxPoint wxXmlResourceHandlerImpl::GetPosition(const wxString& param)
{
wxSize sz = GetSize(param);
return wxPoint(sz.x, sz.y);
}
wxCoord wxXmlResourceHandlerImpl::GetDimension(const wxString& param,
wxCoord defaultv,
wxWindow *windowToUse)
{
wxString s = GetParamValue(param);
if (s.empty()) return defaultv;
bool is_dlg;
long sx;
is_dlg = s[s.length()-1] == wxT('d');
if (is_dlg) s.RemoveLast();
if (!s.ToLong(&sx))
{
ReportParamError
(
param,
wxString::Format("cannot parse dimension value \"%s\"", s)
);
return defaultv;
}
if (is_dlg)
{
if (windowToUse)
{
return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
}
else if (m_handler->m_parentAsWindow)
{
return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, 0)).x;
}
else
{
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 == wxT(#fnt)) return wxSystemSettings::GetFont(fnt);
SYSFNT(wxSYS_OEM_FIXED_FONT)
SYSFNT(wxSYS_ANSI_FIXED_FONT)
SYSFNT(wxSYS_ANSI_VAR_FONT)
SYSFNT(wxSYS_SYSTEM_FONT)
SYSFNT(wxSYS_DEVICE_DEFAULT_FONT)
SYSFNT(wxSYS_SYSTEM_FIXED_FONT)
SYSFNT(wxSYS_DEFAULT_GUI_FONT)
#undef SYSFNT
}
return wxNullFont;
}
wxFont wxXmlResourceHandlerImpl::GetFont(const wxString& param, wxWindow* parent)
{
wxXmlNode *font_node = GetParamNode(param);
if (font_node == NULL)
{
ReportError(
wxString::Format("cannot find font node \"%s\"", param));
return wxNullFont;
}
wxXmlNode *oldnode = m_handler->m_node;
m_handler->m_node = font_node;
// font attributes:
// size
int isize = -1;
bool hasSize = HasParam(wxT("size"));
if (hasSize)
isize = GetLong(wxT("size"), -1);
// style
wxFontStyle istyle = wxFONTSTYLE_NORMAL;
bool hasStyle = HasParam(wxT("style"));
if (hasStyle)
{
wxString style = GetParamValue(wxT("style"));
if (style == wxT("italic"))
istyle = wxFONTSTYLE_ITALIC;
else if (style == wxT("slant"))
istyle = wxFONTSTYLE_SLANT;
else if (style != wxT("normal"))
{
ReportParamError
(
param,
wxString::Format("unknown font style \"%s\"", style)
);
}
}
// weight
wxFontWeight iweight = wxFONTWEIGHT_NORMAL;
bool hasWeight = HasParam(wxT("weight"));
if (hasWeight)
{
wxString weight = GetParamValue(wxT("weight"));
if (weight == wxT("bold"))
iweight = wxFONTWEIGHT_BOLD;
else if (weight == wxT("light"))
iweight = wxFONTWEIGHT_LIGHT;
else if (weight != wxT("normal"))
{
ReportParamError
(
param,
wxString::Format("unknown font weight \"%s\"", weight)
);
}
}
// underline
bool hasUnderlined = HasParam(wxT("underlined"));
bool underlined = hasUnderlined ? GetBool(wxT("underlined"), false) : false;
// family and facename
wxFontFamily ifamily = wxFONTFAMILY_DEFAULT;
bool hasFamily = HasParam(wxT("family"));
if (hasFamily)
{
wxString family = GetParamValue(wxT("family"));
if (family == wxT("decorative")) ifamily = wxFONTFAMILY_DECORATIVE;
else if (family == wxT("roman")) ifamily = wxFONTFAMILY_ROMAN;
else if (family == wxT("script")) ifamily = wxFONTFAMILY_SCRIPT;
else if (family == wxT("swiss")) ifamily = wxFONTFAMILY_SWISS;
else if (family == wxT("modern")) ifamily = wxFONTFAMILY_MODERN;
else if (family == wxT("teletype")) ifamily = wxFONTFAMILY_TELETYPE;
else
{
ReportParamError
(
param,
wxString::Format("unknown font family \"%s\"", family)
);
}
}
wxString facename;
bool hasFacename = HasParam(wxT("face"));
if (hasFacename)
{
wxString faces = GetParamValue(wxT("face"));
wxStringTokenizer tk(faces, wxT(","));
#if wxUSE_FONTENUM
wxArrayString facenames(wxFontEnumerator::GetFacenames());
while (tk.HasMoreTokens())
{
int index = facenames.Index(tk.GetNextToken(), false);
if (index != wxNOT_FOUND)
{
facename = facenames[index];
break;
}
}
#else // !wxUSE_FONTENUM
// just use the first face name if we can't check its availability:
if (tk.HasMoreTokens())
facename = tk.GetNextToken();
#endif // wxUSE_FONTENUM/!wxUSE_FONTENUM
}
// encoding
wxFontEncoding enc = wxFONTENCODING_DEFAULT;
bool hasEncoding = HasParam(wxT("encoding"));
#if wxUSE_FONTMAP
if (hasEncoding)
{
wxString encoding = GetParamValue(wxT("encoding"));
wxFontMapper mapper;
if (!encoding.empty())
enc = mapper.CharsetToEncoding(encoding);
if (enc == wxFONTENCODING_SYSTEM)
enc = wxFONTENCODING_DEFAULT;
}
#endif // wxUSE_FONTMAP
wxFont font;
// is this font based on a system font?
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.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"))));
if (hasStyle)
font.SetStyle(istyle);
if (hasWeight)
font.SetWeight(iweight);
if (hasUnderlined)
font.SetUnderlined(underlined);
if (hasFamily)
font.SetFamily(ifamily);
if (hasFacename)
font.SetFaceName(facename);
if (hasEncoding)
font.SetDefaultEncoding(enc);
}
else // not based on system font
{
font = wxFont(isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize,
ifamily, istyle, iweight,
underlined, facename, enc);
}
m_handler->m_node = oldnode;
return font;
}
void wxXmlResourceHandlerImpl::SetupWindow(wxWindow *wnd)
{
//FIXME : add cursor
const wxString variant = GetParamValue(wxS("variant"));
if (!variant.empty())
{
if (variant == wxS("normal"))
wnd->SetWindowVariant(wxWINDOW_VARIANT_NORMAL);
else if (variant == wxS("small"))
wnd->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
else if (variant == wxS("mini"))
wnd->SetWindowVariant(wxWINDOW_VARIANT_MINI);
else if (variant == wxS("large"))
wnd->SetWindowVariant(wxWINDOW_VARIANT_LARGE);
else
{
ReportParamError
(
wxS("variant"),
wxString::Format
(
"Invalid window variant \"%s\": must be one of "
"normal|small|mini|large.",
variant
)
);
}
}
if (HasParam(wxT("exstyle")))
// Have to OR it with existing style, since
// some implementations (e.g. wxGTK) use the extra style
// during creation
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->SetFocus();
#if wxUSE_TOOLTIPS
if (HasParam(wxT("tooltip")))
wnd->SetToolTip(GetText(wxT("tooltip")));
#endif
if (HasParam(wxT("font")))
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 wxXmlResourceHandlerImpl::CreateChildren(wxObject *parent, bool this_hnd_only)
{
for ( wxXmlNode *n = m_handler->m_node->GetChildren(); n; n = n->GetNext() )
{
if ( IsObjectNode(n) )
{
m_handler->m_resource->DoCreateResFromNode(*n, parent, NULL,
this_hnd_only ? this->GetHandler() : NULL);
}
}
}
void wxXmlResourceHandlerImpl::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
{
wxXmlNode *root;
if (rootnode == NULL) root = m_handler->m_node; else root = rootnode;
wxXmlNode *n = root->GetChildren();
while (n)
{
if (n->GetType() == wxXML_ELEMENT_NODE && GetHandler()->CanHandle(n))
{
CreateResource(n, parent, NULL);
}
n = n->GetNext();
}
}
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
#define XRCID_TABLE_SIZE 1024
struct XRCID_record
{
/* 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};
// Extremely simplistic hash function which probably ought to be replaced with
// wxStringHash::stringHash().
static inline unsigned XRCIdHash(const char *str_id)
{
unsigned index = 0;
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)
{
if (wxStrcmp(rec->key, str_id) == 0)
{
return rec->id;
}
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)->next = NULL;
char *end;
if (value_if_not_found != wxID_NONE)
(*rec_var)->id = value_if_not_found;
else
{
int asint = wxStrtol(str_id, &end, 10);
if (*str_id && *end == 0)
{
// if str_id was integer, keep it verbosely:
(*rec_var)->id = asint;
}
else
{
(*rec_var)->id = wxWindowBase::NewControlId();
}
}
return (*rec_var)->id;
}
namespace
{
// flag indicating whether standard XRC ids were already initialized
static bool gs_stdIDsAdded = false;
void AddStdXRCID_Records()
{
#define stdID(id) XRCID_Lookup(#id, id)
stdID(-1);
stdID(wxID_ANY);
stdID(wxID_SEPARATOR);
stdID(wxID_OPEN);
stdID(wxID_CLOSE);
stdID(wxID_NEW);
stdID(wxID_SAVE);
stdID(wxID_SAVEAS);
stdID(wxID_REVERT);
stdID(wxID_EXIT);
stdID(wxID_UNDO);
stdID(wxID_REDO);
stdID(wxID_HELP);
stdID(wxID_PRINT);
stdID(wxID_PRINT_SETUP);
stdID(wxID_PAGE_SETUP);
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_PASTE);
stdID(wxID_CLEAR);
stdID(wxID_FIND);
stdID(wxID_DUPLICATE);
stdID(wxID_SELECTALL);
stdID(wxID_DELETE);
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_LIST);
stdID(wxID_VIEW_SORTDATE);
stdID(wxID_VIEW_SORTNAME);
stdID(wxID_VIEW_SORTSIZE);
stdID(wxID_VIEW_SORTTYPE);
stdID(wxID_FILE1);
stdID(wxID_FILE2);
stdID(wxID_FILE3);
stdID(wxID_FILE4);
stdID(wxID_FILE5);
stdID(wxID_FILE6);
stdID(wxID_FILE7);
stdID(wxID_FILE8);
stdID(wxID_FILE9);
stdID(wxID_OK);
stdID(wxID_CANCEL);
stdID(wxID_APPLY);
stdID(wxID_YES);
stdID(wxID_NO);
stdID(wxID_STATIC);
stdID(wxID_FORWARD);
stdID(wxID_BACKWARD);
stdID(wxID_DEFAULT);
stdID(wxID_MORE);
stdID(wxID_SETUP);
stdID(wxID_RESET);
stdID(wxID_CONTEXT_HELP);
stdID(wxID_YESTOALL);
stdID(wxID_NOTOALL);
stdID(wxID_ABORT);
stdID(wxID_RETRY);
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_JUSTIFY_FILL);
stdID(wxID_JUSTIFY_RIGHT);
stdID(wxID_JUSTIFY_LEFT);
stdID(wxID_UNDERLINE);
stdID(wxID_INDENT);
stdID(wxID_UNINDENT);
stdID(wxID_ZOOM_100);
stdID(wxID_ZOOM_FIT);
stdID(wxID_ZOOM_IN);
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_RESIZE_FRAME);
stdID(wxID_MAXIMIZE_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
//-----------------------------------------------------------------------------
// 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
{
DECLARE_DYNAMIC_CLASS(wxXmlResourceModule)
public:
wxXmlResourceModule() {}
bool OnInit() wxOVERRIDE
{
wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX);
return true;
}
void OnExit() wxOVERRIDE
{
delete wxXmlResource::Set(NULL);
delete wxIdRangeManager::Set(NULL);
if(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();
}
};
IMPLEMENT_DYNAMIC_CLASS(wxXmlResourceModule, wxModule)
// When wxXml is loaded dynamically after the application is already running
// then the built-in module system won't pick this one up. Add it manually.
void wxXmlInitResourceModule()
{
wxModule* module = new wxXmlResourceModule;
wxModule::RegisterModule(module);
wxModule::InitializeModules();
}
#endif // wxUSE_XRC