]> git.saurik.com Git - wxWidgets.git/blame - contrib/utils/wxrc/wxrc.cpp
Applied [ 652962 ] wxProgressDialog::Show(FALSE) bug
[wxWidgets.git] / contrib / utils / wxrc / wxrc.cpp
CommitLineData
56d2f750
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: wxrc.cpp
3// Purpose: XML resource compiler
4// Author: Vaclav Slavik
5// Created: 2000/03/05
6// RCS-ID: $Id$
7// Copyright: (c) 2000 Vaclav Slavik
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
ab7ce33c 11#if defined(__GNUG__) && !defined(__APPLE__)
56d2f750
VS
12 #pragma implementation
13 #pragma interface
14#endif
15
16// For compilers that support precompilation, includes "wx/wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20 #pragma hdrstop
21#endif
22
23// for all others, include the necessary headers (this file is usually all you
24// need because it includes almost all "standard" wxWindows headers
25#ifndef WX_PRECOMP
26 #include "wx/wx.h"
27#endif
28
29#include "wx/cmdline.h"
999d9a9f 30#include "wx/xrc/xml.h"
56d2f750 31#include "wx/ffile.h"
f6853b4a
VS
32#include "wx/wfstream.h"
33
34
35
36
56d2f750 37
031dfec8 38/*
56d2f750
VS
39#if wxUSE_GUI
40#error "You must compile the resource compiler with wxBase!"
41#endif
031dfec8 42*/
56d2f750
VS
43
44class XmlResApp : public wxApp
45{
46public:
031dfec8
JS
47
48#if wxUSE_GUI
49 bool OnInit();
50#else
56d2f750 51 virtual int OnRun();
031dfec8 52#endif
56d2f750
VS
53
54private:
55
56 void ParseParams(const wxCmdLineParser& cmdline);
57 void CompileRes();
58 wxArrayString PrepareTempFiles();
f6853b4a
VS
59 void FindFilesInXML(wxXmlNode *node, wxArrayString& flist, const wxString& inputPath);
60
a7501aeb 61 wxString GetInternalFileName(const wxString& name, const wxArrayString& flist);
56d2f750
VS
62 void DeleteTempFiles(const wxArrayString& flist);
63 void MakePackageZIP(const wxArrayString& flist);
64 void MakePackageCPP(const wxArrayString& flist);
b8b8c49b 65 void MakePackagePython(const wxArrayString& flist);
c8b7a961
VS
66
67 void OutputGettext();
68 wxArrayString FindStrings();
69 wxArrayString FindStrings(wxXmlNode *node);
56d2f750 70
b8b8c49b 71 bool flagVerbose, flagCPP, flagPython, flagGettext;
56d2f750
VS
72 wxString parOutput, parFuncname, parOutputPath;
73 wxArrayString parFiles;
74 int retCode;
75};
76
77IMPLEMENT_APP(XmlResApp)
78
031dfec8
JS
79#if wxUSE_GUI
80bool XmlResApp::OnInit()
81#else
56d2f750 82int XmlResApp::OnRun()
031dfec8 83#endif
56d2f750
VS
84{
85 static const wxCmdLineEntryDesc cmdLineDesc[] =
86 {
2b5f62a0 87 { wxCMD_LINE_SWITCH, _T("h"), _T("help"), _T("show help message"),
99cd20be 88 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
2b5f62a0
VZ
89 { wxCMD_LINE_SWITCH, _T("v"), _T("verbose"), _T("be verbose") },
90 { wxCMD_LINE_SWITCH, _T("c"), _T("cpp-code"), _T("output C++ source rather than .rsc file") },
91 { wxCMD_LINE_SWITCH, _T("p"), _T("python-code"), _T("output wxPython source rather than .rsc file") },
92 { wxCMD_LINE_SWITCH, _T("g"), _T("gettext"), _T("output list of translatable strings (to stdout or file if -o used)") },
93 { wxCMD_LINE_OPTION, _T("n"), _T("function"), _T("C++/Python function name (with -c or -p) [InitXmlResource]") },
94 { wxCMD_LINE_OPTION, _T("o"), _T("output"), _T("output file [resource.xrs/cpp]") },
99cd20be 95#if 0 // not yet implemented
2b5f62a0 96 { wxCMD_LINE_OPTION, _T("l"), _T("list-of-handlers", _T("output list of neccessary handlers to this file" },
99cd20be 97#endif
2b5f62a0 98 { wxCMD_LINE_PARAM, NULL, NULL, _T("input file(s)"),
99cd20be
VS
99 wxCMD_LINE_VAL_STRING,
100 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_OPTION_MANDATORY },
56d2f750
VS
101
102 { wxCMD_LINE_NONE }
103 };
104
105 wxCmdLineParser parser(cmdLineDesc, argc, argv);
106
107 switch (parser.Parse())
108 {
109 case -1:
110 return 0;
111 break;
112
113 case 0:
114 retCode = 0;
115 ParseParams(parser);
c8b7a961
VS
116 if (flagGettext)
117 OutputGettext();
118 else
119 CompileRes();
031dfec8
JS
120#if wxUSE_GUI
121 return FALSE;
122#else
56d2f750 123 return retCode;
031dfec8 124#endif
56d2f750
VS
125 break;
126
127 default:
031dfec8
JS
128#if wxUSE_GUI
129 return FALSE;
130#else
56d2f750 131 return 1;
031dfec8 132#endif
56d2f750
VS
133 break;
134 }
135}
136
137
138
139
140void XmlResApp::ParseParams(const wxCmdLineParser& cmdline)
141{
2b5f62a0
VZ
142 flagGettext = cmdline.Found(_T("g"));
143 flagVerbose = cmdline.Found(_T("v"));
144 flagCPP = cmdline.Found(_T("c"));
145 flagPython = cmdline.Found(_T("p"));
56d2f750 146
2b5f62a0 147 if (!cmdline.Found(_T("o"), &parOutput))
c8b7a961
VS
148 {
149 if (flagGettext)
150 parOutput = wxEmptyString;
151 else
b8b8c49b
VS
152 {
153 if (flagCPP)
2b5f62a0 154 parOutput = _T("resource.cpp");
b8b8c49b 155 else if (flagPython)
2b5f62a0 156 parOutput = _T("resource.py");
b8b8c49b 157 else
2b5f62a0 158 parOutput = _T("resource.xrs");
b8b8c49b 159 }
c8b7a961 160 }
56d2f750 161 parOutputPath = wxPathOnly(parOutput);
2b5f62a0 162 if (!parOutputPath) parOutputPath = _T(".");
56d2f750 163
2b5f62a0
VZ
164 if (!cmdline.Found(_T("n"), &parFuncname))
165 parFuncname = _T("InitXmlResource");
56d2f750
VS
166
167 for (size_t i = 0; i < cmdline.GetParamCount(); i++)
168 parFiles.Add(cmdline.GetParam(i));
169}
170
171
172
173
174void XmlResApp::CompileRes()
175{
176 wxArrayString files = PrepareTempFiles();
177
178 wxRemoveFile(parOutput);
179
56d2f750
VS
180 if (!retCode)
181 {
182 if (flagCPP)
183 MakePackageCPP(files);
b8b8c49b
VS
184 else if (flagPython)
185 MakePackagePython(files);
56d2f750
VS
186 else
187 MakePackageZIP(files);
188 }
189
190 DeleteTempFiles(files);
191}
192
193
a7501aeb
VS
194wxString XmlResApp::GetInternalFileName(const wxString& name, const wxArrayString& flist)
195{
196 wxString name2 = name;
2b5f62a0
VZ
197 name2.Replace(_T(":"), _T("_"));
198 name2.Replace(_T("/"), _T("_"));
199 name2.Replace(_T("\\"), _T("_"));
200 name2.Replace(_T("*"), _T("_"));
201 name2.Replace(_T("?"), _T("_"));
a7501aeb 202
2b5f62a0 203 wxString s = wxFileNameFromPath(parOutput) + _T("$") + name2;
a7501aeb
VS
204
205 if (wxFileExists(s) && flist.Index(s) == wxNOT_FOUND)
206 {
207 for (int i = 0;; i++)
208 {
2b5f62a0 209 s.Printf(wxFileNameFromPath(parOutput) + _T("$%03i-") + name2, i);
a7501aeb
VS
210 if (!wxFileExists(s) || flist.Index(s) != wxNOT_FOUND)
211 break;
212 }
213 }
214 return s;
215}
56d2f750
VS
216
217wxArrayString XmlResApp::PrepareTempFiles()
218{
219 wxArrayString flist;
220
221 for (size_t i = 0; i < parFiles.Count(); i++)
222 {
223 if (flagVerbose)
2b5f62a0 224 wxPrintf(_T("processing ") + parFiles[i] + _T("...\n"));
56d2f750
VS
225
226 wxXmlDocument doc;
227
228 if (!doc.Load(parFiles[i]))
229 {
2b5f62a0 230 wxLogError(_T("Error parsing file ") + parFiles[i]);
56d2f750
VS
231 retCode = 1;
232 continue;
233 }
234
f6853b4a
VS
235 wxString name, ext, path;
236 wxSplitPath(parFiles[i], &path, &name, &ext);
237
238 FindFilesInXML(doc.GetRoot(), flist, path);
56d2f750 239
a7501aeb
VS
240 wxString internalName = GetInternalFileName(parFiles[i], flist);
241
2b5f62a0 242 doc.Save(parOutputPath + _T("/") + internalName);
a7501aeb 243 flist.Add(internalName);
56d2f750
VS
244 }
245
246 return flist;
247}
248
249
250
f6853b4a
VS
251// find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
252void XmlResApp::FindFilesInXML(wxXmlNode *node, wxArrayString& flist, const wxString& inputPath)
253{
2b5f62a0
VZ
254 // Is 'node' XML node element?
255 if (node == NULL) return;
256 if (node->GetType() != wxXML_ELEMENT_NODE) return;
257
258 // Does 'node' contain filename information at all?
259 bool containsFilename = (
260 // Any bitmaps:
261 (node->GetName() == _T("bitmap")) ||
262 // URLs in wxHtmlWindow:
263 (node->GetName() == _T("url")) ||
264 // wxBitmapButton:
265 (node->GetParent() != NULL &&
266 node->GetParent()->GetPropVal(_T("class"), _T("")) == _T("wxBitmapButton") &&
267 (node->GetName() == _T("focus") ||
268 node->GetName() == _T("disabled") ||
269 node->GetName() == _T("selected")))
270 );
271
272 wxXmlNode *n = node->GetChildren();
f6853b4a
VS
273 while (n)
274 {
2b5f62a0 275 if (containsFilename &&
f6853b4a 276 (n->GetType() == wxXML_TEXT_NODE ||
2b5f62a0 277 n->GetType() == wxXML_CDATA_SECTION_NODE))
f6853b4a
VS
278 {
279 wxString fullname;
2b5f62a0
VZ
280 if (wxIsAbsolutePath(n->GetContent()) || inputPath.empty())
281 fullname = n->GetContent();
282 else
283 fullname = inputPath + _T("/") + n->GetContent();
a7501aeb
VS
284
285 if (flagVerbose)
2b5f62a0
VZ
286 wxPrintf(_T("adding ") + fullname + _T("...\n"));
287
a7501aeb 288 wxString filename = GetInternalFileName(n->GetContent(), flist);
f6853b4a 289 n->SetContent(filename);
f6853b4a 290
2b5f62a0
VZ
291 if (flist.Index(filename) == wxNOT_FOUND)
292 flist.Add(filename);
f6853b4a
VS
293
294 wxFileInputStream sin(fullname);
2b5f62a0 295 wxFileOutputStream sout(parOutputPath + _T("/") + filename);
f6853b4a
VS
296 sin.Read(sout); // copy the stream
297 }
2b5f62a0 298
f6853b4a
VS
299 // subnodes:
300 if (n->GetType() == wxXML_ELEMENT_NODE)
301 FindFilesInXML(n, flist, inputPath);
2b5f62a0 302
f6853b4a
VS
303 n = n->GetNext();
304 }
305}
306
307
308
56d2f750
VS
309void XmlResApp::DeleteTempFiles(const wxArrayString& flist)
310{
311 for (size_t i = 0; i < flist.Count(); i++)
2b5f62a0 312 wxRemoveFile(parOutputPath + _T("/") + flist[i]);
56d2f750
VS
313}
314
315
316
317void XmlResApp::MakePackageZIP(const wxArrayString& flist)
318{
319 wxString files;
320
321 for (size_t i = 0; i < flist.Count(); i++)
2b5f62a0 322 files += flist[i] + _T(" ");
56d2f750
VS
323 files.RemoveLast();
324
325 if (flagVerbose)
2b5f62a0 326 wxPrintf(_T("compressing ") + parOutput + _T("...\n"));
56d2f750 327
2b5f62a0
VZ
328 if (wxExecute(_T("zip -9 -j ") + wxString(flagVerbose ? _T("") : _T("-q ")) +
329 parOutput + _T(" ") + files, TRUE) == -1)
56d2f750 330 {
2b5f62a0
VZ
331 wxLogError(_T("Unable to execute zip program. Make sure it is in the path."));
332 wxLogError(_T("You can download it at http://www.cdrom.com/pub/infozip/"));
56d2f750
VS
333 retCode = 1;
334 return;
335 }
336}
337
338
339
340
341static wxString FileToCppArray(wxString filename, int num)
342{
343 wxString output;
56d2f750 344 wxString tmp;
f6853b4a 345 wxString snum;
56d2f750
VS
346 wxFFile file(filename, "rb");
347 size_t lng = file.Length();
348
2b5f62a0
VZ
349 snum.Printf(_T("%i"), num);
350 output.Printf(_T("static size_t xml_res_size_") + snum + _T(" = %i;\n"), lng);
351 output += _T("static unsigned char xml_res_file_") + snum + _T("[] = {\n");
e066e256
VS
352 // we cannot use string literals because MSVC is dumb wannabe compiler
353 // with arbitrary limitation to 2048 strings :(
56d2f750
VS
354
355 unsigned char *buffer = new unsigned char[lng];
356 file.Read(buffer, lng);
357
f6853b4a 358 for (size_t i = 0, linelng = 0; i < lng; i++)
56d2f750 359 {
2b5f62a0
VZ
360 tmp.Printf(_T("%i"), buffer[i]);
361 if (i != 0) output << _T(',');
e066e256 362 if (linelng > 70)
f6853b4a
VS
363 {
364 linelng = 0;
2b5f62a0 365 output << _T("\n");
f6853b4a 366 }
e066e256
VS
367 output << tmp;
368 linelng += tmp.Length()+1;
56d2f750
VS
369 }
370
371 delete[] buffer;
372
2b5f62a0 373 output += _T("};\n\n");
56d2f750
VS
374
375 return output;
376}
377
378
379void XmlResApp::MakePackageCPP(const wxArrayString& flist)
380{
381 wxFFile file(parOutput, "wt");
382 size_t i;
383
384 if (flagVerbose)
2b5f62a0 385 wxPrintf(_T("creating C++ source file ") + parOutput + _T("...\n"));
56d2f750 386
2b5f62a0
VZ
387 file.Write(_T("")
388_T("//\n")
389_T("// This file was automatically generated by wxrc, do not edit by hand.\n")
390_T("//\n\n")
391_T("#include <wx/wxprec.h>\n")
392_T("\n")
393_T("#ifdef __BORLANDC__\n")
394_T(" #pragma hdrstop\n")
395_T("#endif\n")
396_T("\n")
397_T("#ifndef WX_PRECOMP\n")
398_T(" #include <wx/wx.h>\n")
399_T("#endif\n")
400_T("")
401_T("#include <wx/filesys.h>\n")
402_T("#include <wx/fs_mem.h>\n")
403_T("#include <wx/xrc/xmlres.h>\n")
404_T("#include <wx/xrc/xh_all.h>\n")
405_T("\n"));
56d2f750
VS
406
407 for (i = 0; i < flist.Count(); i++)
408 file.Write(FileToCppArray(flist[i], i));
409
2b5f62a0
VZ
410 file.Write(_T("")
411_T("void " + parFuncname + "()\n")
412_T("{\n")
413_T("\n")
414_T(" // Check for memory FS. If not present, load the handler:\n")
415_T(" {\n")
416_T(" wxMemoryFSHandler::AddFile(\"XRC_resource/dummy_file\", \"dummy one\");\n")
417_T(" wxFileSystem fsys;\n")
418_T(" wxFSFile *f = fsys.OpenFile(\"memory:XRC_resource/dummy_file\");\n")
419_T(" wxMemoryFSHandler::RemoveFile(\"XRC_resource/dummy_file\");\n")
420_T(" if (f) delete f;\n")
421_T(" else wxFileSystem::AddHandler(new wxMemoryFSHandler);\n")
422_T(" }\n")
423_T("\n"));
56d2f750
VS
424
425 for (i = 0; i < flist.Count(); i++)
426 {
427 wxString s;
2b5f62a0
VZ
428 s.Printf(_T(" wxMemoryFSHandler::AddFile(\"XRC_resource/") + flist[i] +
429 _T("\", xml_res_file_%i, xml_res_size_%i);\n"), i, i);
56d2f750
VS
430 file.Write(s);
431 }
f6853b4a
VS
432
433 for (i = 0; i < parFiles.Count(); i++)
434 {
2b5f62a0
VZ
435 file.Write(_T(" wxXmlResource::Get()->Load(\"memory:XRC_resource/") +
436 GetInternalFileName(parFiles[i], flist) + _T("\");\n"));
f6853b4a 437 }
56d2f750 438
2b5f62a0 439 file.Write(_T("}\n"));
56d2f750 440
f6853b4a 441
56d2f750 442}
c8b7a961 443
b8b8c49b
VS
444static wxString FileToPythonArray(wxString filename, int num)
445{
446 wxString output;
447 wxString tmp;
448 wxString snum;
449 wxFFile file(filename, "rb");
450 size_t lng = file.Length();
451
2b5f62a0
VZ
452 snum.Printf(_T("%i"), num);
453 output = _T(" xml_res_file_") + snum + _T(" = \"\"\"\\\n");
b8b8c49b
VS
454
455 unsigned char *buffer = new unsigned char[lng];
456 file.Read(buffer, lng);
457
458 for (size_t i = 0, linelng = 0; i < lng; i++)
459 {
460 unsigned char c = buffer[i];
461 if (c == '\n')
462 {
463 tmp = (wxChar)c;
464 linelng = 0;
465 }
466 else if (c < 32 || c > 127)
2b5f62a0 467 tmp.Printf(_T("\\x%02x"), c);
b8b8c49b 468 else if (c == '\\')
2b5f62a0 469 tmp = _T("\\\\");
b8b8c49b
VS
470 else
471 tmp = (wxChar)c;
472 if (linelng > 70)
473 {
474 linelng = 0;
2b5f62a0 475 output << _T("\\\n");
b8b8c49b
VS
476 }
477 output << tmp;
478 linelng += tmp.Length();
479 }
480
481 delete[] buffer;
482
2b5f62a0 483 output += _T("\"\"\"\n\n");
b8b8c49b
VS
484
485 return output;
486}
487
488
489void XmlResApp::MakePackagePython(const wxArrayString& flist)
490{
491 wxFFile file(parOutput, "wt");
492 size_t i;
493
494 if (flagVerbose)
2b5f62a0 495 wxPrintf(_T("creating Python source file ") + parOutput + _T("...\n"));
b8b8c49b
VS
496
497 file.Write(
2b5f62a0
VZ
498 _T("#\n")
499 _T("# This file was automatically generated by wxrc, do not edit by hand.\n")
500 _T("#\n\n")
501 _T("from wxPython.wx import *\n")
502 _T("from wxPython.xrc import *\n\n")
b8b8c49b
VS
503 );
504
505
2b5f62a0 506 file.Write(_T("def ") + parFuncname + _T("():\n"));
b8b8c49b
VS
507
508 for (i = 0; i < flist.Count(); i++)
509 file.Write(FileToPythonArray(flist[i], i));
510
511 for (i = 0; i < flist.Count(); i++)
512 {
513 wxString s;
2b5f62a0 514 s.Printf(_T(" wxXmlResource_Get().LoadFromString(xml_res_file_%i)\n"), i);
b8b8c49b
VS
515 file.Write(s);
516 }
517}
518
c8b7a961
VS
519
520
521void XmlResApp::OutputGettext()
522{
523 wxArrayString str = FindStrings();
524
525 wxFFile fout;
526 if (!parOutput) fout.Attach(stdout);
2b5f62a0 527 else fout.Open(parOutput, "wt");
c8b7a961
VS
528
529 for (size_t i = 0; i < str.GetCount(); i++)
0653d364 530 fout.Write(_T("_(\"") + str[i] + _T("\");\n"));
c8b7a961
VS
531
532 if (!parOutput) fout.Detach();
533}
534
535
536
537wxArrayString XmlResApp::FindStrings()
538{
539 wxArrayString arr, a2;
540
541 for (size_t i = 0; i < parFiles.Count(); i++)
542 {
543 if (flagVerbose)
2b5f62a0 544 wxPrintf(_T("processing ") + parFiles[i] + _T("...\n"));
c8b7a961
VS
545
546 wxXmlDocument doc;
547 if (!doc.Load(parFiles[i]))
548 {
2b5f62a0 549 wxLogError(_T("Error parsing file ") + parFiles[i]);
c8b7a961
VS
550 retCode = 1;
551 continue;
552 }
553 a2 = FindStrings(doc.GetRoot());
554 WX_APPEND_ARRAY(arr, a2);
555 }
556
557 return arr;
558}
559
560
561
c109ef11
VS
562static wxString ConvertText(const wxString& str)
563{
564 wxString str2;
565 const wxChar *dt;
566
567 for (dt = str.c_str(); *dt; dt++)
568 {
569 if (*dt == wxT('_'))
570 {
571 if ( *(++dt) == wxT('_') )
572 str2 << wxT('_');
573 else
574 str2 << wxT('&') << *dt;
575 }
576 else
577 {
578 switch (*dt)
579 {
580 case wxT('\n') : str2 << wxT("\\n"); break;
581 case wxT('\t') : str2 << wxT("\\t"); break;
582 case wxT('\r') : str2 << wxT("\\r"); break;
2b5f62a0
VZ
583 case wxT('\\') : if ((*(dt+1) != 'n') &&
584 (*(dt+1) != 't') &&
585 (*(dt+1) != 'r'))
586 str2 << wxT("\\\\");
587 else
588 str2 << wxT("\\");
589 break;
904a226c 590 case wxT('"') : str2 << wxT("\\\""); break;
c109ef11
VS
591 default : str2 << *dt; break;
592 }
593 }
594 }
595
596 return str2;
597}
598
599
c8b7a961
VS
600wxArrayString XmlResApp::FindStrings(wxXmlNode *node)
601{
602 wxArrayString arr;
603
604 wxXmlNode *n = node;
605 if (n == NULL) return arr;
606 n = n->GetChildren();
607
608 while (n)
609 {
610 if ((node->GetType() == wxXML_ELEMENT_NODE) &&
611 // parent is an element, i.e. has subnodes...
612 (n->GetType() == wxXML_TEXT_NODE ||
613 n->GetType() == wxXML_CDATA_SECTION_NODE) &&
614 // ...it is textnode...
615 (
616 node/*not n!*/->GetName() == _T("label") ||
617 (node/*not n!*/->GetName() == _T("value") &&
618 !n->GetContent().IsNumber()) ||
619 node/*not n!*/->GetName() == _T("help") ||
620 node/*not n!*/->GetName() == _T("longhelp") ||
621 node/*not n!*/->GetName() == _T("tooltip") ||
622 node/*not n!*/->GetName() == _T("htmlcode") ||
0653d364
VS
623 node/*not n!*/->GetName() == _T("title") ||
624 node/*not n!*/->GetName() == _T("item")
c8b7a961 625 ))
c109ef11 626 // ...and known to contain translatable string
c8b7a961 627 {
c109ef11 628 arr.Add(ConvertText(n->GetContent()));
c8b7a961
VS
629 }
630
631 // subnodes:
632 if (n->GetType() == wxXML_ELEMENT_NODE)
633 {
634 wxArrayString a2 = FindStrings(n);
635 WX_APPEND_ARRAY(arr, a2);
636 }
637
638 n = n->GetNext();
639 }
640 return arr;
641}