]> git.saurik.com Git - wxWidgets.git/commitdiff
First pass at integrating a code generator in XRCed. Initial patch
authorRobin Dunn <robin@alldunn.com>
Sat, 8 Apr 2006 06:17:11 +0000 (06:17 +0000)
committerRobin Dunn <robin@alldunn.com>
Sat, 8 Apr 2006 06:17:11 +0000 (06:17 +0000)
from Eli Golovinsky, with lots of additional mods by me.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38618 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

wxPython/wx/tools/XRCed/globals.py
wxPython/wx/tools/XRCed/xrced.py
wxPython/wx/tools/XRCed/xrced.xrc
wxPython/wx/tools/pywxrc.py

index e731674e41e34543991359fcf6509c7065174163..e2a470d9946d905a4948a9c2c3eb8a4dec6bed2e 100644 (file)
@@ -21,7 +21,7 @@ MinWxVersion = (2,6,0)
 if wxVERSION[:3] < MinWxVersion:
     print '''\
 ******************************* WARNING **************************************
-  This version of XRCed may not work correctly on your version of wxWindows.
+  This version of XRCed may not work correctly on your version of wxWidgets.
   Please upgrade wxWindows to %d.%d.%d or higher.
 ******************************************************************************''' % MinWxVersion    
 
index ddf9add60a899030451af2f48b1b4a2fba6d304b..78b97e54b3a6b59c37c07b17ebd54b8d8da60b1b 100644 (file)
@@ -122,6 +122,9 @@ class Frame(wxFrame):
         menu.AppendSeparator()
         menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file')
         menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name')
+        self.ID_GENERATE_PYTHON = wxNewId()
+        menu.Append(self.ID_GENERATE_PYTHON, '&Generate Python...', 
+                    'Generate a Python module that uses this XRC')
         menu.AppendSeparator()
         menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application')
 
@@ -222,6 +225,7 @@ class Frame(wxFrame):
         EVT_MENU(self, wxID_OPEN, self.OnOpen)
         EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs)
         EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs)
+        EVT_MENU(self, self.ID_GENERATE_PYTHON, self.OnGeneratePython)
         EVT_MENU(self, wxID_EXIT, self.OnExit)
         # Edit
         EVT_MENU(self, wxID_UNDO, self.OnUndo)
@@ -373,6 +377,20 @@ class Frame(wxFrame):
             else:
                 dlg.Destroy()
                 return
+
+            if conf.localconf:
+                # if we already have a localconf then it needs to be
+                # copied to a new config with the new name
+                lc = conf.localconf
+                nc = self.CreateLocalConf(path)
+                flag, key, idx = lc.GetFirstEntry()
+                while flag:
+                    nc.Write(key, lc.Read(key))
+                    flag, key, idx = lc.GetNextEntry(idx)
+                conf.localconf = nc
+            else:
+                # otherwise create a new one
+                conf.localconf = self.CreateLocalConf(path)
         else:
             path = self.dataFile
         self.SetStatusText('Saving...')
@@ -384,6 +402,11 @@ class Frame(wxFrame):
                 self.Save(tmpName) # save temporary file first
                 shutil.move(tmpName, path)
                 self.dataFile = path
+                if conf.localconf.ReadBool("autogenerate", False):
+                    pypath = conf.localconf.Read("filename")
+                    embed = conf.localconf.ReadBool("embedResource", False)
+                    self.GeneratePython(self.dataFile, pypath, embed)
+                    
                 self.SetStatusText('Data saved')
                 self.SaveRecent(path)
             except IOError:
@@ -399,6 +422,28 @@ class Frame(wxFrame):
             EVT_MENU(self, newid, self.OnRecentFile)
             conf.recentfiles[newid] = path
 
+    def GeneratePython(self, dataFile, pypath, embed):
+        try:
+            import wx.tools.pywxrc
+            rescomp = wx.tools.pywxrc.XmlResourceCompiler()
+            rescomp.MakePythonModule(dataFile, pypath, embed)
+        except:
+            inf = sys.exc_info()
+            wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1])
+            wxLogError('Error generating python code : %s' % pypath)
+            raise
+        
+
+    def OnGeneratePython(self, evt):
+        if self.modified or not conf.localconf:
+            wx.MessageBox("Save the XRC file first!", "Error")
+            return
+        
+        dlg = PythonOptions(self, conf.localconf, self.dataFile)
+        dlg.ShowModal()
+        dlg.Destroy()
+
+        
     def OnExit(self, evt):
         self.Close()
 
@@ -1047,8 +1092,16 @@ Homepage: http://xrced.sourceforge.net\
                 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
         evt.Skip()
 
+
+    def CreateLocalConf(self, path):
+        name = os.path.splitext(path)[0]
+        name += '.xcfg'
+        return wx.FileConfig(localFilename=name)
+
+
     def Clear(self):
         self.dataFile = ''
+        conf.localconf = None
         undoMan.Clear()
         self.SetModified(False)
         tree.Clear()
@@ -1094,6 +1147,7 @@ Homepage: http://xrced.sourceforge.net\
             if dir: os.chdir(dir)
             tree.SetData(dom)
             self.SetTitle(progname + ': ' + os.path.basename(path))
+            conf.localconf = self.CreateLocalConf(self.dataFile)
         except:
             # Nice exception printing
             inf = sys.exc_info()
@@ -1146,12 +1200,13 @@ Homepage: http://xrced.sourceforge.net\
             self.domCopy = None
             self.SetModified(False)
             panel.SetModified(False)
+            conf.localconf.Flush()
         except:
             inf = sys.exc_info()
             wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1])
             wxLogError('Error writing file: %s' % path)
             raise
-
+            
     def AskSave(self):
         if not (self.modified or panel.IsModified()): return True
         flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
@@ -1175,6 +1230,72 @@ Homepage: http://xrced.sourceforge.net\
 
 ################################################################################
 
+class PythonOptions(wx.Dialog):
+
+    def __init__(self, parent, cfg, dataFile):
+        pre = wx.PreDialog()
+        g.frame.res.LoadOnDialog(pre, parent, "PYTHON_OPTIONS")
+        self.PostCreate(pre)
+
+        self.cfg = cfg
+        self.dataFile = dataFile
+
+        self.AutoGenerateCB = XRCCTRL(self, "AutoGenerateCB")
+        self.EmbedCB = XRCCTRL(self, "EmbedCB")
+        self.GettextCB = XRCCTRL(self, "GettextCB")
+        self.MakeXRSFileCB = XRCCTRL(self, "MakeXRSFileCB")
+        self.FileNameTC = XRCCTRL(self, "FileNameTC")
+        self.BrowseBtn = XRCCTRL(self, "BrowseBtn")
+        self.GenerateBtn = XRCCTRL(self, "GenerateBtn")
+        self.SaveOptsBtn = XRCCTRL(self, "SaveOptsBtn")
+
+        self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnGenerate, self.GenerateBtn)
+        self.Bind(wx.EVT_BUTTON, self.OnSaveOpts, self.SaveOptsBtn)
+
+        if self.cfg.Read("filename", "") != "":
+            self.FileNameTC.SetValue(self.cfg.Read("filename"))
+        else:
+            name = os.path.splitext(dataFile)[0]
+            name += '_xrc.py'
+            self.FileNameTC.SetValue(name)
+        self.AutoGenerateCB.SetValue(self.cfg.ReadBool("autogenerate", False))
+        self.EmbedCB.SetValue(self.cfg.ReadBool("embedResource", False))
+        self.MakeXRSFileCB.SetValue(self.cfg.ReadBool("makeXRS", False))
+        self.GettextCB.SetValue(self.cfg.ReadBool("genGettext", False))
+        
+                  
+    def OnBrowse(self, evt):
+        path = self.FileNameTC.GetValue()
+        dirname = os.path.abspath(os.path.dirname(path))
+        name = os.path.split(path)[1]
+        dlg = wxFileDialog(self, 'Save As', dirname, name, '*.py',
+                               wxSAVE | wxOVERWRITE_PROMPT)
+        if dlg.ShowModal() == wxID_OK:
+            path = dlg.GetPath()
+            self.FileNameTC.SetValue(path)
+        dlg.Destroy()
+    
+
+    def OnGenerate(self, evt):
+        pypath = self.FileNameTC.GetValue()
+        embed = self.EmbedCB.GetValue()
+        frame.GeneratePython(self.dataFile, pypath, embed)
+        self.OnSaveOpts()
+
+    
+    def OnSaveOpts(self, evt=None):
+        self.cfg.Write("filename", self.FileNameTC.GetValue())
+        self.cfg.WriteBool("autogenerate", self.AutoGenerateCB.GetValue())
+        self.cfg.WriteBool("embedResource", self.EmbedCB.GetValue())
+        self.cfg.WriteBool("makeXRS", self.MakeXRSFileCB.GetValue())
+        self.cfg.WriteBool("genGettext", self.GettextCB.GetValue())
+
+        self.EndModal(wx.ID_OK)
+    
+        
+################################################################################
+
 def usage():
     print >> sys.stderr, 'usage: xrced [-dhiv] [file]'
 
@@ -1210,6 +1331,7 @@ Please upgrade wxWindows to %d.%d.%d or higher.''' % MinWxVersion)
         # Settings
         global conf
         conf = g.conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE)
+        conf.localconf = None
         conf.autoRefresh = conf.ReadInt('autorefresh', True)
         pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1)
         size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
@@ -1246,7 +1368,7 @@ Please upgrade wxWindows to %d.%d.%d or higher.''' % MinWxVersion)
     def OnExit(self):
         # Write config
         global conf
-        wc = wxConfigBase_Get()
+        wc = conf
         wc.WriteInt('autorefresh', conf.autoRefresh)
         wc.WriteInt('x', conf.x)
         wc.WriteInt('y', conf.y)
index 70e7772a8499ba78d63a35b13a59aaa73fc85f13..e80599b59f38dc5d24c6947550ebaf682095dfd6 100644 (file)
       </object>
     </object>
   </object>
+  <object class="wxDialog" name="PYTHON_OPTIONS">
+    <title>Python Module Config</title>
+    <object class="wxBoxSizer">
+      <orient>wxVERTICAL</orient>
+      <object class="sizeritem">
+        <object class="wxBoxSizer">
+          <orient>wxVERTICAL</orient>
+          <object class="sizeritem">
+            <object class="wxCheckBox" name="AutoGenerateCB">
+              <label>Autogenerate Python module when saving XRC</label>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <object class="wxCheckBox" name="EmbedCB">
+              <label>Embed resource in python module</label>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <object class="wxCheckBox" name="GettextCB">
+              <label>Generate gettext strings</label>
+              <enabled>0</enabled>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <object class="wxCheckBox" name="MakeXRSFileCB">
+              <label>Generate and use compressed XRS file</label>
+              <enabled>0</enabled>
+            </object>
+          </object>
+          <object class="spacer">
+            <size>1,10</size>
+          </object>
+          <object class="sizeritem">
+            <object class="wxStaticText">
+              <label>Filename:</label>
+            </object>
+          </object>
+          <object class="sizeritem">
+            <object class="wxBoxSizer">
+              <orient>wxHORIZONTAL</orient>
+              <object class="sizeritem">
+                <object class="wxTextCtrl" name="FileNameTC">
+                  <size>500,-1</size>
+                </object>
+                <flag>wxALIGN_CENTRE_VERTICAL</flag>
+              </object>
+              <object class="spacer">
+                <size>5,0</size>
+              </object>
+              <object class="sizeritem">
+                <object class="wxButton" name="BrowseBtn">
+                  <label>Browse...</label>
+                </object>
+              </object>
+            </object>
+          </object>
+        </object>
+        <flag>wxALL</flag>
+        <border>10</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxStaticLine"/>
+        <flag>wxEXPAND</flag>
+      </object>
+      <object class="sizeritem">
+        <object class="wxBoxSizer">
+          <orient>wxHORIZONTAL</orient>
+          <object class="spacer">
+            <size>1,10</size>
+            <option>1</option>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="GenerateBtn">
+              <label>Generate module</label>
+              <default>1</default>
+            </object>
+          </object>
+          <object class="spacer">
+            <size>10,1</size>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="SaveOptsBtn">
+              <label>Save options</label>
+              <style></style>
+            </object>
+          </object>
+          <object class="spacer">
+            <size>10,1</size>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="wxID_CANCEL">
+              <label></label>
+            </object>
+          </object>
+        </object>
+        <flag>wxALL|wxEXPAND|wxALIGN_CENTRE_HORIZONTAL</flag>
+        <border>10</border>
+      </object>
+    </object>
+  </object>
 </resource>
\ No newline at end of file
index 3a92664d7a53f50b027c6f9985084c34e8588634..dbec81f2cefce48a9fab5631dd8b536c2c7a87c1 100644 (file)
 #              Ported to Python in order to not require yet another
 #              binary in wxPython distributions
 #
+#              Massive rework by Eli Golovinsky
+#
 # RCS-ID:      $Id$
 # Copyright:   (c) 2004 by Total Control Software, 2000 Vaclav Slavik
 # Licence:     wxWindows license
 #----------------------------------------------------------------------
 
 """
-pywxrc -- XML resource compiler
-
-Usage: wxrc [-h] [-v] [-e] [-c] [-p] [-g] [-n <str>] [-o <str>] input file(s)...
-  -h, --help            show help message
-  -v, --verbose         be verbose
-  -e, --extra-cpp-code  output C++ header file with XRC derived classes
-  -c, --cpp-code        output C++ source rather than .xrs file
-  -p, --python-code     output wxPython source rather than .rsc file
-  -g, --gettext         output list of translatable strings (to stdout or file if -o used)
-  -n, --function str    C++/Python function name (with -c or -p) [InitXmlResource]
-  -o, --output str      output file [resource.xrs/cpp/py]
+pywxrc -- Python XML resource compiler
+
+Usage: python pywxrc.py -h
+       python pywxrc.py <resource.xrc> [-e] [-o filename]
+       
+  -h, --help    show help message
+  -e, --embed   embed resources in output file
+  -o, --output  output filename, or - for stdout
 """
 
-import sys, os, getopt, glob
+import sys, os, getopt, glob, re
+import xml.dom.minidom as minidom
 import wx
 import wx.xrc
 
-
-#----------------------------------------------------------------------
-
-class XRCWidgetData:
-    def __init__(self, vname, vclass):
-        self.name = vname
-        self.klass = vclass
-    def GetName(self):
-        return self.name
-    def GetClass(self):
-        return self.klass
-
-
 #----------------------------------------------------------------------
 
-class XRCWndClassData:
-    def __init__(self, className, parentClassName, node):
-        self.className = className
-        self.parentClassName = parentClassName
-        self.BrowseXmlNode(node.GetChildren())
-        self.wdata = []
-
-
-    def BrowseXmlNode(self, node):
-        while node:
-            if node.GetName() == "object" and node.HasProp("class") and node.HasProp("name"):
-                classVal = node.GetPropVal("class", "")
-                nameVal  = node.GetPropVal("name", "")
-                self.wdata.append(XRCWidgetData(nameVal, classVal))
-            children = node.GetChildren()
-            if children:
-                    self.BrowseXmlNode(children)
-            node = node.GetNext()
+class PythonTemplates:
+    FILE_HEADER = """\
+# This file was automatically generated by pywxrc, do not edit by hand.
 
-            
-    def GetWidgetData(self):
-        return self.wdata
+import wx
+import wx.xrc as xrc
 
-    
-    def IsRealClass(self, name):
-        if name in ['tool', 'unknown', 'notebookpage', 'separator',
-                    'sizeritem', 'wxMenuItem']:
-            return False
-        else:
-            return True
+__res = None
 
+def get_resources():
+    \"\"\" This function provides access to the XML resources in this module.\"\"\"
+    global __res
+    if __res == None:
+        __init_resources()
+    return __res
 
-    def GenerateHeaderCode(self, file):
-        file.write("class %s : public %s {\nprotected:\n" % (self.className, self.parentClassName))
+"""
 
-        for w in self.wdata:
-            if not self.IsRealClass(w.GetClass()):
-                continue
-            if not w.GetName():
-                continue
-            file.write(" " + w.GetClass() + "* " + w.GetName() + ";\n")
+    CLASS_HEADER = """\
+class %(windowName)sBase(wx.%(windowClass)s):
+    def PreCreate(self):
+        \"\"\" This function is called during the class's initialization.
         
-        file.write("\nprivate:\n void InitWidgetsFromXRC(){\n",
-                   + "  wxXmlResource::Get()->LoadObject(this,NULL,\""
-                   +  self.className
-                   +  "\",\""
-                   +  self.parentClassName
-                   +  "\");\n");
-            
-        for w in self.wdata:
-            if not self.IsRealClass(w.GetClass()):
-                continue
-            if not w.GetName():
-                continue
-            file.write( "  "
-                        + w.GetName()
-                        + " = XRCCTRL(*this,\""
-                        + w.GetName()
-                        + "\","
-                        + w.GetClass()
-                        + ");\n")
-        file.write(" }\n")
-        file.write("public:\n"
-                   + self.className
-                   + "::"
-                   + self.className
-                   + "(){\n"
-                   + "  InitWidgetsFromXRC();\n"
-                   + " }\n"
-                   + "};\n")
-                
+        Override it for custom setup before the window is created usually to
+        set additional window styles using SetWindowStyle() and SetExtraStyle().\"\"\"
+        pass
 
+    def __init__(self, parent):
+        # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
+        pre = wx.Pre%(windowClass)s()
+        get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
+        self.PreCreate()
+        self.PostCreate(pre)
 
-#----------------------------------------------------------------------
+        # Define variables for the controls
+"""
 
+    CREATE_WIDGET_VAR = """\
+        self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
+"""
 
-class XmlResApp:
-    def __init__(self):
-        self.flagVerbose = False
-        self.flagCPP = False
-        self.flagH = False
-        self.flagPython = False
-        self.flagGettext = False
-        self.parOutput = ""
-        self.parFuncname = "InitXmlResource"
-        self.parFiles = []
-        self.aXRCWndClassData = []
-        
+    INIT_RESOURE_HEADER = """\
+# -------------------------------------------------------------
+# ------------------------ Resource data ----------------------
+# -------------------------------------------------------------
 
-    #--------------------------------------------------
-    def main(self, args):
-        try:
-            opts, args = getopt.getopt(args, "hvecpgn:o:",
-                 "help verbose extra-cpp-code cpp-code python-code gettext function= output=".split())
-        except getopt.GetoptError:
-            print __doc__
-            sys.exit(1)
+def __init_resources():
+"""
 
-        for opt, val in opts:
-            if opt in ["-h", "--help"]:
-                print __doc__
-                sys.exit(1)
+    LOAD_RES_FILE = """\
+    global __res
+    __res = xrc.XmlResource('%(resourceFilename)s')
+"""
 
-            if opt in ["-v", "--verbose"]:
-                self.flagVerbose = True
+    FILE_AS_STRING = """\
+    %(filename)s = '''\\
+%(fileData)s'''
 
-            if opt in ["-e", "--extra-cpp-code"]:
-                self.flagH = True
 
-            if opt in ["-c", "--cpp-code"]:
-                self.flagCPP = True
+"""
 
-            if opt in ["-p", "--python-code"]:
-                self.flagPython = True
+    PREPARE_MEMFS = """\
+    # Load all the strings as memory files
+    
+    wx.FileSystem.AddHandler(wx.MemoryFSHandler())
+"""
 
-            if opt in ["-g", "--gettext"]:
-                self.flagGettext = True
+    ADD_FILE_TO_MEMFS = """\
+    wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
+"""
 
-            if opt in ["-n", "--function"]:
-                self.parFuncname = val
+    LOAD_RES_MEMFS = """\
+    global __res
+    __res = xrc.EmptyXmlResource()
+    __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
+"""
 
-            if opt in ["-o", "--output"]:
-                self.parOutput = val
+#----------------------------------------------------------------------
 
-        if self.flagCPP + self.flagPython + self.flagGettext == 0:
-            print __doc__
-            print "\nYou must specify one of -c, -p or -g!\n"
-            sys.exit(1)
+class XmlResourceCompiler:
+    
+    templates = PythonTemplates()
 
-        if self.flagCPP + self.flagPython + self.flagGettext > 1:
-            print __doc__
-            print "\n-c, -p and -g are mutually exclusive, specify only 1!\n"
-            sys.exit(1)
-            
+    """This class generates Python code from XML resource files (XRC)."""
 
-        if self.parOutput:
-            self.parOutput = os.path.normpath(self.parOutput)
-            self.parOutputPath = os.path.split(self.parOutput)[0]
+    def MakePythonModule(self, resourceFilename, outputFilename, embedResources=False):
+        if outputFilename == "-":
+            outputFile = sys.stdout
         else:
-            self.parOutputPath = "."
-            if self.flagCPP:
-                self.parOutput = "resource.cpp"
-            elif self.flagPython:
-                self.parOutput = "resource.py"
-            elif self.flagGettext:
-                self.parOutput = ""
-            else:
-                self.parOutput = "resource.xrs"
-
-        if not args:
-            print __doc__
-            sys.exit(1)
-        for arg in args:
-            self.parFiles += glob.glob(arg)
+            try:
+                outputFile = open(outputFilename, "wt")
+            except IOError:
+                raise IOError("Can't write output to '%s'" % outputFilename)
 
-        self.retCode = 0
-        if self.flagGettext:
-            self.OutputGettext()
+        resourceDocument = minidom.parse(resourceFilename)
+        print >>outputFile, self.templates.FILE_HEADER
+        print >>outputFile, self.GenerateClasses(resourceDocument)
+        
+        if embedResources:
+            print >>outputFile, self.GenerateInitResourcesEmbedded(resourceFilename, resourceDocument)
         else:
-            self.CompileRes()
-
+            print >>outputFile, self.GenerateInitResourcesFile(resourceFilename, resourceDocument)
 
+    #-------------------------------------------------------------------
 
-    #--------------------------------------------------
-    def CompileRes(self):
-        files = self.PrepareTempFiles()
-        try:
-            os.unlink(self.parOutput)
-        except OSError:
-            pass
-
-        if not self.retCode:
-            if self.flagCPP:
-                self.MakePackageCPP(files)
-                if self.flagH:
-                    self.GenCPPHeader()
-
-            elif self.flagPython:
-                self.MakePackagePython(files)
-
-            else:
-                self.MakePackageZIP(files)
-
-        self.DeleteTempFiles(files)
-
-
-    #--------------------------------------------------
-    def OutputGettext(self):
-        pass
-    
-
-    #--------------------------------------------------
-    def GetInternalFileName(self, name, flist):
-        name2 = name;
-        name2 = name2.replace(":", "_")
-        name2 = name2.replace("/", "_")
-        name2 = name2.replace("\\", "_")
-        name2 = name2.replace("*", "_")
-        name2 = name2.replace("?", "_")
-
-        s = os.path.split(self.parOutput)[1] + "$" + name2
-
-        if os.path.exists(s) and s not in flist:
-            i = 0
-            while True:
-                s = os.path.split(self.parOutput)[1] + ("$%s%03d" % (name2, i))
-                if not os.path.exists(s) or s in flist:
-                    break
-        return s;
-   
-
-    #--------------------------------------------------
-    def PrepareTempFiles(self):
-        flist = []
-        for f in self.parFiles:
-            if self.flagVerbose:
-                print "processing %s..." % f
-
-            doc = wx.xrc.EmptyXmlDocument()
+    def GenerateClasses(self, resourceDocument):
+        outputList = []
+        
+        resource = resourceDocument.firstChild
+        topWindows = [e for e in resource.childNodes
+                      if e.nodeType == e.ELEMENT_NODE and e.tagName == "object"]
+        
+        # Generate a class for each top-window object (Frame, Panel, Dialog, etc.)
+        for topWindow in topWindows:
+            windowClass = topWindow.getAttribute("class")
+            windowClass = re.sub("^wx", "", windowClass)
+            windowName = topWindow.getAttribute("name")
+            outputList.append(self.templates.CLASS_HEADER % locals())
             
-            if not doc.Load(f):
-                print "Error parsing file", f
-                self.retCode = 1
-                continue
-
-            path, name = os.path.split(f)
-            name, ext = os.path.splitext(name)
-
-            self.FindFilesInXML(doc.GetRoot(), flist, path)
-            if self.flagH:
-                node = doc.GetRoot().GetChildren()
-                while node:
-                    if node.GetName() == "object" and node.HasProp("class") and node.HasProp("name"):
-                        classVal = node.GetPropVal("class", "")
-                        nameVal  = node.GetPropVal("name", "")
-                        self.aXRCWndClassData.append(XRCWidgetData(nameVal, classVal))
-                    node = node.GetNext()
-            internalName = self.GetInternalFileName(f, flist)
-
-            doc.Save(os.path.join(self.parOutputPath, internalName))
-            flist.append(internalName)
-
-        return flist
-    
-    
-    #--------------------------------------------------
-    # Does 'node' contain filename information at all?
-    def NodeContainsFilename(self, node):
-        # Any bitmaps:
-        if node.GetName() == "bitmap":
-            return True
-
-        if node.GetName() == "icon":
-            return True
-
-        # URLs in wxHtmlWindow:
-        if node.GetName() == "url":
-            return True
-
-        # wxBitmapButton:
-        parent = node.GetParent()
-        if parent != None and \
-           parent.GetPropVal("class", "") == "wxBitmapButton" and \
-           (node.GetName() == "focus" or node.etName() == "disabled" or
-            node.GetName() == "selected"):
-            return True
-
-        # wxBitmap or wxIcon toplevel resources:
-        if node.GetName() == "object":
-            klass = node.GetPropVal("class", "")
-            if klass == "wxBitmap" or klass == "wxIcon":
-                return True
-
-        return False
-
-    #--------------------------------------------------
-    # find all files mentioned in structure, e.g. <bitmap>filename</bitmap>
-    def FindFilesInXML(self, node, flist, inputPath):
-        # Is 'node' XML node element?
-        if node is None: return
-        if node.GetType() != wx.xrc.XML_ELEMENT_NODE: return
-
-        containsFilename = self.NodeContainsFilename(node);
-
-        n = node.GetChildren()
-        while n:
-            if (containsFilename and
-                (n.GetType() == wx.xrc.XML_TEXT_NODE or
-                 n.GetType() == wx.xrc.XML_CDATA_SECTION_NODE)):
-                
-                if os.path.isabs(n.GetContent()) or inputPath == "":
-                    fullname = n.GetContent()
-                else:
-                    fullname = os.path.join(inputPath, n.GetContent())
-
-                if self.flagVerbose:
-                    print "adding     %s..." % fullname
-
-                filename = self.GetInternalFileName(n.GetContent(), flist)
-                n.SetContent(filename)
-
-                if filename not in flist:
-                    flist.append(filename)
+            # Generate a variable for each control, and standard event handlers
+            # for standard controls.
+            for widget in topWindow.getElementsByTagName("object"):
+                widgetClass = widget.getAttribute("class")
+                widgetClass = re.sub("^wx", "", widgetClass)
+                widgetName = widget.getAttribute("name")
+                if (widgetName != "" and widgetClass != "" and 
+                    widgetClass not in 
+                        ['tool', 'unknown', 'notebookpage', 
+                         'separator', 'sizeritem', 'MenuItem']):
+                    outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
+            outputList.append('\n\n')
                     
-                inp = open(fullname)
-                out = open(os.path.join(self.parOutputPath, filename), "w")
-                out.write(inp.read())
-
-            # subnodes:
-            if n.GetType() == wx.xrc.XML_ELEMENT_NODE:
-                self.FindFilesInXML(n, flist, inputPath);
+        return "".join(outputList)
 
-            n = n.GetNext()
-    
+    #-------------------------------------------------------------------
 
+    def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
+        outputList = []
 
-    #--------------------------------------------------
-    def DeleteTempFiles(self, flist):
-        for f in flist:
-            os.unlink(os.path.join(self.parOutputPath, f))
+        outputList.append(self.templates.INIT_RESOURE_HEADER)
 
+        files = []
 
-    #--------------------------------------------------
-    def MakePackageZIP(self, flist):
-        files = " ".join(flist)
-
-        if self.flagVerbose:
-            print "compressing %s..." % self.parOutput
-
-        cwd = os.getcwd()
-        os.chdir(self.parOutputPath)
-        cmd = "zip -9 -j "
-        if not self.flagVerbose:
-            cmd += "-q "
-        cmd += self.parOutput + " " + files
-
-        from distutils.spawn import spawn
-        try:
-            spawn(cmd.split())
-            success = True
-        except:
-            success = False
-            
-        os.chdir(cwd)
-    
-        if not success:
-            print "Unable to execute zip program. Make sure it is in the path."
-            print "You can download it at http://www.cdrom.com/pub/infozip/"
-            self.retCode = 1
-    
-
-    #--------------------------------------------------
-    def FileToCppArray(self, filename, num):
-        output = []
-        buffer = open(filename, "rb").read()
-        lng = len(buffer)
+        resourcePath = os.path.split(resourceFilename)[0]
+        memoryPath = self.GetMemoryFilename(os.path.splitext(os.path.split(resourceFilename)[1])[0])
+        resourceFilename = self.GetMemoryFilename(os.path.split(resourceFilename)[1])
+        
+        self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
+        
+        filename = resourceFilename
+        fileData = resourceDocument.toxml()
+        outputList.append(self.templates.FILE_AS_STRING % locals())
 
-        output.append("static size_t xml_res_size_%d = %d;\n" % (num, lng))
-        output.append("static unsigned char xml_res_file_%d[] = {\n" % num)
-        # we cannot use string literals because MSVC is dumb wannabe compiler
-        # with arbitrary limitation to 2048 strings :(
+        for f in files:
+            filename = self.GetMemoryFilename(f)
+            fileData = self.FileToString(os.path.join(resourcePath, f))
+            outputList.append(self.templates.FILE_AS_STRING % locals())
 
-        linelng = 0
-        for i in xrange(lng):
-            tmp = "%i" % ord(buffer[i])
-            if i != 0: output.append(',')
-            if linelng > 70:
-                linelng = 0
-                output.append("\n")
+        outputList.append(self.templates.PREPARE_MEMFS % locals())
+        
+        for f in [resourceFilename] + files:
+            filename = self.GetMemoryFilename(f)
+            outputList.append(self.templates.ADD_FILE_TO_MEMFS % locals())
+   
+        outputList.append(self.templates.LOAD_RES_MEMFS % locals())
         
-            output.append(tmp)
-            linelng += len(tmp)+1
+        return "".join(outputList)
+        
+    #-------------------------------------------------------------------
 
-        output.append("};\n\n")
+    def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
+        outputList = []
+        outputList.append(self.templates.INIT_RESOURE_HEADER)
+        outputList.append(self.templates.LOAD_RES_FILE % locals())
+        return "".join(outputList)
 
-        return "".join(output)
+    #-------------------------------------------------------------------
 
+    def GetMemoryFilename(self, filename):
+        # Remove special chars from the filename
+        return re.sub(r"[^A-Za-z0-9_]", "_", filename)
 
-    
-    #--------------------------------------------------
-    def MakePackageCPP(self, flist):
-        file = open(self.parOutput, "wt")
-
-        if self.flagVerbose:
-            print "creating C++ source file %s..." % self.parOutput
-
-        file.write("""\
-//
-// This file was automatically generated by wxrc, do not edit by hand.
-//
-
-#include <wx/wxprec.h>
-
-#ifdef __BORLANDC__
-    #pragma hdrstop
-#endif
-
-#ifndef WX_PRECOMP
-    #include <wx/wx.h>
-#endif
-
-#include <wx/filesys.h>
-#include <wx/fs_mem.h>
-#include <wx/xrc/xmlres.h>
-#include <wx/xrc/xh_all.h>
-
-""")
-
-        num = 0
-        for f in flist:
-            file.write(self.FileToCppArray(os.path.join(self.parOutputPath, f), num))
-            num += 1
-              
-
-        file.write("void " + self.parFuncname + "()\n")
-        file.write("""\
-{
-
-    // Check for memory FS. If not present, load the handler:
-    {
-        wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/dummy_file\"), wxT(\"dummy one\"));
-        wxFileSystem fsys;
-        wxFSFile *f = fsys.OpenFile(wxT(\"memory:XRC_resource/dummy_file\"));
-        wxMemoryFSHandler::RemoveFile(wxT(\"XRC_resource/dummy_file\"));
-        if (f) delete f;
-        else wxFileSystem::AddHandler(new wxMemoryFSHandler);
-    }
-""");
-
-        for i in range(len(flist)):
-            file.write("    wxMemoryFSHandler::AddFile(wxT(\"XRC_resource/" + flist[i])
-            file.write("\"), xml_res_file_%i, xml_res_size_%i);\n" %(i, i))
-
-
-        for i in range(len(self.parFiles)):
-            file.write("    wxXmlResource::Get()->Load(wxT(\"memory:XRC_resource/" +
-                       self.GetInternalFileName(self.parFiles[i], flist) +
-                       "\"));\n")
-    
-        file.write("}\n")
-        
-    
-    #--------------------------------------------------
-    def GenCPPHeader(self):
-        path, name = os.path.split(self.parOutput)
-        name, ext = os.path.splitext(name)
-        heaFileName = name+'.h'
-
-        file = open(heaFileName, "wt")
-        file.write("""\
-//
-// This file was automatically generated by wxrc, do not edit by hand.
-//
-""");
-        file.write("#ifndef __"  + name + "_h__\n")
-        file.write("#define __"  + name + "_h__\n")
-
-        for data in self.aXRCWndClassData:
-            data.GenerateHeaderCode(file)
-
-        file.write("\nvoid \n" + self.parFuncname + "();\n#endif\n")
-        
+    #-------------------------------------------------------------------
 
-    #--------------------------------------------------
-    def FileToPythonArray(self, filename, num):
-        output = []
+    def FileToString(self, filename):
+        outputList = []
+        
         buffer = open(filename, "rb").read()
-        lng = len(buffer)
-
-        output.append("    xml_res_file_%d = '''\\\n" % num)
+        fileLen = len(buffer)
 
         linelng = 0
-        for i in xrange(lng):
+        for i in xrange(fileLen):
             s = buffer[i]
             c = ord(s)
             if s == '\n':
@@ -533,182 +238,121 @@ class XmlResApp:
 
             if linelng > 70:
                 linelng = 0
-                output.append("\\\n")
-
-            output.append(tmp)
+                outputList.append("\\\n")
+            
+            outputList.append(tmp)
             linelng += len(tmp)
+            
+        return "".join(outputList)
+            
+    #-------------------------------------------------------------------
 
-        output.append("'''\n\n")
+    def NodeContainsFilename(self, node):
+        """ Does 'node' contain filename information at all? """
 
-        return "".join(output)
+        # Any bitmaps:
+        if node.nodeName == "bitmap":
+            return True
 
-    #--------------------------------------------------
-    def MakePackagePython(self, flist):
-        file = open(self.parOutput, "wt")
+        if node.nodeName == "icon":
+            return True
 
-        if self.flagVerbose:
-            print "creating Python source file %s..." % self.parOutput
-        
-        file.write("""\
-#
-# This file was automatically generated by wxrc, do not edit by hand.
-#
+        # URLs in wxHtmlWindow:
+        if node.nodeName == "url":
+            return True
 
-import wx
-import wx.xrc
+        # wxBitmapButton:
+        parent = node.parentNode
+        if parent.__class__ != minidom.Document and \
+           parent.getAttribute("class") == "wxBitmapButton" and \
+           (node.nodeName == "focus" or node.nodeName == "disabled" or
+            node.nodeName == "selected"):
+            return True
 
-""")
-        file.write("def " + self.parFuncname + "():\n")
-        
-        num = 0
-        for f in flist:
-            file.write(self.FileToPythonArray(os.path.join(self.parOutputPath, f), num))
-            num += 1
+        # wxBitmap or wxIcon toplevel resources:
+        if node.nodeName == "object":
+            klass = node.getAttribute("class")
+            if klass == "wxBitmap" or klass == "wxIcon":
+                return True
 
-        file.write("""
-        
-    # check if the memory filesystem handler has been loaded yet, and load it if not
-    wx.MemoryFSHandler.AddFile('XRC_resource/dummy_file', 'dummy value')
-    fsys = wx.FileSystem()
-    f = fsys.OpenFile('memory:XRC_resource/dummy_file')
-    wx.MemoryFSHandler.RemoveFile('XRC_resource/dummy_file')
-    if f is not None:
-        f.Destroy()
-    else:
-        wx.FileSystem.AddHandler(wx.MemoryFSHandler())
+        return False
 
-    # load all the strings as memory files and load into XmlRes
-""")
-        
-        for i in range(len(flist)):
-            file.write("    wx.MemoryFSHandler.AddFile('XRC_resource/" + flist[i] +
-                       "', xml_res_file_%i)\n" % i)
+    #-------------------------------------------------------------------
 
-        for pf in self.parFiles:
-            file.write("    wx.xrc.XmlResource.Get().Load('memory:XRC_resource/" +
-                       self.GetInternalFileName(pf, flist) + "')\n")
-            
+    def ReplaceFilenamesInXRC(self, node, files, resourcePath):
+        """ Finds all files mentioned in resource file, e.g. <bitmap>filename</bitmap> 
+        and replaces them with the memory filenames.
+        
+        Fills a list of the filenames found."""
         
-    #--------------------------------------------------
-    def OutputGettext(self):
-        strings = self.FindStrings()
+        # Is 'node' XML node element?
+        if node is None: return
+        if node.nodeType != minidom.Document.ELEMENT_NODE: return
 
-        if not self.parOutput:
-            out = sys.stdout
-        else:
-            out = open(self.parOutput, "wt")
+        containsFilename = self.NodeContainsFilename(node);
 
-        for st in strings:
-            out.write("_(\"%s\")\n" % st)
-            
+        for n in node.childNodes:
 
-        
-    #--------------------------------------------------
-    def FindStrings(self):
-        strings = []
-        for pf in self.parFiles:
-            if self.flagVerbose:
-                print "processing %s..." % pf
-
-            doc = wx.xrc.EmptyXmlDocument()
-            if not doc.Load(pf):
-                print "Error parsing file", pf
-                self.retCode = 1
-                continue
-
-            strings += self.FindStringsInNode(doc.GetRoot())
-
-        return strings
-            
-   
-    #--------------------------------------------------
-    def ConvertText(self, st):
-        st2 = ""
-        dt = list(st)
-
-        skipNext = False
-        for i in range(len(dt)):
-            if skipNext:
-                skipNext = False
-                continue
-            
-            if dt[i] == '_':
-                if dt[i+1] == '_':
-                    st2 += '_'
-                    skipNext = True
-                else:
-                    st2 += '&'
-            elif dt[i] == '\n':
-                st2 += '\\n'
-            elif dt[i] == '\t':
-                st2 += '\\t'
-            elif dt[i] == '\r':
-                st2 += '\\r'
-            elif dt[i] == '\\':
-                if dt[i+1] not in ['n', 't', 'r']:
-                    st2 += '\\\\'
-                else:
-                    st2 += '\\'
-            elif dt[i] == '"':
-                st2 += '\\"'
-            else:            
-                st2 += dt[i]
-
-        return st2                
-             
+            if (containsFilename and
+                (n.nodeType == minidom.Document.TEXT_NODE or
+                 n.nodeType == minidom.Document.CDATA_SECTION_NODE)):
                 
-    
-    #--------------------------------------------------
-    def FindStringsInNode(self, parent):
-        def is_number(st):
-            try:
-                i = int(st)
-                return True
-            except ValueError:
-                return False
-            
-        strings = []
-        if parent is None:
-            return strings;
-        child = parent.GetChildren()
-
-        while child:
-            if ((parent.GetType() == wx.xrc.XML_ELEMENT_NODE) and
-                # parent is an element, i.e. has subnodes...
-                (child.GetType() == wx.xrc.XML_TEXT_NODE or
-                child.GetType() == wx.xrc.XML_CDATA_SECTION_NODE) and
-                # ...it is textnode...
-                (
-                    parent.GetName() == "label" or
-                    (parent.GetName() == "value" and
-                                   not is_number(child.GetContent())) or
-                    parent.GetName() == "help" or
-                    parent.GetName() == "longhelp" or
-                    parent.GetName() == "tooltip" or
-                    parent.GetName() == "htmlcode" or
-                    parent.GetName() == "title" or
-                    parent.GetName() == "item"
-                )):
-                # ...and known to contain translatable string
-                if (not self.flagGettext or
-                    parent.GetPropVal("translate", "1") != "0"):
-
-                    strings.append(self.ConvertText(child.GetContent()))
-
-            # subnodes:
-            if child.GetType() == wx.xrc.XML_ELEMENT_NODE:
-                strings += self.FindStringsInNode(child)
-
-            child = child.GetNext()
-
-        return strings
+                filename = n.nodeValue
+                memoryFilename = self.GetMemoryFilename(filename)
+                n.nodeValue = memoryFilename
+
+                if filename not in files:
+                    files.append(filename)
+
+            # Recurse into children
+            if n.nodeType == minidom.Document.ELEMENT_NODE:
+                self.ReplaceFilenamesInXRC(n, files, resourcePath);
 
 #---------------------------------------------------------------------------
 
-def main():
-    XmlResApp().main(sys.argv[1:])
+def main(args):
+    resourceFilename = ""
+    outputFilename = ""
+    embedResources = False
+
+    try:
+        opts, args = getopt.gnu_getopt(args, "heo:", "help embed output=".split())
+    except getopt.GetoptError:
+        print __doc__
+        sys.exit(1)
 
+    # If there is no input file argument, show help and exit
+    if args:
+        resourceFilename = args[0]
+    else:
+        print __doc__
+        sys.exit(1)
+
+    # Parse options and arguments
+    for opt, val in opts:
+        if opt in ["-h", "--help"]:
+            print __doc__
+            sys.exit(1)
+
+        if opt in ["-o", "--output"]:
+            outputFilename = val
+            
+        if opt in ["-e", "--embed"]:
+            embedResources = True
+
+    if outputFilename is None or outputFilename == "":
+        outputFilename = os.path.splitext(resourceFilename)[0] + "_xrc.py"
+
+    comp = XmlResourceCompiler()
+    
+    try:
+        comp.MakePythonModule(resourceFilename, outputFilename, embedResources)
+    except IOError, e:
+        print >>sys.stderr, "%s." % str(e)
+    else:
+        if outputFilename != "-":
+            print >>sys.stderr, "Resources written to %s." % outputFilename
 
 if __name__ == "__main__":
-    main()
+    main(sys.argv[1:])