]> git.0d.be Git - jack_mixer.git/blob - channel.py
Don't show solo midi cc for Output channels
[jack_mixer.git] / channel.py
1 # This file is part of jack_mixer
2 #
3 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 import gtk
19 import gobject
20 import slider
21 import meter
22 import abspeak
23 from serialization import SerializedObject
24
25 try:
26     import phat
27 except:
28     phat = None
29
30
31 class Channel(gtk.VBox, SerializedObject):
32     '''Widget with slider and meter used as base class for more specific
33        channel widgets'''
34     monitor_button = None
35
36     def __init__(self, app, name, stereo):
37         gtk.VBox.__init__(self)
38         self.app = app
39         self.mixer = app.mixer
40         self.gui_factory = app.gui_factory
41         self._channel_name = name
42         self.stereo = stereo
43         self.meter_scale = self.gui_factory.get_default_meter_scale()
44         self.slider_scale = self.gui_factory.get_default_slider_scale()
45         self.slider_adjustment = slider.AdjustmentdBFS(self.slider_scale, 0.0)
46         self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
47         self.future_out_mute = None
48         self.future_volume_midi_cc = None
49         self.future_balance_midi_cc = None
50         self.future_mute_midi_cc = None
51         self.future_solo_midi_cc = None
52
53     def get_channel_name(self):
54         return self._channel_name
55
56     label_name = None
57     channel = None
58     post_fader_output_channel = None
59     def set_channel_name(self, name):
60         self.app.on_channel_rename(self._channel_name, 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         if self.post_fader_output_channel:
67             self.post_fader_output_channel.name = "%s Out" % name;
68     channel_name = property(get_channel_name, set_channel_name)
69
70     def realize(self):
71         #print "Realizing channel \"%s\"" % self.channel_name
72         if self.future_out_mute != None:
73             self.channel.out_mute = self.future_out_mute
74
75         self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
76         self.balance_adjustment.connect("value-changed", self.on_balance_changed)
77
78         self.slider = None
79         self.create_slider_widget()
80
81         if self.stereo:
82             self.meter = meter.StereoMeterWidget(self.meter_scale)
83         else:
84             self.meter = meter.MonoMeterWidget(self.meter_scale)
85         self.on_vumeter_color_changed(self.gui_factory)
86
87         self.meter.set_events(gtk.gdk.SCROLL_MASK)
88
89         self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
90         self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
91         self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
92         self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
93         self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
94
95         self.abspeak = abspeak.AbspeakWidget()
96         self.abspeak.connect("reset", self.on_abspeak_reset)
97         self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
98
99         self.volume_digits = gtk.Entry()
100         self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
101         self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
102
103         self.connect("key-press-event", self.on_key_pressed)
104         self.connect("scroll-event", self.on_scroll)
105
106     def unrealize(self):
107         #print "Unrealizing channel \"%s\"" % self.channel_name
108         pass
109
110     def create_balance_widget(self):
111         if self.gui_factory.use_custom_widgets and phat:
112             self.balance = phat.HFanSlider()
113             self.balance.set_default_value(0)
114             self.balance.set_adjustment(self.balance_adjustment)
115         else:
116             self.balance = gtk.HScale(self.balance_adjustment)
117             self.balance.set_draw_value(False)
118         self.pack_start(self.balance, False)
119         if self.monitor_button:
120             self.reorder_child(self.monitor_button, -1)
121         self.balance.show()
122
123     def create_slider_widget(self):
124         parent = None
125         if self.slider:
126             parent = self.slider.get_parent()
127             self.slider.destroy()
128         if self.gui_factory.use_custom_widgets:
129             self.slider = slider.CustomSliderWidget(self.slider_adjustment)
130         else:
131             self.slider = slider.GtkSlider(self.slider_adjustment)
132         if parent:
133             parent.pack_start(self.slider)
134             parent.reorder_child(self.slider, 0)
135         self.slider.show()
136
137     def on_default_meter_scale_changed(self, gui_factory, scale):
138         #print "Default meter scale change detected."
139         self.meter.set_scale(scale)
140
141     def on_default_slider_scale_changed(self, gui_factory, scale):
142         #print "Default slider scale change detected."
143         self.slider_scale = scale
144         self.slider_adjustment.set_scale(scale)
145         self.channel.midi_scale = self.slider_scale.scale
146
147     def on_vumeter_color_changed(self, gui_factory, *args):
148         color = gui_factory.get_vumeter_color()
149         color_scheme = gui_factory.get_vumeter_color_scheme()
150         if color_scheme != 'solid':
151             self.meter.set_color(None)
152         else:
153             self.meter.set_color(gtk.gdk.color_parse(color))
154
155     def on_custom_widgets_changed(self, gui_factory, value):
156         self.balance.destroy()
157         self.create_balance_widget()
158         self.create_slider_widget()
159
160     def on_abspeak_adjust(self, abspeak, adjust):
161         #print "abspeak adjust %f" % adjust
162         self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
163         self.channel.abspeak = None
164         #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
165
166     def on_abspeak_reset(self, abspeak):
167         #print "abspeak reset"
168         self.channel.abspeak = None
169
170     def on_volume_digits_key_pressed(self, widget, event):
171         if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
172             db_text = self.volume_digits.get_text()
173             try:
174                 db = float(db_text)
175                 #print "Volume digits confirmation \"%f dBFS\"" % db
176             except (ValueError), e:
177                 #print "Volume digits confirmation ignore, reset to current"
178                 self.update_volume(False)
179                 return
180             self.slider_adjustment.set_value_db(db)
181             #self.grab_focus()
182             #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
183
184     def on_volume_digits_focus_out(self, widget, event):
185         #print "volume digits focus out detected"
186         self.update_volume(False)
187
188     def read_meter(self):
189         if not self.channel:
190             return
191         if self.stereo:
192             meter_left, meter_right = self.channel.meter
193             self.meter.set_values(meter_left, meter_right)
194         else:
195             self.meter.set_value(self.channel.meter[0])
196
197         self.abspeak.set_peak(self.channel.abspeak)
198
199     def on_scroll(self, widget, event):
200         if event.direction == gtk.gdk.SCROLL_DOWN:
201             self.slider_adjustment.step_down()
202         elif event.direction == gtk.gdk.SCROLL_UP:
203             self.slider_adjustment.step_up()
204         return True
205
206     def update_volume(self, update_engine):
207         db = self.slider_adjustment.get_value_db()
208
209         db_text = "%.2f" % db
210         self.volume_digits.set_text(db_text)
211
212         if update_engine:
213             self.channel.volume = db
214             self.app.update_monitor(self)
215
216     def on_volume_changed(self, adjustment):
217         self.update_volume(True)
218
219     def on_balance_changed(self, adjustment):
220         balance = self.balance_adjustment.get_value()
221         #print "%s balance: %f" % (self.channel_name, balance)
222         self.channel.balance = balance
223         self.app.update_monitor(self)
224
225     def on_key_pressed(self, widget, event):
226         if (event.keyval == gtk.keysyms.Up):
227             #print self.channel_name + " Up"
228             self.slider_adjustment.step_up()
229             return True
230         elif (event.keyval == gtk.keysyms.Down):
231             #print self.channel_name + " Down"
232             self.slider_adjustment.step_down()
233             return True
234
235         return False
236
237     def serialize(self, object_backend):
238         object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
239         object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
240
241         if hasattr(self.channel, 'out_mute'):
242             object_backend.add_property('out_mute', str(self.channel.out_mute))
243         if self.channel.volume_midi_cc != -1:
244             object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
245         if self.channel.balance_midi_cc != -1:
246             object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
247         if self.channel.mute_midi_cc != -1:
248             object_backend.add_property('mute_midi_cc', str(self.channel.mute_midi_cc))
249         if self.channel.solo_midi_cc != -1:
250             object_backend.add_property('solo_midi_cc', str(self.channel.solo_midi_cc))
251
252
253     def unserialize_property(self, name, value):
254         if name == "volume":
255             self.slider_adjustment.set_value_db(float(value))
256             return True
257         if name == "balance":
258             self.balance_adjustment.set_value(float(value))
259             return True
260         if name == 'out_mute':
261             self.future_out_mute = (value == 'True')
262             return True
263         if name == 'volume_midi_cc':
264             self.future_volume_midi_cc = int(value)
265             return True
266         if name == 'balance_midi_cc':
267             self.future_balance_midi_cc = int(value)
268             return True
269         if name == 'mute_midi_cc':
270             self.future_mute_midi_cc = int(value)
271             return True
272         if name == 'solo_midi_cc':
273             self.future_solo_midi_cc = int(value)
274             return True
275         return False
276
277     def on_midi_event_received(self, *args):
278         self.slider_adjustment.set_value_db(self.channel.volume)
279         self.balance_adjustment.set_value(self.channel.balance)
280
281     def on_monitor_button_toggled(self, button):
282         if button.get_active():
283             for channel in self.app.channels + self.app.output_channels:
284                 if channel.monitor_button.get_active() and channel.monitor_button is not button:
285                     channel.monitor_button.handler_block_by_func(
286                                 channel.on_monitor_button_toggled)
287                     channel.monitor_button.set_active(False)
288                     channel.monitor_button.handler_unblock_by_func(
289                                 channel.on_monitor_button_toggled)
290             self.app.set_monitored_channel(self)
291
292     def set_monitored(self):
293         if self.channel:
294             self.app.set_monitored_channel(self)
295         self.monitor_button.set_active(True)
296
297 class InputChannel(Channel):
298     post_fader_output_channel = None
299
300     def __init__(self, app, name, stereo):
301         Channel.__init__(self, app, name, stereo)
302
303     def realize(self):
304         self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
305         if self.channel == None:
306             raise Exception,"Cannot create a channel"
307         Channel.realize(self)
308         if self.future_volume_midi_cc != None:
309             self.channel.volume_midi_cc = self.future_volume_midi_cc
310         if self.future_balance_midi_cc != None:
311             self.channel.balance_midi_cc = self.future_balance_midi_cc
312         if self.future_mute_midi_cc != None:
313             self.channel.mute_midi_cc = self.future_mute_midi_cc
314         if self.future_solo_midi_cc != None:
315             self.channel.solo_midi_cc = self.future_solo_midi_cc
316         if self.app._init_solo_channels and self.channel_name in self.app._init_solo_channels:
317             self.channel.solo = True
318
319         self.channel.midi_scale = self.slider_scale.scale
320
321         self.on_volume_changed(self.slider_adjustment)
322         self.on_balance_changed(self.balance_adjustment)
323
324         # vbox child at upper part
325         self.vbox = gtk.VBox()
326         self.pack_start(self.vbox, False)
327         self.label_name = gtk.Label()
328         self.label_name.set_text(self.channel_name)
329         self.label_name.set_size_request(0, -1)
330         self.label_name_event_box = gtk.EventBox()
331         self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
332         self.label_name_event_box.add(self.label_name)
333         self.vbox.pack_start(self.label_name_event_box, True)
334 #         self.label_stereo = gtk.Label()
335 #         if self.stereo:
336 #             self.label_stereo.set_text("stereo")
337 #         else:
338 #             self.label_stereo.set_text("mono")
339 #         self.label_stereo.set_size_request(0, -1)
340 #         self.vbox.pack_start(self.label_stereo, True)
341
342         self.hbox_mutesolo = gtk.HBox()
343
344         self.mute = gtk.ToggleButton()
345         self.mute.set_label("M")
346         self.mute.set_active(self.channel.out_mute)
347         self.mute.connect("toggled", self.on_mute_toggled)
348         self.hbox_mutesolo.pack_start(self.mute, True)
349
350         self.solo = gtk.ToggleButton()
351         self.solo.set_label("S")
352         self.solo.set_active(self.channel.solo)
353         self.solo.connect("toggled", self.on_solo_toggled)
354         self.hbox_mutesolo.pack_start(self.solo, True)
355
356         self.vbox.pack_start(self.hbox_mutesolo, False)
357
358         frame = gtk.Frame()
359         frame.set_shadow_type(gtk.SHADOW_IN)
360         frame.add(self.abspeak);
361         self.pack_start(frame, False)
362
363         # hbox child at lower part
364         self.hbox = gtk.HBox()
365         self.hbox.pack_start(self.slider, True)
366         frame = gtk.Frame()
367         frame.set_shadow_type(gtk.SHADOW_IN)
368         frame.add(self.meter);
369         self.hbox.pack_start(frame, True)
370         frame = gtk.Frame()
371         frame.set_shadow_type(gtk.SHADOW_IN)
372         frame.add(self.hbox);
373         self.pack_start(frame, True)
374
375         self.volume_digits.set_size_request(0, -1)
376         self.pack_start(self.volume_digits, False)
377
378         self.create_balance_widget()
379
380         self.monitor_button = gtk.ToggleButton('MON')
381         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
382         self.pack_start(self.monitor_button, False, False)
383
384     def add_control_group(self, channel):
385         control_group = ControlGroup(channel, self)
386         control_group.show_all()
387         self.vbox.pack_start(control_group, False)
388         return control_group
389
390     def remove_control_group(self, channel):
391         ctlgroup = self.get_control_group(channel)
392         self.vbox.remove(ctlgroup)
393
394     def update_control_group(self, channel):
395         for control_group in self.vbox.get_children():
396             if isinstance(control_group, ControlGroup):
397                 if control_group.output_channel is channel:
398                     control_group.update()
399
400     def get_control_group(self, channel):
401         for control_group in self.vbox.get_children():
402             if isinstance(control_group, ControlGroup):
403                 if control_group.output_channel is channel:
404                     return control_group
405         return None
406
407     def unrealize(self):
408         Channel.unrealize(self)
409         if self.post_fader_output_channel:
410             self.post_fader_output_channel.remove()
411             self.post_fader_output_channel = None
412         self.channel.remove()
413         self.channel = None
414
415     channel_properties_dialog = None
416     def on_channel_properties(self):
417         if not self.channel_properties_dialog:
418             self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
419         self.channel_properties_dialog.show()
420         self.channel_properties_dialog.present()
421
422     def on_label_mouse(self, widget, event):
423         if event.type == gtk.gdk._2BUTTON_PRESS:
424             if event.button == 1:
425                 self.on_channel_properties()
426
427     def on_mute_toggled(self, button):
428         self.channel.out_mute = self.mute.get_active()
429
430     def on_solo_toggled(self, button):
431         self.channel.solo = self.solo.get_active()
432
433     def midi_events_check(self):
434         if self.channel.midi_in_got_events:
435             self.mute.set_active(self.channel.out_mute)
436             self.solo.set_active(self.channel.solo)
437             Channel.on_midi_event_received(self)
438
439     def on_solo_button_pressed(self, button, event, *args):
440         if event.button == 3:
441             # right click on the solo button, act on all output channels
442             if button.get_active(): # was soloed
443                 button.set_active(False)
444                 if hasattr(button, 'touched_channels'):
445                     touched_channels = button.touched_channels
446                     for chan in touched_channels:
447                         ctlgroup = self.get_control_group(chan)
448                         ctlgroup.solo.set_active(False)
449                     del button.touched_channels
450             else: # was not soloed
451                 button.set_active(True)
452                 touched_channels = []
453                 for chan in self.app.output_channels:
454                     ctlgroup = self.get_control_group(chan)
455                     if not ctlgroup.solo.get_active():
456                         ctlgroup.solo.set_active(True)
457                         touched_channels.append(chan)
458                 button.touched_channels = touched_channels
459             return True
460         return False
461
462     @classmethod
463     def serialization_name(cls):
464         return 'input_channel'
465
466     def serialize(self, object_backend):
467         object_backend.add_property("name", self.channel_name)
468         if self.stereo:
469             object_backend.add_property("type", "stereo")
470         else:
471             object_backend.add_property("type", "mono")
472         Channel.serialize(self, object_backend)
473
474     def unserialize_property(self, name, value):
475         if name == "name":
476             self.channel_name = str(value)
477             return True
478         if name == "type":
479             if value == "stereo":
480                 self.stereo = True
481                 return True
482             if value == "mono":
483                 self.stereo = False
484                 return True
485         return Channel.unserialize_property(self, name, value)
486
487
488 available_colours = [
489     ('#ef2929', '#cc0000', '#840000'),
490     ('#729fcf', '#3465a4', '#204a67'),
491     ('#8aa234', '#73d216', '#4e7a06'),
492     ('#fce84f', '#edd400', '#c48000'),
493     ('#fcaf3e', '#f57900', '#ae5c00'),
494     ('#ad7fa8', '#75507b', '#4c3556'),
495     ('#e9b96e', '#c17d11', '#6f4902'),
496 ]
497
498 class OutputChannel(Channel):
499     colours = available_colours[:]
500     _display_solo_buttons = False
501
502     _init_muted_channels = None
503     _init_solo_channels = None
504
505     def __init__(self, app, name, stereo):
506         Channel.__init__(self, app, name, stereo)
507
508     def get_display_solo_buttons(self):
509         return self._display_solo_buttons
510
511     def set_display_solo_buttons(self, value):
512         self._display_solo_buttons = value
513         # notifying control groups
514         for inputchannel in self.app.channels:
515             inputchannel.update_control_group(self)
516
517     display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
518
519     def realize(self):
520         self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
521         if self.channel == None:
522             raise Exception,"Cannot create a channel"
523         Channel.realize(self)
524         if self.future_volume_midi_cc != None:
525             self.channel.volume_midi_cc = self.future_volume_midi_cc
526         if self.future_balance_midi_cc != None:
527             self.channel.balance_midi_cc = self.future_balance_midi_cc
528         if self.future_mute_midi_cc != None:
529             self.channel.mute_midi_cc = self.future_mute_midi_cc
530         self.channel.midi_scale = self.slider_scale.scale
531
532         self.on_volume_changed(self.slider_adjustment)
533         self.on_balance_changed(self.balance_adjustment)
534
535         # vbox child at upper part
536         self.vbox = gtk.VBox()
537         self.pack_start(self.vbox, False)
538         self.label_name = gtk.Label()
539         self.label_name.set_text(self.channel_name)
540         self.label_name.set_size_request(0, -1)
541         self.label_name_event_box = gtk.EventBox()
542         self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
543         self.label_name_event_box.add(self.label_name)
544         if not self.colours:
545             OutputChannel.colours = available_colours[:]
546         for color in self.colours:
547             self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
548             self.colours.remove(color)
549             break
550         self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
551         self.vbox.pack_start(self.label_name_event_box, True)
552         self.mute = gtk.ToggleButton()
553         self.mute.set_label("M")
554         self.mute.set_active(self.channel.out_mute)
555         self.mute.connect("toggled", self.on_mute_toggled)
556         self.vbox.pack_start(self.mute, False)
557
558         frame = gtk.Frame()
559         frame.set_shadow_type(gtk.SHADOW_IN)
560         frame.add(self.abspeak);
561         self.vbox.pack_start(frame, False)
562
563         # hbox child at lower part
564         self.hbox = gtk.HBox()
565         self.hbox.pack_start(self.slider, True)
566         frame = gtk.Frame()
567         frame.set_shadow_type(gtk.SHADOW_IN)
568         frame.add(self.meter);
569         self.hbox.pack_start(frame, True)
570         frame = gtk.Frame()
571         frame.set_shadow_type(gtk.SHADOW_IN)
572         frame.add(self.hbox);
573         self.pack_start(frame, True)
574
575         self.volume_digits.set_size_request(0, -1)
576         self.pack_start(self.volume_digits, False)
577
578         self.create_balance_widget()
579
580         self.monitor_button = gtk.ToggleButton('MON')
581         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
582         self.pack_start(self.monitor_button, False, False)
583
584         # add control groups to the input channels, and initialize them
585         # appropriately
586         for input_channel in self.app.channels:
587             ctlgroup = input_channel.add_control_group(self)
588             if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
589                 ctlgroup.mute.set_active(True)
590             if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
591                 ctlgroup.solo.set_active(True)
592         self._init_muted_channels = None
593         self._init_solo_channels = None
594
595     channel_properties_dialog = None
596     def on_channel_properties(self):
597         if not self.channel_properties_dialog:
598             self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
599         self.channel_properties_dialog.show()
600         self.channel_properties_dialog.present()
601
602     def on_label_mouse(self, widget, event):
603         if event.type == gtk.gdk._2BUTTON_PRESS:
604             if event.button == 1:
605                 self.on_channel_properties()
606
607     def on_mute_toggled(self, button):
608         self.channel.out_mute = self.mute.get_active()
609
610     def midi_events_check(self):
611         if self.channel.midi_in_got_events:
612             self.mute.set_active(self.channel.out_mute)
613             Channel.on_midi_event_received(self)
614
615     def unrealize(self):
616         # remove control groups from input channels
617         for input_channel in self.app.channels:
618             input_channel.remove_control_group(self)
619         # then remove itself
620         Channel.unrealize(self)
621         self.channel.remove()
622         self.channel = None
623
624     @classmethod
625     def serialization_name(cls):
626         return 'output_channel'
627
628     def serialize(self, object_backend):
629         object_backend.add_property("name", self.channel_name)
630         if self.stereo:
631             object_backend.add_property("type", "stereo")
632         else:
633             object_backend.add_property("type", "mono")
634         if self.display_solo_buttons:
635             object_backend.add_property("solo_buttons", "true")
636         muted_channels = []
637         solo_channels = []
638         for input_channel in self.app.channels:
639             if self.channel.is_muted(input_channel.channel):
640                 muted_channels.append(input_channel)
641             if self.channel.is_solo(input_channel.channel):
642                 solo_channels.append(input_channel)
643         if muted_channels:
644             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
645         if solo_channels:
646             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
647         Channel.serialize(self, object_backend)
648
649     def unserialize_property(self, name, value):
650         if name == "name":
651             self.channel_name = str(value)
652             return True
653         if name == "type":
654             if value == "stereo":
655                 self.stereo = True
656                 return True
657             if value == "mono":
658                 self.stereo = False
659                 return True
660         if name == "solo_buttons":
661             if value == "true":
662                 self.display_solo_buttons = True
663                 return True
664         if name == 'muted_channels':
665             self._init_muted_channels = value.split('|')
666             return True
667         if name == 'solo_channels':
668             self._init_solo_channels = value.split('|')
669             return True
670         return Channel.unserialize_property(self, name, value)
671
672 class ChannelPropertiesDialog(gtk.Dialog):
673     channel = None
674
675     def __init__(self, parent, app):
676         self.channel = parent
677         self.app = app
678         self.mixer = self.channel.mixer
679         gtk.Dialog.__init__(self,
680                         'Channel "%s" Properties' % self.channel.channel_name,
681                         self.channel.gui_factory.topwindow)
682
683         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
684         self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
685         self.set_default_response(gtk.RESPONSE_APPLY);
686
687         self.create_ui()
688         self.fill_ui()
689
690         self.connect('response', self.on_response_cb)
691         self.connect('delete-event', self.on_response_cb)
692
693     def create_frame(self, label, child):
694         frame = gtk.Frame('')
695         frame.set_border_width(3)
696         #frame.set_shadow_type(gtk.SHADOW_NONE)
697         frame.get_label_widget().set_markup('<b>%s</b>' % label)
698
699         alignment = gtk.Alignment(0, 0, 1, 1)
700         alignment.set_padding(0, 0, 12, 0)
701         frame.add(alignment)
702         alignment.add(child)
703
704         return frame
705
706     def create_ui(self):
707         vbox = gtk.VBox()
708         self.vbox.add(vbox)
709
710         table = gtk.Table(2, 3, False)
711         vbox.pack_start(self.create_frame('Properties', table))
712         table.set_row_spacings(5)
713         table.set_col_spacings(5)
714
715         table.attach(gtk.Label('Name'), 0, 1, 0, 1)
716         self.entry_name = gtk.Entry()
717         self.entry_name.set_activates_default(True)
718         self.entry_name.connect('changed', self.on_entry_name_changed)
719         table.attach(self.entry_name, 1, 2, 0, 1)
720
721         table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
722         self.mode_hbox = gtk.HBox()
723         table.attach(self.mode_hbox, 1, 2, 1, 2)
724         self.mono = gtk.RadioButton(label='Mono')
725         self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
726         self.mode_hbox.pack_start(self.mono)
727         self.mode_hbox.pack_start(self.stereo)
728
729         table = gtk.Table(2, 3, False)
730         vbox.pack_start(self.create_frame('MIDI Control Channels', table))
731         table.set_row_spacings(5)
732         table.set_col_spacings(5)
733
734         table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
735         self.entry_volume_cc = gtk.Entry()
736         self.entry_volume_cc.set_activates_default(True)
737         self.entry_volume_cc.set_editable(False)
738         self.entry_volume_cc.set_width_chars(3)
739         table.attach(self.entry_volume_cc, 1, 2, 0, 1)
740         self.button_sense_midi_volume = gtk.Button('Autoset')
741         self.button_sense_midi_volume.connect('clicked',
742                         self.on_sense_midi_volume_clicked)
743         table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
744
745         table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
746         self.entry_balance_cc = gtk.Entry()
747         self.entry_balance_cc.set_activates_default(True)
748         self.entry_balance_cc.set_width_chars(3)
749         self.entry_balance_cc.set_editable(False)
750         table.attach(self.entry_balance_cc, 1, 2, 1, 2)
751         self.button_sense_midi_balance = gtk.Button('Autoset')
752         self.button_sense_midi_balance.connect('clicked',
753                         self.on_sense_midi_balance_clicked)
754         table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
755
756         table.attach(gtk.Label('Mute'), 0, 1, 2, 3)
757         self.entry_mute_cc = gtk.Entry()
758         self.entry_mute_cc.set_activates_default(True)
759         self.entry_mute_cc.set_editable(False)
760         self.entry_mute_cc.set_width_chars(3)
761         table.attach(self.entry_mute_cc, 1, 2, 2, 3)
762         self.button_sense_midi_mute = gtk.Button('Autoset')
763         self.button_sense_midi_mute.connect('clicked',
764                         self.on_sense_midi_mute_clicked)
765         table.attach(self.button_sense_midi_mute, 2, 3, 2, 3)
766
767         if (isinstance(self, NewChannelDialog) or (self.channel and
768             isinstance(self.channel, InputChannel))):
769             table.attach(gtk.Label('Solo'), 0, 1, 3, 4)
770             self.entry_solo_cc = gtk.Entry()
771             self.entry_solo_cc.set_activates_default(True)
772             self.entry_solo_cc.set_editable(False)
773             self.entry_solo_cc.set_width_chars(3)
774             table.attach(self.entry_solo_cc, 1, 2, 3, 4)
775             self.button_sense_midi_solo = gtk.Button('Autoset')
776             self.button_sense_midi_solo.connect('clicked',
777                             self.on_sense_midi_solo_clicked)
778             table.attach(self.button_sense_midi_solo, 2, 3, 3, 4)
779
780         self.vbox.show_all()
781
782     def fill_ui(self):
783         self.entry_name.set_text(self.channel.channel_name)
784         if self.channel.channel.is_stereo:
785             self.stereo.set_active(True)
786         else:
787             self.mono.set_active(True)
788         self.mode_hbox.set_sensitive(False)
789         self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
790         self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
791         self.entry_mute_cc.set_text('%s' % self.channel.channel.mute_midi_cc)
792         if (self.channel and isinstance(self.channel, InputChannel)):
793             self.entry_solo_cc.set_text('%s' % self.channel.channel.solo_midi_cc)
794
795     def sense_popup_dialog(self, entry):
796         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
797         window.set_destroy_with_parent(True)
798         window.set_transient_for(self)
799         window.set_decorated(False)
800         window.set_modal(True)
801         window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
802         window.set_border_width(10)
803
804         vbox = gtk.VBox(10)
805         window.add(vbox)
806         window.timeout = 5
807         vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
808         timeout_label = gtk.Label('This window will close in 5 seconds')
809         vbox.pack_start(timeout_label)
810         def close_sense_timeout(window, entry):
811             window.timeout -= 1
812             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
813             if window.timeout == 0:
814                 window.destroy()
815                 entry.set_text('%s' % self.mixer.last_midi_channel)
816                 return False
817             return True
818         window.show_all()
819         gobject.timeout_add_seconds(1, close_sense_timeout, window, entry)
820
821     def on_sense_midi_volume_clicked(self, *args):
822         self.mixer.last_midi_channel = int(self.entry_volume_cc.get_text())
823         self.sense_popup_dialog(self.entry_volume_cc)
824
825     def on_sense_midi_balance_clicked(self, *args):
826         self.mixer.last_midi_channel = int(self.entry_balance_cc.get_text())
827         self.sense_popup_dialog(self.entry_balance_cc)
828
829     def on_sense_midi_mute_clicked(self, *args):
830         self.mixer.last_midi_channel = int(self.entry_mute_cc.get_text())
831         self.sense_popup_dialog(self.entry_mute_cc)
832
833     def on_sense_midi_solo_clicked(self, *args):
834         self.mixer.last_midi_channel = int(self.entry_solo_cc.get_text())
835         self.sense_popup_dialog(self.entry_solo_cc)
836
837     def on_response_cb(self, dlg, response_id, *args):
838         self.channel.channel_properties_dialog = None
839         name = self.entry_name.get_text()
840         if response_id == gtk.RESPONSE_APPLY:
841             self.channel.channel_name = name
842             try:
843                 if self.entry_volume_cc.get_text() != '-1':
844                     self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
845             except ValueError:
846                 pass
847             try:
848                 if self.entry_balance_cc.get_text() != '-1':
849                     self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
850             except ValueError:
851                 pass
852             try:
853                 if self.entry_mute_cc.get_text() != '-1':
854                     self.channel.channel.mute_midi_cc = int(self.entry_mute_cc.get_text())
855             except ValueError:
856                 pass
857             try:
858                 if hasattr(self, 'entry_solo_cc') and self.entry_solo_cc.get_text() != '-1':
859                     self.channel.channel.solo_midi_cc = int(self.entry_solo_cc.get_text())
860             except ValueError:
861                 pass
862         self.destroy()
863
864     def on_entry_name_changed(self, entry):
865         sensitive = False
866         if len(entry.get_text()):
867             if self.channel and self.channel.channel.name == entry.get_text():
868                 sensitive = True
869             elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
870                         [x.channel.name for x in self.app.output_channels] + ['MAIN']:
871                 sensitive = True
872         self.ok_button.set_sensitive(sensitive)
873
874
875 class NewChannelDialog(ChannelPropertiesDialog):
876     def __init__(self, app):
877         gtk.Dialog.__init__(self, 'New Channel', app.window)
878         self.mixer = app.mixer
879         self.app = app
880         self.create_ui()
881         self.fill_ui()
882
883         self.stereo.set_active(True) # default to stereo
884
885         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
886         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
887         self.ok_button.set_sensitive(False)
888         self.set_default_response(gtk.RESPONSE_OK);
889
890     def fill_ui(self):
891         self.entry_volume_cc.set_text('-1')
892         self.entry_balance_cc.set_text('-1')
893         self.entry_mute_cc.set_text('-1')
894         self.entry_solo_cc.set_text('-1')
895
896     def get_result(self):
897         return {'name': self.entry_name.get_text(),
898                 'stereo': self.stereo.get_active(),
899                 'volume_cc': self.entry_volume_cc.get_text(),
900                 'balance_cc': self.entry_balance_cc.get_text(),
901                 'mute_cc': self.entry_mute_cc.get_text(),
902                 'solo_cc': self.entry_solo_cc.get_text()
903                }
904
905 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
906     def create_ui(self):
907         ChannelPropertiesDialog.create_ui(self)
908
909         vbox = gtk.VBox()
910         self.vbox.pack_start(self.create_frame('Input Channels', vbox))
911
912         self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
913         vbox.pack_start(self.display_solo_buttons)
914
915         self.vbox.show_all()
916
917     def fill_ui(self):
918         ChannelPropertiesDialog.fill_ui(self)
919         self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
920
921     def on_response_cb(self, dlg, response_id, *args):
922         if response_id == gtk.RESPONSE_APPLY:
923             self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
924         ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
925
926
927 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
928     def __init__(self, app):
929         gtk.Dialog.__init__(self, 'New Output Channel', app.window)
930         self.mixer = app.mixer
931         self.app = app
932         self.create_ui()
933         self.fill_ui()
934
935         # TODO: disable mode for output channels as mono output channels may
936         # not be correctly handled yet.
937         self.mode_hbox.set_sensitive(False)
938         self.stereo.set_active(True) # default to stereo
939
940         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
941         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
942         self.ok_button.set_sensitive(False)
943         self.set_default_response(gtk.RESPONSE_OK);
944
945     def fill_ui(self):
946         self.entry_volume_cc.set_text('-1')
947         self.entry_balance_cc.set_text('-1')
948         self.entry_mute_cc.set_text('-1')
949
950     def get_result(self):
951         return {'name': self.entry_name.get_text(),
952                 'stereo': self.stereo.get_active(),
953                 'volume_cc': self.entry_volume_cc.get_text(),
954                 'balance_cc': self.entry_balance_cc.get_text(),
955                 'mute_cc': self.entry_mute_cc.get_text(),
956                 'display_solo_buttons': self.display_solo_buttons.get_active(),
957                }
958
959
960 class ControlGroup(gtk.Alignment):
961     def __init__(self, output_channel, input_channel):
962         gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
963         self.output_channel = output_channel
964         self.input_channel = input_channel
965         self.app = input_channel.app
966
967         hbox = gtk.HBox()
968         self.hbox = hbox
969         self.add(hbox)
970
971         mute = gtk.ToggleButton()
972         self.mute = mute
973         mute.set_label("M")
974         mute.connect("toggled", self.on_mute_toggled)
975         muteevent = gtk.EventBox()
976         muteevent.add(mute)
977         hbox.pack_start(muteevent, False)
978
979         solo = gtk.ToggleButton()
980         self.solo = solo
981         solo.set_label("S")
982         solo.connect("toggled", self.on_solo_toggled)
983         soloevent = gtk.EventBox()
984         soloevent.add(solo)
985         if self.output_channel.display_solo_buttons:
986             hbox.pack_start(soloevent, True)
987
988         print output_channel.color_tuple
989         muteevent.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
990         muteevent.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
991         muteevent.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
992         soloevent.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
993         soloevent.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
994         soloevent.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
995
996     def update(self):
997         if self.output_channel.display_solo_buttons:
998             if not self.solo in self.hbox.get_children():
999                 self.hbox.pack_start(self.solo, True)
1000                 self.solo.show()
1001         else:
1002             if self.solo in self.hbox.get_children():
1003                 self.hbox.remove(self.solo)
1004
1005     def on_mute_toggled(self, button):
1006         self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1007         self.app.update_monitor(self)
1008
1009     def on_solo_toggled(self, button):
1010         self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1011         self.app.update_monitor(self)
1012