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