普通文本  |  1432行  |  69.11 KB

"""
 File:  
 JetCreator.py
 
 Contents and purpose:
 Jet file creation utility for JET sound engine
 
 Copyright (c) 2008 Android Open Source Project
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
      http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
"""

from __future__ import with_statement

import wx
import sys
import thread
import copy
import wx.html
import operator

from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
from eas import *
from JetFile import *
from JetUtils import *
from JetCtrls import *
from JetDialogs import *
from JetSegGraph import SegmentGraph, Marker
from JetAudition import *
from JetStatusEvent import *

import img_favicon
import img_New

provider = wx.SimpleHelpProvider()
wx.HelpProvider_Set(provider)


class JetCreator(wx.Frame):
    """ Main window of JetCreator utility """
    def __init__(self, parent, id, jetConfigFile, importFlag=False):
        wx.Frame.__init__(self, parent, id, size=(1050, 720), style=wx.DEFAULT_FRAME_STYLE | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX)

        self.myicon = img_favicon.getIcon() 
        self.SetIcon(self.myicon) 
        self.UndoStack = []
        self.RedoStack = []
        self.queueSegs = []
        self.clipBoard = None
        self.jet = None
        self.playerLock = threading.RLock()
        self.SetKeepPlayingFlag(True)
        self.currentSegmentName = None
        self.currentSegmentIndex = None
        self.currentEventName = None
        self.currentEventIndex = None
        self.currentCtrl = ""
        self.currentJetConfigFile = jetConfigFile
        self.paused = False
        self.eventlistSort = (0, 1)
        self.seglistSort = (0, 1)
        if self.currentJetConfigFile == "":
            FileKillClean(JetDefs.UNTITLED_FILE)
            self.currentJetConfigFile = JetDefs.UNTITLED_FILE
        
        self.jet_file = JetFile(self.currentJetConfigFile, "")
        
        if not ValidateConfig(self.jet_file):
            FileKillClean(JetDefs.UNTITLED_FILE)
            self.currentJetConfigFile = JetDefs.UNTITLED_FILE
            self.jet_file = JetFile(self.currentJetConfigFile, "")

        if self.currentJetConfigFile == JetDefs.UNTITLED_FILE:
            self.LoadDefaultProperties()
            
        self.initLayout()
        self.initStatusBar()
        self.createMenuBar()
        self.createToolbar()
        self.SetCurrentFile(self.currentJetConfigFile)
        self.initHelp()
        
        self.graph.ClickCallbackFct = self.GraphTriggerClip
        
        EVT_JET_STATUS(self, self.OnJetStatusUpdate)
        
        wx.EVT_CLOSE(self, self.OnClose)

        self.Centre()
        self.Show(True)
        
        if importFlag:
            self.OnJetImportArchive(None)
     
        self.eventList.OnSortOrderChangedAlert = self.OnEventSortOrderChanged
        self.segList.OnSortOrderChangedAlert = self.OnSegSortOrderChanged
        
    def initLayout(self):
        """ Initializes the screen layout """
        panel = wx.Panel(self, -1)

        hboxMain = wx.BoxSizer(wx.HORIZONTAL)

        leftPanel = wx.Panel(panel, -1)
        leftTopPanel = wx.Panel(leftPanel, -1)
        leftBotPanel = wx.Panel(leftPanel, -1)
        rightPanel = wx.Panel(panel, -1)
        
        self.segList = JetCheckListCtrl(rightPanel)
        for title, width, fld in JetDefs.SEGMENT_GRID:
            self.segList.AddCol(title, width)

        self.eventList = JetListCtrl(rightPanel)
        for title, width, fld in JetDefs.CLIPS_GRID:
            self.eventList.AddCol(title, width)

        self.eventList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnEventListClick)
        self.segList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSegListClick)
        self.segList.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnSegmentUpdate)
        self.eventList.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnEventUpdate)
        
        self.segList.BindCheckBox(self.OnSegmentChecked)
        
        BUT_SIZE = (95, 25)
        self.btnAddSeg = wx.Button(leftTopPanel, -1, JetDefs.BUT_ADD, size=BUT_SIZE)
        self.btnRevSeg = wx.Button(leftTopPanel, -1, JetDefs.BUT_REVISE, size=BUT_SIZE)
        self.btnDelSeg = wx.Button(leftTopPanel, -1, JetDefs.BUT_DELETE, size=BUT_SIZE)
        self.btnMoveSeg = wx.Button(leftTopPanel, -1, JetDefs.BUT_MOVE, size=BUT_SIZE)

        self.btnQueueAll = wx.Button(leftTopPanel, -1, JetDefs.BUT_QUEUEALL, size=BUT_SIZE)
        self.btnDequeueAll = wx.Button(leftTopPanel, -1, JetDefs.BUT_DEQUEUEALL, size=BUT_SIZE)
        self.btnPlay = wx.Button(leftTopPanel, -1, JetDefs.BUT_PLAY, size=BUT_SIZE)
        self.btnPause = wx.Button(leftTopPanel, -1, JetDefs.BUT_PAUSE, size=BUT_SIZE)
        self.btnAudition = wx.Button(leftTopPanel, -1, JetDefs.BUT_AUDITION, size=BUT_SIZE)
        
        self.btnAddEvt = wx.Button(leftBotPanel, -1, JetDefs.BUT_ADD, size=BUT_SIZE)
        self.btnRevEvt = wx.Button(leftBotPanel, -1, JetDefs.BUT_REVISE, size=BUT_SIZE)
        self.btnDelEvt = wx.Button(leftBotPanel, -1, JetDefs.BUT_DELETE, size=BUT_SIZE)
        self.btnMoveEvents = wx.Button(leftBotPanel, -1, JetDefs.BUT_MOVE, size=BUT_SIZE)
        
        self.Bind(wx.EVT_BUTTON, self.OnSegmentAdd, id=self.btnAddSeg.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnSegmentUpdate, id=self.btnRevSeg.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnSegmentDelete, id=self.btnDelSeg.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnSegmentsMove, id=self.btnMoveSeg.GetId())

        self.Bind(wx.EVT_BUTTON, self.OnSelectAll, id=self.btnQueueAll.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnDeselectAll, id=self.btnDequeueAll.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnPlay, id=self.btnPlay.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnPause, id=self.btnPause.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnAudition, id=self.btnAudition.GetId())
        
        self.Bind(wx.EVT_BUTTON, self.OnEventAdd, id=self.btnAddEvt.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnEventUpdate, id=self.btnRevEvt.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnEventDelete, id=self.btnDelEvt.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnEventsMove, id=self.btnMoveEvents.GetId())
        
        BORDER = 5
        BUT_SPACE = 3
        vBoxLeftTop = wx.BoxSizer(wx.VERTICAL)
        vBoxLeftBot = wx.BoxSizer(wx.VERTICAL)

        vBoxLeftTop.Add(self.btnAddSeg, 0, wx.TOP, BORDER)
        vBoxLeftTop.Add(self.btnRevSeg, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnDelSeg, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnMoveSeg, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add((-1, 12))
        vBoxLeftTop.Add(self.btnQueueAll, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnDequeueAll, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnPlay, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnPause, 0, wx.TOP, BUT_SPACE)
        vBoxLeftTop.Add(self.btnAudition, 0, wx.TOP, BUT_SPACE)

        vBoxLeftBot.Add(self.btnAddEvt, 0)
        vBoxLeftBot.Add(self.btnRevEvt, 0, wx.TOP, BUT_SPACE)
        vBoxLeftBot.Add(self.btnDelEvt, 0, wx.TOP, BUT_SPACE)
        vBoxLeftBot.Add(self.btnMoveEvents, 0, wx.TOP, BUT_SPACE)
        
        leftTopPanel.SetSizer(vBoxLeftTop)
        leftBotPanel.SetSizer(vBoxLeftBot)

        vboxLeft = wx.BoxSizer(wx.VERTICAL)
        vboxLeft.Add(leftTopPanel, 1, wx.EXPAND)
        vboxLeft.Add(leftBotPanel, 1, wx.EXPAND)
        vboxLeft.Add((-1, 25))
        
        leftPanel.SetSizer(vboxLeft)

        self.log = wx.TextCtrl(rightPanel, -1)
        self.graph = SegmentGraph(rightPanel, size=(-1, 50))
        
        vboxRight = wx.BoxSizer(wx.VERTICAL)
        vboxRight.Add(self.segList, 4, wx.EXPAND | wx.TOP, BORDER)
        vboxRight.Add((-1, 10))
        vboxRight.Add(self.eventList, 3, wx.EXPAND | wx.TOP, BORDER)
        vboxRight.Add((-1, 10))
        vboxRight.Add(self.log, 0, wx.EXPAND)
        vboxRight.Add((-1, 5))
        vboxRight.Add(self.graph, 1, wx.EXPAND)
        vboxRight.Add((-1, 10))

        rightPanel.SetSizer(vboxRight)

        hboxMain.Add(leftPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT, BORDER)
        hboxMain.Add(rightPanel, 1, wx.EXPAND)
        hboxMain.Add((BORDER, -1))

        panel.SetSizer(hboxMain)

        pnlGraph = wx.Panel(leftBotPanel, -1)
        graphSizer1 = wx.BoxSizer(wx.VERTICAL)
        pnlGraph.SetSizer(graphSizer1)
        
        graphBox = wx.StaticBox(pnlGraph, wx.ID_ANY, label='Graph')
        graphSizer2 = wx.StaticBoxSizer(graphBox, wx.VERTICAL)

        self.chkGraphLabels = wx.CheckBox(pnlGraph, -1, JetDefs.GRAPH_LBLS)
        self.chkGraphClips = wx.CheckBox(pnlGraph, -1, JetDefs.GRAPH_TRIGGER)
        self.chkGraphAppEvts = wx.CheckBox(pnlGraph, -1, JetDefs.GRAPH_APP)
        
        graphSizer2.Add(self.chkGraphLabels, 0, wx.TOP, BUT_SPACE)
        graphSizer2.Add(self.chkGraphClips, 0, wx.TOP, BUT_SPACE)
        graphSizer2.Add(self.chkGraphAppEvts, 0, wx.TOP | wx.BOTTOM, BUT_SPACE)
        graphSizer1.Add((-1, 10))
        graphSizer1.Add(graphSizer2)

        vBoxLeftBot.Add(pnlGraph, 0, wx.TOP, BUT_SPACE)

        self.Bind(wx.EVT_CHECKBOX, self.OnSetGraphOptions, id=self.chkGraphLabels.GetId())
        self.Bind(wx.EVT_CHECKBOX, self.OnSetGraphOptions, id=self.chkGraphClips.GetId())
        self.Bind(wx.EVT_CHECKBOX, self.OnSetGraphOptions, id=self.chkGraphAppEvts.GetId())
        

    def initHelp(self):
        """ Initializes the help text for screen elements """
        self.SetHelpText(GetJetHelpText(JetDefs.MAIN_DLG_CTRLS, ''))
        self.segList.SetHelpText(GetJetHelpText(JetDefs.MAIN_DLG_CTRLS, JetDefs.MAIN_SEGLIST))
        self.eventList.SetHelpText(GetJetHelpText(JetDefs.MAIN_DLG_CTRLS, JetDefs.MAIN_EVENTLIST))
        self.graph.SetHelpText(GetJetHelpText(JetDefs.AUDITION_CTRLS, JetDefs.AUDITION_GRAPH))

    def initStatusBar(self):
        """ Initializes the status bar """
        self.statusbar = self.CreateStatusBar()

    def OnSelectAll(self, event):
        """ Called from select all button """
        num = self.segList.GetItemCount()
        for i in range(num-1, -1, -1):
            self.segList.CheckItem(i)

    def OnDeselectAll(self, event):
        """ Called from deselect all button """
        num = self.segList.GetItemCount()
        for i in range(num-1, -1, -1):
            self.segList.CheckItem(i, False)

    def LoadSegList(self):
        """ Loads up the list of segments """
        self.seglistSort = (IniGetValue(self.currentJetConfigFile, JetDefs.INI_SEGSORT, JetDefs.INI_SEGSORT_0, 'int', 0), IniGetValue(self.currentJetConfigFile, JetDefs.INI_SEGSORT, JetDefs.INI_SEGSORT_1, 'int', 1))
        segments = self.jet_file.GetSegments()
        if self.seglistSort[0] == 0:
            self.SegSort(segments, "segname")
        elif self.seglistSort[0] == 1:
            self.SegSort(segments, "filename")
        elif self.seglistSort[0] == 2:
            self.SegSort(segments, "dlsfile")
        elif self.seglistSort[0] == 3:
            self.SegSort(segments, "start")
        elif self.seglistSort[0] == 4:
            self.SegSort(segments, "end")
        elif self.seglistSort[0] == 5:
            self.SegSort(segments, "quantize")
        elif self.seglistSort[0] == 6:
            self.SegSort(segments, "transpose")
        elif self.seglistSort[0] == 7:
            self.SegSort(segments, "repeat")
        elif self.seglistSort[0] == 8:
            self.SegSort(segments, "mute_flags")
        listDataMap = []
        self.currentSegmentIndex = None
        self.currentSegmentName = None
        self.segList.DeleteAllItems()
        self.eventList.DeleteAllItems()
        self.menuItems[JetDefs.MNU_UPDATE_SEG].Enable(False)
        self.menuItems[JetDefs.MNU_DELETE_SEG].Enable(False)
        self.menuItems[JetDefs.MNU_MOVE_SEG].Enable(False)
        
        self.menuItems[JetDefs.MNU_ADD_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_MOVE_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_UPDATE_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_DELETE_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_MOVE_EVENT].Enable(False)
        for segment in self.jet_file.GetSegments():
            index = self.segList.InsertStringItem(sys.maxint, StrNoneChk(segment.segname))
            self.segList.SetStringItem(index, 1, FileJustName(StrNoneChk(segment.filename)))
            self.segList.SetStringItem(index, 2, FileJustName(StrNoneChk(segment.dlsfile)))
            self.segList.SetStringItem(index, 3, mbtFct(segment.start, 1))
            self.segList.SetStringItem(index, 4, mbtFct(segment.end, 1))
            self.segList.SetStringItem(index, 5, StrNoneChk(segment.quantize))
            self.segList.SetStringItem(index, 6, StrNoneChk(segment.transpose))
            self.segList.SetStringItem(index, 7, StrNoneChk(segment.repeat))
            self.segList.SetStringItem(index, 8, StrNoneChk(segment.mute_flags))
            
            self.segList.SetItemData(index, index)
            listDataMap.append((getColumnText(self.segList, index, 0).upper(), getColumnText(self.segList, index, 1).upper(), getColumnText(self.segList, index, 2).upper(), MbtVal(getColumnText(self.segList, index, 3)), MbtVal(getColumnText(self.segList, index, 4)), int(getColumnText(self.segList, index, 5)), int(getColumnText(self.segList, index, 6)), int(getColumnText(self.segList, index, 7)), int(getColumnText(self.segList, index, 8))))
            
        self.segList.itemDataMap = listDataMap
        self.segList.InitSorting(9)
        self.graph.ClearGraph()
            
    def LoadEventsForSeg(self, segName):
        """ Loads up the events associated with a segment """
        self.currentEventIndex = None
        self.eventList.DeleteAllItems()
        self.menuItems[JetDefs.MNU_UPDATE_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_DELETE_EVENT].Enable(False)
        self.menuItems[JetDefs.MNU_MOVE_EVENT].Enable(False)
        self.eventlistSort = (IniGetValue(self.currentJetConfigFile, JetDefs.INI_EVENTSORT, JetDefs.INI_EVENTSORT_0, 'int', 0), IniGetValue(self.currentJetConfigFile, JetDefs.INI_EVENTSORT, JetDefs.INI_EVENTSORT_1, 'int', 1))
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment is not None:
            if self.eventlistSort[0] == 0:
                self.EventSort(segment.jetevents, "event_name")
            elif self.eventlistSort[0] == 1:
                self.EventSort(segment.jetevents, "event_type")
            elif self.eventlistSort[0] == 2:
                self.EventSort(segment.jetevents, "event_start")
            elif self.eventlistSort[0] == 3:
                self.EventSort(segment.jetevents, "event_end")
            elif self.eventlistSort[0] == 4:
                self.EventSort(segment.jetevents, "track_num")
            elif self.eventlistSort[0] == 5:
                self.EventSort(segment.jetevents, "channel_num")
            elif self.eventlistSort[0] == 6:
                self.EventSort(segment.jetevents, "event_id")
            listDataMap = []
            for jet_event in self.jet_file.GetEvents(segName):
                index = self.eventList.InsertStringItem(sys.maxint, StrNoneChk(jet_event.event_name))
                self.eventList.SetStringItem(index, 1, StrNoneChk(jet_event.event_type))
                self.eventList.SetStringItem(index, 2, mbtFct(jet_event.event_start, 1))
                self.eventList.SetStringItem(index, 3, mbtFct(jet_event.event_end, 1))
                self.eventList.SetStringItem(index, 4, StrNoneChk(jet_event.track_num))
                self.eventList.SetStringItem(index, 5, StrNoneChk(jet_event.channel_num + 1))
                self.eventList.SetStringItem(index, 6, StrNoneChk(jet_event.event_id))
                
                self.eventList.SetItemData(index, index)
                listDataMap.append((getColumnText(self.eventList, index, 0).upper(), getColumnText(self.eventList, index, 1).upper(), MbtVal(getColumnText(self.eventList, index, 2)), MbtVal(getColumnText(self.eventList, index, 3)), int(getColumnText(self.eventList, index, 4)), int(getColumnText(self.eventList, index, 5)), int(getColumnText(self.eventList, index, 6))))
                    
            self.eventList.itemDataMap = listDataMap
            self.eventList.InitSorting(7)

    def OnEventListClick(self, event):
        """ Sets the current event variable when selecting from the list """
        self.currentCtrl = "eventList"
        self.currentEventIndex = event.m_itemIndex
        self.currentEventName = getColumnText(self.eventList, event.m_itemIndex, 0)       
        self.menuItems[JetDefs.MNU_UPDATE_EVENT].Enable(True)
        self.menuItems[JetDefs.MNU_DELETE_EVENT].Enable(True)
        self.menuItems[JetDefs.MNU_MOVE_EVENT].Enable(True)
               
    def OnSegmentChecked(self, index, checked):
        """ Selects the segment when checkbox clicked """
        ClearRowSelections(self.segList)
        SetRowSelection(self.segList, index, True)
        
    def SelectSegment(self, segName):
        """ Selects a segment by segment name """
        itm = self.segList.FindItem(-1, segName)
        self.segList.EnsureVisible(itm)
        ClearRowSelections(self.segList)
        SetRowSelection(self.segList, itm, True)
        
    def SelectEvent(self, eventName):
        """ Selects an event by event name """
        itm = self.eventList.FindItem(-1, eventName)
        self.eventList.EnsureVisible(itm)
        ClearRowSelections(self.eventList)
        SetRowSelection(self.eventList, itm, True)
        
    def OnSegListClick(self, event):
        """ Loads up a segment when the list is clicked """
        self.currentCtrl = "segList"
        self.currentSegmentIndex = event.m_itemIndex
        self.currentSegmentName = getColumnText(self.segList, event.m_itemIndex, 0)
        self.LoadEventsForSeg(getColumnText(self.segList, event.m_itemIndex, 0))
        self.menuItems[JetDefs.MNU_UPDATE_SEG].Enable(True)
        self.menuItems[JetDefs.MNU_DELETE_SEG].Enable(True)
        self.menuItems[JetDefs.MNU_MOVE_SEG].Enable(True)
        self.menuItems[JetDefs.MNU_ADD_EVENT].Enable(True)
        info = self.graph.LoadSegment(self.jet_file.GetSegment(self.currentSegmentName), showLabels=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'), showClips=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'), showAppEvts=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))
        if info == None:
            self.log.SetValue("")
        else:
            iLength = info.iLengthInMs
            if iLength > 0:
                self.log.SetValue("%s      %.2f Seconds" % (self.currentSegmentName, iLength / 1000.00))
            else:
                self.log.SetValue("%s" % (self.currentSegmentName))    
    
    def OnSegmentAdd(self, event):
        """ Calls the dialog box for adding a segment """
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        
        dlg = SegEdit(JetDefs.MAIN_ADDSEGTITLE, self.currentJetConfigFile)
       
        for filename in self.jet_file.GetMidiFiles():
            dlg.je.ctrls[JetDefs.F_MIDIFILE].Append(filename)
        for library in self.jet_file.GetLibraries():
            dlg.je.ctrls[JetDefs.F_DLSFILE].Append(library)
        
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            if len(dlg.lstReplicate) > 0:
                if dlg.chkReplaceMatching:
                    self.jet_file.DeleteSegmentsMatchingPrefix(dlg.replicatePrefix) 
                
                for replicate in dlg.lstReplicate:
                    self.jet_file.AddSegment(replicate[0], dlg.GetValue(JetDefs.F_MIDIFILE), 
                                             mbtFct(replicate[1],-1), mbtFct(replicate[2],-1), 
                                             JetDefs.MBT_ZEROSTR, 
                                             SegmentOutputFile(dlg.GetValue(JetDefs.F_SEGNAME), self.currentJetConfigFile), 
                                             dlg.GetValue(JetDefs.F_QUANTIZE),
                                             [], dlg.GetValue(JetDefs.F_DLSFILE),
                                             None,
                                             dlg.GetValue(JetDefs.F_TRANSPOSE),
                                             dlg.GetValue(JetDefs.F_REPEAT),
                                             dlg.GetValue(JetDefs.F_MUTEFLAGS))

                self.LoadSegList()
                self.SelectSegment(dlg.lstReplicate[0][0])
            else:
                self.jet_file.AddSegment(dlg.GetValue(JetDefs.F_SEGNAME), dlg.GetValue(JetDefs.F_MIDIFILE), 
                                     dlg.GetValue(JetDefs.F_START), dlg.GetValue(JetDefs.F_END), 
                                     JetDefs.MBT_ZEROSTR, 
                                     SegmentOutputFile(dlg.GetValue(JetDefs.F_SEGNAME), self.currentJetConfigFile), 
                                     dlg.GetValue(JetDefs.F_QUANTIZE),
                                     [], dlg.GetValue(JetDefs.F_DLSFILE),
                                     None,
                                     dlg.GetValue(JetDefs.F_TRANSPOSE),
                                     dlg.GetValue(JetDefs.F_REPEAT),
                                     dlg.GetValue(JetDefs.F_MUTEFLAGS))
                self.LoadSegList()
                self.SelectSegment(dlg.GetValue(JetDefs.F_SEGNAME))
            self.UndoAdd(saveState)
        dlg.Destroy()
        
    def OnSegmentUpdate(self, event):
        """ Calls the dialog box for updating a segment """
        if self.currentSegmentName is None:
            return
        
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment == None:
            return
        
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        
        dlg = SegEdit(JetDefs.MAIN_REVSEGTITLE, self.currentJetConfigFile)
       
        for filename in self.jet_file.GetMidiFiles():
            dlg.je.ctrls[JetDefs.F_MIDIFILE].Append(filename)
        for library in self.jet_file.GetLibraries():
            dlg.je.ctrls[JetDefs.F_DLSFILE].Append(library)
                    
        dlg.SetValue(JetDefs.F_SEGNAME, segment.segname)
        dlg.SetValue(JetDefs.F_MUTEFLAGS, segment.mute_flags)
        dlg.SetValue(JetDefs.F_MIDIFILE, segment.filename)
        dlg.SetValue(JetDefs.F_DLSFILE, segment.dlsfile)
        dlg.SetValue(JetDefs.F_START, segment.start)
        dlg.SetValue(JetDefs.F_END, segment.end)
        dlg.SetValue(JetDefs.F_QUANTIZE, segment.quantize)
        dlg.SetValue(JetDefs.F_TRANSPOSE, segment.transpose)
        dlg.SetValue(JetDefs.F_REPEAT, segment.repeat)
        dlg.jetevents = segment.jetevents
        
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            self.jet_file.UpdateSegment(self.currentSegmentName, dlg.GetValue(JetDefs.F_SEGNAME), 
                                     dlg.GetValue(JetDefs.F_MIDIFILE), 
                                     dlg.GetValue(JetDefs.F_START), dlg.GetValue(JetDefs.F_END), 
                                     JetDefs.MBT_ZEROSTR, #dlg.GetValue(JetDefs.F_LENGTH), 
                                     SegmentOutputFile(dlg.GetValue(JetDefs.F_SEGNAME), self.currentJetConfigFile), 
                                     dlg.GetValue(JetDefs.F_QUANTIZE),
                                     [], dlg.GetValue(JetDefs.F_DLSFILE),
                                     None,
                                     dlg.GetValue(JetDefs.F_TRANSPOSE),
                                     dlg.GetValue(JetDefs.F_REPEAT),
                                     dlg.GetValue(JetDefs.F_MUTEFLAGS))
            
            if len(dlg.lstReplicate) > 0:
                if dlg.chkReplaceMatching:
                    self.jet_file.DeleteSegmentsMatchingPrefix(dlg.replicatePrefix) 
                
                for replicate in dlg.lstReplicate:
                    self.jet_file.AddSegment(replicate[0], dlg.GetValue(JetDefs.F_MIDIFILE), 
                                             mbtFct(replicate[1],-1), mbtFct(replicate[2],-1), 
                                             JetDefs.MBT_ZEROSTR, 
                                             SegmentOutputFile(dlg.GetValue(JetDefs.F_SEGNAME), self.currentJetConfigFile), 
                                             dlg.GetValue(JetDefs.F_QUANTIZE),
                                             [], dlg.GetValue(JetDefs.F_DLSFILE),
                                             None,
                                             dlg.GetValue(JetDefs.F_TRANSPOSE),
                                             dlg.GetValue(JetDefs.F_REPEAT),
                                             dlg.GetValue(JetDefs.F_MUTEFLAGS))
    
                self.LoadSegList()
                self.SelectSegment(dlg.lstReplicate[0][0])
            else:          
                self.LoadSegList()
                self.SelectSegment(dlg.GetValue(JetDefs.F_SEGNAME))
            self.UndoAdd(saveState)
        dlg.Destroy()
        
    def OnSegmentDelete(self, event):
        """ Confirms the deletion segment(s) by user action """
        if self.currentSegmentName is None:
            return
        
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment == None:
            return
        
        count = 0
        deleteMsg = ''
        item = self.segList.GetFirstSelected()
        while item != -1:
            if count == 0:
                deleteMsg = getColumnText(self.segList,item,0)
            else:
                if count == 40:
                    deleteMsg = deleteMsg + "\n" + "....more"
                elif count < 40:
                    deleteMsg = deleteMsg + "\n" + getColumnText(self.segList,item,0)
            count = count + 1
            item = self.segList.GetNextSelected(item)
        
        if YesNo(JetDefs.MAIN_CONFIRM, deleteMsg + JetDefs.MAIN_CONFIRM_SEG_DLT, False):
            item = self.segList.GetFirstSelected()
            while item != -1:
                segName = getColumnText(self.segList,item,0)
                self.SegmentDelete(segName)
                item = self.segList.GetNextSelected(item)
            
            self.graph.ClearGraph()
            self.LoadSegList()
         
    def SegmentDelete(self, segName):
        """ Deletes a segment """
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        self.jet_file.DeleteSegment(segName)
        self.UndoAdd(saveState)
    
    def OnSegmentsMove(self, event):
        """ Move segment(s) """
        if self.currentSegmentName is None:
            return
        
        lstMoveItems = []
        count = 0
        item = self.segList.GetFirstSelected()
        while item != -1:
            max = GetMidiInfo(self.jet_file.GetSegment(getColumnText(self.segList,item,0)).filename).endMbtStr
            lstMoveItems.append((getColumnText(self.segList,item,0), mbtFct(getColumnText(self.segList,item,3),-1), mbtFct(getColumnText(self.segList,item,4),-1), max))
            count = count + 1
            item = self.segList.GetNextSelected(item)

        if count == 0:
            InfoMsg("Move", "Select one or more items to move.")
            return
        
        dlg = JetMove("Move Segments")
        dlg.lstMoveItems = lstMoveItems
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            if len(dlg.lstMoveMbt) > 0:
                saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
                
                for moveitem in dlg.lstMoveMbt:
                    self.jet_file.MoveSegment(moveitem[0], moveitem[1], moveitem[2])
                
                self.LoadSegList()
                self.UndoAdd(saveState)
                
        dlg.Destroy()
        
    def UndoAdd(self, saveState):
        """ Adds the current state to the undo stack """
        self.UndoStack.append(saveState)
        self.menuItems[JetDefs.MNU_UNDO].Enable(True)
       
    def RedoAdd(self, saveState):
        """ Adds the current state the the redo stack """
        self.RedoStack.append(saveState)
        self.menuItems[JetDefs.MNU_REDO].Enable(True)
       
    def OnRedo(self, event):
        """ Redo if there's one in the stack """
        if len(self.RedoStack) > 0:
            self.UndoAdd(JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex))
            state = self.RedoStack.pop()
            self.jet_file = copy.deepcopy(state.jet_file)
            self.LoadSegList()
            self.currentSegmentIndex = state.currentSegmentIndex
            if self.currentSegmentIndex != None:
                SetRowSelection(self.segList, self.currentSegmentIndex, True)
            if len(self.RedoStack) == 0:
                self.menuItems[JetDefs.MNU_REDO].Enable(False)
                
    def OnUndo(self, event):
        """ Undo if there's one in the stack """
        if len(self.UndoStack) > 0:
            self.RedoAdd(JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex))
            state = self.UndoStack.pop()
            self.jet_file = copy.deepcopy(state.jet_file)
            self.LoadSegList()
            self.currentSegmentIndex = state.currentSegmentIndex
            if self.currentSegmentIndex != None:
                SetRowSelection(self.segList, self.currentSegmentIndex, True)
            if len(self.UndoStack) == 0:
                self.menuItems[JetDefs.MNU_UNDO].Enable(False)

    def OnEventAdd(self, event):
        """ Calls a dialog box to add an event to the current segment """
        if self.currentSegmentName is None:
            return
        
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment == None:
            return
        
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        
        dlg = EventEdit(JetDefs.MAIN_ADDEVENTTITLE, self.currentJetConfigFile)
        editSegment = copy.deepcopy(segment)
        dlg.SetSegment(editSegment)
        dlg.SetEventId()
                      
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            if dlg.GetValue(JetDefs.F_ETYPE) == JetDefs.E_EOS:
                #check for an existing EOS event
                events = self.jet_file.GetEvents(self.currentSegmentName)
                for evt in events:
                    if evt.event_type == JetDefs.E_EOS:
                        self.jet_file.DeleteEvent(self.currentSegmentName, evt.event_name)
                dlg.SetValue(JetDefs.F_ESTART, dlg.GetValue(JetDefs.F_EEND))
            
            if len(dlg.lstReplicate) > 0:
                if dlg.chkReplaceMatching:
                    self.jet_file.DeleteEventsMatchingPrefix(self.currentSegmentName, dlg.replicatePrefix) 
                
                for replicate in dlg.lstReplicate:
                    self.jet_file.AddEvent(self.currentSegmentName, replicate[0],
                                         dlg.GetValue(JetDefs.F_ETYPE), 
                                         dlg.GetValue(JetDefs.F_EEVENTID), 
                                         dlg.GetValue(JetDefs.F_ETRACK), 
                                         dlg.GetValue(JetDefs.F_ECHANNEL), 
                                         mbtFct(replicate[1],-1),
                                         mbtFct(replicate[2],-1))
                self.SelectSegment(self.currentSegmentName)
                self.SelectEvent(dlg.lstReplicate[0][0])
            else:
                self.jet_file.AddEvent(self.currentSegmentName, dlg.GetValue(JetDefs.F_ENAME),
                                     dlg.GetValue(JetDefs.F_ETYPE), 
                                     dlg.GetValue(JetDefs.F_EEVENTID), 
                                     dlg.GetValue(JetDefs.F_ETRACK), 
                                     dlg.GetValue(JetDefs.F_ECHANNEL), 
                                     dlg.GetValue(JetDefs.F_ESTART),
                                     dlg.GetValue(JetDefs.F_EEND))
            
                self.SelectSegment(self.currentSegmentName)
                self.SelectEvent(dlg.GetValue(JetDefs.F_ENAME))
                
            self.UndoAdd(saveState)
        dlg.Destroy()
        
    def OnEventUpdate(self, event):
        """ Calls the dialog box to update the current event """
        if self.currentSegmentName is None:
            return

        if self.currentEventName  is None:
            return
        
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment == None:
            return
        
        curEvent = copy.deepcopy(self.jet_file.GetEvent(self.currentSegmentName, self.currentEventName))
        if curEvent == None:
            return
        
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        
        #only want the event we are editing to show up in graph
        editSegment = copy.deepcopy(segment)
        editSegment.jetevents = []
        editSegment.jetevents.append(curEvent)
        
        dlg = EventEdit(JetDefs.MAIN_REVEVENTTITLE, self.currentJetConfigFile)
        dlg.SetSegment(editSegment)
        dlg.SetValue(JetDefs.F_ENAME, curEvent.event_name)
        dlg.SetValue(JetDefs.F_ETYPE, curEvent.event_type)
        dlg.SetValue(JetDefs.F_ESTART, curEvent.event_start)
        dlg.SetValue(JetDefs.F_EEND, curEvent.event_end)
        dlg.SetValue(JetDefs.F_ETRACK, curEvent.track_num)
        dlg.SetValue(JetDefs.F_ECHANNEL, curEvent.channel_num)
        dlg.SetValue(JetDefs.F_EEVENTID, curEvent.event_id)
        dlg.OnEventSelect()   
        
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            if dlg.GetValue(JetDefs.F_ETYPE) == JetDefs.E_EOS:
                dlg.SetValue(JetDefs.F_ESTART, dlg.GetValue(JetDefs.F_EEND))
                
            self.jet_file.UpdateEvent(self.currentSegmentName, 
                                      self.currentEventName, 
                                     dlg.GetValue(JetDefs.F_ENAME),
                                     dlg.GetValue(JetDefs.F_ETYPE), 
                                     dlg.GetValue(JetDefs.F_EEVENTID), 
                                     dlg.GetValue(JetDefs.F_ETRACK), 
                                     dlg.GetValue(JetDefs.F_ECHANNEL), 
                                     dlg.GetValue(JetDefs.F_ESTART),
                                     dlg.GetValue(JetDefs.F_EEND))

            if len(dlg.lstReplicate) > 0:
                if dlg.chkReplaceMatching:
                    self.jet_file.DeleteEventsMatchingPrefix(self.currentSegmentName, dlg.replicatePrefix) 
                
                for replicate in dlg.lstReplicate:
                    self.jet_file.AddEvent(self.currentSegmentName, replicate[0],
                                         dlg.GetValue(JetDefs.F_ETYPE), 
                                         dlg.GetValue(JetDefs.F_EEVENTID), 
                                         dlg.GetValue(JetDefs.F_ETRACK), 
                                         dlg.GetValue(JetDefs.F_ECHANNEL), 
                                         mbtFct(replicate[1],-1),
                                         mbtFct(replicate[2],-1))
                self.SelectSegment(self.currentSegmentName)
                self.SelectEvent(dlg.lstReplicate[0][0])
            else:            
                self.SelectSegment(self.currentSegmentName)
                self.SelectEvent(dlg.GetValue(JetDefs.F_ENAME))
            self.UndoAdd(saveState)
        dlg.Destroy()
        
    def OnEventDelete(self, event):
        """ Confirms the deletion of event(s) """
        if self.currentSegmentName is None:
            return
        
        if self.currentEventName  is None:
            return
        
        curEvent = self.jet_file.GetEvent(self.currentSegmentName, self.currentEventName)
        if curEvent == None:
            return

        count = 0
        deleteMsg = ''
        item = self.eventList.GetFirstSelected()
        while item != -1:
            if count == 0:
                deleteMsg = getColumnText(self.eventList,item,0)
            else:
                if count == 40:
                    deleteMsg = deleteMsg + "\n" + "....more"
                elif count < 40:
                    deleteMsg = deleteMsg + "\n" + getColumnText(self.eventList,item,0)
            count = count + 1
            item = self.eventList.GetNextSelected(item)
        

        if YesNo(JetDefs.MAIN_CONFIRM, deleteMsg + JetDefs.MAIN_CONRIRM_EVT_DLT, False):
            item = self.eventList.GetFirstSelected()
            while item != -1:
                eventName = getColumnText(self.eventList,item,0)
                self.EventDelete(eventName)
                item = self.eventList.GetNextSelected(item)

            self.SelectSegment(self.currentSegmentName)
            self.LoadEventsForSeg(self.currentSegmentName)
          
    def EventDelete(self, eventName):
        """ Deletes an event """
        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
        self.jet_file.DeleteEvent(self.currentSegmentName, eventName)
        self.UndoAdd(saveState)

    def OnEventsMove(self, event):
        """ Move event(s) """
        if self.currentSegmentName is None:
            return
        
        if self.currentEventName  is None:
            return
        
        segment = self.jet_file.GetSegment(self.currentSegmentName)
        if segment == None:
            return
        
        curEvent = self.jet_file.GetEvent(self.currentSegmentName, self.currentEventName)
        if curEvent == None:
            return

        lstMoveItems = []
        count = 0
        item = self.eventList.GetFirstSelected()
        while item != -1:
            lstMoveItems.append((getColumnText(self.eventList,item,0), mbtFct(getColumnText(self.eventList,item,2),-1), mbtFct(getColumnText(self.eventList,item,3),-1), segment.end))
            count = count + 1
            item = self.eventList.GetNextSelected(item)

        if count == 0:
            InfoMsg("Move", "Select one or more items to move.")
            return
        
        dlg = JetMove("Move Events")
        dlg.lstMoveItems = lstMoveItems
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            if len(dlg.lstMoveMbt) > 0:
                saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
                
                for moveitem in dlg.lstMoveMbt:
                    self.jet_file.MoveEvent(self.currentSegmentName, moveitem[0], moveitem[1], moveitem[2])
                
                self.SelectSegment(self.currentSegmentName)
                self.LoadEventsForSeg(self.currentSegmentName)
                
                self.UndoAdd(saveState)
                
        dlg.Destroy()
        
    def OnJetOpen(self, event):
        """ Calls a dialog box to get a jet config file to open """
        dlg = JetOpen()
        result = dlg.ShowModal()
        if result == JetDefs.ID_JET_OPEN:
            self.jet_file = JetFile(dlg.fileName , "")
            if not ValidateConfig(self.jet_file):
                FileKillClean(JetDefs.UNTITLED_FILE)
                self.currentJetConfigFile = JetDefs.UNTITLED_FILE
                self.jet_file = JetFile(self.currentJetConfigFile, "")
            else:
                self.SetCurrentFile(dlg.fileName)
        elif result == JetDefs.ID_JET_NEW:
            self.jet_file = JetFile("" , "")
            self.SetCurrentFile(JetDefs.UNTITLED_FILE)
            self.LoadDefaultProperties()
        elif result == JetDefs.ID_JET_IMPORT:
            self.OnJetImportArchive(event)
        dlg.Destroy()

    def OnJetSaveAs(self, event): 
        """ Calls a dialog box to allow saving the current jet file as another name """
        defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.JTC_FILE_SPEC, 'str', str(os.getcwd()))
        dialog = wx.FileDialog(None, JetDefs.SAVE_PROMPT, defDir, "", JetDefs.JTC_FILE_SPEC, wx.SAVE | wx.OVERWRITE_PROMPT )
        if dialog.ShowModal() == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.JTC_FILE_SPEC, str(FileJustPath(dialog.GetPath())))
            self.currentJetConfigFile = FileJustRoot(dialog.GetPath()) + ".jtc"            
            self.jet_file.config.filename = FileJustRoot(self.currentJetConfigFile)  + ".jet"
            self.jet_file.SaveJetConfig(self.currentJetConfigFile)
            self.jet_file.WriteJetFileFromConfig(self.currentJetConfigFile)
            self.SetCurrentFile(self.currentJetConfigFile)
        dialog.Destroy()
    
    def OnJetSave(self, event):
        """ Saves the current jet file to disk """
        if self.currentJetConfigFile == JetDefs.UNTITLED_FILE:
            self.OnJetSaveAs(event)
        else:
            self.jet_file.SaveJetConfig(self.currentJetConfigFile)
            self.jet_file.WriteJetFileFromConfig(self.currentJetConfigFile)
        
    def OnJetNew(self, event):
        """ Initializes the state to a new jet file """
        self.jet_file = JetFile("" , "")
        self.SetCurrentFile(JetDefs.UNTITLED_FILE)
        self.LoadDefaultProperties()
                
    def SetCurrentFile(self, fileName):
        """ Sets the state for the current jet file """
        self.clipBoard = None
        self.currentJetConfigFile = fileName
        self.SetTitle(JetDefs.MAIN_TITLEPREFIX + FileJustName(fileName))
        AppendRecentJetFile(fileName)
        self.LoadSegList()
        self.graph.ClearGraph()
        self.chkGraphLabels.SetValue(IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'))
        self.chkGraphClips.SetValue(IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'))
        self.chkGraphAppEvts.SetValue(IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))

    def createMenuBar(self):
        """ Creates a menu bar """
        self.menuItems = {}
        menuBar = wx.MenuBar()
        for eachMenuData in JetDefs.MENU_SPEC:
            menuLabel = eachMenuData[0]
            menuItems = eachMenuData[1:]
            menuBar.Append(self.createMenu(menuItems), menuLabel)
        self.SetMenuBar(menuBar)

    def createMenu(self, menuData):
        """ Creates a menu from the structure menuData in JetDefs """
        menu = wx.Menu()
        for eachLabel, eachStatus, eachHandler, eachEnable in menuData:
            if not eachLabel:
                menu.AppendSeparator()
                continue
            self.menuItems[eachLabel] = menu.Append(-1, eachLabel, eachStatus)
            self.menuItems[eachLabel].Enable(eachEnable)
            try:
                self.Bind(wx.EVT_MENU, getattr(self, eachHandler) , self.menuItems[eachLabel])
            except:
                print("def " + eachHandler + "(self, event): pass")  
        return menu

    def createToolbar(self):
        """ Creates the toolbar """
        toolbar = self.CreateToolBar()
        toolbar.SetToolBitmapSize((32,32))
        self.toolItems = {}
        for eachTool in JetDefs.TOOLBAR_SPEC:
            if eachTool[0] == '-':
                toolbar.AddSeparator()
            else:
                b = __import__(eachTool[1])
                bitMap = b.getBitmap()
                self.toolItems[eachTool[0]] = toolbar.AddLabelTool(-1, label=eachTool[0], 
                        bitmap=bitMap, 
                                     shortHelp=eachTool[0], longHelp=eachTool[2])
                self.Bind(wx.EVT_TOOL, getattr(self, eachTool[3]) , self.toolItems[eachTool[0]])
        toolbar.Realize()
        
    def OnAudition(self, event):
        """ Calls the audition window for simple preview of jet file """
        jet_file = CreateTempJetFile(self.jet_file)

        w, h = self.GetSize()
        w = w - 50
        if w < 900:
            w = 900
        h = h - 50
        if h < 650:
            h = 650
        dlg = Audition(jet_file, (w,h))
        dlg.ShowModal()   
        CleanupTempJetFile(jet_file)
        
    def SetKeepPlayingFlag(self, val):
        """ Sets a flag to communicate playing state to the play thread """
        with self.playerLock:
            self.keepPlaying = val

    def GetKeepPlayingFlag(self):   
        """ Gets the playing state flag """   
        with self.playerLock:
            return self.keepPlaying
        
    def GraphTriggerClip(self, sClipName, iEventId):
        """ Triggers a clip when they click on the graph """
        with self.playerLock:
            try:
                self.jet.TriggerClip(iEventId)
                self.log.SetValue(JetDefs.PLAY_TRIGGERCLIP_MSG % (iEventId, sClipName))
                return True
            except:
                return False       
        
    def OnHelpJet(self, event):
        """ Loads the jet help file """
        import webbrowser
        webbrowser.open(JetDefs.MAIN_HELPFILE)
        return

    def OnHelpJetGuidelines(self, event):
        """ Loads the authoring guidelines file """
        import webbrowser
        webbrowser.open(JetDefs.MAIN_HELPGUIDELINESFILE)
        return
            
    def OnAbout(self, event): 
        """ Loads the about dialog box """
        dlg = JetAbout()
        result = dlg.ShowModal()
        dlg.Destroy()

    def OnJetImportArchive(self, event):
        """ Imports a jet archive file """
        defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC, 'str', str(os.getcwd()))
        dialog = wx.FileDialog(None, JetDefs.IMPORT_ARCHIVE_PROMPT, defDir, "", JetDefs.ARCHIVE_FILE_SPEC, wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC, str(FileJustPath(dialog.GetPath())))
            defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC + "Dir", 'str', str(os.getcwd()))
            dlg1 = wx.DirDialog(self, JetDefs.IMPORT_ARCHIVEDIR_PROMPT, style=wx.DD_DEFAULT_STYLE, defaultPath=defDir)
            if dlg1.ShowModal() == wx.ID_OK:
                IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC + "Dir", str(FileJustPath(dlg1.GetPath())))
                if YesNo(JetDefs.MAIN_IMPORTTITLE, JetDefs.MAIN_IMPORTMSG % (dialog.GetPath(),dlg1.GetPath()), False):
                    projectPath = dlg1.GetPath()
                    zipFile = dialog.GetPath()
                    z = __import__('zipfile')
                    
                    if not z.is_zipfile(zipFile):
                        wx.MessageBox(JetDefs.IMPORT_ARCHIVE_NO_JTC)
                    else:
                        zip = z.ZipFile(zipFile, 'r')
                        
                        jtcFile = ""
                        fileList = zip.namelist()
                        
                        isArchive = False
                        for myFile in fileList:
                            if myFile == 'JetArchive':
                                isArchive = True
                                break
                        if not isArchive:
                            wx.MessageBox(JetDefs.IMPORT_NOT_JET_ARCHIVE)
                        else:
                            for myFile in fileList:
                                if FileJustExt(myFile) == '.JTC':
                                    jtcFile = myFile
                                    break
                            if jtcFile == "":
                                wx.MessageBox(JetDefs.IMPORT_ARCHIVE_NO_JTC)
                            else:
                                for name in zip.namelist(): 
                                    ext = FileJustExt(name)
                                    if ext == '.MID' or ext == '.DLS'  or ext == '.JET':
                                        file(FileFixPath(projectPath + "/" + name), 'wb').write(zip.read(name))
                                    else:
                                        if len(ext) > 0 and ext != '.DS_STORE':
                                            file(FileFixPath(projectPath + "/" + name), 'w').write(zip.read(name))
                            zip.close()
                            self.currentJetConfigFile = FileFixPath(projectPath + "/") + jtcFile
                            self.jet_file = JetFile(self.currentJetConfigFile, "")
                            
                            #fix paths
                            self.jet_file.config.filename = FileJustRoot(self.currentJetConfigFile) + ".JET"
                            for index, segment in enumerate(self.jet_file.segments):
                                self.jet_file.segments[index].filename = FileFixPath(projectPath + "/" + segment.filename)
                                if segment.dlsfile > "":
                                    self.jet_file.segments[index].dlsfile = FileFixPath(projectPath + "/" + segment.dlsfile)
                                self.jet_file.segments[index].output = FileFixPath(projectPath + "/" + segment.output)                        
                            
                            for index, library in enumerate(self.jet_file.libraries):
                                self.jet_file.libraries[index] = FileFixPath(projectPath + "/" + library)
                                
                            if ValidateConfig(self.jet_file):
                                self.jet_file.SaveJetConfig(self.currentJetConfigFile)
                                self.jet_file.WriteJetFileFromConfig(self.currentJetConfigFile)
                                self.jet_file = JetFile(self.currentJetConfigFile , "")
                                self.SetCurrentFile(self.currentJetConfigFile)

            dlg1.Destroy()
        dialog.Destroy()        
       
    def OnJetExportArchive(self, event):
        """ Exports the current setup to an archive file """
        self.OnJetSave(event)
        defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC, 'str', str(os.getcwd()))
        dialog = wx.FileDialog(None, JetDefs.EXPORT_ARCHIVE_PROMPT, defDir, "", JetDefs.ARCHIVE_FILE_SPEC, wx.SAVE | wx.OVERWRITE_PROMPT )
        if dialog.ShowModal() == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, JetDefs.ARCHIVE_FILE_SPEC, str(FileJustPath(dialog.GetPath())))
            ExportJetArchive(FileJustRoot(dialog.GetPath()) + ".zip", self.currentJetConfigFile, self.jet_file)
        dialog.Destroy()
        
    def OnCopy(self, event):
        """ Copies the current segment or event to the clipboard """
        if self.currentCtrl == JetDefs.MAIN_SEGLIST:
            if self.currentSegmentName is None:
                return ""
            
            segment = self.jet_file.GetSegment(self.currentSegmentName)
            if segment == None:
                return ""
            self.clipBoard = JetCutCopy(self.currentCtrl, segment, self.currentSegmentName)
            return self.currentCtrl
        elif self.currentCtrl == JetDefs.MAIN_EVENTLIST:
            if self.currentSegmentName is None:
                return ""
    
            if self.currentEventName  is None:
                return ""
            
            segment = self.jet_file.GetSegment(self.currentSegmentName)
            if segment == None:
                return ""
            
            curEvent = self.jet_file.GetEvent(self.currentSegmentName, self.currentEventName)
            if curEvent == None:
                return ""
            self.clipBoard = JetCutCopy(self.currentCtrl, curEvent, self.currentSegmentName)
            return self.currentCtrl
            
    def OnCut(self, event):
        """ Cuts the current segment or event to the clipboard """
        retCopy = self.OnCopy(event)
        if retCopy == JetDefs.MAIN_SEGLIST:
            self.SegmentDelete(self.currentSegmentName) 
            self.LoadSegList()
        elif retCopy == JetDefs.MAIN_EVENTLIST:
            self.EventDelete(self.currentEventName)
            self.LoadEventsForSeg(self.currentSegmentName)
        
    def OnPaste(self, event):
        """ Pastes the current segment or event from the clipboard """
        if self.clipBoard is not None:
            if self.currentCtrl == JetDefs.MAIN_SEGLIST and self.clipBoard.objType == JetDefs.MAIN_SEGLIST:
                saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
                self.jet_file.segments.append(self.clipBoard.GetObj(self.jet_file.segments))
                self.UndoAdd(saveState)
                self.LoadSegList()
            elif self.currentCtrl == JetDefs.MAIN_EVENTLIST and self.clipBoard.objType == JetDefs.MAIN_EVENTLIST and self.clipBoard.currentSegmentName == self.currentSegmentName:
                for segment in self.jet_file.segments:
                    if segment.segname == self.currentSegmentName:
                        saveState = JetState(self.jet_file, self.currentSegmentIndex, self.currentEventIndex)
                        segment.jetevents.append(self.clipBoard.GetObj(segment.jetevents))
                        self.UndoAdd(saveState)
                        self.LoadEventsForSeg(self.currentSegmentName)
                        self.graph.LoadSegment(self.jet_file.GetSegment(self.currentSegmentName), showLabels=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'), showClips=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'), showAppEvts=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))
                        
    def OnJetProperties(self, event):
        """ Opens a dialog box for jetfile properties """
        dlg = JetPropertiesDialog()
        dlg.SetValue(JetDefs.F_JETFILENAME, self.jet_file.config.filename)
        dlg.SetValue(JetDefs.F_COPYRIGHT, StrNoneChk(self.jet_file.config.copyright))
        dlg.SetValue(JetDefs.F_CHASECONTROLLERS, StrNoneChk(self.jet_file.config.chase_controllers))
        dlg.SetValue(JetDefs.F_DELETEEMPTYTRACKS, StrNoneChk(self.jet_file.config.delete_empty_tracks))
        
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            self.jet_file.config.filename = dlg.je.ctrls[JetDefs.F_JETFILENAME].GetValue()
            self.jet_file.config.copyright = dlg.je.ctrls[JetDefs.F_COPYRIGHT].GetValue()
            self.jet_file.config.chase_controllers = dlg.je.ctrls[JetDefs.F_CHASECONTROLLERS].GetValue()
            self.jet_file.config.delete_empty_tracks = dlg.je.ctrls[JetDefs.F_DELETEEMPTYTRACKS].GetValue()
        dlg.Destroy()
       
    def OnPreferences(self, event):
        """ Opens a dialog box to capture preferences and saves them to a configuration file """
        dlg = JetPreferences()
        dlg.SetValue(JetDefs.F_COPYRIGHT, IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_COPYRIGHT))
        dlg.SetValue(JetDefs.F_CHASECONTROLLERS, IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_CHASECONTROLLERS, 'bool', True))
        dlg.SetValue(JetDefs.F_DELETEEMPTYTRACKS, IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_DELETEEMPTYTRACKS, 'bool', False))
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_COPYRIGHT, dlg.GetValue(JetDefs.F_COPYRIGHT))
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_CHASECONTROLLERS, dlg.GetValue(JetDefs.F_CHASECONTROLLERS))
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_DELETEEMPTYTRACKS, dlg.GetValue(JetDefs.F_DELETEEMPTYTRACKS))
        dlg.Destroy()

    def LoadDefaultProperties(self):
        """ Loads default properties from the a configuration file """
        self.jet_file.config.copyright = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_COPYRIGHT)
        self.jet_file.config.chase_controllers = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_CHASECONTROLLERS, 'bool', True)
        self.jet_file.config.delete_empty_tracks = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_PREF_SECTION, JetDefs.F_DELETEEMPTYTRACKS, 'bool', False)               
                
    def OnClose(self, event):
        """ Called upon closing the JetCreator main window """
        if self.isDirty():
            ret = YesNoCancel(JetDefs.MAIN_JETCREATOR, JetDefs.MAIN_SAVEBEFOREEXIT, True)
            if ret == wx.ID_YES:
                self.OnJetSave(None)
            if ret == wx.ID_CANCEL:
                return
            
        if self.jet is not None:
            SafeJetShutdown(self.playerLock, self.jet)                
        self.Destroy()
        
    def OnPlay(self, event):
        """ Plays the currently queued segments """
        if self.btnPlay.GetLabel() == JetDefs.BUT_PLAY:
            if not ValidateConfig(self.jet_file):
                return
            
            #make sure something is queued
            iQueued = False
            num = self.segList.GetItemCount()
            for seg_num in range(num):
                if self.segList.IsChecked(seg_num):
                    iQueued = True
            if not iQueued:
                InfoMsg(JetDefs.MAIN_PLAYSEG, JetDefs.MAIN_PLAYSEGMSG)
                return
        
            for segment in self.jet_file.segments:
                if FileExists(segment.dlsfile):
                    if not segment.dlsfile in self.jet_file.libraries:
                        self.jet_file.libraries.append(segment.dlsfile)
                    
            self.eventList.DeleteAllItems()
            num = self.segList.GetItemCount()
            for seg_num in range(num):
                if seg_num == 0: self.log.Clear()
                if self.segList.IsChecked(seg_num):
                    segment = self.jet_file.GetSegment(getColumnText(self.segList, seg_num, 0))
                    if segment != None:
                        #so we can determine which segment is playing, make these the same
                        userID = seg_num
                        if FileExists(segment.dlsfile):
                            dls_num = FindDlsNum(self.jet_file.libraries, segment.dlsfile)
                        else:
                            dls_num = -1
                        self.queueSegs.append(QueueSeg(segment.segname, userID, seg_num, dls_num, segment.repeat, segment.transpose, segment.mute_flags))
                    
            if len(self.queueSegs) == 0:
                return
            
            self.btnPlay.SetLabel(JetDefs.BUT_STOP)
            
            thread.start_new_thread(self.PlaySegs, ())
        else:
            with self.playerLock:
                self.jet.Clear_Queue()
            self.SetKeepPlayingFlag(False)
    
    def PlaySegs(self):
        """ Thread writes a temporary copy of the jet file, and calls the library to play it """
        if len(self.queueSegs) == 0:
            return
        
        jet_file = CreateTempJetFile(self.jet_file)
        
        if not ValidateConfig(jet_file):
            CleanupTempJetFile(jet_file)
            return
             
        self.jet = JET()
        self.jet.eas.StartWave()
        self.jet.OpenFile(jet_file.config.filename)

        lastID = -1

        # queue first segment and start playback
        Queue(self.jet, self.queueSegs[0])
        index = 1
        self.jet.Play()
        self.paused = False
    
        # continue playing until all segments are done
        self.SetKeepPlayingFlag(True)
        while self.GetKeepPlayingFlag():
            self.jet.Render()
            status = self.jet.Status()
            
            if status.currentUserID <> lastID and status.currentUserID <> -1:
                wx.PostEvent(self, JetStatusEvent(JetDefs.PST_PLAY, status.currentUserID))
                lastID = status.currentUserID

            # if no more segments - we're done
            if status.numQueuedSegments == 0:
                break
    
            self.jet.GetAppEvent()
    
            # if less than 2 segs queued - queue another one            
            if (index < len(self.queueSegs)) and (status.numQueuedSegments < 2):
                Queue(self.jet, self.queueSegs[index])
                index += 1
                
            wx.PostEvent(self, JetStatusEvent(JetDefs.PST_UPD_LOCATION, status.location))
        
        
        SafeJetShutdown(self.playerLock, self.jet)   
        
        self.queueSegs = []
        
        CleanupTempJetFile(jet_file)
        
        wx.PostEvent(self, JetStatusEvent(JetDefs.PST_DONE, None))
                
    def OnJetStatusUpdate(self, evt):
        """ These are screen updates called for from within the thread.  Communication 
            is via a postevent call.
        """
        if evt.mode == JetDefs.PST_PLAY:
            segName = getColumnText(self.segList, evt.data, 0)
            self.LoadEventsForSeg(segName)
            self.log.SetValue(segName)
            ClearRowSelections(self.segList)
            SetRowSelection(self.segList, evt.data, True)
        elif evt.mode == JetDefs.PST_UPD_LOCATION:
            self.graph.UpdateLocation(evt.data)
        elif evt.mode == 3:
            self.graph.UpdateLocation(0)
            ClearRowSelections(self.segList)
            self.eventList.DeleteAllItems()
            self.SetKeepPlayingFlag(False)
            self.btnPlay.SetLabel(JetDefs.BUT_PLAY)
            self.btnPause.SetLabel(JetDefs.BUT_PAUSE)            
            
    def OnPause(self, evt):
        """ Pauses the playback """
        if self.jet is None:
            return
        if not self.paused:
            self.jet.Pause()
            self.paused = True
            self.btnPause.SetLabel(JetDefs.BUT_RESUME)
        else:
            self.jet.Play()
            self.paused = False
            self.btnPause.SetLabel(JetDefs.BUT_PAUSE)
           
    def isDirty(self):
        if len(self.UndoStack) == 0 and len(self.RedoStack) == 0:
            return False
        else:
            return True
        
    def OnSetGraphOptions(self, evt):
        """ Sets graph options """
        IniSetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, self.chkGraphLabels.GetValue())
        IniSetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, self.chkGraphClips.GetValue())
        IniSetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, self.chkGraphAppEvts.GetValue())
        self.graph.LoadSegment(self.jet_file.GetSegment(self.currentSegmentName), showLabels=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'), showClips=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'), showAppEvts=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))

    def OnEventSortOrderChanged(self):
        """ Called when sort order has changed """
        IniSetValue(self.currentJetConfigFile, JetDefs.INI_EVENTSORT, JetDefs.INI_EVENTSORT_0, self.eventList.GetSortState()[0])
        IniSetValue(self.currentJetConfigFile, JetDefs.INI_EVENTSORT, JetDefs.INI_EVENTSORT_1, self.eventList.GetSortState()[1])
        self.LoadEventsForSeg(self.currentSegmentName)
        self.graph.LoadSegment(self.jet_file.GetSegment(self.currentSegmentName), showLabels=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'), showClips=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'), showAppEvts=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))

    def EventSortCmp(self, a, b):
        """ Sorts based on selected sort order """
        if self.eventlistSort[0] == 0 or self.eventlistSort[0] == 1:
            if self.eventlistSort[1] == 1:
                return cmp(a[0].upper(), b[0].upper())
            else:
                return cmp(b[0].upper(), a[0].upper())                
        elif self.eventlistSort[0] == 2 or self.eventlistSort[0] == 3: 
            if self.eventlistSort[1] == 1:
                return cmp(MbtVal(a[0]), MbtVal(b[0]))
            else:
                return cmp(MbtVal(b[0]), MbtVal(a[0]))   
        else:
            return cmp(a[0], b[0])             

    def EventSort2(self, seq, attr):
        """ Does Sorting """
        intermed = map(None, map(getattr, seq, (attr,)*len(seq)), xrange(len(seq)), seq)
        intermed.sort(self.EventSortCmp)
        return map(operator.getitem, intermed, (-1,) * len(intermed))

    def EventSort(self, lst, attr):
        """ Does Sorting """
        lst[:] = self.EventSort2(lst, attr)
        
    def OnSegSortOrderChanged(self):
        """ Called when sort order has changed """
        IniSetValue(self.currentJetConfigFile, JetDefs.INI_SEGSORT, JetDefs.INI_SEGSORT_0, self.segList.GetSortState()[0])
        IniSetValue(self.currentJetConfigFile, JetDefs.INI_SEGSORT, JetDefs.INI_SEGSORT_1, self.segList.GetSortState()[1])
        self.LoadEventsForSeg(self.currentSegmentName)
        self.graph.LoadSegment(self.jet_file.GetSegment(self.currentSegmentName), showLabels=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHLABELS, JetDefs.F_GRAPHLABELS, 'bool', 'True'), showClips=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHCLIPS, JetDefs.F_GRAPHCLIPS, 'bool', 'True'), showAppEvts=IniGetValue(self.currentJetConfigFile, JetDefs.F_GRAPHAPPEVTS, JetDefs.F_GRAPHAPPEVTS, 'bool', 'True'))

    def SegSortCmp(self, a, b):
        """ Sorts based on selected sort order """
        if self.seglistSort[0] == 0:
            if self.seglistSort[1] == 1:
                return cmp(a[0].upper(), b[0].upper())
            else:
                return cmp(b[0].upper(), a[0].upper())   
        elif self.seglistSort[0] == 1 or self.seglistSort[0] == 2:   
            if self.seglistSort[1] == 1:
                return cmp(FileJustName(a[0]).upper(), FileJustName(b[0]).upper())
            else:
                return cmp(FileJustName(b[0]).upper(), FileJustName(a[0]).upper())   
        elif self.seglistSort[0] == 3 or self.seglistSort[0] == 4: 
            if self.seglistSort[1] == 1:
                return cmp(MbtVal(a[0]), MbtVal(b[0]))
            else:
                return cmp(MbtVal(b[0]), MbtVal(a[0]))   
        else:
            return cmp(a[0], b[0])             

    def SegSort2(self, seq, attr):
        """ Does Sorting """
        intermed = map(None, map(getattr, seq, (attr,)*len(seq)), xrange(len(seq)), seq)
        intermed.sort(self.SegSortCmp)
        return map(operator.getitem, intermed, (-1,) * len(intermed))

    def SegSort(self, lst, attr):
        """ Does Sorting """
        lst[:] = self.SegSort2(lst, attr)

if __name__ == '__main__':
    """ Sets the logging level, then calls the open dialog box before initializing main window"""
    
    logLevel = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_LOGGING, JetDefs.INI_LOGGING)
    if logLevel == 'DEBUG':
        logging.basicConfig(level=logging.DEBUG, format='%(funcName)s[%(lineno)d]: %(message)s')
    elif logLevel == 'INFO':
        logging.basicConfig(level=logging.INFO, format='%(funcName)s[%(lineno)d]: %(message)s')
    elif logLevel == 'ERROR':
        logging.basicConfig(level=logging.ERROR, format='%(funcName)s[%(lineno)d]: %(message)s')
    elif logLevel == 'WARNING':
        logging.basicConfig(level=logging.WARNING, format='%(funcName)s[%(lineno)d]: %(message)s')
    else:
        install_release_loggers()
        
    app = wx.App(None)
    
    if not os.path.isdir(JetDefs.TEMP_JET_DIR):
        os.mkdir(JetDefs.TEMP_JET_DIR)
        
    openFile = ""
    dlg = JetOpen()
    result = dlg.ShowModal()
    if result == wx.ID_CANCEL:
        dlg.Destroy()
    elif result == JetDefs.ID_JET_IMPORT:
        dlg.Destroy()
    
        au = JetCreator(None, -1, "", importFlag=True)
        app.MainLoop()
    else:
        openFile = dlg.fileName
        dlg.Destroy()
    
        au = JetCreator(None, -1, openFile)
        app.MainLoop()