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