]> git.0d.be Git - jack_mixer.git/blob - channel.py
Preliminary control groups
[jack_mixer.git] / channel.py
1 #!/usr/bin/env python
2 #
3 # This file is part of jack_mixer
4 #
5 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
6 #  
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 of the License
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 import gtk
21 import gobject
22 import glib
23 import scale
24 import slider
25 import meter
26 import abspeak
27 import math
28 import random
29 from serialization import serialized_object
30
31 try:
32     import phat
33 except:
34     print "PHAT audio widgets not found, some features will not be available"
35     phat = None
36
37 import fpconst
38
39 class channel(gtk.VBox, serialized_object):
40     '''Widget with slider and meter used as base class for more specific channel widgets'''
41     def __init__(self, mixer, gui_factory, name, stereo):
42         gtk.VBox.__init__(self)
43         self.mixer = mixer
44         self.gui_factory = gui_factory
45         self._channel_name = name
46         self.stereo = stereo
47         self.meter_scale = gui_factory.get_default_meter_scale()
48         self.slider_scale = gui_factory.get_default_slider_scale()
49         self.slider_adjustment = slider.adjustment_dBFS(self.slider_scale, 0.0)
50         self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
51         self.future_volume_midi_cc = None
52         self.future_balance_midi_cc = None
53
54     def get_channel_name(self):
55         return self._channel_name
56
57     label_name = None
58     channel = None
59     def set_channel_name(self, name):
60         self._channel_name = name
61         if self.label_name:
62             self.label_name.set_text(name)
63         if self.channel:
64             self.channel.name = name
65     channel_name = property(get_channel_name, set_channel_name)
66
67     def realize(self):
68         #print "Realizing channel \"%s\"" % self.channel_name
69
70         self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
71         self.balance_adjustment.connect("value-changed", self.on_balance_changed)
72         self.connect('midi-event-received', self.on_midi_event_received)
73
74         self.slider = None
75         self.create_slider_widget()
76
77         if self.stereo:
78             self.meter = meter.stereo(self.meter_scale)
79         else:
80             self.meter = meter.mono(self.meter_scale)
81         self.on_vumeter_color_changed(self.gui_factory)
82
83         self.meter.set_events(gtk.gdk.SCROLL_MASK)
84
85         self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
86         self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
87         self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
88         self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
89         self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
90
91         self.abspeak = abspeak.widget()
92         self.abspeak.connect("reset", self.on_abspeak_reset)
93         self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
94
95         self.volume_digits = gtk.Entry()
96         self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
97         self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
98
99         self.connect("key-press-event", self.on_key_pressed)
100         self.connect("scroll-event", self.on_scroll)
101
102     def unrealize(self):
103         #print "Unrealizing channel \"%s\"" % self.channel_name
104         pass
105
106     def create_balance_widget(self):
107         if self.gui_factory.use_custom_widgets and phat:
108             self.balance = phat.HFanSlider()
109             self.balance.set_default_value(0)
110             self.balance.set_adjustment(self.balance_adjustment)
111         else:
112             self.balance = gtk.HScale(self.balance_adjustment)
113             self.balance.set_draw_value(False)
114         self.pack_start(self.balance, False)
115         self.balance.show()
116
117     def create_slider_widget(self):
118         parent = None
119         if self.slider:
120             parent = self.slider.get_parent()
121             self.slider.destroy()
122         if self.gui_factory.use_custom_widgets:
123             self.slider = slider.CustomSliderWidget(self.slider_adjustment)
124         else:
125             self.slider = slider.GtkSlider(self.slider_adjustment)
126         if parent:
127             parent.pack_start(self.slider)
128             parent.reorder_child(self.slider, 0)
129         self.slider.show()
130
131     def on_default_meter_scale_changed(self, gui_factory, scale):
132         #print "Default meter scale change detected."
133         self.meter.set_scale(scale)
134
135     def on_default_slider_scale_changed(self, gui_factory, scale):
136         #print "Default slider scale change detected."
137         self.slider_scale = scale
138         self.slider_adjustment.set_scale(scale)
139         self.channel.set_midi_scale(self.slider_scale.scale)
140
141     def on_vumeter_color_changed(self, gui_factory, *args):
142         color = gui_factory.get_vumeter_color()
143         color_scheme = gui_factory.get_vumeter_color_scheme()
144         if color_scheme != 'solid':
145             self.meter.set_color(None)
146         else:
147             self.meter.set_color(gtk.gdk.color_parse(color))
148
149     def on_custom_widgets_changed(self, gui_factory, value):
150         self.balance.destroy()
151         self.create_balance_widget()
152         self.create_slider_widget()
153
154     def on_abspeak_adjust(self, abspeak, adjust):
155         #print "abspeak adjust %f" % adjust
156         self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
157         self.channel.abspeak = None
158         #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
159
160     def on_abspeak_reset(self, abspeak):
161         #print "abspeak reset"
162         self.channel.abspeak = None
163
164     def on_volume_digits_key_pressed(self, widget, event):
165         if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
166             db_text = self.volume_digits.get_text()
167             try:
168                 db = float(db_text)
169                 #print "Volume digits confirmation \"%f dBFS\"" % db
170             except (ValueError), e:
171                 #print "Volume digits confirmation ignore, reset to current"
172                 self.update_volume(False)
173                 return
174             self.slider_adjustment.set_value_db(db)
175             #self.grab_focus()
176             #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
177
178     def on_volume_digits_focus_out(self, widget, event):
179         #print "volume digits focus out detected"
180         self.update_volume(False)
181
182     def read_meter(self):
183         if self.stereo:
184             meter_left, meter_right = self.channel.meter
185             self.meter.set_values(meter_left, meter_right)
186         else:
187             self.meter.set_value(self.channel.meter[0])
188
189         self.abspeak.set_peak(self.channel.abspeak)
190
191     def on_scroll(self, widget, event):
192         if event.direction == gtk.gdk.SCROLL_DOWN:
193             self.slider_adjustment.step_down()
194         elif event.direction == gtk.gdk.SCROLL_UP:
195             self.slider_adjustment.step_up()
196         return True
197
198     def update_volume(self, update_engine):
199         db = self.slider_adjustment.get_value_db()
200
201         db_text = "%.2f" % db
202         self.volume_digits.set_text(db_text)
203
204         if update_engine:
205             #print "Setting engine volume to " + db_text
206             self.channel.volume = db
207
208     def on_volume_changed(self, adjustment):
209         self.update_volume(True)
210
211     def on_balance_changed(self, adjustment):
212         balance = self.balance_adjustment.get_value()
213         #print "%s balance: %f" % (self.channel_name, balance)
214         self.channel.balance = balance
215
216     def on_key_pressed(self, widget, event):
217         if (event.keyval == gtk.keysyms.Up):
218             #print self.channel_name + " Up"
219             self.slider_adjustment.step_up()
220             return True
221         elif (event.keyval == gtk.keysyms.Down):
222             #print self.channel_name + " Down"
223             self.slider_adjustment.step_down()
224             return True
225
226         return False
227
228     def serialize(self, object_backend):
229         object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
230         object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
231
232         if self.channel.volume_midi_cc:
233             object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
234         if self.channel.balance_midi_cc:
235             object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
236
237     def unserialize_property(self, name, value):
238         if name == "volume":
239             self.slider_adjustment.set_value_db(float(value))
240             return True
241         if name == "balance":
242             self.balance_adjustment.set_value(float(value))
243             return True
244         if name == 'volume_midi_cc':
245             self.future_volume_midi_cc = int(value)
246             return True
247         if name == 'balance_midi_cc':
248             self.future_balance_midi_cc = int(value)
249             return True
250         return False
251
252     def on_midi_event_received(self, *args):
253         self.slider_adjustment.set_value_db(self.channel.volume)
254         self.balance_adjustment.set_value(self.channel.balance)
255
256     def midi_change_callback(self, *args):
257         # the changes are not applied directly to the widgets as they
258         # absolutely have to be done from the gtk thread.
259         self.emit('midi-event-received')
260
261 gobject.signal_new('midi-event-received', channel,
262                 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
263                 gobject.TYPE_NONE, ())
264
265 class input_channel(channel):
266     def __init__(self, mixer, gui_factory, name, stereo):
267         channel.__init__(self, mixer, gui_factory, name, stereo)
268
269     def realize(self):
270         self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
271         if self.channel == None:
272             raise Exception,"Cannot create a channel"
273         channel.realize(self)
274         if self.future_volume_midi_cc:
275             self.channel.volume_midi_cc = self.future_volume_midi_cc
276         if self.future_balance_midi_cc:
277             self.channel.balance_midi_cc = self.future_balance_midi_cc
278         self.channel.midi_scale = self.slider_scale.scale
279         self.channel.midi_change_callback = self.midi_change_callback
280
281         self.on_volume_changed(self.slider_adjustment)
282         self.on_balance_changed(self.balance_adjustment)
283
284         # vbox child at upper part
285         self.vbox = gtk.VBox()
286         self.pack_start(self.vbox, False)
287         self.label_name = gtk.Label()
288         self.label_name.set_text(self.channel_name)
289         self.label_name.set_size_request(0, -1)
290         self.label_name_event_box = gtk.EventBox()
291         self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
292         self.label_name_event_box.add(self.label_name)
293         self.vbox.pack_start(self.label_name_event_box, True)
294 #         self.label_stereo = gtk.Label()
295 #         if self.stereo:
296 #             self.label_stereo.set_text("stereo")
297 #         else:
298 #             self.label_stereo.set_text("mono")
299 #         self.label_stereo.set_size_request(0, -1)
300 #         self.vbox.pack_start(self.label_stereo, True)
301
302         # hbox for mute and solo buttons
303         self.hbox_mutesolo = gtk.HBox()
304
305         self.mute = gtk.ToggleButton()
306         self.mute.set_label("M")
307         self.mute.set_active(self.channel.mute)
308         self.mute.connect("toggled", self.on_mute_toggled)
309         self.hbox_mutesolo.pack_start(self.mute, True)
310
311         self.solo = gtk.ToggleButton()
312         self.solo.set_label("S")
313         self.solo.set_active(self.channel.solo)
314         self.solo.connect("toggled", self.on_solo_toggled)
315         self.hbox_mutesolo.pack_start(self.solo, True)
316
317         self.vbox.pack_start(self.hbox_mutesolo, False)
318
319         frame = gtk.Frame()
320         frame.set_shadow_type(gtk.SHADOW_IN)
321         frame.add(self.abspeak);
322         self.vbox.pack_start(frame, False)
323
324         # hbox child at lower part
325         self.hbox = gtk.HBox()
326         self.hbox.pack_start(self.slider, True)
327         frame = gtk.Frame()
328         frame.set_shadow_type(gtk.SHADOW_IN)
329         frame.add(self.meter);
330         self.hbox.pack_start(frame, True)
331         frame = gtk.Frame()
332         frame.set_shadow_type(gtk.SHADOW_IN)
333         frame.add(self.hbox);
334         self.pack_start(frame, True)
335
336         self.volume_digits.set_size_request(0, -1)
337         self.pack_start(self.volume_digits, False)
338
339         self.create_balance_widget()
340
341     def add_control_group(self, channel):
342         control_group = ControlGroup(channel)
343         control_group.show_all()
344         self.pack_start(control_group, False)
345
346     def unrealize(self):
347         channel.unrealize(self)
348         self.channel.remove()
349         self.channel = False
350
351     channel_properties_dialog = None
352     def on_channel_properties(self):
353         if not self.channel_properties_dialog:
354             self.channel_properties_dialog = ChannelPropertiesDialog(self)
355         self.channel_properties_dialog.show()
356         self.channel_properties_dialog.present()
357
358     def on_label_mouse(self, widget, event):
359         if event.type == gtk.gdk._2BUTTON_PRESS:
360             if event.button == 1:
361                 self.on_channel_properties()
362
363     def on_mute_toggled(self, button):
364         self.channel.mute = self.mute.get_active()
365
366     def on_solo_toggled(self, button):
367         self.channel.solo = self.solo.get_active()
368
369     def serialization_name(self):
370         return input_channel_serialization_name()
371
372     def serialize(self, object_backend):
373         object_backend.add_property("name", self.channel_name)
374         if self.stereo:
375             object_backend.add_property("type", "stereo")
376         else:
377             object_backend.add_property("type", "mono")
378         channel.serialize(self, object_backend)
379
380     def unserialize_property(self, name, value):
381         if name == "name":
382             self.channel_name = str(value)
383             return True
384         if name == "type":
385             if value == "stereo":
386                 self.stereo = True
387                 return True
388             if value == "mono":
389                 self.stereo = False
390                 return True
391         return channel.unserialize_property(self, name, value)
392
393 def input_channel_serialization_name():
394     return "input_channel"
395
396
397 available_colours = [
398     gtk.gdk.color_parse('#cc0000'),
399     gtk.gdk.color_parse('#3465a4'),
400     gtk.gdk.color_parse('#73d216'),
401     gtk.gdk.color_parse('#edd400'),
402     gtk.gdk.color_parse('#f57900'),
403     gtk.gdk.color_parse('#c17d11'),
404     gtk.gdk.color_parse('#75507b'),
405 ]
406
407 class output_channel(channel):
408     colours = available_colours[:]
409
410     def __init__(self, mixer, gui_factory, name, stereo):
411         channel.__init__(self, mixer, gui_factory, name, stereo)
412
413     def realize(self):
414         channel.realize(self)
415         self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
416         if self.channel == None:
417             raise Exception,"Cannot create a channel"
418         channel.realize(self)
419
420         self.channel.midi_scale = self.slider_scale.scale
421         self.channel.midi_change_callback = self.midi_change_callback
422
423         self.on_volume_changed(self.slider_adjustment)
424         self.on_balance_changed(self.balance_adjustment)
425
426         # vbox child at upper part
427         self.vbox = gtk.VBox()
428         self.pack_start(self.vbox, False)
429         self.label_name = gtk.Label()
430         self.label_name.set_text(self.channel_name)
431         self.label_name.set_size_request(0, -1)
432         self.label_name_event_box = gtk.EventBox()
433         self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
434         self.label_name_event_box.add(self.label_name)
435         if not self.colours:
436             self.colours = available_colours[:]
437         for color in self.colours:
438             self.color = color
439             self.colours.remove(color)
440             break
441         self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color)
442         self.vbox.pack_start(self.label_name_event_box, True)
443         frame = gtk.Frame()
444         frame.set_shadow_type(gtk.SHADOW_IN)
445         frame.add(self.abspeak);
446         self.vbox.pack_start(frame, False)
447
448         # hbox child at lower part
449         self.hbox = gtk.HBox()
450         self.hbox.pack_start(self.slider, True)
451         frame = gtk.Frame()
452         frame.set_shadow_type(gtk.SHADOW_IN)
453         frame.add(self.meter);
454         self.hbox.pack_start(frame, True)
455         frame = gtk.Frame()
456         frame.set_shadow_type(gtk.SHADOW_IN)
457         frame.add(self.hbox);
458         self.pack_start(frame, True)
459
460         self.volume_digits.set_size_request(0, -1)
461         self.pack_start(self.volume_digits, False)
462
463         self.create_balance_widget()
464
465     channel_properties_dialog = None
466     def on_channel_properties(self):
467         if not self.channel_properties_dialog:
468             self.channel_properties_dialog = ChannelPropertiesDialog(self)
469         self.channel_properties_dialog.show()
470         self.channel_properties_dialog.present()
471
472     def on_label_mouse(self, widget, event):
473         if event.type == gtk.gdk._2BUTTON_PRESS:
474             if event.button == 1:
475                 self.on_channel_properties()
476
477     def unrealize(self):
478         channel.unrealize(self)
479         self.channel = False
480
481     def serialization_name(self):
482         return output_channel_serialization_name()
483
484     def serialize(self, object_backend):
485         object_backend.add_property("name", self.channel_name)
486         if self.stereo:
487             object_backend.add_property("type", "stereo")
488         else:
489             object_backend.add_property("type", "mono")
490         channel.serialize(self, object_backend)
491
492     def unserialize_property(self, name, value):
493         if name == "name":
494             self.channel_name = str(value)
495             return True
496         if name == "type":
497             if value == "stereo":
498                 self.stereo = True
499                 return True
500             if value == "mono":
501                 self.stereo = False
502                 return True
503         return channel.unserialize_property(self, name, value)
504
505 def output_channel_serialization_name():
506     return "output_channel"
507
508 class main_mix(channel):
509     def __init__(self, mixer, gui_factory):
510         channel.__init__(self, mixer, gui_factory, "MAIN", True)
511
512     def realize(self):
513         channel.realize(self)
514         self.channel = self.mixer.main_mix_channel
515         self.channel.midi_scale = self.slider_scale.scale
516         self.channel.midi_change_callback = self.midi_change_callback
517
518         self.on_volume_changed(self.slider_adjustment)
519         self.on_balance_changed(self.balance_adjustment)
520
521         # vbox child at upper part
522         self.vbox = gtk.VBox()
523         self.pack_start(self.vbox, False)
524         self.label_name = gtk.Label()
525         self.label_name.set_text(self.channel_name)
526         self.label_name.set_size_request(0, -1)
527         self.vbox.pack_start(self.label_name, False)
528         frame = gtk.Frame()
529         frame.set_shadow_type(gtk.SHADOW_IN)
530         frame.add(self.abspeak);
531         self.vbox.pack_start(frame, False)
532
533         # hbox child at lower part
534         self.hbox = gtk.HBox()
535         self.hbox.pack_start(self.slider, True)
536         frame = gtk.Frame()
537         frame.set_shadow_type(gtk.SHADOW_IN)
538         frame.add(self.meter);
539         self.hbox.pack_start(frame, True)
540         frame = gtk.Frame()
541         frame.set_shadow_type(gtk.SHADOW_IN)
542         frame.add(self.hbox);
543         self.pack_start(frame, True)
544
545         self.volume_digits.set_size_request(0, -1)
546         self.pack_start(self.volume_digits, False)
547
548         self.create_balance_widget()
549
550     def unrealize(self):
551         channel.unrealize(self)
552         self.channel = False
553
554     def serialization_name(self):
555         return main_mix_serialization_name()
556
557 def main_mix_serialization_name():
558     return "main_mix_channel"
559
560
561 class ChannelPropertiesDialog(gtk.Dialog):
562     def __init__(self, parent):
563         self.channel = parent
564         self.mixer = self.channel.mixer
565         gtk.Dialog.__init__(self,
566                         'Channel "%s" Properties' % self.channel.channel_name,
567                         self.channel.gui_factory.topwindow)
568         self.create_ui()
569         self.fill_ui()
570
571         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
572         self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
573
574         self.connect('response', self.on_response_cb)
575         self.connect('delete-event', self.on_response_cb)
576
577     def create_frame(self, label, child):
578         frame = gtk.Frame('')
579         frame.set_border_width(3)
580         frame.set_shadow_type(gtk.SHADOW_NONE)
581         frame.get_label_widget().set_markup('<b>%s</b>' % label)
582
583         alignment = gtk.Alignment()
584         alignment.set_padding(0, 0, 12, 0)
585         frame.add(alignment)
586         alignment.add(child)
587
588         return frame
589
590     def create_ui(self):
591         vbox = gtk.VBox()
592         self.vbox.add(vbox)
593
594         table = gtk.Table(2, 2, False)
595         vbox.pack_start(self.create_frame('Properties', table))
596         table.set_row_spacings(5)
597         table.set_col_spacings(5)
598
599         table.attach(gtk.Label('Name'), 0, 1, 0, 1)
600         self.entry_name = gtk.Entry()
601         table.attach(self.entry_name, 1, 2, 0, 1)
602
603         table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
604         self.mode_hbox = gtk.HBox()
605         table.attach(self.mode_hbox, 1, 2, 1, 2)
606         self.mono = gtk.RadioButton(label='Mono')
607         self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
608         self.mode_hbox.pack_start(self.mono)
609         self.mode_hbox.pack_start(self.stereo)
610
611         table = gtk.Table(2, 3, False)
612         vbox.pack_start(self.create_frame('MIDI Control Channels', table))
613         table.set_row_spacings(5)
614         table.set_col_spacings(5)
615
616         table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
617         self.entry_volume_cc = gtk.Entry()
618         self.entry_volume_cc.set_editable(False)
619         self.entry_volume_cc.set_width_chars(3)
620         table.attach(self.entry_volume_cc, 1, 2, 0, 1)
621         self.button_sense_midi_volume = gtk.Button('Autoset')
622         self.button_sense_midi_volume.connect('clicked',
623                         self.on_sense_midi_volume_clicked)
624         table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
625
626         table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
627         self.entry_balance_cc = gtk.Entry()
628         self.entry_balance_cc.set_width_chars(3)
629         self.entry_balance_cc.set_editable(False)
630         table.attach(self.entry_balance_cc, 1, 2, 1, 2)
631         self.button_sense_midi_balance = gtk.Button('Autoset')
632         self.button_sense_midi_balance.connect('clicked',
633                         self.on_sense_midi_balance_clicked)
634         table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
635
636         self.vbox.show_all()
637
638     def fill_ui(self):
639         self.entry_name.set_text(self.channel.channel_name)
640         if self.channel.channel.is_stereo:
641             self.stereo.set_active(True)
642         else:
643             self.mono.set_active(True)
644         self.mode_hbox.set_sensitive(False)
645         self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
646         self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
647
648     def sense_popup_dialog(self, entry):
649         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
650         window.set_destroy_with_parent(True)
651         window.set_transient_for(self)
652         window.set_decorated(False)
653         window.set_modal(True)
654         window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
655         window.set_border_width(10)
656
657         vbox = gtk.VBox(10)
658         window.add(vbox)
659         window.timeout = 5
660         vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
661         timeout_label = gtk.Label('This window will close in 5 seconds')
662         vbox.pack_start(timeout_label)
663         def close_sense_timeout(window, entry):
664             window.timeout -= 1
665             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
666             if window.timeout == 0:
667                 window.destroy()
668                 entry.set_text('%s' % self.mixer.last_midi_channel)
669                 return False
670             return True
671         window.show_all()
672         glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
673
674     def on_sense_midi_volume_clicked(self, *args):
675         self.sense_popup_dialog(self.entry_volume_cc)
676
677     def on_sense_midi_balance_clicked(self, *args):
678         self.sense_popup_dialog(self.entry_balance_cc)
679
680     def on_response_cb(self, dlg, response_id, *args):
681         self.channel.channel_properties_dialog = None
682         self.destroy()
683         if response_id == gtk.RESPONSE_APPLY:
684             name = self.entry_name.get_text()
685             self.channel.channel_name = name
686             self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
687             self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
688
689
690 class NewChannelDialog(ChannelPropertiesDialog):
691     def __init__(self, parent, mixer):
692         gtk.Dialog.__init__(self, 'New Channel', parent)
693         self.mixer = mixer
694         self.create_ui()
695
696         self.stereo.set_active(True) # default to stereo
697
698         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
699         self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
700
701     def get_result(self):
702         return {'name': self.entry_name.get_text(),
703                 'stereo': self.stereo.get_active(),
704                 'volume_cc': self.entry_volume_cc.get_text(),
705                 'balance_cc': self.entry_balance_cc.get_text()
706                }
707
708 class NewOutputChannelDialog(ChannelPropertiesDialog):
709     def __init__(self, parent, mixer):
710         gtk.Dialog.__init__(self, 'New Output Channel', parent)
711         self.mixer = mixer
712         self.create_ui()
713
714         # TODO: disable mode for output channels as mono output channels may
715         # not be correctly handled yet.
716         self.mode_hbox.set_sensitive(False)
717         self.stereo.set_active(True) # default to stereo
718
719         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
720         self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
721
722     def get_result(self):
723         return {'name': self.entry_name.get_text(),
724                 'stereo': self.stereo.get_active(),
725                 'volume_cc': self.entry_volume_cc.get_text(),
726                 'balance_cc': self.entry_balance_cc.get_text()
727                }
728
729
730 class ControlGroup(gtk.HBox):
731     def __init__(self, output_channel):
732         self.output_channel = output_channel
733         gtk.HBox.__init__(self)
734
735         mute = gtk.ToggleButton()
736         mute.set_label("M")
737         #mute.set_active(self.channel.mute)
738         #mute.connect("toggled", self.on_mute_toggled)
739         self.pack_start(mute, True)
740
741         solo = gtk.ToggleButton()
742         solo.set_label("S")
743         #solo.set_active(self.channel.solo)
744         #solo.connect("toggled", self.on_solo_toggled)
745         self.pack_start(solo, True)
746
747         mute.modify_bg(gtk.STATE_NORMAL, output_channel.color)
748         solo.modify_bg(gtk.STATE_NORMAL, output_channel.color)
749
750