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