]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/tools/pywxrc.py
fix building/running of tex2rtf
[wxWidgets.git] / wxPython / wx / tools / pywxrc.py
index dbec81f2cefce48a9fab5631dd8b536c2c7a87c1..9093b2e12591a64e78414a82f24c2b0a7b3e07e7 100644 (file)
 
 """
 pywxrc -- Python XML resource compiler
 
 """
 pywxrc -- Python XML resource compiler
+          (see http://wiki.wxpython.org/index.cgi/pywxrc for more info)
 
 Usage: python pywxrc.py -h
 
 Usage: python pywxrc.py -h
-       python pywxrc.py <resource.xrc> [-e] [-o filename]
+       python pywxrc.py [-p] [-g] [-e] [-o filename] xrc input files... 
        
        
-  -h, --help    show help message
-  -e, --embed   embed resources in output file
-  -o, --output  output filename, or - for stdout
+  -h, --help     show help message
+  -p, --python   generate python module
+  -g, --gettext  output list of translatable strings (may be combined with -p)
+  -e, --embed    embed XRC resources in the output file
+  -o, --output   output filename, or - for stdout
 """
 
 import sys, os, getopt, glob, re
 """
 
 import sys, os, getopt, glob, re
@@ -35,6 +38,7 @@ import wx.xrc
 class PythonTemplates:
     FILE_HEADER = """\
 # This file was automatically generated by pywxrc, do not edit by hand.
 class PythonTemplates:
     FILE_HEADER = """\
 # This file was automatically generated by pywxrc, do not edit by hand.
+# -*- coding: UTF-8 -*-
 
 import wx
 import wx.xrc as xrc
 
 import wx
 import wx.xrc as xrc
@@ -51,8 +55,8 @@ def get_resources():
 """
 
     CLASS_HEADER = """\
 """
 
     CLASS_HEADER = """\
-class %(windowName)sBase(wx.%(windowClass)s):
-    def PreCreate(self):
+class xrc%(windowName)s(wx.%(windowClass)s):
+    def PreCreate(self, pre):
         \"\"\" This function is called during the class's initialization.
         
         Override it for custom setup before the window is created usually to
         \"\"\" This function is called during the class's initialization.
         
         Override it for custom setup before the window is created usually to
@@ -62,8 +66,8 @@ class %(windowName)sBase(wx.%(windowClass)s):
     def __init__(self, parent):
         # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
         pre = wx.Pre%(windowClass)s()
     def __init__(self, parent):
         # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
         pre = wx.Pre%(windowClass)s()
+        self.PreCreate(pre)
         get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
         get_resources().LoadOn%(windowClass)s(pre, parent, "%(windowName)s")
-        self.PreCreate()
         self.PostCreate(pre)
 
         # Define variables for the controls
         self.PostCreate(pre)
 
         # Define variables for the controls
@@ -73,29 +77,46 @@ class %(windowName)sBase(wx.%(windowClass)s):
         self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
 """
 
         self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
 """
 
+    MENU_CLASS_HEADER = """\
+class xrc%(windowName)s(wx.%(windowClass)s):
+    def __init__(self):
+        pre = get_resources().LoadMenu("%(windowName)s")
+
+        # This is a copy of Robin's PostCreate voodoo magic in wx.Window that
+        # relinks the self object with the menu object.
+        self.this = pre.this
+        self.thisown = pre.thisown
+        pre.thisown = 0
+        if hasattr(self, '_setOORInfo'):
+            self._setOORInfo(self)
+        if hasattr(self, '_setCallbackInfo'):
+            self._setCallbackInfo(self, self.__class__)
+
+        # Define variables for the menu items
+"""
+
+    CREATE_MENUITEM_VAR = """\
+        self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
+"""
+
     INIT_RESOURE_HEADER = """\
     INIT_RESOURE_HEADER = """\
-# -------------------------------------------------------------
 # ------------------------ Resource data ----------------------
 # ------------------------ Resource data ----------------------
-# -------------------------------------------------------------
 
 def __init_resources():
 
 def __init_resources():
+    global __res
+    __res = xrc.EmptyXmlResource()
 """
 
     LOAD_RES_FILE = """\
 """
 
     LOAD_RES_FILE = """\
-    global __res
-    __res = xrc.XmlResource('%(resourceFilename)s')
-"""
+    __res.Load('%(resourceFilename)s')"""
 
     FILE_AS_STRING = """\
     %(filename)s = '''\\
 %(fileData)s'''
 
 
     FILE_AS_STRING = """\
     %(filename)s = '''\\
 %(fileData)s'''
 
-
 """
 
     PREPARE_MEMFS = """\
 """
 
     PREPARE_MEMFS = """\
-    # Load all the strings as memory files
-    
     wx.FileSystem.AddHandler(wx.MemoryFSHandler())
 """
 
     wx.FileSystem.AddHandler(wx.MemoryFSHandler())
 """
 
@@ -104,11 +125,24 @@ def __init_resources():
 """
 
     LOAD_RES_MEMFS = """\
 """
 
     LOAD_RES_MEMFS = """\
-    global __res
-    __res = xrc.EmptyXmlResource()
     __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
 """
 
     __res.Load('memory:XRC/%(memoryPath)s/%(resourceFilename)s')
 """
 
+    GETTEXT_DUMMY_FUNC = """
+# ----------------------- Gettext strings ---------------------
+
+def __gettext_strings():
+    # This is a dummy function that lists all the strings that are used in
+    # the XRC file in the _("a string") format to be recognized by GNU
+    # gettext utilities (specificaly the xgettext utility) and the
+    # mki18n.py script.  For more information see:
+    # http://wiki.wxpython.org/index.cgi/Internationalization 
+    
+    def _(str): pass
+    
+%s
+"""
+
 #----------------------------------------------------------------------
 
 class XmlResourceCompiler:
 #----------------------------------------------------------------------
 
 class XmlResourceCompiler:
@@ -117,23 +151,66 @@ class XmlResourceCompiler:
 
     """This class generates Python code from XML resource files (XRC)."""
 
 
     """This class generates Python code from XML resource files (XRC)."""
 
-    def MakePythonModule(self, resourceFilename, outputFilename, embedResources=False):
-        if outputFilename == "-":
-            outputFile = sys.stdout
-        else:
-            try:
-                outputFile = open(outputFilename, "wt")
-            except IOError:
-                raise IOError("Can't write output to '%s'" % outputFilename)
+    def MakePythonModule(self, inputFiles, outputFilename,
+                         embedResources=False, generateGetText=False):
+
+        outputFile = self._OpenOutputFile(outputFilename)
+
+        classes = []
+        resources = []
+        gettextStrings = []
 
 
-        resourceDocument = minidom.parse(resourceFilename)
+        # process all the inputFiles, collecting the output data
+        for inFile in inputFiles:
+            resourceDocument = minidom.parse(inFile)
+            classes.append(self.GenerateClasses(resourceDocument))
+
+            if embedResources:
+                res = self.GenerateInitResourcesEmbedded(inFile, resourceDocument)
+            else:
+                res = self.GenerateInitResourcesFile(inFile, resourceDocument)
+            resources.append(res)
+
+            if generateGetText:
+                gettextStrings += self.FindStringsInNode(resourceDocument.firstChild)
+                
+        # now write it all out
         print >>outputFile, self.templates.FILE_HEADER
         print >>outputFile, self.templates.FILE_HEADER
-        print >>outputFile, self.GenerateClasses(resourceDocument)
-        
+
+        # Note: Technically it is not legal to have anything other
+        # than ascii for class and variable names, but since the user
+        # can create the XML with non-ascii names we'll go ahead and
+        # allow for it here, and then let Python complain about it
+        # later when they try to run the program.
+        classes = u"\n".join(classes)
+        print >>outputFile, classes.encode("UTF-8")
+
+        print >>outputFile, self.templates.INIT_RESOURE_HEADER
         if embedResources:
         if embedResources:
-            print >>outputFile, self.GenerateInitResourcesEmbedded(resourceFilename, resourceDocument)
-        else:
-            print >>outputFile, self.GenerateInitResourcesFile(resourceFilename, resourceDocument)
+            print >>outputFile, self.templates.PREPARE_MEMFS
+        resources = u"\n".join(resources)
+        print >>outputFile, resources.encode("UTF-8")
+
+        if generateGetText:
+            # These have already been converted to utf-8...
+            gettextStrings = ['    _("%s")' % s for s in gettextStrings]
+            gettextStrings = "\n".join(gettextStrings)
+            print >>outputFile, self.templates.GETTEXT_DUMMY_FUNC % gettextStrings
+
+    #-------------------------------------------------------------------
+
+    def MakeGetTextOutput(self, inputFiles, outputFilename):
+        """
+        Just output the gettext strings by themselves, with no other
+        code generation.
+        """
+        outputFile = self._OpenOutputFile(outputFilename)
+        for inFile in inputFiles:
+            resourceDocument = minidom.parse(inFile)
+            resource = resourceDocument.firstChild
+            strings = self.FindStringsInNode(resource)
+            strings = ['_("%s");' % s for s in strings]
+            print >>outputFile, "\n".join(strings)
 
     #-------------------------------------------------------------------
 
 
     #-------------------------------------------------------------------
 
@@ -149,7 +226,11 @@ class XmlResourceCompiler:
             windowClass = topWindow.getAttribute("class")
             windowClass = re.sub("^wx", "", windowClass)
             windowName = topWindow.getAttribute("name")
             windowClass = topWindow.getAttribute("class")
             windowClass = re.sub("^wx", "", windowClass)
             windowName = topWindow.getAttribute("name")
-            outputList.append(self.templates.CLASS_HEADER % locals())
+            
+            if windowClass in ["Menu", "MenuItem"]:
+                outputList.append(self.templates.MENU_CLASS_HEADER % locals())
+            else:
+                outputList.append(self.templates.CLASS_HEADER % locals())
             
             # Generate a variable for each control, and standard event handlers
             # for standard controls.
             
             # Generate a variable for each control, and standard event handlers
             # for standard controls.
@@ -157,11 +238,12 @@ class XmlResourceCompiler:
                 widgetClass = widget.getAttribute("class")
                 widgetClass = re.sub("^wx", "", widgetClass)
                 widgetName = widget.getAttribute("name")
                 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())
+                if (widgetName != "" and widgetClass != ""):
+                    if widgetClass == "MenuItem":
+                        outputList.append(self.templates.CREATE_MENUITEM_VAR % locals())
+                    elif widgetClass not in \
+                        ['tool', 'unknown', 'notebookpage', 'separator', 'sizeritem']:
+                        outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
             outputList.append('\n\n')
                     
         return "".join(outputList)
             outputList.append('\n\n')
                     
         return "".join(outputList)
@@ -170,9 +252,6 @@ class XmlResourceCompiler:
 
     def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
         outputList = []
 
     def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
         outputList = []
-
-        outputList.append(self.templates.INIT_RESOURE_HEADER)
-
         files = []
 
         resourcePath = os.path.split(resourceFilename)[0]
         files = []
 
         resourcePath = os.path.split(resourceFilename)[0]
@@ -182,7 +261,7 @@ class XmlResourceCompiler:
         self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
         
         filename = resourceFilename
         self.ReplaceFilenamesInXRC(resourceDocument.firstChild, files, resourcePath)
         
         filename = resourceFilename
-        fileData = resourceDocument.toxml()
+        fileData = resourceDocument.toxml() # what about this? encoding=resourceDocument.encoding)
         outputList.append(self.templates.FILE_AS_STRING % locals())
 
         for f in files:
         outputList.append(self.templates.FILE_AS_STRING % locals())
 
         for f in files:
@@ -190,8 +269,6 @@ class XmlResourceCompiler:
             fileData = self.FileToString(os.path.join(resourcePath, f))
             outputList.append(self.templates.FILE_AS_STRING % locals())
 
             fileData = self.FileToString(os.path.join(resourcePath, f))
             outputList.append(self.templates.FILE_AS_STRING % locals())
 
-        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())
         for f in [resourceFilename] + files:
             filename = self.GetMemoryFilename(f)
             outputList.append(self.templates.ADD_FILE_TO_MEMFS % locals())
@@ -203,8 +280,9 @@ class XmlResourceCompiler:
     #-------------------------------------------------------------------
 
     def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
     #-------------------------------------------------------------------
 
     def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
+        # take only the filename portion out of resourceFilename
+        resourceFilename = os.path.split(resourceFilename)[1]
         outputList = []
         outputList = []
-        outputList.append(self.templates.INIT_RESOURE_HEADER)
         outputList.append(self.templates.LOAD_RES_FILE % locals())
         return "".join(outputList)
 
         outputList.append(self.templates.LOAD_RES_FILE % locals())
         return "".join(outputList)
 
@@ -308,24 +386,122 @@ class XmlResourceCompiler:
             if n.nodeType == minidom.Document.ELEMENT_NODE:
                 self.ReplaceFilenamesInXRC(n, files, resourcePath);
 
             if n.nodeType == minidom.Document.ELEMENT_NODE:
                 self.ReplaceFilenamesInXRC(n, files, resourcePath);
 
+    #-------------------------------------------------------------------
+
+    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;
+
+        for child in parent.childNodes:
+            if ((parent.nodeType == parent.ELEMENT_NODE) and
+                # parent is an element, i.e. has subnodes...
+                (child.nodeType == child.TEXT_NODE or
+                child.nodeType == child.CDATA_SECTION_NODE) and
+                # ...it is textnode...
+                (
+                    parent.tagName == "label" or
+                    (parent.tagName == "value" and
+                                   not is_number(child.nodeValue)) or
+                    parent.tagName == "help" or
+                    parent.tagName == "longhelp" or
+                    parent.tagName == "tooltip" or
+                    parent.tagName == "htmlcode" or
+                    parent.tagName == "title" or
+                    parent.tagName == "item"
+                )):
+                # ...and known to contain translatable string
+                if (parent.getAttribute("translate") != "0"):
+                    strings.append(self.ConvertText(child.nodeValue))
+
+            # subnodes:
+            if child.nodeType == child.ELEMENT_NODE:
+                strings += self.FindStringsInNode(child)
+
+        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.encode("UTF-8")                
+
+
+    #-------------------------------------------------------------------
+
+    def _OpenOutputFile(self, outputFilename):
+        if outputFilename == "-":
+            outputFile = sys.stdout
+        else:
+            try:
+                outputFile = open(outputFilename, "wt")
+            except IOError:
+                raise IOError("Can't write output to '%s'" % outputFilename)
+        return outputFile
+
+    
+
+
+
 #---------------------------------------------------------------------------
 
 def main(args):
     resourceFilename = ""
 #---------------------------------------------------------------------------
 
 def main(args):
     resourceFilename = ""
-    outputFilename = ""
+    outputFilename = None
     embedResources = False
     embedResources = False
+    generateGetText = False
+    generatePython = False
 
     try:
 
     try:
-        opts, args = getopt.gnu_getopt(args, "heo:", "help embed output=".split())
-    except getopt.GetoptError:
+        opts, args = getopt.gnu_getopt(args,
+                                       "hpgeo:",
+                                       "help python gettext embed output=".split())
+    except getopt.GetoptError, e:
+        print "\nError : %s\n" % str(e)
         print __doc__
         sys.exit(1)
 
     # If there is no input file argument, show help and exit
         print __doc__
         sys.exit(1)
 
     # If there is no input file argument, show help and exit
-    if args:
-        resourceFilename = args[0]
-    else:
+    if not args:
         print __doc__
         print __doc__
+        print "No xrc input file was specified."
         sys.exit(1)
 
     # Parse options and arguments
         sys.exit(1)
 
     # Parse options and arguments
@@ -334,19 +510,45 @@ def main(args):
             print __doc__
             sys.exit(1)
 
             print __doc__
             sys.exit(1)
 
+        if opt in ["-p", "--python"]:
+            generatePython = True
+
         if opt in ["-o", "--output"]:
             outputFilename = val
             
         if opt in ["-e", "--embed"]:
             embedResources = True
 
         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"
+        if opt in ["-g", "--gettext"]:
+            generateGetText = True
+
+
+    # check for and expand any wildcards in the list of input files
+    inputFiles = []
+    for arg in args:
+        inputFiles += glob.glob(arg)
+
 
     comp = XmlResourceCompiler()
     
     try:
 
     comp = XmlResourceCompiler()
     
     try:
-        comp.MakePythonModule(resourceFilename, outputFilename, embedResources)
+        if generatePython:
+            if not outputFilename:
+                outputFilename = os.path.splitext(args[0])[0] + "_xrc.py"
+            comp.MakePythonModule(inputFiles, outputFilename,
+                                  embedResources, generateGetText)
+
+        elif generateGetText:
+            if not outputFilename:
+                outputFilename = '-'
+            comp.MakeGetTextOutput(inputFiles, outputFilename)
+
+        else:
+            print __doc__
+            print "One or both of -p, -g must be specified."
+            sys.exit(1)
+            
+            
     except IOError, e:
         print >>sys.stderr, "%s." % str(e)
     else:
     except IOError, e:
         print >>sys.stderr, "%s." % str(e)
     else: