Comments button now indicates when comments are present, more dialog work (ask about...
[ardour.git] / gtk2_ardour / mixer_strip.cc
1 /*
2     Copyright (C) 2000-2002 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <cmath>
22 #include <glib.h>
23
24 #include <sigc++/bind.h>
25
26 #include <gtkmm2ext/gtk_ui.h>
27 #include <gtkmm2ext/utils.h>
28 #include <gtkmm2ext/choice.h>
29 #include <gtkmm2ext/slider_controller.h>
30 #include <gtkmm2ext/stop_signal.h>
31 #include <gtkmm2ext/bindable_button.h>
32 #include <gtkmm2ext/doi.h>
33
34 #include <ardour/ardour.h>
35 #include <ardour/session.h>
36 #include <ardour/audioengine.h>
37 #include <ardour/route.h>
38 #include <ardour/audio_track.h>
39 #include <ardour/diskstream.h>
40 #include <ardour/panner.h>
41 #include <ardour/send.h>
42 #include <ardour/insert.h>
43 #include <ardour/ladspa_plugin.h>
44 #include <ardour/connection.h>
45 #include <ardour/session_connection.h>
46
47 #include "ardour_ui.h"
48 #include "ardour_dialog.h"
49 #include "mixer_strip.h"
50 #include "mixer_ui.h"
51 #include "keyboard.h"
52 #include "plugin_selector.h"
53 #include "public_editor.h"
54
55 #include "plugin_ui.h"
56 #include "send_ui.h"
57 #include "io_selector.h"
58 #include "utils.h"
59 #include "gui_thread.h"
60
61 #include "i18n.h"
62
63 using namespace sigc;
64 using namespace ARDOUR;
65 using namespace Gtk;
66 using namespace Gtkmm2ext;
67
68 #ifdef VARISPEED_IN_MIXER_STRIP
69 static void 
70 speed_printer (char buf[32], Gtk::Adjustment& adj, void* arg)
71 {
72         float val = adj.get_value ();
73
74         if (val == 1.0) {
75                 strcpy (buf, "1");
76         } else {
77                 snprintf (buf, 32, "%.3f", val);
78         }
79 }
80 #endif 
81
82 MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, Route& rt, bool in_mixer)
83         : AxisView(sess),
84           RouteUI (rt, sess, _("mute"), _("solo"), _("RECORD")),
85           _mixer(mx),
86           pre_redirect_box (PreFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
87           post_redirect_box (PostFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
88           gpm (_route, sess),
89           panners (_route, sess),
90           button_table (6, 2),
91           gain_automation_style_button (""),
92           gain_automation_state_button (""),
93           pan_automation_style_button (""),
94           pan_automation_state_button (""),
95           comment_button (_("Comments")),
96           speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
97           speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
98
99 {
100         if (set_color_from_route()) {
101                 set_color (unique_random_color());
102         }
103
104         input_selector = 0;
105         output_selector = 0;
106         group_menu = 0;
107         _marked_for_display = false;
108         route_ops_menu = 0;
109         ignore_comment_edit = false;
110         ignore_toggle = false;
111         ignore_speed_adjustment = false;
112         comment_window = 0;
113         comment_area = 0;
114
115         width_button.add (*(manage (new Gtk::Image (get_xpm("lr.xpm")))));
116         hide_button.add (*(manage (new Gtk::Image (get_xpm("small_x.xpm")))));
117
118
119         input_label.set_text (_("INPUT"));
120         input_button.add (input_label);
121         input_button.set_name ("MixerIOButton");
122         input_label.set_name ("MixerIOButtonLabel");
123
124         output_label.set_text (_("OUTPUT"));
125         output_button.add (output_label);
126         output_button.set_name ("MixerIOButton");
127         output_label.set_name ("MixerIOButtonLabel");
128
129         rec_enable_button->set_name ("MixerRecordEnableButton");
130         rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
131
132         solo_button->set_name ("MixerSoloButton");
133         mute_button->set_name ("MixerMuteButton");
134         gain_automation_style_button.set_name ("MixerAutomationModeButton");
135         gain_automation_state_button.set_name ("MixerAutomationPlaybackButton");
136         pan_automation_style_button.set_name ("MixerAutomationModeButton");
137         pan_automation_state_button.set_name ("MixerAutomationPlaybackButton");
138
139         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_state_button, _("Pan automation mode"));
140         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_state_button, _("Gain automation mode"));
141
142         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_style_button, _("Pan automation type"));
143         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_style_button, _("Gain automation type"));
144
145         hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
146
147         width_button.unset_flags (Gtk::CAN_FOCUS);
148         hide_button.unset_flags (Gtk::CAN_FOCUS);
149         input_button.unset_flags (Gtk::CAN_FOCUS);
150         output_button.unset_flags (Gtk::CAN_FOCUS);
151         solo_button->unset_flags (Gtk::CAN_FOCUS);
152         mute_button->unset_flags (Gtk::CAN_FOCUS);
153         gain_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
154         gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
155         pan_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
156         pan_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
157
158         button_table.set_homogeneous (true);
159         button_table.set_spacings (0);
160
161         button_table.attach (name_button, 0, 2, 0, 1);
162         button_table.attach (group_button, 0, 2, 1, 2);
163         button_table.attach (input_button, 0, 2, 2, 3);
164
165         button_table.attach (*solo_button, 0, 1, 3, 4);
166         button_table.attach (*mute_button, 1, 2, 3, 4);
167
168         button_table.attach (gain_automation_state_button, 0, 1, 4, 5);
169         button_table.attach (pan_automation_state_button, 1, 2, 4, 5);
170
171         using namespace Menu_Helpers;
172         
173         gain_astate_menu.items().push_back (MenuElem (_("off"), 
174                                                       bind (mem_fun (_route, &IO::set_gain_automation_state), (AutoState) Off)));
175         gain_astate_menu.items().push_back (MenuElem (_("play"),
176                                                       bind (mem_fun (_route, &IO::set_gain_automation_state), (AutoState) Play)));
177         gain_astate_menu.items().push_back (MenuElem (_("write"),
178                                                       bind (mem_fun (_route, &IO::set_gain_automation_state), (AutoState) Write)));
179         gain_astate_menu.items().push_back (MenuElem (_("touch"),
180                                                       bind (mem_fun (_route, &IO::set_gain_automation_state), (AutoState) Touch)));
181         
182         gain_astyle_menu.items().push_back (MenuElem (_("trim")));
183         gain_astyle_menu.items().push_back (MenuElem (_("abs")));
184
185         pan_astate_menu.items().push_back (MenuElem (_("off"), 
186                                                      bind (mem_fun (_route.panner(), &Panner::set_automation_state), (AutoState) Off)));
187         pan_astate_menu.items().push_back (MenuElem (_("play"),
188                                                      bind (mem_fun (_route.panner(), &Panner::set_automation_state), (AutoState) Play)));
189         pan_astate_menu.items().push_back (MenuElem (_("write"),
190                                                      bind (mem_fun (_route.panner(), &Panner::set_automation_state), (AutoState) Write)));
191         pan_astate_menu.items().push_back (MenuElem (_("touch"),
192                                                      bind (mem_fun (_route.panner(), &Panner::set_automation_state), (AutoState) Touch)));
193
194         pan_astyle_menu.items().push_back (MenuElem (_("trim")));
195         pan_astyle_menu.items().push_back (MenuElem (_("abs")));
196         
197         gain_astate_menu.set_name ("ArdourContextMenu");
198         gain_astyle_menu.set_name ("ArdourContextMenu");
199         pan_astate_menu.set_name ("ArdourContextMenu");
200         pan_astyle_menu.set_name ("ArdourContextMenu");
201
202         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_style_button, _("gain automation mode"));
203         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_style_button, _("pan automation mode"));
204         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_state_button, _("gain automation state"));
205         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_state_button, _("pan automation state"));
206
207         if (is_audio_track()) {
208                 
209                 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
210
211                 at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen));
212
213 #ifdef VARISPEED_IN_MIXER_STRIP
214                 speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
215                 
216                 speed_frame.set_name ("BaseFrame");
217                 speed_frame.set_shadow_type (Gtk::SHADOW_IN);
218                 speed_frame.add (speed_spinner);
219                 
220                 speed_spinner.set_print_func (speed_printer, 0);
221
222                 ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("varispeed"));
223
224                 button_table.attach (speed_frame, 0, 2, 5, 6);
225 #endif /* VARISPEED_IN_MIXER_STRIP */
226
227                 button_table.attach (*rec_enable_button, 0, 2, 5, 6);
228         }
229
230         name_button.add (name_label);
231         name_button.set_name ("MixerNameButton");
232         Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
233
234         name_label.set_name ("MixerNameButtonLabel");
235         name_label.set_text (_route.name());
236
237         group_button.add (group_label);
238         group_button.set_name ("MixerGroupButton");
239         group_label.set_name ("MixerGroupButtonLabel");
240
241         comment_button.set_name ("MixerCommentButton");
242
243         ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route.comment()=="" ?
244                                                         _("Click to Add/Edit Comments"):
245                                                         _route.comment());
246
247         comment_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked));
248         
249         global_vpacker.set_border_width (0);
250         global_vpacker.set_spacing (0);
251
252         Gtk::VBox *whvbox = manage (new Gtk::VBox);
253
254         width_button.set_name ("MixerWidthButton");
255         hide_button.set_name ("MixerHideButton");
256
257         width_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::width_clicked));
258         hide_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::hide_clicked));
259
260         width_hide_box.pack_start (width_button, false, true);
261         width_hide_box.pack_end (hide_button, false, true);
262
263         whvbox->pack_start (width_hide_box, true, true);
264
265         global_vpacker.pack_start (*whvbox, Gtk::PACK_SHRINK);
266         global_vpacker.pack_start (button_table,Gtk::PACK_SHRINK);
267         global_vpacker.pack_start (pre_redirect_box, true, true);
268         global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK, 4);
269         global_vpacker.pack_start (post_redirect_box, true, true);
270         global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
271         global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
272         global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
273
274         global_frame.add (global_vpacker);
275         global_frame.set_shadow_type (Gtk::SHADOW_IN);
276         global_frame.set_name ("BaseFrame");
277
278         add (global_frame);
279
280         /* force setting of visible selected status */
281
282         _selected = true;
283         set_selected (false);
284
285         _packed = false;
286         _embedded = false;
287
288         _route.input_changed.connect (mem_fun(*this, &MixerStrip::input_changed));
289         _route.output_changed.connect (mem_fun(*this, &MixerStrip::output_changed));
290         _route.mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
291         _route.solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
292         _route.solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
293         _route.mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed));
294         _route.gain_automation_curve().automation_state_changed.connect (mem_fun(*this, &MixerStrip::gain_automation_state_changed));
295         _route.gain_automation_curve().automation_style_changed.connect (mem_fun(*this, &MixerStrip::gain_automation_style_changed));
296         _route.panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan));
297
298         if (is_audio_track()) {
299                 audio_track()->diskstream_changed.connect (mem_fun(*this, &MixerStrip::diskstream_changed));
300                 get_diskstream()->speed_changed.connect (mem_fun(*this, &MixerStrip::speed_changed));
301         }
302
303         _route.name_changed.connect (mem_fun(*this, &RouteUI::name_changed));
304         _route.comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed));
305         _route.gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed));
306
307         input_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::input_press), false);
308         output_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::output_press), false);
309
310         rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press));
311         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
312         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
313         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
314         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
315
316         gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::gain_automation_style_button_event), false);
317         gain_automation_style_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::gain_automation_style_button_event), false);
318         pan_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::pan_automation_style_button_event), false);
319         pan_automation_style_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::pan_automation_style_button_event), false);
320
321         gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::gain_automation_state_button_event), false);
322         gain_automation_state_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::gain_automation_state_button_event), false);
323         pan_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::pan_automation_state_button_event), false);
324         pan_automation_state_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::pan_automation_state_button_event), false);
325
326         name_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::name_button_button_release), false);
327
328         group_button.signal_button_release_event().connect (mem_fun(*this, &MixerStrip::select_mix_group), false);
329
330         _width = (Width) -1;
331         set_stuff_from_route ();
332
333         /* start off as a passthru strip. we'll correct this, if necessary,
334            in update_diskstream_display().
335         */
336
337         set_name ("AudioTrackStripBase");
338
339         /* now force an update of all the various elements */
340
341         pre_redirect_box.update();
342         post_redirect_box.update();
343         mute_changed (0);
344         solo_changed (0);
345         name_changed (0);
346         comment_changed (0);
347         mix_group_changed (0);
348         gain_automation_state_changed ();
349         pan_automation_state_changed ();
350         connect_to_pan ();
351
352         panners.setup_pan ();
353
354         if (is_audio_track()) {
355                 speed_changed ();
356         }
357
358         update_diskstream_display ();
359         update_input_display ();
360         update_output_display ();
361
362         add_events (Gdk::BUTTON_RELEASE_MASK);
363 }
364
365 MixerStrip::~MixerStrip ()
366 {
367         GoingAway(); /* EMIT_SIGNAL */
368
369         if (input_selector) {
370                 delete input_selector;
371         }
372
373         if (output_selector) {
374                 delete output_selector;
375         }
376 }
377
378 void
379 MixerStrip::set_stuff_from_route ()
380 {
381         XMLProperty *prop;
382         
383         ensure_xml_node ();
384
385         if ((prop = xml_node->property ("strip_width")) != 0) {
386                 if (prop->value() == "wide") {
387                         set_width (Wide);
388                 } else if (prop->value() == "narrow") {
389                         set_width (Narrow);
390                 }
391                 else {
392                         error << string_compose(_("unknown strip width \"%1\" in XML GUI information"), prop->value()) << endmsg;
393                         set_width (Wide);
394                 }
395         }
396         else {
397                 set_width (Wide);
398         }
399
400         if ((prop = xml_node->property ("shown_mixer")) != 0) {
401                 if (prop->value() == "no") {
402                         _marked_for_display = false;
403                 } else {
404                         _marked_for_display = true;
405                 }
406         }
407         else {
408                 /* backwards compatibility */
409                 _marked_for_display = true;
410         }
411 }
412
413 void
414 MixerStrip::set_width (Width w)
415 {
416         /* always set the gpm width again, things may be hidden */
417         gpm.set_width (w);
418         panners.set_width (w);
419         pre_redirect_box.set_width (w);
420         post_redirect_box.set_width (w);
421         
422         if (_width == w) {
423                 return;
424         }
425
426         ensure_xml_node ();
427         
428         _width = w;
429
430         switch (w) {
431         case Wide:
432                 set_size_request (-1, -1);
433                 xml_node->add_property ("strip_width", "wide");
434
435                 rec_enable_button->set_label (_("RECORD"));
436                 mute_button->set_label  (_("mute"));
437                 solo_button->set_label (_("solo"));
438
439                 if (_route.comment() == "") {
440                        comment_button.set_label (_("Comments"));
441                 } else {
442                        comment_button.set_label (_("*Comments*"));
443                 }
444
445                 gain_automation_style_button.set_label (astyle_string(_route.gain_automation_curve().automation_style()));
446                 gain_automation_state_button.set_label (astate_string(_route.gain_automation_curve().automation_state()));
447                 pan_automation_style_button.set_label (astyle_string(_route.panner().automation_style()));
448                 pan_automation_state_button.set_label (astate_string(_route.panner().automation_state()));
449                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "long", 2, 2);
450                 break;
451
452         case Narrow:
453                 set_size_request (50, -1);
454                 xml_node->add_property ("strip_width", "narrow");
455
456                 rec_enable_button->set_label (_("REC"));
457                 mute_button->set_label (_("m"));
458                 solo_button->set_label (_("s"));
459
460                 if (_route.comment() == "") {
461                        comment_button.set_label (_("Cmt"));
462                 } else {
463                        comment_button.set_label (_("*Cmt*"));
464                 }
465
466                 gain_automation_style_button.set_label (short_astyle_string(_route.gain_automation_curve().automation_style()));
467                 gain_automation_state_button.set_label (short_astate_string(_route.gain_automation_curve().automation_state()));
468                 pan_automation_style_button.set_label (short_astyle_string(_route.panner().automation_style()));
469                 pan_automation_state_button.set_label (short_astate_string(_route.panner().automation_state()));
470                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
471                 break;
472         }
473
474         update_input_display ();
475         update_output_display ();
476         mix_group_changed (0);
477         name_changed (0);
478
479 }
480
481 void
482 MixerStrip::set_packed (bool yn)
483 {
484         _packed = yn;
485
486         ensure_xml_node ();
487
488         if (_packed) {
489                 xml_node->add_property ("shown_mixer", "yes");
490         } else {
491                 xml_node->add_property ("shown_mixer", "no");
492         }
493 }
494
495
496 gint
497 MixerStrip::output_press (GdkEventButton *ev)
498 {
499         using namespace Menu_Helpers;
500
501         if (!_session.engine().connected()) {
502                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
503                 msg.run ();
504                 return true;
505         }
506
507         MenuList& citems = output_menu.items();
508         output_menu.set_name ("ArdourContextMenu");
509         citems.clear();
510
511         citems.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_output_configuration)));
512         citems.push_back (SeparatorElem());
513         citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
514         citems.push_back (SeparatorElem());
515
516         _session.foreach_connection (this, &MixerStrip::add_connection_to_output_menu);
517
518         output_menu.popup (1, ev->time);
519
520         return TRUE;
521 }
522
523 void
524 MixerStrip::edit_output_configuration ()
525 {
526         if (output_selector == 0) {
527                 output_selector = new IOSelectorWindow (_session, _route, false);
528         } 
529
530         if (output_selector->is_visible()) {
531                 output_selector->get_toplevel()->get_window()->raise();
532         } else {
533                 output_selector->show_all ();
534         }
535 }
536
537 void
538 MixerStrip::edit_input_configuration ()
539 {
540         if (input_selector == 0) {
541                 input_selector = new IOSelectorWindow (_session, _route, true);
542         } 
543
544         if (input_selector->is_visible()) {
545                 input_selector->get_toplevel()->get_window()->raise();
546         } else {
547                 input_selector->show_all ();
548         }
549 }
550
551 gint
552 MixerStrip::input_press (GdkEventButton *ev)
553 {
554         using namespace Menu_Helpers;
555
556         MenuList& citems = input_menu.items();
557         input_menu.set_name ("ArdourContextMenu");
558         citems.clear();
559
560         if (!_session.engine().connected()) {
561                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
562                 msg.run ();
563                 return true;
564         }
565
566 #if ADVANCED_ROUTE_DISKSTREAM_CONNECTIVITY
567         if (is_audio_track()) {
568                 citems.push_back (MenuElem (_("Track"), mem_fun(*this, &MixerStrip::select_stream_input)));
569         }
570 #endif
571         citems.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_input_configuration)));
572         citems.push_back (SeparatorElem());
573         citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
574         citems.push_back (SeparatorElem());
575
576         _session.foreach_connection (this, &MixerStrip::add_connection_to_input_menu);
577         input_menu.popup (1, ev->time);
578
579         return TRUE;
580 }
581
582 void
583 MixerStrip::connection_input_chosen (ARDOUR::Connection *c)
584 {
585         if (!ignore_toggle) {
586
587                 try { 
588                         _route.use_input_connection (*c, this);
589                 }
590
591                 catch (AudioEngine::PortRegistrationFailure& err) {
592                         error << _("could not register new ports required for that connection")
593                               << endmsg;
594                 }
595         }
596 }
597
598 void
599 MixerStrip::connection_output_chosen (ARDOUR::Connection *c)
600 {
601         if (!ignore_toggle) {
602
603                 try { 
604                         _route.use_output_connection (*c, this);
605                 }
606
607                 catch (AudioEngine::PortRegistrationFailure& err) {
608                         error << _("could not register new ports required for that connection")
609                               << endmsg;
610                 }
611         }
612 }
613
614 void
615 MixerStrip::add_connection_to_input_menu (ARDOUR::Connection* c)
616 {
617         using namespace Menu_Helpers;
618
619         if (dynamic_cast<InputConnection *> (c) == 0) {
620                 return;
621         }
622
623         MenuList& citems = input_menu.items();
624         
625         if (c->nports() == _route.n_inputs()) {
626
627                 citems.push_back (CheckMenuElem (c->name(), bind (mem_fun(*this, &MixerStrip::connection_input_chosen), c)));
628                 
629                 ARDOUR::Connection *current = _route.input_connection();
630                 
631                 if (current == c) {
632                         ignore_toggle = true;
633                         dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
634                         ignore_toggle = false;
635                 }
636         }
637 }
638
639 void
640 MixerStrip::add_connection_to_output_menu (ARDOUR::Connection* c)
641 {
642         using namespace Menu_Helpers;
643
644         if (dynamic_cast<OutputConnection *> (c) == 0) {
645                 return;
646         }
647
648         if (c->nports() == _route.n_outputs()) {
649
650                 MenuList& citems = output_menu.items();
651                 citems.push_back (CheckMenuElem (c->name(), bind (mem_fun(*this, &MixerStrip::connection_output_chosen), c)));
652                 
653                 ARDOUR::Connection *current = _route.output_connection();
654                 
655                 if (current == c) {
656                         ignore_toggle = true;
657                         dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
658                         ignore_toggle = false;
659                 }
660         }
661 }
662
663 void
664 MixerStrip::select_stream_input ()
665 {
666         using namespace Menu_Helpers;
667
668         Menu *stream_menu = manage (new Menu);
669         MenuList& items = stream_menu->items();
670         stream_menu->set_name ("ArdourContextMenu");
671         
672         Session::DiskStreamList streams = _session.disk_streams();
673
674         for (Session::DiskStreamList::iterator i = streams.begin(); i != streams.end(); ++i) {
675
676                 if (!(*i)->hidden()) {
677
678                         items.push_back (CheckMenuElem ((*i)->name(), bind (mem_fun(*this, &MixerStrip::stream_input_chosen), *i)));
679                         
680                         if (get_diskstream() == *i) {
681                                 ignore_toggle = true;
682                                 static_cast<CheckMenuItem *> (&items.back())->set_active (true);
683                                 ignore_toggle = false;
684                         } 
685                 }
686         }
687         
688         stream_menu->popup (1, 0);
689 }
690
691 void
692 MixerStrip::stream_input_chosen (DiskStream *stream)
693 {
694         if (is_audio_track()) {
695                 audio_track()->set_diskstream (*stream, this);
696         }
697 }
698
699 void
700 MixerStrip::update_diskstream_display ()
701 {
702         if (is_audio_track()) {
703
704                 map_frozen ();
705
706                 update_input_display ();
707
708                 if (input_selector) {
709                         input_selector->hide_all ();
710                 }
711
712                 show_route_color ();
713
714         } else {
715
716                 map_frozen ();
717
718                 update_input_display ();
719                 show_passthru_color ();
720         }
721 }
722
723 void
724 MixerStrip::connect_to_pan ()
725 {
726         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan));
727         
728         panstate_connection.disconnect ();
729         panstyle_connection.disconnect ();
730
731         if (!_route.panner().empty()) {
732                 StreamPanner* sp = _route.panner().front();
733
734                 panstate_connection = sp->automation().automation_state_changed.connect (mem_fun(*this, &MixerStrip::pan_automation_state_changed));
735                 panstyle_connection = sp->automation().automation_style_changed.connect (mem_fun(*this, &MixerStrip::pan_automation_style_changed));
736         }
737
738         panners.pan_changed (this);
739 }
740
741 void
742 MixerStrip::update_input_display ()
743 {
744         ARDOUR::Connection *c;
745
746         if ((c = _route.input_connection()) != 0) {
747                 input_label.set_text (c->name());
748         } else {
749                 switch (_width) {
750                 case Wide:
751                         input_label.set_text (_("INPUT"));
752                         break;
753                 case Narrow:
754                         input_label.set_text (_("IN"));
755                         break;
756                 }
757         }
758
759         panners.setup_pan ();
760 }
761
762 void
763 MixerStrip::update_output_display ()
764 {
765         ARDOUR::Connection *c;
766
767         if ((c = _route.output_connection()) != 0) {
768                 output_label.set_text (c->name());
769         } else {
770                 switch (_width) {
771                 case Wide:
772                         output_label.set_text (_("OUTPUT"));
773                         break;
774                 case Narrow:
775                         output_label.set_text (_("OUT"));
776                         break;
777                 }
778         }
779
780         gpm.setup_meters ();
781         panners.setup_pan ();
782 }
783
784 void
785 MixerStrip::fast_update ()
786 {
787         gpm.update_meters ();
788 }
789
790 gint
791 MixerStrip::gain_automation_state_button_event (GdkEventButton *ev)
792 {
793         if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
794                 return TRUE;
795         }
796         
797         switch (ev->button) {
798         case 1:
799                 switch (ev->button) {
800                 case 1:
801                         gain_astate_menu.popup (1, ev->time);
802                         break;
803                 default:
804                         break;
805                 }
806         }
807
808         return TRUE;
809 }
810
811 gint
812 MixerStrip::gain_automation_style_button_event (GdkEventButton *ev)
813 {
814         if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
815                 return TRUE;
816         }
817
818         switch (ev->button) {
819         case 1:
820                 gain_astyle_menu.popup (1, ev->time);
821                 break;
822         default:
823                 break;
824         }
825         return TRUE;
826 }
827
828 gint
829 MixerStrip::pan_automation_state_button_event (GdkEventButton *ev)
830 {
831         using namespace Menu_Helpers;
832
833         if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
834                 return TRUE;
835         }
836
837         switch (ev->button) {
838         case 1:
839                 pan_astate_menu.popup (1, ev->time);
840                 break;
841         default:
842                 break;
843         }
844
845         return TRUE;
846 }
847
848 gint
849 MixerStrip::pan_automation_style_button_event (GdkEventButton *ev)
850 {
851         switch (ev->button) {
852         case 1:
853                 pan_astyle_menu.popup (1, ev->time);
854                 break;
855         default:
856                 break;
857         }
858         return TRUE;
859 }
860
861 string
862 MixerStrip::astate_string (AutoState state)
863 {
864         return _astate_string (state, false);
865 }
866
867 string
868 MixerStrip::short_astate_string (AutoState state)
869 {
870         return _astate_string (state, true);
871 }
872
873 string
874 MixerStrip::_astate_string (AutoState state, bool shrt)
875 {
876         string sstr;
877
878         switch (state) {
879         case Off:
880                 sstr = (shrt ? "--" : _("off"));
881                 break;
882         case Play:
883                 sstr = (shrt ? "P" : _("aplay"));
884                 break;
885         case Touch:
886                 sstr = (shrt ? "T" : _("touch"));
887                 break;
888         case Write:
889                 sstr = (shrt ? "W" : _("awrite"));
890                 break;
891         }
892
893         return sstr;
894 }
895
896 string
897 MixerStrip::astyle_string (AutoStyle style)
898 {
899         return _astyle_string (style, false);
900 }
901
902 string
903 MixerStrip::short_astyle_string (AutoStyle style)
904 {
905         return _astyle_string (style, true);
906 }
907
908 string
909 MixerStrip::_astyle_string (AutoStyle style, bool shrt)
910 {
911         if (style & Trim) {
912                 return _("trim");
913         } else {
914                 /* XXX it might different in different languages */
915
916                 return (shrt ? _("abs") : _("abs"));
917         }
918 }
919
920 void
921 MixerStrip::diskstream_changed (void *src)
922 {
923         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display));
924 }       
925
926 void
927 MixerStrip::gain_automation_style_changed ()
928 {
929         switch (_width) {
930         case Wide:
931                 static_cast<Gtk::Label*> (gain_automation_style_button.get_child())->set_text (astyle_string(_route.gain_automation_curve().automation_style()));
932                 break;
933         case Narrow:
934                 static_cast<Gtk::Label*> (gain_automation_style_button.get_child())->set_text (short_astyle_string(_route.gain_automation_curve().automation_style()));
935                 break;
936         }
937 }
938
939 void
940 MixerStrip::gain_automation_state_changed ()
941 {
942         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::gain_automation_state_changed));
943         
944         bool x;
945
946         switch (_width) {
947         case Wide:
948                 static_cast<Gtk::Label*> (gain_automation_state_button.get_child())->set_text (astate_string(_route.gain_automation_curve().automation_state()));
949                 break;
950         case Narrow:
951                 static_cast<Gtk::Label*> (gain_automation_state_button.get_child())->set_text (short_astate_string(_route.gain_automation_curve().automation_state()));
952                 break;
953         }
954
955         x = (_route.gain_automation_state() != Off);
956         
957         if (gain_automation_state_button.get_active() != x) {
958                 ignore_toggle = true;
959                 gain_automation_state_button.set_active (x);
960                 ignore_toggle = false;
961         }
962
963         gpm.update_gain_sensitive ();
964         
965         /* start watching automation so that things move */
966         
967         gain_watching.disconnect();
968
969         if (x) {
970                 gain_watching = ARDOUR_UI::RapidScreenUpdate.connect (mem_fun (gpm, &GainMeter::effective_gain_display));
971         }
972 }
973
974 void
975 MixerStrip::pan_automation_style_changed ()
976 {
977         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::pan_automation_style_changed));
978         
979         switch (_width) {
980         case Wide:
981                 static_cast<Gtk::Label*> (pan_automation_style_button.get_child())->set_text (astyle_string(_route.panner().automation_style()));
982                 break;
983         case Narrow:
984                 static_cast<Gtk::Label*> (pan_automation_style_button.get_child())->set_text (short_astyle_string(_route.panner().automation_style()));
985                 break;
986         }
987 }
988
989 void
990 MixerStrip::pan_automation_state_changed ()
991 {
992         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::pan_automation_state_changed));
993         
994         bool x;
995
996         switch (_width) {
997         case Wide:
998                 static_cast<Gtk::Label*> (pan_automation_state_button.get_child())->set_text (astate_string(_route.panner().automation_state()));
999                 break;
1000         case Narrow:
1001                 static_cast<Gtk::Label*> (pan_automation_state_button.get_child())->set_text (short_astate_string(_route.panner().automation_state()));
1002                 break;
1003         }
1004
1005         /* when creating a new session, we get to create busses (and
1006            sometimes tracks) with no outputs by the time they get
1007            here.
1008         */
1009
1010         if (_route.panner().empty()) {
1011                 return;
1012         }
1013
1014         x = (_route.panner().front()->automation().automation_state() != Off);
1015
1016         if (pan_automation_state_button.get_active() != x) {
1017                 ignore_toggle = true;
1018                 pan_automation_state_button.set_active (x);
1019                 ignore_toggle = false;
1020         }
1021
1022         panners.update_pan_sensitive ();
1023         
1024         /* start watching automation so that things move */
1025         
1026         pan_watching.disconnect();
1027
1028         if (x) {
1029                 pan_watching = ARDOUR_UI::RapidScreenUpdate.connect (mem_fun (panners, &PannerUI::effective_pan_display));
1030         }
1031 }
1032
1033 void
1034 MixerStrip::input_changed (IOChange change, void *src)
1035 {
1036         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display));
1037 }
1038
1039 void
1040 MixerStrip::output_changed (IOChange change, void *src)
1041 {
1042         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display));
1043 }
1044
1045 void
1046 MixerStrip::comment_button_clicked ()
1047 {
1048         if (comment_window == 0) {
1049                 setup_comment_editor ();
1050         }
1051
1052         if (comment_window->is_visible()) {
1053                 comment_window->hide ();
1054                 return;
1055         } 
1056
1057         comment_window->set_position (Gtk::WIN_POS_MOUSE);
1058         comment_window->show();
1059         comment_window->present();
1060         comment_window->run(); // we don't care what the response is
1061         comment_window->hide();
1062
1063         string str =  comment_area->get_buffer()->get_text();
1064         if (_route.comment() != str) {
1065                _route.set_comment (str, this);
1066
1067                switch (_width) {
1068                  
1069                case Wide:
1070                       if (! str.empty()) {
1071                              comment_button.set_label (_("*Comments*"));
1072                       } else {
1073                              comment_button.set_label (_("Comments"));
1074                       }
1075                       break;
1076                  
1077                case Narrow:
1078                       if (! str.empty()) {
1079                              comment_button.set_label (_("*Cmt*"));
1080                       } else {
1081                              comment_button.set_label (_("Cmt"));
1082                       } 
1083                       break;
1084                }
1085                
1086                ARDOUR_UI::instance()->tooltips().set_tip (comment_button, 
1087                                                           str.empty() ? _("Click to Add/Edit Comments") : str);
1088         }
1089 }
1090
1091 void
1092 MixerStrip::setup_comment_editor ()
1093 {
1094         string title;
1095         title = _route.name();
1096         title += _(": comment editor");
1097
1098         comment_window = new ArdourDialog (title, false);
1099         comment_area = manage (new TextView());
1100
1101         comment_area->set_name ("MixerTrackCommentArea");
1102         comment_area->set_editable (true);
1103         comment_area->get_buffer()->set_text (_route.comment());
1104         comment_area->set_size_request (200,124);
1105         comment_area->show ();
1106
1107         comment_window->get_vbox()->pack_start (*comment_area);
1108         comment_window->get_action_area()->hide();
1109 }
1110
1111 void
1112 MixerStrip::comment_changed (void *src)
1113 {
1114         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed), src));
1115         
1116         if (src != this) {
1117                 ignore_comment_edit = true;
1118                 if (comment_area) {
1119                         comment_area->get_buffer()->set_text (_route.comment());
1120                 }
1121                 ignore_comment_edit = false;
1122         }
1123 }
1124
1125 void
1126 MixerStrip::set_mix_group (RouteGroup *rg)
1127 {
1128         _route.set_mix_group (rg, this);
1129 }
1130
1131 void
1132 MixerStrip::add_mix_group_to_menu (RouteGroup *rg, RadioMenuItem::Group* group)
1133 {
1134         using namespace Menu_Helpers;
1135
1136         MenuList& items = group_menu->items();
1137
1138         items.push_back (RadioMenuElem (*group, rg->name(), bind (mem_fun(*this, &MixerStrip::set_mix_group), rg)));
1139
1140         if (_route.mix_group() == rg) {
1141                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
1142         }
1143 }
1144
1145 bool
1146 MixerStrip::select_mix_group (GdkEventButton *ev)
1147 {
1148         using namespace Menu_Helpers;
1149
1150         if (group_menu == 0) {
1151                 group_menu = new Menu;
1152         } 
1153         group_menu->set_name ("ArdourContextMenu");
1154         MenuList& items = group_menu->items();
1155         RadioMenuItem::Group group;
1156
1157         items.clear ();
1158         items.push_back (RadioMenuElem (group, _("no group"), bind (mem_fun(*this, &MixerStrip::set_mix_group), (RouteGroup *) 0)));
1159
1160         _session.foreach_mix_group (bind (mem_fun (*this, &MixerStrip::add_mix_group_to_menu), &group));
1161         
1162         group_menu->popup (1, 0);
1163
1164         return true;
1165 }       
1166
1167 void
1168 MixerStrip::mix_group_changed (void *ignored)
1169 {
1170         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::mix_group_changed), ignored));
1171         
1172         RouteGroup *rg = _route.mix_group();
1173         
1174         if (rg) {
1175                 group_label.set_text (rg->name());
1176         } else {
1177                 switch (_width) {
1178                 case Wide:
1179                         group_label.set_text (_("no group"));
1180                         break;
1181                 case Narrow:
1182                         group_label.set_text (_("~G"));
1183                         break;
1184                 }
1185         }
1186 }
1187
1188
1189 void 
1190 MixerStrip::route_gui_changed (string what_changed, void* ignored)
1191 {
1192         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed), what_changed, ignored));
1193         
1194         if (what_changed == "color") {
1195                 if (set_color_from_route () == 0) {
1196                         show_route_color ();
1197                 }
1198         }
1199 }
1200
1201 void
1202 MixerStrip::show_route_color ()
1203 {
1204         name_button.modify_bg (STATE_NORMAL, color());
1205         route_active_changed ();
1206 }
1207
1208 void
1209 MixerStrip::show_passthru_color ()
1210 {
1211         route_active_changed ();
1212 }
1213
1214 void
1215 MixerStrip::build_route_ops_menu ()
1216 {
1217         using namespace Menu_Helpers;
1218
1219         route_ops_menu = manage (new Menu);
1220         route_ops_menu->set_name ("ArdourContextMenu");
1221
1222         MenuList& items = route_ops_menu->items();
1223         
1224         items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename)));
1225         items.push_back (SeparatorElem());
1226         items.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active)));
1227         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1228         route_active_menu_item->set_active (_route.active());
1229         items.push_back (SeparatorElem());
1230         items.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity)));
1231         polarity_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1232         polarity_menu_item->set_active (_route.phase_invert());
1233
1234         build_remote_control_menu ();
1235         
1236         items.push_back (SeparatorElem());
1237         items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
1238
1239         items.push_back (SeparatorElem());
1240         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
1241 }
1242
1243 gint
1244 MixerStrip::name_button_button_release (GdkEventButton* ev)
1245 {
1246         if (ev->button == 3) {
1247                 list_route_operations ();
1248         }
1249         return FALSE;
1250 }
1251
1252 void
1253 MixerStrip::list_route_operations ()
1254 {
1255         if (route_ops_menu == 0) {
1256                 build_route_ops_menu ();
1257         }
1258         
1259         refresh_remote_control_menu();
1260
1261         route_ops_menu->popup (1, 0);
1262 }
1263
1264
1265 void
1266 MixerStrip::speed_adjustment_changed ()
1267 {
1268         /* since there is a usable speed adjustment, there has to be a diskstream */
1269         if (!ignore_speed_adjustment) {
1270                 get_diskstream()->set_speed (speed_adjustment.get_value());
1271         }
1272 }
1273
1274 void
1275 MixerStrip::speed_changed ()
1276 {
1277         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_speed_display));
1278 }
1279
1280 void
1281 MixerStrip::update_speed_display ()
1282 {
1283         float val;
1284         
1285         val = get_diskstream()->speed();
1286
1287         if (val != 1.0) {
1288                 speed_spinner.set_name ("MixerStripSpeedBaseNotOne");
1289         } else {
1290                 speed_spinner.set_name ("MixerStripSpeedBase");
1291         }
1292
1293         if (speed_adjustment.get_value() != val) {
1294                 ignore_speed_adjustment = true;
1295                 speed_adjustment.set_value (val);
1296                 ignore_speed_adjustment = false;
1297         }
1298 }                       
1299
1300
1301 void
1302 MixerStrip::set_selected (bool yn)
1303 {
1304         AxisView::set_selected (yn);
1305         if (_selected) {
1306                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1307                 global_frame.set_name ("MixerStripSelectedFrame");
1308         } else {
1309                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1310                 global_frame.set_name ("MixerStripFrame");
1311         }
1312         global_frame.queue_draw ();
1313 }
1314
1315 void
1316 MixerStrip::name_changed (void *src)
1317 {
1318         switch (_width) {
1319         case Wide:
1320                 RouteUI::name_changed (src);
1321                 break;
1322         case Narrow:
1323                 name_label.set_text (short_version (_route.name(), 5));
1324                 break;
1325         }
1326 }
1327
1328 void
1329 MixerStrip::width_clicked ()
1330 {
1331         switch (_width) {
1332         case Wide:
1333                 set_width (Narrow);
1334                 break;
1335         case Narrow:
1336                 set_width (Wide);
1337                 break;
1338         }
1339 }
1340
1341 void
1342 MixerStrip::hide_clicked ()
1343 {
1344         if (_embedded) {
1345                  Hiding(); /* EMIT_SIGNAL */
1346         } else {
1347                 _mixer.hide_strip (this);
1348         }
1349 }
1350
1351 void
1352 MixerStrip::set_embedded (bool yn)
1353 {
1354         _embedded = yn;
1355 }
1356
1357 void
1358 MixerStrip::map_frozen ()
1359 {
1360         ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen));
1361
1362         AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
1363
1364         if (at) {
1365                 switch (at->freeze_state()) {
1366                 case AudioTrack::Frozen:
1367                         pre_redirect_box.set_sensitive (false);
1368                         post_redirect_box.set_sensitive (false);
1369                         speed_spinner.set_sensitive (false);
1370                         break;
1371                 default:
1372                         pre_redirect_box.set_sensitive (true);
1373                         post_redirect_box.set_sensitive (true);
1374                         speed_spinner.set_sensitive (true);
1375                         break;
1376                 }
1377         }
1378         _route.foreach_redirect (this, &MixerStrip::hide_redirect_editor);
1379 }
1380
1381 void
1382 MixerStrip::hide_redirect_editor (Redirect* redirect)
1383 {
1384         void* gui = redirect->get_gui ();
1385         
1386         if (gui) {
1387                 static_cast<Gtk::Widget*>(gui)->hide ();
1388         }
1389 }
1390
1391 void
1392 MixerStrip::route_active_changed ()
1393 {
1394         RouteUI::route_active_changed ();
1395
1396         if (is_audio_track()) {
1397                 if (_route.active()) {
1398                         set_name ("AudioTrackStripBase");
1399                         gpm.set_meter_strip_name ("AudioTrackStripBase");
1400                 } else {
1401                         set_name ("AudioTrackStripBaseInactive");
1402                         gpm.set_meter_strip_name ("AudioTrackStripBaseInactive");
1403                 }
1404                 gpm.set_fader_name ("AudioTrackFader");
1405         } else {
1406                 if (_route.active()) {
1407                         set_name ("AudioBusStripBase");
1408                         gpm.set_meter_strip_name ("AudioBusStripBase");
1409                 } else {
1410                         set_name ("AudioBusStripBaseInactive");
1411                         gpm.set_meter_strip_name ("AudioBusStripBaseInactive");
1412                 }
1413                 gpm.set_fader_name ("AudioBusFader");
1414         }
1415 }
1416
1417 RouteGroup*
1418 MixerStrip::mix_group() const
1419 {
1420         return _route.mix_group();
1421 }