1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: XML resource compiler
4 // Author: Vaclav Slavik
7 // Copyright: (c) 2000 Vaclav Slavik
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
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 (this file is usually all you
24 // need because it includes almost all "standard" wxWindows headers
29 #include "wx/cmdline.h"
30 #include "wx/xrc/xml.h"
32 #include "wx/wfstream.h"
40 #error "You must compile the resource compiler with wxBase!"
44 class XmlResApp
: public wxApp
56 void ParseParams(const wxCmdLineParser
& cmdline
);
58 wxArrayString
PrepareTempFiles();
59 void FindFilesInXML(wxXmlNode
*node
, wxArrayString
& flist
, const wxString
& inputPath
);
61 wxString
GetInternalFileName(const wxString
& name
, const wxArrayString
& flist
);
62 void DeleteTempFiles(const wxArrayString
& flist
);
63 void MakePackageZIP(const wxArrayString
& flist
);
64 void MakePackageCPP(const wxArrayString
& flist
);
65 void MakePackagePython(const wxArrayString
& flist
);
68 wxArrayString
FindStrings();
69 wxArrayString
FindStrings(wxXmlNode
*node
);
71 bool flagVerbose
, flagCPP
, flagPython
, flagGettext
;
72 wxString parOutput
, parFuncname
, parOutputPath
;
73 wxArrayString parFiles
;
77 IMPLEMENT_APP(XmlResApp
)
80 bool XmlResApp::OnInit()
82 int XmlResApp::OnRun()
85 static const wxCmdLineEntryDesc cmdLineDesc
[] =
87 { wxCMD_LINE_SWITCH
, "h", "help", "show help message",
88 wxCMD_LINE_VAL_NONE
, wxCMD_LINE_OPTION_HELP
},
89 { wxCMD_LINE_SWITCH
, "v", "verbose", "be verbose" },
90 { wxCMD_LINE_SWITCH
, "c", "cpp-code", "output C++ source rather than .rsc file" },
91 { wxCMD_LINE_SWITCH
, "p", "python-code", "output wxPython source rather than .rsc file" },
92 { wxCMD_LINE_SWITCH
, "g", "gettext", "output list of translatable strings (to stdout or file if -o used)" },
93 { wxCMD_LINE_OPTION
, "n", "function", "C++/Python function name (with -c or -p) [InitXmlResource]" },
94 { wxCMD_LINE_OPTION
, "o", "output", "output file [resource.xrs/cpp]" },
95 #if 0 // not yet implemented
96 { wxCMD_LINE_OPTION
, "l", "list-of-handlers", "output list of neccessary handlers to this file" },
98 { wxCMD_LINE_PARAM
, NULL
, NULL
, "input file(s)",
99 wxCMD_LINE_VAL_STRING
,
100 wxCMD_LINE_PARAM_MULTIPLE
| wxCMD_LINE_OPTION_MANDATORY
},
105 wxCmdLineParser
parser(cmdLineDesc
, argc
, argv
);
107 switch (parser
.Parse())
140 void XmlResApp::ParseParams(const wxCmdLineParser
& cmdline
)
142 flagGettext
= cmdline
.Found("g");
143 flagVerbose
= cmdline
.Found("v");
144 flagCPP
= cmdline
.Found("c");
145 flagPython
= cmdline
.Found("p");
147 if (!cmdline
.Found("o", &parOutput
))
150 parOutput
= wxEmptyString
;
154 parOutput
= "resource.cpp";
156 parOutput
= "resource.py";
158 parOutput
= "resource.xrs";
161 parOutputPath
= wxPathOnly(parOutput
);
162 if (!parOutputPath
) parOutputPath
= ".";
164 if (!cmdline
.Found("n", &parFuncname
))
165 parFuncname
= "InitXmlResource";
167 for (size_t i
= 0; i
< cmdline
.GetParamCount(); i
++)
168 parFiles
.Add(cmdline
.GetParam(i
));
174 void XmlResApp::CompileRes()
176 wxArrayString files
= PrepareTempFiles();
178 wxRemoveFile(parOutput
);
183 MakePackageCPP(files
);
185 MakePackagePython(files
);
187 MakePackageZIP(files
);
190 DeleteTempFiles(files
);
194 wxString
XmlResApp::GetInternalFileName(const wxString
& name
, const wxArrayString
& flist
)
196 wxString name2
= name
;
197 name2
.Replace(":", "_");
198 name2
.Replace("/", "_");
199 name2
.Replace("\\", "_");
200 name2
.Replace("*", "_");
201 name2
.Replace("?", "_");
203 wxString s
= wxFileNameFromPath(parOutput
) + "$" + name2
;
205 if (wxFileExists(s
) && flist
.Index(s
) == wxNOT_FOUND
)
207 for (int i
= 0;; i
++)
209 s
.Printf(wxFileNameFromPath(parOutput
) + "$%03i-" + name2
, i
);
210 if (!wxFileExists(s
) || flist
.Index(s
) != wxNOT_FOUND
)
217 wxArrayString
XmlResApp::PrepareTempFiles()
221 for (size_t i
= 0; i
< parFiles
.Count(); i
++)
224 wxPrintf("processing " + parFiles
[i
] + "...\n");
228 if (!doc
.Load(parFiles
[i
]))
230 wxLogError("Error parsing file " + parFiles
[i
]);
235 wxString name
, ext
, path
;
236 wxSplitPath(parFiles
[i
], &path
, &name
, &ext
);
238 FindFilesInXML(doc
.GetRoot(), flist
, path
);
240 wxString internalName
= GetInternalFileName(parFiles
[i
], flist
);
242 doc
.Save(parOutputPath
+ "/" + internalName
);
243 flist
.Add(internalName
);
251 // find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
252 void XmlResApp::FindFilesInXML(wxXmlNode
*node
, wxArrayString
& flist
, const wxString
& inputPath
)
255 if (n
== NULL
) return;
256 n
= n
->GetChildren();
260 if ((node
->GetType() == wxXML_ELEMENT_NODE
) &&
261 // parent is an element, i.e. has subnodes...
262 (n
->GetType() == wxXML_TEXT_NODE
||
263 n
->GetType() == wxXML_CDATA_SECTION_NODE
) &&
264 // ...it is textnode...
265 ((node
/*not n!*/->GetName() == "bitmap") ||
266 (node
/*not n!*/->GetName() == "url")))
267 // ...and known to contain filename
270 if (wxIsAbsolutePath(n
->GetContent()) || inputPath
== "") fullname
= n
->GetContent();
271 else fullname
= inputPath
+ "/" + n
->GetContent();
274 wxPrintf("adding " + fullname
+ "...\n");
276 wxString filename
= GetInternalFileName(n
->GetContent(), flist
);
277 n
->SetContent(filename
);
281 wxFileInputStream
sin(fullname
);
282 wxFileOutputStream
sout(parOutputPath
+ "/" + filename
);
283 sin
.Read(sout
); // copy the stream
287 if (n
->GetType() == wxXML_ELEMENT_NODE
)
288 FindFilesInXML(n
, flist
, inputPath
);
296 void XmlResApp::DeleteTempFiles(const wxArrayString
& flist
)
298 for (size_t i
= 0; i
< flist
.Count(); i
++)
299 wxRemoveFile(parOutputPath
+ "/" + flist
[i
]);
304 void XmlResApp::MakePackageZIP(const wxArrayString
& flist
)
308 for (size_t i
= 0; i
< flist
.Count(); i
++)
309 files
+= flist
[i
] + " ";
313 wxPrintf("compressing " + parOutput
+ "...\n");
315 if (wxExecute("zip -9 -j " + wxString(flagVerbose
? "" : "-q ") +
316 parOutput
+ " " + files
, TRUE
) == -1)
318 wxLogError("Unable to execute zip program. Make sure it is in the path.");
319 wxLogError("You can download it at http://www.cdrom.com/pub/infozip/");
328 static wxString
FileToCppArray(wxString filename
, int num
)
333 wxFFile
file(filename
, "rb");
334 size_t lng
= file
.Length();
336 snum
.Printf("%i", num
);
337 output
.Printf("static size_t xml_res_size_" + snum
+ " = %i;\n", lng
);
338 output
+= "static unsigned char xml_res_file_" + snum
+ "[] = {\n";
339 // we cannot use string literals because MSVC is dumb wannabe compiler
340 // with arbitrary limitation to 2048 strings :(
342 unsigned char *buffer
= new unsigned char[lng
];
343 file
.Read(buffer
, lng
);
345 for (size_t i
= 0, linelng
= 0; i
< lng
; i
++)
347 tmp
.Printf("%i", buffer
[i
]);
348 if (i
!= 0) output
<< ',';
355 linelng
+= tmp
.Length()+1;
366 void XmlResApp::MakePackageCPP(const wxArrayString
& flist
)
368 wxFFile
file(parOutput
, "wt");
372 wxPrintf("creating C++ source file " + parOutput
+ "...\n");
376 // This file was automatically generated by wxrc, do not edit by hand.\n\
378 #include <wx/wxprec.h>\n\
380 #ifdef __BORLANDC__\n\
384 #ifndef WX_PRECOMP\n\
385 #include <wx/wx.h>\n\
388 #include <wx/filesys.h>\n\
389 #include <wx/fs_mem.h>\n\
390 #include <wx/xrc/xmlres.h>\n\
391 #include <wx/xrc/xh_all.h>\n\
394 for (i
= 0; i
< flist
.Count(); i
++)
395 file
.Write(FileToCppArray(flist
[i
], i
));
398 void " + parFuncname
+ "()\n\
401 // Check for memory FS. If not present, load the handler:\n\
403 wxMemoryFSHandler::AddFile(\"XRC_resource/dummy_file\", \"dummy one\");\n\
404 wxFileSystem fsys;\n\
405 wxFSFile *f = fsys.OpenFile(\"memory:XRC_resource/dummy_file\");\n\
406 wxMemoryFSHandler::RemoveFile(\"XRC_resource/dummy_file\");\n\
408 else wxFileSystem::AddHandler(new wxMemoryFSHandler);\n\
412 for (i
= 0; i
< flist
.Count(); i
++)
415 s
.Printf(" wxMemoryFSHandler::AddFile(\"XRC_resource/" + flist
[i
] +
416 "\", xml_res_file_%i, xml_res_size_%i);\n", i
, i
);
420 for (i
= 0; i
< parFiles
.Count(); i
++)
422 file
.Write(" wxXmlResource::Get()->Load(\"memory:XRC_resource/" +
423 GetInternalFileName(parFiles
[i
], flist
) + "\");\n");
431 static wxString
FileToPythonArray(wxString filename
, int num
)
436 wxFFile
file(filename
, "rb");
437 size_t lng
= file
.Length();
439 snum
.Printf("%i", num
);
440 output
= " xml_res_file_" + snum
+ " = \"\"\"\\\n";
442 unsigned char *buffer
= new unsigned char[lng
];
443 file
.Read(buffer
, lng
);
445 for (size_t i
= 0, linelng
= 0; i
< lng
; i
++)
447 unsigned char c
= buffer
[i
];
453 else if (c
< 32 || c
> 127)
454 tmp
.Printf("\\x%02x", c
);
465 linelng
+= tmp
.Length();
470 output
+= "\"\"\"\n\n";
476 void XmlResApp::MakePackagePython(const wxArrayString
& flist
)
478 wxFFile
file(parOutput
, "wt");
482 wxPrintf("creating Python source file " + parOutput
+ "...\n");
486 "# This file was automatically generated by wxrc, do not edit by hand.\n"
488 "from wxPython.wx import *\n"
489 "from wxPython.xrc import *\n\n"
493 file
.Write("def " + parFuncname
+ "():\n");
495 for (i
= 0; i
< flist
.Count(); i
++)
496 file
.Write(FileToPythonArray(flist
[i
], i
));
498 for (i
= 0; i
< flist
.Count(); i
++)
501 s
.Printf(" wxXmlResource_Get().LoadFromString(xml_res_file_%i)\n", i
);
508 void XmlResApp::OutputGettext()
510 wxArrayString str
= FindStrings();
513 if (!parOutput
) fout
.Attach(stdout
);
514 else fout
.Open(parOutput
, _T("wt"));
516 for (size_t i
= 0; i
< str
.GetCount(); i
++)
517 fout
.Write(_T("_(\"") + str
[i
] + _T("\");\n"));
519 if (!parOutput
) fout
.Detach();
524 wxArrayString
XmlResApp::FindStrings()
526 wxArrayString arr
, a2
;
528 for (size_t i
= 0; i
< parFiles
.Count(); i
++)
531 wxPrintf("processing " + parFiles
[i
] + "...\n");
534 if (!doc
.Load(parFiles
[i
]))
536 wxLogError("Error parsing file " + parFiles
[i
]);
540 a2
= FindStrings(doc
.GetRoot());
541 WX_APPEND_ARRAY(arr
, a2
);
549 static wxString
ConvertText(const wxString
& str
)
554 for (dt
= str
.c_str(); *dt
; dt
++)
558 if ( *(++dt
) == wxT('_') )
561 str2
<< wxT('&') << *dt
;
567 case wxT('\n') : str2
<< wxT("\\n"); break;
568 case wxT('\t') : str2
<< wxT("\\t"); break;
569 case wxT('\r') : str2
<< wxT("\\r"); break;
570 case wxT('\\') : str2
<< wxT("\\\\"); break;
571 case wxT('"') : str2
<< wxT("\\\""); break;
572 default : str2
<< *dt
; break;
581 wxArrayString
XmlResApp::FindStrings(wxXmlNode
*node
)
586 if (n
== NULL
) return arr
;
587 n
= n
->GetChildren();
591 if ((node
->GetType() == wxXML_ELEMENT_NODE
) &&
592 // parent is an element, i.e. has subnodes...
593 (n
->GetType() == wxXML_TEXT_NODE
||
594 n
->GetType() == wxXML_CDATA_SECTION_NODE
) &&
595 // ...it is textnode...
597 node
/*not n!*/->GetName() == _T("label") ||
598 (node
/*not n!*/->GetName() == _T("value") &&
599 !n
->GetContent().IsNumber()) ||
600 node
/*not n!*/->GetName() == _T("help") ||
601 node
/*not n!*/->GetName() == _T("longhelp") ||
602 node
/*not n!*/->GetName() == _T("tooltip") ||
603 node
/*not n!*/->GetName() == _T("htmlcode") ||
604 node
/*not n!*/->GetName() == _T("title") ||
605 node
/*not n!*/->GetName() == _T("item")
607 // ...and known to contain translatable string
609 arr
.Add(ConvertText(n
->GetContent()));
613 if (n
->GetType() == wxXML_ELEMENT_NODE
)
615 wxArrayString a2
= FindStrings(n
);
616 WX_APPEND_ARRAY(arr
, a2
);