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