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
23import wx
24import sys
25
26from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin, ColumnSorterMixin
27from JetUtils import *
28from JetDefs import *
29
30class 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
44class 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
63class 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
81class 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
99class 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
148class 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
206class 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
238class 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
306class 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
362def 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
372def 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
379def 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
385def 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
391class 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
534if __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