]> git.saurik.com Git - wxWidgets.git/blob - contrib/utils/wxrc/wxrc.cpp
1) added wxFrame support to wxrcedit
[wxWidgets.git] / contrib / utils / wxrc / wxrc.cpp
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
11 #if defined(__GNUG__) && !defined(__APPLE__)
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"
30 #include "wx/xrc/xml.h"
31 #include "wx/ffile.h"
32 #include "wx/wfstream.h"
33
34
35
36
37
38 /*
39 #if wxUSE_GUI
40 #error "You must compile the resource compiler with wxBase!"
41 #endif
42 */
43
44 class XmlResApp : public wxApp
45 {
46 public:
47
48 #if wxUSE_GUI
49 bool OnInit();
50 #else
51 virtual int OnRun();
52 #endif
53
54 private:
55
56 void ParseParams(const wxCmdLineParser& cmdline);
57 void CompileRes();
58 wxArrayString PrepareTempFiles();
59 void FindFilesInXML(wxXmlNode *node, wxArrayString& flist, const wxString& inputPath);
60
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);
66
67 void OutputGettext();
68 wxArrayString FindStrings();
69 wxArrayString FindStrings(wxXmlNode *node);
70
71 bool flagVerbose, flagCPP, flagPython, flagGettext;
72 wxString parOutput, parFuncname, parOutputPath;
73 wxArrayString parFiles;
74 int retCode;
75 };
76
77 IMPLEMENT_APP(XmlResApp)
78
79 #if wxUSE_GUI
80 bool XmlResApp::OnInit()
81 #else
82 int XmlResApp::OnRun()
83 #endif
84 {
85 static const wxCmdLineEntryDesc cmdLineDesc[] =
86 {
87 { wxCMD_LINE_SWITCH, _T("h"), _T("help"), _T("show help message"),
88 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
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]") },
95 #if 0 // not yet implemented
96 { wxCMD_LINE_OPTION, _T("l"), _T("list-of-handlers", _T("output list of neccessary handlers to this file" },
97 #endif
98 { wxCMD_LINE_PARAM, NULL, NULL, _T("input file(s)"),
99 wxCMD_LINE_VAL_STRING,
100 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_OPTION_MANDATORY },
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);
116 if (flagGettext)
117 OutputGettext();
118 else
119 CompileRes();
120 #if wxUSE_GUI
121 return FALSE;
122 #else
123 return retCode;
124 #endif
125 break;
126
127 default:
128 #if wxUSE_GUI
129 return FALSE;
130 #else
131 return 1;
132 #endif
133 break;
134 }
135 }
136
137
138
139
140 void XmlResApp::ParseParams(const wxCmdLineParser& cmdline)
141 {
142 flagGettext = cmdline.Found(_T("g"));
143 flagVerbose = cmdline.Found(_T("v"));
144 flagCPP = cmdline.Found(_T("c"));
145 flagPython = cmdline.Found(_T("p"));
146
147 if (!cmdline.Found(_T("o"), &parOutput))
148 {
149 if (flagGettext)
150 parOutput = wxEmptyString;
151 else
152 {
153 if (flagCPP)
154 parOutput = _T("resource.cpp");
155 else if (flagPython)
156 parOutput = _T("resource.py");
157 else
158 parOutput = _T("resource.xrs");
159 }
160 }
161 parOutputPath = wxPathOnly(parOutput);
162 if (!parOutputPath) parOutputPath = _T(".");
163
164 if (!cmdline.Found(_T("n"), &parFuncname))
165 parFuncname = _T("InitXmlResource");
166
167 for (size_t i = 0; i < cmdline.GetParamCount(); i++)
168 parFiles.Add(cmdline.GetParam(i));
169 }
170
171
172
173
174 void XmlResApp::CompileRes()
175 {
176 wxArrayString files = PrepareTempFiles();
177
178 wxRemoveFile(parOutput);
179
180 if (!retCode)
181 {
182 if (flagCPP)
183 MakePackageCPP(files);
184 else if (flagPython)
185 MakePackagePython(files);
186 else
187 MakePackageZIP(files);
188 }
189
190 DeleteTempFiles(files);
191 }
192
193
194 wxString XmlResApp::GetInternalFileName(const wxString& name, const wxArrayString& flist)
195 {
196 wxString name2 = name;
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("_"));
202
203 wxString s = wxFileNameFromPath(parOutput) + _T("$") + name2;
204
205 if (wxFileExists(s) && flist.Index(s) == wxNOT_FOUND)
206 {
207 for (int i = 0;; i++)
208 {
209 s.Printf(wxFileNameFromPath(parOutput) + _T("$%03i-") + name2, i);
210 if (!wxFileExists(s) || flist.Index(s) != wxNOT_FOUND)
211 break;
212 }
213 }
214 return s;
215 }
216
217 wxArrayString XmlResApp::PrepareTempFiles()
218 {
219 wxArrayString flist;
220
221 for (size_t i = 0; i < parFiles.Count(); i++)
222 {
223 if (flagVerbose)
224 wxPrintf(_T("processing ") + parFiles[i] + _T("...\n"));
225
226 wxXmlDocument doc;
227
228 if (!doc.Load(parFiles[i]))
229 {
230 wxLogError(_T("Error parsing file ") + parFiles[i]);
231 retCode = 1;
232 continue;
233 }
234
235 wxString name, ext, path;
236 wxSplitPath(parFiles[i], &path, &name, &ext);
237
238 FindFilesInXML(doc.GetRoot(), flist, path);
239
240 wxString internalName = GetInternalFileName(parFiles[i], flist);
241
242 doc.Save(parOutputPath + _T("/") + internalName);
243 flist.Add(internalName);
244 }
245
246 return flist;
247 }
248
249
250
251 // find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
252 void XmlResApp::FindFilesInXML(wxXmlNode *node, wxArrayString& flist, const wxString& inputPath)
253 {
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();
273 while (n)
274 {
275 if (containsFilename &&
276 (n->GetType() == wxXML_TEXT_NODE ||
277 n->GetType() == wxXML_CDATA_SECTION_NODE))
278 {
279 wxString fullname;
280 if (wxIsAbsolutePath(n->GetContent()) || inputPath.empty())
281 fullname = n->GetContent();
282 else
283 fullname = inputPath + _T("/") + n->GetContent();
284
285 if (flagVerbose)
286 wxPrintf(_T("adding ") + fullname + _T("...\n"));
287
288 wxString filename = GetInternalFileName(n->GetContent(), flist);
289 n->SetContent(filename);
290
291 if (flist.Index(filename) == wxNOT_FOUND)
292 flist.Add(filename);
293
294 wxFileInputStream sin(fullname);
295 wxFileOutputStream sout(parOutputPath + _T("/") + filename);
296 sin.Read(sout); // copy the stream
297 }
298
299 // subnodes:
300 if (n->GetType() == wxXML_ELEMENT_NODE)
301 FindFilesInXML(n, flist, inputPath);
302
303 n = n->GetNext();
304 }
305 }
306
307
308
309 void XmlResApp::DeleteTempFiles(const wxArrayString& flist)
310 {
311 for (size_t i = 0; i < flist.Count(); i++)
312 wxRemoveFile(parOutputPath + _T("/") + flist[i]);
313 }
314
315
316
317 void XmlResApp::MakePackageZIP(const wxArrayString& flist)
318 {
319 wxString files;
320
321 for (size_t i = 0; i < flist.Count(); i++)
322 files += flist[i] + _T(" ");
323 files.RemoveLast();
324
325 if (flagVerbose)
326 wxPrintf(_T("compressing ") + parOutput + _T("...\n"));
327
328 if (wxExecute(_T("zip -9 -j ") + wxString(flagVerbose ? _T("") : _T("-q ")) +
329 parOutput + _T(" ") + files, TRUE) == -1)
330 {
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/"));
333 retCode = 1;
334 return;
335 }
336 }
337
338
339
340
341 static wxString FileToCppArray(wxString filename, int num)
342 {
343 wxString output;
344 wxString tmp;
345 wxString snum;
346 wxFFile file(filename, "rb");
347 size_t lng = file.Length();
348
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");
352 // we cannot use string literals because MSVC is dumb wannabe compiler
353 // with arbitrary limitation to 2048 strings :(
354
355 unsigned char *buffer = new unsigned char[lng];
356 file.Read(buffer, lng);
357
358 for (size_t i = 0, linelng = 0; i < lng; i++)
359 {
360 tmp.Printf(_T("%i"), buffer[i]);
361 if (i != 0) output << _T(',');
362 if (linelng > 70)
363 {
364 linelng = 0;
365 output << _T("\n");
366 }
367 output << tmp;
368 linelng += tmp.Length()+1;
369 }
370
371 delete[] buffer;
372
373 output += _T("};\n\n");
374
375 return output;
376 }
377
378
379 void XmlResApp::MakePackageCPP(const wxArrayString& flist)
380 {
381 wxFFile file(parOutput, "wt");
382 size_t i;
383
384 if (flagVerbose)
385 wxPrintf(_T("creating C++ source file ") + parOutput + _T("...\n"));
386
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"));
406
407 for (i = 0; i < flist.Count(); i++)
408 file.Write(FileToCppArray(flist[i], i));
409
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"));
424
425 for (i = 0; i < flist.Count(); i++)
426 {
427 wxString s;
428 s.Printf(_T(" wxMemoryFSHandler::AddFile(\"XRC_resource/") + flist[i] +
429 _T("\", xml_res_file_%i, xml_res_size_%i);\n"), i, i);
430 file.Write(s);
431 }
432
433 for (i = 0; i < parFiles.Count(); i++)
434 {
435 file.Write(_T(" wxXmlResource::Get()->Load(\"memory:XRC_resource/") +
436 GetInternalFileName(parFiles[i], flist) + _T("\");\n"));
437 }
438
439 file.Write(_T("}\n"));
440
441
442 }
443
444 static 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
452 snum.Printf(_T("%i"), num);
453 output = _T(" xml_res_file_") + snum + _T(" = \"\"\"\\\n");
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)
467 tmp.Printf(_T("\\x%02x"), c);
468 else if (c == '\\')
469 tmp = _T("\\\\");
470 else
471 tmp = (wxChar)c;
472 if (linelng > 70)
473 {
474 linelng = 0;
475 output << _T("\\\n");
476 }
477 output << tmp;
478 linelng += tmp.Length();
479 }
480
481 delete[] buffer;
482
483 output += _T("\"\"\"\n\n");
484
485 return output;
486 }
487
488
489 void XmlResApp::MakePackagePython(const wxArrayString& flist)
490 {
491 wxFFile file(parOutput, "wt");
492 size_t i;
493
494 if (flagVerbose)
495 wxPrintf(_T("creating Python source file ") + parOutput + _T("...\n"));
496
497 file.Write(
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")
503 );
504
505
506 file.Write(_T("def ") + parFuncname + _T("():\n"));
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;
514 s.Printf(_T(" wxXmlResource_Get().LoadFromString(xml_res_file_%i)\n"), i);
515 file.Write(s);
516 }
517 }
518
519
520
521 void XmlResApp::OutputGettext()
522 {
523 wxArrayString str = FindStrings();
524
525 wxFFile fout;
526 if (!parOutput) fout.Attach(stdout);
527 else fout.Open(parOutput, "wt");
528
529 for (size_t i = 0; i < str.GetCount(); i++)
530 fout.Write(_T("_(\"") + str[i] + _T("\");\n"));
531
532 if (!parOutput) fout.Detach();
533 }
534
535
536
537 wxArrayString XmlResApp::FindStrings()
538 {
539 wxArrayString arr, a2;
540
541 for (size_t i = 0; i < parFiles.Count(); i++)
542 {
543 if (flagVerbose)
544 wxPrintf(_T("processing ") + parFiles[i] + _T("...\n"));
545
546 wxXmlDocument doc;
547 if (!doc.Load(parFiles[i]))
548 {
549 wxLogError(_T("Error parsing file ") + parFiles[i]);
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
562 static 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;
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;
590 case wxT('"') : str2 << wxT("\\\""); break;
591 default : str2 << *dt; break;
592 }
593 }
594 }
595
596 return str2;
597 }
598
599
600 wxArrayString 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") ||
623 node/*not n!*/->GetName() == _T("title") ||
624 node/*not n!*/->GetName() == _T("item")
625 ))
626 // ...and known to contain translatable string
627 {
628 arr.Add(ConvertText(n->GetContent()));
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 }