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