#!/usr/bin/python
__author__ = 'David Malcolm <dmalcolm@redhat.com>, Zack Cerza <zcerza@redhat.com>'
appName = 'Script Recorder'
import os
os.environ['GTK_MODULES']=''

from dogtail.utils import checkForA11yInteractively
checkForA11yInteractively()

import dogtail.tree
pyatspi = dogtail.tree.pyatspi
pyatspi.accessible.setCacheLevel(pyatspi.constants.CACHE_PROPERTIES)
Accessibility = dogtail.tree.Accessibility
from dogtail.predicate import GenericPredicate
import gtk.gdk
import dogtail.rawinput
import re

# Begin GUI code
import threading
class PlaybackThread(threading.Thread):
    def __init__(self, script):
        threading.Thread.__init__(self)
        self.script = script

    def run(self):
        exec self.script

import gobject
import gnome
import gtk.glade

try:
    import gtksourceview
    useGtkSourceView = True
except ImportError:
    useGtkSourceView = False

def createSourceView():
    langManager = gtksourceview.SourceLanguagesManager()
    lang = langManager.get_language_from_mime_type("text/x-python")
    buffer = gtksourceview.SourceBuffer()
    sourceView = gtksourceview.SourceView(buffer)
    buffer.set_language(lang)
    buffer.set_highlight(True)
    return sourceView

class RecorderGUI(gnome.Program):
    def __init__(self):
        gnome.Program.__init__(self)
        appAuthors = ['Zack Cerza <zcerza@redhat.com>']
        program = gnome.program_init(appName, '0.1')

        if os.path.exists('recorder.glade'):
            x = gtk.glade.XML('recorder.glade')
        else:
            import sys
            exec_root = sys.argv[0].split("/bin/")[0]
            if exec_root[0] is not '/':
                exec_root = "/usr"
            x = gtk.glade.XML(exec_root + '/share/dogtail/glade/recorder.glade')

        self.window = x.get_widget('window')

        try:
            self.window.set_icon_from_file('../icons/dogtail-head.svg')
        except Exception:
            self.window.set_icon_from_file('/usr/share/icons/hicolor/scalable/apps/dogtail-head.svg')

        self.recordButton = x.get_widget('recordButton')
        self.playButton = x.get_widget('playButton')
        self.playButton.set_sensitive(False)
        self.clearButton = x.get_widget('clearButton')
        self.clearButton.set_sensitive(False)
        self.saveButton = x.get_widget('saveButton')
        self.saveButton.set_sensitive(False)
        if useGtkSourceView:
            oldTextView = x.get_widget('scriptTextView')
            parent = oldTextView.get_parent()
            parent.remove(oldTextView)
            sourceView = createSourceView()
            parent.add(sourceView)
            sourceView.show()
            self.scriptTextView = sourceView
        else:
            self.scriptTextView = x.get_widget('scriptTextView')
        self.scriptTextView.set_editable(False)
        #self.writerComboBox = x.get_widget('writerComboBox')
        #self.writerComboBox.set_active(0)
        #self.writerComboBox.set_sensitive(True)

        # The following line added because self.writerComboBox is gone:
        recorder.writerClass = ProceduralScriptWriter

        self.connectSignals()

        self.window.show_all()
        gtk.main()

    def connectSignals(self):
        #self.writerComboBox.connect('changed', self.setWriterClass)
        self.recordButton.connect('clicked', self.toggleRecording, self.scriptTextView)
        self.playButton.connect('clicked', self.playScript, self.scriptTextView)
        self.clearButton.connect('clicked', self.clearScript, self.scriptTextView)
        self.saveButton.connect('clicked', self.saveScript)
        self.window.connect('delete_event', self.quit)

    def setWriterClass (self, comboBox):
        selected = comboBox.get_active_text()
        if selected == "Procedural":
            recorder.writerClass = ProceduralScriptWriter
        elif selected == "Object-Oriented":
            recorder.writerClass = OOScriptWriter
        else:
            print selected, "isn't a ScriptWriter, but it is selected. How?"

    def toggleRecording(self, recordButton = None, scriptTextView = None):
        label = self.recordButton.get_label()
        recordID = 'gtk-media-record'
        stopID = 'gtk-media-stop'
        
        if label == recordID:
            #self.writerComboBox.set_sensitive(False)
            self.playButton.set_sensitive(False)
            self.clearButton.set_sensitive(False)
            self.saveButton.set_sensitive(False)
            self.scriptTextView.set_editable(False)
            self.recordButton.set_label(stopID)
            recorder.startRecording(self.recordButton, self.scriptTextView)
        
        elif label == stopID:
            #self.writerComboBox.set_sensitive(True)
            self.playButton.set_sensitive(True)
            self.clearButton.set_sensitive(True)
            self.saveButton.set_sensitive(True)
            self.scriptTextView.set_editable(True)
            self.recordButton.set_label(recordID)
            recorder.stopRecording(self.recordButton, self.scriptTextView)

    def stopRecording(self):
        self.recordButton.set_label('gtk-media-stop')
        self.toggleRecording()

    def playScript(self, button = None, scriptTextView = None):
        self.recordButton.set_sensitive(False)
        self.playButton.set_sensitive(False)
        self.scriptTextView.set_editable(False)

        buffer = self.scriptTextView.get_buffer()
        startIter = buffer.get_start_iter()
        endIter = buffer.get_end_iter()
        scriptText = buffer.get_text(startIter, endIter)

        self.playbackThread = PlaybackThread(scriptText)
        self.playbackThread.start()
        self.playbackThread.join()

        self.playButton.set_sensitive(True)
        self.recordButton.set_sensitive(True)
        self.scriptTextView.set_editable(True)

    def clearScript(self, button = None, scriptTextView = None):
        self.scriptTextView.get_buffer().set_text('')
        self.clearButton.set_sensitive(False)
        self.saveButton.set_sensitive(False)

    def saveScript(self, button):
        """
        Brings up a file chooser dialog asking where to save the script.
        """
        self.saveFileChooser = gtk.FileChooserDialog("Save Script...", None, \
            gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, \
            gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        self.saveFileChooser.set_default_response(gtk.RESPONSE_OK)

        # Why this isn't default, I do not understand.
        self.saveFileChooser.set_do_overwrite_confirmation(True)

        filter = gtk.FileFilter()
        filter.set_name('Python files')
        filter.add_pattern('*.py')
        self.saveFileChooser.add_filter(filter)
        filter = gtk.FileFilter()
        filter.set_name('All Files')
        filter.add_pattern('*')
        self.saveFileChooser.add_filter(filter)

        response = self.saveFileChooser.run()
        if response == gtk.RESPONSE_OK:
            fileName = self.saveFileChooser.get_filename()
            # Append a .py to the file name if necessary
            if fileName[-3:] != '.py':
                fileName += '.py'
            file = open(fileName, 'w')
            buffer = self.scriptTextView.get_buffer()
            startIter = buffer.get_start_iter()
            endIter = buffer.get_end_iter()
            scriptText = buffer.get_text(startIter, endIter)
            file.write(scriptText)
            file.close()
        self.saveFileChooser.destroy()

    def quit(self, *args):
        self.stopRecording()
        gtk.main_quit()

# End GUI code

def logEvent(event):
    print str(event)
    return

class ScriptWriter:
    """
    Abstract Writer subclass which writes out Python scripts
    """
    def __init__(self, scriptTextView = None):
        self.scriptBuffer = ""
        self.scriptTextView = scriptTextView
        self.debug = False

    def _hasPreamble(self):
        print "preamble?"
        if self.scriptTextView:
            pLines = self.preamble.strip().split('\n')
            buffer = self.scriptTextView.get_buffer()
            if buffer.get_line_count() < len(pLines): return False
            i = 0
            match = False
            for i in range(len(pLines)):
                iter1 = buffer.get_iter_at_line(i)
                iter2 = iter1.copy()
                iter2.forward_to_line_end()
                bLine = iter1.get_text(iter2)
                print "'''%s''' =? '''%s'''" % (bLine, pLines[i])
                if bLine == pLines[i]: match = True
                else: return False
            return match
        else: return self.scriptBuffer.startswith(preamble)
    hasPreamble = property(_hasPreamble)

    def recordLine(self, string):
        print string
        if self.scriptTextView:
            buffer = self.scriptTextView.get_buffer()
            iter = buffer.get_end_iter()
            if buffer.get_line_count() > 1:
                string = '\n' + string
            buffer.insert(iter, string)
            
            # Scroll to the end
            iter = buffer.get_end_iter()
            mark = buffer.create_mark('end', iter, True)
            self.scriptTextView.scroll_mark_onscreen(mark)
            buffer.delete_mark(mark)
        else:
            self.scriptBuffer += '\n' + string

    def recordClick(self, node):
        raise NotImplementedError

    def recordTyping(self, string, type, node):
        raise NotImplementedError

    def recordKeyCombo(self, string, type, node):
        raise NotImplementedError

class OOScriptWriter(ScriptWriter):
    """
    Concrete Writer subclass which writes out Python scripts in an object-oriented
    style
    """
    preamble = "#!/usr/bin/python\nfrom dogtail.tree import *\n"

    def __init__(self, scriptTextView = None):
        ScriptWriter.__init__(self, scriptTextView)

        self.debugVariables = False

        if not self.hasPreamble: self.recordLine(self.preamble)

        # maintain a dict from variable names to search paths
        self.variables = {}

    def generateVariableName(self, predicate):
        # Ensure uniqueness
        result = predicate.makeScriptVariableName()
        if result in self.variables:
            # This variable name is already in use; need to append a number:
            index = 1
            while result+str(index) in self.variables:
                index+=1
            return result+str(index)
        else:
            return result

    def printVariables(self):
        # debug hook
        print "variables:"
        for (varName, varAbsPath) in self.variables.iteritems():
            print "varName:%s -> absPath:%s"%(varName, varAbsPath)

    def generateAbsSearchPathMethodCall(self, absSearchPath):
        """
        Generates a method call that identifies the absolute search path,
        optimizing away prefixes where possible with variable names.
        """
        # We optimize away the longest common absolute path prefix, i.e. the
        # shortest relative path suffix:
        if self.debug:
            print "*******************"
            print "generateAbsSearchPathMethodCall for %s"%absSearchPath
            self.printVariables()

        shortestRelativePath = None
        for (varName, varAbsPath) in self.variables.iteritems():
            relPath = varAbsPath.getRelativePath(absSearchPath)
            if relPath:
                if shortestRelativePath:
                    if relPath.length() < shortestRelativePath[2].length():
                        shortestRelativePath = (varName, varAbsPath, relPath)
                else:
                    shortestRelativePath = (varName, varAbsPath, relPath)

        if self.debug:
            if shortestRelativePath:
                (varName, varAbsPath, relPath) = shortestRelativePath
                print "shortestRelativePath: (%s, %s, %s)"%(varName, varAbsPath, relPath)
            else:
                print "shortestRelativePath: None"
            print "*******************"

        if shortestRelativePath:
            (varName, varAbsPath, relPath) = shortestRelativePath
            return varName+relPath.makeScriptMethodCall()
        else:
            # Have to specify it as an absolute path:
            return "root"+absSearchPath.makeScriptMethodCall()

    def recordClick(self, node):
        """
        Record a mouse click
        """
        if node == None: return
        searchPath = node.getAbsoluteSearchPath()

        if self.debug:
            print "----------------------------------"
            print "click on %s"%searchPath
            print "Full script would be: root%s"%searchPath.makeScriptMethodCall()

        # Generate variables for nodes likely to be referred to often (application, window)
        # FIXME: make this smarter?
        for i in [1,2,3]:
            if i<searchPath.length():

                prefixPath = searchPath.getPrefix(i)

                if self.debugVariables:
                    print "Considering: %s"%prefixPath

                if not prefixPath in self.variables.values():
                    if self.debugVariables:
                        print "It is not yet a variable"
                        self.printVariables()

                    predicate = prefixPath.getPredicate(i-1)
                    varName = predicate.makeScriptVariableName()
                    self.recordLine(varName+" = "+self.generateAbsSearchPathMethodCall(prefixPath))
                    self.variables[varName]=prefixPath
                else:
                    if self.debugVariables:
                        print "It is already a variable"

        result = self.generateAbsSearchPathMethodCall(searchPath)
        result +=".click()"

        if self.debug:
            print "----------------------------------"

        self.recordLine(result)

class ProceduralScriptWriter(ScriptWriter):
    """
    Concrete Writer subclass which writes out Python scripts in a procedural
    style
    """
    
    currentWidget = None
    currentDialog = None
    currentFrame = None
    currentApplication = None
    preamble = "#!/usr/bin/python\nfrom dogtail.procedural import *\n"

    def __init__(self, scriptTextView = None):
        ScriptWriter.__init__(self, scriptTextView)

        if not self.hasPreamble: self.recordLine(self.preamble)
    
    def setUpFocus(self, node):
        """
        Writes out the necessary focus.application() and focus.dialog() lines 
        to the script.
        """
        if node is None:
            #print "Node is None!"
            return

        application = node.getApplication()
        if application:
            needApp = True
            if self.currentApplication:
                if application == self.currentApplication: needApp = False
            if needApp:
                self.recordLine("focus.application('%s')" % application.name)
                self.currentApplication = application
        elif application == None:
            print "Warning: could not determine which application you are clicking or typing on."
            print "    It is most likely not reporting its toplevel Accessible as having a"
            print "    role name of 'application'. Please file a bug against it!"

        dialog = node.findAncestor(GenericPredicate(roleName = 'dialog'))
        if dialog:
            needDialog = True
            if dialog == self.currentDialog: needDialog = False
            if needDialog:
                self.recordLine("focus.dialog('%s')" % dialog.name)
                self.currentDialog = dialog
        else:
            frame = node.findAncestor(GenericPredicate(roleName='frame'))
            if frame:
                needFrame = True
                if frame == self.currentFrame: needFrame = False
                if needFrame:
                    self.recordLine("focus.frame('%s')" % frame.name)
                    self.currentFrame = frame

        
    def recordClick(self, node, button):
        if node == None: return False

        self.setUpFocus(node)
        
        widget = node
        #possibleActions = ['click', 'activate', 'open', 'menu']
        possibleActions = ['click', 'activate', 'menu']
        foundAnAction = False
        for action in possibleActions:
            if action in widget.actions.keys():
                if button == 1 and action == 'menu': break
                foundAnAction = True
                self.recordLine("%s('%s', roleName='%s')" % \
                        (action, widget.name.replace('\n','\\n'), widget.roleName))
                break
        if not foundAnAction: 
            if hasattr(widget, 'select') and button != 1:
                self.recordLine("select('%s', roleName='%s')" % \
                        (widget.name.replace('\n','\\n'), widget.roleName))
            else:
                if button != 1: btn = ', button = ' + str(button)
                else: btn = ''
                s = "click('%s', roleName='%s', raw=True%s)" % \
                        (widget.name.replace('\n','\\n'), widget.roleName, btn)
                self.recordLine(s)
        self.currentWidget = widget
        
    def recordTyping(self, string, type, node):
        if not string: return
        self.setUpFocus(node)
        self.recordLine("type(\"" + string.replace('"','\\"') + "\")")

    def recordKeyCombo(self, string, type, node):
        if not string: return
        self.setUpFocus(node)
        self.recordLine("keyCombo(\"" + string + "\")")

# Singleton EventRecorder
global recorder

class EventRecorder:
    """
    Event Recorder
    """
    modifiers = {
            pyatspi.MODIFIER_NUMLOCK: 'NumLock',
            pyatspi.MODIFIER_META3: 'Meta3',
            pyatspi.MODIFIER_META2: 'Meta2',
            pyatspi.MODIFIER_META: 'Meta',
            pyatspi.MODIFIER_ALT: 'Alt',
            pyatspi.MODIFIER_CONTROL: 'Control',
            pyatspi.MODIFIER_SHIFTLOCK: 'ShiftLock',
            pyatspi.MODIFIER_SHIFT: 'Shift' }

    def __init__(self, writerClass = ProceduralScriptWriter):
        self.writer = None
        self.writerClass = writerClass
        self.lastFocusedNode = None
        self.lastSelectedNode = None
        self.lastPressedNode = None
        self.lastReleasedNode = None
        self.typedTextBuffer = ""
        self.lastTypedNode = None
        self.absoluteNodePaths = True
        self.modMasks = []
        for mask in range(0, (1 << max(self.modifiers.keys()) + 1)):
            self.modMasks.append(mask)
        self.modsDown = {}

        import gtk.keysyms
        self.listeners = {
                "window:create": marshalOnWindowCreate,
                "focus:": marshalOnFocus,
                "object:state-changed:selected": marshalOnSelect,
                "mouse:button": marshalOnMouseButton
                }

    def __registerEvents(self):
        print "registering events"
        for eventName, callback in self.listeners.iteritems():
            ret = pyatspi.Registry.registerEventListener(callback, eventName)
            print eventName, str(ret)

        # Keystroke events:
        pyatspi.Registry.registerKeystrokeListener(marshalOnKeyPress, [], self.modMasks, [pyatspi.KEY_PRESSED_EVENT, pyatspi.KEY_RELEASED_EVENT], True, True, False)
        
    def __deregisterEvents(self):
        for eventName, callback in self.listeners.iteritems():
            pyatspi.Registry.deregisterEventListener(callback, eventName)

        # Keystroke events:
        pyatspi.Registry.deregisterKeystrokeListener(marshalOnKeyPress, [], self.modMasks, [pyatspi.KEY_PRESSED_EVENT, pyatspi.KEY_RELEASED_EVENT])
        

    def startRecording(self, unused = None, scriptTextView = None):
        self.writer = self.writerClass(scriptTextView)
        self.__registerEvents()
        # set lastKeyPressTimeStamp to 1, which is an invalid value.
        self.lastKeyPressTimeStamp = 1
        pyatspi.Registry.start(False, False)

    def stopRecording(self, unused = None, scriptTextView = None):
        self.__deregisterEvents()
        pyatspi.Registry.stop()
        self.writer = None

    def onFocus(self, event):
        #if sourceNode == self.lastPressedNode or \
        #        sourceNode == self.lastReleasedNode:
        #    sourceNode.blink()
        self.lastFocusedNode = event.source

    def onSelect(self, event):
        self.lastSelectedNode = event.source

    def onMouseButton(self, event):
        self.writer.recordTyping(self.typedTextBuffer, "pressed", self.lastFocusedNode)
        self.typedTextBuffer = ""

        isPress = isRelease = False
        g=re.match('(\d)(p|r)', event.type.minor).groups()
        button = int(g[0])
        if g[1] == 'p': isPress = True
        elif g[1] == 'r': isRelease = True

        # The source node is always "main" - which sucks. We have to detect
        # the real source ourselves.

        def detectByCoordinates(nodes, x, y):
            for node in nodes:
                if not node: continue
                candidate = node.getChildAtPoint(x, y)
                if candidate:
                    return candidate

        x = event.detail1
        y = event.detail2
        # Below is a hack using a dict to avoid using a set, as older pythons
        # don't have sets.
        possibleNodes = {self.lastSelectedNode : None, 
                self.lastFocusedNode : None}.keys()
        try:
            detectedNode = detectByCoordinates(possibleNodes, x, y)
        except LookupError:
            return

        if detectedNode and ((detectedNode.name == appName and \
                detectedNode.roleName == 'frame') or \
                detectedNode.findAncestor(\
                GenericPredicate(roleName = 'frame', name = appName))):
            self.lastPressedNode = None
            self.lastReleasedNode = None
            return
        if isPress: 
            self.lastPressedNode = detectedNode
        elif isRelease:
            self.lastReleasedNode = detectedNode
        
        if isRelease and detectedNode:
            self.writer.recordClick(detectedNode, button)

    def onKeyPress(self, event):
        # Keystroke recording callback.
    
        # The Fn key on my Thinkpad has a keyID of 0. Ignore it.
        if not event.id: return
        
        isPress = isRelease = False
        if event.type == pyatspi.KEY_PRESSED:
            isPress = True
        elif event.type == pyatspi.KEY_RELEASED:
            isRelease = True


        # This elapsed time code isn't used right now.
        self.lastKeyPressTimeStamp = event.timestamp
        if self.lastKeyPressTimeStamp < 0:
            elapsedTime = event.timestamp - self.lastKeyPressTimeStamp
        else:
            elapsedTime = 0

        # Get the name of the key in question.
        keyName = dogtail.rawinput.keySymToKeyName(event.id)
        # If the key is printable, get its character.
        keyChar = dogtail.rawinput.keySymToUniChar(event.id)
        #print "string '%s' char '%s'" % (keyName, keyChar)

        # Is the only key being pressed a modifier?
        if keyName.startswith("Alt_") or \
                keyName.startswith("Control_") or \
                keyName.startswith("Meta_") or \
                keyName.startswith("Shift") or \
                keyName == 'Caps_Lock':
            isJustAMod = True
            #print "Just a mod: %s" % keyName
        else: isJustAMod = False
            
        
        # We keep track of which modifiers are down here.
        def mod(string):
            if isPress:
                self.modsDown[string] = None
            elif isRelease and self.modsDown.has_key(string):
                del self.modsDown[string]

        if isJustAMod:
            # If this is just a modifier event, note that the modifier's
            # state and then we're done.
            mod(keyName)
            return
        
        # If this is a release event, we're done.
        if isRelease: return

        # This builds modifier strings in the format a dogtail script
        # needs, e.g. <Ctrl><Alt>
        def buildModifierString(modsDict):
            s = ''
            if modsDict:
                for mod in modsDict.keys():
                    #if not 'Shift' in mod and event.is_text:
                    if not ('Shift' in mod and keyChar):
                        s = s + '<' + mod.split('_')[0] + '>'
            return s
        modString = buildModifierString(self.modsDown)

        # Okay, so now it's time to make some decisions about how to 
        #  record this event. This mainly depends on whether or not it's
        #  a key combo. 
        # If it's a key combo, we're going to write it out right away.
        # If it's not, we store characters in a buffer and flush the 
        #  buffer by writing out a script line when it makes sense.
        # But if we have changed our keyboard focus since the last time
        #  we've been called, we're going to flush the buffer and start
        #  a new one.

        # If modifiers are present, we're dealing with a key combo
        if modString: combo = True
        # Otherwise, we assume for a second that we're not.
        else: combo = False
        
        # If the key represents a printable character, use that.
        if keyChar != '': key = keyChar
        else:
            # Otherwise, use the keyName, e.g. 'Return'.
            key = keyName
            # We treat nonprintable characters like key combos.
            combo = True

        # If our focus has changed, we're going to flush the buffer.
        if not combo and self.lastTypedNode is not None and \
                (self.lastTypedNode != self.lastFocusedNode):
            #print "changed node, flushing"
            flush = True
        else: flush = False

        #print "%s ! %s ! %s" % (str(self.lastTypedNode), str(self.lastFocusedNode), self.typedTextBuffer)

        if combo:
            self.writer.recordTyping(self.typedTextBuffer, "pressed", self.lastFocusedNode)
            self.typedTextBuffer = ""
            self.writer.recordKeyCombo(modString + key, type, self.lastFocusedNode)
            self.lastTypedNode = self.lastFocusedNode
        elif flush:
            self.writer.recordTyping(self.typedTextBuffer, "pressed", self.lastTypedNode)
            self.typedTextBuffer = key
            self.lastTypedNode = self.lastFocusedNode
        else:
            if self.typedTextBuffer == "":
                self.lastTypedNode = self.lastFocusedNode
            self.typedTextBuffer = self.typedTextBuffer + key
        return

        # If we're using the keyName or have modifiers, flush 
        # self.typedTextBuffer by recording a line and also record the key 
        # combo.
        if modString or flush:
            if self.typedTextBuffer:
                self.writer.recordTyping(self.typedTextBuffer, "pressed", self.lastFocusedNode)
                if not combo:
                    self.typedTextBuffer = key
                else:
                    self.typedTextBuffer = ""
                self.lastTypedNode = self.lastFocusedNode
            if modString or combo:
                self.writer.recordKeyCombo(modString + key, type, self.lastFocusedNode)
        # Otherwise add to the buffer, and wait to flush it.
        else:
            if self.typedTextBuffer == "":
                self.lastTypedNode = self.lastFocusedNode
            self.typedTextBuffer = self.typedTextBuffer + key

    def onWindowCreate(self, event):
        sourceNode = event.source
        print "Window creation: %s" % str(sourceNode)

    def getLogStringForNode(self, node):
        if self.absoluteNodePaths:
            return node.getAbsoluteSearchPath()
        else:
            return node

# We wrap our callbacks because we don't necessarily want to fail
# when an exception is raised.
import traceback
def marshalOnFocus(event):
    #logEvent(event)
    try: recorder.onFocus(event)
    except Exception: traceback.print_exc()

def marshalOnSelect(event):
    #logEvent(event)
    try: recorder.onSelect(event)
    except Exception: traceback.print_exc()

def marshalOnMouseButton(event):
    #logEvent(event)
    try: recorder.onMouseButton(event)
    except Exception: traceback.print_exc()

def marshalOnKeyPress(event):
    #logEvent(event)
    try: recorder.onKeyPress(event)
    except Exception: traceback.print_exc()

def marshalOnWindowCreate(event):
    #logEvent(event)
    try: recorder.onWindowCreate(event)
    except Exception: traceback.print_exc()

recorder = EventRecorder()
recorderGUI = RecorderGUI()
#recorder.writer.debug = True
#recorder.writer.debugVariables = True


# vim: sw=4 ts=4 sts=4 et ai
