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