3 # This file is part of jack_mixer
5 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
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
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.
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.
29 from serialization import serialized_object
34 print "PHAT audio widgets not found, some features will not be available"
39 class channel(gtk.VBox, serialized_object):
40 '''Widget with slider and meter used as base class for more specific channel widgets'''
41 def __init__(self, mixer, gui_factory, name, stereo):
42 gtk.VBox.__init__(self)
44 self.gui_factory = gui_factory
45 self._channel_name = name
47 self.meter_scale = gui_factory.get_default_meter_scale()
48 self.slider_scale = gui_factory.get_default_slider_scale()
49 self.slider_adjustment = slider.adjustment_dBFS(self.slider_scale, 0.0)
50 self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
51 self.future_volume_midi_cc = None
52 self.future_balance_midi_cc = None
54 def get_channel_name(self):
55 return self._channel_name
59 def set_channel_name(self, name):
60 self._channel_name = name
62 self.label_name.set_text(name)
64 self.channel.name = name
65 channel_name = property(get_channel_name, set_channel_name)
68 #print "Realizing channel \"%s\"" % self.channel_name
70 self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
71 self.balance_adjustment.connect("value-changed", self.on_balance_changed)
72 self.connect('midi-event-received', self.on_midi_event_received)
75 self.create_slider_widget()
78 self.meter = meter.stereo(self.meter_scale)
80 self.meter = meter.mono(self.meter_scale)
81 self.on_vumeter_color_changed(self.gui_factory)
83 self.meter.set_events(gtk.gdk.SCROLL_MASK)
85 self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
86 self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
87 self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
88 self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
89 self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
91 self.abspeak = abspeak.widget()
92 self.abspeak.connect("reset", self.on_abspeak_reset)
93 self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
95 self.volume_digits = gtk.Entry()
96 self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
97 self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
99 self.connect("key-press-event", self.on_key_pressed)
100 self.connect("scroll-event", self.on_scroll)
103 #print "Unrealizing channel \"%s\"" % self.channel_name
106 def create_balance_widget(self):
107 if self.gui_factory.use_custom_widgets and phat:
108 self.balance = phat.HFanSlider()
109 self.balance.set_default_value(0)
110 self.balance.set_adjustment(self.balance_adjustment)
112 self.balance = gtk.HScale(self.balance_adjustment)
113 self.balance.set_draw_value(False)
114 self.pack_start(self.balance, False)
117 def create_slider_widget(self):
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)
125 self.slider = slider.GtkSlider(self.slider_adjustment)
127 parent.pack_start(self.slider)
128 parent.reorder_child(self.slider, 0)
131 def on_default_meter_scale_changed(self, gui_factory, scale):
132 #print "Default meter scale change detected."
133 self.meter.set_scale(scale)
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)
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)
147 self.meter.set_color(gtk.gdk.color_parse(color))
149 def on_custom_widgets_changed(self, gui_factory, value):
150 self.balance.destroy()
151 self.create_balance_widget()
152 self.create_slider_widget()
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)
160 def on_abspeak_reset(self, abspeak):
161 #print "abspeak reset"
162 self.channel.abspeak = None
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()
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)
174 self.slider_adjustment.set_value_db(db)
176 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
178 def on_volume_digits_focus_out(self, widget, event):
179 #print "volume digits focus out detected"
180 self.update_volume(False)
182 def read_meter(self):
184 meter_left, meter_right = self.channel.meter
185 self.meter.set_values(meter_left, meter_right)
187 self.meter.set_value(self.channel.meter[0])
189 self.abspeak.set_peak(self.channel.abspeak)
191 def on_scroll(self, widget, event):
192 if event.direction == gtk.gdk.SCROLL_DOWN:
193 self.slider_adjustment.step_down()
194 elif event.direction == gtk.gdk.SCROLL_UP:
195 self.slider_adjustment.step_up()
198 def update_volume(self, update_engine):
199 db = self.slider_adjustment.get_value_db()
201 db_text = "%.2f" % db
202 self.volume_digits.set_text(db_text)
205 #print "Setting engine volume to " + db_text
206 self.channel.volume = db
208 def on_volume_changed(self, adjustment):
209 self.update_volume(True)
211 def on_balance_changed(self, adjustment):
212 balance = self.balance_adjustment.get_value()
213 #print "%s balance: %f" % (self.channel_name, balance)
214 self.channel.balance = balance
216 def on_key_pressed(self, widget, event):
217 if (event.keyval == gtk.keysyms.Up):
218 #print self.channel_name + " Up"
219 self.slider_adjustment.step_up()
221 elif (event.keyval == gtk.keysyms.Down):
222 #print self.channel_name + " Down"
223 self.slider_adjustment.step_down()
228 def serialize(self, object_backend):
229 object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
230 object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
232 if self.channel.volume_midi_cc:
233 object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
234 if self.channel.balance_midi_cc:
235 object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
237 def unserialize_property(self, name, value):
239 self.slider_adjustment.set_value_db(float(value))
241 if name == "balance":
242 self.balance_adjustment.set_value(float(value))
244 if name == 'volume_midi_cc':
245 self.future_volume_midi_cc = int(value)
247 if name == 'balance_midi_cc':
248 self.future_balance_midi_cc = int(value)
252 def on_midi_event_received(self, *args):
253 self.slider_adjustment.set_value_db(self.channel.volume)
254 self.balance_adjustment.set_value(self.channel.balance)
256 def midi_change_callback(self, *args):
257 # the changes are not applied directly to the widgets as they
258 # absolutely have to be done from the gtk thread.
259 self.emit('midi-event-received')
261 gobject.signal_new('midi-event-received', channel,
262 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
263 gobject.TYPE_NONE, ())
265 class input_channel(channel):
266 def __init__(self, mixer, gui_factory, name, stereo):
267 channel.__init__(self, mixer, gui_factory, name, stereo)
270 self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
271 if self.channel == None:
272 raise Exception,"Cannot create a channel"
273 channel.realize(self)
274 if self.future_volume_midi_cc:
275 self.channel.volume_midi_cc = self.future_volume_midi_cc
276 if self.future_balance_midi_cc:
277 self.channel.balance_midi_cc = self.future_balance_midi_cc
278 self.channel.midi_scale = self.slider_scale.scale
279 self.channel.midi_change_callback = self.midi_change_callback
281 self.on_volume_changed(self.slider_adjustment)
282 self.on_balance_changed(self.balance_adjustment)
284 # vbox child at upper part
285 self.vbox = gtk.VBox()
286 self.pack_start(self.vbox, False)
287 self.label_name = gtk.Label()
288 self.label_name.set_text(self.channel_name)
289 self.label_name.set_size_request(0, -1)
290 self.label_name_event_box = gtk.EventBox()
291 self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
292 self.label_name_event_box.add(self.label_name)
293 self.vbox.pack_start(self.label_name_event_box, True)
294 # self.label_stereo = gtk.Label()
296 # self.label_stereo.set_text("stereo")
298 # self.label_stereo.set_text("mono")
299 # self.label_stereo.set_size_request(0, -1)
300 # self.vbox.pack_start(self.label_stereo, True)
302 # hbox for mute and solo buttons
303 self.hbox_mutesolo = gtk.HBox()
305 self.mute = gtk.ToggleButton()
306 self.mute.set_label("M")
307 self.mute.set_active(self.channel.mute)
308 self.mute.connect("toggled", self.on_mute_toggled)
309 self.hbox_mutesolo.pack_start(self.mute, True)
311 self.solo = gtk.ToggleButton()
312 self.solo.set_label("S")
313 self.solo.set_active(self.channel.solo)
314 self.solo.connect("toggled", self.on_solo_toggled)
315 self.hbox_mutesolo.pack_start(self.solo, True)
317 self.vbox.pack_start(self.hbox_mutesolo, False)
320 frame.set_shadow_type(gtk.SHADOW_IN)
321 frame.add(self.abspeak);
322 self.vbox.pack_start(frame, False)
324 # hbox child at lower part
325 self.hbox = gtk.HBox()
326 self.hbox.pack_start(self.slider, True)
328 frame.set_shadow_type(gtk.SHADOW_IN)
329 frame.add(self.meter);
330 self.hbox.pack_start(frame, True)
332 frame.set_shadow_type(gtk.SHADOW_IN)
333 frame.add(self.hbox);
334 self.pack_start(frame, True)
336 self.volume_digits.set_size_request(0, -1)
337 self.pack_start(self.volume_digits, False)
339 self.create_balance_widget()
341 def add_control_group(self, channel):
342 control_group = ControlGroup(channel)
343 control_group.show_all()
344 self.pack_start(control_group, False)
347 channel.unrealize(self)
348 self.channel.remove()
351 channel_properties_dialog = None
352 def on_channel_properties(self):
353 if not self.channel_properties_dialog:
354 self.channel_properties_dialog = ChannelPropertiesDialog(self)
355 self.channel_properties_dialog.show()
356 self.channel_properties_dialog.present()
358 def on_label_mouse(self, widget, event):
359 if event.type == gtk.gdk._2BUTTON_PRESS:
360 if event.button == 1:
361 self.on_channel_properties()
363 def on_mute_toggled(self, button):
364 self.channel.mute = self.mute.get_active()
366 def on_solo_toggled(self, button):
367 self.channel.solo = self.solo.get_active()
369 def serialization_name(self):
370 return input_channel_serialization_name()
372 def serialize(self, object_backend):
373 object_backend.add_property("name", self.channel_name)
375 object_backend.add_property("type", "stereo")
377 object_backend.add_property("type", "mono")
378 channel.serialize(self, object_backend)
380 def unserialize_property(self, name, value):
382 self.channel_name = str(value)
385 if value == "stereo":
391 return channel.unserialize_property(self, name, value)
393 def input_channel_serialization_name():
394 return "input_channel"
397 available_colours = [
398 gtk.gdk.color_parse('#cc0000'),
399 gtk.gdk.color_parse('#3465a4'),
400 gtk.gdk.color_parse('#73d216'),
401 gtk.gdk.color_parse('#edd400'),
402 gtk.gdk.color_parse('#f57900'),
403 gtk.gdk.color_parse('#c17d11'),
404 gtk.gdk.color_parse('#75507b'),
407 class output_channel(channel):
408 colours = available_colours[:]
410 def __init__(self, mixer, gui_factory, name, stereo):
411 channel.__init__(self, mixer, gui_factory, name, stereo)
414 channel.realize(self)
415 self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
416 if self.channel == None:
417 raise Exception,"Cannot create a channel"
418 channel.realize(self)
420 self.channel.midi_scale = self.slider_scale.scale
421 self.channel.midi_change_callback = self.midi_change_callback
423 self.on_volume_changed(self.slider_adjustment)
424 self.on_balance_changed(self.balance_adjustment)
426 # vbox child at upper part
427 self.vbox = gtk.VBox()
428 self.pack_start(self.vbox, False)
429 self.label_name = gtk.Label()
430 self.label_name.set_text(self.channel_name)
431 self.label_name.set_size_request(0, -1)
432 self.label_name_event_box = gtk.EventBox()
433 self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
434 self.label_name_event_box.add(self.label_name)
436 self.colours = available_colours[:]
437 for color in self.colours:
439 self.colours.remove(color)
441 self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color)
442 self.vbox.pack_start(self.label_name_event_box, True)
444 frame.set_shadow_type(gtk.SHADOW_IN)
445 frame.add(self.abspeak);
446 self.vbox.pack_start(frame, False)
448 # hbox child at lower part
449 self.hbox = gtk.HBox()
450 self.hbox.pack_start(self.slider, True)
452 frame.set_shadow_type(gtk.SHADOW_IN)
453 frame.add(self.meter);
454 self.hbox.pack_start(frame, True)
456 frame.set_shadow_type(gtk.SHADOW_IN)
457 frame.add(self.hbox);
458 self.pack_start(frame, True)
460 self.volume_digits.set_size_request(0, -1)
461 self.pack_start(self.volume_digits, False)
463 self.create_balance_widget()
465 channel_properties_dialog = None
466 def on_channel_properties(self):
467 if not self.channel_properties_dialog:
468 self.channel_properties_dialog = ChannelPropertiesDialog(self)
469 self.channel_properties_dialog.show()
470 self.channel_properties_dialog.present()
472 def on_label_mouse(self, widget, event):
473 if event.type == gtk.gdk._2BUTTON_PRESS:
474 if event.button == 1:
475 self.on_channel_properties()
478 channel.unrealize(self)
481 def serialization_name(self):
482 return output_channel_serialization_name()
484 def serialize(self, object_backend):
485 object_backend.add_property("name", self.channel_name)
487 object_backend.add_property("type", "stereo")
489 object_backend.add_property("type", "mono")
490 channel.serialize(self, object_backend)
492 def unserialize_property(self, name, value):
494 self.channel_name = str(value)
497 if value == "stereo":
503 return channel.unserialize_property(self, name, value)
505 def output_channel_serialization_name():
506 return "output_channel"
508 class main_mix(channel):
509 def __init__(self, mixer, gui_factory):
510 channel.__init__(self, mixer, gui_factory, "MAIN", True)
513 channel.realize(self)
514 self.channel = self.mixer.main_mix_channel
515 self.channel.midi_scale = self.slider_scale.scale
516 self.channel.midi_change_callback = self.midi_change_callback
518 self.on_volume_changed(self.slider_adjustment)
519 self.on_balance_changed(self.balance_adjustment)
521 # vbox child at upper part
522 self.vbox = gtk.VBox()
523 self.pack_start(self.vbox, False)
524 self.label_name = gtk.Label()
525 self.label_name.set_text(self.channel_name)
526 self.label_name.set_size_request(0, -1)
527 self.vbox.pack_start(self.label_name, False)
529 frame.set_shadow_type(gtk.SHADOW_IN)
530 frame.add(self.abspeak);
531 self.vbox.pack_start(frame, False)
533 # hbox child at lower part
534 self.hbox = gtk.HBox()
535 self.hbox.pack_start(self.slider, True)
537 frame.set_shadow_type(gtk.SHADOW_IN)
538 frame.add(self.meter);
539 self.hbox.pack_start(frame, True)
541 frame.set_shadow_type(gtk.SHADOW_IN)
542 frame.add(self.hbox);
543 self.pack_start(frame, True)
545 self.volume_digits.set_size_request(0, -1)
546 self.pack_start(self.volume_digits, False)
548 self.create_balance_widget()
551 channel.unrealize(self)
554 def serialization_name(self):
555 return main_mix_serialization_name()
557 def main_mix_serialization_name():
558 return "main_mix_channel"
561 class ChannelPropertiesDialog(gtk.Dialog):
562 def __init__(self, parent):
563 self.channel = parent
564 self.mixer = self.channel.mixer
565 gtk.Dialog.__init__(self,
566 'Channel "%s" Properties' % self.channel.channel_name,
567 self.channel.gui_factory.topwindow)
571 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
572 self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
574 self.connect('response', self.on_response_cb)
575 self.connect('delete-event', self.on_response_cb)
577 def create_frame(self, label, child):
578 frame = gtk.Frame('')
579 frame.set_border_width(3)
580 frame.set_shadow_type(gtk.SHADOW_NONE)
581 frame.get_label_widget().set_markup('<b>%s</b>' % label)
583 alignment = gtk.Alignment()
584 alignment.set_padding(0, 0, 12, 0)
594 table = gtk.Table(2, 2, False)
595 vbox.pack_start(self.create_frame('Properties', table))
596 table.set_row_spacings(5)
597 table.set_col_spacings(5)
599 table.attach(gtk.Label('Name'), 0, 1, 0, 1)
600 self.entry_name = gtk.Entry()
601 table.attach(self.entry_name, 1, 2, 0, 1)
603 table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
604 self.mode_hbox = gtk.HBox()
605 table.attach(self.mode_hbox, 1, 2, 1, 2)
606 self.mono = gtk.RadioButton(label='Mono')
607 self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
608 self.mode_hbox.pack_start(self.mono)
609 self.mode_hbox.pack_start(self.stereo)
611 table = gtk.Table(2, 3, False)
612 vbox.pack_start(self.create_frame('MIDI Control Channels', table))
613 table.set_row_spacings(5)
614 table.set_col_spacings(5)
616 table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
617 self.entry_volume_cc = gtk.Entry()
618 self.entry_volume_cc.set_editable(False)
619 self.entry_volume_cc.set_width_chars(3)
620 table.attach(self.entry_volume_cc, 1, 2, 0, 1)
621 self.button_sense_midi_volume = gtk.Button('Autoset')
622 self.button_sense_midi_volume.connect('clicked',
623 self.on_sense_midi_volume_clicked)
624 table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
626 table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
627 self.entry_balance_cc = gtk.Entry()
628 self.entry_balance_cc.set_width_chars(3)
629 self.entry_balance_cc.set_editable(False)
630 table.attach(self.entry_balance_cc, 1, 2, 1, 2)
631 self.button_sense_midi_balance = gtk.Button('Autoset')
632 self.button_sense_midi_balance.connect('clicked',
633 self.on_sense_midi_balance_clicked)
634 table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
639 self.entry_name.set_text(self.channel.channel_name)
640 if self.channel.channel.is_stereo:
641 self.stereo.set_active(True)
643 self.mono.set_active(True)
644 self.mode_hbox.set_sensitive(False)
645 self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
646 self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
648 def sense_popup_dialog(self, entry):
649 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
650 window.set_destroy_with_parent(True)
651 window.set_transient_for(self)
652 window.set_decorated(False)
653 window.set_modal(True)
654 window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
655 window.set_border_width(10)
660 vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
661 timeout_label = gtk.Label('This window will close in 5 seconds')
662 vbox.pack_start(timeout_label)
663 def close_sense_timeout(window, entry):
665 timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
666 if window.timeout == 0:
668 entry.set_text('%s' % self.mixer.last_midi_channel)
672 glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
674 def on_sense_midi_volume_clicked(self, *args):
675 self.sense_popup_dialog(self.entry_volume_cc)
677 def on_sense_midi_balance_clicked(self, *args):
678 self.sense_popup_dialog(self.entry_balance_cc)
680 def on_response_cb(self, dlg, response_id, *args):
681 self.channel.channel_properties_dialog = None
683 if response_id == gtk.RESPONSE_APPLY:
684 name = self.entry_name.get_text()
685 self.channel.channel_name = name
686 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
687 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
690 class NewChannelDialog(ChannelPropertiesDialog):
691 def __init__(self, parent, mixer):
692 gtk.Dialog.__init__(self, 'New Channel', parent)
696 self.stereo.set_active(True) # default to stereo
698 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
699 self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
701 def get_result(self):
702 return {'name': self.entry_name.get_text(),
703 'stereo': self.stereo.get_active(),
704 'volume_cc': self.entry_volume_cc.get_text(),
705 'balance_cc': self.entry_balance_cc.get_text()
708 class NewOutputChannelDialog(ChannelPropertiesDialog):
709 def __init__(self, parent, mixer):
710 gtk.Dialog.__init__(self, 'New Output Channel', parent)
714 # TODO: disable mode for output channels as mono output channels may
715 # not be correctly handled yet.
716 self.mode_hbox.set_sensitive(False)
717 self.stereo.set_active(True) # default to stereo
719 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
720 self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
722 def get_result(self):
723 return {'name': self.entry_name.get_text(),
724 'stereo': self.stereo.get_active(),
725 'volume_cc': self.entry_volume_cc.get_text(),
726 'balance_cc': self.entry_balance_cc.get_text()
730 class ControlGroup(gtk.HBox):
731 def __init__(self, output_channel):
732 self.output_channel = output_channel
733 gtk.HBox.__init__(self)
735 mute = gtk.ToggleButton()
737 #mute.set_active(self.channel.mute)
738 #mute.connect("toggled", self.on_mute_toggled)
739 self.pack_start(mute, True)
741 solo = gtk.ToggleButton()
743 #solo.set_active(self.channel.solo)
744 #solo.connect("toggled", self.on_solo_toggled)
745 self.pack_start(solo, True)
747 mute.modify_bg(gtk.STATE_NORMAL, output_channel.color)
748 solo.modify_bg(gtk.STATE_NORMAL, output_channel.color)