Populate strips output menus with a more user-friendly heuristic
[ardour.git] / gtk2_ardour / mixer_strip.cc
1 /*
2     Copyright (C) 2000-2006 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
19 #include <cmath>
20 #include <list>
21 #include <algorithm>
22
23 #include <sigc++/bind.h>
24
25 #include <gtkmm/messagedialog.h>
26
27 #include "pbd/convert.h"
28 #include "pbd/enumwriter.h"
29 #include "pbd/replace_all.h"
30 #include "pbd/stacktrace.h"
31
32 #include "ardour/amp.h"
33 #include "ardour/audio_track.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/internal_send.h"
36 #include "ardour/io.h"
37 #include "ardour/meter.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/pannable.h"
40 #include "ardour/panner.h"
41 #include "ardour/panner_shell.h"
42 #include "ardour/panner_manager.h"
43 #include "ardour/port.h"
44 #include "ardour/profile.h"
45 #include "ardour/route.h"
46 #include "ardour/route_group.h"
47 #include "ardour/send.h"
48 #include "ardour/session.h"
49 #include "ardour/types.h"
50 #include "ardour/user_bundle.h"
51 #include "ardour/vca.h"
52 #include "ardour/vca_manager.h"
53
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/menu_elems.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/doi.h"
58
59 #include "widgets/tooltips.h"
60
61 #include "ardour_window.h"
62 #include "enums_convert.h"
63 #include "mixer_strip.h"
64 #include "mixer_ui.h"
65 #include "keyboard.h"
66 #include "public_editor.h"
67 #include "send_ui.h"
68 #include "io_selector.h"
69 #include "utils.h"
70 #include "gui_thread.h"
71 #include "route_group_menu.h"
72 #include "meter_patterns.h"
73 #include "ui_config.h"
74
75 #include "pbd/i18n.h"
76
77 using namespace ARDOUR;
78 using namespace ArdourWidgets;
79 using namespace PBD;
80 using namespace Gtk;
81 using namespace Gtkmm2ext;
82 using namespace std;
83 using namespace ArdourMeter;
84
85 MixerStrip* MixerStrip::_entered_mixer_strip;
86 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
87
88 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89         : SessionHandlePtr (sess)
90         , RouteUI (sess)
91         , _mixer(mx)
92         , _mixer_owned (in_mixer)
93         , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
94         , gpm (sess, 250)
95         , panners (sess)
96         , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
97         , rec_mon_table (2, 2)
98         , solo_iso_table (1, 2)
99         , mute_solo_table (1, 2)
100         , bottom_button_table (1, 3)
101         , monitor_section_button (0)
102         , midi_input_enable_button (0)
103         , _plugin_insert_cnt (0)
104         , _comment_button (_("Comments"))
105         , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
106         , _visibility (X_("mixer-element-visibility"))
107         , control_slave_ui (sess)
108 {
109         init ();
110
111         if (!_mixer_owned) {
112                 /* the editor mixer strip: don't destroy it every time
113                    the underlying route goes away.
114                 */
115
116                 self_destruct = false;
117         }
118 }
119
120 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
121         : SessionHandlePtr (sess)
122         , RouteUI (sess)
123         , _mixer(mx)
124         , _mixer_owned (in_mixer)
125         , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
126         , gpm (sess, 250)
127         , panners (sess)
128         , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
129         , rec_mon_table (2, 2)
130         , solo_iso_table (1, 2)
131         , mute_solo_table (1, 2)
132         , bottom_button_table (1, 3)
133         , monitor_section_button (0)
134         , midi_input_enable_button (0)
135         , _plugin_insert_cnt (0)
136         , _comment_button (_("Comments"))
137         , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
138         , _visibility (X_("mixer-element-visibility"))
139         , control_slave_ui (sess)
140 {
141         init ();
142         set_route (rt);
143 }
144
145 void
146 MixerStrip::init ()
147 {
148         _entered_mixer_strip= 0;
149         group_menu = 0;
150         route_ops_menu = 0;
151         ignore_comment_edit = false;
152         ignore_toggle = false;
153         comment_area = 0;
154         _width_owner = 0;
155
156         /* the length of this string determines the width of the mixer strip when it is set to `wide' */
157         longest_label = "longest label";
158
159         string t = _("Click to toggle the width of this mixer strip.");
160         if (_mixer_owned) {
161                 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
162         }
163
164         width_button.set_icon (ArdourIcon::StripWidth);
165         hide_button.set_tweaks (ArdourButton::Square);
166         set_tooltip (width_button, t);
167
168         hide_button.set_icon (ArdourIcon::CloseCross);
169         hide_button.set_tweaks (ArdourButton::Square);
170         set_tooltip (&hide_button, _("Hide this mixer strip"));
171
172         input_button_box.set_spacing(2);
173
174         input_button.set_text (_("Input"));
175         input_button.set_name ("mixer strip button");
176         input_button_box.pack_start (input_button, true, true);
177
178         output_button.set_text (_("Output"));
179         output_button.set_name ("mixer strip button");
180
181         bottom_button_table.attach (gpm.meter_point_button, 2, 3, 0, 1);
182
183         hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
184
185         solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186         solo_isolated_led->show ();
187         solo_isolated_led->set_no_show_all (true);
188         solo_isolated_led->set_name (X_("solo isolate"));
189         solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190         solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191         UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
192
193         solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194         solo_safe_led->show ();
195         solo_safe_led->set_no_show_all (true);
196         solo_safe_led->set_name (X_("solo safe"));
197         solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198         solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199         UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
200
201         solo_safe_led->set_text (S_("SoloLock|Lock"));
202         solo_isolated_led->set_text (_("Iso"));
203
204         solo_iso_table.set_homogeneous (true);
205         solo_iso_table.set_spacings (2);
206         if (!ARDOUR::Profile->get_trx()) {
207                 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208                 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
209         }
210         solo_iso_table.show ();
211
212         rec_mon_table.set_homogeneous (true);
213         rec_mon_table.set_row_spacings (2);
214         rec_mon_table.set_col_spacings (2);
215         if (ARDOUR::Profile->get_mixbus()) {
216                 rec_mon_table.resize (1, 3);
217                 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218                 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219         } else if (!ARDOUR::Profile->get_trx()) {
220                 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221                 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
222         }
223         rec_mon_table.show ();
224
225         if (solo_isolated_led) {
226                 button_size_group->add_widget (*solo_isolated_led);
227         }
228         if (solo_safe_led) {
229                 button_size_group->add_widget (*solo_safe_led);
230         }
231
232         if (!ARDOUR::Profile->get_mixbus()) {
233                 if (rec_enable_button) {
234                         button_size_group->add_widget (*rec_enable_button);
235                 }
236                 if (monitor_disk_button) {
237                         button_size_group->add_widget (*monitor_disk_button);
238                 }
239                 if (monitor_input_button) {
240                         button_size_group->add_widget (*monitor_input_button);
241                 }
242         }
243
244         mute_solo_table.set_homogeneous (true);
245         mute_solo_table.set_spacings (2);
246
247         bottom_button_table.set_spacings (2);
248         bottom_button_table.set_homogeneous (true);
249         bottom_button_table.attach (group_button, 1, 2, 0, 1);
250         bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
251
252         name_button.set_name ("mixer strip button");
253         name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254         name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
255
256         set_tooltip (&group_button, _("Mix group"));
257         group_button.set_name ("mixer strip button");
258
259         _comment_button.set_name (X_("mixer strip button"));
260         _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
261         _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262         _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
263
264         // TODO implement ArdourKnob::on_size_request properly
265 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
266         trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
267 #undef PX_SCALE
268         trim_control.set_tooltip_prefix (_("Trim: "));
269         trim_control.set_name ("trim knob");
270         trim_control.set_no_show_all (true);
271         trim_control.StartGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_start_touch));
272         trim_control.StopGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_end_touch));
273         input_button_box.pack_start (trim_control, false, false);
274
275         global_vpacker.set_border_width (1);
276         global_vpacker.set_spacing (0);
277
278         width_button.set_name ("mixer strip button");
279         hide_button.set_name ("mixer strip button");
280
281         width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
282         hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
283
284         width_hide_box.set_spacing (2);
285         width_hide_box.pack_start (width_button, false, true);
286         width_hide_box.pack_start (number_label, true, true);
287         width_hide_box.pack_end (hide_button, false, true);
288
289         number_label.set_text ("-");
290         number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
291         number_label.set_no_show_all ();
292         number_label.set_name ("tracknumber label");
293         number_label.set_fixed_colors (0x80808080, 0x80808080);
294         number_label.set_alignment (.5, .5);
295         number_label.set_fallthrough_to_parent (true);
296         number_label.set_tweaks (ArdourButton::OccasionalText);
297
298         global_vpacker.set_spacing (2);
299         if (!ARDOUR::Profile->get_trx()) {
300                 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
301                 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
302                 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
303                 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
304                 global_vpacker.pack_start (processor_box, true, true);
305         }
306         global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
307         global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
308         global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
309         global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
310         global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
311         global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
312         global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
313         if (!ARDOUR::Profile->get_trx()) {
314                 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
315                 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
316         } else {
317                 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
318         }
319
320 #ifndef MIXBUS
321         //add a spacer underneath the master bus;
322         //this fills the area that is taken up by the scrollbar on the tracks;
323         //and therefore keeps the faders "even" across the bottom
324         int scrollbar_height = 0;
325         {
326                 Gtk::Window window (WINDOW_TOPLEVEL);
327                 HScrollbar scrollbar;
328                 window.add (scrollbar);
329                 scrollbar.set_name ("MixerWindow");
330                 scrollbar.ensure_style();
331                 Gtk::Requisition requisition(scrollbar.size_request ());
332                 scrollbar_height = requisition.height;
333         }
334         spacer.set_size_request (-1, scrollbar_height);
335         global_vpacker.pack_end (spacer, false, false);
336 #endif
337
338         global_frame.add (global_vpacker);
339         global_frame.set_shadow_type (Gtk::SHADOW_IN);
340         global_frame.set_name ("BaseFrame");
341
342         add (global_frame);
343
344         /* force setting of visible selected status */
345
346         _selected = true;
347         set_selected (false);
348
349         _packed = false;
350         _embedded = false;
351
352         _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
353         _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
354
355         input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
356         input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
357         input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
358
359         input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
360         output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
361
362         output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
363         output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
364         output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
365
366         number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
367
368         name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
369
370         group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
371
372         _width = (Width) -1;
373
374         /* start off as a passthru strip. we'll correct this, if necessary,
375            in update_diskstream_display().
376         */
377
378         /* start off as a passthru strip. we'll correct this, if necessary,
379            in update_diskstream_display().
380         */
381
382         if (is_midi_track()) {
383                 set_name ("MidiTrackStripBase");
384         } else {
385                 set_name ("AudioTrackStripBase");
386         }
387
388         add_events (Gdk::BUTTON_RELEASE_MASK|
389                     Gdk::ENTER_NOTIFY_MASK|
390                     Gdk::LEAVE_NOTIFY_MASK|
391                     Gdk::KEY_PRESS_MASK|
392                     Gdk::KEY_RELEASE_MASK);
393
394         set_flags (get_flags() | Gtk::CAN_FOCUS);
395
396         AudioEngine::instance()->PortConnectedOrDisconnected.connect (
397                 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
398                 );
399
400         /* Add the widgets under visibility control to the VisibilityGroup; the names used here
401            must be the same as those used in RCOptionEditor so that the configuration changes
402            are recognised when they occur.
403         */
404         _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
405         _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
406         _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
407         _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
408         _visibility.add (&output_button, X_("Output"), _("Output"), false);
409         _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
410         _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
411
412         parameter_changed (X_("mixer-element-visibility"));
413         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
414         Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
415         _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
416
417         //watch for mouse enter/exit so we can do some stuff
418         signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
419         signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
420
421         gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
422 }
423
424 MixerStrip::~MixerStrip ()
425 {
426         CatchDeletion (this);
427
428         if (this ==_entered_mixer_strip)
429                 _entered_mixer_strip = NULL;
430 }
431
432 void
433 MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
434 {
435         boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
436         if (sl)
437                 sl->assign(vca);
438 }
439
440 void
441 MixerStrip::vca_unassign (boost::shared_ptr<ARDOUR::VCA> vca)
442 {
443         boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
444         if (sl)
445                 sl->unassign(vca);
446 }
447
448 bool
449 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
450 {
451         _entered_mixer_strip = this;
452
453         //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
454         //because the mixerstrip control is a parent that encompasses the strip
455         deselect_all_processors();
456
457         return false;
458 }
459
460 bool
461 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
462 {
463         //if we have moved outside our strip, but not into a child view, then deselect ourselves
464         if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
465                 _entered_mixer_strip= 0;
466
467                 //clear keyboard focus in the gain display.  this is cheesy but fixes a longstanding "bug" where the user starts typing in the gain entry, and leaves it active, thereby prohibiting other keybindings from working
468                 gpm.gain_display.set_sensitive(false);
469                 gpm.show_gain();
470                 gpm.gain_display.set_sensitive(true);
471
472                 //if we leave this mixer strip we need to clear out any selections
473                 //processor_box.processor_display.select_none();  //but this doesn't work, because it gets triggered when (for example) you open the menu or start a drag
474         }
475
476         return false;
477 }
478
479 string
480 MixerStrip::name() const
481 {
482         if (_route) {
483                 return _route->name();
484         }
485         return string();
486 }
487
488 void
489 MixerStrip::update_trim_control ()
490 {
491         if (route()->trim() && route()->trim()->active() &&
492             route()->n_inputs().n_audio() > 0) {
493                 trim_control.show ();
494                 trim_control.set_controllable (route()->trim()->gain_control());
495         } else {
496                 trim_control.hide ();
497                 boost::shared_ptr<Controllable> none;
498                 trim_control.set_controllable (none);
499         }
500 }
501
502 void
503 MixerStrip::trim_start_touch ()
504 {
505         assert (_route && _session);
506         if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
507                 route()->trim()->gain_control ()->start_touch (_session->transport_frame());
508         }
509 }
510
511 void
512 MixerStrip::trim_end_touch ()
513 {
514         assert (_route && _session);
515         if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
516                 route()->trim()->gain_control ()->stop_touch (_session->transport_frame());
517         }
518 }
519
520 void
521 MixerStrip::set_route (boost::shared_ptr<Route> rt)
522 {
523         //the rec/monitor stuff only shows up for tracks.
524         //the show_sends only shows up for buses.
525         //remove them all here, and we may add them back later
526         if (show_sends_button->get_parent()) {
527                 rec_mon_table.remove (*show_sends_button);
528         }
529         if (rec_enable_button->get_parent()) {
530                 rec_mon_table.remove (*rec_enable_button);
531         }
532         if (monitor_input_button->get_parent()) {
533                 rec_mon_table.remove (*monitor_input_button);
534         }
535         if (monitor_disk_button->get_parent()) {
536                 rec_mon_table.remove (*monitor_disk_button);
537         }
538         if (group_button.get_parent()) {
539                 bottom_button_table.remove (group_button);
540         }
541
542         RouteUI::set_route (rt);
543
544         control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
545
546         /* ProcessorBox needs access to _route so that it can read
547            GUI object state.
548         */
549         processor_box.set_route (rt);
550
551         revert_to_default_display ();
552
553         /* unpack these from the parent and stuff them into our own
554            table
555         */
556
557         if (gpm.peak_display.get_parent()) {
558                 gpm.peak_display.get_parent()->remove (gpm.peak_display);
559         }
560         if (gpm.gain_display.get_parent()) {
561                 gpm.gain_display.get_parent()->remove (gpm.gain_display);
562         }
563
564         gpm.set_type (rt->meter_type());
565
566         mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
567         mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
568
569         if (solo_button->get_parent()) {
570                 mute_solo_table.remove (*solo_button);
571         }
572
573         if (mute_button->get_parent()) {
574                 mute_solo_table.remove (*mute_button);
575         }
576
577         if (route()->is_master()) {
578                 solo_button->hide ();
579                 mute_button->show ();
580                 rec_mon_table.hide ();
581                 solo_iso_table.set_sensitive(false);
582                 control_slave_ui.set_sensitive(false);
583                 if (monitor_section_button == 0) {
584                         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
585                         _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
586
587                         monitor_section_button = manage (new ArdourButton);
588                         monitor_changed ();
589                         monitor_section_button->set_related_action (act);
590                         set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
591                         mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
592                         monitor_section_button->show();
593                         monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
594                 }
595                 parameter_changed ("use-monitor-bus");
596         } else {
597                 bottom_button_table.attach (group_button, 1, 2, 0, 1);
598                 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
599                 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
600                 mute_button->show ();
601                 solo_button->show ();
602                 rec_mon_table.show ();
603                 solo_iso_table.set_sensitive(true);
604                 control_slave_ui.set_sensitive(true);
605         }
606
607         if (_mixer_owned && route()->is_master() ) {
608                 spacer.show();
609         } else {
610                 spacer.hide();
611         }
612
613         if (is_track()) {
614                 monitor_input_button->show ();
615                 monitor_disk_button->show ();
616         } else {
617                 monitor_input_button->hide();
618                 monitor_disk_button->hide ();
619         }
620
621         update_trim_control();
622
623         if (is_midi_track()) {
624                 if (midi_input_enable_button == 0) {
625                         midi_input_enable_button = manage (new ArdourButton);
626                         midi_input_enable_button->set_name ("midi input button");
627                         midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
628                         midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
629                         midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
630                         midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
631                         set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
632                 } else {
633                         input_button_box.remove (*midi_input_enable_button);
634                 }
635                 /* get current state */
636                 midi_input_status_changed ();
637                 input_button_box.pack_start (*midi_input_enable_button, false, false);
638                 /* follow changes */
639                 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
640         } else {
641                 if (midi_input_enable_button) {
642                         /* removal from the container will delete it */
643                         input_button_box.remove (*midi_input_enable_button);
644                         midi_input_enable_button = 0;
645                 }
646         }
647
648         if (is_audio_track()) {
649                 boost::shared_ptr<AudioTrack> at = audio_track();
650                 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
651         }
652
653         if (is_track ()) {
654
655                 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
656                 rec_enable_button->show();
657
658                 if (ARDOUR::Profile->get_mixbus()) {
659                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
660                         rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
661                 } else if (ARDOUR::Profile->get_trx()) {
662                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
663                 } else {
664                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
665                         rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
666                 }
667
668         } else {
669
670                 /* non-master bus */
671
672                 if (!_route->is_master()) {
673                         rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
674                         show_sends_button->show();
675                 }
676         }
677
678         gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
679
680         delete route_ops_menu;
681         route_ops_menu = 0;
682
683         _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
684         _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
685         _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
686         _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
687
688         _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
689
690         if (_route->panner_shell()) {
691                 update_panner_choices();
692                 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
693         }
694
695         if (is_audio_track()) {
696                 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
697         }
698
699         _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
700
701         set_stuff_from_route ();
702
703         /* now force an update of all the various elements */
704
705         update_mute_display ();
706         update_solo_display ();
707         name_changed ();
708         comment_changed ();
709         route_group_changed ();
710         update_track_number_visibility ();
711
712         connect_to_pan ();
713         panners.setup_pan ();
714
715         if (has_audio_outputs ()) {
716                 panners.show_all ();
717         } else {
718                 panners.hide_all ();
719         }
720
721         update_diskstream_display ();
722         update_input_display ();
723         update_output_display ();
724
725         add_events (Gdk::BUTTON_RELEASE_MASK);
726
727         processor_box.show ();
728
729         if (!route()->is_master() && !route()->is_monitor()) {
730                 /* we don't allow master or control routes to be hidden */
731                 hide_button.show();
732                 number_label.show();
733         }
734
735         gpm.reset_peak_display ();
736         gpm.gain_display.show ();
737         gpm.peak_display.show ();
738
739         width_button.show();
740         width_hide_box.show();
741         global_frame.show();
742         global_vpacker.show();
743         mute_solo_table.show();
744         bottom_button_table.show();
745         gpm.show_all ();
746         gpm.meter_point_button.show();
747         input_button_box.show_all();
748         output_button.show();
749         name_button.show();
750         _comment_button.show();
751         group_button.show();
752         gpm.gain_automation_state_button.show();
753
754         parameter_changed ("mixer-element-visibility");
755         map_frozen();
756
757         show ();
758 }
759
760 void
761 MixerStrip::set_stuff_from_route ()
762 {
763         /* if width is not set, it will be set by the MixerUI or editor */
764
765         Width width;
766         if (get_gui_property ("strip-width", width)) {
767                 set_width_enum (width, this);
768         }
769 }
770
771 void
772 MixerStrip::set_width_enum (Width w, void* owner)
773 {
774         /* always set the gpm width again, things may be hidden */
775
776         gpm.set_width (w);
777         panners.set_width (w);
778
779         boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
780
781         _width_owner = owner;
782
783         _width = w;
784
785         if (_width_owner == this) {
786                 set_gui_property ("strip-width", _width);
787         }
788
789         set_button_names ();
790
791         const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
792
793         switch (w) {
794         case Wide:
795
796                 if (show_sends_button)  {
797                         show_sends_button->set_text (_("Aux"));
798                 }
799
800                 gpm.gain_automation_state_button.set_text (
801                                 gpm.astate_string(gain_automation->automation_state()));
802
803                 if (_route->panner()) {
804                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
805                                         panners.astate_string(_route->panner()->automation_state()));
806                 }
807
808                 {
809                         // panners expect an even number of horiz. pixels
810                         int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
811                         width &= ~1;
812                         set_size_request (width, -1);
813                 }
814                 break;
815
816         case Narrow:
817
818                 if (show_sends_button) {
819                         show_sends_button->set_text (_("Snd"));
820                 }
821
822                 gpm.gain_automation_state_button.set_text (
823                                 gpm.short_astate_string(gain_automation->automation_state()));
824                 gain_meter().setup_meters (); // recalc meter width
825
826                 if (_route->panner()) {
827                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
828                         panners.short_astate_string(_route->panner()->automation_state()));
829                 }
830
831                 {
832                         // panners expect an even number of horiz. pixels
833                         int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
834                         width &= ~1;
835                         set_size_request (width, -1);
836                 }
837                 break;
838         }
839
840         processor_box.set_width (w);
841
842         update_input_display ();
843         update_output_display ();
844         setup_comment_button ();
845         route_group_changed ();
846         name_changed ();
847         WidthChanged ();
848 }
849
850 void
851 MixerStrip::set_packed (bool yn)
852 {
853         _packed = yn;
854         set_gui_property ("visible", _packed);
855 }
856
857
858 struct RouteCompareByName {
859         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
860                 return a->name().compare (b->name()) < 0;
861         }
862 };
863
864 gint
865 MixerStrip::output_release (GdkEventButton *ev)
866 {
867         switch (ev->button) {
868         case 3:
869                 edit_output_configuration ();
870                 break;
871         }
872
873         return false;
874 }
875
876 gint
877 MixerStrip::output_press (GdkEventButton *ev)
878 {
879         using namespace Menu_Helpers;
880         if (!_session->engine().connected()) {
881                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
882                 msg.run ();
883                 return true;
884         }
885
886         MenuList& citems = output_menu.items();
887         switch (ev->button) {
888
889         case 3:
890                 return false;  //wait for the mouse-up to pop the dialog
891
892         case 1:
893         {
894                 output_menu.set_name ("ArdourContextMenu");
895                 citems.clear ();
896                 output_menu_bundles.clear ();
897
898                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
899
900                 citems.push_back (SeparatorElem());
901                 uint32_t const n_with_separator = citems.size ();
902
903                 ARDOUR::BundleList current = _route->output()->bundles_connected ();
904
905                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
906
907                 /* guess the user-intended main type of the route output */
908                 DataType intended_type = guess_main_type(false);
909
910                 /* try adding the master bus first */
911                 boost::shared_ptr<Route> master = _session->master_out();
912                 if (master) {
913                         maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
914                 }
915
916                 /* then other routes inputs */
917                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
918                 RouteList copy = *routes;
919                 copy.sort (RouteCompareByName ());
920                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
921                         maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
922                 }
923
924                 /* then try adding user bundles, often labeled/grouped physical inputs */
925                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
926                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
927                                 maybe_add_bundle_to_output_menu (*i, current, intended_type);
928                         }
929                 }
930
931                 /* then all other bundles, including physical outs or other sofware */
932                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
933                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
934                                 maybe_add_bundle_to_output_menu (*i, current, intended_type);
935                         }
936                 }
937
938                 if (citems.size() == n_with_separator) {
939                         /* no routes added; remove the separator */
940                         citems.pop_back ();
941                 }
942
943                 if (!ARDOUR::Profile->get_mixbus()) {
944                         citems.push_back (SeparatorElem());
945
946                         for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
947                                 citems.push_back (
948                                                 MenuElem (
949                                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
950                                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
951                                                         )
952                                                 );
953                         }
954                 }
955
956                 citems.push_back (SeparatorElem());
957                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
958
959                 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
960                                                1, ev->time);
961
962                 break;
963         }
964
965         default:
966                 break;
967         }
968         return TRUE;
969 }
970
971 gint
972 MixerStrip::input_release (GdkEventButton *ev)
973 {
974         switch (ev->button) {
975
976         case 3:
977                 edit_input_configuration ();
978                 break;
979         default:
980                 break;
981
982         }
983
984         return false;
985 }
986
987
988 gint
989 MixerStrip::input_press (GdkEventButton *ev)
990 {
991         using namespace Menu_Helpers;
992
993         MenuList& citems = input_menu.items();
994         input_menu.set_name ("ArdourContextMenu");
995         citems.clear();
996
997         if (!_session->engine().connected()) {
998                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
999                 msg.run ();
1000                 return true;
1001         }
1002
1003         if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
1004                 return true;
1005
1006         switch (ev->button) {
1007
1008         case 3:
1009                 return false;  //don't handle the mouse-down here.  wait for mouse-up to pop the menu
1010
1011         case 1:
1012         {
1013                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
1014
1015                 citems.push_back (SeparatorElem());
1016                 uint32_t const n_with_separator = citems.size ();
1017
1018                 input_menu_bundles.clear ();
1019
1020                 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1021
1022                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1023
1024                 /* give user bundles first chance at being in the menu */
1025
1026                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1027                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1028                                 maybe_add_bundle_to_input_menu (*i, current);
1029                         }
1030                 }
1031
1032                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1033                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1034                                 maybe_add_bundle_to_input_menu (*i, current);
1035                         }
1036                 }
1037
1038                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1039                 RouteList copy = *routes;
1040                 copy.sort (RouteCompareByName ());
1041                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1042                         maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1043                 }
1044
1045                 if (citems.size() == n_with_separator) {
1046                         /* no routes added; remove the separator */
1047                         citems.pop_back ();
1048                 }
1049
1050                 citems.push_back (SeparatorElem());
1051                 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1052                         citems.push_back (
1053                                 MenuElem (
1054                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1055                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1056                                         )
1057                                 );
1058                 }
1059
1060                 citems.push_back (SeparatorElem());
1061                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1062
1063                 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1064                                                1, ev->time);
1065
1066                 break;
1067         }
1068         default:
1069                 break;
1070         }
1071         return TRUE;
1072 }
1073
1074 void
1075 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1076 {
1077         if (ignore_toggle) {
1078                 return;
1079         }
1080
1081         ARDOUR::BundleList current = _route->input()->bundles_connected ();
1082
1083         if (std::find (current.begin(), current.end(), c) == current.end()) {
1084                 _route->input()->connect_ports_to_bundle (c, true, this);
1085         } else {
1086                 _route->input()->disconnect_ports_from_bundle (c, this);
1087         }
1088 }
1089
1090 void
1091 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1092 {
1093         if (ignore_toggle) {
1094                 return;
1095         }
1096
1097         ARDOUR::BundleList current = _route->output()->bundles_connected ();
1098
1099         if (std::find (current.begin(), current.end(), c) == current.end()) {
1100                 _route->output()->connect_ports_to_bundle (c, true, this);
1101         } else {
1102                 _route->output()->disconnect_ports_from_bundle (c, this);
1103         }
1104 }
1105
1106 void
1107 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1108 {
1109         using namespace Menu_Helpers;
1110
1111         if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1112                 return;
1113         }
1114
1115         list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1116         while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1117                 ++i;
1118         }
1119
1120         if (i != input_menu_bundles.end()) {
1121                 return;
1122         }
1123
1124         input_menu_bundles.push_back (b);
1125
1126         MenuList& citems = input_menu.items();
1127         citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1128 }
1129
1130 void
1131 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/,
1132                                              DataType type)
1133 {
1134         using namespace Menu_Helpers;
1135
1136         /* The bundle should be an input one, but not ours */
1137         if (b->ports_are_inputs() == false || *b == *_route->input()->bundle()) {
1138                 return;
1139         }
1140
1141         /* Don't add the monitor input unless we are Master */
1142         boost::shared_ptr<Route> monitor = _session->monitor_out();
1143         if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
1144                 return;
1145
1146         /* It should either match exactly our outputs (if |type| is DataType::NIL)
1147          * or have the same number of |type| channels than our outputs. */
1148         if (type == DataType::NIL) {
1149                 if(b->nchannels() != _route->n_outputs())
1150                         return;
1151         } else {
1152                 if (b->nchannels().n(type) != _route->n_outputs().n(type))
1153                         return;
1154         }
1155
1156         /* Avoid adding duplicates */
1157         list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1158         while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1159                 ++i;
1160         }
1161         if (i != output_menu_bundles.end()) {
1162                 return;
1163         }
1164
1165         /* Now add the bundle to the menu */
1166         output_menu_bundles.push_back (b);
1167
1168         MenuList& citems = output_menu.items();
1169         citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1170 }
1171
1172 void
1173 MixerStrip::update_diskstream_display ()
1174 {
1175         if (is_track() && input_selector) {
1176                 input_selector->hide_all ();
1177         }
1178
1179         route_color_changed ();
1180 }
1181
1182 void
1183 MixerStrip::connect_to_pan ()
1184 {
1185         ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1186
1187         panstate_connection.disconnect ();
1188         panstyle_connection.disconnect ();
1189
1190         if (!_route->panner()) {
1191                 return;
1192         }
1193
1194         boost::shared_ptr<Pannable> p = _route->pannable ();
1195
1196         p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1197
1198         /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1199          * However, that only works a panner was previously set.
1200          *
1201          * PannerUI must remain subscribed to _panshell->Changed() in case
1202          * we switch the panner eg. AUX-Send and back
1203          * _route->panner_shell()->Changed() vs _panshell->Changed
1204          */
1205         if (panners._panner == 0) {
1206                 panners.panshell_changed ();
1207         }
1208         update_panner_choices();
1209 }
1210
1211 void
1212 MixerStrip::update_panner_choices ()
1213 {
1214         ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1215         if (!_route->panner_shell()) { return; }
1216
1217         uint32_t in = _route->output()->n_ports().n_audio();
1218         uint32_t out = in;
1219         if (_route->panner()) {
1220                 in = _route->panner()->in().n_audio();
1221         }
1222
1223         panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1224 }
1225
1226 DataType
1227 MixerStrip::guess_main_type(bool for_input, bool favor_connected) const
1228 {
1229         /* The heuristic follows these principles:
1230          *  A) If all ports that the user connected are of the same type, then he
1231          *     very probably intends to use the IO with that type. A common subcase
1232          *     is when the IO has only ports of the same type (connected or not).
1233          *  B) If several types of ports are connected, then we should guess based
1234          *     on the likeliness of the user wanting to use a given type.
1235          *     We assume that the DataTypes are ordered from the most likely to the
1236          *     least likely when iterating or comparing them with "<".
1237          *  C) If no port is connected, the same logic can be applied with all ports
1238          *     instead of connected ones. TODO: Try other ideas, for instance look at
1239          *     the last plugin output when |for_input| is false (note: when StrictIO
1240          *     the outs of the last plugin should be the same as the outs of the route
1241          *     modulo the panner which forwards non-audio anyway).
1242          * All of these constraints are respected by the following algorithm that
1243          * just returns the most likely datatype found in connected ports if any, or
1244          * available ports if any (since if all ports are of the same type, the most
1245          * likely found will be that one obviously). */
1246
1247         boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1248
1249         /* Find most likely type among connected ports */
1250         if (favor_connected) {
1251                 DataType type = DataType::NIL; /* NIL is always last so least likely */
1252                 for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
1253                         if (p->connected() && p->type() < type)
1254                                 type = p->type();
1255                 }
1256                 if (type != DataType::NIL) {
1257                         /* There has been a connected port (necessarily non-NIL) */
1258                         return type;
1259                 }
1260         }
1261
1262         /* Find most likely type among available ports.
1263          * The iterator stops before NIL. */
1264         for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1265                 if (io->n_ports().n(*t) > 0)
1266                         return *t;
1267         }
1268
1269         /* No port at all, return the most likely datatype by default */
1270         return DataType::front();
1271 }
1272
1273 /*
1274  * Output port labelling
1275  * =====================
1276  *
1277  * Case 1: Each output has one connection, all connections are to system:playback_%i
1278  *   out 1 -> system:playback_1
1279  *   out 2 -> system:playback_2
1280  *   out 3 -> system:playback_3
1281  *   Display as: 1/2/3
1282  *
1283  * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1284  *   out 1 -> ardour:track_x/in 1
1285  *   out 2 -> ardour:track_x/in 2
1286  *   Display as: track_x
1287  *
1288  * Case 3: Each output has one connection, all connections are to Jack client "program x"
1289  *   out 1 -> program x:foo
1290  *   out 2 -> program x:foo
1291  *   Display as: program x
1292  *
1293  * Case 4: No connections (Disconnected)
1294  *   Display as: -
1295  *
1296  * Default case (unusual routing):
1297  *   Display as: *number of connections*
1298  *
1299  * Tooltips
1300  * ========
1301  * .-----------------------------------------------.
1302  * | Mixdown                                       |
1303  * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1304  * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1305  * '-----------------------------------------------'
1306  * .-----------------------------------------------.
1307  * | Guitar SM58                                   |
1308  * | Disconnected                                  |
1309  * '-----------------------------------------------'
1310  */
1311
1312 void
1313 MixerStrip::update_io_button (bool for_input)
1314 {
1315         vector<string> port_connections;
1316
1317         uint32_t total_connection_count = 0;
1318         uint32_t io_connection_count = 0;
1319         uint32_t ardour_connection_count = 0;
1320         uint32_t system_connection_count = 0;
1321         uint32_t other_connection_count = 0;
1322         uint32_t typed_connection_count = 0;
1323
1324         ostringstream label;
1325
1326         bool have_label = false;
1327         bool each_io_has_one_connection = true;
1328
1329         string connection_name;
1330         string ardour_track_name;
1331         string other_connection_type;
1332         string system_ports;
1333         string system_port;
1334
1335         ostringstream tooltip;
1336         char * tooltip_cstr;
1337
1338         DataType dt = guess_main_type(for_input);
1339
1340         if ( dt == DataType::MIDI ) {
1341                 tooltip << _("MIDI ");
1342         }
1343
1344         if (for_input) {
1345                 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1346         } else {
1347                 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1348         }
1349
1350         boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1351         for (PortSet::iterator port = io->ports().begin(); port != io->ports().end(); ++port) {
1352                 port_connections.clear ();
1353                 port->get_connections(port_connections);
1354
1355                 //ignore any port connections that don't match our DataType
1356                 if (port->type() != dt) {
1357                         if (!port_connections.empty()) {
1358                                 ++typed_connection_count;
1359                         }
1360                         continue;
1361                 }
1362
1363                 io_connection_count = 0;
1364
1365                 if (!port_connections.empty()) {
1366                         for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1367                                 string pn = "";
1368                                 string& connection_name (*i);
1369
1370                                 if (connection_name.find("system:") == 0) {
1371                                         pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1372                                 }
1373
1374                                 if (io_connection_count == 0) {
1375                                         tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1376                                                 << " -> "
1377                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1378                                 } else {
1379                                         tooltip << ", "
1380                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1381                                 }
1382
1383                                 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1384                                         if (ardour_track_name.empty()) {
1385                                                 // "ardour:Master/in 1" -> "ardour:Master/"
1386                                                 string::size_type slash = connection_name.find("/");
1387                                                 if (slash != string::npos) {
1388                                                         ardour_track_name = connection_name.substr(0, slash + 1);
1389                                                 }
1390                                         }
1391
1392                                         if (connection_name.find(ardour_track_name) == 0) {
1393                                                 ++ardour_connection_count;
1394                                         }
1395                                 } else if (!pn.empty()) {
1396                                         if (system_ports.empty()) {
1397                                                 system_ports += pn;
1398                                         } else {
1399                                                 system_ports += "/" + pn;
1400                                         }
1401                                         if (connection_name.find("system:") == 0) {
1402                                                 ++system_connection_count;
1403                                         }
1404                                 } else if (connection_name.find("system:midi_") == 0) {
1405                                         if (for_input) {
1406                                                 // "system:midi_capture_123" -> "123"
1407                                                 system_port = "M " + connection_name.substr(20);
1408                                         } else {
1409                                                 // "system:midi_playback_123" -> "123"
1410                                                 system_port = "M " + connection_name.substr(21);
1411                                         }
1412
1413                                         if (system_ports.empty()) {
1414                                                 system_ports += system_port;
1415                                         } else {
1416                                                 system_ports += "/" + system_port;
1417                                         }
1418
1419                                         ++system_connection_count;
1420
1421                                 } else if (connection_name.find("system:") == 0) {
1422                                         if (for_input) {
1423                                                 // "system:capture_123" -> "123"
1424                                                 system_port = connection_name.substr(15);
1425                                         } else {
1426                                                 // "system:playback_123" -> "123"
1427                                                 system_port = connection_name.substr(16);
1428                                         }
1429
1430                                         if (system_ports.empty()) {
1431                                                 system_ports += system_port;
1432                                         } else {
1433                                                 system_ports += "/" + system_port;
1434                                         }
1435
1436                                         ++system_connection_count;
1437                                 } else {
1438                                         if (other_connection_type.empty()) {
1439                                                 // "jamin:in 1" -> "jamin:"
1440                                                 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1441                                         }
1442
1443                                         if (connection_name.find(other_connection_type) == 0) {
1444                                                 ++other_connection_count;
1445                                         }
1446                                 }
1447
1448                                 ++total_connection_count;
1449                                 ++io_connection_count;
1450                         }
1451                 }
1452
1453                 if (io_connection_count != 1) {
1454                         each_io_has_one_connection = false;
1455                 }
1456         }
1457
1458         if (total_connection_count == 0) {
1459                 tooltip << endl << _("Disconnected");
1460         }
1461
1462         tooltip_cstr = new char[tooltip.str().size() + 1];
1463         strcpy(tooltip_cstr, tooltip.str().c_str());
1464
1465         if (for_input) {
1466                 set_tooltip (&input_button, tooltip_cstr);
1467         } else {
1468                 set_tooltip (&output_button, tooltip_cstr);
1469         }
1470
1471         delete [] tooltip_cstr;
1472
1473         if (each_io_has_one_connection) {
1474                 if (total_connection_count == ardour_connection_count) {
1475                         // all connections are to the same track in ardour
1476                         // "ardour:Master/" -> "Master"
1477                         string::size_type slash = ardour_track_name.find("/");
1478                         if (slash != string::npos) {
1479                                 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1480                                 label << ardour_track_name.substr (ppps, slash - ppps);
1481                                 have_label = true;
1482                         }
1483                 }
1484                 else if (total_connection_count == system_connection_count) {
1485                         // all connections are to system ports
1486                         label << system_ports;
1487                         have_label = true;
1488                 }
1489                 else if (total_connection_count == other_connection_count) {
1490                         // all connections are to the same external program eg jamin
1491                         // "jamin:" -> "jamin"
1492                         label << other_connection_type.substr(0, other_connection_type.size() - 1);
1493                         have_label = true;
1494                 }
1495         }
1496
1497         if (!have_label) {
1498                 if (total_connection_count == 0) {
1499                         // Disconnected
1500                         label << "-";
1501                 } else {
1502                         // Odd configuration
1503                         label << "*" << total_connection_count << "*";
1504                 }
1505                 if (typed_connection_count > 0) {
1506                         label << "\u2295"; // circled plus
1507                 }
1508         }
1509
1510         if (for_input) {
1511                 input_button.set_text (label.str());
1512         } else {
1513                 output_button.set_text (label.str());
1514         }
1515 }
1516
1517 void
1518 MixerStrip::update_input_display ()
1519 {
1520         update_io_button (true);
1521         panners.setup_pan ();
1522
1523         if (has_audio_outputs ()) {
1524                 panners.show_all ();
1525         } else {
1526                 panners.hide_all ();
1527         }
1528
1529 }
1530
1531 void
1532 MixerStrip::update_output_display ()
1533 {
1534         update_io_button (false);
1535         gpm.setup_meters ();
1536         panners.setup_pan ();
1537
1538         if (has_audio_outputs ()) {
1539                 panners.show_all ();
1540         } else {
1541                 panners.hide_all ();
1542         }
1543 }
1544
1545 void
1546 MixerStrip::fast_update ()
1547 {
1548         gpm.update_meters ();
1549 }
1550
1551 void
1552 MixerStrip::diskstream_changed ()
1553 {
1554         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1555 }
1556
1557 void
1558 MixerStrip::io_changed_proxy ()
1559 {
1560         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1561         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1562 }
1563
1564 void
1565 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1566 {
1567         boost::shared_ptr<Port> a = wa.lock ();
1568         boost::shared_ptr<Port> b = wb.lock ();
1569
1570         if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1571                 update_input_display ();
1572                 set_width_enum (_width, this);
1573         }
1574
1575         if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1576                 update_output_display ();
1577                 set_width_enum (_width, this);
1578         }
1579 }
1580
1581 void
1582 MixerStrip::setup_comment_button ()
1583 {
1584         std::string comment = _route->comment();
1585
1586         set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1587
1588         if (comment.empty ()) {
1589                 _comment_button.set_name ("generic button");
1590                 _comment_button.set_text (_width  == Wide ? _("Comments") : _("Cmt"));
1591                 return;
1592         }
1593
1594         _comment_button.set_name ("comment button");
1595
1596         string::size_type pos = comment.find_first_of (" \t\n");
1597         if (pos != string::npos) {
1598                 comment = comment.substr (0, pos);
1599         }
1600         if (comment.empty()) {
1601                 _comment_button.set_text (_width  == Wide ? _("Comments") : _("Cmt"));
1602         } else {
1603                 _comment_button.set_text (comment);
1604         }
1605 }
1606
1607 bool
1608 MixerStrip::select_route_group (GdkEventButton *ev)
1609 {
1610         using namespace Menu_Helpers;
1611
1612         if (ev->button == 1) {
1613
1614                 if (group_menu == 0) {
1615
1616                         PropertyList* plist = new PropertyList();
1617
1618                         plist->add (Properties::group_gain, true);
1619                         plist->add (Properties::group_mute, true);
1620                         plist->add (Properties::group_solo, true);
1621
1622                         group_menu = new RouteGroupMenu (_session, plist);
1623                 }
1624
1625                 WeakRouteList r;
1626                 r.push_back (route ());
1627                 group_menu->build (r);
1628
1629                 RouteGroup *rg = _route->route_group();
1630
1631                 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1632                                                rg ? rg->name() : _("No Group"),
1633                                                1, ev->time);
1634         }
1635
1636         return true;
1637 }
1638
1639 void
1640 MixerStrip::route_group_changed ()
1641 {
1642         ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1643
1644         RouteGroup *rg = _route->route_group();
1645
1646         if (rg) {
1647                 group_button.set_text (PBD::short_version (rg->name(), 5));
1648         } else {
1649                 switch (_width) {
1650                 case Wide:
1651                         group_button.set_text (_("Grp"));
1652                         break;
1653                 case Narrow:
1654                         group_button.set_text (_("~G"));
1655                         break;
1656                 }
1657         }
1658 }
1659
1660 void
1661 MixerStrip::route_color_changed ()
1662 {
1663         using namespace ARDOUR_UI_UTILS;
1664         name_button.modify_bg (STATE_NORMAL, color());
1665         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1666         reset_strip_style ();
1667 }
1668
1669 void
1670 MixerStrip::show_passthru_color ()
1671 {
1672         reset_strip_style ();
1673 }
1674
1675
1676 void
1677 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1678 {
1679         boost::shared_ptr<Processor> processor (p.lock ());
1680         if (!processor || !processor->display_to_user()) {
1681                 return;
1682         }
1683         boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1684 #ifdef MIXBUS
1685         if (pi && pi->is_channelstrip ()) {
1686                 return;
1687         }
1688 #endif
1689         if (pi) {
1690                 ++_plugin_insert_cnt;
1691         }
1692 }
1693 void
1694 MixerStrip::build_route_ops_menu ()
1695 {
1696         using namespace Menu_Helpers;
1697         route_ops_menu = new Menu;
1698         route_ops_menu->set_name ("ArdourContextMenu");
1699
1700         MenuList& items = route_ops_menu->items();
1701
1702         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1703
1704         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1705
1706         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1707
1708         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1709
1710         if (!Profile->get_mixbus()) {
1711                 items.push_back (SeparatorElem());
1712         }
1713
1714         if (!_route->is_master()
1715 #ifdef MIXBUS
1716                         && !_route->mixbus()
1717 #endif
1718                         ) {
1719                 if (Profile->get_mixbus()) {
1720                         items.push_back (SeparatorElem());
1721                 }
1722                 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1723         }
1724
1725         if (!Profile->get_mixbus()) {
1726                 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1727                 /* do not allow rename if the track is record-enabled */
1728                 items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1729         }
1730
1731         items.push_back (SeparatorElem());
1732         items.push_back (CheckMenuElem (_("Active")));
1733         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1734         i->set_active (_route->active());
1735         i->set_sensitive(! _session->transport_rolling());
1736         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1737
1738         if (!Profile->get_mixbus ()) {
1739                 items.push_back (SeparatorElem());
1740                 items.push_back (CheckMenuElem (_("Strict I/O")));
1741                 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1742                 i->set_active (_route->strict_io());
1743                 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1744         }
1745
1746         _plugin_insert_cnt = 0;
1747         _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1748         if (_plugin_insert_cnt > 0) {
1749                 items.push_back (SeparatorElem());
1750                 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1751         }
1752
1753         if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1754                 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1755                 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1756                 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1757         }
1758
1759         items.push_back (SeparatorElem());
1760         items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1761
1762         items.push_back (SeparatorElem());
1763         items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1764         denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1765         denormal_menu_item->set_active (_route->denormal_protection());
1766
1767         if (_route) {
1768                 /* note that this relies on selection being shared across editor and
1769                    mixer (or global to the backend, in the future), which is the only
1770                    sane thing for users anyway.
1771                 */
1772
1773                 StripableTimeAxisView* stav = PublicEditor::instance().get_stripable_time_axis_by_id (_route->id());
1774                 if (stav) {
1775                         Selection& selection (PublicEditor::instance().get_selection());
1776                         if (!selection.selected (stav)) {
1777                                 selection.set (stav);
1778                         }
1779
1780                         if (!_route->is_master()) {
1781                                 items.push_back (SeparatorElem());
1782                                 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1783                         }
1784
1785                         items.push_back (SeparatorElem());
1786                         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1787                 }
1788         }
1789 }
1790
1791 gboolean
1792 MixerStrip::name_button_button_press (GdkEventButton* ev)
1793 {
1794         if (ev->button == 1 || ev->button == 3) {
1795                 list_route_operations ();
1796
1797                 if (ev->button == 1) {
1798                         Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1799                                                        1, ev->time);
1800                 } else {
1801                         route_ops_menu->popup (3, ev->time);
1802                 }
1803
1804                 return true;
1805         }
1806
1807         return false;
1808 }
1809
1810 gboolean
1811 MixerStrip::number_button_button_press (GdkEventButton* ev)
1812 {
1813         if (  ev->button == 3 ) {
1814                 list_route_operations ();
1815
1816                 route_ops_menu->popup (1, ev->time);
1817
1818                 return true;
1819         }
1820
1821         return false;
1822 }
1823
1824 void
1825 MixerStrip::list_route_operations ()
1826 {
1827         delete route_ops_menu;
1828         build_route_ops_menu ();
1829 }
1830
1831 void
1832 MixerStrip::set_selected (bool yn)
1833 {
1834         AxisView::set_selected (yn);
1835
1836         if (selected()) {
1837                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1838                 global_frame.set_name ("MixerStripSelectedFrame");
1839         } else {
1840                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1841                 global_frame.set_name ("MixerStripFrame");
1842         }
1843
1844         global_frame.queue_draw ();
1845
1846 //      if (!yn)
1847 //              processor_box.deselect_all_processors();
1848 }
1849
1850 void
1851 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1852 {
1853         if (what_changed.contains (ARDOUR::Properties::name)) {
1854                 name_changed ();
1855         }
1856 }
1857
1858 void
1859 MixerStrip::name_changed ()
1860 {
1861         switch (_width) {
1862                 case Wide:
1863                         name_button.set_text (_route->name());
1864                         break;
1865                 case Narrow:
1866                         name_button.set_text (PBD::short_version (_route->name(), 5));
1867                         break;
1868         }
1869
1870         set_tooltip (name_button, _route->name());
1871
1872         if (_session->config.get_track_name_number()) {
1873                 const int64_t track_number = _route->track_number ();
1874                 if (track_number == 0) {
1875                         number_label.set_text ("-");
1876                 } else {
1877                         number_label.set_text (PBD::to_string (abs(_route->track_number ())));
1878                 }
1879         } else {
1880                 number_label.set_text ("");
1881         }
1882 }
1883
1884 void
1885 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1886 {
1887         input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1888 }
1889
1890 void
1891 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1892 {
1893         output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1894 }
1895
1896 void
1897 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1898 {
1899         name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1900 }
1901
1902 void
1903 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1904 {
1905         _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1906 }
1907
1908 bool
1909 MixerStrip::width_button_pressed (GdkEventButton* ev)
1910 {
1911         if (ev->button != 1) {
1912                 return false;
1913         }
1914
1915         if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1916                 switch (_width) {
1917                 case Wide:
1918                         _mixer.set_strip_width (Narrow, true);
1919                         break;
1920
1921                 case Narrow:
1922                         _mixer.set_strip_width (Wide, true);
1923                         break;
1924                 }
1925         } else {
1926                 switch (_width) {
1927                 case Wide:
1928                         set_width_enum (Narrow, this);
1929                         break;
1930                 case Narrow:
1931                         set_width_enum (Wide, this);
1932                         break;
1933                 }
1934         }
1935
1936         return true;
1937 }
1938
1939 void
1940 MixerStrip::hide_clicked ()
1941 {
1942         // LAME fix to reset the button status for when it is redisplayed (part 1)
1943         hide_button.set_sensitive(false);
1944
1945         if (_embedded) {
1946                 Hiding(); /* EMIT_SIGNAL */
1947         } else {
1948                 _mixer.hide_strip (this);
1949         }
1950
1951         // (part 2)
1952         hide_button.set_sensitive(true);
1953 }
1954
1955 void
1956 MixerStrip::set_embedded (bool yn)
1957 {
1958         _embedded = yn;
1959 }
1960
1961 void
1962 MixerStrip::map_frozen ()
1963 {
1964         ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1965
1966         boost::shared_ptr<AudioTrack> at = audio_track();
1967
1968         if (at) {
1969                 switch (at->freeze_state()) {
1970                 case AudioTrack::Frozen:
1971                         processor_box.set_sensitive (false);
1972                         hide_redirect_editors ();
1973                         break;
1974                 default:
1975                         processor_box.set_sensitive (true);
1976                         // XXX need some way, maybe, to retoggle redirect editors
1977                         break;
1978                 }
1979         } else {
1980                 processor_box.set_sensitive (true);
1981         }
1982         RouteUI::map_frozen ();
1983 }
1984
1985 void
1986 MixerStrip::hide_redirect_editors ()
1987 {
1988         _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1989 }
1990
1991 void
1992 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1993 {
1994         boost::shared_ptr<Processor> processor (p.lock ());
1995         if (!processor) {
1996                 return;
1997         }
1998
1999         Gtk::Window* w = processor_box.get_processor_ui (processor);
2000
2001         if (w) {
2002                 w->hide ();
2003         }
2004 }
2005
2006 void
2007 MixerStrip::reset_strip_style ()
2008 {
2009         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2010
2011                 gpm.set_fader_name ("SendStripBase");
2012
2013         } else {
2014
2015                 if (is_midi_track()) {
2016                         if (_route->active()) {
2017                                 set_name ("MidiTrackStripBase");
2018                         } else {
2019                                 set_name ("MidiTrackStripBaseInactive");
2020                         }
2021                         gpm.set_fader_name ("MidiTrackFader");
2022                 } else if (is_audio_track()) {
2023                         if (_route->active()) {
2024                                 set_name ("AudioTrackStripBase");
2025                         } else {
2026                                 set_name ("AudioTrackStripBaseInactive");
2027                         }
2028                         gpm.set_fader_name ("AudioTrackFader");
2029                 } else {
2030                         if (_route->active()) {
2031                                 set_name ("AudioBusStripBase");
2032                         } else {
2033                                 set_name ("AudioBusStripBaseInactive");
2034                         }
2035                         gpm.set_fader_name ("AudioBusFader");
2036
2037                         /* (no MIDI busses yet) */
2038                 }
2039         }
2040 }
2041
2042
2043 void
2044 MixerStrip::engine_stopped ()
2045 {
2046 }
2047
2048 void
2049 MixerStrip::engine_running ()
2050 {
2051 }
2052
2053 string
2054 MixerStrip::meter_point_string (MeterPoint mp)
2055 {
2056         switch (_width) {
2057         case Wide:
2058                 switch (mp) {
2059                 case MeterInput:
2060                         return _("In");
2061                         break;
2062
2063                 case MeterPreFader:
2064                         return _("Pre");
2065                         break;
2066
2067                 case MeterPostFader:
2068                         return _("Post");
2069                         break;
2070
2071                 case MeterOutput:
2072                         return _("Out");
2073                         break;
2074
2075                 case MeterCustom:
2076                 default:
2077                         return _("Custom");
2078                         break;
2079                 }
2080                 break;
2081         case Narrow:
2082                 switch (mp) {
2083                 case MeterInput:
2084                         return S_("Meter|In");
2085                         break;
2086
2087                 case MeterPreFader:
2088                         return S_("Meter|Pr");
2089                         break;
2090
2091                 case MeterPostFader:
2092                         return S_("Meter|Po");
2093                         break;
2094
2095                 case MeterOutput:
2096                         return S_("Meter|O");
2097                         break;
2098
2099                 case MeterCustom:
2100                 default:
2101                         return S_("Meter|C");
2102                         break;
2103                 }
2104                 break;
2105         }
2106
2107         return string();
2108 }
2109
2110 /** Called when the monitor-section state */
2111 void
2112 MixerStrip::monitor_changed ()
2113 {
2114         assert (monitor_section_button);
2115         if (_session->monitor_active()) {
2116                 monitor_section_button->set_name ("master monitor section button active");
2117         } else {
2118                 monitor_section_button->set_name ("master monitor section button normal");
2119         }
2120 }
2121
2122 /** Called when the metering point has changed */
2123 void
2124 MixerStrip::meter_changed ()
2125 {
2126         gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2127         gpm.setup_meters ();
2128         // reset peak when meter point changes
2129         gpm.reset_peak_display();
2130 }
2131
2132 /** The bus that we are displaying sends to has changed, or been turned off.
2133  *  @param send_to New bus that we are displaying sends to, or 0.
2134  */
2135 void
2136 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2137 {
2138         RouteUI::bus_send_display_changed (send_to);
2139
2140         if (send_to) {
2141                 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2142
2143                 if (send) {
2144                         show_send (send);
2145                 } else {
2146                         revert_to_default_display ();
2147                 }
2148         } else {
2149                 revert_to_default_display ();
2150         }
2151 }
2152
2153 void
2154 MixerStrip::drop_send ()
2155 {
2156         boost::shared_ptr<Send> current_send;
2157
2158         if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2159                 current_send->set_metering (false);
2160         }
2161
2162         send_gone_connection.disconnect ();
2163         input_button.set_sensitive (true);
2164         output_button.set_sensitive (true);
2165         group_button.set_sensitive (true);
2166         set_invert_sensitive (true);
2167         gpm.meter_point_button.set_sensitive (true);
2168         mute_button->set_sensitive (true);
2169         solo_button->set_sensitive (true);
2170         solo_isolated_led->set_sensitive (true);
2171         solo_safe_led->set_sensitive (true);
2172         monitor_input_button->set_sensitive (true);
2173         monitor_disk_button->set_sensitive (true);
2174         _comment_button.set_sensitive (true);
2175         RouteUI::check_rec_enable_sensitivity ();
2176         set_button_names (); // update solo button visual state
2177 }
2178
2179 void
2180 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2181 {
2182         _current_delivery = d;
2183         DeliveryChanged (_current_delivery);
2184 }
2185
2186 void
2187 MixerStrip::show_send (boost::shared_ptr<Send> send)
2188 {
2189         assert (send != 0);
2190
2191         drop_send ();
2192
2193         set_current_delivery (send);
2194
2195         send->meter()->set_type(_route->shared_peak_meter()->get_type());
2196         send->set_metering (true);
2197         _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2198
2199         gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2200         gain_meter().setup_meters ();
2201
2202         uint32_t const in = _current_delivery->pans_required();
2203         uint32_t const out = _current_delivery->pan_outs();
2204
2205         panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2206         panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2207         panner_ui().setup_pan ();
2208         panner_ui().set_send_drawing_mode (true);
2209         panner_ui().show_all ();
2210
2211         input_button.set_sensitive (false);
2212         group_button.set_sensitive (false);
2213         set_invert_sensitive (false);
2214         gpm.meter_point_button.set_sensitive (false);
2215         mute_button->set_sensitive (false);
2216         solo_button->set_sensitive (false);
2217         rec_enable_button->set_sensitive (false);
2218         solo_isolated_led->set_sensitive (false);
2219         solo_safe_led->set_sensitive (false);
2220         monitor_input_button->set_sensitive (false);
2221         monitor_disk_button->set_sensitive (false);
2222         _comment_button.set_sensitive (false);
2223
2224         if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2225                 output_button.set_sensitive (false);
2226         }
2227
2228         reset_strip_style ();
2229 }
2230
2231 void
2232 MixerStrip::revert_to_default_display ()
2233 {
2234         drop_send ();
2235
2236         set_current_delivery (_route->main_outs ());
2237
2238         gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2239         gain_meter().setup_meters ();
2240
2241         panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2242         update_panner_choices();
2243         panner_ui().setup_pan ();
2244         panner_ui().set_send_drawing_mode (false);
2245
2246         if (has_audio_outputs ()) {
2247                 panners.show_all ();
2248         } else {
2249                 panners.hide_all ();
2250         }
2251
2252         reset_strip_style ();
2253 }
2254
2255 void
2256 MixerStrip::set_button_names ()
2257 {
2258         switch (_width) {
2259         case Wide:
2260                 mute_button->set_text (_("Mute"));
2261                 monitor_input_button->set_text (_("In"));
2262                 monitor_disk_button->set_text (_("Disk"));
2263                 if (monitor_section_button) {
2264                         monitor_section_button->set_text (_("Mon"));
2265                 }
2266
2267                 if (_route && _route->solo_safe_control()->solo_safe()) {
2268                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2269                 } else {
2270                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2271                 }
2272                 if (!Config->get_solo_control_is_listen_control()) {
2273                         solo_button->set_text (_("Solo"));
2274                 } else {
2275                         switch (Config->get_listen_position()) {
2276                         case AfterFaderListen:
2277                                 solo_button->set_text (_("AFL"));
2278                                 break;
2279                         case PreFaderListen:
2280                                 solo_button->set_text (_("PFL"));
2281                                 break;
2282                         }
2283                 }
2284                 solo_isolated_led->set_text (_("Iso"));
2285                 solo_safe_led->set_text (S_("SoloLock|Lock"));
2286                 break;
2287
2288         default:
2289                 mute_button->set_text (S_("Mute|M"));
2290                 monitor_input_button->set_text (S_("MonitorInput|I"));
2291                 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2292                 if (monitor_section_button) {
2293                         monitor_section_button->set_text (S_("Mon|O"));
2294                 }
2295
2296                 if (_route && _route->solo_safe_control()->solo_safe()) {
2297                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2298                 } else {
2299                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2300                 }
2301                 if (!Config->get_solo_control_is_listen_control()) {
2302                         solo_button->set_text (S_("Solo|S"));
2303                 } else {
2304                         switch (Config->get_listen_position()) {
2305                         case AfterFaderListen:
2306                                 solo_button->set_text (S_("AfterFader|A"));
2307                                 break;
2308                         case PreFaderListen:
2309                                 solo_button->set_text (S_("Prefader|P"));
2310                                 break;
2311                         }
2312                 }
2313
2314                 solo_isolated_led->set_text (S_("SoloIso|I"));
2315                 solo_safe_led->set_text (S_("SoloLock|L"));
2316                 break;
2317         }
2318
2319         if (_route) {
2320                 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2321         } else {
2322                 gpm.meter_point_button.set_text ("");
2323         }
2324 }
2325
2326 PluginSelector*
2327 MixerStrip::plugin_selector()
2328 {
2329         return _mixer.plugin_selector();
2330 }
2331
2332 void
2333 MixerStrip::hide_things ()
2334 {
2335         processor_box.hide_things ();
2336 }
2337
2338 bool
2339 MixerStrip::input_active_button_press (GdkEventButton*)
2340 {
2341         /* nothing happens on press */
2342         return true;
2343 }
2344
2345 bool
2346 MixerStrip::input_active_button_release (GdkEventButton* ev)
2347 {
2348         boost::shared_ptr<MidiTrack> mt = midi_track ();
2349
2350         if (!mt) {
2351                 return true;
2352         }
2353
2354         boost::shared_ptr<RouteList> rl (new RouteList);
2355
2356         rl->push_back (route());
2357
2358         _session->set_exclusive_input_active (rl, !mt->input_active(),
2359                                               Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2360
2361         return true;
2362 }
2363
2364 void
2365 MixerStrip::midi_input_status_changed ()
2366 {
2367         if (midi_input_enable_button) {
2368                 boost::shared_ptr<MidiTrack> mt = midi_track ();
2369                 assert (mt);
2370                 midi_input_enable_button->set_active (mt->input_active ());
2371         }
2372 }
2373
2374 string
2375 MixerStrip::state_id () const
2376 {
2377         return string_compose ("strip %1", _route->id().to_s());
2378 }
2379
2380 void
2381 MixerStrip::parameter_changed (string p)
2382 {
2383         if (p == _visibility.get_state_name()) {
2384                 /* The user has made changes to the mixer strip visibility, so get
2385                    our VisibilityGroup to reflect these changes in our widgets.
2386                 */
2387                 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2388         } else if (p == "track-name-number") {
2389                 name_changed ();
2390         } else if (p == "use-monitor-bus") {
2391                 if (monitor_section_button) {
2392                         if (mute_button->get_parent()) {
2393                                 mute_button->get_parent()->remove(*mute_button);
2394                         }
2395                         if (monitor_section_button->get_parent()) {
2396                                 monitor_section_button->get_parent()->remove(*monitor_section_button);
2397                         }
2398                         if (Config->get_use_monitor_bus ()) {
2399                                 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2400                                 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2401                                 mute_button->show();
2402                                 monitor_section_button->show();
2403                         } else {
2404                                 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2405                                 mute_button->show();
2406                         }
2407                 }
2408         } else if (p == "track-name-number") {
2409                 update_track_number_visibility();
2410         }
2411 }
2412
2413 /** Called to decide whether the solo isolate / solo lock button visibility should
2414  *  be overridden from that configured by the user.  We do this for the master bus.
2415  *
2416  *  @return optional value that is present if visibility state should be overridden.
2417  */
2418 boost::optional<bool>
2419 MixerStrip::override_solo_visibility () const
2420 {
2421         if (_route && _route->is_master ()) {
2422                 return boost::optional<bool> (false);
2423         }
2424
2425         return boost::optional<bool> ();
2426 }
2427
2428 void
2429 MixerStrip::add_input_port (DataType t)
2430 {
2431         _route->input()->add_port ("", this, t);
2432 }
2433
2434 void
2435 MixerStrip::add_output_port (DataType t)
2436 {
2437         _route->output()->add_port ("", this, t);
2438 }
2439
2440 void
2441 MixerStrip::route_active_changed ()
2442 {
2443         reset_strip_style ();
2444 }
2445
2446 void
2447 MixerStrip::copy_processors ()
2448 {
2449         processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2450 }
2451
2452 void
2453 MixerStrip::cut_processors ()
2454 {
2455         processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2456 }
2457
2458 void
2459 MixerStrip::paste_processors ()
2460 {
2461         processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2462 }
2463
2464 void
2465 MixerStrip::select_all_processors ()
2466 {
2467         processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2468 }
2469
2470 void
2471 MixerStrip::deselect_all_processors ()
2472 {
2473         processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2474 }
2475
2476 bool
2477 MixerStrip::delete_processors ()
2478 {
2479         return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2480 }
2481
2482 void
2483 MixerStrip::toggle_processors ()
2484 {
2485         processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2486 }
2487
2488 void
2489 MixerStrip::ab_plugins ()
2490 {
2491         processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2492 }
2493
2494 bool
2495 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2496 {
2497         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2498                 return false;
2499         }
2500         if (ev->button == 3) {
2501                 popup_level_meter_menu (ev);
2502                 return true;
2503         }
2504
2505         return false;
2506 }
2507
2508 void
2509 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2510 {
2511         using namespace Gtk::Menu_Helpers;
2512
2513         Gtk::Menu* m = manage (new Menu);
2514         MenuList& items = m->items ();
2515
2516         RadioMenuItem::Group group;
2517
2518         _suspend_menu_callbacks = true;
2519         add_level_meter_item_point (items, group, _("Input"), MeterInput);
2520         add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2521         add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2522         add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2523         add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2524
2525         if (gpm.meter_channels().n_audio() == 0) {
2526                 m->popup (ev->button, ev->time);
2527                 _suspend_menu_callbacks = false;
2528                 return;
2529         }
2530
2531         RadioMenuItem::Group tgroup;
2532         items.push_back (SeparatorElem());
2533
2534         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2535         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2536         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms),  MeterKrms);
2537         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2538         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2539         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2540         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2541         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2542         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2543         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2544         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU),  MeterVU);
2545
2546         int _strip_type;
2547         if (_route->is_master()) {
2548                 _strip_type = 4;
2549         }
2550         else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2551                         && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2552                 /* non-master bus */
2553                 _strip_type = 3;
2554         }
2555         else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2556                 _strip_type = 2;
2557         }
2558         else {
2559                 _strip_type = 1;
2560         }
2561
2562         MeterType cmt = _route->meter_type();
2563         const std::string cmn = ArdourMeter::meter_type_string(cmt);
2564
2565         items.push_back (SeparatorElem());
2566         items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2567                                 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2568         items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2569                                 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2570         items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2571                                 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2572
2573         m->popup (ev->button, ev->time);
2574         _suspend_menu_callbacks = false;
2575 }
2576
2577 void
2578 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2579                 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2580 {
2581         using namespace Menu_Helpers;
2582
2583         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2584         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2585         i->set_active (_route->meter_point() == point);
2586 }
2587
2588 void
2589 MixerStrip::set_meter_point (MeterPoint p)
2590 {
2591         if (_suspend_menu_callbacks) return;
2592         _route->set_meter_point (p);
2593 }
2594
2595 void
2596 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2597                 RadioMenuItem::Group& group, string const & name, MeterType type)
2598 {
2599         using namespace Menu_Helpers;
2600
2601         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2602         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2603         i->set_active (_route->meter_type() == type);
2604 }
2605
2606 void
2607 MixerStrip::set_meter_type (MeterType t)
2608 {
2609         if (_suspend_menu_callbacks) return;
2610         gpm.set_type (t);
2611 }
2612
2613 void
2614 MixerStrip::update_track_number_visibility ()
2615 {
2616         DisplaySuspender ds;
2617         bool show_label = _session->config.get_track_name_number();
2618
2619         if (_route && _route->is_master()) {
2620                 show_label = false;
2621         }
2622
2623         if (show_label) {
2624                 number_label.show ();
2625                 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2626                 // except the width of the number label is subtracted from the name-hbox, so we
2627                 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2628                 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2629                 if (tnw & 1) --tnw;
2630                 number_label.set_size_request(tnw, -1);
2631                 number_label.show ();
2632         } else {
2633                 number_label.hide ();
2634         }
2635 }
2636
2637 Gdk::Color
2638 MixerStrip::color () const
2639 {
2640         return route_color ();
2641 }
2642
2643 bool
2644 MixerStrip::marked_for_display () const
2645 {
2646         return !_route->presentation_info().hidden();
2647 }
2648
2649 bool
2650 MixerStrip::set_marked_for_display (bool yn)
2651 {
2652         return RouteUI::mark_hidden (!yn);
2653 }