///////////////////////////////////////////////////////////////////////////// // Name: xmlres.cpp // Purpose: XML resources // Author: Vaclav Slavik // Created: 2000/03/05 // RCS-ID: $Id$ // Copyright: (c) 2000 Vaclav Slavik // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #ifdef __GNUG__ #pragma implementation "xmlres.h" #endif // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #include "wx/dialog.h" #include "wx/panel.h" #include "wx/wfstream.h" #include "wx/filesys.h" #include "wx/log.h" #include "wx/intl.h" #include "wx/tokenzr.h" #include "wx/module.h" #include "wx/xml/xml.h" #include "wx/xml/xmlres.h" #include "wx/arrimpl.cpp" WX_DEFINE_OBJARRAY(wxXmlResourceDataRecords); wxXmlResource::wxXmlResource() { m_Handlers.DeleteContents(TRUE); } wxXmlResource::wxXmlResource(const wxString& filemask, int type) { m_Handlers.DeleteContents(TRUE); Load(filemask, type); } wxXmlResource::~wxXmlResource() { ClearHandlers(); } bool wxXmlResource::Load(const wxString& filemask, int type) { wxString fnd; wxXmlResourceDataRecord *drec; #if wxUSE_FILESYSTEM wxFileSystem fsys; # define wxXmlFindFirst fsys.FindFirst(filemask, wxFILE) # define wxXmlFindNext fsys.FindNext() #else # define wxXmlFindFirst wxFindFirstFile(filemask, wxFILE) # define wxXmlFindNext wxFindNextFile() wxASSERT_MSG(type != wxXML_ARCHIVE, wxT("ZIP archive XML resources supported only with wxUSE_FILESYSTEM set to 1!")); #endif fnd = wxXmlFindFirst; while (!!fnd) { #if wxUSE_FILESYSTEM if (type == wxXML_ARCHIVE) { wxFileSystem fs2; wxString fnd2; fnd2 = fs2.FindFirst(fnd + wxT("#zip:*.xmb"), wxFILE); while (!!fnd2) { drec = new wxXmlResourceDataRecord; drec->File = fnd2; m_Data.Add(drec); fnd2 = fs2.FindNext(); } } else #endif { drec = new wxXmlResourceDataRecord; drec->File = fnd; m_Data.Add(drec); } fnd = wxXmlFindNext; } # undef wxXmlFindFirst # undef wxXmlFindNext return TRUE; } void wxXmlResource::AddHandler(wxXmlResourceHandler *handler) { m_Handlers.Append(handler); handler->SetParentResource(this); } void wxXmlResource::ClearHandlers() { m_Handlers.Clear(); } wxMenu *wxXmlResource::LoadMenu(const wxString& name) { return (wxMenu*)CreateResFromNode(FindResource(name, wxT("menu")), NULL, NULL); } wxMenuBar *wxXmlResource::LoadMenuBar(const wxString& name) { return (wxMenuBar*)CreateResFromNode(FindResource(name, wxT("menubar")), NULL, NULL); } wxDialog *wxXmlResource::LoadDialog(wxWindow *parent, const wxString& name) { wxDialog *dialog = new wxDialog; if (!LoadDialog(dialog, parent, name)) { delete dialog; return NULL; } else return dialog; } bool wxXmlResource::LoadDialog(wxDialog *dlg, wxWindow *parent, const wxString& name) { return CreateResFromNode(FindResource(name, wxT("dialog")), parent, dlg) != NULL; } wxPanel *wxXmlResource::LoadPanel(wxWindow *parent, const wxString& name) { wxPanel *panel = new wxPanel; if (!LoadPanel(panel, parent, name)) { delete panel; return NULL; } else return panel; } bool wxXmlResource::LoadPanel(wxPanel *panel, wxWindow *parent, const wxString& name) { return CreateResFromNode(FindResource(name, wxT("panel")), parent, panel) != NULL; } void wxXmlResource::UpdateResources() { bool modif; # if wxUSE_FILESYSTEM wxFSFile *file; wxFileSystem fsys; # endif for (size_t i = 0; i < m_Data.GetCount(); i++) { modif = (m_Data[i].Doc == NULL); if (!modif) { # if wxUSE_FILESYSTEM file = fsys.OpenFile(m_Data[i].File); modif = file && file->GetModificationTime() > m_Data[i].Time; if (!file) wxLogError(_("Cannot open file '%s'."), m_Data[i].File.c_str()); delete file; # else modif = wxDateTime(wxFileModificationTime(m_Data[i].File)) > m_Data[i].Time; # endif } if (modif) { wxInputStream *stream; # if wxUSE_FILESYSTEM file = fsys.OpenFile(m_Data[i].File); stream = file->GetStream(); # else stream = new wxFileInputStream(m_Data[i].File); # endif if (stream) { delete m_Data[i].Doc; m_Data[i].Doc = new wxXmlDocument; } if (!stream || !m_Data[i].Doc->Load(*stream)) wxLogError(_("Cannot load resources from file '%s'."), m_Data[i].File.c_str()); if (m_Data[i].Doc->GetRoot()->GetName() != _T("resource")) wxLogError(_("Invalid XML resource '%s': doesn't have root node 'resource'."), m_Data[i].File.c_str()); # if wxUSE_FILESYSTEM delete file; # else delete stream; # endif } } } wxXmlNode *wxXmlResource::FindResource(const wxString& name, const wxString& type) { UpdateResources(); //ensure everything is up-to-date wxString dummy; for (size_t f = 0; f < m_Data.GetCount(); f++) { for (wxXmlNode *node = m_Data[f].Doc->GetRoot()->GetChildren(); node; node = node->GetNext()) if ( node->GetType() == wxXML_ELEMENT_NODE && (!type || node->GetName() == type) && node->GetPropVal(wxT("name"), &dummy) && dummy == name && wxXmlResourceHandler::CheckPlatform(node)) return node; } wxLogError(_("XML resource '%s' (type '%s') not found!"), name.c_str(), type.c_str()); return NULL; } wxObject *wxXmlResource::CreateResFromNode(wxXmlNode *node, wxObject *parent, wxObject *instance) { if (node == NULL) return NULL; wxXmlResourceHandler *handler; wxObject *ret; wxNode * ND = m_Handlers.GetFirst(); while (ND) { handler = (wxXmlResourceHandler*)ND->GetData(); if (handler->CanHandle(node)) { ret = handler->CreateResource(node, parent, instance); if (ret) return ret; } ND = ND->GetNext(); } wxLogError(_("No handler found for XML node '%s'!"), node->GetName().c_str()); return NULL; } wxXmlResourceHandler::wxXmlResourceHandler() : m_Node(NULL), m_Parent(NULL), m_Instance(NULL), m_ParentAsWindow(NULL), m_InstanceAsWindow(NULL) {} wxObject *wxXmlResourceHandler::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance) { if (!CheckPlatform(node)) return NULL; wxXmlNode *myNode = m_Node; wxObject *myParent = m_Parent, *myInstance = m_Instance; wxWindow *myParentAW = m_ParentAsWindow, *myInstanceAW = m_InstanceAsWindow; m_Node = node; m_Parent = parent; m_Instance = instance; m_ParentAsWindow = wxDynamicCast(m_Parent, wxWindow); m_InstanceAsWindow = wxDynamicCast(m_Instance, wxWindow); wxObject *returned = DoCreateResource(); m_Node = myNode; m_Parent = myParent; m_ParentAsWindow = myParentAW; m_Instance = myInstance; m_InstanceAsWindow = myInstanceAW; return returned; } /*static*/ bool wxXmlResourceHandler::CheckPlatform(wxXmlNode *node) { wxString s; if (!node->GetPropVal(_T("platform"), &s)) return TRUE; #ifdef __WXMSW__ return s == wxString(_T("win")); #elif defined(__UNIX__) return s == wxString(_T("unix")); #elif defined(__MAC__) return s == wxString(_T("mac")); #elif defined(__OS2__) return s == wxString(_T("os2")); #else wxLogWarning(_("You're running the application on unknown platform, check for platfrom '%s' failed."), s.mb_str()); return TRUE; // unknown platform #endif } void wxXmlResourceHandler::AddStyle(const wxString& name, int value) { m_StyleNames.Add(name); m_StyleValues.Add(value); } bool wxXmlResourceHandler::HasParam(const wxString& param) { return (GetParamNode(param) != NULL); } int wxXmlResourceHandler::GetStyle(const wxString& param, int defaults) { wxString s = GetParamValue(param); if (!s) return defaults; wxStringTokenizer tkn(s, _T("| "), wxTOKEN_STRTOK); int style = 0; int index; wxString fl; while (tkn.HasMoreTokens()) { fl = tkn.GetNextToken(); index = m_StyleNames.Index(fl); if (index != wxNOT_FOUND) style |= m_StyleValues[index]; else wxLogError(_("Unknown style flag ") + fl); } return style; } wxString wxXmlResourceHandler::GetText(const wxString& param) { wxString str1 = GetParamValue(param); wxString str2; const wxChar *dt; for (dt = str1.c_str(); *dt; dt++) { // Remap $ to &, map $$ to $ (for things like "&File..." -- // this is illegal in XML, so we use "$File..."): if (*dt == '$') switch (*(++dt)) { case '$' : str2 << '$'; break; default : str2 << '&' << *dt; break; } // Remap \n to CR, \r LF, \t to TAB: else if (*dt == '\\') switch (*(++dt)) { case 'n' : str2 << '\n'; break; case 't' : str2 << '\t'; break; case 'r' : str2 << '\r'; break; default : str2 << '\\' << *dt; break; } else str2 << *dt; } return str2; } long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv) { long value; wxString str1 = GetParamValue(param); if (!str1.ToLong(&value)) value = defaultv; return value; } int wxXmlResourceHandler::GetID() { wxString sid = GetName(); long num; if (sid == _T("-1")) return -1; else if (sid.IsNumber() && sid.ToLong(&num)) return num; #define stdID(id) else if (sid == _T(#id)) return id 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_PREVIEW); stdID(wxID_ABOUT); stdID(wxID_HELP_CONTENTS); stdID(wxID_HELP_COMMANDS); stdID(wxID_HELP_PROCEDURES); stdID(wxID_CUT); stdID(wxID_COPY); stdID(wxID_PASTE); stdID(wxID_CLEAR); stdID(wxID_FIND); stdID(wxID_DUPLICATE); stdID(wxID_SELECTALL); 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_HELP_CONTEXT); #undef stdID else return XMLID(sid.c_str()); } wxString wxXmlResourceHandler::GetName() { return m_Node->GetPropVal(_T("name"), _T("-1")); } bool wxXmlResourceHandler::GetBool(const wxString& param, bool defaultv) { wxString v = GetParamValue(param); v.MakeLower(); if (!v) return defaultv; else return v == _T("1") || v == _T("t") || v == _T("yes") || v == _T("on") || v == _T("true"); } wxColour wxXmlResourceHandler::GetColour(const wxString& param) { wxString v = GetParamValue(param); unsigned long tmp = 0; if (v.Length() != 7 || v[0] != _T('#') || wxSscanf(v.c_str(), _T("#%lX"), &tmp) != 1) { wxLogError(_("XML resource: Incorrect colour specification '%s' for property '%s'."), v.c_str(), param.c_str()); return wxNullColour; } return wxColour((tmp & 0xFF0000) >> 16 , (tmp & 0x00FF00) >> 8, (tmp & 0x0000FF)); } wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param) { wxXmlNode *n = m_Node->GetChildren(); while (n) { if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param) return n; n = n->GetNext(); } return NULL; } wxString wxXmlResourceHandler::GetNodeContent(wxXmlNode *node) { 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; } wxString wxXmlResourceHandler::GetParamValue(const wxString& param) { return GetNodeContent(GetParamNode(param)); } wxSize wxXmlResourceHandler::GetSize(const wxString& param) { wxString s = GetParamValue(param); if (!s) s = _T("-1,-1"); bool is_dlg; long sx, sy; is_dlg = s[s.Length()-1] == _T('d'); if (is_dlg) s.RemoveLast(); if (!s.BeforeFirst(_T(',')).ToLong(&sx) || !s.AfterLast(_T(',')).ToLong(&sy)) { wxLogError(_("Cannot parse coordinates from '%s'."), s.mb_str()); return wxDefaultSize; } if (is_dlg) { if (m_InstanceAsWindow) return wxDLG_UNIT(m_InstanceAsWindow, wxSize(sx, sy)); else if (m_ParentAsWindow) return wxDLG_UNIT(m_ParentAsWindow, wxSize(sx, sy)); else { wxLogError(_("Cannot convert dialog units: dialog unknown.")); return wxDefaultSize; } } else return wxSize(sx, sy); } wxPoint wxXmlResourceHandler::GetPosition(const wxString& param) { wxSize sz = GetSize(param); return wxPoint(sz.x, sz.y); } void wxXmlResourceHandler::SetupWindow(wxWindow *wnd) { //FIXME : add font, cursor if (HasParam(_T("exstyle"))) wnd->SetExtraStyle(GetStyle(_T("exstyle"))); if (HasParam(_T("bg"))) wnd->SetBackgroundColour(GetColour(_T("bg"))); if (HasParam(_T("fg"))) wnd->SetForegroundColour(GetColour(_T("fg"))); if (GetBool(_T("enabled"), 1) == 0) wnd->Enable(FALSE); if (GetBool(_T("focused"), 0) == 1) wnd->SetFocus(); if (GetBool(_T("hidden"), 0) == 1) wnd->Show(FALSE); #if wxUSE_TOOLTIPS if (HasParam(_T("tooltip"))) wnd->SetToolTip(GetText(_T("tooltip"))); #endif } void wxXmlResourceHandler::CreateChildren(wxObject *parent, bool only_this_handler, wxXmlNode *children_node) { if (children_node == NULL) children_node = GetParamNode(_T("children")); if (children_node == NULL) return; wxXmlNode *n = children_node->GetChildren(); while (n) { if (n->GetType() == wxXML_ELEMENT_NODE) { if (only_this_handler) { if (CanHandle(n)) CreateResource(n, parent, NULL); } else m_Resource->CreateResFromNode(n, parent, NULL); } n = n->GetNext(); } } // --------------- XMLID implementation ----------------------------- #define XMLID_TABLE_SIZE 1024 struct XMLID_record { int id; const char *key; XMLID_record *next; }; static XMLID_record *XMLID_Records[XMLID_TABLE_SIZE] = {NULL}; static int XMLID_LastID = wxID_HIGHEST; /*static*/ int wxXmlResource::GetXMLID(const char *str_id) { int index = 0; for (const char *c = str_id; *c != '\0'; c++) index += (int)*c; index %= XMLID_TABLE_SIZE; XMLID_record *oldrec = NULL; for (XMLID_record *rec = XMLID_Records[index]; rec; rec = rec->next) { if (strcmp(rec->key, str_id) == 0) return rec->id; oldrec = rec; } XMLID_record **rec_var = (oldrec == NULL) ? &XMLID_Records[index] : &oldrec->next; *rec_var = new XMLID_record; (*rec_var)->id = ++XMLID_LastID; (*rec_var)->key = str_id; (*rec_var)->next = NULL; return (*rec_var)->id; } static void CleanXMLID_Record(XMLID_record *rec) { if (rec) { CleanXMLID_Record(rec->next); delete rec; } } static void CleanXMLID_Records() { for (int i = 0; i < XMLID_TABLE_SIZE; i++) CleanXMLID_Record(XMLID_Records[i]); } // --------------- module and globals ----------------------------- static wxXmlResource gs_XmlResource; wxXmlResource *wxTheXmlResource = &gs_XmlResource; class wxXmlResourceModule: public wxModule { DECLARE_DYNAMIC_CLASS(wxXmlResourceModule) public: wxXmlResourceModule() {} bool OnInit() {return TRUE;} void OnExit() { wxTheXmlResource->ClearHandlers(); CleanXMLID_Records(); } }; IMPLEMENT_DYNAMIC_CLASS(wxXmlResourceModule, wxModule)