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