<commit_before># graph_editor.py copyright 2002 by <NAME> http://cpbotha.net/
# $Id: graphEditor.py,v 1.26 2003/06/06 16:10:24 cpbotha Exp $
# the graph-editor thingy where one gets to connect modules together

import cPickle
from wxPython.wx import *
from internal.wxPyCanvas import wxpc
import genUtils
import string
import sys
import traceback

# ----------------------------------------------------------------------------
class geCanvasDropTarget(wxTextDropTarget):
    
    def __init__(self, graphEditor):
        wxTextDropTarget.__init__(self)        
        self._graphEditor = graphEditor

    def OnDropText(self, x, y, text):
        self._graphEditor.canvasDropText(x, y, text)

# ----------------------------------------------------------------------------
class graphEditor:
    def __init__(self, dscas3_app):
        # initialise vars
        self._dscas3_app = dscas3_app

        import resources.python.graphEditorFrame
        self._graphFrame = resources.python.graphEditorFrame.graphEditorFrame(
            self._dscas3_app.get_main_window(),
            -1, title='dummy', wxpcCanvas=wxpc.canvas)

        self._graphFrame.SetIcon(self._dscas3_app.getApplicationIcon())

        EVT_CLOSE(self._graphFrame, self.close_graph_frame_cb)

        EVT_BUTTON(self._graphFrame, self._graphFrame.rescanButtonId,
                   lambda e, s=self: s.fill_module_tree())

        EVT_MENU(self._graphFrame, self._graphFrame.fileNewId,
                 self._fileNewCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileExitId,
                 self._fileExitCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileOpenId,
                 self._fileOpenCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileSaveId,
                 self._fileSaveCallback)

        # this will be filled in by self.fill_module_tree; it's here for
        # completeness
        self._availableModuleList = None
        self.fill_module_tree()

        EVT_TREE_BEGIN_DRAG(self._graphFrame, self._graphFrame.treeCtrlId,
                            self.treeCtrlBeginDragHandler)

        # setup the canvas...
        self._graphFrame.canvas.SetVirtualSize((1024, 1024))
        self._graphFrame.canvas.SetScrollRate(20,20)

        # the canvas is a drop target
        self._canvasDropTarget = geCanvasDropTarget(self)
        self._graphFrame.canvas.SetDropTarget(self._canvasDropTarget)
        
        # bind events on the canvas
        self._graphFrame.canvas.addObserver('buttonUp',
                                            self._canvasButtonUp)
        
        # now display the shebang
        self.show()

    def treeCtrlBeginDragHandler(self, event):
        itemText = self._graphFrame.treeCtrl.GetItemText(event.GetItem())
        dataObject = wxTextDataObject(itemText)
        
        dropSource = wxDropSource(self._graphFrame)
        dropSource.SetData(dataObject)
        # we don't need the result of the DoDragDrop call (phew)
        dropSource.DoDragDrop(TRUE)

    def canvasDropText(self, x, y, text):
        self.createModuleAndGlyph(x, y, text)
        # check if the text is in our module list (then we should create it)

    def close(self):
        """This gracefull takes care of all graphEditor shutdown and is mostly
        called at application shutdown.
        """

        # this should take care of just about everything!
        self.clearAllGlyphsFromCanvas()

    def createGlyph(self, rx, ry, moduleName, moduleInstance):
        """Create only a glyph on the canvas given an already created
        moduleInstance.
        """
        

        co = wxpc.coGlyph((rx, ry),
                          len(moduleInstance.getInputDescriptions()),
                          len(moduleInstance.getOutputDescriptions()),
                          moduleName, moduleInstance)
        
        canvas = self._graphFrame.canvas
        canvas.addObject(co)


        co.addObserver('motion', self._glyphMotion)
                    
        co.addObserver('buttonDown',
                       self._glyphRightClick, moduleInstance)
        co.addObserver('buttonUp',
                       self._glyphButtonUp, moduleInstance)
        co.addObserver('drag',
                       self._glyphDrag, moduleInstance)

        # first have to draw the just-placed glyph so it has
        # time to update its (label-dependent) dimensions
        dc = wxClientDC(self._graphFrame.canvas)
        self._graphFrame.canvas.PrepareDC(dc)
        co.draw(dc)

        # the network loading needs this
        return co
    
    def createModuleAndGlyph(self, x, y, moduleName):
        # check that it's a valid module name
        if moduleName in self._availableModuleList:
            # we have a valid module, we should try and instantiate
            mm = self._dscas3_app.getModuleManager()
            temp_module = mm.createModule(moduleName)
            # if the module_manager did its trick, we can make a glyph
            if temp_module:
                # create and draw the actual glyph
                rx, ry = self._graphFrame.canvas.eventToRealCoords(x, y)
                self.createGlyph(rx,ry,moduleName,temp_module)

                # route all lines
                self._routeAllLines()


    def fill_module_tree(self):
        self._graphFrame.treeCtrl.DeleteAllItems()

        tree_root = self._graphFrame.treeCtrl.AddRoot('Modules')
        rdrn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Readers')
        wrtn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Writers')
        vwrn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Viewers')
        fltn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Filters')
        miscn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Misc')

        mm = self._dscas3_app.getModuleManager()
        mm.scanModules()
        self._availableModuleList = mm.getAvailableModuleList()
        for cur_mod in self._availableModuleList:
            mtype = cur_mod[-3:].lower()
            if mtype == 'rdr':
                self._graphFrame.treeCtrl.AppendItem(rdrn, cur_mod)
            elif mtype == 'wrt':
                self._graphFrame.treeCtrl.AppendItem(wrtn, cur_mod)
            elif mtype == 'vwr':
                self._graphFrame.treeCtrl.AppendItem(vwrn, cur_mod)
            elif mtype == 'flt':
                self._graphFrame.treeCtrl.AppendItem(fltn, cur_mod)
            else:
                self._graphFrame.treeCtrl.AppendItem(miscn, cur_mod)

        # only do stuff if !ItemHasChildren()

        self._graphFrame.treeCtrl.Expand(tree_root)

    def close_graph_frame_cb(self, event):
        self.hide()

    def show(self):
        self._graphFrame.Show(True)

    def hide(self):
        self._graphFrame.Show(False)

    def _drawPreviewLine(self, beginp, endp0, endp1):

        # make a DC to draw on
        dc = wxClientDC(self._graphFrame.canvas)
        self._graphFrame.canvas.PrepareDC(dc)

        dc.BeginDrawing()
        
        # dotted line
        dc.SetBrush(wxBrush('WHITE', wx.wxTRANSPARENT))
        dc.SetPen(wxPen('BLACK', 1, wxDOT))
        dc.SetLogicalFunction(wxINVERT) # NOT dst

        # nuke the previous line, draw the new one
        dc.DrawLine(beginp[0], beginp[1], endp0[0], endp0[1])
        dc.DrawLine(beginp[0], beginp[1], endp1[0], endp1[1])
        
        dc.EndDrawing()


    def _disconnect(self, glyph, inputIdx):
        """Disconnect inputIdx'th input of glyph.
        """

        try:
            # first disconnect the actual modules
            mm = self._dscas3_app.getModuleManager()
            mm.disconnectModules(glyph.moduleInstance, inputIdx)

            deadLine = glyph.inputLines[inputIdx]
            if deadLine:
                # remove the line from the destination module input lines
                glyph.inputLines[inputIdx] = None
                # and for the source module output lines
                outlines = deadLine.fromGlyph.outputLines[
                    deadLine.fromOutputIdx]
                del outlines[outlines.index(deadLine)]

                # and from the canvas
                self._graphFrame.canvas.removeObject(deadLine)
                deadLine.close()
            
        except Exception, e:
            genUtils.logError('Could not disconnect modules: %s' \
                              % (str(e)))
                

    def _canvasButtonUp(self, canvas, eventName, event, userData):
        if event.LeftUp():
            if canvas.getDraggedObject() and \
                   canvas.getDraggedObject().draggedPort and \
                   canvas.getDraggedObject().draggedPort != (-1,-1):

                if canvas.getDraggedObject().draggedPort[0] == 0:
                    # the user was dragging an input port and dropped it
                    # on the canvas, so she probably wants us to disconnect
                    inputIdx = canvas.getDraggedObject().draggedPort[1]
                    self._disconnect(canvas.getDraggedObject(),
                                     inputIdx)
            

    def _checkAndConnect(self, draggedObject, draggedPort,
                         droppedObject, droppedInputPort):

        if droppedObject.inputLines[droppedInputPort]:
            # the user dropped us on a connected input, we can just bail
            return
            
        if draggedPort[0] == 1:
            # this is a good old "I'm connecting an output to an input"
            self._connect(draggedObject, draggedPort[1],
                          droppedObject, droppedInputPort)
            
            self._graphFrame.canvas.Refresh()

        elif draggedObject.inputLines[draggedPort[1]]:
            # this means the user was dragging a connected input port and has
            # now dropped it on another input port... (we've already eliminated
            # the case of a drop on an occupied input port, and thus also
            # a drop on the dragged port)

            oldLine = draggedObject.inputLines[draggedPort[1]]
            fromGlyph = oldLine.fromGlyph
            fromOutputIdx = oldLine.fromOutputIdx
            toGlyph = oldLine.toGlyph
            toInputIdx = oldLine.toInputIdx

            # delete the old one
            self._disconnect(toGlyph, toInputIdx)

            # connect up the new one
            self._connect(fromGlyph, fromOutputIdx,
                          droppedObject, droppedInputPort)

            self._graphFrame.canvas.Refresh()

    def clearAllGlyphsFromCanvas(self):
        allGlyphs = self._graphFrame.canvas.getObjectsOfClass(wxpc.coGlyph)

        mm = self._dscas3_app.getModuleManager()

        # we take care of the "difficult" modules first
        safeGlyphs = []
        for glyph in allGlyphs:
            if glyph.moduleInstance.__class__.__name__ \
               in mm.dangerousConsumerModules:
                self._deleteModule(glyph)
            else:
                safeGlyphs.append(glyph)

        # and then the rest
        for glyph in safeGlyphs:
            # delete each module, do NOT refresh canvas
            self._deleteModule(glyph, False)

        # only here!
        self._graphFrame.canvas.Refresh()

    def _createLine(self, fromObject, fromOutputIdx, toObject, toInputIdx):
        l1 = wxpc.coLine(fromObject, fromOutputIdx,
                         toObject, toInputIdx)
        self._graphFrame.canvas.addObject(l1)
            
        # also record the line in the glyphs
        toObject.inputLines[toInputIdx] = l1
        fromObject.outputLines[fromOutputIdx].append(l1)

        # REROUTE THIS LINE
        self._routeLine(l1)

    def _connect(self, fromObject, fromOutputIdx,
                 toObject, toInputIdx):

        try:
            # connect the actual modules
            mm = self._dscas3_app.getModuleManager()
            mm.connectModules(fromObject.moduleInstance, fromOutputIdx,
                              toObject.moduleInstance, toInputIdx)

            # if that worked, we can make a linypoo
            self._createLine(fromObject, fromOutputIdx, toObject, toInputIdx)

        except Exception, e:
            genUtils.logError('Could not connect modules: %s' % (str(e)))

    def loadNetwork(self, filename, position=(-1,-1)):
        print "Loading from %s" % (filename)

        f = None
        try:
            # load the stream
            f = open(filename, 'r')
            stream = f.read()
        except Exception, e:
            genUtils.logError('Could not load network from %s: %s' % \
                              (filename,str(e)))
            if f:
                f.close()
            return

        f.close()

        try:
            # unpickle it
            tup = cPickle.loads(stream)
            pmsDict, connectionList, glyphPosDict = tup
        except Exception, e:
            genUtils.logError('Could not interpret network from %s: %s' % \
                              (filename,str(e)))
            return


        # get the module manager to deserialise
        mm = self._dscas3_app.getModuleManager()
        newModulesDict, newConnections = mm.deserialiseModuleInstances(
            pmsDict, connectionList)
            
        # newModulesDict and newConnections contain the modules and
        # connections which were _actually_ realised... let's draw
        # glyphs!

        # store the new glyphs in a dictionary keyed on OLD pickled
        # instanceName so that we can connect them up in the next step
        newGlyphDict = {} 
        for newModulePickledName in newModulesDict.keys():
            position = glyphPosDict[newModulePickledName]
            moduleInstance = newModulesDict[newModulePickledName]
            newGlyph = self.createGlyph(
                position[0], position[1],
                moduleInstance.__class__.__name__,
                moduleInstance)
            newGlyphDict[newModulePickledName] = newGlyph

        # now make lines for all the existing connections
        for connection in connectionList:
            sGlyph = newGlyphDict[connection.sourceInstanceName]
            tGlyph = newGlyphDict[connection.targetInstanceName]
            self._createLine(sGlyph, connection.outputIdx,
                             tGlyph, connection.inputIdx)
        
                                         

    def updatePortInfoStatusBar(self, currentGlyph, currentPort):
        
        """You can only call this during motion IN a port of a glyph.
        """
        
        msg = ''
        canvas = currentGlyph.getCanvas()

        draggedObject = canvas.getDraggedObject()
        if draggedObject and draggedObject.draggedPort and \
               draggedObject.draggedPort != (-1, -1):

            if draggedObject.draggedPort[0] == 0:
                pstr = draggedObject.moduleInstance.getInputDescriptions()[
                    draggedObject.draggedPort[1]]
            else:
                pstr = draggedObject.moduleInstance.getOutputDescriptions()[
                    draggedObject.draggedPort[1]]

            msg = '|%s|-[%s] ===>> ' % (draggedObject.getLabel(), pstr)

        if currentPort[0] == 0:
            pstr = currentGlyph.moduleInstance.getInputDescriptions()[
                currentPort[1]]
        else:
            pstr = currentGlyph.moduleInstance.getOutputDescriptions()[
                currentPort[1]]
             
        msg += '|%s|-[%s]' % (currentGlyph.getLabel(), pstr)

        self._graphFrame.GetStatusBar().SetStatusText(msg)            
                                   
    def _fileExitCallback(self, event):
        self._dscas3_app.quit()

    def _fileNewCallback(self, event):
        self.clearAllGlyphsFromCanvas()

    def _fileOpenCallback(self, event):
        filename = wxFileSelector(
            "Choose DSCAS3 network to load",
            "", "", "d3n",
            "DSCAS3 networks (*.d3n)|*.d3n|All files (*.*)|*.*")
        
        if filename:
            self.clearAllGlyphsFromCanvas()
            self.loadNetwork(filename)

    def _fileSaveCallback(self, event):
        filename = wxFileSelector(
            "Choose filename for DSCAS3 network",
            "", "", "d3n",
            "DSCAS3 networks (*.d3n)|*.d3n|All files (*.*)|*.*")
        
        if filename:
            print "Saving to %s" % (filename)
            # make a list of all module instances
            allGlyphs = self._graphFrame.canvas.getObjectsOfClass(wxpc.coGlyph)
            moduleInstances = [glyph.moduleInstance for glyph in allGlyphs]
            mm = self._dscas3_app.getModuleManager()

            # let the moduleManager serialise what it can
            pmsDict, connectionList = mm.serialiseModuleInstances(
                moduleInstances)

            savedInstanceNames = [pms.instanceName for pms in pmsDict.values()]
                                  
            # now we also get to store the coordinates of the glyphs which
            # have been saved (keyed on instanceName)
            savedGlyphs = [glyph for glyph in allGlyphs
                           if mm.getInstanceName(glyph.moduleInstance)\
                           in savedInstanceNames]
            
            glyphPosDict = {}
            for savedGlyph in savedGlyphs:
                instanceName = mm.getInstanceName(savedGlyph.moduleInstance)
                glyphPosDict[instanceName] = savedGlyph.getPosition()
                
            # change the serialised moduleInstances to a pickled stream
            stream = cPickle.dumps(\
                     (pmsDict, connectionList, glyphPosDict), True)

            # FIXME: check for file errors, check for overwriting!
            f = open(filename, 'w')
            f.write(stream)
            f.close()

    def _glyphDrag(self, glyph, eventName, event, module):

        canvas = glyph.getCanvas()        

        # this clause will execute once at the beginning of a drag...
        if not glyph.draggedPort:
            # we're dragging, but we don't know if we're dragging a port yet
            port = glyph.findPortContainingMouse(event.realX, event.realY)
            if port:
                # 
                glyph.draggedPort = port
            else:
                # this indicates that the glyph is being dragged, but that
                # we don't have to check for a port during this drag
                glyph.draggedPort = (-1, -1)

        # when we get here, glyph.draggedPort CAN't BE None
        if glyph.draggedPort == (-1, -1):<commit_msg>Changed saving of networks to text mode.<commit_after># graph_editor.py copyright 2002 by <NAME> http://cpbotha.net/
# $Id: graphEditor.py,v 1.27 2003/06/06 16:16:30 cpbotha Exp $
# the graph-editor thingy where one gets to connect modules together

import cPickle
from wxPython.wx import *
from internal.wxPyCanvas import wxpc
import genUtils
import string
import sys
import traceback

# ----------------------------------------------------------------------------
class geCanvasDropTarget(wxTextDropTarget):
    
    def __init__(self, graphEditor):
        wxTextDropTarget.__init__(self)        
        self._graphEditor = graphEditor

    def OnDropText(self, x, y, text):
        self._graphEditor.canvasDropText(x, y, text)

# ----------------------------------------------------------------------------
class graphEditor:
    def __init__(self, dscas3_app):
        # initialise vars
        self._dscas3_app = dscas3_app

        import resources.python.graphEditorFrame
        self._graphFrame = resources.python.graphEditorFrame.graphEditorFrame(
            self._dscas3_app.get_main_window(),
            -1, title='dummy', wxpcCanvas=wxpc.canvas)

        self._graphFrame.SetIcon(self._dscas3_app.getApplicationIcon())

        EVT_CLOSE(self._graphFrame, self.close_graph_frame_cb)

        EVT_BUTTON(self._graphFrame, self._graphFrame.rescanButtonId,
                   lambda e, s=self: s.fill_module_tree())

        EVT_MENU(self._graphFrame, self._graphFrame.fileNewId,
                 self._fileNewCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileExitId,
                 self._fileExitCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileOpenId,
                 self._fileOpenCallback)

        EVT_MENU(self._graphFrame, self._graphFrame.fileSaveId,
                 self._fileSaveCallback)

        # this will be filled in by self.fill_module_tree; it's here for
        # completeness
        self._availableModuleList = None
        self.fill_module_tree()

        EVT_TREE_BEGIN_DRAG(self._graphFrame, self._graphFrame.treeCtrlId,
                            self.treeCtrlBeginDragHandler)

        # setup the canvas...
        self._graphFrame.canvas.SetVirtualSize((1024, 1024))
        self._graphFrame.canvas.SetScrollRate(20,20)

        # the canvas is a drop target
        self._canvasDropTarget = geCanvasDropTarget(self)
        self._graphFrame.canvas.SetDropTarget(self._canvasDropTarget)
        
        # bind events on the canvas
        self._graphFrame.canvas.addObserver('buttonUp',
                                            self._canvasButtonUp)
        
        # now display the shebang
        self.show()

    def treeCtrlBeginDragHandler(self, event):
        itemText = self._graphFrame.treeCtrl.GetItemText(event.GetItem())
        dataObject = wxTextDataObject(itemText)
        
        dropSource = wxDropSource(self._graphFrame)
        dropSource.SetData(dataObject)
        # we don't need the result of the DoDragDrop call (phew)
        dropSource.DoDragDrop(TRUE)

    def canvasDropText(self, x, y, text):
        self.createModuleAndGlyph(x, y, text)
        # check if the text is in our module list (then we should create it)

    def close(self):
        """This gracefull takes care of all graphEditor shutdown and is mostly
        called at application shutdown.
        """

        # this should take care of just about everything!
        self.clearAllGlyphsFromCanvas()

    def createGlyph(self, rx, ry, moduleName, moduleInstance):
        """Create only a glyph on the canvas given an already created
        moduleInstance.
        """
        

        co = wxpc.coGlyph((rx, ry),
                          len(moduleInstance.getInputDescriptions()),
                          len(moduleInstance.getOutputDescriptions()),
                          moduleName, moduleInstance)
        
        canvas = self._graphFrame.canvas
        canvas.addObject(co)


        co.addObserver('motion', self._glyphMotion)
                    
        co.addObserver('buttonDown',
                       self._glyphRightClick, moduleInstance)
        co.addObserver('buttonUp',
                       self._glyphButtonUp, moduleInstance)
        co.addObserver('drag',
                       self._glyphDrag, moduleInstance)

        # first have to draw the just-placed glyph so it has
        # time to update its (label-dependent) dimensions
        dc = wxClientDC(self._graphFrame.canvas)
        self._graphFrame.canvas.PrepareDC(dc)
        co.draw(dc)

        # the network loading needs this
        return co
    
    def createModuleAndGlyph(self, x, y, moduleName):
        # check that it's a valid module name
        if moduleName in self._availableModuleList:
            # we have a valid module, we should try and instantiate
            mm = self._dscas3_app.getModuleManager()
            temp_module = mm.createModule(moduleName)
            # if the module_manager did its trick, we can make a glyph
            if temp_module:
                # create and draw the actual glyph
                rx, ry = self._graphFrame.canvas.eventToRealCoords(x, y)
                self.createGlyph(rx,ry,moduleName,temp_module)

                # route all lines
                self._routeAllLines()


    def fill_module_tree(self):
        self._graphFrame.treeCtrl.DeleteAllItems()

        tree_root = self._graphFrame.treeCtrl.AddRoot('Modules')
        rdrn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Readers')
        wrtn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Writers')
        vwrn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Viewers')
        fltn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Filters')
        miscn = self._graphFrame.treeCtrl.AppendItem(tree_root, 'Misc')

        mm = self._dscas3_app.getModuleManager()
        mm.scanModules()
        self._availableModuleList = mm.getAvailableModuleList()
        for cur_mod in self._availableModuleList:
            mtype = cur_mod[-3:].lower()
            if mtype == 'rdr':
                self._graphFrame.treeCtrl.AppendItem(rdrn, cur_mod)
            elif mtype == 'wrt':
                self._graphFrame.treeCtrl.AppendItem(wrtn, cur_mod)
            elif mtype == 'vwr':
                self._graphFrame.treeCtrl.AppendItem(vwrn, cur_mod)
            elif mtype == 'flt':
                self._graphFrame.treeCtrl.AppendItem(fltn, cur_mod)
            else:
                self._graphFrame.treeCtrl.AppendItem(miscn, cur_mod)

        # only do stuff if !ItemHasChildren()

        self._graphFrame.treeCtrl.Expand(tree_root)

    def close_graph_frame_cb(self, event):
        self.hide()

    def show(self):
        self._graphFrame.Show(True)

    def hide(self):
        self._graphFrame.Show(False)

    def _drawPreviewLine(self, beginp, endp0, endp1):

        # make a DC to draw on
        dc = wxClientDC(self._graphFrame.canvas)
        self._graphFrame.canvas.PrepareDC(dc)

        dc.BeginDrawing()
        
        # dotted line
        dc.SetBrush(wxBrush('WHITE', wx.wxTRANSPARENT))
        dc.SetPen(wxPen('BLACK', 1, wxDOT))
        dc.SetLogicalFunction(wxINVERT) # NOT dst

        # nuke the previous line, draw the new one
        dc.DrawLine(beginp[0], beginp[1], endp0[0], endp0[1])
        dc.DrawLine(beginp[0], beginp[1], endp1[0], endp1[1])
        
        dc.EndDrawing()


    def _disconnect(self, glyph, inputIdx):
        """Disconnect inputIdx'th input of glyph.
        """

        try:
            # first disconnect the actual modules
            mm = self._dscas3_app.getModuleManager()
            mm.disconnectModules(glyph.moduleInstance, inputIdx)

            deadLine = glyph.inputLines[inputIdx]
            if deadLine:
                # remove the line from the destination module input lines
                glyph.inputLines[inputIdx] = None
                # and for the source module output lines
                outlines = deadLine.fromGlyph.outputLines[
                    deadLine.fromOutputIdx]
                del outlines[outlines.index(deadLine)]

                # and from the canvas
                self._graphFrame.canvas.removeObject(deadLine)
                deadLine.close()
            
        except Exception, e:
            genUtils.logError('Could not disconnect modules: %s' \
                              % (str(e)))
                

    def _canvasButtonUp(self, canvas, eventName, event, userData):
        if event.LeftUp():
            if canvas.getDraggedObject() and \
                   canvas.getDraggedObject().draggedPort and \
                   canvas.getDraggedObject().draggedPort != (-1,-1):

                if canvas.getDraggedObject().draggedPort[0] == 0:
                    # the user was dragging an input port and dropped it
                    # on the canvas, so she probably wants us to disconnect
                    inputIdx = canvas.getDraggedObject().draggedPort[1]
                    self._disconnect(canvas.getDraggedObject(),
                                     inputIdx)
            

    def _checkAndConnect(self, draggedObject, draggedPort,
                         droppedObject, droppedInputPort):

        if droppedObject.inputLines[droppedInputPort]:
            # the user dropped us on a connected input, we can just bail
            return
            
        if draggedPort[0] == 1:
            # this is a good old "I'm connecting an output to an input"
            self._connect(draggedObject, draggedPort[1],
                          droppedObject, droppedInputPort)
            
            self._graphFrame.canvas.Refresh()

        elif draggedObject.inputLines[draggedPort[1]]:
            # this means the user was dragging a connected input port and has
            # now dropped it on another input port... (we've already eliminated
            # the case of a drop on an occupied input port, and thus also
            # a drop on the dragged port)

            oldLine = draggedObject.inputLines[draggedPort[1]]
            fromGlyph = oldLine.fromGlyph
            fromOutputIdx = oldLine.fromOutputIdx
            toGlyph = oldLine.toGlyph
            toInputIdx = oldLine.toInputIdx

            # delete the old one
            self._disconnect(toGlyph, toInputIdx)

            # connect up the new one
            self._connect(fromGlyph, fromOutputIdx,
                          droppedObject, droppedInputPort)

            self._graphFrame.canvas.Refresh()

    def clearAllGlyphsFromCanvas(self):
        allGlyphs = self._graphFrame.canvas.getObjectsOfClass(wxpc.coGlyph)

        mm = self._dscas3_app.getModuleManager()

        # we take care of the "difficult" modules first
        safeGlyphs = []
        for glyph in allGlyphs:
            if glyph.moduleInstance.__class__.__name__ \
               in mm.dangerousConsumerModules:
                self._deleteModule(glyph)
            else:
                safeGlyphs.append(glyph)

        # and then the rest
        for glyph in safeGlyphs:
            # delete each module, do NOT refresh canvas
            self._deleteModule(glyph, False)

        # only here!
        self._graphFrame.canvas.Refresh()

    def _createLine(self, fromObject, fromOutputIdx, toObject, toInputIdx):
        l1 = wxpc.coLine(fromObject, fromOutputIdx,
                         toObject, toInputIdx)
        self._graphFrame.canvas.addObject(l1)
            
        # also record the line in the glyphs
        toObject.inputLines[toInputIdx] = l1
        fromObject.outputLines[fromOutputIdx].append(l1)

        # REROUTE THIS LINE
        self._routeLine(l1)

    def _connect(self, fromObject, fromOutputIdx,
                 toObject, toInputIdx):

        try:
            # connect the actual modules
            mm = self._dscas3_app.getModuleManager()
            mm.connectModules(fromObject.moduleInstance, fromOutputIdx,
                              toObject.moduleInstance, toInputIdx)

            # if that worked, we can make a linypoo
            self._createLine(fromObject, fromOutputIdx, toObject, toInputIdx)

        except Exception, e:
            genUtils.logError('Could not connect modules: %s' % (str(e)))

    def loadNetwork(self, filename, position=(-1,-1)):
        print "Loading from %s" % (filename)

        f = None
        try:
            # load the stream
            f = open(filename, 'r')
            stream = f.read()
        except Exception, e:
            genUtils.logError('Could not load network from %s: %s' % \
                              (filename,str(e)))
            if f:
                f.close()
            return

        f.close()

        try:
            # unpickle it
            tup = cPickle.loads(stream)
            pmsDict, connectionList, glyphPosDict = tup
        except Exception, e:
            genUtils.logError('Could not interpret network from %s: %s' % \
                              (filename,str(e)))
            return


        # get the module manager to deserialise
        mm = self._dscas3_app.getModuleManager()
        newModulesDict, newConnections = mm.deserialiseModuleInstances(
            pmsDict, connectionList)
            
        # newModulesDict and newConnections contain the modules and
        # connections which were _actually_ realised... let's draw
        # glyphs!

        # store the new glyphs in a dictionary keyed on OLD pickled
        # instanceName so that we can connect them up in the next step
        newGlyphDict = {} 
        for newModulePickledName in newModulesDict.keys():
            position = glyphPosDict[newModulePickledName]
            moduleInstance = newModulesDict[newModulePickledName]
            newGlyph = self.createGlyph(
                position[0], position[1],
                moduleInstance.__class__.__name__,
                moduleInstance)
            newGlyphDict[newModulePickledName] = newGlyph

        # now make lines for all the existing connections
        for connection in connectionList:
            sGlyph = newGlyphDict[connection.sourceInstanceName]
            tGlyph = newGlyphDict[connection.targetInstanceName]
            self._createLine(sGlyph, connection.outputIdx,
                             tGlyph, connection.inputIdx)
        
                                         

    def updatePortInfoStatusBar(self, currentGlyph, currentPort):
        
        """You can only call this during motion IN a port of a glyph.
        """
        
        msg = ''
        canvas = currentGlyph.getCanvas()

        draggedObject = canvas.getDraggedObject()
        if draggedObject and draggedObject.draggedPort and \
               draggedObject.draggedPort != (-1, -1):

            if draggedObject.draggedPort[0] == 0:
                pstr = draggedObject.moduleInstance.getInputDescriptions()[
                    draggedObject.draggedPort[1]]
            else:
                pstr = draggedObject.moduleInstance.getOutputDescriptions()[
                    draggedObject.draggedPort[1]]

            msg = '|%s|-[%s] ===>> ' % (draggedObject.getLabel(), pstr)

        if currentPort[0] == 0:
            pstr = currentGlyph.moduleInstance.getInputDescriptions()[
                currentPort[1]]
        else:
            pstr = currentGlyph.moduleInstance.getOutputDescriptions()[
                currentPort[1]]
             
        msg += '|%s|-[%s]' % (currentGlyph.getLabel(), pstr)

        self._graphFrame.GetStatusBar().SetStatusText(msg)            
                                   
    def _fileExitCallback(self, event):
        self._dscas3_app.quit()

    def _fileNewCallback(self, event):
        self.clearAllGlyphsFromCanvas()

    def _fileOpenCallback(self, event):
        filename = wxFileSelector(
            "Choose DSCAS3 network to load",
            "", "", "d3n",
            "DSCAS3 networks (*.d3n)|*.d3n|All files (*.*)|*.*")
        
        if filename:
            self.clearAllGlyphsFromCanvas()
            self.loadNetwork(filename)

    def _fileSaveCallback(self, event):
        filename = wxFileSelector(
            "Choose filename for DSCAS3 network",
            "", "", "d3n",
            "DSCAS3 networks (*.d3n)|*.d3n|All files (*.*)|*.*")
        
        if filename:
            print "Saving to %s" % (filename)
            # make a list of all module instances
            allGlyphs = self._graphFrame.canvas.getObjectsOfClass(wxpc.coGlyph)
            moduleInstances = [glyph.moduleInstance for glyph in allGlyphs]
            mm = self._dscas3_app.getModuleManager()

            # let the moduleManager serialise what it can
            pmsDict, connectionList = mm.serialiseModuleInstances(
                moduleInstances)

            savedInstanceNames = [pms.instanceName for pms in pmsDict.values()]
                                  
            # now we also get to store the coordinates of the glyphs which
            # have been saved (keyed on instanceName)
            savedGlyphs = [glyph for glyph in allGlyphs
                           if mm.getInstanceName(glyph.moduleInstance)\
                           in savedInstanceNames]
            
            glyphPosDict = {}
            for savedGlyph in savedGlyphs:
                instanceName = mm.getInstanceName(savedGlyph.moduleInstance)
                glyphPosDict[instanceName] = savedGlyph.getPosition()
                
            # change the serialised moduleInstances to a pickled stream
            stream = cPickle.dumps(\
                     (pmsDict, connectionList, glyphPosDict), False)

            # FIXME: check for file errors, check for overwriting!
            f = open(filename, 'w')
            f.write(stream)
            f.close()

    def _glyphDrag(self, glyph, eventName, event, module):

        canvas = glyph.getCanvas()        

        # this clause will execute once at the beginning of a drag...
        if not glyph.draggedPort:
            # we're dragging, but we don't know if we're dragging a port yet
            port = glyph.findPortContainingMouse(event.realX, event.realY)
            if port:
                # 
                glyph.draggedPort = port
            else:
                # this indicates that the glyph is being dragged, but that
                # we don't have to check for a port during this drag
                glyph.draggedPort = (-1, -1)

        # when we get here, glyph.draggedPort CAN't BE None
        if glyph.draggedPort == (-1, -1):
{'fraction_non_alphanumeric': 0.0894938484783078, 'fraction_numerical': 0.005719835959421541, 'mean_word_length': 1.8489623366641046, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7506881', 'n_tokens_mistral': 10221, 'n_tokens_neox': 9716, 'n_words': 2397}