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