use Slavable API in GUI
[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         _route->gain_control()->MasterStatusChange.connect (route_connections,
645                                                             invalidator (*this),
646                                                             boost::bind (&MixerStrip::update_vca_display, this),
647                                                             gui_context());
648
649         set_stuff_from_route ();
650
651         /* now force an update of all the various elements */
652
653         update_mute_display ();
654         update_solo_display ();
655         update_vca_display ();
656         name_changed ();
657         comment_changed ();
658         route_group_changed ();
659         update_track_number_visibility ();
660
661         connect_to_pan ();
662         panners.setup_pan ();
663
664         if (has_audio_outputs ()) {
665                 panners.show_all ();
666         } else {
667                 panners.hide_all ();
668         }
669
670         update_diskstream_display ();
671         update_input_display ();
672         update_output_display ();
673
674         add_events (Gdk::BUTTON_RELEASE_MASK);
675
676         processor_box.show ();
677
678         if (!route()->is_master() && !route()->is_monitor()) {
679                 /* we don't allow master or control routes to be hidden */
680                 hide_button.show();
681                 number_label.show();
682         }
683
684         gpm.reset_peak_display ();
685         gpm.gain_display.show ();
686         gpm.peak_display.show ();
687
688         width_button.show();
689         width_hide_box.show();
690         global_frame.show();
691         global_vpacker.show();
692         mute_solo_table.show();
693         bottom_button_table.show();
694         gpm.show_all ();
695         meter_point_button.show();
696         input_button_box.show_all();
697         output_button.show();
698         name_button.show();
699         _comment_button.show();
700         group_button.show();
701         gpm.gain_automation_state_button.show();
702
703         parameter_changed ("mixer-element-visibility");
704         map_frozen();
705
706         show ();
707 }
708
709 void
710 MixerStrip::set_stuff_from_route ()
711 {
712         /* if width is not set, it will be set by the MixerUI or editor */
713
714         string str = gui_property ("strip-width");
715         if (!str.empty()) {
716                 set_width_enum (Width (string_2_enum (str, _width)), this);
717         }
718 }
719
720 void
721 MixerStrip::set_width_enum (Width w, void* owner)
722 {
723         /* always set the gpm width again, things may be hidden */
724
725         gpm.set_width (w);
726         panners.set_width (w);
727
728         boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
729
730         _width_owner = owner;
731
732         _width = w;
733
734         if (_width_owner == this) {
735                 set_gui_property ("strip-width", enum_2_string (_width));
736         }
737
738         set_button_names ();
739
740         const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
741
742         switch (w) {
743         case Wide:
744
745                 if (show_sends_button)  {
746                         show_sends_button->set_text (_("Aux"));
747                 }
748
749                 gpm.gain_automation_style_button.set_text (
750                                 gpm.astyle_string(gain_automation->automation_style()));
751                 gpm.gain_automation_state_button.set_text (
752                                 gpm.astate_string(gain_automation->automation_state()));
753
754                 if (_route->panner()) {
755                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
756                                         panners.astyle_string(_route->panner()->automation_style()));
757                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
758                                         panners.astate_string(_route->panner()->automation_state()));
759                 }
760
761                 {
762                         // panners expect an even number of horiz. pixels
763                         int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
764                         width &= ~1;
765                         set_size_request (width, -1);
766                 }
767                 break;
768
769         case Narrow:
770
771                 if (show_sends_button) {
772                         show_sends_button->set_text (_("Snd"));
773                 }
774
775                 gpm.gain_automation_style_button.set_text (
776                                 gpm.short_astyle_string(gain_automation->automation_style()));
777                 gpm.gain_automation_state_button.set_text (
778                                 gpm.short_astate_string(gain_automation->automation_state()));
779                 gain_meter().setup_meters (); // recalc meter width
780
781                 if (_route->panner()) {
782                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
783                         panners.short_astyle_string(_route->panner()->automation_style()));
784                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
785                         panners.short_astate_string(_route->panner()->automation_state()));
786                 }
787
788                 {
789                         // panners expect an even number of horiz. pixels
790                         int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
791                         width &= ~1;
792                         set_size_request (width, -1);
793                 }
794                 break;
795         }
796
797         processor_box.set_width (w);
798
799         update_input_display ();
800         update_output_display ();
801         setup_comment_button ();
802         route_group_changed ();
803         name_changed ();
804         WidthChanged ();
805 }
806
807 void
808 MixerStrip::set_packed (bool yn)
809 {
810         _packed = yn;
811
812         if (_packed) {
813                 set_gui_property ("visible", true);
814         } else {
815                 set_gui_property ("visible", false);
816         }
817 }
818
819
820 struct RouteCompareByName {
821         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
822                 return a->name().compare (b->name()) < 0;
823         }
824 };
825
826 gint
827 MixerStrip::output_release (GdkEventButton *ev)
828 {
829         switch (ev->button) {
830         case 3:
831                 edit_output_configuration ();
832                 break;
833         }
834
835         return false;
836 }
837
838 gint
839 MixerStrip::output_press (GdkEventButton *ev)
840 {
841         using namespace Menu_Helpers;
842         if (!_session->engine().connected()) {
843                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
844                 msg.run ();
845                 return true;
846         }
847
848         MenuList& citems = output_menu.items();
849         switch (ev->button) {
850
851         case 3:
852                 return false;  //wait for the mouse-up to pop the dialog
853
854         case 1:
855         {
856                 output_menu.set_name ("ArdourContextMenu");
857                 citems.clear ();
858                 output_menu_bundles.clear ();
859
860                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
861
862                 citems.push_back (SeparatorElem());
863                 uint32_t const n_with_separator = citems.size ();
864
865                 ARDOUR::BundleList current = _route->output()->bundles_connected ();
866
867                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
868
869                 /* give user bundles first chance at being in the menu */
870
871                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
872                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
873                                 maybe_add_bundle_to_output_menu (*i, current);
874                         }
875                 }
876
877                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
878                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
879                                 maybe_add_bundle_to_output_menu (*i, current);
880                         }
881                 }
882
883                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
884                 RouteList copy = *routes;
885                 copy.sort (RouteCompareByName ());
886                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
887                         maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
888                 }
889
890                 if (citems.size() == n_with_separator) {
891                         /* no routes added; remove the separator */
892                         citems.pop_back ();
893                 }
894
895                 if (!ARDOUR::Profile->get_mixbus()) {
896                         citems.push_back (SeparatorElem());
897
898                         for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
899                                 citems.push_back (
900                                                 MenuElem (
901                                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
902                                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
903                                                         )
904                                                 );
905                         }
906                 }
907
908                 citems.push_back (SeparatorElem());
909                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
910
911                 output_menu.popup (1, ev->time);
912                 break;
913         }
914
915         default:
916                 break;
917         }
918         return TRUE;
919 }
920
921 gint
922 MixerStrip::input_release (GdkEventButton *ev)
923 {
924         switch (ev->button) {
925
926         case 3:
927                 edit_input_configuration ();
928                 break;
929         default:
930                 break;
931
932         }
933
934         return false;
935 }
936
937
938 gint
939 MixerStrip::input_press (GdkEventButton *ev)
940 {
941         using namespace Menu_Helpers;
942
943         MenuList& citems = input_menu.items();
944         input_menu.set_name ("ArdourContextMenu");
945         citems.clear();
946
947         if (!_session->engine().connected()) {
948                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
949                 msg.run ();
950                 return true;
951         }
952
953         if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
954                 return true;
955
956         switch (ev->button) {
957
958         case 3:
959                 return false;  //don't handle the mouse-down here.  wait for mouse-up to pop the menu
960
961         case 1:
962         {
963                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
964
965                 citems.push_back (SeparatorElem());
966                 uint32_t const n_with_separator = citems.size ();
967
968                 input_menu_bundles.clear ();
969
970                 ARDOUR::BundleList current = _route->input()->bundles_connected ();
971
972                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
973
974                 /* give user bundles first chance at being in the menu */
975
976                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
977                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
978                                 maybe_add_bundle_to_input_menu (*i, current);
979                         }
980                 }
981
982                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
983                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
984                                 maybe_add_bundle_to_input_menu (*i, current);
985                         }
986                 }
987
988                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
989                 RouteList copy = *routes;
990                 copy.sort (RouteCompareByName ());
991                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
992                         maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
993                 }
994
995                 if (citems.size() == n_with_separator) {
996                         /* no routes added; remove the separator */
997                         citems.pop_back ();
998                 }
999
1000                 citems.push_back (SeparatorElem());
1001                 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1002                         citems.push_back (
1003                                 MenuElem (
1004                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1005                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1006                                         )
1007                                 );
1008                 }
1009
1010                 citems.push_back (SeparatorElem());
1011                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1012
1013                 input_menu.popup (1, ev->time);
1014
1015                 break;
1016         }
1017         default:
1018                 break;
1019         }
1020         return TRUE;
1021 }
1022
1023 void
1024 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1025 {
1026         if (ignore_toggle) {
1027                 return;
1028         }
1029
1030         ARDOUR::BundleList current = _route->input()->bundles_connected ();
1031
1032         if (std::find (current.begin(), current.end(), c) == current.end()) {
1033                 _route->input()->connect_ports_to_bundle (c, true, this);
1034         } else {
1035                 _route->input()->disconnect_ports_from_bundle (c, this);
1036         }
1037 }
1038
1039 void
1040 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1041 {
1042         if (ignore_toggle) {
1043                 return;
1044         }
1045
1046         ARDOUR::BundleList current = _route->output()->bundles_connected ();
1047
1048         if (std::find (current.begin(), current.end(), c) == current.end()) {
1049                 _route->output()->connect_ports_to_bundle (c, true, this);
1050         } else {
1051                 _route->output()->disconnect_ports_from_bundle (c, this);
1052         }
1053 }
1054
1055 void
1056 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1057 {
1058         using namespace Menu_Helpers;
1059
1060         if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1061                 return;
1062         }
1063
1064         list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1065         while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1066                 ++i;
1067         }
1068
1069         if (i != input_menu_bundles.end()) {
1070                 return;
1071         }
1072
1073         input_menu_bundles.push_back (b);
1074
1075         MenuList& citems = input_menu.items();
1076
1077         std::string n = b->name ();
1078         replace_all (n, "_", " ");
1079
1080         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1081 }
1082
1083 void
1084 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1085 {
1086         using namespace Menu_Helpers;
1087
1088         if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1089                 return;
1090         }
1091
1092         list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1093         while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1094                 ++i;
1095         }
1096
1097         if (i != output_menu_bundles.end()) {
1098                 return;
1099         }
1100
1101         output_menu_bundles.push_back (b);
1102
1103         MenuList& citems = output_menu.items();
1104
1105         std::string n = b->name ();
1106         replace_all (n, "_", " ");
1107
1108         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1109 }
1110
1111 void
1112 MixerStrip::update_diskstream_display ()
1113 {
1114         if (is_track() && input_selector) {
1115                         input_selector->hide_all ();
1116         }
1117
1118         route_color_changed ();
1119 }
1120
1121 void
1122 MixerStrip::connect_to_pan ()
1123 {
1124         ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1125
1126         panstate_connection.disconnect ();
1127         panstyle_connection.disconnect ();
1128
1129         if (!_route->panner()) {
1130                 return;
1131         }
1132
1133         boost::shared_ptr<Pannable> p = _route->pannable ();
1134
1135         p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1136         p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1137
1138         /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1139          * However, that only works a panner was previously set.
1140          *
1141          * PannerUI must remain subscribed to _panshell->Changed() in case
1142          * we switch the panner eg. AUX-Send and back
1143          * _route->panner_shell()->Changed() vs _panshell->Changed
1144          */
1145         if (panners._panner == 0) {
1146                 panners.panshell_changed ();
1147         }
1148         update_panner_choices();
1149 }
1150
1151 void
1152 MixerStrip::update_panner_choices ()
1153 {
1154         ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1155         if (!_route->panner_shell()) { return; }
1156
1157         uint32_t in = _route->output()->n_ports().n_audio();
1158         uint32_t out = in;
1159         if (_route->panner()) {
1160                 in = _route->panner()->in().n_audio();
1161         }
1162
1163         panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1164 }
1165
1166 /*
1167  * Output port labelling
1168  * =====================
1169  *
1170  * Case 1: Each output has one connection, all connections are to system:playback_%i
1171  *   out 1 -> system:playback_1
1172  *   out 2 -> system:playback_2
1173  *   out 3 -> system:playback_3
1174  *   Display as: 1/2/3
1175  *
1176  * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1177  *   out 1 -> ardour:track_x/in 1
1178  *   out 2 -> ardour:track_x/in 2
1179  *   Display as: track_x
1180  *
1181  * Case 3: Each output has one connection, all connections are to Jack client "program x"
1182  *   out 1 -> program x:foo
1183  *   out 2 -> program x:foo
1184  *   Display as: program x
1185  *
1186  * Case 4: No connections (Disconnected)
1187  *   Display as: -
1188  *
1189  * Default case (unusual routing):
1190  *   Display as: *number of connections*
1191  *
1192  * Tooltips
1193  * ========
1194  * .-----------------------------------------------.
1195  * | Mixdown                                       |
1196  * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1197  * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1198  * '-----------------------------------------------'
1199  * .-----------------------------------------------.
1200  * | Guitar SM58                                   |
1201  * | Disconnected                                  |
1202  * '-----------------------------------------------'
1203  */
1204
1205 void
1206 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1207 {
1208         uint32_t io_count;
1209         uint32_t io_index;
1210         boost::shared_ptr<Port> port;
1211         vector<string> port_connections;
1212
1213         uint32_t total_connection_count = 0;
1214         uint32_t io_connection_count = 0;
1215         uint32_t ardour_connection_count = 0;
1216         uint32_t system_connection_count = 0;
1217         uint32_t other_connection_count = 0;
1218         uint32_t typed_connection_count = 0;
1219
1220         ostringstream label;
1221
1222         bool have_label = false;
1223         bool each_io_has_one_connection = true;
1224
1225         string connection_name;
1226         string ardour_track_name;
1227         string other_connection_type;
1228         string system_ports;
1229         string system_port;
1230
1231         ostringstream tooltip;
1232         char * tooltip_cstr;
1233
1234         //to avoid confusion, the button caption should only show connections that match the datatype of the track
1235         DataType dt = DataType::AUDIO;
1236         if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1237                 dt = DataType::MIDI;
1238                 // avoid further confusion with Midi-tracks that have a synth.
1239                 // Audio-ports may be connected, but button says "Disconnected"
1240                 tooltip << _("MIDI ");
1241         }
1242
1243         if (for_input) {
1244                 io_count = route->n_inputs().n_total();
1245                 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1246         } else {
1247                 io_count = route->n_outputs().n_total();
1248                 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1249         }
1250
1251
1252         for (io_index = 0; io_index < io_count; ++io_index) {
1253                 if (for_input) {
1254                         port = route->input()->nth (io_index);
1255                 } else {
1256                         port = route->output()->nth (io_index);
1257                 }
1258
1259                 port_connections.clear ();
1260                 port->get_connections(port_connections);
1261
1262                 //ignore any port connections that don't match our DataType
1263                 if (port->type() != dt) {
1264                         if (!port_connections.empty()) {
1265                                 ++typed_connection_count;
1266                         }
1267                         continue;
1268                 }
1269
1270                 io_connection_count = 0;
1271
1272                 if (!port_connections.empty()) {
1273                         for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1274                                 string pn = "";
1275                                 string& connection_name (*i);
1276
1277                                 if (connection_name.find("system:") == 0) {
1278                                         pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1279                                 }
1280
1281                                 if (io_connection_count == 0) {
1282                                         tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1283                                                 << " -> "
1284                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1285                                 } else {
1286                                         tooltip << ", "
1287                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1288                                 }
1289
1290                                 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1291                                         if (ardour_track_name.empty()) {
1292                                                 // "ardour:Master/in 1" -> "ardour:Master/"
1293                                                 string::size_type slash = connection_name.find("/");
1294                                                 if (slash != string::npos) {
1295                                                         ardour_track_name = connection_name.substr(0, slash + 1);
1296                                                 }
1297                                         }
1298
1299                                         if (connection_name.find(ardour_track_name) == 0) {
1300                                                 ++ardour_connection_count;
1301                                         }
1302                                 } else if (!pn.empty()) {
1303                                         if (system_ports.empty()) {
1304                                                 system_ports += pn;
1305                                         } else {
1306                                                 system_ports += "/" + pn;
1307                                         }
1308                                         if (connection_name.find("system:") == 0) {
1309                                                 ++system_connection_count;
1310                                         }
1311                                 } else if (connection_name.find("system:midi_") == 0) {
1312                                         if (for_input) {
1313                                                 // "system:midi_capture_123" -> "123"
1314                                                 system_port = "M " + connection_name.substr(20);
1315                                         } else {
1316                                                 // "system:midi_playback_123" -> "123"
1317                                                 system_port = "M " + connection_name.substr(21);
1318                                         }
1319
1320                                         if (system_ports.empty()) {
1321                                                 system_ports += system_port;
1322                                         } else {
1323                                                 system_ports += "/" + system_port;
1324                                         }
1325
1326                                         ++system_connection_count;
1327
1328                                 } else if (connection_name.find("system:") == 0) {
1329                                         if (for_input) {
1330                                                 // "system:capture_123" -> "123"
1331                                                 system_port = connection_name.substr(15);
1332                                         } else {
1333                                                 // "system:playback_123" -> "123"
1334                                                 system_port = connection_name.substr(16);
1335                                         }
1336
1337                                         if (system_ports.empty()) {
1338                                                 system_ports += system_port;
1339                                         } else {
1340                                                 system_ports += "/" + system_port;
1341                                         }
1342
1343                                         ++system_connection_count;
1344                                 } else {
1345                                         if (other_connection_type.empty()) {
1346                                                 // "jamin:in 1" -> "jamin:"
1347                                                 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1348                                         }
1349
1350                                         if (connection_name.find(other_connection_type) == 0) {
1351                                                 ++other_connection_count;
1352                                         }
1353                                 }
1354
1355                                 ++total_connection_count;
1356                                 ++io_connection_count;
1357                         }
1358                 }
1359
1360                 if (io_connection_count != 1) {
1361                         each_io_has_one_connection = false;
1362                 }
1363         }
1364
1365         if (total_connection_count == 0) {
1366                 tooltip << endl << _("Disconnected");
1367         }
1368
1369         tooltip_cstr = new char[tooltip.str().size() + 1];
1370         strcpy(tooltip_cstr, tooltip.str().c_str());
1371
1372         if (for_input) {
1373                 set_tooltip (&input_button, tooltip_cstr);
1374         } else {
1375                 set_tooltip (&output_button, tooltip_cstr);
1376         }
1377
1378         delete [] tooltip_cstr;
1379
1380         if (each_io_has_one_connection) {
1381                 if (total_connection_count == ardour_connection_count) {
1382                         // all connections are to the same track in ardour
1383                         // "ardour:Master/" -> "Master"
1384                         string::size_type slash = ardour_track_name.find("/");
1385                         if (slash != string::npos) {
1386                                 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1387                                 label << ardour_track_name.substr (ppps, slash - ppps);
1388                                 have_label = true;
1389                         }
1390                 }
1391                 else if (total_connection_count == system_connection_count) {
1392                         // all connections are to system ports
1393                         label << system_ports;
1394                         have_label = true;
1395                 }
1396                 else if (total_connection_count == other_connection_count) {
1397                         // all connections are to the same external program eg jamin
1398                         // "jamin:" -> "jamin"
1399                         label << other_connection_type.substr(0, other_connection_type.size() - 1);
1400                         have_label = true;
1401                 }
1402         }
1403
1404         if (!have_label) {
1405                 if (total_connection_count == 0) {
1406                         // Disconnected
1407                         label << "-";
1408                 } else {
1409                         // Odd configuration
1410                         label << "*" << total_connection_count << "*";
1411                 }
1412                 if (typed_connection_count > 0) {
1413                         label << "\u2295"; // circled plus
1414                 }
1415         }
1416
1417         if (for_input) {
1418                 input_button.set_text (label.str());
1419         } else {
1420                 output_button.set_text (label.str());
1421         }
1422 }
1423
1424 void
1425 MixerStrip::update_input_display ()
1426 {
1427         update_io_button (_route, _width, true);
1428         panners.setup_pan ();
1429
1430         if (has_audio_outputs ()) {
1431                 panners.show_all ();
1432         } else {
1433                 panners.hide_all ();
1434         }
1435
1436 }
1437
1438 void
1439 MixerStrip::update_output_display ()
1440 {
1441         update_io_button (_route, _width, false);
1442         gpm.setup_meters ();
1443         panners.setup_pan ();
1444
1445         if (has_audio_outputs ()) {
1446                 panners.show_all ();
1447         } else {
1448                 panners.hide_all ();
1449         }
1450 }
1451
1452 void
1453 MixerStrip::fast_update ()
1454 {
1455         gpm.update_meters ();
1456 }
1457
1458 void
1459 MixerStrip::diskstream_changed ()
1460 {
1461         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1462 }
1463
1464 void
1465 MixerStrip::io_changed_proxy ()
1466 {
1467         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1468 }
1469
1470 void
1471 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1472 {
1473         boost::shared_ptr<Port> a = wa.lock ();
1474         boost::shared_ptr<Port> b = wb.lock ();
1475
1476         if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1477                 update_input_display ();
1478                 set_width_enum (_width, this);
1479         }
1480
1481         if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1482                 update_output_display ();
1483                 set_width_enum (_width, this);
1484         }
1485 }
1486
1487 void
1488 MixerStrip::setup_comment_button ()
1489 {
1490         switch (_width) {
1491
1492         case Wide:
1493                 if (_route->comment().empty ()) {
1494                         _comment_button.unset_bg (STATE_NORMAL);
1495                         _comment_button.set_text (_("Comments"));
1496                 } else {
1497                         _comment_button.modify_bg (STATE_NORMAL, color ());
1498                         _comment_button.set_text (_("*Comments*"));
1499                 }
1500                 break;
1501
1502         case Narrow:
1503                 if (_route->comment().empty ()) {
1504                         _comment_button.unset_bg (STATE_NORMAL);
1505                         _comment_button.set_text (_("Cmt"));
1506                 } else {
1507                         _comment_button.modify_bg (STATE_NORMAL, color ());
1508                         _comment_button.set_text (_("*Cmt*"));
1509                 }
1510                 break;
1511         }
1512
1513         set_tooltip (
1514                 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1515                 );
1516
1517 }
1518
1519 bool
1520 MixerStrip::select_route_group (GdkEventButton *ev)
1521 {
1522         using namespace Menu_Helpers;
1523
1524         if (ev->button == 1) {
1525
1526                 if (group_menu == 0) {
1527
1528                         PropertyList* plist = new PropertyList();
1529
1530                         plist->add (Properties::gain, true);
1531                         plist->add (Properties::mute, true);
1532                         plist->add (Properties::solo, true);
1533
1534                         group_menu = new RouteGroupMenu (_session, plist);
1535                 }
1536
1537                 WeakRouteList r;
1538                 r.push_back (route ());
1539                 group_menu->build (r);
1540                 group_menu->menu()->popup (1, ev->time);
1541         }
1542
1543         return true;
1544 }
1545
1546 void
1547 MixerStrip::route_group_changed ()
1548 {
1549         ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1550
1551         RouteGroup *rg = _route->route_group();
1552
1553         if (rg) {
1554                 group_button.set_text (PBD::short_version (rg->name(), 5));
1555         } else {
1556                 switch (_width) {
1557                 case Wide:
1558                         group_button.set_text (_("Grp"));
1559                         break;
1560                 case Narrow:
1561                         group_button.set_text (_("~G"));
1562                         break;
1563                 }
1564         }
1565 }
1566
1567 void
1568 MixerStrip::route_color_changed ()
1569 {
1570         name_button.modify_bg (STATE_NORMAL, color());
1571         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1572         reset_strip_style ();
1573 }
1574
1575 void
1576 MixerStrip::show_passthru_color ()
1577 {
1578         reset_strip_style ();
1579 }
1580
1581 void
1582 MixerStrip::build_route_ops_menu ()
1583 {
1584         using namespace Menu_Helpers;
1585         route_ops_menu = new Menu;
1586         route_ops_menu->set_name ("ArdourContextMenu");
1587
1588         MenuList& items = route_ops_menu->items();
1589
1590         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1591
1592         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1593
1594         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1595
1596         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1597
1598         items.push_back (SeparatorElem());
1599
1600         if (!_route->is_master()) {
1601                 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1602         }
1603         items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1604         rename_menu_item = &items.back();
1605
1606         items.push_back (SeparatorElem());
1607         items.push_back (CheckMenuElem (_("Active")));
1608         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1609         i->set_active (_route->active());
1610         i->set_sensitive(! _session->transport_rolling());
1611         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1612
1613         if (!Profile->get_mixbus ()) {
1614                 items.push_back (SeparatorElem());
1615                 items.push_back (CheckMenuElem (_("Strict I/O")));
1616                 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1617                 i->set_active (_route->strict_io());
1618                 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1619         }
1620
1621         if (1 /* TODO IFF >= 1 plugin-insert */) {
1622                 items.push_back (SeparatorElem());
1623                 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1624         }
1625
1626         items.push_back (SeparatorElem());
1627         items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1628
1629         items.push_back (SeparatorElem());
1630         items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1631         denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1632         denormal_menu_item->set_active (_route->denormal_protection());
1633
1634         items.push_back (SeparatorElem());
1635         items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1636
1637         if (_route) {
1638                 /* note that this relies on selection being shared across editor and
1639                    mixer (or global to the backend, in the future), which is the only
1640                    sane thing for users anyway.
1641                 */
1642
1643                 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1644                 if (rtav) {
1645                         Selection& selection (PublicEditor::instance().get_selection());
1646                         if (!selection.selected (rtav)) {
1647                                 selection.set (rtav);
1648                         }
1649
1650                         if (!_route->is_master()) {
1651                                 items.push_back (SeparatorElem());
1652                                 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1653                         }
1654
1655                         items.push_back (SeparatorElem());
1656                         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1657                 }
1658         }
1659 }
1660
1661 gboolean
1662 MixerStrip::name_button_button_press (GdkEventButton* ev)
1663 {
1664         if (ev->button == 3) {
1665                 list_route_operations ();
1666
1667                 /* do not allow rename if the track is record-enabled */
1668                 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1669                 route_ops_menu->popup (1, ev->time);
1670
1671                 return true;
1672         }
1673
1674         return false;
1675 }
1676
1677 gboolean
1678 MixerStrip::name_button_button_release (GdkEventButton* ev)
1679 {
1680         if (ev->button == 1) {
1681                 list_route_operations ();
1682
1683                 /* do not allow rename if the track is record-enabled */
1684                 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1685                 route_ops_menu->popup (1, ev->time);
1686         }
1687
1688         return false;
1689 }
1690
1691 gboolean
1692 MixerStrip::number_button_button_press (GdkEventButton* ev)
1693 {
1694         if (  ev->button == 3 ) {
1695                 list_route_operations ();
1696
1697                 /* do not allow rename if the track is record-enabled */
1698                 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1699                 route_ops_menu->popup (1, ev->time);
1700
1701                 return true;
1702         }
1703
1704         return false;
1705 }
1706
1707 void
1708 MixerStrip::list_route_operations ()
1709 {
1710         delete route_ops_menu;
1711         build_route_ops_menu ();
1712 }
1713
1714 void
1715 MixerStrip::set_selected (bool yn)
1716 {
1717         AxisView::set_selected (yn);
1718         if (_selected) {
1719                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1720                 global_frame.set_name ("MixerStripSelectedFrame");
1721         } else {
1722                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1723                 global_frame.set_name ("MixerStripFrame");
1724         }
1725         global_frame.queue_draw ();
1726
1727 //      if (!yn)
1728 //              processor_box.deselect_all_processors();
1729 }
1730
1731 void
1732 MixerStrip::property_changed (const PropertyChange& what_changed)
1733 {
1734         RouteUI::property_changed (what_changed);
1735
1736         if (what_changed.contains (ARDOUR::Properties::name)) {
1737                 name_changed ();
1738         }
1739 }
1740
1741 void
1742 MixerStrip::name_changed ()
1743 {
1744         switch (_width) {
1745                 case Wide:
1746                         name_button.set_text (_route->name());
1747                         break;
1748                 case Narrow:
1749                         name_button.set_text (PBD::short_version (_route->name(), 5));
1750                         break;
1751         }
1752
1753         set_tooltip (name_button, _route->name());
1754
1755         if (_session->config.get_track_name_number()) {
1756                 const int64_t track_number = _route->track_number ();
1757                 if (track_number == 0) {
1758                         number_label.set_text ("-");
1759                 } else {
1760                         number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1761                 }
1762         } else {
1763                 number_label.set_text ("");
1764         }
1765 }
1766
1767 void
1768 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1769 {
1770         input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1771 }
1772
1773 void
1774 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1775 {
1776         output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1777 }
1778
1779 void
1780 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1781 {
1782         name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1783 }
1784
1785 bool
1786 MixerStrip::width_button_pressed (GdkEventButton* ev)
1787 {
1788         if (ev->button != 1) {
1789                 return false;
1790         }
1791
1792         if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1793                 switch (_width) {
1794                 case Wide:
1795                         _mixer.set_strip_width (Narrow, true);
1796                         break;
1797
1798                 case Narrow:
1799                         _mixer.set_strip_width (Wide, true);
1800                         break;
1801                 }
1802         } else {
1803                 switch (_width) {
1804                 case Wide:
1805                         set_width_enum (Narrow, this);
1806                         break;
1807                 case Narrow:
1808                         set_width_enum (Wide, this);
1809                         break;
1810                 }
1811         }
1812
1813         return true;
1814 }
1815
1816 void
1817 MixerStrip::hide_clicked ()
1818 {
1819         // LAME fix to reset the button status for when it is redisplayed (part 1)
1820         hide_button.set_sensitive(false);
1821
1822         if (_embedded) {
1823                 Hiding(); /* EMIT_SIGNAL */
1824         } else {
1825                 _mixer.hide_strip (this);
1826         }
1827
1828         // (part 2)
1829         hide_button.set_sensitive(true);
1830 }
1831
1832 void
1833 MixerStrip::set_embedded (bool yn)
1834 {
1835         _embedded = yn;
1836 }
1837
1838 void
1839 MixerStrip::map_frozen ()
1840 {
1841         ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1842
1843         boost::shared_ptr<AudioTrack> at = audio_track();
1844
1845         if (at) {
1846                 switch (at->freeze_state()) {
1847                 case AudioTrack::Frozen:
1848                         processor_box.set_sensitive (false);
1849                         hide_redirect_editors ();
1850                         break;
1851                 default:
1852                         processor_box.set_sensitive (true);
1853                         // XXX need some way, maybe, to retoggle redirect editors
1854                         break;
1855                 }
1856         } else {
1857                 processor_box.set_sensitive (true);
1858         }
1859         RouteUI::map_frozen ();
1860 }
1861
1862 void
1863 MixerStrip::hide_redirect_editors ()
1864 {
1865         _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1866 }
1867
1868 void
1869 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1870 {
1871         boost::shared_ptr<Processor> processor (p.lock ());
1872         if (!processor) {
1873                 return;
1874         }
1875
1876         Gtk::Window* w = processor_box.get_processor_ui (processor);
1877
1878         if (w) {
1879                 w->hide ();
1880         }
1881 }
1882
1883 void
1884 MixerStrip::reset_strip_style ()
1885 {
1886         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1887
1888                 gpm.set_fader_name ("SendStripBase");
1889
1890         } else {
1891
1892                 if (is_midi_track()) {
1893                         if (_route->active()) {
1894                                 set_name ("MidiTrackStripBase");
1895                         } else {
1896                                 set_name ("MidiTrackStripBaseInactive");
1897                         }
1898                         gpm.set_fader_name ("MidiTrackFader");
1899                 } else if (is_audio_track()) {
1900                         if (_route->active()) {
1901                                 set_name ("AudioTrackStripBase");
1902                         } else {
1903                                 set_name ("AudioTrackStripBaseInactive");
1904                         }
1905                         gpm.set_fader_name ("AudioTrackFader");
1906                 } else {
1907                         if (_route->active()) {
1908                                 set_name ("AudioBusStripBase");
1909                         } else {
1910                                 set_name ("AudioBusStripBaseInactive");
1911                         }
1912                         gpm.set_fader_name ("AudioBusFader");
1913
1914                         /* (no MIDI busses yet) */
1915                 }
1916         }
1917 }
1918
1919
1920 void
1921 MixerStrip::engine_stopped ()
1922 {
1923 }
1924
1925 void
1926 MixerStrip::engine_running ()
1927 {
1928 }
1929
1930 string
1931 MixerStrip::meter_point_string (MeterPoint mp)
1932 {
1933         switch (_width) {
1934         case Wide:
1935                 switch (mp) {
1936                 case MeterInput:
1937                         return _("In");
1938                         break;
1939
1940                 case MeterPreFader:
1941                         return _("Pre");
1942                         break;
1943
1944                 case MeterPostFader:
1945                         return _("Post");
1946                         break;
1947
1948                 case MeterOutput:
1949                         return _("Out");
1950                         break;
1951
1952                 case MeterCustom:
1953                 default:
1954                         return _("Custom");
1955                         break;
1956                 }
1957                 break;
1958         case Narrow:
1959                 switch (mp) {
1960                 case MeterInput:
1961                         return S_("Meter|In");
1962                         break;
1963
1964                 case MeterPreFader:
1965                         return S_("Meter|Pr");
1966                         break;
1967
1968                 case MeterPostFader:
1969                         return S_("Meter|Po");
1970                         break;
1971
1972                 case MeterOutput:
1973                         return S_("Meter|O");
1974                         break;
1975
1976                 case MeterCustom:
1977                 default:
1978                         return S_("Meter|C");
1979                         break;
1980                 }
1981                 break;
1982         }
1983
1984         return string();
1985 }
1986
1987 /** Called when the monitor-section state */
1988 void
1989 MixerStrip::monitor_changed ()
1990 {
1991         assert (monitor_section_button);
1992         if (_session->monitor_active()) {
1993                 monitor_section_button->set_name ("master monitor section button active");
1994         } else {
1995                 monitor_section_button->set_name ("master monitor section button normal");
1996         }
1997 }
1998
1999 /** Called when the metering point has changed */
2000 void
2001 MixerStrip::meter_changed ()
2002 {
2003         meter_point_button.set_text (meter_point_string (_route->meter_point()));
2004         gpm.setup_meters ();
2005         // reset peak when meter point changes
2006         gpm.reset_peak_display();
2007 }
2008
2009 /** The bus that we are displaying sends to has changed, or been turned off.
2010  *  @param send_to New bus that we are displaying sends to, or 0.
2011  */
2012 void
2013 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2014 {
2015         RouteUI::bus_send_display_changed (send_to);
2016
2017         if (send_to) {
2018                 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2019
2020                 if (send) {
2021                         show_send (send);
2022                 } else {
2023                         revert_to_default_display ();
2024                 }
2025         } else {
2026                 revert_to_default_display ();
2027         }
2028 }
2029
2030 void
2031 MixerStrip::drop_send ()
2032 {
2033         boost::shared_ptr<Send> current_send;
2034
2035         if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2036                 current_send->set_metering (false);
2037         }
2038
2039         send_gone_connection.disconnect ();
2040         input_button.set_sensitive (true);
2041         output_button.set_sensitive (true);
2042         group_button.set_sensitive (true);
2043         set_invert_sensitive (true);
2044         meter_point_button.set_sensitive (true);
2045         mute_button->set_sensitive (true);
2046         solo_button->set_sensitive (true);
2047         solo_isolated_led->set_sensitive (true);
2048         solo_safe_led->set_sensitive (true);
2049         monitor_input_button->set_sensitive (true);
2050         monitor_disk_button->set_sensitive (true);
2051         _comment_button.set_sensitive (true);
2052         RouteUI::check_rec_enable_sensitivity ();
2053         set_button_names (); // update solo button visual state
2054 }
2055
2056 void
2057 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2058 {
2059         _current_delivery = d;
2060         DeliveryChanged (_current_delivery);
2061 }
2062
2063 void
2064 MixerStrip::show_send (boost::shared_ptr<Send> send)
2065 {
2066         assert (send != 0);
2067
2068         drop_send ();
2069
2070         set_current_delivery (send);
2071
2072         send->meter()->set_type(_route->shared_peak_meter()->get_type());
2073         send->set_metering (true);
2074         _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2075
2076         gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2077         gain_meter().setup_meters ();
2078
2079         uint32_t const in = _current_delivery->pans_required();
2080         uint32_t const out = _current_delivery->pan_outs();
2081
2082         panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2083         panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2084         panner_ui().setup_pan ();
2085         panner_ui().set_send_drawing_mode (true);
2086         panner_ui().show_all ();
2087
2088         input_button.set_sensitive (false);
2089         group_button.set_sensitive (false);
2090         set_invert_sensitive (false);
2091         meter_point_button.set_sensitive (false);
2092         mute_button->set_sensitive (false);
2093         solo_button->set_sensitive (false);
2094         rec_enable_button->set_sensitive (false);
2095         solo_isolated_led->set_sensitive (false);
2096         solo_safe_led->set_sensitive (false);
2097         monitor_input_button->set_sensitive (false);
2098         monitor_disk_button->set_sensitive (false);
2099         _comment_button.set_sensitive (false);
2100
2101         if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2102                 output_button.set_sensitive (false);
2103         }
2104
2105         reset_strip_style ();
2106 }
2107
2108 void
2109 MixerStrip::revert_to_default_display ()
2110 {
2111         drop_send ();
2112
2113         set_current_delivery (_route->main_outs ());
2114
2115         gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2116         gain_meter().setup_meters ();
2117
2118         panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2119         update_panner_choices();
2120         panner_ui().setup_pan ();
2121         panner_ui().set_send_drawing_mode (false);
2122
2123         if (has_audio_outputs ()) {
2124                 panners.show_all ();
2125         } else {
2126                 panners.hide_all ();
2127         }
2128
2129         reset_strip_style ();
2130 }
2131
2132 void
2133 MixerStrip::set_button_names ()
2134 {
2135         switch (_width) {
2136         case Wide:
2137                 mute_button->set_text (_("Mute"));
2138                 monitor_input_button->set_text (_("In"));
2139                 monitor_disk_button->set_text (_("Disk"));
2140                 if (monitor_section_button) {
2141                         monitor_section_button->set_text (_("Mon"));
2142                 }
2143
2144                 if (_route && _route->solo_safe_control()->solo_safe()) {
2145                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2146                 } else {
2147                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2148                 }
2149                 if (!Config->get_solo_control_is_listen_control()) {
2150                         solo_button->set_text (_("Solo"));
2151                 } else {
2152                         switch (Config->get_listen_position()) {
2153                         case AfterFaderListen:
2154                                 solo_button->set_text (_("AFL"));
2155                                 break;
2156                         case PreFaderListen:
2157                                 solo_button->set_text (_("PFL"));
2158                                 break;
2159                         }
2160                 }
2161                 solo_isolated_led->set_text (_("Iso"));
2162                 solo_safe_led->set_text (S_("SoloLock|Lock"));
2163                 break;
2164
2165         default:
2166                 mute_button->set_text (S_("Mute|M"));
2167                 monitor_input_button->set_text (S_("MonitorInput|I"));
2168                 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2169                 if (monitor_section_button) {
2170                         monitor_section_button->set_text (S_("Mon|O"));
2171                 }
2172
2173                 if (_route && _route->solo_safe_control()->solo_safe()) {
2174                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2175                 } else {
2176                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2177                 }
2178                 if (!Config->get_solo_control_is_listen_control()) {
2179                         solo_button->set_text (S_("Solo|S"));
2180                 } else {
2181                         switch (Config->get_listen_position()) {
2182                         case AfterFaderListen:
2183                                 solo_button->set_text (S_("AfterFader|A"));
2184                                 break;
2185                         case PreFaderListen:
2186                                 solo_button->set_text (S_("Prefader|P"));
2187                                 break;
2188                         }
2189                 }
2190
2191                 solo_isolated_led->set_text (S_("SoloIso|I"));
2192                 solo_safe_led->set_text (S_("SoloLock|L"));
2193                 break;
2194         }
2195
2196         if (_route) {
2197                 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2198         } else {
2199                 meter_point_button.set_text ("");
2200         }
2201 }
2202
2203 PluginSelector*
2204 MixerStrip::plugin_selector()
2205 {
2206         return _mixer.plugin_selector();
2207 }
2208
2209 void
2210 MixerStrip::hide_things ()
2211 {
2212         processor_box.hide_things ();
2213 }
2214
2215 bool
2216 MixerStrip::input_active_button_press (GdkEventButton*)
2217 {
2218         /* nothing happens on press */
2219         return true;
2220 }
2221
2222 bool
2223 MixerStrip::input_active_button_release (GdkEventButton* ev)
2224 {
2225         boost::shared_ptr<MidiTrack> mt = midi_track ();
2226
2227         if (!mt) {
2228                 return true;
2229         }
2230
2231         boost::shared_ptr<RouteList> rl (new RouteList);
2232
2233         rl->push_back (route());
2234
2235         _session->set_exclusive_input_active (rl, !mt->input_active(),
2236                                               Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2237
2238         return true;
2239 }
2240
2241 void
2242 MixerStrip::midi_input_status_changed ()
2243 {
2244         if (midi_input_enable_button) {
2245                 boost::shared_ptr<MidiTrack> mt = midi_track ();
2246                 assert (mt);
2247                 midi_input_enable_button->set_active (mt->input_active ());
2248         }
2249 }
2250
2251 string
2252 MixerStrip::state_id () const
2253 {
2254         return string_compose ("strip %1", _route->id().to_s());
2255 }
2256
2257 void
2258 MixerStrip::parameter_changed (string p)
2259 {
2260         if (p == _visibility.get_state_name()) {
2261                 /* The user has made changes to the mixer strip visibility, so get
2262                    our VisibilityGroup to reflect these changes in our widgets.
2263                 */
2264                 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2265         } else if (p == "track-name-number") {
2266                 name_changed ();
2267         } else if (p == "use-monitor-bus") {
2268                 if (monitor_section_button) {
2269                         if (mute_button->get_parent()) {
2270                                 mute_button->get_parent()->remove(*mute_button);
2271                         }
2272                         if (monitor_section_button->get_parent()) {
2273                                 monitor_section_button->get_parent()->remove(*monitor_section_button);
2274                         }
2275                         if (Config->get_use_monitor_bus ()) {
2276                                 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2277                                 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2278                                 mute_button->show();
2279                                 monitor_section_button->show();
2280                         } else {
2281                                 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2282                                 mute_button->show();
2283                         }
2284                 }
2285         } else if (p == "track-name-number") {
2286                 update_track_number_visibility();
2287         }
2288 }
2289
2290 /** Called to decide whether the solo isolate / solo lock button visibility should
2291  *  be overridden from that configured by the user.  We do this for the master bus.
2292  *
2293  *  @return optional value that is present if visibility state should be overridden.
2294  */
2295 boost::optional<bool>
2296 MixerStrip::override_solo_visibility () const
2297 {
2298         if (_route && _route->is_master ()) {
2299                 return boost::optional<bool> (false);
2300         }
2301
2302         return boost::optional<bool> ();
2303 }
2304
2305 void
2306 MixerStrip::add_input_port (DataType t)
2307 {
2308         _route->input()->add_port ("", this, t);
2309 }
2310
2311 void
2312 MixerStrip::add_output_port (DataType t)
2313 {
2314         _route->output()->add_port ("", this, t);
2315 }
2316
2317 void
2318 MixerStrip::route_active_changed ()
2319 {
2320         reset_strip_style ();
2321 }
2322
2323 void
2324 MixerStrip::copy_processors ()
2325 {
2326         processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2327 }
2328
2329 void
2330 MixerStrip::cut_processors ()
2331 {
2332         processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2333 }
2334
2335 void
2336 MixerStrip::paste_processors ()
2337 {
2338         processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2339 }
2340
2341 void
2342 MixerStrip::select_all_processors ()
2343 {
2344         processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2345 }
2346
2347 void
2348 MixerStrip::deselect_all_processors ()
2349 {
2350         processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2351 }
2352
2353 bool
2354 MixerStrip::delete_processors ()
2355 {
2356         return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2357 }
2358
2359 void
2360 MixerStrip::toggle_processors ()
2361 {
2362         processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2363 }
2364
2365 void
2366 MixerStrip::ab_plugins ()
2367 {
2368         processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2369 }
2370
2371 bool
2372 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2373 {
2374         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2375                 return false;
2376         }
2377         if (ev->button == 3) {
2378                 popup_level_meter_menu (ev);
2379                 return true;
2380         }
2381
2382         return false;
2383 }
2384
2385 void
2386 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2387 {
2388         using namespace Gtk::Menu_Helpers;
2389
2390         Gtk::Menu* m = manage (new Menu);
2391         MenuList& items = m->items ();
2392
2393         RadioMenuItem::Group group;
2394
2395         _suspend_menu_callbacks = true;
2396         add_level_meter_item_point (items, group, _("Input"), MeterInput);
2397         add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2398         add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2399         add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2400         add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2401
2402         if (gpm.meter_channels().n_audio() == 0) {
2403                 m->popup (ev->button, ev->time);
2404                 _suspend_menu_callbacks = false;
2405                 return;
2406         }
2407
2408         RadioMenuItem::Group tgroup;
2409         items.push_back (SeparatorElem());
2410
2411         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2412         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2413         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms),  MeterKrms);
2414         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2415         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2416         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2417         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2418         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2419         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2420         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2421         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU),  MeterVU);
2422
2423         int _strip_type;
2424         if (_route->is_master()) {
2425                 _strip_type = 4;
2426         }
2427         else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2428                         && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2429                 /* non-master bus */
2430                 _strip_type = 3;
2431         }
2432         else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2433                 _strip_type = 2;
2434         }
2435         else {
2436                 _strip_type = 1;
2437         }
2438
2439         MeterType cmt = _route->meter_type();
2440         const std::string cmn = ArdourMeter::meter_type_string(cmt);
2441
2442         items.push_back (SeparatorElem());
2443         items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2444                                 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2445         items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2446                                 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2447         items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2448                                 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2449
2450         m->popup (ev->button, ev->time);
2451         _suspend_menu_callbacks = false;
2452 }
2453
2454 void
2455 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2456                 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2457 {
2458         using namespace Menu_Helpers;
2459
2460         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2461         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2462         i->set_active (_route->meter_point() == point);
2463 }
2464
2465 void
2466 MixerStrip::set_meter_point (MeterPoint p)
2467 {
2468         if (_suspend_menu_callbacks) return;
2469         _route->set_meter_point (p);
2470 }
2471
2472 void
2473 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2474                 RadioMenuItem::Group& group, string const & name, MeterType type)
2475 {
2476         using namespace Menu_Helpers;
2477
2478         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2479         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2480         i->set_active (_route->meter_type() == type);
2481 }
2482
2483 void
2484 MixerStrip::set_meter_type (MeterType t)
2485 {
2486         if (_suspend_menu_callbacks) return;
2487         gpm.set_type (t);
2488 }
2489
2490 void
2491 MixerStrip::vca_menu_toggle (CheckMenuItem* menuitem, uint32_t n)
2492 {
2493         if (!_route) {
2494                 return;
2495         }
2496
2497         boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2498
2499         if (!vca) {
2500                 return;
2501         }
2502
2503         if (!_selected) {
2504                 /* if this strip is not selected, add it before carrying out
2505                    changes to assignment. the user probably didn't notice
2506                    that they were clicking on an unselected track.
2507                 */
2508                 _mixer.select_strip (*this);
2509         }
2510
2511         if (!menuitem->get_active()) {
2512                 _mixer.do_vca_unassign (vca);
2513         } else {
2514                 _mixer.do_vca_assign (vca);
2515         }
2516 }
2517
2518 void
2519 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2520 {
2521         if (!vca || !_route) {
2522                 return;
2523         }
2524
2525         _route->assign (vca);
2526 }
2527
2528 void
2529 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2530 {
2531         if (!_route) {
2532                 return;
2533         }
2534
2535         _route->unassign (vca);
2536 }
2537
2538 bool
2539 MixerStrip::vca_button_release (GdkEventButton* ev)
2540 {
2541         using namespace Gtk::Menu_Helpers;
2542
2543         if (!_session) {
2544                 return false;
2545         }
2546
2547         /* primary click only */
2548
2549         if (ev->button != 1) {
2550                 return false;
2551         }
2552
2553         if (!_route) {
2554                 /* no route - nothing to do */
2555                 return false;
2556         }
2557
2558         VCAList vcas (_session->vca_manager().vcas());
2559
2560         if (vcas.empty()) {
2561                 /* XXX should probably show a message saying "No VCA masters" */
2562                 return true;
2563         }
2564
2565         Menu* menu = new Menu;
2566         MenuList& items = menu->items();
2567
2568         items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2569
2570         for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2571                 items.push_back (CheckMenuElem ((*v)->name()));
2572                 CheckMenuItem* item = dynamic_cast<CheckMenuItem*> (&items.back());
2573                 if (_route->slaved_to (*v)) {
2574                         item->set_active (true);
2575                 }
2576                 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2577         }
2578
2579         menu->popup (1, ev->time);
2580
2581         return true;
2582 }
2583
2584 void
2585 MixerStrip::update_track_number_visibility ()
2586 {
2587         DisplaySuspender ds;
2588         bool show_label = _session->config.get_track_name_number();
2589
2590         if (_route && _route->is_master()) {
2591                 show_label = false;
2592         }
2593
2594         if (show_label) {
2595                 number_label.show ();
2596                 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2597                 // except the width of the number label is subtracted from the name-hbox, so we
2598                 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2599                 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2600                 if (tnw & 1) --tnw;
2601                 number_label.set_size_request(tnw, -1);
2602                 number_label.show ();
2603                 //name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
2604         } else {
2605                 number_label.hide ();
2606         }
2607 }