#----------------------------------------------------------------------
# Name:        wx.tools.pywxrc
# Purpose:     XML resource compiler
#
# Author:      Robin Dunn
#              Based on wxrc.cpp by Vaclav Slavik, Eduardo Marques
#              Ported to Python in order to not require yet another
#              binary in wxPython distributions
#
#              Massive rework by Eli Golovinsky
#
#              Editable blocks by Roman Rolinsky
#
# RCS-ID:      $Id: pywxrc.py 51157 2008-01-11 00:19:46Z RD $
# Copyright:   (c) 2004 by Total Control Software, 2000 Vaclav Slavik
# Licence:     wxWindows license
#----------------------------------------------------------------------

"""
pywxrc -- Python XML resource compiler
          (see http://wiki.wxpython.org/index.cgi/pywxrc for more info)

Usage: python pywxrc.py -h
       python pywxrc.py [-p] [-g] [-e] [-v] [-o filename] xrc input files... 
       
  -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
  -v, --novar    suppress default assignment of variables
  -o, --output   output filename, or - for stdout
"""

import sys, os, getopt, glob, re, cPickle
import xml.dom.minidom as minidom
import wx
import wx.xrc

#----------------------------------------------------------------------

reBeginBlock = re.compile(r'^#!XRCED:begin-block:(\S+)')
reEndBlock = re.compile(r'^#!XRCED:end-block:(\S+)')

class PythonTemplates:
    FILE_HEADER = """\
# This file was automatically generated by pywxrc.
# -*- coding: UTF-8 -*-

import wx
import wx.xrc as xrc

__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

"""

    CLASS_HEADER = """\
class xrc%(windowName)s(wx.%(windowClass)s):
#!XRCED:begin-block:xrc%(windowName)s.PreCreate
    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
        set additional window styles using SetWindowStyle() and SetExtraStyle().
        \"\"\"
        pass
        
#!XRCED:end-block:xrc%(windowName)s.PreCreate

    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")
        self.PostCreate(pre)

        # Define variables for the controls, bind event handlers
"""

    SUBCLASS_HEADER = """\
class %(subclass)s(wx.%(windowClass)s):
    def __init__(self):
        # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation)
        pre = wx.Pre%(windowClass)s()
        self.PostCreate(pre)
        self.Bind(wx.EVT_WINDOW_CREATE, self.OnCreate)

#!XRCED:begin-block:%(subclass)s._PostInit
    def _PostInit(self):
        \"\"\" This function is called after the subclassed object is created.
        
        Override it for custom setup before the window is created usually to
        set additional window styles using SetWindowStyle() and SetExtraStyle().
        \"\"\"
        pass
#!XRCED:end-block:%(subclass)s._PostInit
        
    def OnCreate(self, evt):
        self.Unbind(wx.EVT_WINDOW_CREATE)
        self._PostInit()
"""

    CREATE_WIDGET_VAR = """\
        self.%(widgetName)s = xrc.XRCCTRL(self, \"%(widgetName)s\")
"""

    FRAME_MENUBAR_VAR = """\
        self.%(widgetName)s = self.GetMenuBar()
"""

    FRAME_MENUBAR_MENUITEM_VAR = """\
        self.%(widgetName)s = self.GetMenuBar().FindItemById(xrc.XRCID(\"%(widgetName)s\"))
"""

    FRAME_MENUBAR_MENU_VAR = """\
        idx = self.GetMenuBar().FindMenu(\"%(label)s\")
        if idx != wx.NOT_FOUND:
            self.%(widgetName)s = self.GetMenuBar().GetMenu(idx)
        else:
            self.%(widgetName)s = self.GetMenuBar().FindItemById(xrc.XRCID(\"%(widgetName)s\")).GetSubMenu()
"""


    MENUBAR_MENUITEM_VAR = """\
        self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
"""

    MENUBAR_MENU_VAR = """\
        idx = self.FindMenu(\"%(label)s\")
        if idx != wx.NOT_FOUND:
            self.%(widgetName)s = self.GetMenu(idx)
        else:
            self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\")).GetSubMenu()
"""

    MENU_MENUITEM_VAR = """\
        self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
"""

    MENU_MENU_VAR = """\
        self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\")).GetSubMenu()
"""

    FRAME_TOOLBAR_VAR = """\
        self.%(widgetName)s = self.GetToolBar()
"""

    FRAME_TOOLBAR_TOOL_VAR = """\
        self.%(widgetName)s = self.GetToolBar().FindById(xrc.XRCID(\"%(widgetName)s\"))
"""

    TOOLBAR_TOOL_VAR = """\
        self.%(widgetName)s = self.FindById(xrc.XRCID(\"%(widgetName)s\"))
"""

    BIND_WIDGET_EVENT = """\
        self.Bind(wx.%(event)s, self.%(eventHandler)s, %(eventObject)s)
"""

    BIND_EVENT = """\
        self.Bind(wx.%(event)s, self.%(eventHandler)s)
"""

    CREATE_EVENT_HANDLER = """\
#!XRCED:begin-block:xrc%(windowName)s.%(eventHandler)s
    def %(eventHandler)s(self, evt):
        # Replace with event handler code
        print \"%(eventHandler)s()\"
#!XRCED:end-block:xrc%(windowName)s.%(eventHandler)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)

        # Define variables for the menu items
"""

    MENUBAR_CLASS_HEADER = """\
class xrc%(windowName)s(wx.%(windowClass)s):
    def __init__(self):
        pre = get_resources().LoadMenuBar("%(windowName)s")
        self.PostCreate(pre)
        
        # Define variables for the menu items
"""

    TOOLBAR_CLASS_HEADER = """\
class xrc%(windowName)s(wx.%(windowClass)s):
    def __init__(self, parent):
        pre = get_resources().LoadToolBar(parent, "%(windowName)s")
        self.PostCreate(pre)
        
        # Define variables for the toolbar items
"""

    INIT_RESOURE_HEADER = """\
# ------------------------ Resource data ----------------------

def __init_resources():
    global __res
    __res = xrc.EmptyXmlResource()
"""

    LOAD_RES_FILE = """\
    __res.Load('%(resourceFilename)s')"""

    FILE_AS_STRING = """\
    %(filename)s = '''\\
%(fileData)s'''

"""

    PREPARE_MEMFS = """\
    wx.FileSystem.AddHandler(wx.MemoryFSHandler())
"""

    ADD_FILE_TO_MEMFS = """\
    wx.MemoryFSHandler.AddFile('XRC/%(memoryPath)s/%(filename)s', %(filename)s)
"""

    LOAD_RES_MEMFS = """\
    __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:
    
    templates = PythonTemplates()

    """This class generates Python code from XML resource files (XRC)."""

    def MakePythonModule(self, inputFiles, outputFilename,
                         embedResources=False, generateGetText=False,
                         assignVariables=True):

        self.blocks = {}
        self.outputFilename = outputFilename
        outputFile = self._OpenOutputFile(outputFilename)
        self.assignVariables = assignVariables

        classes = []
        subclasses = []
        resources = []
        gettextStrings = []

        # process all the inputFiles, collecting the output data
        for inFile in inputFiles:
            resourceDocument = minidom.parse(inFile)
            subclasses.append(self.GenerateSubclasses(resourceDocument))
            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

        # 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.
        if subclasses:
            subclasses = self.ReplaceBlocks(u"\n".join(subclasses))
            print >>outputFile, subclasses.encode("UTF-8")
        if classes:
            classes = self.ReplaceBlocks(u"\n".join(classes))
            print >>outputFile, classes.encode("UTF-8")

        print >>outputFile, self.templates.INIT_RESOURE_HEADER
        if embedResources:
            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)

    #-------------------------------------------------------------------

    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" \
                      and not e.getAttribute('subclass')]
        
        # 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")
            if not windowName: continue
            
            if windowClass in ["MenuBar"]:
                genfunc = self.GenerateMenuBarClass
            elif windowClass in ["Menu"]:
                genfunc = self.GenerateMenuClass
            elif windowClass in ["ToolBar"]:
                genfunc = self.GenerateToolBarClass
            else:
                genfunc = self.GenerateWidgetClass

            vars = []
            outputList += genfunc(windowClass, windowName, topWindow, vars)
            outputList.append('\n')

            outputList += self.GenerateEventHandlers(windowClass, windowName, topWindow, vars)
                    
        return "".join(outputList)

    #-------------------------------------------------------------------

    def CheckAssignVar(self, widget):
        if self.assignVariables: return True # assign_var override mode
        assign_var = False
        for node in widget.getElementsByTagName("XRCED"):
            if node.parentNode is widget:
                try:
                    elem = node.getElementsByTagName("assign_var")[0]
                except IndexError:
                    continue
                if elem.childNodes:
                    ch = elem.childNodes[0]
                    if ch.nodeType == ch.TEXT_NODE and bool(ch.nodeValue):
                        assign_var = True
        return assign_var


    def GenerateMenuBarClass(self, windowClass, windowName, topWindow, vars):
        outputList = []

        # output the header
        outputList.append(self.templates.MENUBAR_CLASS_HEADER % locals())

        # create an attribute for menus and menu items that have names
        for widget in topWindow.getElementsByTagName("object"):
            if not self.CheckAssignVar(widget): continue
            widgetClass = widget.getAttribute("class")
            widgetClass = re.sub("^wx", "", widgetClass)
            widgetName = widget.getAttribute("name")
            if widgetName != "" and widgetClass != "":
                vars.append(widgetName)
                if widgetClass == "MenuItem":
                    outputList.append(self.templates.MENUBAR_MENUITEM_VAR % locals())
                elif widgetClass == "Menu":
                    label = widget.getElementsByTagName("label")[0]
                    label = label.childNodes[0].data
                    outputList.append(self.templates.MENUBAR_MENU_VAR % locals())
                else:
                    raise RuntimeError("Unexpected widgetClass for MenuBar: %s" % widgetClass)

        return outputList


    def GenerateMenuClass(self, windowClass, windowName, topWindow, vars):
        outputList = []

        # output the header
        outputList.append(self.templates.MENU_CLASS_HEADER % locals())
        for widget in topWindow.getElementsByTagName("object"):
            if not self.CheckAssignVar(widget): continue
            widgetClass = widget.getAttribute("class")
            widgetClass = re.sub("^wx", "", widgetClass)
            widgetName = widget.getAttribute("name")
            if widgetName != "" and widgetClass != "":
                vars.append(widgetName)
                if widgetClass == "MenuItem":
                    outputList.append(self.templates.MENU_MENUITEM_VAR % locals())
                elif widgetClass == "Menu":
                    label = widget.getElementsByTagName("label")[0]
                    label = label.childNodes[0].data
                    outputList.append(self.templates.MENU_MENU_VAR % locals())
                else:
                    raise RuntimeError("Unexpected widgetClass for Menu: %s" % widgetClass)

        return outputList


    def GenerateToolBarClass(self, windowClass, windowName, topWindow, vars):
        outputList = []

        # output the header
        outputList.append(self.templates.TOOLBAR_CLASS_HEADER % locals())

        # create an attribute for menus and menu items that have names
        for widget in topWindow.getElementsByTagName("object"):
            if not self.CheckAssignVar(widget): continue
            widgetClass = widget.getAttribute("class")
            widgetClass = re.sub("^wx", "", widgetClass)
            widgetName = widget.getAttribute("name")
            if widgetName != "" and widgetClass != "":
                vars.append(widgetName)
                if widgetClass == "tool":
                    outputList.append(self.templates.TOOLBAR_TOOL_VAR % locals())
                else:
                    raise RuntimeError("Unexpected widgetClass for ToolBar: %s" % widgetClass)

        return outputList


    def GenerateWidgetClass(self, windowClass, windowName, topWindow, vars):
        outputList = []

        # output the header
        outputList.append(self.templates.CLASS_HEADER % locals())

        # Generate an attribute for each named item in the container
        for widget in topWindow.getElementsByTagName("object"):
            if not self.CheckAssignVar(widget): continue
            widgetClass = widget.getAttribute("class")
            widgetClass = re.sub("^wx", "", widgetClass)
            widgetName = widget.getAttribute("name")
            if widgetName != "" and widgetClass != "":
                vars.append(widgetName)
                if widgetClass not in \
                       ['tool', 'unknown', 'notebookpage', 'separator',
                        'sizeritem', 'Menu', 'MenuBar', 'MenuItem']:
                    outputList.append(self.templates.CREATE_WIDGET_VAR % locals())
                elif widgetClass == "MenuBar":
                    outputList.append(self.templates.FRAME_MENUBAR_VAR % locals())
                elif widgetClass == "MenuItem":
                    outputList.append(self.templates.FRAME_MENUBAR_MENUITEM_VAR % locals())
                elif widgetClass == "Menu":
                    label = widget.getElementsByTagName("label")[0]
                    label = label.childNodes[0].data
                    outputList.append(self.templates.FRAME_MENUBAR_MENU_VAR % locals())
                elif widgetClass == "ToolBar":
                    outputList.append(self.templates.FRAME_TOOLBAR_VAR % locals())
                elif widgetClass == "tool":
                    outputList.append(self.templates.FRAME_TOOLBAR_TOOL_VAR % locals())

        return outputList

    #-------------------------------------------------------------------

    def GenerateSubclasses(self, resourceDocument):
        outputList = []
        
        objectNodes = resourceDocument.getElementsByTagName("object")
        subclasses = set()
        bases = {}
        baseName = os.path.splitext(self.outputFilename)[0]
        for node in objectNodes:
            subclass = node.getAttribute('subclass')
            if subclass:
                module = subclass[:subclass.find('.')]
                if module != baseName: continue
                klass = node.getAttribute("class")
                if subclass not in subclasses:
                    subclasses.add(subclass)
                    bases[subclass] = klass
                else:
                    if klass != bases[subclass]:
                        print 'pywxrc: error: conflicting base classes for subclass %(subclass)s' \
                              % subclass
            
        # Generate subclasses
        for subclass in subclasses:
            windowClass = bases[subclass]
            subclass = re.sub("^\S+\.", "", subclass)
            windowClass = re.sub("^wx", "", windowClass)
            outputList.append(self.templates.SUBCLASS_HEADER % locals())
            outputList.append('\n')
                    
        return "".join(outputList)

    #-------------------------------------------------------------------

    def GenerateEventHandlers(self, windowClass, windowName, topWindow, vars):
        outputList = []

        # Generate child event handlers
        eventHandlers = []            
        for elem in topWindow.getElementsByTagName("XRCED"):
            try:
                eventNode = elem.getElementsByTagName("events")[0]
            except IndexError:
                continue
            events = eventNode.childNodes[0].data.split('|')
            for event in events:
                if elem.parentNode is topWindow:
                    eventHandler = "On%s" % event[4:].capitalize()
                    outputList.append(self.templates.BIND_EVENT % locals())
                    eventHandlers.append(eventHandler)
                else:
                    widget = elem.parentNode
                    widgetClass = widget.getAttribute("class")
                    widgetClass = re.sub("^wx", "", widgetClass)
                    widgetName = widget.getAttribute("name")
                    if not widgetName or not widgetClass: continue
                    eventObject = None
                    if widgetClass == "MenuItem" and windowClass != "MenuBar":
                        if widgetName[:2] == "wx":
                            eventObject = 'id=wx.%s' % re.sub("^wx", "", widgetName)
                        eventHandler = "On%s_%s" % (event[4:].capitalize(), widgetName)
                        if widgetName in vars: eventObject = "self.%s" % widgetName
                    else:
                        eventHandler = "On%s_%s" % (event[4:].capitalize(), widgetName)
                        if widgetName in vars: eventObject = "self.%s" % widgetName
                    if not eventObject:
                        eventObject = "id=xrc.XRCID('%s')" % widgetName
                    outputList.append(self.templates.BIND_WIDGET_EVENT % locals())
                    eventHandlers.append(eventHandler)
        outputList.append("\n")

        for eventHandler in eventHandlers:
            block = "xrc%(windowName)s.%(eventHandler)s" % locals()
            try:
                outputList.append(self.blocks[block])
            except KeyError:
                outputList.append(self.templates.CREATE_EVENT_HANDLER % locals())
            outputList.append("\n")

        outputList.append("\n")
        return "".join(outputList)

    #-------------------------------------------------------------------

    def GenerateInitResourcesEmbedded(self, resourceFilename, resourceDocument):
        outputList = []
        files = []

        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() # what about this? encoding=resourceDocument.encoding)
        outputList.append(self.templates.FILE_AS_STRING % locals())

        for f in files:
            filename = self.GetMemoryFilename(f)
            fileData = self.FileToString(os.path.join(resourcePath, f))
            outputList.append(self.templates.FILE_AS_STRING % 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())
        
        return "".join(outputList)
        
    #-------------------------------------------------------------------

    def GenerateInitResourcesFile(self, resourceFilename, resourceDocument):
        # take only the filename portion out of resourceFilename
        resourceFilename = os.path.split(resourceFilename)[1]
        outputList = []
        outputList.append(self.templates.LOAD_RES_FILE % locals())
        return "".join(outputList)

    #-------------------------------------------------------------------

    def GetMemoryFilename(self, filename):
        # Remove special chars from the filename
        return re.sub(r"[^A-Za-z0-9_]", "_", filename)

    #-------------------------------------------------------------------

    def FileToString(self, filename):
        outputList = []
        
        buffer = open(filename, "rb").read()
        fileLen = len(buffer)

        linelng = 0
        for i in xrange(fileLen):
            s = buffer[i]
            c = ord(s)
            if s == '\n':
                tmp = s
                linelng = 0
            elif c < 32 or c > 127 or s == "'":
                tmp = "\\x%02x" % c
            elif s == "\\":
                tmp = "\\\\"            
            else:
                tmp = s

            if linelng > 70:
                linelng = 0
                outputList.append("\\\n")
            
            outputList.append(tmp)
            linelng += len(tmp)
            
        return "".join(outputList)
            
    #-------------------------------------------------------------------

    def NodeContainsFilename(self, node):
        """ Does 'node' contain filename information at all? """

        # Any bitmaps:
        if node.nodeName == "bitmap":
            return True

        if node.nodeName == "icon":
            return True

        # URLs in wxHtmlWindow:
        if node.nodeName == "url":
            return True

        # 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

        # wxBitmap or wxIcon toplevel resources:
        if node.nodeName == "object":
            klass = node.getAttribute("class")
            if klass == "wxBitmap" or klass == "wxIcon":
                return True

        return False

    #-------------------------------------------------------------------

    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."""
        
        # Is 'node' XML node element?
        if node is None: return
        if node.nodeType != minidom.Document.ELEMENT_NODE: return

        containsFilename = self.NodeContainsFilename(node);

        for n in node.childNodes:

            if (containsFilename and
                (n.nodeType == minidom.Document.TEXT_NODE or
                 n.nodeType == minidom.Document.CDATA_SECTION_NODE)):
                
                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 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")                

    #-------------------------------------------------------------------

    # Replace editable block contents with previous
    def ReplaceBlocks(self, input):
        output = []
        block = None
        blockLines = []
        for l in input.split('\n'):
            if not block:
                mo = reBeginBlock.match(l)
                if mo and mo.groups()[0] in self.blocks:
                    block = mo.groups()[0]
                    output.append(self.blocks[block])
                else:
                    output.append(l + '\n')
            else:
                mo = reEndBlock.match(l)
                if mo:
                    if mo.groups()[0] != block:
                        print "pywxrc: error: block mismatch: %s != %s" % (block, mo.groups()[0])
                    block = None
        return ''.join(output)

    #-------------------------------------------------------------------

    def _OpenOutputFile(self, outputFilename):
        if outputFilename == "-":
            outputFile = sys.stdout
        else:
            # Parse existing file to collect editable blocks
            if os.path.isfile(outputFilename):
                outputFile = open(outputFilename)
                block = None
                blockLines = []
                for l in outputFile.readlines():
                    if not block:
                        mo = reBeginBlock.match(l)
                        if mo:
                            block = mo.groups()[0]
                            blockLines = [l]
                    else:
                        blockLines.append(l)
                        mo = reEndBlock.match(l)
                        if mo:
                            if mo.groups()[0] != block:
                                print "pywxrc: error: block mismatch: %s != %s" % (block, mo.groups()[0])
                            self.blocks[block] = "".join(blockLines)
                            block = None
            
            try:
                outputFile = open(outputFilename, "wt")
            except IOError:
                raise IOError("Can't write output to '%s'" % outputFilename)
        return outputFile

    



#---------------------------------------------------------------------------

def main(args=None):
    if not args:
        args = sys.argv[1:]
        
    resourceFilename = ""
    outputFilename = None
    embedResources = False
    generateGetText = False
    assignVariables = True
    generatePython = False

    try:
        opts, args = getopt.gnu_getopt(args,
                                       "hpgevo:",
                                       "help python gettext embed novar 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
    if not args:
        print __doc__
        print "No xrc input file was specified."
        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 ["-p", "--python"]:
            generatePython = True

        if opt in ["-o", "--output"]:
            outputFilename = val
            
        if opt in ["-e", "--embed"]:
            embedResources = True

        if opt in ["-v", "--novar"]:
            assignVariables = False

        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:
        if generatePython:
            if not outputFilename:
                outputFilename = os.path.splitext(args[0])[0] + "_xrc.py"
            comp.MakePythonModule(inputFiles, outputFilename,
                                  embedResources, generateGetText, 
                                  assignVariables)

        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:
        if outputFilename != "-":
            print >>sys.stderr, "Resources written to %s." % outputFilename

if __name__ == "__main__":
    main(sys.argv[1:])
