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