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