Home | History | Annotate | Download | only in JetCreator
      1 """
      2  File:  
      3  JetCtrls.py
      4  
      5  Contents and purpose:
      6  Auditions a jet file to simulate interactive music functions
      7  
      8  Copyright (c) 2008 Android Open Source Project
      9  
     10  Licensed under the Apache License, Version 2.0 (the "License");
     11  you may not use this file except in compliance with the License.
     12  You may obtain a copy of the License at
     13  
     14       http://www.apache.org/licenses/LICENSE-2.0
     15  
     16  Unless required by applicable law or agreed to in writing, software
     17  distributed under the License is distributed on an "AS IS" BASIS,
     18  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     19  See the License for the specific language governing permissions and
     20  limitations under the License.
     21 """
     22 
     23 import wx
     24 import sys
     25 
     26 from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin, ColumnSorterMixin
     27 from JetUtils import *
     28 from JetDefs import *
     29 
     30 class JetSpin(wx.SpinCtrl):
     31     """ Spin control """
     32     def __init__(self, parent, id=-1,value=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize,style=wx.SP_ARROW_KEYS,min=0,max=100,initial=0):
     33         wx.SpinCtrl.__init__(self, parent, id=id,value=value,pos=(pos[0]-MacOffset(),pos[1]),size=size,style=style,min=min,max=max,initial=initial)
     34 
     35     def SetValue(self, val):
     36         try:
     37             if type(val).__name__=='str':
     38                 wx.SpinCtrl.SetValue(self, int(val))
     39             else:
     40                 wx.SpinCtrl.SetValue(self, val)
     41         except:
     42             wx.SpinCtrl.SetValue(self, 0)
     43             
     44 class JetSpinOneBased(JetSpin):
     45     """ Spin control that's one based """
     46     def __init__(self, parent, id=-1,value=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize,style=wx.SP_ARROW_KEYS,min=0,max=100,initial=0):
     47         wx.SpinCtrl.__init__(self, parent, id=id,value=value,pos=(pos[0]-MacOffset(),pos[1]),size=size,style=style,min=min,max=max,initial=initial)
     48 
     49     def SetValue(self, val):
     50         try:
     51             if type(val).__name__=='str':
     52                 wx.SpinCtrl.SetValue(self, int(val) + 1)
     53             else: 
     54                 wx.SpinCtrl.SetValue(self, val + 1)
     55         except:
     56             wx.SpinCtrl.SetValue(self, 1)
     57             
     58     def GetValue(self):
     59         val = wx.SpinCtrl.GetValue(self)
     60         val = val - 1
     61         return val
     62             
     63 class JetCheckBox(wx.CheckBox):
     64     """ Checkbox control """
     65     def __init__(self, parent, id=-1,label=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize):
     66         wx.CheckBox.__init__(self, parent, id=id, label=label, pos=pos, size=size)
     67         
     68     def SetValue(self, val):
     69         try:
     70             if type(val).__name__=='str':
     71                 if val == 'True':
     72                     val = True
     73                 else:
     74                     val = False
     75                 wx.CheckBox.SetValue(self, val)
     76             else:
     77                 wx.CheckBox.SetValue(self, val)
     78         except:
     79             wx.CheckBox.SetValue(self, False)
     80              
     81 class JetRadioButton(wx.RadioButton):
     82     """ Radio button control """
     83     def __init__(self, parent, id=-1,label=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize):
     84         wx.RadioButton.__init__(self, parent, id=id, label=label, pos=pos, size=size)
     85         
     86     def SetValue(self, val):
     87         try:
     88             if type(val).__name__=='str':
     89                 if val == 'True':
     90                     val = True
     91                 else:
     92                     val = False
     93                 wx.RadioButton.SetValue(self, val)
     94             else:
     95                 wx.RadioButton.SetValue(self, val)
     96         except:
     97             wx.RadioButton.SetValue(self, False)
     98              
     99 class JetListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin):
    100     """ List control """
    101     def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
    102         wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
    103         ListCtrlAutoWidthMixin.__init__(self)
    104         self.iCol = 0
    105         self.iWidth = 0
    106         self.OnSortOrderChangedAlert = None
    107         self.iInitialized = False
    108 
    109     def AddCol(self, title, width):
    110         self.InsertColumn(self.iCol, title)
    111         if width > 0:
    112             self.SetColumnWidth(self.iCol, width)
    113         else:
    114             width = self.GetColumnWidth(self.iCol)
    115         self.iCol += 1
    116         self.iWidth = self.iWidth + width
    117         self.SetSize((self.iWidth + 10, -1))
    118 
    119     def AddRows(self, values):
    120         for value in values:
    121             iCol = 0
    122             for row in value:
    123                 if iCol == 0:
    124                     index = self.InsertStringItem(sys.maxint, row)
    125                 else:
    126                     self.SetStringItem(index, iCol, row) 
    127                 iCol = iCol + 1
    128 
    129     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py

    130     def GetListCtrl(self):
    131         return self
    132     
    133     def InitSorting(self, cols):
    134         if not self.iInitialized:
    135             ColumnSorterMixin.__init__(self, cols)
    136             self.iInitialized = True
    137         
    138     def OnSortOrderChanged(self):
    139         if self.OnSortOrderChangedAlert is not None:
    140             self.OnSortOrderChangedAlert()
    141 
    142     def __OnColClick(self, evt):
    143         oldCol = self._col
    144         self._col = col = evt.GetColumn()
    145         self._colSortFlag[col] = int(not self._colSortFlag[col])
    146         self.OnSortOrderChanged()
    147         
    148 class JetCheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin, ColumnSorterMixin):
    149     """ List control with checkboxes on each line """
    150     def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT | wx.SUNKEN_BORDER):
    151         wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=style)
    152         CheckListCtrlMixin.__init__(self)
    153         ListCtrlAutoWidthMixin.__init__(self)
    154 
    155         self.iCol = 0
    156         self.iWidth = 0
    157         self.OnSortOrderChangedAlert = None
    158         self.iInitialized = False
    159 
    160     def AddCol(self, title, width):
    161         self.InsertColumn(self.iCol, title)
    162         if width > 0:
    163             self.SetColumnWidth(self.iCol, width)
    164         else:
    165             width = self.GetColumnWidth(self.iCol)
    166         self.iCol += 1
    167         self.iWidth = self.iWidth + width
    168         self.SetSize((self.iWidth + 10, -1))
    169 
    170     def OnCheckItem(self, index, flag):
    171         if hasattr(self, 'BindCheckBoxFct'):
    172             self.BindCheckBoxFct(index, flag)
    173         
    174     def BindCheckBox(self, fct):
    175         self.BindCheckBoxFct = fct
    176 
    177     def AddRows(self, values):
    178         for value in values:
    179             iCol = 0
    180             for row in value:
    181                 if iCol == 0:
    182                     index = self.InsertStringItem(sys.maxint, row)
    183                 else:
    184                     self.SetStringItem(index, iCol, row) 
    185                 iCol = iCol + 1
    186 
    187     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py

    188     def GetListCtrl(self):
    189         return self
    190     
    191     def InitSorting(self, cols):
    192         if not self.iInitialized:
    193             ColumnSorterMixin.__init__(self, cols)
    194             self.iInitialized = True
    195      
    196     def OnSortOrderChanged(self):
    197         if self.OnSortOrderChangedAlert is not None:
    198             self.OnSortOrderChangedAlert()
    199         
    200     def __OnColClick(self, evt):
    201         oldCol = self._col
    202         self._col = col = evt.GetColumn()
    203         self._colSortFlag[col] = int(not self._colSortFlag[col])
    204         self.OnSortOrderChanged()
    205 
    206 class JetTrackCtrl(JetCheckListCtrl):
    207     """ List control specifically designed to show tracks in midi file """
    208     def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT | wx.SUNKEN_BORDER):
    209         wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=style)
    210         CheckListCtrlMixin.__init__(self)
    211         ListCtrlAutoWidthMixin.__init__(self)
    212 
    213         self.iCol = 0
    214         self.iWidth = 0
    215         self.muteFlags = 0
    216 
    217     def SetValue(self, muteFlags):
    218         self.muteFlags = muteFlags
    219         
    220     def GetValue(self):
    221         return self.muteFlags
    222 
    223     def CheckTracks(self, muteFlags):
    224         num = self.GetItemCount()
    225         for iRow in range(num):
    226             track_num = self.GetTrackNumber(iRow)
    227             self.CheckItem(iRow, GetMute(track_num, muteFlags))
    228             
    229     def AddTrackRow(self, track, loadEmpty=False):
    230         if loadEmpty or not track.empty: 
    231             index = self.InsertStringItem(sys.maxint, str(track.track))
    232             self.SetStringItem(index, 1, str(track.channel))
    233             self.SetStringItem(index, 2, str(track.name))
    234                
    235     def GetTrackNumber(self, index):
    236         return getColumnValue(self, index, 0)
    237                           
    238 class JetFileCombo():
    239     """ Combo box with file open button """
    240     def __init__(self, parent, pos=(0,0), size=(200,-1), title='Open File', spec='*.*', id=-1):
    241         self.spec = spec
    242         self.title = title
    243         self.EventFire = False
    244         BUTWIDTH = 20
    245         BORDER = 5
    246         w = size[0] - (BUTWIDTH + BORDER)
    247         col = pos[0] + w + BORDER
    248         
    249         self.cmb = wx.ComboBox(parent, id, "", pos=(pos[0]-MacOffset(),pos[1]), size=(w, -1), style=wx.CB_DROPDOWN)
    250         self.btn = wx.Button(parent, -1, "...", pos=(col, pos[1]+MacOffset()), size=(BUTWIDTH,self.cmb.GetSize()[1]))
    251         self.btn.Bind(wx.EVT_BUTTON, self.OnBrowse, self.btn) 
    252 
    253     def OnBrowse(self, event):
    254         os = __import__('os')
    255         defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, 'str', str(os.getcwd()))
    256         if OsWindows():
    257             defDir = defDir.replace('/','\\')
    258         else:
    259             defDir = defDir.replace('\\', '/')
    260             
    261         dlg = wx.FileDialog(None, self.title, defDir, '', self.spec, wx.FD_OPEN)
    262         ret = dlg.ShowModal()
    263         if ret == wx.ID_OK:
    264             IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, str(FileJustPath(dlg.GetPath())))
    265             val = dlg.GetPath()
    266 
    267             self.Append(val)
    268             self.cmb.SetValue(val)
    269             
    270             if self.EventFire:
    271                 SendEvent(self.cmb, wx.EVT_COMBOBOX.evtType[0])
    272         dlg.Destroy()
    273         
    274     def SetEventFire(self, fire):
    275         self.EventFire = fire
    276         
    277     def GetValue(self):
    278         return StrNoneChk(self.cmb.GetValue())   
    279     
    280     def SetValue(self, val):
    281         try:
    282             self.cmb.SetValue(val)
    283         except:
    284             pass
    285 
    286     def Append(self, val):
    287         try:
    288             self.cmb.Append(val)
    289         except:
    290             pass
    291         
    292     def SetFocus(self):
    293         self.cmb.SetFocus()
    294         
    295     def SetListValues(self, list):
    296         self.cmb.AppendItems(list)
    297         
    298     def Enable(self, enable):
    299         self.cmb.Enable(enable)
    300         self.btn.Enable(enable)
    301                 
    302     def SetHelpText(self, Lbl):
    303         self.cmb.SetHelpText(Lbl)
    304         self.btn.SetHelpText(Lbl)
    305         
    306 class JetFileText():
    307     """ Capture a filename with a button to browse for a file """
    308     def __init__(self, parent, pos=(0,0), size=(200,-1), title='Open File', spec='*.*', id=-1):
    309         self.spec = spec
    310         self.title = title
    311         BUTWIDTH = 20
    312         BORDER = 5
    313         w = size[0] - (BUTWIDTH + BORDER)
    314         col = pos[0] + w + BORDER
    315         
    316         self.txt = wx.TextCtrl(parent, id, "", pos=(pos[0]-MacOffset(),pos[1]), size=(w, -1))
    317         self.btn = wx.Button(parent, -1, "...", pos=(col, pos[1]), size=(BUTWIDTH,self.txt.GetSize()[1]))
    318         self.btn.Bind(wx.EVT_BUTTON, self.OnBrowse, self.btn) 
    319             
    320     def OnBrowse(self, event):
    321         os = __import__('os')
    322         defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, 'str', str(os.getcwd()))
    323         if OsWindows():
    324             defDir = defDir.replace('/','\\')
    325         else:
    326             defDir = defDir.replace('\\', '/')
    327         
    328         dlg = wx.FileDialog(None, self.title, defDir, '', self.spec, wx.FD_OPEN)
    329         ret = dlg.ShowModal()
    330         if ret == wx.ID_OK:
    331             IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, str(FileJustPath(dlg.GetPath())))
    332             val = dlg.GetPath()
    333             self.txt.SetValue(val)
    334         dlg.Destroy()
    335         
    336     def GetValue(self):
    337         return StrNoneChk(self.txt.GetValue())   
    338     
    339     def SetValue(self, val):
    340         try:
    341             self.txt.SetValue(val)
    342         except:
    343             pass
    344 
    345     def Append(self, val):
    346         try:
    347             self.txt.Append(val)
    348         except:
    349             pass
    350         
    351     def SetFocus(self):
    352         self.txt.SetFocus()
    353 
    354     def Enable(self, enable):
    355         self.txt.Enable(enable)
    356         self.btn.Enable(enable)
    357 
    358     def SetHelpText(self, Lbl):
    359         self.txt.SetHelpText(Lbl)
    360         self.btn.SetHelpText(Lbl)
    361         
    362 def YesNo(title, question, default):
    363     """ Simple Yes/No question box """
    364     dlg = wx.MessageDialog(None, question, title, wx.YES_NO | wx.ICON_QUESTION) 
    365     if dlg.ShowModal() == wx.ID_YES:
    366         result = True
    367     else:
    368         result = False
    369     dlg.Destroy()
    370     return result
    371 
    372 def YesNoCancel(title, question, default):
    373     """ Simple Yes/No question box """
    374     dlg = wx.MessageDialog(None, question, title, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
    375     result = dlg.ShowModal() 
    376     dlg.Destroy()
    377     return result
    378 
    379 def ErrorMsg(title, message):
    380     """ Dipslay an error message """
    381     dlg = wx.MessageDialog(None, message, title, wx.ICON_ERROR) 
    382     dlg.ShowModal()
    383     dlg.Destroy()
    384 
    385 def InfoMsg(title, message):
    386     """ Displays an informational message """
    387     dlg = wx.MessageDialog(None, message, title, wx.ICON_INFORMATION) 
    388     dlg.ShowModal()
    389     dlg.Destroy()
    390 
    391 class TimeCtrl(wx.Frame):
    392     """ Combination of controls to capture measure, beat, tick times """
    393     def __init__(self, parent, pos=(0,0), minimums=(1,1,0), maximums=(999,4,480), value=JetDefs.MBT_DEFAULT, ctlName=''):
    394         wx.Frame.__init__(self, parent, -1)
    395         
    396         self.ChangeCallbackFct = None
    397         self.ctlName = ctlName
    398         self.mx = maximums
    399         self.mn = minimums
    400         self.maxTicks = 0
    401         self.iCtrl = 0
    402         p1 = pos[0]
    403         top = pos[1] + MacOffset()
    404         w1 = 30
    405         self.time = (wx.TextCtrl(parent, -1, str(value[0]), pos=(p1, top), size=(w1, -1), style=wx.TE_NOHIDESEL),
    406                 wx.TextCtrl(parent, -1, str(value[1]), pos=(p1 + (w1 + 3), top), size=(w1, -1), style=wx.TE_NOHIDESEL),
    407                 wx.TextCtrl(parent, -1, str(value[2]), pos=(p1 + (w1 + 3) *2, top), size=(w1, -1), style=wx.TE_NOHIDESEL),
    408                 )
    409         h = self.time[2].GetSize().height
    410         w = self.time[2].GetSize().width + self.time[2].GetPosition().x + 8
    411         
    412         self.spin = wx.SpinButton(parent, -1, (w, top), (h*2/3, h), wx.SP_VERTICAL)
    413         self.spin.SetValue(1)
    414         self.spin.SetRange(-999,999)
    415         
    416         self.spin.Bind(wx.EVT_SPIN_UP, self.OnSpinUp, self.spin)
    417         self.spin.Bind(wx.EVT_SPIN_DOWN, self.OnSpinDown, self.spin)
    418         
    419         self.time[0].Bind(wx.EVT_SET_FOCUS, self.OnFocusMeasure, self.time[0] )
    420         self.time[1].Bind(wx.EVT_SET_FOCUS, self.OnFocusBeat, self.time[1] )
    421         self.time[2].Bind(wx.EVT_SET_FOCUS, self.OnFocusTick, self.time[2] ) 
    422                 
    423         self.time[0].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[0] )
    424         self.time[1].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[1] )
    425         self.time[2].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[2] )         
    426 
    427         self.SetValue(value)
    428 
    429     def UnBindKillFocus(self):
    430         self.time[0].Unbind(wx.EVT_KILL_FOCUS, self.time[0])
    431         self.time[1].Unbind(wx.EVT_KILL_FOCUS, self.time[1])
    432         self.time[2].Unbind(wx.EVT_KILL_FOCUS, self.time[2])         
    433                 
    434     def SetChangeCallbackFct(self, ChangeCallbackFct):
    435         self.ChangeCallbackFct = ChangeCallbackFct
    436         
    437     def OnChangeVal(self, event=None):
    438         if not OsWindows():
    439             self.time[self.iCtrl].SetSelection(-1,-1)
    440         if int(self.time[self.iCtrl].GetValue()) > self.mx[self.iCtrl]:
    441             self.time[self.iCtrl].SetValue(str(self.mx[self.iCtrl]))
    442         if int(self.time[self.iCtrl].GetValue()) < self.mn[self.iCtrl]:
    443             self.time[self.iCtrl].SetValue(str(self.mn[self.iCtrl]))
    444         if self.ChangeCallbackFct is not None:
    445             self.ChangeCallbackFct()
    446         if event is not None:
    447             event.Skip()
    448         
    449     def OnSpinUp(self, event):
    450         if int(self.time[self.iCtrl].GetValue()) < self.mx[self.iCtrl]:
    451             self.time[self.iCtrl].SetValue(str(int(self.time[self.iCtrl].GetValue()) + 1))
    452             self.OnChangeVal()
    453                                              
    454     def OnSpinDown(self, event):
    455         if int(self.time[self.iCtrl].GetValue()) > self.mn[self.iCtrl]:
    456             self.time[self.iCtrl].SetValue(str(int(self.time[self.iCtrl].GetValue()) - 1))
    457             self.OnChangeVal()
    458                                                
    459     def OnFocusMeasure(self, event):
    460         self.iCtrl = 0
    461 
    462     def OnFocusBeat(self, event):
    463         self.iCtrl = 1
    464 
    465     def OnFocusTick(self, event):
    466         self.iCtrl = 2
    467         
    468     def SetValue(self, mbt):
    469         try:
    470             if type(mbt).__name__=='str' or type(mbt).__name__=='unicode':
    471                 mbt = ConvertStrTimeToTuple(mbt)
    472             mbt = mbtFct(mbt, 1)
    473             self.time[0].SetValue(str(mbt[0]))
    474             self.time[1].SetValue(str(mbt[1]))
    475             self.time[2].SetValue(str(mbt[2]))
    476         except:
    477             self.time[0].SetValue(str(self.mn[0]))
    478             self.time[1].SetValue(str(self.mn[1]))
    479             self.time[2].SetValue(str(self.mn[2]))
    480         if not OsWindows():
    481             self.time[0].SetSelection(-1,-1)
    482             self.time[1].SetSelection(-1,-1)
    483             self.time[2].SetSelection(-1,-1)
    484            
    485     def GetValue(self, typ='str'):
    486         try:
    487             if typ == 'str':
    488                 ret = "%d:%d:%d" % (int(self.time[0].GetValue()), int(self.time[1].GetValue()), int(self.time[2].GetValue()))
    489             else:
    490                 ret = (int(self.time[0].GetValue()), int(self.time[1].GetValue()), int(self.time[2].GetValue()))
    491         except:
    492             ret = self.minimums
    493         return mbtFct(ret, -1)
    494 
    495     def Enable(self, enable):
    496         self.time[0].Enable(enable)
    497         self.time[1].Enable(enable)
    498         self.time[2].Enable(enable)
    499         self.spin.Enable(enable)
    500 
    501     def SetFocus(self):
    502         self.time[0].SetFocus()
    503         
    504     def SetMaxMbt(self, m, b, t):
    505         self.mx = (m,b,t)
    506 
    507     def GetMaxMbt(self):
    508         return "%d:%d:%d" % self.mx
    509 
    510     def SetMinMbt(self, m, b, t):
    511         self.mn = (m,b,t)
    512           
    513     def SetMaxTicks(self, maxTicks): 
    514         self.maxTicks = maxTicks
    515         
    516     def GetMaxTicks(self): 
    517         return self.maxTicks
    518     
    519     def SetHelpText(self, Lbl):
    520         self.spin.SetHelpText(Lbl)
    521         self.time[0].SetHelpText(Lbl)
    522         self.time[1].SetHelpText(Lbl)
    523         self.time[2].SetHelpText(Lbl)
    524         
    525     def GetMeasure(self):
    526         return int(self.time[0].GetValue())
    527     
    528     def GetBeat(self):
    529         return int(self.time[1].GetValue())
    530     
    531     def GetTick(self):
    532         return int(self.time[2].GetValue())
    533  
    534 if __name__ == '__main__':
    535     """ Test code for controls """
    536     class TestFrame(wx.Frame):
    537         def __init__(self, parent, id, title):
    538             wx.Frame.__init__(self, parent, id, title, size=(350, 220))
    539     
    540             panel = wx.Panel(self, -1)
    541             
    542             self.tc = TimeCtrl(panel, pos=(30, 20), maximums=(25, 4, 120), value=(2, 3, 4))
    543             #tc.Enable(True)
    544             #tc.SetValue((2,3,4))
    545             #tc.SetValue("1:2:3")
    546             #print(tc.GetValue())
    547             
    548             js = JetSpin(panel, -1, pos=(30, 100))
    549             js.SetValue("1")
    550             #js.SetValue(1)
    551             
    552             #fl = JetFileCombo(panel)
    553 
    554             wx.EVT_CLOSE(self, self.OnClose)
    555             
    556             self.Centre()
    557             self.Show(True)
    558     
    559         def OnClose(self, event):
    560             self.tc.UnBindKillFocus()
    561             self.Destroy()
    562             
    563     app = wx.App(None)
    564     TestFrame(None, -1, 'TestFrame')
    565     app.MainLoop()
    566 
    567     
    568