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