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