]> git.0d.be Git - jack_mixer.git/blob - channel.py
Add effective UI to display/hide the solo buttons of output channels
[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 update_control_group(self, channel):
348         for control_group in self.vbox.get_children():
349             if isinstance(control_group, ControlGroup):
350                 if control_group.output_channel is channel:
351                     control_group.update()
352
353     def unrealize(self):
354         channel.unrealize(self)
355         self.channel.remove()
356         self.channel = False
357
358     channel_properties_dialog = None
359     def on_channel_properties(self):
360         if not self.channel_properties_dialog:
361             self.channel_properties_dialog = ChannelPropertiesDialog(self)
362         self.channel_properties_dialog.show()
363         self.channel_properties_dialog.present()
364
365     def on_label_mouse(self, widget, event):
366         if event.type == gtk.gdk._2BUTTON_PRESS:
367             if event.button == 1:
368                 self.on_channel_properties()
369
370     def on_mute_toggled(self, button):
371         self.channel.mute = self.mute.get_active()
372
373     def on_solo_toggled(self, button):
374         self.channel.solo = self.solo.get_active()
375
376     def serialization_name(self):
377         return input_channel_serialization_name()
378
379     def serialize(self, object_backend):
380         object_backend.add_property("name", self.channel_name)
381         if self.stereo:
382             object_backend.add_property("type", "stereo")
383         else:
384             object_backend.add_property("type", "mono")
385         channel.serialize(self, object_backend)
386
387     def unserialize_property(self, name, value):
388         if name == "name":
389             self.channel_name = str(value)
390             return True
391         if name == "type":
392             if value == "stereo":
393                 self.stereo = True
394                 return True
395             if value == "mono":
396                 self.stereo = False
397                 return True
398         return channel.unserialize_property(self, name, value)
399
400 def input_channel_serialization_name():
401     return "input_channel"
402
403
404 available_colours = [
405     ('#ef2929', '#cc0000', '#a40000'),
406     ('#729fcf', '#3465a4', '#204a87'),
407     ('#8aa234', '#73d216', '#4e9a06'),
408     ('#fce84f', '#edd400', '#c4a000'),
409     ('#fcaf3e', '#f57900', '#ce5c00'),
410     ('#e9b96e', '#c17d11', '#8f5902'),
411     ('#ad7fa8', '#75507b', '#5c3566'),
412 ]
413
414 class output_channel(channel):
415     colours = available_colours[:]
416     _display_solo_buttons = False
417
418     def __init__(self, app, name, stereo):
419         channel.__init__(self, app, name, stereo)
420
421     def get_display_solo_buttons(self):
422         return self._display_solo_buttons
423
424     def set_display_solo_buttons(self, value):
425         self._display_solo_buttons = value
426         # notifying control groups
427         for inputchannel in self.app.channels:
428             inputchannel.update_control_group(self)
429
430     display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
431
432     def realize(self):
433         channel.realize(self)
434         self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
435         if self.channel == None:
436             raise Exception,"Cannot create a channel"
437         channel.realize(self)
438
439         self.channel.midi_scale = self.slider_scale.scale
440         self.channel.midi_change_callback = self.midi_change_callback
441
442         self.on_volume_changed(self.slider_adjustment)
443         self.on_balance_changed(self.balance_adjustment)
444
445         # vbox child at upper part
446         self.vbox = gtk.VBox()
447         self.pack_start(self.vbox, False)
448         self.label_name = gtk.Label()
449         self.label_name.set_text(self.channel_name)
450         self.label_name.set_size_request(0, -1)
451         self.label_name_event_box = gtk.EventBox()
452         self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
453         self.label_name_event_box.add(self.label_name)
454         if not self.colours:
455             self.colours = available_colours[:]
456         for color in self.colours:
457             self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
458             self.colours.remove(color)
459             break
460         self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
461         self.vbox.pack_start(self.label_name_event_box, True)
462         frame = gtk.Frame()
463         frame.set_shadow_type(gtk.SHADOW_IN)
464         frame.add(self.abspeak);
465         self.vbox.pack_start(frame, False)
466
467         # hbox child at lower part
468         self.hbox = gtk.HBox()
469         self.hbox.pack_start(self.slider, True)
470         frame = gtk.Frame()
471         frame.set_shadow_type(gtk.SHADOW_IN)
472         frame.add(self.meter);
473         self.hbox.pack_start(frame, True)
474         frame = gtk.Frame()
475         frame.set_shadow_type(gtk.SHADOW_IN)
476         frame.add(self.hbox);
477         self.pack_start(frame, True)
478
479         self.volume_digits.set_size_request(0, -1)
480         self.pack_start(self.volume_digits, False)
481
482         self.create_balance_widget()
483
484     channel_properties_dialog = None
485     def on_channel_properties(self):
486         if not self.channel_properties_dialog:
487             self.channel_properties_dialog = OutputChannelPropertiesDialog(self)
488         self.channel_properties_dialog.show()
489         self.channel_properties_dialog.present()
490
491     def on_label_mouse(self, widget, event):
492         if event.type == gtk.gdk._2BUTTON_PRESS:
493             if event.button == 1:
494                 self.on_channel_properties()
495
496     def unrealize(self):
497         channel.unrealize(self)
498         self.channel = False
499
500     def serialization_name(self):
501         return output_channel_serialization_name()
502
503     def serialize(self, object_backend):
504         object_backend.add_property("name", self.channel_name)
505         if self.stereo:
506             object_backend.add_property("type", "stereo")
507         else:
508             object_backend.add_property("type", "mono")
509         if self.display_solo_buttons:
510             object_backend.add_property("solo-buttons", "true")
511         channel.serialize(self, object_backend)
512
513     def unserialize_property(self, name, value):
514         if name == "name":
515             self.channel_name = str(value)
516             return True
517         if name == "type":
518             if value == "stereo":
519                 self.stereo = True
520                 return True
521             if value == "mono":
522                 self.stereo = False
523                 return True
524         if name == "solo-buttons":
525             if value == "true":
526                 self.display_solo_buttons = True
527         return channel.unserialize_property(self, name, value)
528
529 def output_channel_serialization_name():
530     return "output_channel"
531
532 class main_mix(channel):
533     def __init__(self, app):
534         channel.__init__(self, app, "MAIN", True)
535
536     def realize(self):
537         channel.realize(self)
538         self.channel = self.mixer.main_mix_channel
539         self.channel.midi_scale = self.slider_scale.scale
540         self.channel.midi_change_callback = self.midi_change_callback
541
542         self.on_volume_changed(self.slider_adjustment)
543         self.on_balance_changed(self.balance_adjustment)
544
545         # vbox child at upper part
546         self.vbox = gtk.VBox()
547         self.pack_start(self.vbox, False)
548         self.label_name = gtk.Label()
549         self.label_name.set_text(self.channel_name)
550         self.label_name.set_size_request(0, -1)
551         self.vbox.pack_start(self.label_name, False)
552         frame = gtk.Frame()
553         frame.set_shadow_type(gtk.SHADOW_IN)
554         frame.add(self.abspeak);
555         self.vbox.pack_start(frame, False)
556
557         # hbox child at lower part
558         self.hbox = gtk.HBox()
559         self.hbox.pack_start(self.slider, True)
560         frame = gtk.Frame()
561         frame.set_shadow_type(gtk.SHADOW_IN)
562         frame.add(self.meter);
563         self.hbox.pack_start(frame, True)
564         frame = gtk.Frame()
565         frame.set_shadow_type(gtk.SHADOW_IN)
566         frame.add(self.hbox);
567         self.pack_start(frame, True)
568
569         self.volume_digits.set_size_request(0, -1)
570         self.pack_start(self.volume_digits, False)
571
572         self.create_balance_widget()
573
574     def unrealize(self):
575         channel.unrealize(self)
576         self.channel = False
577
578     def serialization_name(self):
579         return main_mix_serialization_name()
580
581 def main_mix_serialization_name():
582     return "main_mix_channel"
583
584
585 class ChannelPropertiesDialog(gtk.Dialog):
586     def __init__(self, parent):
587         self.channel = parent
588         self.mixer = self.channel.mixer
589         gtk.Dialog.__init__(self,
590                         'Channel "%s" Properties' % self.channel.channel_name,
591                         self.channel.gui_factory.topwindow)
592
593         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
594         self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
595
596         self.create_ui()
597         self.fill_ui()
598
599         self.connect('response', self.on_response_cb)
600         self.connect('delete-event', self.on_response_cb)
601
602     def create_frame(self, label, child):
603         frame = gtk.Frame('')
604         frame.set_border_width(3)
605         #frame.set_shadow_type(gtk.SHADOW_NONE)
606         frame.get_label_widget().set_markup('<b>%s</b>' % label)
607
608         alignment = gtk.Alignment(0, 0, 1, 1)
609         alignment.set_padding(0, 0, 12, 0)
610         frame.add(alignment)
611         alignment.add(child)
612
613         return frame
614
615     def create_ui(self):
616         vbox = gtk.VBox()
617         self.vbox.add(vbox)
618
619         table = gtk.Table(2, 2, False)
620         vbox.pack_start(self.create_frame('Properties', table))
621         table.set_row_spacings(5)
622         table.set_col_spacings(5)
623
624         table.attach(gtk.Label('Name'), 0, 1, 0, 1)
625         self.entry_name = gtk.Entry()
626         self.entry_name.set_activates_default(True)
627         self.entry_name.connect('changed', self.on_entry_name_changed)
628         table.attach(self.entry_name, 1, 2, 0, 1)
629
630         table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
631         self.mode_hbox = gtk.HBox()
632         table.attach(self.mode_hbox, 1, 2, 1, 2)
633         self.mono = gtk.RadioButton(label='Mono')
634         self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
635         self.mode_hbox.pack_start(self.mono)
636         self.mode_hbox.pack_start(self.stereo)
637
638         table = gtk.Table(2, 3, False)
639         vbox.pack_start(self.create_frame('MIDI Control Channels', table))
640         table.set_row_spacings(5)
641         table.set_col_spacings(5)
642
643         table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
644         self.entry_volume_cc = gtk.Entry()
645         self.entry_volume_cc.set_activates_default(True)
646         self.entry_volume_cc.set_editable(False)
647         self.entry_volume_cc.set_width_chars(3)
648         table.attach(self.entry_volume_cc, 1, 2, 0, 1)
649         self.button_sense_midi_volume = gtk.Button('Autoset')
650         self.button_sense_midi_volume.connect('clicked',
651                         self.on_sense_midi_volume_clicked)
652         table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
653
654         table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
655         self.entry_balance_cc = gtk.Entry()
656         self.entry_balance_cc.set_activates_default(True)
657         self.entry_balance_cc.set_width_chars(3)
658         self.entry_balance_cc.set_editable(False)
659         table.attach(self.entry_balance_cc, 1, 2, 1, 2)
660         self.button_sense_midi_balance = gtk.Button('Autoset')
661         self.button_sense_midi_balance.connect('clicked',
662                         self.on_sense_midi_balance_clicked)
663         table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
664
665         self.vbox.show_all()
666
667     def fill_ui(self):
668         self.entry_name.set_text(self.channel.channel_name)
669         if self.channel.channel.is_stereo:
670             self.stereo.set_active(True)
671         else:
672             self.mono.set_active(True)
673         self.mode_hbox.set_sensitive(False)
674         self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
675         self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
676
677     def sense_popup_dialog(self, entry):
678         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
679         window.set_destroy_with_parent(True)
680         window.set_transient_for(self)
681         window.set_decorated(False)
682         window.set_modal(True)
683         window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
684         window.set_border_width(10)
685
686         vbox = gtk.VBox(10)
687         window.add(vbox)
688         window.timeout = 5
689         vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
690         timeout_label = gtk.Label('This window will close in 5 seconds')
691         vbox.pack_start(timeout_label)
692         def close_sense_timeout(window, entry):
693             window.timeout -= 1
694             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
695             if window.timeout == 0:
696                 window.destroy()
697                 entry.set_text('%s' % self.mixer.last_midi_channel)
698                 return False
699             return True
700         window.show_all()
701         glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
702
703     def on_sense_midi_volume_clicked(self, *args):
704         self.sense_popup_dialog(self.entry_volume_cc)
705
706     def on_sense_midi_balance_clicked(self, *args):
707         self.sense_popup_dialog(self.entry_balance_cc)
708
709     def on_response_cb(self, dlg, response_id, *args):
710         self.channel.channel_properties_dialog = None
711         self.destroy()
712         if response_id == gtk.RESPONSE_APPLY:
713             name = self.entry_name.get_text()
714             self.channel.channel_name = name
715             self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
716             self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
717
718     def on_entry_name_changed(self, entry):
719         self.ok_button.set_sensitive(len(entry.get_text()))
720
721 class NewChannelDialog(ChannelPropertiesDialog):
722     def __init__(self, parent, mixer):
723         gtk.Dialog.__init__(self, 'New Channel', parent)
724         self.mixer = mixer
725         self.create_ui()
726
727         self.stereo.set_active(True) # default to stereo
728
729         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
730         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
731         self.ok_button.set_sensitive(False)
732         self.set_default_response(gtk.RESPONSE_OK);
733
734     def get_result(self):
735         return {'name': self.entry_name.get_text(),
736                 'stereo': self.stereo.get_active(),
737                 'volume_cc': self.entry_volume_cc.get_text(),
738                 'balance_cc': self.entry_balance_cc.get_text()
739                }
740
741 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
742     def create_ui(self):
743         ChannelPropertiesDialog.create_ui(self)
744
745         vbox = gtk.VBox()
746         self.vbox.pack_start(self.create_frame('Input Channels', vbox))
747
748         self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
749         vbox.pack_start(self.display_solo_buttons)
750
751         self.vbox.show_all()
752
753     def fill_ui(self):
754         ChannelPropertiesDialog.fill_ui(self)
755         self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
756
757     def on_response_cb(self, dlg, response_id, *args):
758         ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
759         if response_id == gtk.RESPONSE_APPLY:
760             self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
761
762
763 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
764     def __init__(self, parent, mixer):
765         gtk.Dialog.__init__(self, 'New Output Channel', parent)
766         self.mixer = mixer
767         self.create_ui()
768
769         # TODO: disable mode for output channels as mono output channels may
770         # not be correctly handled yet.
771         self.mode_hbox.set_sensitive(False)
772         self.stereo.set_active(True) # default to stereo
773
774         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
775         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
776         self.ok_button.set_sensitive(False)
777         self.set_default_response(gtk.RESPONSE_OK);
778
779     def get_result(self):
780         return {'name': self.entry_name.get_text(),
781                 'stereo': self.stereo.get_active(),
782                 'volume_cc': self.entry_volume_cc.get_text(),
783                 'balance_cc': self.entry_balance_cc.get_text(),
784                 'display_solo_buttons': self.display_solo_buttons.get_active(),
785                }
786
787
788 class ControlGroup(gtk.Alignment):
789     def __init__(self, output_channel):
790         gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
791         self.output_channel = output_channel
792
793         hbox = gtk.HBox()
794         self.hbox = hbox
795         self.add(hbox)
796
797         mute = gtk.ToggleButton()
798         mute.set_label("M")
799         #mute.set_active(self.channel.mute)
800         #mute.connect("toggled", self.on_mute_toggled)
801         hbox.pack_start(mute, False)
802
803         solo = gtk.ToggleButton()
804         self.solo = solo
805         solo.set_label("S")
806         #solo.set_active(self.channel.solo)
807         #solo.connect("toggled", self.on_solo_toggled)
808         if self.output_channel.display_solo_buttons:
809             hbox.pack_start(solo, True)
810
811         mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
812         mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
813         mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
814         solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
815         solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
816         solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
817
818     def update(self):
819         if self.output_channel.display_solo_buttons:
820             if not self.solo in self.hbox.get_children():
821                 self.hbox.pack_start(self.solo, True)
822                 self.solo.show()
823         else:
824             if self.solo in self.hbox.get_children():
825                 self.hbox.remove(self.solo)