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