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