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