1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: XML resource compiler
4 // Author: Vaclav Slavik, Eduardo Marques <edrdo@netcabo.pt>
7 // Copyright: (c) 2000 Vaclav Slavik
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #if defined(__GNUG__) && !defined(__APPLE__)
12 #pragma implementation
16 // For compilers that support precompilation, includes "wx/wx.h".
17 #include "wx/wxprec.h"
23 // for all others, include the necessary headers
29 #include "wx/cmdline.h"
30 #include "wx/xml/xml.h"
32 #include "wx/filename.h"
33 #include "wx/wfstream.h"
39 XRCWidgetData(const wxString
& vname
,const wxString
& vclass
)
40 : m_class(vclass
), m_name(vname
) {}
41 const wxString
& GetName() const { return m_name
; }
42 const wxString
& GetClass() const { return m_class
; }
47 #include "wx/arrimpl.cpp"
48 WX_DECLARE_OBJARRAY(XRCWidgetData
,ArrayOfXRCWidgetData
);
49 WX_DEFINE_OBJARRAY(ArrayOfXRCWidgetData
);
55 wxString m_parentClassName
;
56 ArrayOfXRCWidgetData m_wdata
;
58 void BrowseXmlNode(wxXmlNode
* node
)
65 if (node
->GetName() == _T("object")
66 && node
->GetPropVal(_T("class"),&classValue
)
67 && node
->GetPropVal(_T("name"),&nameValue
))
69 m_wdata
.Add(XRCWidgetData(nameValue
,classValue
));
71 children
= node
->GetChildren();
73 BrowseXmlNode(children
);
74 node
= node
->GetNext();
79 XRCWndClassData(const wxString
& className
,const wxString
& parentClassName
, const wxXmlNode
* node
) :
80 m_className(className
) , m_parentClassName(parentClassName
) {
82 BrowseXmlNode(node
->GetChildren());
86 const ArrayOfXRCWidgetData
& GetWidgetData(){
89 void GenerateHeaderCode(wxFFile
& file
){
91 file
.Write(_T("class ") + m_className
+ _T(" : public ") + m_parentClassName
92 + _T(" {\nprotected:\n"));
94 for(i
=0;i
<m_wdata
.Count();++i
){
95 const XRCWidgetData
& w
= m_wdata
.Item(i
);
97 _T(" ") + w
.GetClass() + _T("* ") + w
.GetName()
100 file
.Write(_T("\nprivate:\n void InitWidgetsFromXRC(){\n")
101 _T(" wxXmlResource::Get()->LoadObject(this,NULL,\"")
106 for(i
=0;i
<m_wdata
.Count();++i
){
107 const XRCWidgetData
& w
= m_wdata
.Item(i
);
110 + _T(" = XRCCTRL(*this,\"")
117 file
.Write(_T(" }\n"));
125 + _T(" InitWidgetsFromXRC();\n")
130 WX_DECLARE_OBJARRAY(XRCWndClassData
,ArrayOfXRCWndClassData
);
131 WX_DEFINE_OBJARRAY(ArrayOfXRCWndClassData
);
134 class XmlResApp
: public wxAppConsole
137 // don't use builtin cmd line parsing:
138 virtual bool OnInit() { return true; }
142 void ParseParams(const wxCmdLineParser
& cmdline
);
144 wxArrayString
PrepareTempFiles();
145 void FindFilesInXML(wxXmlNode
*node
, wxArrayString
& flist
, const wxString
& inputPath
);
147 wxString
GetInternalFileName(const wxString
& name
, const wxArrayString
& flist
);
148 void DeleteTempFiles(const wxArrayString
& flist
);
149 void MakePackageZIP(const wxArrayString
& flist
);
150 void MakePackageCPP(const wxArrayString
& flist
);
151 void MakePackagePython(const wxArrayString
& flist
);
153 void OutputGettext();
154 wxArrayString
FindStrings();
155 wxArrayString
FindStrings(wxXmlNode
*node
);
157 bool flagVerbose
, flagCPP
, flagPython
, flagGettext
;
158 wxString parOutput
, parFuncname
, parOutputPath
;
159 wxArrayString parFiles
;
162 ArrayOfXRCWndClassData aXRCWndClassData
;
167 IMPLEMENT_APP_CONSOLE(XmlResApp
)
169 int XmlResApp::OnRun()
171 static const wxCmdLineEntryDesc cmdLineDesc
[] =
173 { wxCMD_LINE_SWITCH
, _T("h"), _T("help"), _T("show help message"),
174 wxCMD_LINE_VAL_NONE
, wxCMD_LINE_OPTION_HELP
},
175 { wxCMD_LINE_SWITCH
, _T("v"), _T("verbose"), _T("be verbose") },
176 { wxCMD_LINE_SWITCH
, _T("e"), _T("extra-cpp-code"), _T("output C++ header file with XRC derived classes") },
177 { wxCMD_LINE_SWITCH
, _T("c"), _T("cpp-code"), _T("output C++ source rather than .rsc file") },
178 { wxCMD_LINE_SWITCH
, _T("p"), _T("python-code"), _T("output wxPython source rather than .rsc file") },
179 { wxCMD_LINE_SWITCH
, _T("g"), _T("gettext"), _T("output list of translatable strings (to stdout or file if -o used)") },
180 { wxCMD_LINE_OPTION
, _T("n"), _T("function"), _T("C++/Python function name (with -c or -p) [InitXmlResource]") },
181 { wxCMD_LINE_OPTION
, _T("o"), _T("output"), _T("output file [resource.xrs/cpp]") },
182 #if 0 // not yet implemented
183 { wxCMD_LINE_OPTION
, _T("l"), _T("list-of-handlers", _T("output list of neccessary handlers to this file" },
185 { wxCMD_LINE_PARAM
, NULL
, NULL
, _T("input file(s)"),
186 wxCMD_LINE_VAL_STRING
,
187 wxCMD_LINE_PARAM_MULTIPLE
| wxCMD_LINE_OPTION_MANDATORY
},
192 wxCmdLineParser
parser(cmdLineDesc
, argc
, argv
);
194 switch (parser
.Parse())
199 // break is unreachable because of return earlier
212 // break is unreachable because of return earlier
217 // default return moved outside of switch to avoid warning about lack of return in function
229 void XmlResApp::ParseParams(const wxCmdLineParser
& cmdline
)
231 flagGettext
= cmdline
.Found(_T("g"));
232 flagVerbose
= cmdline
.Found(_T("v"));
233 flagCPP
= cmdline
.Found(_T("c"));
234 flagPython
= cmdline
.Found(_T("p"));
235 flagH
= flagCPP
&& cmdline
.Found(_T("e"));
238 if (!cmdline
.Found(_T("o"), &parOutput
))
241 parOutput
= wxEmptyString
;
245 parOutput
= _T("resource.cpp");
247 parOutput
= _T("resource.py");
249 parOutput
= _T("resource.xrs");
252 if (!parOutput
.empty())
254 wxFileName
fn(parOutput
);
256 parOutput
= fn
.GetFullPath();
257 parOutputPath
= wxPathOnly(parOutput
);
259 if (!parOutputPath
) parOutputPath
= _T(".");
261 if (!cmdline
.Found(_T("n"), &parFuncname
))
262 parFuncname
= _T("InitXmlResource");
264 for (size_t i
= 0; i
< cmdline
.GetParamCount(); i
++)
267 wxString fn
=wxFindFirstFile(cmdline
.GetParam(i
), wxFILE
);
268 while (!fn
.IsEmpty())
274 parFiles
.Add(cmdline
.GetParam(i
));
282 void XmlResApp::CompileRes()
284 wxArrayString files
= PrepareTempFiles();
286 wxRemoveFile(parOutput
);
291 MakePackageCPP(files
);
296 MakePackagePython(files
);
298 MakePackageZIP(files
);
301 DeleteTempFiles(files
);
305 wxString
XmlResApp::GetInternalFileName(const wxString
& name
, const wxArrayString
& flist
)
307 wxString name2
= name
;
308 name2
.Replace(_T(":"), _T("_"));
309 name2
.Replace(_T("/"), _T("_"));
310 name2
.Replace(_T("\\"), _T("_"));
311 name2
.Replace(_T("*"), _T("_"));
312 name2
.Replace(_T("?"), _T("_"));
314 wxString s
= wxFileNameFromPath(parOutput
) + _T("$") + name2
;
316 if (wxFileExists(s
) && flist
.Index(s
) == wxNOT_FOUND
)
318 for (int i
= 0;; i
++)
320 s
.Printf(wxFileNameFromPath(parOutput
) + _T("$%03i-") + name2
, i
);
321 if (!wxFileExists(s
) || flist
.Index(s
) != wxNOT_FOUND
)
328 wxArrayString
XmlResApp::PrepareTempFiles()
332 for (size_t i
= 0; i
< parFiles
.Count(); i
++)
335 wxPrintf(_T("processing ") + parFiles
[i
] + _T("...\n"));
339 if (!doc
.Load(parFiles
[i
]))
341 wxLogError(_T("Error parsing file ") + parFiles
[i
]);
346 wxString name
, ext
, path
;
347 wxSplitPath(parFiles
[i
], &path
, &name
, &ext
);
349 FindFilesInXML(doc
.GetRoot(), flist
, path
);
352 wxXmlNode
* node
= (doc
.GetRoot())->GetChildren();
353 wxString classValue
,nameValue
;
355 if(node
->GetName() == _T("object")
356 && node
->GetPropVal(_T("class"),&classValue
)
357 && node
->GetPropVal(_T("name"),&nameValue
)){
359 aXRCWndClassData
.Add(
360 XRCWndClassData(nameValue
,classValue
,node
)
363 node
= node
-> GetNext();
366 wxString internalName
= GetInternalFileName(parFiles
[i
], flist
);
368 doc
.Save(parOutputPath
+ wxFILE_SEP_PATH
+ internalName
);
369 flist
.Add(internalName
);
376 // Does 'node' contain filename information at all?
377 static bool NodeContainsFilename(wxXmlNode
*node
)
380 if (node
->GetName() == _T("bitmap"))
383 // URLs in wxHtmlWindow:
384 if (node
->GetName() == _T("url"))
388 wxXmlNode
*parent
= node
->GetParent();
389 if (parent
!= NULL
&&
390 parent
->GetPropVal(_T("class"), _T("")) == _T("wxBitmapButton") &&
391 (node
->GetName() == _T("focus") ||
392 node
->GetName() == _T("disabled") ||
393 node
->GetName() == _T("selected")))
396 // wxBitmap or wxIcon toplevel resources:
397 if (node
->GetName() == _T("object"))
399 wxString klass
= node
->GetPropVal(_T("class"), wxEmptyString
);
400 if (klass
== _T("wxBitmap") || klass
== _T("wxIcon"))
407 // find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
408 void XmlResApp::FindFilesInXML(wxXmlNode
*node
, wxArrayString
& flist
, const wxString
& inputPath
)
410 // Is 'node' XML node element?
411 if (node
== NULL
) return;
412 if (node
->GetType() != wxXML_ELEMENT_NODE
) return;
414 bool containsFilename
= NodeContainsFilename(node
);
416 wxXmlNode
*n
= node
->GetChildren();
419 if (containsFilename
&&
420 (n
->GetType() == wxXML_TEXT_NODE
||
421 n
->GetType() == wxXML_CDATA_SECTION_NODE
))
424 if (wxIsAbsolutePath(n
->GetContent()) || inputPath
.empty())
425 fullname
= n
->GetContent();
427 fullname
= inputPath
+ wxFILE_SEP_PATH
+ n
->GetContent();
430 wxPrintf(_T("adding ") + fullname
+ _T("...\n"));
432 wxString filename
= GetInternalFileName(n
->GetContent(), flist
);
433 n
->SetContent(filename
);
435 if (flist
.Index(filename
) == wxNOT_FOUND
)
438 wxFileInputStream
sin(fullname
);
439 wxFileOutputStream
sout(parOutputPath
+ wxFILE_SEP_PATH
+ filename
);
440 sin
.Read(sout
); // copy the stream
444 if (n
->GetType() == wxXML_ELEMENT_NODE
)
445 FindFilesInXML(n
, flist
, inputPath
);
453 void XmlResApp::DeleteTempFiles(const wxArrayString
& flist
)
455 for (size_t i
= 0; i
< flist
.Count(); i
++)
456 wxRemoveFile(parOutputPath
+ wxFILE_SEP_PATH
+ flist
[i
]);
461 void XmlResApp::MakePackageZIP(const wxArrayString
& flist
)
465 for (size_t i
= 0; i
< flist
.Count(); i
++)
466 files
+= flist
[i
] + _T(" ");
470 wxPrintf(_T("compressing ") + parOutput
+ _T("...\n"));
472 wxString cwd
= wxGetCwd();
473 wxSetWorkingDirectory(parOutputPath
);
474 int execres
= wxExecute(_T("zip -9 -j ") +
475 wxString(flagVerbose
? _T("") : _T("-q ")) +
476 parOutput
+ _T(" ") + files
, TRUE
);
477 wxSetWorkingDirectory(cwd
);
480 wxLogError(_T("Unable to execute zip program. Make sure it is in the path."));
481 wxLogError(_T("You can download it at http://www.cdrom.com/pub/infozip/"));
489 static wxString
FileToCppArray(wxString filename
, int num
)
494 wxFFile
file(filename
, wxT("rb"));
495 size_t lng
= file
.Length();
497 snum
.Printf(_T("%i"), num
);
498 output
.Printf(_T("static size_t xml_res_size_") + snum
+ _T(" = %i;\n"), lng
);
499 output
+= _T("static unsigned char xml_res_file_") + snum
+ _T("[] = {\n");
500 // we cannot use string literals because MSVC is dumb wannabe compiler
501 // with arbitrary limitation to 2048 strings :(
503 unsigned char *buffer
= new unsigned char[lng
];
504 file
.Read(buffer
, lng
);
506 for (size_t i
= 0, linelng
= 0; i
< lng
; i
++)
508 tmp
.Printf(_T("%i"), buffer
[i
]);
509 if (i
!= 0) output
<< _T(',');
516 linelng
+= tmp
.Length()+1;
521 output
+= _T("};\n\n");
527 void XmlResApp::MakePackageCPP(const wxArrayString
& flist
)
529 wxFFile
file(parOutput
, wxT("wt"));
533 wxPrintf(_T("creating C++ source file ") + parOutput
+ _T("...\n"));
537 _T("// This file was automatically generated by wxrc, do not edit by hand.\n")
539 _T("#include <wx/wxprec.h>\n")
541 _T("#ifdef __BORLANDC__\n")
542 _T(" #pragma hdrstop\n")
545 _T("#ifndef WX_PRECOMP\n")
546 _T(" #include <wx/wx.h>\n")
549 _T("#include <wx/filesys.h>\n")
550 _T("#include <wx/fs_mem.h>\n")
551 _T("#include <wx/xrc/xmlres.h>\n")
552 _T("#include <wx/xrc/xh_all.h>\n")
555 for (i
= 0; i
< flist
.Count(); i
++)
557 FileToCppArray(parOutputPath
+ wxFILE_SEP_PATH
+ flist
[i
], i
));
560 _T("void ") + parFuncname
+ wxT("()\n")
563 _T(" // Check for memory FS. If not present, load the handler:\n")
565 _T(" wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/dummy_file\"), wxT(\"dummy one\"));\n")
566 _T(" wxFileSystem fsys;\n")
567 _T(" wxFSFile *f = fsys.OpenFile(wxT(\"memory:XRC_resource/dummy_file\"));\n")
568 _T(" wxMemoryFSHandler::RemoveFile(wxT(\"XRC_resource/dummy_file\"));\n")
569 _T(" if (f) delete f;\n")
570 _T(" else wxFileSystem::AddHandler(new wxMemoryFSHandler);\n")
574 for (i
= 0; i
< flist
.Count(); i
++)
577 s
.Printf(_T(" wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/") + flist
[i
] +
578 _T("\"), xml_res_file_%i, xml_res_size_%i);\n"), i
, i
);
582 for (i
= 0; i
< parFiles
.Count(); i
++)
584 file
.Write(_T(" wxXmlResource::Get()->Load(wxT(\"memory:XRC_resource/") +
585 GetInternalFileName(parFiles
[i
], flist
) + _T("\"));\n"));
588 file
.Write(_T("}\n"));
593 void XmlResApp::GenCPPHeader()
595 wxString fileSpec
= (parOutput
.BeforeLast('.')).AfterLast('/');
596 wxString heaFileName
= fileSpec
+ _T(".h");
598 wxFFile
file(heaFileName
, wxT("wt"));
601 _T("// This file was automatically generated by wxrc, do not edit by hand.\n")
603 _T("#ifndef __") + fileSpec
+ _T("_h__\n")
604 _T("#define __") + fileSpec
+ _T("_h__\n")
606 for(size_t i
=0;i
<aXRCWndClassData
.Count();++i
){
607 aXRCWndClassData
.Item(i
).GenerateHeaderCode(file
);
612 + _T("();\n#endif\n"));
615 static wxString
FileToPythonArray(wxString filename
, int num
)
620 wxFFile
file(filename
, wxT("rb"));
621 size_t lng
= file
.Length();
623 snum
.Printf(_T("%i"), num
);
624 output
= _T(" xml_res_file_") + snum
+ _T(" = \"\"\"\\\n");
626 unsigned char *buffer
= new unsigned char[lng
];
627 file
.Read(buffer
, lng
);
629 for (size_t i
= 0, linelng
= 0; i
< lng
; i
++)
631 unsigned char c
= buffer
[i
];
637 else if (c
< 32 || c
> 127)
638 tmp
.Printf(_T("\\x%02x"), c
);
646 output
<< _T("\\\n");
649 linelng
+= tmp
.Length();
654 output
+= _T("\"\"\"\n\n");
660 void XmlResApp::MakePackagePython(const wxArrayString
& flist
)
662 wxFFile
file(parOutput
, wxT("wt"));
666 wxPrintf(_T("creating Python source file ") + parOutput
+ _T("...\n"));
670 _T("# This file was automatically generated by wxrc, do not edit by hand.\n")
672 _T("from wxPython.wx import *\n")
673 _T("from wxPython.xrc import *\n\n")
677 file
.Write(_T("def ") + parFuncname
+ _T("():\n"));
679 for (i
= 0; i
< flist
.Count(); i
++)
681 FileToPythonArray(parOutputPath
+ wxFILE_SEP_PATH
+ flist
[i
], i
));
683 for (i
= 0; i
< flist
.Count(); i
++)
686 s
.Printf(_T(" wxXmlResource_Get().LoadFromString(xml_res_file_%i)\n"), i
);
693 void XmlResApp::OutputGettext()
695 wxArrayString str
= FindStrings();
698 if (parOutput
.empty())
701 fout
.Open(parOutput
, wxT("wt"));
703 for (size_t i
= 0; i
< str
.GetCount(); i
++)
704 fout
.Write(_T("_(\"") + str
[i
] + _T("\");\n"));
706 if (!parOutput
) fout
.Detach();
711 wxArrayString
XmlResApp::FindStrings()
713 wxArrayString arr
, a2
;
715 for (size_t i
= 0; i
< parFiles
.Count(); i
++)
718 wxPrintf(_T("processing ") + parFiles
[i
] + _T("...\n"));
721 if (!doc
.Load(parFiles
[i
]))
723 wxLogError(_T("Error parsing file ") + parFiles
[i
]);
727 a2
= FindStrings(doc
.GetRoot());
728 WX_APPEND_ARRAY(arr
, a2
);
736 static wxString
ConvertText(const wxString
& str
)
741 for (dt
= str
.c_str(); *dt
; dt
++)
745 if ( *(++dt
) == wxT('_') )
748 str2
<< wxT('&') << *dt
;
754 case wxT('\n') : str2
<< wxT("\\n"); break;
755 case wxT('\t') : str2
<< wxT("\\t"); break;
756 case wxT('\r') : str2
<< wxT("\\r"); break;
757 case wxT('\\') : if ((*(dt
+1) != 'n') &&
764 case wxT('"') : str2
<< wxT("\\\""); break;
765 default : str2
<< *dt
; break;
774 wxArrayString
XmlResApp::FindStrings(wxXmlNode
*node
)
779 if (n
== NULL
) return arr
;
780 n
= n
->GetChildren();
784 if ((node
->GetType() == wxXML_ELEMENT_NODE
) &&
785 // parent is an element, i.e. has subnodes...
786 (n
->GetType() == wxXML_TEXT_NODE
||
787 n
->GetType() == wxXML_CDATA_SECTION_NODE
) &&
788 // ...it is textnode...
790 node
/*not n!*/->GetName() == _T("label") ||
791 (node
/*not n!*/->GetName() == _T("value") &&
792 !n
->GetContent().IsNumber()) ||
793 node
/*not n!*/->GetName() == _T("help") ||
794 node
/*not n!*/->GetName() == _T("longhelp") ||
795 node
/*not n!*/->GetName() == _T("tooltip") ||
796 node
/*not n!*/->GetName() == _T("htmlcode") ||
797 node
/*not n!*/->GetName() == _T("title") ||
798 node
/*not n!*/->GetName() == _T("item")
800 // ...and known to contain translatable string
802 arr
.Add(ConvertText(n
->GetContent()));
806 if (n
->GetType() == wxXML_ELEMENT_NODE
)
808 wxArrayString a2
= FindStrings(n
);
809 WX_APPEND_ARRAY(arr
, a2
);