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