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