Fix some memory leaks in the plugin menu.
[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 <algorithm>
21
22 #include <sigc++/bind.h>
23
24 #include "pbd/convert.h"
25 #include "pbd/enumwriter.h"
26 #include "pbd/replace_all.h"
27
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/choice.h>
31 #include <gtkmm2ext/stop_signal.h>
32 #include <gtkmm2ext/doi.h>
33 #include <gtkmm2ext/slider_controller.h>
34 #include <gtkmm2ext/bindable_button.h>
35
36 #include "ardour/ardour.h"
37 #include "ardour/amp.h"
38 #include "ardour/session.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/route.h"
41 #include "ardour/route_group.h"
42 #include "ardour/audio_track.h"
43 #include "ardour/audio_diskstream.h"
44 #include "ardour/panner.h"
45 #include "ardour/send.h"
46 #include "ardour/processor.h"
47 #include "ardour/profile.h"
48 #include "ardour/ladspa_plugin.h"
49 #include "ardour/user_bundle.h"
50
51 #include "ardour_ui.h"
52 #include "ardour_dialog.h"
53 #include "mixer_strip.h"
54 #include "mixer_ui.h"
55 #include "keyboard.h"
56 #include "public_editor.h"
57 #include "send_ui.h"
58 #include "io_selector.h"
59 #include "utils.h"
60 #include "gui_thread.h"
61 #include "route_group_menu.h"
62
63 #include "i18n.h"
64
65 using namespace sigc;
66 using namespace ARDOUR;
67 using namespace PBD;
68 using namespace Gtk;
69 using namespace Gtkmm2ext;
70 using namespace std;
71
72 sigc::signal<void,boost::shared_ptr<Route> > MixerStrip::SwitchIO;
73
74 int MixerStrip::scrollbar_height = 0;
75
76 MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, bool in_mixer)
77         : AxisView(sess)
78         , RouteUI (sess)
79         ,_mixer(mx)
80         , _mixer_owned (in_mixer)
81         , processor_box (sess, sigc::mem_fun(*this, &MixerStrip::plugin_selector), mx.selection(), this, in_mixer)
82         , gpm (sess)
83         , panners (sess)
84         , _mono_button (_("Mono"))
85         , button_table (3, 2)
86         , middle_button_table (1, 2)
87         , bottom_button_table (1, 2)
88         , meter_point_label (_("pre"))
89         , comment_button (_("Comments"))
90 {
91         init ();
92
93         if (!_mixer_owned) {
94                 /* the editor mixer strip: don't destroy it every time
95                    the underlying route goes away.
96                 */
97
98                 self_destruct = false;
99         }
100 }
101
102 MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
103         : AxisView(sess)
104         , RouteUI (sess)
105         ,_mixer(mx)
106         , _mixer_owned (in_mixer)
107         , processor_box (sess, sigc::mem_fun(*this, &MixerStrip::plugin_selector), mx.selection(), this, in_mixer)
108         , gpm (sess)
109         , panners (sess)
110         , button_table (3, 2)
111         , middle_button_table (1, 2)
112         , bottom_button_table (1, 2)
113         , meter_point_label (_("pre"))
114         , comment_button (_("Comments"))
115 {
116         init ();
117         set_button_names ();
118         set_route (rt);
119 }
120
121 void
122 MixerStrip::init ()
123 {
124         input_selector = 0;
125         output_selector = 0;
126         group_menu = 0;
127         _marked_for_display = false;
128         route_ops_menu = 0;
129         ignore_comment_edit = false;
130         ignore_toggle = false;
131         comment_window = 0;
132         comment_area = 0;
133         _width_owner = 0;
134         spacer = 0;
135
136         Gtk::Image* img;
137
138         img = manage (new Gtk::Image (::get_icon("strip_width")));
139         img->show ();
140
141         width_button.add (*img);
142
143         img = manage (new Gtk::Image (::get_icon("hide")));
144         img->show ();
145
146         hide_button.add (*img);
147
148         input_label.set_text (_("Input"));
149         ARDOUR_UI::instance()->set_tip (&input_button, _("Button 1 to choose inputs from a port matrix, button 3 to select inputs from a menu"), "");
150         input_button.add (input_label);
151         input_button.set_name ("MixerIOButton");
152         input_label.set_name ("MixerIOButtonLabel");
153         Gtkmm2ext::set_size_request_to_display_given_text (input_button, "longest label", 4, 4);
154
155         output_label.set_text (_("Output"));
156         ARDOUR_UI::instance()->set_tip (&output_button, _("Button 1 to choose outputs from a port matrix, button 3 to select inputs from a menu"), "");
157         output_button.add (output_label);
158         output_button.set_name ("MixerIOButton");
159         output_label.set_name ("MixerIOButtonLabel");
160         Gtkmm2ext::set_size_request_to_display_given_text (output_button, "longest label", 4, 4);
161
162         ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Select metering point"), "");
163         meter_point_button.add (meter_point_label);
164         meter_point_button.set_name ("MixerStripMeterPreButton");
165         meter_point_label.set_name ("MixerStripMeterPreButton");
166
167         /* TRANSLATORS: this string should be longest of the strings
168            used to describe meter points. In english, it's "input".
169         */
170         set_size_request_to_display_given_text (meter_point_button, _("tupni"), 5, 5);
171
172         bottom_button_table.attach (meter_point_button, 1, 2, 0, 1);
173
174         meter_point_button.signal_button_press_event().connect (mem_fun (gpm, &GainMeter::meter_press), false);
175         meter_point_button.signal_button_release_event().connect (mem_fun (gpm, &GainMeter::meter_release), false);
176
177         hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
178
179         mute_button->set_name ("MixerMuteButton");
180         solo_button->set_name ("MixerSoloButton");
181
182         button_table.set_homogeneous (true);
183         button_table.set_spacings (0);
184
185         button_table.attach (name_button, 0, 2, 0, 1);
186         button_table.attach (input_button, 0, 2, 1, 2);
187
188         middle_button_table.set_homogeneous (true);
189         middle_button_table.set_spacings (0);
190         middle_button_table.attach (*mute_button, 0, 1, 0, 1);
191         middle_button_table.attach (*solo_button, 1, 2, 0, 1);
192
193         bottom_button_table.set_col_spacings (0);
194         bottom_button_table.set_homogeneous (true);
195         bottom_button_table.attach (group_button, 0, 1, 0, 1);
196
197         name_button.add (name_label);
198         name_button.set_name ("MixerNameButton");
199         Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
200
201         name_label.set_name ("MixerNameButtonLabel");
202         ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
203         group_button.add (group_label);
204         group_button.set_name ("MixerGroupButton");
205         group_label.set_name ("MixerGroupButtonLabel");
206
207         comment_button.set_name ("MixerCommentButton");
208
209         comment_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked));
210
211         global_vpacker.set_border_width (0);
212         global_vpacker.set_spacing (0);
213
214         width_button.set_name ("MixerWidthButton");
215         hide_button.set_name ("MixerHideButton");
216         top_event_box.set_name ("MixerTopEventBox");
217
218         width_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::width_clicked));
219         hide_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::hide_clicked));
220
221         width_hide_box.pack_start (width_button, false, true);
222         width_hide_box.pack_start (top_event_box, true, true);
223         width_hide_box.pack_end (hide_button, false, true);
224         gain_meter_alignment.set_padding(0, 4, 0, 0);
225         gain_meter_alignment.add(gpm);
226
227         whvbox.pack_start (width_hide_box, true, true);
228
229         global_vpacker.pack_start (whvbox, Gtk::PACK_SHRINK);
230         global_vpacker.pack_start (button_table,Gtk::PACK_SHRINK);
231         global_vpacker.pack_start (processor_box, true, true);
232         global_vpacker.pack_start (middle_button_table,Gtk::PACK_SHRINK);
233         global_vpacker.pack_start (gain_meter_alignment,Gtk::PACK_SHRINK);
234         global_vpacker.pack_start (bottom_button_table,Gtk::PACK_SHRINK);
235         if (!is_midi_track()) {
236                 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
237         }
238         global_vpacker.pack_start (_mono_button, Gtk::PACK_SHRINK);
239         global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
240         global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
241
242         global_frame.add (global_vpacker);
243         global_frame.set_shadow_type (Gtk::SHADOW_IN);
244         global_frame.set_name ("BaseFrame");
245
246         add (global_frame);
247
248         /* force setting of visible selected status */
249
250         _selected = true;
251         set_selected (false);
252
253         _packed = false;
254         _embedded = false;
255
256         _session.engine().Stopped.connect (mem_fun(*this, &MixerStrip::engine_stopped));
257         _session.engine().Running.connect (mem_fun(*this, &MixerStrip::engine_running));
258
259         input_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::input_press), false);
260         output_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::output_press), false);
261
262         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
263         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
264         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
265         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
266
267         /* we don't need this if its not an audio track, but we don't know that yet and it doesn't
268            hurt (much).
269         */
270
271         rec_enable_button->set_name ("MixerRecordEnableButton");
272         rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
273         rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
274
275         /* ditto for this button and busses */
276
277         show_sends_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::show_sends_press), false);
278         show_sends_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::show_sends_release));
279
280         name_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::name_button_button_press), false);
281         group_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::select_route_group), false);
282
283         _width = (Width) -1;
284
285         /* start off as a passthru strip. we'll correct this, if necessary,
286            in update_diskstream_display().
287         */
288
289         /* start off as a passthru strip. we'll correct this, if necessary,
290            in update_diskstream_display().
291         */
292
293         if (is_midi_track())
294                 set_name ("MidiTrackStripBase");
295         else
296                 set_name ("AudioTrackStripBase");
297
298         add_events (Gdk::BUTTON_RELEASE_MASK|
299                     Gdk::ENTER_NOTIFY_MASK|
300                     Gdk::LEAVE_NOTIFY_MASK|
301                     Gdk::KEY_PRESS_MASK|
302                     Gdk::KEY_RELEASE_MASK);
303
304         set_flags (get_flags() | Gtk::CAN_FOCUS);
305
306         SwitchIO.connect (mem_fun (*this, &MixerStrip::switch_io));
307 }
308
309 MixerStrip::~MixerStrip ()
310 {
311         GoingAway(); /* EMIT_SIGNAL */
312
313         delete input_selector;
314         delete output_selector;
315         delete comment_window;
316 }
317
318 void
319 MixerStrip::set_route (boost::shared_ptr<Route> rt)
320 {
321         if (rec_enable_button->get_parent()) {
322                 button_table.remove (*rec_enable_button);
323         }
324
325         if (show_sends_button->get_parent()) {
326                 button_table.remove (*show_sends_button);
327         }
328
329         RouteUI::set_route (rt);
330
331         delete input_selector;
332         input_selector = 0;
333
334         delete output_selector;
335         output_selector = 0;
336
337         boost::shared_ptr<Send> send;
338
339         if (_current_delivery && (send = boost::dynamic_pointer_cast<Send>(_current_delivery))) {
340                 send->set_metering (false);
341         }
342
343         _current_delivery = _route->main_outs ();
344
345         panners.set_panner (rt->main_outs()->panner());
346         gpm.set_controls (rt, rt->shared_peak_meter(), rt->amp());
347         processor_box.set_route (rt);
348
349         if (set_color_from_route()) {
350                 set_color (unique_random_color());
351         }
352
353         if (_mixer_owned && (route()->is_master() || route()->is_control())) {
354
355                 if (scrollbar_height == 0) {
356                         HScrollbar scrollbar;
357                         Gtk::Requisition requisition(scrollbar.size_request ());
358                         scrollbar_height = requisition.height;
359                 }
360
361                 spacer = manage (new EventBox);
362                 spacer->set_size_request (-1, scrollbar_height);
363                 global_vpacker.pack_start (*spacer, false, false);
364         }
365
366         if (is_audio_track()) {
367
368                 boost::shared_ptr<AudioTrack> at = audio_track();
369
370                 connections.push_back (at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen)));
371
372                 button_table.attach (*rec_enable_button, 0, 2, 2, 3);
373                 rec_enable_button->set_sensitive (_session.writable());
374                 rec_enable_button->show();
375
376         } else if (!is_track()) {
377                 /* non-master bus */
378
379                 if (!_route->is_master()) {
380                         button_table.attach (*show_sends_button, 0, 2, 2, 3);
381                         show_sends_button->show();
382                 }
383         }
384
385         if (_route->phase_invert()) {
386                 name_label.set_text (X_("Ø ") + name_label.get_text());
387         } else {
388                 name_label.set_text (_route->name());
389         }
390
391         _mono_button.set_name ("MixerMonoButton");
392         _mono_button.signal_clicked().connect (mem_fun (*this, &MixerStrip::mono_button_clicked));
393
394         switch (_route->meter_point()) {
395         case MeterInput:
396                 meter_point_label.set_text (_("input"));
397                 break;
398
399         case MeterPreFader:
400                 meter_point_label.set_text (_("pre"));
401                 break;
402
403         case MeterPostFader:
404                 meter_point_label.set_text (_("post"));
405                 break;
406         }
407
408         delete route_ops_menu;
409         route_ops_menu = 0;
410
411         ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment().empty() ?
412                                                    _("Click to Add/Edit Comments"):
413                                                    _route->comment());
414
415         connections.push_back (_route->meter_change.connect (
416                         mem_fun(*this, &MixerStrip::meter_changed)));
417         connections.push_back (_route->input()->changed.connect (
418                         mem_fun(*this, &MixerStrip::input_changed)));
419         connections.push_back (_route->output()->changed.connect (
420                         mem_fun(*this, &MixerStrip::output_changed)));
421         connections.push_back (_route->route_group_changed.connect (
422                         mem_fun(*this, &MixerStrip::route_group_changed)));
423
424         if (_route->panner()) {
425                 connections.push_back (_route->panner()->Changed.connect (
426                         mem_fun(*this, &MixerStrip::connect_to_pan)));
427         }
428
429         if (is_audio_track()) {
430                 connections.push_back (audio_track()->DiskstreamChanged.connect (
431                         mem_fun(*this, &MixerStrip::diskstream_changed)));
432         }
433
434         connections.push_back (_route->NameChanged.connect (
435                         mem_fun(*this, &RouteUI::name_changed)));
436         connections.push_back (_route->comment_changed.connect (
437                         mem_fun(*this, &MixerStrip::comment_changed)));
438         connections.push_back (_route->gui_changed.connect (
439                         mem_fun(*this, &MixerStrip::route_gui_changed)));
440
441         set_stuff_from_route ();
442
443         /* now force an update of all the various elements */
444
445         processor_box.update();
446         mute_changed (0);
447         solo_changed (0);
448         name_changed ();
449         comment_changed (0);
450         route_group_changed (0);
451
452         connect_to_pan ();
453
454         panners.setup_pan ();
455
456         update_diskstream_display ();
457         update_input_display ();
458         update_output_display ();
459
460         add_events (Gdk::BUTTON_RELEASE_MASK);
461
462         processor_box.show();
463
464         if (!route()->is_master() && !route()->is_control()) {
465                 /* we don't allow master or control routes to be hidden */
466                 hide_button.show();
467         }
468
469         width_button.show();
470         width_hide_box.show();
471         whvbox.show ();
472         global_frame.show();
473         global_vpacker.show();
474         button_table.show();
475         middle_button_table.show();
476         bottom_button_table.show();
477         processor_box.show_all ();
478         gpm.show_all ();
479         panners.show_all ();
480         gain_meter_alignment.show ();
481         gain_unit_button.show();
482         gain_unit_label.show();
483         meter_point_button.show();
484         meter_point_label.show();
485         diskstream_button.show();
486         diskstream_label.show();
487         input_button.show();
488         input_label.show();
489         output_button.show();
490         output_label.show();
491         name_label.show();
492         name_button.show();
493         comment_button.show();
494         group_button.show();
495         group_label.show();
496
497         show ();
498 }
499
500 void
501 MixerStrip::set_stuff_from_route ()
502 {
503         XMLProperty *prop;
504
505         ensure_xml_node ();
506
507         /* if width is not set, it will be set by the MixerUI or editor */
508
509         if ((prop = xml_node->property ("strip-width")) != 0) {
510                 set_width_enum (Width (string_2_enum (prop->value(), _width)), this);
511         }
512
513         if ((prop = xml_node->property ("shown-mixer")) != 0) {
514                 if (prop->value() == "no") {
515                         _marked_for_display = false;
516                 } else {
517                         _marked_for_display = true;
518                 }
519         } else {
520                 /* backwards compatibility */
521                 _marked_for_display = true;
522         }
523 }
524
525 void
526 MixerStrip::set_width_enum (Width w, void* owner)
527 {
528         /* always set the gpm width again, things may be hidden */
529
530         gpm.set_width (w);
531         panners.set_width (w);
532         processor_box.set_width (w);
533
534         boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
535
536         _width_owner = owner;
537
538         ensure_xml_node ();
539
540         _width = w;
541
542         if (_width_owner == this) {
543                 xml_node->add_property ("strip-width", enum_2_string (_width));
544         }
545
546         set_button_names ();
547
548         switch (w) {
549         case Wide:
550                 if (show_sends_button)  {
551                         ((Gtk::Label*)show_sends_button->get_child())->set_text (_("Sends"));
552                 }
553
554                 if (_route->comment() == "") {
555                         comment_button.unset_bg (STATE_NORMAL);
556                         ((Gtk::Label*)comment_button.get_child())->set_text (_("Comments"));
557                 } else {
558                         comment_button.modify_bg (STATE_NORMAL, color());
559                         ((Gtk::Label*)comment_button.get_child())->set_text (_("*Comments*"));
560                 }
561
562                 ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (
563                                 gpm.astyle_string(gain_automation->automation_style()));
564                 ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (
565                                 gpm.astate_string(gain_automation->automation_state()));
566
567                 if (_route->panner()) {
568                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
569                                         panners.astyle_string(_route->panner()->automation_style()));
570                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
571                                         panners.astate_string(_route->panner()->automation_state()));
572                 }
573
574                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "long", 2, 2);
575                 set_size_request (-1, -1);
576                 break;
577
578         case Narrow:
579                 if (show_sends_button) {
580                         ((Gtk::Label*)show_sends_button->get_child())->set_text (_("Snd"));
581                 }
582
583                 if (_route->comment() == "") {
584                        comment_button.unset_bg (STATE_NORMAL);
585                        ((Gtk::Label*)comment_button.get_child())->set_text (_("Cmt"));
586                 } else {
587                        comment_button.modify_bg (STATE_NORMAL, color());
588                        ((Gtk::Label*)comment_button.get_child())->set_text (_("*Cmt*"));
589                 }
590
591                 ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (
592                                 gpm.short_astyle_string(gain_automation->automation_style()));
593                 ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (
594                                 gpm.short_astate_string(gain_automation->automation_state()));
595
596                 if (_route->panner()) {
597                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
598                         panners.short_astyle_string(_route->panner()->automation_style()));
599                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
600                         panners.short_astate_string(_route->panner()->automation_state()));
601                 }
602
603                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
604                 set_size_request (max (50, gpm.get_gm_width()), -1);
605                 break;
606         }
607         update_input_display ();
608         update_output_display ();
609         route_group_changed (0);
610         name_changed ();
611         WidthChanged ();
612 }
613
614 void
615 MixerStrip::set_packed (bool yn)
616 {
617         _packed = yn;
618
619         ensure_xml_node ();
620
621         if (_packed) {
622                 xml_node->add_property ("shown-mixer", "yes");
623         } else {
624                 xml_node->add_property ("shown-mixer", "no");
625         }
626 }
627
628
629 gint
630 MixerStrip::output_press (GdkEventButton *ev)
631 {
632         using namespace Menu_Helpers;
633         if (!_session.engine().connected()) {
634                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
635                 msg.run ();
636                 return true;
637         }
638
639         MenuList& citems = output_menu.items();
640         switch (ev->button) {
641
642         case 1:
643                 edit_output_configuration ();
644                 break;
645
646         case 3:
647         {
648                 output_menu.set_name ("ArdourContextMenu");
649                 citems.clear ();
650                 output_menu_bundles.clear ();
651
652                 citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
653                 citems.push_back (SeparatorElem());
654
655                 ARDOUR::BundleList current = _route->output()->bundles_connected ();
656
657                 boost::shared_ptr<ARDOUR::BundleList> b = _session.bundles ();
658
659                 /* give user bundles first chance at being in the menu */
660                 
661                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
662                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
663                                 maybe_add_bundle_to_output_menu (*i, current);
664                         }
665                 }
666
667                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
668                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
669                                 maybe_add_bundle_to_output_menu (*i, current);
670                         }
671                 }
672                 
673                 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
674                 for (ARDOUR::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
675                         maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
676                 }
677
678                 if (citems.size() == 2) {
679                         /* no routes added; remove the separator */
680                         citems.pop_back ();
681                 }
682
683                 output_menu.popup (1, ev->time);
684                 break;
685         }
686
687         default:
688                 break;
689         }
690         return TRUE;
691 }
692
693 void
694 MixerStrip::edit_output_configuration ()
695 {
696         if (output_selector == 0) {
697                 output_selector = new IOSelectorWindow (_session, _route->output());
698         }
699
700         if (output_selector->is_visible()) {
701                 output_selector->get_toplevel()->get_window()->raise();
702         } else {
703                 output_selector->present ();
704         }
705 }
706
707 void
708 MixerStrip::edit_input_configuration ()
709 {
710         if (input_selector == 0) {
711                 input_selector = new IOSelectorWindow (_session, _route->input());
712         }
713
714         if (input_selector->is_visible()) {
715                 input_selector->get_toplevel()->get_window()->raise();
716         } else {
717                 input_selector->present ();
718         }
719 }
720
721 gint
722 MixerStrip::input_press (GdkEventButton *ev)
723 {
724         using namespace Menu_Helpers;
725
726         MenuList& citems = input_menu.items();
727         input_menu.set_name ("ArdourContextMenu");
728         citems.clear();
729
730         if (!_session.engine().connected()) {
731                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
732                 msg.run ();
733                 return true;
734         }
735
736         switch (ev->button) {
737
738         case 1:
739                 edit_input_configuration ();
740                 break;
741
742         case 3:
743         {
744                 citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
745                 citems.push_back (SeparatorElem());
746                 input_menu_bundles.clear ();
747
748                 ARDOUR::BundleList current = _route->input()->bundles_connected ();
749
750                 boost::shared_ptr<ARDOUR::BundleList> b = _session.bundles ();
751
752                 /* give user bundles first chance at being in the menu */
753                 
754                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
755                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
756                                 maybe_add_bundle_to_input_menu (*i, current);
757                         }
758                 }
759
760                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
761                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
762                                 maybe_add_bundle_to_input_menu (*i, current);
763                         }
764                 }
765                 
766                 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
767                 for (ARDOUR::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
768                         maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
769                 }
770
771                 if (citems.size() == 2) {
772                         /* no routes added; remove the separator */
773                         citems.pop_back ();
774                 }
775
776                 input_menu.popup (1, ev->time);
777                 break;
778         }
779         default:
780                 break;
781         }
782         return TRUE;
783 }
784
785 void
786 MixerStrip::bundle_input_toggled (boost::shared_ptr<ARDOUR::Bundle> c)
787 {
788         if (ignore_toggle) {
789                 return;
790         }
791
792         ARDOUR::BundleList current = _route->input()->bundles_connected ();
793
794         if (std::find (current.begin(), current.end(), c) == current.end()) {
795                 _route->input()->connect_ports_to_bundle (c, this);
796         } else {
797                 _route->input()->disconnect_ports_from_bundle (c, this);
798         }
799 }
800
801 void
802 MixerStrip::bundle_output_toggled (boost::shared_ptr<ARDOUR::Bundle> c)
803 {
804         if (ignore_toggle) {
805                 return;
806         }
807
808         ARDOUR::BundleList current = _route->output()->bundles_connected ();
809
810         if (std::find (current.begin(), current.end(), c) == current.end()) {
811                 _route->output()->connect_ports_to_bundle (c, this);
812         } else {
813                 _route->output()->disconnect_ports_from_bundle (c, this);
814         }
815 }
816
817 void
818 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const & current)
819 {
820         using namespace Menu_Helpers;
821
822         if (b->ports_are_outputs() == false ||
823             route()->input()->default_type() != b->type() ||
824             b->nchannels() != _route->n_inputs().get (b->type ())) {
825
826                 return;
827         }
828
829         list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
830         while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
831                 ++i;
832         }
833
834         if (i != input_menu_bundles.end()) {
835                 return;
836         }
837
838         input_menu_bundles.push_back (b);
839
840         MenuList& citems = input_menu.items();
841
842         std::string n = b->name ();
843         replace_all (n, "_", " ");
844
845         citems.push_back (CheckMenuElem (n, bind (mem_fun(*this, &MixerStrip::bundle_input_toggled), b)));
846
847         if (std::find (current.begin(), current.end(), b) != current.end()) {
848                 ignore_toggle = true;
849                 dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
850                 ignore_toggle = false;
851         }
852 }
853
854 void
855 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const & current)
856 {
857         using namespace Menu_Helpers;
858
859         if (b->ports_are_inputs() == false ||
860             route()->output()->default_type() != b->type() ||
861             b->nchannels() != _route->n_outputs().get (b->type ())) {
862
863                 return;
864         }
865
866         list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
867         while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
868                 ++i;
869         }
870
871         if (i != output_menu_bundles.end()) {
872                 return;
873         }
874
875         output_menu_bundles.push_back (b);
876         
877         MenuList& citems = output_menu.items();
878
879         std::string n = b->name ();
880         replace_all (n, "_", " ");
881
882         citems.push_back (CheckMenuElem (n, bind (mem_fun(*this, &MixerStrip::bundle_output_toggled), b)));
883
884         if (std::find (current.begin(), current.end(), b) != current.end()) {
885                 ignore_toggle = true;
886                 dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
887                 ignore_toggle = false;
888         }
889 }
890
891 void
892 MixerStrip::update_diskstream_display ()
893 {
894         if (is_track()) {
895
896                 if (input_selector) {
897                         input_selector->hide_all ();
898                 }
899
900                 show_route_color ();
901
902         } else {
903
904                 show_passthru_color ();
905         }
906 }
907
908 void
909 MixerStrip::connect_to_pan ()
910 {
911         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan));
912
913         panstate_connection.disconnect ();
914         panstyle_connection.disconnect ();
915
916         if (!_route->panner()) {
917                 return;
918         }
919
920         boost::shared_ptr<ARDOUR::AutomationControl> pan_control
921                 = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
922                                 _route->panner()->data().control(Evoral::Parameter(PanAutomation)));
923
924         if (pan_control) {
925                 panstate_connection = pan_control->alist()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed));
926                 panstyle_connection = pan_control->alist()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed));
927         }
928
929         panners.pan_changed (this);
930 }
931
932
933 /*
934  * Output port labelling
935  * =====================
936  *
937  * Case 1: Each output has one connection, all connections are to system:playback_%i
938  *   out 1 -> system:playback_1
939  *   out 2 -> system:playback_2
940  *   out 3 -> system:playback_3
941  *   Display as: 1/2/3
942  *
943  * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
944  *   out 1 -> ardour:track_x/in 1
945  *   out 2 -> ardour:track_x/in 2
946  *   Display as: track_x
947  *
948  * Case 3: Each output has one connection, all connections are to Jack client "program x"
949  *   out 1 -> program x:foo
950  *   out 2 -> program x:foo
951  *   Display as: program x
952  *
953  * Case 4: No connections (Disconnected)
954  *   Display as: -
955  *
956  * Default case (unusual routing):
957  *   Display as: *number of connections*
958  *
959  * Tooltips
960  * ========
961  * .-----------------------------------------------.
962  * | Mixdown                                       |
963  * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
964  * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
965  * '-----------------------------------------------'
966  * .-----------------------------------------------.
967  * | Guitar SM58                                   |
968  * | Disconnected                                  |
969  * '-----------------------------------------------'
970  */
971
972 void
973 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
974 {
975         uint32_t io_count;
976         uint32_t io_index;
977         Port *port;
978         vector<string> connections;
979
980         uint32_t total_connection_count = 0;
981         uint32_t io_connection_count = 0;
982         uint32_t ardour_connection_count = 0;
983         uint32_t system_connection_count = 0;
984         uint32_t other_connection_count = 0;
985
986         ostringstream label;
987         string label_string;
988         char * label_cstr;
989
990         bool have_label = false;
991         bool each_io_has_one_connection = true;
992
993         string connection_name;
994         string ardour_track_name;
995         string other_connection_type;
996         string system_ports;
997         string system_port;
998
999         ostringstream tooltip;
1000         char * tooltip_cstr;
1001
1002         tooltip << route->name();
1003
1004         if (for_input) {
1005                 io_count = route->n_inputs().n_total();
1006         } else {
1007                 io_count = route->n_outputs().n_total();
1008         }
1009
1010         for (io_index = 0; io_index < io_count; ++io_index) {
1011                 if (for_input) {
1012                         port = route->input()->nth (io_index);
1013                 } else {
1014                         port = route->output()->nth (io_index);
1015                 }
1016
1017                 connections.clear ();
1018                 port->get_connections(connections);
1019                 io_connection_count = 0;
1020
1021                 if (!connections.empty()) {
1022                         for (vector<string>::iterator i = connections.begin(); i != connections.end(); ++i) {
1023                                 string& connection_name (*i);
1024
1025                                 if (io_connection_count == 0) {
1026                                         tooltip << endl << port->name().substr(port->name().find("/") + 1) << " -> " << connection_name;
1027                                 } else {
1028                                         tooltip << ", " << connection_name;
1029                                 }
1030
1031                                 if (connection_name.find("ardour:") == 0) {
1032                                         if (ardour_track_name.empty()) {
1033                                                 // "ardour:Master/in 1" -> "ardour:Master/"
1034                                                 string::size_type slash = connection_name.find("/");
1035                                                 if (slash != string::npos) {
1036                                                         ardour_track_name = connection_name.substr(0, slash + 1);
1037                                                 }
1038                                         }
1039
1040                                         if (connection_name.find(ardour_track_name) == 0) {
1041                                                 ++ardour_connection_count;
1042                                         }
1043                                 } else if (connection_name.find("system:") == 0) {
1044                                         if (for_input) {
1045                                                 // "system:capture_123" -> "123"
1046                                                 system_port = connection_name.substr(15);
1047                                         } else {
1048                                                 // "system:playback_123" -> "123"
1049                                                 system_port = connection_name.substr(16);
1050                                         }
1051
1052                                         if (system_ports.empty()) {
1053                                                 system_ports += system_port;
1054                                         } else {
1055                                                 system_ports += "/" + system_port;
1056                                         }
1057
1058                                         ++system_connection_count;
1059                                 } else {
1060                                         if (other_connection_type.empty()) {
1061                                                 // "jamin:in 1" -> "jamin:"
1062                                                 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1063                                         }
1064
1065                                         if (connection_name.find(other_connection_type) == 0) {
1066                                                 ++other_connection_count;
1067                                         }
1068                                 }
1069
1070                                 ++total_connection_count;
1071                                 ++io_connection_count;
1072                         }
1073                 }
1074
1075                 if (io_connection_count != 1) {
1076                         each_io_has_one_connection = false;
1077                 }
1078         }
1079
1080         if (total_connection_count == 0) {
1081                 tooltip << endl << _("Disconnected");
1082         }
1083
1084         tooltip_cstr = new char[tooltip.str().size() + 1];
1085         strcpy(tooltip_cstr, tooltip.str().c_str());
1086
1087         if (for_input) {
1088                 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1089         } else {
1090                 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1091         }
1092
1093         if (each_io_has_one_connection) {
1094                 if ((total_connection_count == ardour_connection_count)) {
1095                         // all connections are to the same track in ardour
1096                         // "ardour:Master/" -> "Master"
1097                         string::size_type slash = ardour_track_name.find("/");
1098                         if (slash != string::npos) {
1099                                 label << ardour_track_name.substr(7, slash - 7);
1100                                 have_label = true;
1101                         }
1102                 }
1103                 else if (total_connection_count == system_connection_count) {
1104                         // all connections are to system ports
1105                         label << system_ports;
1106                         have_label = true;
1107                 }
1108                 else if (total_connection_count == other_connection_count) {
1109                         // all connections are to the same external program eg jamin
1110                         // "jamin:" -> "jamin"
1111                         label << other_connection_type.substr(0, other_connection_type.size() - 1);
1112                         have_label = true;
1113                 }
1114         }
1115
1116         if (!have_label) {
1117                 if (total_connection_count == 0) {
1118                         // Disconnected
1119                         label << "-";
1120                 } else {
1121                         // Odd configuration
1122                         label << "*" << total_connection_count << "*";
1123                 }
1124         }
1125
1126         switch (width) {
1127         case Wide:
1128                 label_string = label.str().substr(0, 6);
1129                 break;
1130         case Narrow:
1131                 label_string = label.str().substr(0, 3);
1132                 break;
1133         }
1134
1135         label_cstr = new char[label_string.size() + 1];
1136         strcpy(label_cstr, label_string.c_str());
1137
1138         if (for_input) {
1139                 input_label.set_text (label_cstr);
1140         } else {
1141                 output_label.set_text (label_cstr);
1142         }
1143 }
1144
1145 void
1146 MixerStrip::update_input_display ()
1147 {
1148         update_io_button (_route, _width, true);
1149         panners.setup_pan ();
1150 }
1151
1152 void
1153 MixerStrip::update_output_display ()
1154 {
1155         update_io_button (_route, _width, false);
1156         gpm.setup_meters ();
1157         panners.setup_pan ();
1158 }
1159
1160 void
1161 MixerStrip::fast_update ()
1162 {
1163         gpm.update_meters ();
1164 }
1165
1166 void
1167 MixerStrip::diskstream_changed ()
1168 {
1169         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display));
1170 }
1171
1172 void
1173 MixerStrip::input_changed (IOChange /*change*/, void */*src*/)
1174 {
1175         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display));
1176         set_width_enum (_width, this);
1177 }
1178
1179 void
1180 MixerStrip::output_changed (IOChange /*change*/, void */*src*/)
1181 {
1182         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display));
1183         set_width_enum (_width, this);
1184 }
1185
1186
1187 void
1188 MixerStrip::comment_editor_done_editing()
1189 {
1190         string str =  comment_area->get_buffer()->get_text();
1191         if (_route->comment() != str) {
1192                 _route->set_comment (str, this);
1193
1194                 switch (_width) {
1195
1196                 case Wide:
1197                         if (! str.empty()) {
1198                                 comment_button.modify_bg (STATE_NORMAL, color());
1199                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("*Comments*"));
1200                         } else {
1201                                 comment_button.unset_bg (STATE_NORMAL);
1202                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("Comments"));
1203                         }
1204                         break;
1205
1206                 case Narrow:
1207                         if (! str.empty()) {
1208                                 comment_button.modify_bg (STATE_NORMAL, color());
1209                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("*Cmt*"));
1210                         } else {
1211                                 comment_button.unset_bg (STATE_NORMAL);
1212                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("Cmt"));
1213                         }
1214                         break;
1215                 }
1216
1217                 ARDOUR_UI::instance()->tooltips().set_tip (comment_button,
1218                                 str.empty() ? _("Click to Add/Edit Comments") : str);
1219         }
1220
1221 }
1222
1223 void
1224 MixerStrip::comment_button_clicked ()
1225 {
1226         if (comment_window == 0) {
1227                 setup_comment_editor ();
1228         }
1229
1230     int x, y, cw_width, cw_height;
1231
1232         if (comment_window->is_visible()) {
1233                 comment_window->hide ();
1234                 return;
1235         }
1236
1237         comment_window->get_size (cw_width, cw_height);
1238         comment_window->get_position(x, y);
1239         comment_window->move(x, y - (cw_height / 2) - 45);
1240         /*
1241            half the dialog height minus the comments button height
1242            with some window decoration fudge thrown in.
1243         */
1244
1245         comment_window->show();
1246         comment_window->present();
1247 }
1248
1249 void
1250 MixerStrip::setup_comment_editor ()
1251 {
1252         string title;
1253         title = _route->name();
1254         title += _(": comment editor");
1255
1256         comment_window = new ArdourDialog (title, false);
1257         comment_window->set_position (Gtk::WIN_POS_MOUSE);
1258         comment_window->set_skip_taskbar_hint (true);
1259         comment_window->signal_hide().connect (mem_fun(*this, &MixerStrip::comment_editor_done_editing));
1260
1261         comment_area = manage (new TextView());
1262         comment_area->set_name ("MixerTrackCommentArea");
1263         comment_area->set_size_request (110, 178);
1264         comment_area->set_wrap_mode (WRAP_WORD);
1265         comment_area->set_editable (true);
1266         comment_area->get_buffer()->set_text (_route->comment());
1267         comment_area->show ();
1268
1269         comment_window->get_vbox()->pack_start (*comment_area);
1270         comment_window->get_action_area()->hide();
1271 }
1272
1273 void
1274 MixerStrip::comment_changed (void *src)
1275 {
1276         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed), src));
1277
1278         if (src != this) {
1279                 ignore_comment_edit = true;
1280                 if (comment_area) {
1281                         comment_area->get_buffer()->set_text (_route->comment());
1282                 }
1283                 ignore_comment_edit = false;
1284         }
1285 }
1286
1287 void
1288 MixerStrip::set_route_group (RouteGroup *rg)
1289 {
1290         _route->set_route_group (rg, this);
1291 }
1292
1293 bool
1294 MixerStrip::select_route_group (GdkEventButton *ev)
1295 {
1296         using namespace Menu_Helpers;
1297
1298         if (ev->button == 1) {
1299
1300                 if (group_menu == 0) {
1301
1302                         group_menu = new RouteGroupMenu (
1303                                 _session,
1304                                 (RouteGroup::Property) (RouteGroup::Gain | RouteGroup::Mute | RouteGroup::Solo)
1305                                 );
1306
1307                         group_menu->GroupSelected.connect (mem_fun (*this, &MixerStrip::set_route_group));
1308                 }
1309
1310                 group_menu->popup (1, ev->time);
1311         }
1312
1313         return true;
1314 }
1315
1316 void
1317 MixerStrip::route_group_changed (void *ignored)
1318 {
1319         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_group_changed), ignored));
1320
1321         RouteGroup *rg = _route->route_group();
1322
1323         if (rg) {
1324                 /* XXX: this needs a better algorithm */
1325                 string truncated = rg->name ();
1326                 if (truncated.length () > 5) {
1327                         truncated = truncated.substr (0, 5);
1328                 }
1329                 group_label.set_text (truncated);
1330         } else {
1331                 switch (_width) {
1332                 case Wide:
1333                         group_label.set_text (_("Grp"));
1334                         break;
1335                 case Narrow:
1336                         group_label.set_text (_("~G"));
1337                         break;
1338                 }
1339         }
1340 }
1341
1342
1343 void
1344 MixerStrip::route_gui_changed (string what_changed, void* ignored)
1345 {
1346         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed), what_changed, ignored));
1347
1348         if (what_changed == "color") {
1349                 if (set_color_from_route () == 0) {
1350                         show_route_color ();
1351                 }
1352         }
1353 }
1354
1355 void
1356 MixerStrip::show_route_color ()
1357 {
1358         name_button.modify_bg (STATE_NORMAL, color());
1359         top_event_box.modify_bg (STATE_NORMAL, color());
1360         route_active_changed ();
1361 }
1362
1363 void
1364 MixerStrip::show_passthru_color ()
1365 {
1366         route_active_changed ();
1367 }
1368
1369 void
1370 MixerStrip::build_route_ops_menu ()
1371 {
1372         using namespace Menu_Helpers;
1373         route_ops_menu = new Menu;
1374         route_ops_menu->set_name ("ArdourContextMenu");
1375
1376         MenuList& items = route_ops_menu->items();
1377
1378         items.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template)));
1379         items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename)));
1380         rename_menu_item = &items.back();
1381         items.push_back (SeparatorElem());
1382         items.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active)));
1383         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1384         route_active_menu_item->set_active (_route->active());
1385
1386         items.push_back (SeparatorElem());
1387
1388         items.push_back (MenuElem (_("Adjust latency"), mem_fun (*this, &RouteUI::adjust_latency)));
1389
1390         items.push_back (SeparatorElem());
1391         items.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity)));
1392         polarity_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1393         polarity_menu_item->set_active (_route->phase_invert());
1394         items.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1395         denormal_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1396         denormal_menu_item->set_active (_route->denormal_protection());
1397
1398         if (!Profile->get_sae()) {
1399                 items.push_back (SeparatorElem());
1400                 items.push_back (MenuElem (_("Remote Control ID..."), mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1401         }
1402
1403         items.push_back (SeparatorElem());
1404         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
1405 }
1406
1407 gint
1408 MixerStrip::name_button_button_press (GdkEventButton* ev)
1409 {
1410         if (ev->button == 1 || ev->button == 3) {
1411                 list_route_operations ();
1412
1413                 /* do not allow rename if the track is record-enabled */
1414                 rename_menu_item->set_sensitive (!_route->record_enabled());
1415                 route_ops_menu->popup (1, ev->time);
1416         }
1417         return FALSE;
1418 }
1419
1420 void
1421 MixerStrip::list_route_operations ()
1422 {
1423         if (route_ops_menu == 0) {
1424                 build_route_ops_menu ();
1425         }
1426 }
1427
1428 void
1429 MixerStrip::set_selected (bool yn)
1430 {
1431         AxisView::set_selected (yn);
1432         if (_selected) {
1433                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1434                 global_frame.set_name ("MixerStripSelectedFrame");
1435         } else {
1436                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1437                 global_frame.set_name ("MixerStripFrame");
1438         }
1439         global_frame.queue_draw ();
1440 }
1441
1442 void
1443 MixerStrip::name_changed ()
1444 {
1445         switch (_width) {
1446         case Wide:
1447                 RouteUI::name_changed ();
1448                 break;
1449         case Narrow:
1450                 name_label.set_text (PBD::short_version (_route->name(), 5));
1451                 break;
1452         }
1453         if (_route->phase_invert()) {
1454                 name_label.set_text (X_("Ø ") + name_label.get_text());
1455         }
1456 }
1457
1458 void
1459 MixerStrip::width_clicked ()
1460 {
1461         switch (_width) {
1462         case Wide:
1463                 set_width_enum (Narrow, this);
1464                 break;
1465         case Narrow:
1466                 set_width_enum (Wide, this);
1467                 break;
1468         }
1469 }
1470
1471 void
1472 MixerStrip::hide_clicked ()
1473 {
1474         // LAME fix to reset the button status for when it is redisplayed (part 1)
1475         hide_button.set_sensitive(false);
1476
1477         if (_embedded) {
1478                 Hiding(); /* EMIT_SIGNAL */
1479         } else {
1480                 _mixer.hide_strip (this);
1481         }
1482
1483         // (part 2)
1484         hide_button.set_sensitive(true);
1485 }
1486
1487 void
1488 MixerStrip::set_embedded (bool yn)
1489 {
1490         _embedded = yn;
1491 }
1492
1493 void
1494 MixerStrip::map_frozen ()
1495 {
1496         ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen));
1497
1498         boost::shared_ptr<AudioTrack> at = audio_track();
1499
1500         if (at) {
1501                 switch (at->freeze_state()) {
1502                 case AudioTrack::Frozen:
1503                         processor_box.set_sensitive (false);
1504                         break;
1505                 default:
1506                         processor_box.set_sensitive (true);
1507                         // XXX need some way, maybe, to retoggle redirect editors
1508                         break;
1509                 }
1510         }
1511
1512         hide_redirect_editors ();
1513 }
1514
1515 void
1516 MixerStrip::hide_redirect_editors ()
1517 {
1518         _route->foreach_processor (mem_fun (*this, &MixerStrip::hide_processor_editor));
1519 }
1520
1521 void
1522 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1523 {
1524         boost::shared_ptr<Processor> processor (p.lock ());
1525         if (!processor) {
1526                 return;
1527         }
1528
1529         void* gui = processor->get_gui ();
1530
1531         if (gui) {
1532                 static_cast<Gtk::Widget*>(gui)->hide ();
1533         }
1534 }
1535
1536 void
1537 MixerStrip::route_active_changed ()
1538 {
1539         RouteUI::route_active_changed ();
1540
1541         if (is_midi_track()) {
1542                 if (_route->active()) {
1543                         set_name ("MidiTrackStripBase");
1544                         gpm.set_meter_strip_name ("MidiTrackStripBase");
1545                 } else {
1546                         set_name ("MidiTrackStripBaseInactive");
1547                         gpm.set_meter_strip_name ("MidiTrackStripBaseInactive");
1548                 }
1549                 gpm.set_fader_name ("MidiTrackFader");
1550         } else if (is_audio_track()) {
1551                 if (_route->active()) {
1552                         set_name ("AudioTrackStripBase");
1553                         gpm.set_meter_strip_name ("AudioTrackMetrics");
1554                 } else {
1555                         set_name ("AudioTrackStripBaseInactive");
1556                         gpm.set_meter_strip_name ("AudioTrackMetricsInactive");
1557                 }
1558                 gpm.set_fader_name ("AudioTrackFader");
1559         } else {
1560                 if (_route->active()) {
1561                         set_name ("AudioBusStripBase");
1562                         gpm.set_meter_strip_name ("AudioBusMetrics");
1563                 } else {
1564                         set_name ("AudioBusStripBaseInactive");
1565                         gpm.set_meter_strip_name ("AudioBusMetricsInactive");
1566                 }
1567                 gpm.set_fader_name ("AudioBusFader");
1568
1569                 /* (no MIDI busses yet) */
1570         }
1571 }
1572
1573 RouteGroup*
1574 MixerStrip::route_group() const
1575 {
1576         return _route->route_group();
1577 }
1578
1579 void
1580 MixerStrip::engine_stopped ()
1581 {
1582 }
1583
1584 void
1585 MixerStrip::engine_running ()
1586 {
1587 }
1588
1589 void
1590 MixerStrip::meter_changed (void *src)
1591 {
1592         ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed), src));
1593
1594         switch (_route->meter_point()) {
1595         case MeterInput:
1596                 meter_point_label.set_text (_("input"));
1597                 break;
1598
1599         case MeterPreFader:
1600                 meter_point_label.set_text (_("pre"));
1601                 break;
1602
1603         case MeterPostFader:
1604                 meter_point_label.set_text (_("post"));
1605                 break;
1606         }
1607
1608         gpm.setup_meters ();
1609         // reset peak when meter point changes
1610         gpm.reset_peak_display();
1611         set_width_enum (_width, this);
1612 }
1613
1614 void
1615 MixerStrip::switch_io (boost::shared_ptr<Route> target)
1616 {
1617         if (_route == target || _route->is_master()) {
1618                 /* don't change the display for the target or the master bus */
1619                 return;
1620         } else if (!is_track() && show_sends_button) {
1621                 /* make sure our show sends button is inactive, and we no longer blink,
1622                    since we're not the target.
1623                 */
1624                 send_blink_connection.disconnect ();
1625                 show_sends_button->set_active (false);
1626                 show_sends_button->set_state (STATE_NORMAL);
1627         }
1628
1629         if (!target) {
1630                 /* switch back to default */
1631                 revert_to_default_display ();
1632                 return;
1633         }
1634
1635         boost::shared_ptr<Send> send;
1636
1637         if (_current_delivery && (send = boost::dynamic_pointer_cast<Send>(_current_delivery))) {
1638                 send->set_metering (false);
1639         }
1640
1641         _current_delivery = _route->internal_send_for (target);
1642
1643         cerr << "internal send from " << _route->name() << " to " << target->name() << " = "
1644              << _current_delivery << endl;
1645
1646         if (_current_delivery) {
1647                 send = boost::dynamic_pointer_cast<Send>(_current_delivery);
1648                 send->set_metering (true);
1649                 _current_delivery->GoingAway.connect (mem_fun (*this, &MixerStrip::revert_to_default_display));
1650                 gain_meter().set_controls (_route, send->meter(), send->amp());
1651                 panner_ui().set_panner (_current_delivery->panner());
1652
1653         } else {
1654                 _current_delivery = _route->main_outs ();
1655                 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
1656                 panner_ui().set_panner (_route->main_outs()->panner());
1657         }
1658
1659         gain_meter().setup_meters ();
1660         panner_ui().setup_pan ();
1661 }
1662
1663
1664 void
1665 MixerStrip::revert_to_default_display ()
1666 {
1667         show_sends_button->set_active (false);
1668
1669         boost::shared_ptr<Send> send;
1670
1671         if (_current_delivery && (send = boost::dynamic_pointer_cast<Send>(_current_delivery))) {
1672                 send->set_metering (false);
1673         }
1674
1675         _current_delivery = _route->main_outs();
1676
1677         gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
1678         gain_meter().setup_meters ();
1679         panner_ui().set_panner (_route->main_outs()->panner());
1680         panner_ui().setup_pan ();
1681 }
1682
1683 void
1684 MixerStrip::set_button_names ()
1685 {
1686         switch (_width) {
1687         case Wide:
1688                 rec_enable_button_label.set_text (_("Rec"));
1689                 mute_button_label.set_text (_("Mute"));
1690                 if (!Config->get_solo_control_is_listen_control()) {
1691                         solo_button_label.set_text (_("Solo"));
1692                 } else {
1693                         switch (Config->get_listen_position()) {
1694                         case AfterFaderListen:
1695                                 solo_button_label.set_text (_("AFL"));
1696                                 break;
1697                         case PreFaderListen:
1698                                 solo_button_label.set_text (_("PFL"));
1699                                 break;
1700                         }
1701                 }
1702                 break;
1703
1704         default:
1705                 rec_enable_button_label.set_text (_("R"));
1706                 mute_button_label.set_text (_("M"));
1707                 if (!Config->get_solo_control_is_listen_control()) {
1708                         solo_button_label.set_text (_("S"));
1709                 } else {
1710                         switch (Config->get_listen_position()) {
1711                         case AfterFaderListen:
1712                                 solo_button_label.set_text (_("A"));
1713                                 break;
1714                         case PreFaderListen:
1715                                 solo_button_label.set_text (_("P"));
1716                                 break;
1717                         }
1718                 }
1719                 break;
1720
1721         }
1722 }
1723
1724 bool
1725 MixerStrip::on_key_press_event (GdkEventKey* ev)
1726 {
1727         GdkEventButton fake;
1728         fake.type = GDK_BUTTON_PRESS;
1729         fake.button = 1;
1730         fake.state = ev->state;
1731
1732         switch (ev->keyval) {
1733         case GDK_m:
1734                 mute_press (&fake);
1735                 return true;
1736                 break;
1737
1738         case GDK_s:
1739                 solo_press (&fake);
1740                 return true;
1741                 break;
1742
1743         case GDK_r:
1744                 rec_enable_press (&fake);
1745                 return true;
1746                 break;
1747
1748         case GDK_e:
1749                 show_sends_press (&fake);
1750                 return true;
1751                 break;
1752
1753         case GDK_g:
1754                 if (ev->state & Keyboard::PrimaryModifier) {
1755                         step_gain_down ();
1756                 } else {
1757                         step_gain_up ();
1758                 }
1759                 return true;
1760                 break;
1761
1762         case GDK_0:
1763                 if (_route) {
1764                         _route->set_gain (1.0, this);
1765                 }
1766                 return true;
1767
1768         default:
1769                 break;
1770         }
1771
1772         return false;
1773 }
1774
1775
1776 bool
1777 MixerStrip::on_key_release_event (GdkEventKey* ev)
1778 {
1779         GdkEventButton fake;
1780         fake.type = GDK_BUTTON_RELEASE;
1781         fake.button = 1;
1782         fake.state = ev->state;
1783
1784         switch (ev->keyval) {
1785         case GDK_m:
1786                 mute_release (&fake);
1787                 return true;
1788                 break;
1789
1790         case GDK_s:
1791                 solo_release (&fake);
1792                 return true;
1793                 break;
1794
1795         case GDK_r:
1796                 rec_enable_release (&fake);
1797                 return true;
1798                 break;
1799
1800         case GDK_e:
1801                 show_sends_release (&fake);
1802                 return true;
1803                 break;
1804
1805         case GDK_g:
1806                 return true;
1807                 break;
1808
1809         default:
1810                 break;
1811         }
1812
1813         return false;
1814 }
1815
1816 bool
1817 MixerStrip::on_enter_notify_event (GdkEventCrossing*)
1818 {
1819         Keyboard::magic_widget_grab_focus ();
1820         grab_focus ();
1821         return false;
1822 }
1823
1824 bool
1825 MixerStrip::on_leave_notify_event (GdkEventCrossing* ev)
1826 {
1827         switch (ev->detail) {
1828         case GDK_NOTIFY_INFERIOR:
1829                 break;
1830         default:
1831                 Keyboard::magic_widget_drop_focus ();
1832         }
1833
1834         return false;
1835 }
1836
1837 void
1838 MixerStrip::mono_button_clicked ()
1839 {
1840         panners.set_mono (_mono_button.get_active ());
1841 }
1842
1843 PluginSelector*
1844 MixerStrip::plugin_selector()
1845 {
1846         return _mixer.plugin_selector();
1847 }