# Let's define ourselves a GUI!

import wx, time, os, threading, socket
import oommfq_core as qcore
import oommfq_net as net

TimedUpdateOverlapLock = threading.Lock()

#############
# CONSTANTS #
#############

#Abnormal font sizes
ENORM_FONT = wx.Font(26, wx.FONTFAMILY_DEFAULT, style=wx.NORMAL, weight=wx.FONTWEIGHT_BOLD)
BIG_FONT = wx.Font(14, wx.FONTFAMILY_DEFAULT, style=wx.NORMAL, weight=wx.FONTWEIGHT_BOLD)
TINY_FONT = wx.Font(8, wx.FONTFAMILY_DEFAULT, style=wx.NORMAL, weight=wx.FONTWEIGHT_NORMAL)


############
# MAIN GUI #
############

class MainFrame(wx.Frame):
    def __init__(self, dataManager, GUIMustProcessLazyReactor):
        wx.Frame.__init__(self, None, -1, "OOMMFq Server", size=(750,680))

        self.dataManager = dataManager

        self.dropTarget = OOMMFqDropTarget(self)
        self.SetDropTarget(self.dropTarget)

        #Special on-close behavior
        self.Bind(wx.EVT_CLOSE, self.onClose)

        #Is the internet our problem?
        if GUIMustProcessLazyReactor:
            #We're stupid and block, so we set up a timer to allow the Twisted reactor,
            #which *also* blocks, to be called occasionally.
            #Networking is latency-insensitive, so this should work OK
            #and is cleaner to read than a bucket of callFromThread calls.
            self.timer = wx.Timer(self, 1001)
            self.Bind(wx.EVT_TIMER, self.pingReactor, id=1001)
            self.timer.Start(150, False)

        #This timer allows users to work with textboxes without saving every char -
        #it'll wait until they've left them alone for a bit
        self.lazySaveTimer = LazySaveTimer(self.dataManager)

        #TODO - add serverconf to menubar?
        menubar = wx.MenuBar()
        about = wx.Menu()
        about.Append(999, 'About', 'Program information and license')
        menubar.Append(about, "About")
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.showAbout, id=999)

        #Next, let's build up the actual GUI!
        self.panel = panel = wx.Panel(self, -1)

        #Primary organization is vertical - oommf configuration, live sims, waiting sims, services.
        sizer = wx.BoxSizer(wx.VERTICAL)

        ##
        ### OOMMF-Finding
        ##

        sectorTitle = wx.StaticText(panel, -1, "Path to OOMMF")
        sectorTitle.SetFont(BIG_FONT)
        sizer.Add(sectorTitle, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)

        #Add the combo box, based on the config value; the path; and the button
        hsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.TclCall = wx.ComboBox(panel, 101, value=self.dataManager.conf["tclshCall"], choices=["tclsh","tclsh85"], style=wx.CB_READONLY)
        hsizer.Add(self.TclCall, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 2)

        self.Bind(wx.EVT_TEXT, self.updateTclsh, id=101)

        self.OOMMFPathLabel = wx.StaticText(panel, -1, self.dataManager.conf["oommfPath"], style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE | wx.ALIGN_CENTER_VERTICAL)
        hsizer.Add(self.OOMMFPathLabel, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 10)

        self.LoadOOMMFButton = wx.Button(panel, 10, "Load OOMMF")
        self.Bind(wx.EVT_BUTTON, self.GUILocateOOMMF, id=10)
        hsizer.Add(self.LoadOOMMFButton, 0)
        sizer.Add(hsizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, 10)

        #Separator - end of title sector
        sizer.Add(wx.StaticLine(panel, -1), 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 1)

        ##
        ### Active Sim Monitor
        ##

        #OK, next we show the active-sims part
        sectorTitle = wx.StaticText(panel, -1, "Active Simulations")
        sectorTitle.SetFont(BIG_FONT)
        sizer.Add(sectorTitle, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)

        #Now we need a horizontal sizer to accomodate the listbox and controls....
        activeHsizer = wx.BoxSizer(wx.HORIZONTAL)

        self.activeSims = ActiveSimListCtrl(self.panel, 201, self)
        activeHsizer.Add(self.activeSims, 1, wx.EXPAND | wx.LEFT | wx.ALIGN_CENTER, 15)

        #... and yes, another vertical sizer for the controls
        simCountVsizer = wx.BoxSizer(wx.VERTICAL)
        text = wx.StaticText(panel, -1, "Using")
        simCountVsizer.Add(text, 0, wx.ALIGN_CENTER | wx.BOTTOM, 8)

        self.activeSimCount = wx.StaticText(panel, -1, "0")
        self.activeSimCount.SetFont(ENORM_FONT)
        simCountVsizer.Add(self.activeSimCount, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 15)

        text = wx.StaticText(panel, -1, "of")
        simCountVsizer.Add(text, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 3)

        self.maxSimCount = wx.TextCtrl(panel, 202, value=self.dataManager.conf["maxSims"], style = wx.TE_CENTER)
        self.maxSimCount.SetMaxLength(3)
        self.maxSimCount.SetFont(ENORM_FONT)
        simCountVsizer.Add(self.maxSimCount, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        #Watch for updates here!
        self.Bind(wx.EVT_TEXT, self.updateMaxSimCount, id=202)

        text = wx.StaticText(panel, -1, "slots")
        simCountVsizer.Add(text, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 8)

        scHsizer = wx.BoxSizer(wx.HORIZONTAL)

        #Buttons to start sims and clear errored sims
        self.startSimsButton = wx.Button(panel, 204, "Start")
        scHsizer.Add(self.startSimsButton, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, 5)

        #However, disable this if autostart is on
        if int(self.dataManager.conf["autoStart"]):
            self.startSimsButton.Disable()

        self.Bind(wx.EVT_BUTTON, self.launchSims, id=204)

        a = wx.Button(panel, 205, "Clear Errors")
        scHsizer.Add(a, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, 5)

        self.Bind(wx.EVT_BUTTON, self.clearErrors, id=205)

        simCountVsizer.Add(scHsizer,0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)

        activeHsizer.Add(simCountVsizer, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 40)
        activeHsizer.AddSpacer(25)
        #Finally, incorporate this sector
        sizer.Add(activeHsizer, 3, wx.EXPAND | wx.ALL, 10)

        #Separator - end of active sector
        sizer.Add(wx.StaticLine(panel, -1), 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 1)

        ##
        ### Queue Management
        ##

        #And now, a grand HSizer with two things - a queue, and server conf
        grandHsizer = wx.BoxSizer(wx.HORIZONTAL)

        #Good stuff! Let's add QUEUEING POWER
        titlingVsizer = wx.BoxSizer(wx.VERTICAL)

        sectorTitle = wx.StaticText(panel, -1, "Queued Simulations")
        sectorTitle.SetFont(BIG_FONT)
        titlingVsizer.Add(sectorTitle, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)

        #The H-V nesting is going to look about the same
        queuedHsizer = wx.BoxSizer(wx.HORIZONTAL)

        self.queuedSims = QueuedSimListCtrl(self.panel, 301, self)
        queuedHsizer.Add(self.queuedSims, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER, 15)

        #Buttons to bump the queue order, or delete items
        buttonVsizer = wx.BoxSizer(wx.VERTICAL)

        a = wx.BitmapButton(panel, 302, wx.Bitmap(qcore.localPath("imres","up.png")))
        buttonVsizer.Add(a, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.Bind(wx.EVT_BUTTON, self.queuedSims.queueShiftUp, id=302)

        a = wx.BitmapButton(panel, 303, wx.Bitmap(qcore.localPath("imres","down.png")))
        buttonVsizer.Add(a, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 3)

        self.Bind(wx.EVT_BUTTON, self.queuedSims.queueShiftDown, id=303)

        a = wx.BitmapButton(panel, 304, wx.Bitmap(qcore.localPath("imres","kill.png")))
        buttonVsizer.Add(a, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.Bind(wx.EVT_BUTTON, self.queuedSims.queueKill, id=304)

        #Set parameters for users locally adding files, and add a button to help.

        a = wx.Button(panel, 305, "Add Sims")
        buttonVsizer.Add(a, 0, wx.ALIGN_CENTER | wx.TOP, 5)

        self.Bind(wx.EVT_BUTTON, self.GUIGetSims, id=305)

        text = wx.StaticText(panel, -1, "as")
        buttonVsizer.Add(text, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 3)

        self.consoleUser = wx.TextCtrl(panel, 306, value=self.dataManager.conf["consoleUsername"], style = wx.TE_CENTER)
        #Watch for updates here, too!
        self.Bind(wx.EVT_TEXT, self.updateConsoleUser, id=306)

        buttonVsizer.Add(self.consoleUser, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        #Incorporate the button sizer...
        queuedHsizer.Add(buttonVsizer, 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 10)

        titlingVsizer.Add(queuedHsizer, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)

        #Finally, add the queued-items/server sector to the bottom H sizer
        grandHsizer.Add(titlingVsizer, 1, wx.EXPAND | wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)

        grandHsizer.Add(wx.StaticLine(panel, -1, style=wx.LI_VERTICAL), 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 15)

        ##
        ### Server Sector
        ##

        #...gets its own vsizer!

        serverVsizer = wx.BoxSizer(wx.VERTICAL)


        sectorTitle = wx.StaticText(panel, -1, "OOMMFLink Server")
        sectorTitle.SetFont(BIG_FONT)
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(sectorTitle, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT, 10)

        portAnnouncement = wx.StaticText(panel, -1, "Serve OOMMFLink at")
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(portAnnouncement, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.ownPort = wx.StaticText(panel, -1, socket.gethostbyname_ex(socket.gethostname())[0])
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(self.ownPort, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 2)

        self.ownHostname = wx.StaticText(panel, -1, "( " + socket.gethostbyname_ex(socket.gethostname())[2][0] + " )")
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(self.ownHostname, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 2)

        portAnnouncement = wx.StaticText(panel, -1, "on port")
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(portAnnouncement, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        #Now, let's make the port configurable.
        self.serverPort = wx.TextCtrl(panel, 401, value=self.dataManager.conf["port"], style = wx.TE_CENTER)
        self.serverPort.SetMaxLength(5)
        serverVsizer.Add(self.serverPort, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.Bind(wx.EVT_TEXT, self.updateServerPort, id=401)

        self.autoServeCheck = wx.CheckBox(panel, 402, "Start server automatically")
        serverVsizer.Add(self.autoServeCheck, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.Bind(wx.EVT_CHECKBOX, self.updateServerAuto, id=402)

        self.startServerButton = wx.Button(panel, 403, "Start Server")
        serverVsizer.Add(self.startServerButton, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6)

        self.Bind(wx.EVT_BUTTON, self.toggleServer, id=403)

        self.serverStatus = wx.StaticText(panel, -1, "Server OFF")
        self.serverStatus.SetFont(BIG_FONT)
        self.serverStatus.SetForegroundColour(wx.RED)
        #You know, all this bordering code isn't the cleanest. Still not a GUI fan.
        serverVsizer.Add(self.serverStatus, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM , 4)



        #The server may already be running, in which case several controls must be disabled
        #and this section must be cleaned up a bit.
        if int(self.dataManager.conf["autoServe"]):
            self.autoServeCheck.SetValue(True)
            self.serverPort.Disable()
            self.startServerButton.SetLabel("Stop Server")
            self.serverStatus.SetForegroundColour(wx.GREEN)
            self.serverStatus.SetLabel("Server ON")

        #Server sector is done, goes into bottom group
        grandHsizer.Add(serverVsizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 10)

        #Add the bottom panel to the main...
        sizer.Add(grandHsizer, 4, wx.EXPAND | wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 2)

        ##
        ### Finish up
        ##

        #Cleanup initialization
        panel.SetSizer(sizer)
        self.Show()

        #And be ready to watch times!
        self.TimedUpdate()
        self.TimedSimCheck()

    def TimedUpdate(self):
        #Update active sim count
        self.activeSimCount.SetLabel(str(len(self.dataManager.runningSims)))

        #Update sim times
        with TimedUpdateOverlapLock:
            self.activeSims.refreshTimes()

        #Get ready to do it again
        wx.FutureCall(int(self.dataManager.conf["refreshTime"]), self.TimedUpdate)

    def TimedSimCheck(self):
        #Update active sim count
        if self.dataManager.checkSimsForCompletion():
            with TimedUpdateOverlapLock:
                self.activeSims.refreshData()
                self.queuedSims.refreshData()

        #Get ready to do it again
        wx.FutureCall(int(self.dataManager.conf["simRecheckTime"]), self.TimedSimCheck)

    def GUIGetSims(self, evt):
        dlg = wx.FileDialog(self, "Enqueue MIF Files", os.getcwd(), "", "OOMMF MIF File (*.mif)|*.mif",wx.OPEN | wx.FD_MULTIPLE)
        if dlg.ShowModal() == wx.ID_OK and dlg.GetFilenames():
            for file in dlg.GetFilenames():
                self.dataManager.enqueueSim(dlg.GetDirectory() + os.path.sep + file)
            self.queuedSims.refreshData()

    def GUILocateOOMMF(self, evt):
        #Use a dialog box to locate a copy of oommf.tcl
        dlg = wx.FileDialog(self, "Find OOMMF Location", os.getcwd(), "", "OOMMF TCL File (*.tcl)|*.tcl",wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK and dlg.GetFilename():
            filename = dlg.GetPath()
            self.updateOOMMFLocation(filename)
            self.confPush()
        try:
            self.panel.SendSizeEvent()
        except:
            print "wx.Panel.SendSizeEvent() missed - probably using old wxPython. Cosmetic bug will result."

    def updateTclsh(self, evt):
        self.dataManager.confUpdate("tclshCall", evt.GetString())

    def updateOOMMFLocation(self, path):
        self.dataManager.confUpdate("oommfPath", path)

    def updateMaxSimCount(self, evt):
        self.dataManager.confUpdate("maxSims", evt.GetString(), lazy=True)
        self.lazySaveTimer.Reboot()

    def updateConsoleUser(self, evt):
        self.dataManager.confUpdate("consoleUsername", evt.GetString(), lazy=True)
        self.lazySaveTimer.Reboot()

    def updateServerPort(self, evt):
        self.dataManager.confUpdate("port", evt.GetString(), lazy=True)
        self.lazySaveTimer.Reboot()

    def updateServerAuto(self, evt):
        if self.autoServeCheck.GetValue():
            self.dataManager.confUpdate("autoServe", "1")
        else:
            self.dataManager.confUpdate("autoServe", "0")

    def launchSims(self, evt):
        self.startSimsButton.Disable()
        self.dataManager.startMoreSims()

    def toggleServer(self, evt):
        if net.ServeManager.serverAlive:
            self.stopServer()
        else:
            self.startServer()

    def startServer(self):
        self.serverPort.Disable()
        net.ServeManager.startServing()
        self.startServerButton.SetLabel("Stop Server")
        self.serverStatus.SetForegroundColour(wx.GREEN)
        self.serverStatus.SetLabel("Server ON")

    def stopServer(self):
        self.serverPort.Enable()
        net.ServeManager.stopServing()
        self.startServerButton.SetLabel("Start Server")
        self.serverStatus.SetForegroundColour(wx.RED)
        self.serverStatus.SetLabel("Server OFF")

    def confPush(self):
        #Make sure all display values adequately reflect configuration data after a change
        self.OOMMFPathLabel.SetLabel(self.dataManager.conf["oommfPath"])
        self.TclCall.SetStringSelection(self.dataManager.conf["tclshCall"])

    def onClose(self, evt):
        #Make sure.
        net.ServeManager.reactorDown()
        self.dataManager.saveQueue()
        self.dataManager.writeConf()
        self.dataManager.goDown()
        self.Destroy()

    def clearErrors(self, evt):
        self.dataManager.clearErrors()
        with TimedUpdateOverlapLock:
            self.activeSims.refreshData()

    def showAbout(self, evt):
        info = wx.AboutDialogInfo()
        mydesc = """OOMMFq is a management system for queueing simulation files
for automatic launch, including simulation files sent remotely."
\nOOMMFq is part of OOMMFTools."""
        mylicense = """OOMMFq is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version."""
        info.SetName("OOMMFq")
        info.SetVersion("0.1a")
        info.SetDescription(mydesc)
        info.SetLicense(mylicense)
        info.SetCopyright('(C) 2010 Mark Mascaro')
        info.SetWebSite('http://web.mit.edu/daigohji/projects/OOMMFTools/')
        wx.AboutBox(info)

    def pingReactor(self, evt):
        net.ServeManager.netDoWork()

################################
# MAIN GUI SUPPORTING ELEMENTS #
################################

#Listctrl for currently running sims: username, filename, date
class ActiveSimListCtrl(wx.ListCtrl):
    def __init__(self, parent, id, dataparent):
        wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT)

        #Handle to the main GUI's data object
        self.dataManager = dataparent.dataManager

        self.InsertColumn(0, "Username")
        self.SetColumnWidth(0, 110)
        self.InsertColumn(1, "Filename")
        self.SetColumnWidth(1, 200)
        self.InsertColumn(2, "Start Time")
        self.SetColumnWidth(2, 138)
        self.InsertColumn(3, "Runtime", wx.LIST_FORMAT_RIGHT)
        self.SetColumnWidth(3, 80)

        #These watch for special cases where you need to refresh.
        self._lastRunCount = 0
        self._lastErrCount = 0

        self.refreshData()

    def refreshData(self):
        #Relatively small amount of work to save an order-matching headache later.
        #Wouldn't do this to a big list; not complaining here.

        self.DeleteAllItems()
        with qcore.QueueLock:
            for p, item in enumerate(self.dataManager.runningSims + self.dataManager.erroredSims):
                self.InsertStringItem(p, item.username)
                self.SetStringItem(p, 1, item.path)
                self.SetStringItem(p, 2, qcore.secsToDisplayTime(item.startTime))
                self.SetStringItem(p, 3, "0")

            #Highlight errored sims - these require attention!
            for p, item in enumerate(self.dataManager.erroredSims):
                #British.
                self.SetItemBackgroundColour(p+len(self.dataManager.runningSims), wx.RED)

                #Make a note of how many items you're tracking. If, somehow, you lose track of a sim being added or changed,
                #this will safely trigger a refresh.
                self._lastRunCount = len(self.dataManager.runningSims)
                self._lastErrCount = len(self.dataManager.erroredSims)



    def refreshTimes(self):
        #Sometimes, a sim changes out from under you.
        if not len(self.dataManager.runningSims) == self._lastRunCount and len(self.dataManager.erroredSims) == self._lastErrCount:
            #print "Log note: refreshing."
            self.refreshData()

        with qcore.QueueLock:
            for p, item in enumerate(self.dataManager.runningSims):
                #Convert the start-to-current time delta to something display-friendly
                self.SetStringItem(p, 3, qcore.secsToDuration(time.time(), item.startTime))

#A second listctrl for enqueued sims: username, filename. Nice and small - save room for options on the right.
class QueuedSimListCtrl(wx.ListCtrl):
    def __init__(self, parent, id, dataParent):
        wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT)

        #Handle to the main GUI's data object
        self.dataManager = dataParent.dataManager

        self.InsertColumn(0, "Username")
        self.SetColumnWidth(0, 110)
        self.InsertColumn(1, "Filename")
        self.SetColumnWidth(1, 200)

        self.refreshData()

    def refreshData(self):
        #Preserve selection items, since the worst that can happen is an add to the end of the list
        preserve = self.getSelected()
        #Relatively small amount of work to save an order-matching headache later.
        #Wouldn't do this to a big list; not complaining here.
        self.DeleteAllItems()

        for p, item in enumerate(self.dataManager.queuedSims):
            self.InsertStringItem(p, item.username)
            self.SetStringItem(p, 1, item.path)

        for item in preserve:
            self.Select(item)

    def getSelected(self):
        #Return the indices of all selected items!
        itemIndex = -1
        selItems = []
        while True:
            itemIndex = self.GetNextSelected(itemIndex)
            if itemIndex == -1:
                break
            else:
                selItems.append(itemIndex)
        return selItems

    def queueShiftUp(self, evt):
        old = set(self.getSelected())
        select = set(self.dataManager.listbump(self.getSelected(), -1))
        self.refreshData()

        #Do select deltas using set math, because laziness.
        for item in select - old:
            self.Select(item)
        for item in old - select:
            self.Select(item, False)


    def queueShiftDown(self, evt):
        old = set(self.getSelected())
        select = set(self.dataManager.listbump(self.getSelected(), 1))
        self.refreshData()

        #Do select deltas using set math, because laziness.
        for item in select - old:
            self.Select(item)
        for item in old - select:
            self.Select(item, False)

    def queueKill(self, evt):
        self.dataManager.clearQueueItems(self.getSelected())
        self.refreshData()

class OOMMFqDropTarget(wx.FileDropTarget):
    def __init__(self, parent):
        wx.FileDropTarget.__init__(self)
        self.parent = parent

    def OnDropFiles(self, x, y, filenames):
        #Find OOMMF, then a config, then files
        oommfFile = qcore.filterOnExtensions(["tcl"], filenames)
        if oommfFile:
            self.parent.updateOOMMFLocation(oommfFile[-1])
            #Just in case you do something *insane*, only the last one counts.
            #Also, this update doesn't go through the GUI, so we need to do a confpush to make sure it shows correct values
            self.parent.confPush()

        #OK, let's test our recursion powers - dive all supplied files and directories to scoop up MIF files!
        collectedSims = []
        for item in filenames:
            collectedSims.extend(qcore.MIFDiveAndScoop(item))

        #Load them all! We'll be using the console username for any files
        #dropped by the user - the username showing in the menu.
        #Therefore, we don't need to specify anything but the sim.
        for sim in collectedSims:
            self.parent.dataManager.enqueueSim(sim)
        if collectedSims:
            with TimedUpdateOverlapLock:
                self.parent.activeSims.refreshData()
                self.parent.queuedSims.refreshData()

class LazySaveTimer(wx.Timer):
    def __init__(self, alert):
        wx.Timer.__init__(self)
        self.Stop()
        self.alert = alert

    def Reboot(self):
        self.Start(2000, True)

    def Notify(self):
        self.alert.writeConf()
