#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"
node->GetName() == wxS("object_ref"));
}
+// special XML attribute with name of input file, see GetFileNameFromNode()
+const char *ATTR_INPUT_FILENAME = "__wx:filename";
+
+// helper to get filename corresponding to an XML node
+wxString
+GetFileNameFromNode(wxXmlNode *node, const wxXmlResourceDataRecords& files)
+{
+ // this loop does two things: it looks for ATTR_INPUT_FILENAME among
+ // parents and if it isn't used, it finds the root of the XML tree 'node'
+ // is in
+ for ( ;; )
+ {
+ // in some rare cases (specifically, when an <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
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)
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);
}
else if (rec->Doc->GetRoot()->GetName() != wxT("resource"))
{
- wxLogError(_("Invalid XRC resource '%s': doesn't have root node 'resource'."), rec->File);
+ ReportError
+ (
+ rec->Doc->GetRoot(),
+ "invalid XRC resource, doesn't have root node <resource>"
+ );
wxDELETE(rec->Doc);
rt = false;
}
m_version = version;
if (m_version != version)
{
- wxLogError(_("Resource files must have same version number!"));
+ wxLogError("Resource files must have same version number.");
rt = false;
}
if ( !node )
{
- wxLogError(_("XRC resource '%s' (class '%s') not found!"),
- name, classname);
+ ReportError
+ (
+ NULL,
+ wxString::Format
+ (
+ "XRC resource \"%s\" (class \"%s\") not found",
+ name, classname
+ )
+ );
}
#if wxUSE_FILESYSTEM
else // node was found
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,
if ( !refNode )
{
- wxLogError(_("Referenced object node with ref=\"%s\" not found!"),
- refName.c_str());
+ ReportError
+ (
+ node,
+ wxString::Format
+ (
+ "referenced object node with ref=\"%s\" not found",
+ refName
+ )
+ );
return NULL;
}
- wxXmlNode copy(*refNode);
- MergeNodes(copy, *node);
+ if ( !node->GetChildren() )
+ {
+ // In the typical, simple case, <object_ref> is used to link
+ // to another node and doesn't have any content of its own that
+ // would overwrite linked object's properties. In this case,
+ // we can simply create the resource from linked node.
+
+ return CreateResFromNode(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 CreateResFromNode(©, parent, instance);
+ return CreateResFromNode(©, parent, instance);
+ }
}
if (handlerToUse)
}
}
- wxLogError(_("No handler found for XML node '%s', class '%s'!"),
- node->GetName().c_str(),
- node->GetAttribute(wxT("class"), wxEmptyString).c_str());
+ ReportError
+ (
+ node,
+ wxString::Format
+ (
+ "no handler found for XML node \"%s\" (class \"%s\")",
+ node->GetName(),
+ node->GetAttribute("class", wxEmptyString)
+ )
+ );
return NULL;
}
if (!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
+ )
+ );
}
}
}
fl = tkn.GetNextToken();
index = m_styleNames.Index(fl);
if (index != wxNOT_FOUND)
+ {
style |= m_styleValues[index];
+ }
else
- wxLogError(_("Unknown style flag ") + fl);
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("unknown style flag \"%s\"", fl)
+ );
+ }
}
return style;
}
if (clr.Ok())
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
+{
+
+// if 'param' has stock_id/stock_client, extracts them and returns true
+bool GetStockArtAttrs(const wxXmlNode *paramNode,
+ const wxString& defaultArtClient,
+ wxString& art_id, wxString& art_client)
+{
+ if ( paramNode )
+ {
+ art_id = paramNode->GetAttribute("stock_id", "");
+
+ if ( !art_id.empty() )
+ {
+ art_id = wxART_MAKE_ART_ID_FROM_STR(art_id);
+
+ art_client = paramNode->GetAttribute("stock_client", "");
+ if ( art_client.empty() )
+ art_client = defaultArtClient;
+ else
+ art_client = wxART_MAKE_CLIENT_ID_FROM_STR(art_client);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+} // anonymous namespace
wxBitmap wxXmlResourceHandler::GetBitmap(const wxString& param,
const wxArtClient& defaultArtClient,
wxSize size)
{
- /* If the bitmap is specified as stock item, query wxArtProvider for it: */
- wxXmlNode *bmpNode = GetParamNode(param);
- if ( bmpNode )
+ // it used to be possible to pass an empty string here to indicate that the
+ // bitmap name should be read from this node itself but this is not
+ // supported any more because GetBitmap(m_node) can be used directly
+ // instead
+ wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" );
+
+ const wxXmlNode* const node = GetParamNode(param);
+
+ if ( !node )
{
- wxString sid = bmpNode->GetAttribute(wxT("stock_id"), wxEmptyString);
- if ( !sid.empty() )
- {
- wxString scl = bmpNode->GetAttribute(wxT("stock_client"), wxEmptyString);
- if (scl.empty())
- scl = defaultArtClient;
- else
- scl = wxART_MAKE_CLIENT_ID_FROM_STR(scl);
+ // this is not an error as bitmap parameter could be optional
+ return wxNullBitmap;
+ }
- wxBitmap stockArt =
- wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(sid),
- scl, size);
- if ( stockArt.Ok() )
- return stockArt;
- }
+ return GetBitmap(node, defaultArtClient, size);
+}
+
+wxBitmap wxXmlResourceHandler::GetBitmap(const wxXmlNode* node,
+ const wxArtClient& defaultArtClient,
+ wxSize size)
+{
+ wxCHECK_MSG( node, wxNullBitmap, "bitmap node can't be NULL" );
+
+ /* If the bitmap is specified as stock item, query wxArtProvider for it: */
+ wxString art_id, art_client;
+ if ( GetStockArtAttrs(node, defaultArtClient,
+ art_id, art_client) )
+ {
+ wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size));
+ if ( stockArt.Ok() )
+ return stockArt;
}
/* ...or load the bitmap from file: */
- wxString name = GetParamValue(param);
+ wxString name = GetParamValue(node);
if (name.empty()) return wxNullBitmap;
#if wxUSE_FILESYSTEM
wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, 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()));
if (!img.Ok())
{
- 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);
wxIcon wxXmlResourceHandler::GetIcon(const wxString& param,
const wxArtClient& defaultArtClient,
wxSize size)
+{
+ // see comment in GetBitmap(wxString) overload
+ wxASSERT_MSG( !param.empty(), "icon parameter name can't be empty" );
+
+ const wxXmlNode* const node = GetParamNode(param);
+
+ if ( !node )
+ {
+ // this is not an error as icon parameter could be optional
+ return wxIcon();
+ }
+
+ return GetIcon(node, defaultArtClient, size);
+}
+
+wxIcon wxXmlResourceHandler::GetIcon(const wxXmlNode* node,
+ const wxArtClient& defaultArtClient,
+ wxSize size)
{
wxIcon icon;
- icon.CopyFromBitmap(GetBitmap(param, defaultArtClient, size));
+ icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
return icon;
}
+wxIconBundle wxXmlResourceHandler::GetIconBundle(const wxString& param,
+ const wxArtClient& defaultArtClient)
+{
+ wxString art_id, art_client;
+ if ( GetStockArtAttrs(GetParamNode(param), defaultArtClient,
+ art_id, art_client) )
+ {
+ wxIconBundle stockArt(wxArtProvider::GetIconBundle(art_id, art_client));
+ if ( stockArt.IsOk() )
+ return stockArt;
+ }
+
+ const wxString name = GetParamValue(param);
+ if ( name.empty() )
+ return wxNullIconBundle;
+
+#if wxUSE_FILESYSTEM
+ wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
+ if ( fsfile == NULL )
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("cannot open icon resource \"%s\"", name)
+ );
+ return wxNullIconBundle;
+ }
+
+ wxIconBundle bundle(*(fsfile->GetStream()));
+ delete fsfile;
+#else
+ wxIconBundle bundle(name);
+#endif
+
+ if ( !bundle.IsOk() )
+ {
+ ReportParamError
+ (
+ param,
+ wxString::Format("cannot create icon from \"%s\"", name)
+ );
+ return wxNullIconBundle;
+ }
+
+ return bundle;
+}
+
+
+wxImageList *wxXmlResourceHandler::GetImageList(const wxString& param)
+{
+ wxXmlNode * const imagelist_node = GetParamNode(param);
+ if ( !imagelist_node )
+ return NULL;
+
+ wxXmlNode * const oldnode = m_node;
+ m_node = imagelist_node;
+
+ // size
+ wxSize size = GetSize();
+ size.SetDefaults(wxSize(wxSystemSettings::GetMetric(wxSYS_ICON_X),
+ wxSystemSettings::GetMetric(wxSYS_ICON_Y)));
+
+ // mask: true by default
+ bool mask = HasParam(wxT("mask")) ? GetBool(wxT("mask"), true) : true;
+
+ // now we have everything we need to create the image list
+ wxImageList *imagelist = new wxImageList(size.x, size.y, mask);
+
+ // add images
+ wxString parambitmap = wxT("bitmap");
+ if ( HasParam(parambitmap) )
+ {
+ wxXmlNode *n = m_node->GetChildren();
+ while (n)
+ {
+ if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
+ {
+ // add icon instead of bitmap to keep the bitmap mask
+ imagelist->Add(GetIcon(n));
+ }
+ n = n->GetNext();
+ }
+ }
+
+ m_node = oldnode;
+ return imagelist;
+}
wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
{
-wxString wxXmlResourceHandler::GetNodeContent(wxXmlNode *node)
+wxString wxXmlResourceHandler::GetNodeContent(const wxXmlNode *node)
{
- wxXmlNode *n = node;
+ const wxXmlNode *n = node;
if (n == NULL) return wxEmptyString;
n = n->GetChildren();
return GetNodeContent(GetParamNode(param));
}
+wxString wxXmlResourceHandler::GetParamValue(const wxXmlNode* node)
+{
+ return GetNodeContent(node);
+}
wxSize wxXmlResourceHandler::GetSize(const wxString& 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;
}
}
else
{
- wxLogError(_("Cannot convert dialog units: dialog unknown."));
+ ReportParamError
+ (
+ param,
+ "cannot convert dialog units: dialog unknown"
+ );
return wxDefaultSize;
}
}
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;
}
}
else
{
- wxLogError(_("Cannot convert dialog units: dialog unknown."));
+ ReportParamError
+ (
+ param,
+ "cannot convert dialog units: dialog unknown"
+ );
return defaultv;
}
}
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;
}
}
+//-----------------------------------------------------------------------------
+// errors reporting
+//-----------------------------------------------------------------------------
+
+void wxXmlResourceHandler::ReportError(const wxString& message)
+{
+ m_resource->ReportError(m_node, message);
+}
+
+void wxXmlResourceHandler::ReportError(wxXmlNode *context,
+ const wxString& message)
+{
+ m_resource->ReportError(context ? context : m_node, message);
+}
+
+void wxXmlResourceHandler::ReportParamError(const wxString& param,
+ const wxString& message)
+{
+ m_resource->ReportError(GetParamNode(param), message);
+}
+
+void wxXmlResource::ReportError(wxXmlNode *context, const wxString& message)
+{
+ if ( !context )
+ {
+ DoReportError("", NULL, message);
+ return;
+ }
+
+ // We need to find out the file that 'context' is part of. Performance of
+ // this code is not critical, so we simply find the root XML node and
+ // compare it with all loaded XRC files.
+ const wxString filename = GetFileNameFromNode(context, Data());
+
+ DoReportError(filename, context, message);
+}
+void wxXmlResource::DoReportError(const wxString& xrcFile, wxXmlNode *position,
+ const wxString& message)
+{
+ const int line = position ? position->GetLineNumber() : -1;
+ wxString loc;
+ if ( !xrcFile.empty() )
+ loc = xrcFile + ':';
+ if ( line != -1 )
+ loc += wxString::Format("%d:", line);
+ if ( !loc.empty() )
+ loc += ' ';
+ wxLogError("XRC error: %s%s", loc, message);
+}
-// --------------- XRCID implementation -----------------------------
+//-----------------------------------------------------------------------------
+// XRCID implementation
+//-----------------------------------------------------------------------------
#define XRCID_TABLE_SIZE 1024
-// --------------- 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