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