make context menu-driven stuff on ruler bars happen in the right place (e.g. new...
[ardour.git] / gtk2_ardour / mixer_strip.cc
1 /*
2     Copyright (C) 2000-2002 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
20 #include <cmath>
21 #include <algorithm>
22
23 #include <sigc++/bind.h>
24
25 #include <pbd/convert.h>
26 #include <pbd/enumwriter.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/session.h>
38 #include <ardour/audioengine.h>
39 #include <ardour/route.h>
40 #include <ardour/audio_track.h>
41 #include <ardour/audio_diskstream.h>
42 #include <ardour/panner.h>
43 #include <ardour/send.h>
44 #include <ardour/insert.h>
45 #include <ardour/profile.h>
46 #include <ardour/ladspa_plugin.h>
47 #include <ardour/connection.h>
48 #include <ardour/session_connection.h>
49
50 #include "ardour_ui.h"
51 #include "ardour_dialog.h"
52 #include "mixer_strip.h"
53 #include "mixer_ui.h"
54 #include "keyboard.h"
55 #include "public_editor.h"
56 #include "send_ui.h"
57 #include "io_selector.h"
58 #include "utils.h"
59 #include "gui_thread.h"
60
61 #include "i18n.h"
62
63 using namespace sigc;
64 using namespace ARDOUR;
65 using namespace PBD;
66 using namespace Gtk;
67 using namespace Gtkmm2ext;
68 using namespace std;
69
70 int MixerStrip::scrollbar_height = 0;
71
72 #ifdef VARISPEED_IN_MIXER_STRIP
73 static void 
74 speed_printer (char buf[32], Gtk::Adjustment& adj, void* arg)
75 {
76         float val = adj.get_value ();
77
78         if (val == 1.0) {
79                 strcpy (buf, "1");
80         } else {
81                 snprintf (buf, 32, "%.3f", val);
82         }
83 }
84 #endif 
85
86 MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, bool in_mixer)
87         : AxisView(sess),
88           RouteUI (sess, _("Mute"), _("Solo"), _("Record")),
89           _mixer(mx),
90           _mixer_owned (in_mixer),
91           pre_redirect_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
92           post_redirect_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
93           gpm (sess),
94           panners (sess),
95           button_table (3, 2),
96           middle_button_table (1, 2),
97           bottom_button_table (1, 2),
98           meter_point_label (_("pre")),
99           comment_button (_("Comments")),
100           speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
101           speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
102
103 {
104         init ();
105
106         if (!_mixer_owned) {
107                 /* the editor mixer strip: don't destroy it every time
108                    the underlying route goes away.
109                 */
110
111                 self_destruct = false;
112         }
113 }
114
115 MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
116         : AxisView(sess),
117           RouteUI (sess, _("Mute"), _("Solo"), _("Record")),
118           _mixer(mx),
119           _mixer_owned (in_mixer),
120           pre_redirect_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
121           post_redirect_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
122           gpm (sess),
123           panners (sess),
124           button_table (3, 2),
125           middle_button_table (1, 2),
126           bottom_button_table (1, 2),
127           meter_point_label (_("pre")),
128           comment_button (_("Comments")),
129           speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
130           speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
131
132 {
133         init ();
134         set_route (rt);
135 }
136
137 void
138 MixerStrip::init ()
139 {
140         input_selector = 0;
141         output_selector = 0;
142         group_menu = 0;
143         _marked_for_display = false;
144         route_ops_menu = 0;
145         rename_menu_item = 0;
146         ignore_comment_edit = false;
147         ignore_toggle = false;
148         ignore_speed_adjustment = false;
149         comment_window = 0;
150         comment_area = 0;
151         _width_owner = 0;
152
153         width_button.add (*(manage (new Gtk::Image (::get_icon("strip_width")))));
154         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
155
156         input_label.set_text (_("Input"));
157         ARDOUR_UI::instance()->set_tip (&input_button, _("Click to choose inputs"), "");
158         input_button.add (input_label);
159         input_button.set_name ("MixerIOButton");
160         input_label.set_name ("MixerIOButtonLabel");
161
162         output_label.set_text (_("Output"));
163         ARDOUR_UI::instance()->set_tip (&output_button, _("Click to choose outputs"), "");
164         output_button.add (output_label);
165         output_button.set_name ("MixerIOButton");
166         output_label.set_name ("MixerIOButtonLabel");
167
168         ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Select metering point"), "");
169         meter_point_button.add (meter_point_label);
170         meter_point_button.set_name ("MixerStripMeterPreButton");
171         meter_point_label.set_name ("MixerStripMeterPreButton");
172         
173         /* TRANSLATORS: this string should be longest of the strings
174            used to describe meter points. In english, it's "input".
175         */
176         set_size_request_to_display_given_text (meter_point_button, _("tupni"), 5, 5);
177     
178         bottom_button_table.attach (meter_point_button, 1, 2, 0, 1);
179     
180         meter_point_button.signal_button_press_event().connect (mem_fun (gpm, &GainMeter::meter_press), false);
181         /* XXX what is this meant to do? */
182         //meter_point_button.signal_button_release_event().connect (mem_fun (gpm, &GainMeter::meter_release), false);
183
184         hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185
186         mute_button->set_name ("MixerMuteButton");
187         solo_button->set_name ("MixerSoloButton");
188
189         button_table.set_homogeneous (true);
190         button_table.set_spacings (0);
191
192         button_table.attach (name_button, 0, 2, 0, 1);
193         button_table.attach (input_button, 0, 2, 1, 2);
194
195         middle_button_table.set_homogeneous (true);
196         middle_button_table.set_spacings (0);
197         middle_button_table.attach (*mute_button, 0, 1, 0, 1);
198         middle_button_table.attach (*solo_button, 1, 2, 0, 1);
199
200         bottom_button_table.set_col_spacings (0);
201         bottom_button_table.set_homogeneous (true);
202         bottom_button_table.attach (group_button, 0, 1, 0, 1);
203         
204         name_button.add (name_label);
205         name_button.set_name ("MixerNameButton");
206         Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
207
208         name_label.set_name ("MixerNameButtonLabel");
209         ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
210         group_button.add (group_label);
211         group_button.set_name ("MixerGroupButton");
212         group_label.set_name ("MixerGroupButtonLabel");
213
214         comment_button.set_name ("MixerCommentButton");
215
216         comment_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked));
217         
218         global_vpacker.set_border_width (0);
219         global_vpacker.set_spacing (0);
220
221         VBox *whvbox = manage (new VBox);
222
223         width_button.set_name ("MixerWidthButton");
224         hide_button.set_name ("MixerHideButton");
225         top_event_box.set_name ("MixerTopEventBox");
226
227         width_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::width_clicked));
228         hide_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::hide_clicked));
229
230         width_hide_box.pack_start (width_button, false, true);
231         width_hide_box.pack_start (top_event_box, true, true);
232         width_hide_box.pack_end (hide_button, false, true);
233         Gtk::Alignment *gain_meter_alignment = Gtk::manage(new Gtk::Alignment());
234         gain_meter_alignment->set_padding(0, 4, 0, 0);
235         gain_meter_alignment->add(gpm);
236
237         whvbox->pack_start (width_hide_box, true, true);
238
239         global_vpacker.pack_start (*whvbox, Gtk::PACK_SHRINK);
240         global_vpacker.pack_start (button_table,Gtk::PACK_SHRINK);
241         global_vpacker.pack_start (pre_redirect_box, true, true);
242         global_vpacker.pack_start (middle_button_table,Gtk::PACK_SHRINK);
243         global_vpacker.pack_start (*gain_meter_alignment,Gtk::PACK_SHRINK);
244         global_vpacker.pack_start (bottom_button_table,Gtk::PACK_SHRINK);
245         global_vpacker.pack_start (post_redirect_box, true, true);
246         global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
247         global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
248         global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
249
250         global_frame.add (global_vpacker);
251         global_frame.set_shadow_type (Gtk::SHADOW_IN);
252         global_frame.set_name ("BaseFrame");
253
254         add (global_frame);
255
256         /* force setting of visible selected status */
257
258         _selected = true;
259         set_selected (false);
260
261         _packed = false;
262         _embedded = false;
263
264         _session.engine().Stopped.connect (mem_fun(*this, &MixerStrip::engine_stopped));
265         _session.engine().Running.connect (mem_fun(*this, &MixerStrip::engine_running));
266
267         input_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::input_press), false);
268         output_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::output_press), false);
269
270         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
271         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
272         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
273         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
274
275         /* we don't need this if its not an audio track, but we don't know that yet and it doesn't
276            hurt (much).
277         */
278
279         rec_enable_button->set_name ("MixerRecordEnableButton");
280         rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
281         rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
282
283         name_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::name_button_button_press), false);
284         group_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::select_mix_group), false);
285
286         _width = (Width) -1;
287
288         /* start off as a passthru strip. we'll correct this, if necessary,
289            in update_diskstream_display().
290         */
291
292         set_name ("AudioTrackStripBase");
293
294         add_events (Gdk::BUTTON_RELEASE_MASK);
295 }
296
297 MixerStrip::~MixerStrip ()
298 {
299         GoingAway(); /* EMIT_SIGNAL */
300
301         if (input_selector) {
302                 delete input_selector;
303         }
304
305         if (output_selector) {
306                 delete output_selector;
307         }
308 }
309
310 void
311 MixerStrip::set_route (boost::shared_ptr<Route> rt)
312 {
313         if (rec_enable_button->get_parent()) {
314                 button_table.remove (*rec_enable_button);
315         }
316
317 #ifdef VARISPEED_IN_MIXER_STRIP
318         if (speed_frame->get_parent()) {
319                 button_table.remove (*speed_frame);
320         }
321 #endif
322
323         RouteUI::set_route (rt);
324
325         if (input_selector) {
326                 delete input_selector;
327                 input_selector = 0;
328         }
329
330         if (output_selector) {
331                 delete output_selector;
332                 output_selector = 0;
333         }
334
335         panners.set_io (rt);
336         gpm.set_io (rt);
337         pre_redirect_box.set_route (rt);
338         post_redirect_box.set_route (rt);
339
340         if (set_color_from_route()) {
341                 set_color (unique_random_color());
342         }
343
344         if (_mixer_owned && (route()->master() || route()->control())) {
345                 
346                 if (scrollbar_height == 0) {
347                         HScrollbar scrollbar;
348                         Gtk::Requisition requisition(scrollbar.size_request ());
349                         scrollbar_height = requisition.height;
350                 }
351
352                 EventBox* spacer = manage (new EventBox);
353                 spacer->set_size_request (-1, scrollbar_height);
354                 global_vpacker.pack_start (*spacer, false, false);
355         }
356
357         if (is_audio_track()) {
358
359                 boost::shared_ptr<AudioTrack> at = audio_track();
360
361                 connections.push_back (at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen)));
362
363 #ifdef VARISPEED_IN_MIXER_STRIP
364                 speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
365                 
366                 speed_frame.set_name ("BaseFrame");
367                 speed_frame.set_shadow_type (Gtk::SHADOW_IN);
368                 speed_frame.add (speed_spinner);
369                 
370                 speed_spinner.set_print_func (speed_printer, 0);
371
372                 ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("Varispeed"));
373
374                 button_table.attach (speed_frame, 0, 2, 5, 6);
375 #endif /* VARISPEED_IN_MIXER_STRIP */
376
377                 button_table.attach (*rec_enable_button, 0, 2, 2, 3);
378                 rec_enable_button->show();
379         }
380
381         if (_route->phase_invert()) {
382                 name_label.set_text (X_("Ø ") + name_label.get_text());
383         } else {
384                 name_label.set_text (_route->name());
385         }
386
387         switch (_route->meter_point()) {
388         case MeterInput:
389                 meter_point_label.set_text (_("input"));
390                 break;
391                 
392         case MeterPreFader:
393                 meter_point_label.set_text (_("pre"));
394                 break;
395                 
396         case MeterPostFader:
397                 meter_point_label.set_text (_("post"));
398                 break;
399         }
400
401         delete route_ops_menu;
402         route_ops_menu = 0;
403         
404         ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment().empty() ?
405                                                    _("Click to Add/Edit Comments"):
406                                                    _route->comment());
407
408         connections.push_back (_route->meter_change.connect (mem_fun(*this, &MixerStrip::meter_changed)));
409         connections.push_back (_route->input_changed.connect (mem_fun(*this, &MixerStrip::input_changed)));
410         connections.push_back (_route->output_changed.connect (mem_fun(*this, &MixerStrip::output_changed)));
411         connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
412         connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
413         connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
414         connections.push_back (_route->mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed)));
415         connections.push_back (_route->panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan)));
416
417         if (is_audio_track()) {
418                 connections.push_back (audio_track()->DiskstreamChanged.connect (mem_fun(*this, &MixerStrip::diskstream_changed)));
419                 connections.push_back (get_diskstream()->SpeedChanged.connect (mem_fun(*this, &MixerStrip::speed_changed)));
420         }
421
422         connections.push_back (_route->name_changed.connect (mem_fun(*this, &RouteUI::name_changed)));
423         connections.push_back (_route->comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed)));
424         connections.push_back (_route->gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed)));
425
426         set_stuff_from_route ();
427
428         /* now force an update of all the various elements */
429
430         pre_redirect_box.update();
431         post_redirect_box.update();
432         mute_changed (0);
433         solo_changed (0);
434         name_changed (0);
435         comment_changed (0);
436         mix_group_changed (0);
437
438         connect_to_pan ();
439
440         panners.setup_pan ();
441
442         if (is_audio_track()) {
443                 speed_changed ();
444         }
445
446         update_diskstream_display ();
447         update_input_display ();
448         update_output_display ();
449 }
450
451 void
452 MixerStrip::set_stuff_from_route ()
453 {
454         XMLProperty *prop;
455
456         ensure_xml_node ();
457
458         /* if width is not set, it will be set by the MixerUI or editor */
459
460         if ((prop = xml_node->property ("strip_width")) != 0) {
461                 set_width (Width (string_2_enum (prop->value(), _width)), this);
462         }
463
464         if ((prop = xml_node->property ("shown_mixer")) != 0) {
465                 if (prop->value() == "no") {
466                         _marked_for_display = false;
467                 } else {
468                         _marked_for_display = true;
469                 }
470         } else {
471                 /* backwards compatibility */
472                 _marked_for_display = true;
473         }
474 }
475
476 void
477 MixerStrip::set_width (Width w, void* owner)
478 {
479         /* always set the gpm width again, things may be hidden */
480
481         gpm.set_width (w);
482         panners.set_width (w);
483         pre_redirect_box.set_width (w);
484         post_redirect_box.set_width (w);
485
486         _width_owner = owner;
487
488         ensure_xml_node ();
489         
490         _width = w;
491
492         if (_width_owner == this) {
493                 xml_node->add_property ("strip_width", enum_2_string (_width));
494         }
495
496         switch (w) {
497         case Wide:
498
499                 if (rec_enable_button)  {
500                         ((Gtk::Label*)rec_enable_button->get_child())->set_text (_("Record"));
501                 }
502                 ((Gtk::Label*)mute_button->get_child())->set_text  (_("Mute"));
503                 ((Gtk::Label*)solo_button->get_child())->set_text (_("Solo"));
504
505                 if (_route->comment() == "") {
506                        comment_button.unset_bg (STATE_NORMAL);
507                        ((Gtk::Label*)comment_button.get_child())->set_text (_("Comments"));
508                 } else {
509                        comment_button.modify_bg (STATE_NORMAL, color());
510                        ((Gtk::Label*)comment_button.get_child())->set_text (_("*Comments*"));
511                 }
512
513                 ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.astyle_string(_route->gain_automation_curve().automation_style()));
514                 ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.astate_string(_route->gain_automation_curve().automation_state()));
515                 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.astyle_string(_route->panner().automation_style()));
516                 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.astate_string(_route->panner().automation_state()));
517                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "long", 2, 2);
518                 set_size_request (-1, -1);
519                 break;
520
521         case Narrow:
522                 if (rec_enable_button) {
523                         ((Gtk::Label*)rec_enable_button->get_child())->set_text (_("Rec"));
524                 }
525                 ((Gtk::Label*)mute_button->get_child())->set_text (_("M"));
526                 ((Gtk::Label*)solo_button->get_child())->set_text (_("S"));
527
528                 if (_route->comment() == "") {
529                        comment_button.unset_bg (STATE_NORMAL);
530                        ((Gtk::Label*)comment_button.get_child())->set_text (_("Cmt"));
531                 } else {
532                        comment_button.modify_bg (STATE_NORMAL, color());
533                        ((Gtk::Label*)comment_button.get_child())->set_text (_("*Cmt*"));
534                 }
535
536                 ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.short_astyle_string(_route->gain_automation_curve().automation_style()));
537                 ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.short_astate_string(_route->gain_automation_curve().automation_state()));
538                 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.short_astyle_string(_route->panner().automation_style()));
539                 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.short_astate_string(_route->panner().automation_state()));
540                 Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
541                 set_size_request (max (50, gpm.get_gm_width()), -1);
542                 break;
543         }
544         update_input_display ();
545         update_output_display ();
546         mix_group_changed (0);
547         name_changed (0);
548 #ifdef GTKOSX
549         WidthChanged();
550 #endif
551 }
552
553 void
554 MixerStrip::set_packed (bool yn)
555 {
556         _packed = yn;
557
558         ensure_xml_node ();
559
560         if (_packed) {
561                 xml_node->add_property ("shown_mixer", "yes");
562         } else {
563                 xml_node->add_property ("shown_mixer", "no");
564         }
565 }
566
567
568 gint
569 MixerStrip::output_press (GdkEventButton *ev)
570 {
571         using namespace Menu_Helpers;
572         if (!_session.engine().connected()) {
573                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
574                 msg.run ();
575                 return true;
576         }
577
578         MenuList& citems = output_menu.items();
579         switch (ev->button) {
580
581         case 1:
582                 output_menu.set_name ("ArdourContextMenu");
583                 citems.clear();
584                 
585                 citems.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_output_configuration)));
586                 citems.push_back (SeparatorElem());
587                 citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
588                 citems.push_back (SeparatorElem());
589                 
590                 _session.foreach_connection (this, &MixerStrip::add_connection_to_output_menu);
591
592                 output_menu.popup (1, ev->time);
593                 break;
594                 
595         default:
596                 break;
597         }
598         return TRUE;
599 }
600
601 void
602 MixerStrip::edit_output_configuration ()
603 {
604         if (output_selector == 0) {
605                 output_selector = new IOSelectorWindow (_session, _route, false);
606         } 
607
608         if (output_selector->is_visible()) {
609                 output_selector->get_toplevel()->get_window()->raise();
610         } else {
611                 output_selector->show_all ();
612         }
613 }
614
615 void
616 MixerStrip::edit_input_configuration ()
617 {
618         if (input_selector == 0) {
619                 input_selector = new IOSelectorWindow (_session, _route, true);
620         } 
621
622         if (input_selector->is_visible()) {
623                 input_selector->get_toplevel()->get_window()->raise();
624         } else {
625                 input_selector->show_all ();
626         }
627 }
628
629 gint
630 MixerStrip::input_press (GdkEventButton *ev)
631 {
632         using namespace Menu_Helpers;
633
634         MenuList& citems = input_menu.items();
635         input_menu.set_name ("ArdourContextMenu");
636         citems.clear();
637         
638         if (!_session.engine().connected()) {
639                 MessageDialog msg (_("Not connected to JACK - no I/O changes are possible"));
640                 msg.run ();
641                 return true;
642         }
643
644         switch (ev->button) {
645
646         case 1:
647                 citems.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_input_configuration)));
648                 citems.push_back (SeparatorElem());
649                 citems.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
650                 citems.push_back (SeparatorElem());
651                 
652                 _session.foreach_connection (this, &MixerStrip::add_connection_to_input_menu);
653
654                 input_menu.popup (1, ev->time);
655                 break;
656                 
657         default:
658                 break;
659         }
660         return TRUE;
661 }
662
663 void
664 MixerStrip::connection_input_chosen (ARDOUR::Connection *c)
665 {
666         if (!ignore_toggle) {
667
668                 try { 
669                         _route->use_input_connection (*c, this);
670                 }
671
672                 catch (AudioEngine::PortRegistrationFailure& err) {
673                         error << _("could not register new ports required for that connection")
674                               << endmsg;
675                 }
676         }
677 }
678
679 void
680 MixerStrip::connection_output_chosen (ARDOUR::Connection *c)
681 {
682         if (!ignore_toggle) {
683
684                 try { 
685                         _route->use_output_connection (*c, this);
686                 }
687
688                 catch (AudioEngine::PortRegistrationFailure& err) {
689                         error << _("could not register new ports required for that connection")
690                               << endmsg;
691                 }
692         }
693 }
694
695 void
696 MixerStrip::add_connection_to_input_menu (ARDOUR::Connection* c)
697 {
698         using namespace Menu_Helpers;
699
700         if (dynamic_cast<InputConnection *> (c) == 0) {
701                 return;
702         }
703
704         MenuList& citems = input_menu.items();
705         
706         if (c->nports() == _route->n_inputs()) {
707
708                 citems.push_back (CheckMenuElem (c->name(), bind (mem_fun(*this, &MixerStrip::connection_input_chosen), c)));
709                 
710                 ARDOUR::Connection *current = _route->input_connection();
711                 
712                 if (current == c) {
713                         ignore_toggle = true;
714                         dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
715                         ignore_toggle = false;
716                 }
717         }
718 }
719
720 void
721 MixerStrip::add_connection_to_output_menu (ARDOUR::Connection* c)
722 {
723         using namespace Menu_Helpers;
724
725         if (dynamic_cast<OutputConnection *> (c) == 0) {
726                 return;
727         }
728
729         if (c->nports() == _route->n_outputs()) {
730
731                 MenuList& citems = output_menu.items();
732                 citems.push_back (CheckMenuElem (c->name(), bind (mem_fun(*this, &MixerStrip::connection_output_chosen), c)));
733                 
734                 ARDOUR::Connection *current = _route->output_connection();
735                 
736                 if (current == c) {
737                         ignore_toggle = true;
738                         dynamic_cast<CheckMenuItem *> (&citems.back())->set_active (true);
739                         ignore_toggle = false;
740                 }
741         }
742 }
743
744 void
745 MixerStrip::update_diskstream_display ()
746 {
747         map_frozen ();
748         update_input_display ();
749
750         if (is_audio_track()) {
751
752                 if (input_selector) {
753                         input_selector->hide_all ();
754                 }
755
756                 show_route_color ();
757
758         } else {
759
760                 show_passthru_color ();
761         }
762 }
763
764 void
765 MixerStrip::connect_to_pan ()
766 {
767         ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan));
768
769         panstate_connection.disconnect ();
770         panstyle_connection.disconnect ();
771
772         if (!_route->panner().empty()) {
773                 StreamPanner* sp = _route->panner().front();
774
775                 panstate_connection = sp->automation().automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed));
776                 panstyle_connection = sp->automation().automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed));
777         }
778
779         panners.pan_changed (this);
780 }
781
782 void
783 MixerStrip::update_input_display ()
784 {
785         ARDOUR::Connection *c;
786
787         if ((c = _route->input_connection()) != 0) {
788                 input_label.set_text (c->name());
789         } else {
790                 switch (_width) {
791                 case Wide:
792                         input_label.set_text (_(" Input"));
793                         break;
794                 case Narrow:
795                         input_label.set_text (_("I"));
796                         break;
797                 }
798         }
799         panners.setup_pan ();
800 }
801
802 void
803 MixerStrip::update_output_display ()
804 {
805         ARDOUR::Connection *c;
806
807         if ((c = _route->output_connection()) != 0) {
808                 output_label.set_text (c->name());
809         } else {
810                 switch (_width) {
811                 case Wide:
812                         output_label.set_text (_("Output"));
813                         break;
814                 case Narrow:
815                         output_label.set_text (_("O"));
816                         break;
817                 }
818         }
819
820         gpm.setup_meters ();
821         panners.setup_pan ();
822 }
823
824 void
825 MixerStrip::fast_update ()
826 {
827         gpm.update_meters ();
828 }
829
830 void
831 MixerStrip::diskstream_changed ()
832 {
833         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display));
834 }       
835
836 void
837 MixerStrip::input_changed (IOChange change, void *src)
838 {
839         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display));
840         set_width(_width, this);
841 }
842
843 void
844 MixerStrip::output_changed (IOChange change, void *src)
845 {
846         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display));
847         set_width(_width, this);
848 }
849
850
851 void 
852 MixerStrip::comment_editor_done_editing() 
853 {
854         string str =  comment_area->get_buffer()->get_text();
855         if (_route->comment() != str) {
856                 _route->set_comment (str, this);
857
858                 switch (_width) {
859                    
860                 case Wide:
861                         if (! str.empty()) {
862                                 comment_button.modify_bg (STATE_NORMAL, color());
863                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("*Comments*"));
864                         } else {
865                                 comment_button.unset_bg (STATE_NORMAL);
866                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("Comments"));
867                         }
868                         break;
869                    
870                 case Narrow:
871                         if (! str.empty()) {
872                                 comment_button.modify_bg (STATE_NORMAL, color());
873                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("*Cmt*"));
874                         } else {
875                                 comment_button.unset_bg (STATE_NORMAL);
876                                 ((Gtk::Label*)comment_button.get_child())->set_text (_("Cmt"));
877                         } 
878                         break;
879                 }
880                  
881                 ARDOUR_UI::instance()->tooltips().set_tip (comment_button, 
882                                 str.empty() ? _("Click to Add/Edit Comments") : str);
883         }
884
885 }
886
887 void
888 MixerStrip::comment_button_clicked ()
889 {
890         if (comment_window == 0) {
891                 setup_comment_editor ();
892         }
893
894     int x, y, cw_width, cw_height;
895
896         if (comment_window->is_visible()) {
897                 comment_window->hide ();
898                 return;
899         }
900
901         comment_window->get_size (cw_width, cw_height);
902         comment_window->get_position(x, y);
903         comment_window->move(x, y - (cw_height / 2) - 45);
904         /* 
905            half the dialog height minus the comments button height 
906            with some window decoration fudge thrown in.
907         */
908
909         comment_window->show();
910         comment_window->present();
911 }
912
913 void
914 MixerStrip::setup_comment_editor ()
915 {
916         string title;
917         title = _route->name();
918         title += _(": comment editor");
919
920         comment_window = new ArdourDialog (title, false);
921         comment_window->set_position (Gtk::WIN_POS_MOUSE);
922         comment_window->set_skip_taskbar_hint (true);
923         comment_window->signal_hide().connect (mem_fun(*this, &MixerStrip::comment_editor_done_editing));
924
925         comment_area = manage (new TextView());
926         comment_area->set_name ("MixerTrackCommentArea");
927         comment_area->set_size_request (110, 178);
928         comment_area->set_wrap_mode (WRAP_WORD);
929         comment_area->set_editable (true);
930         comment_area->get_buffer()->set_text (_route->comment());
931         comment_area->show ();
932
933         comment_window->get_vbox()->pack_start (*comment_area);
934         comment_window->get_action_area()->hide();
935 }
936
937 void
938 MixerStrip::comment_changed (void *src)
939 {
940         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed), src));
941         
942         if (src != this) {
943                 ignore_comment_edit = true;
944                 if (comment_area) {
945                         comment_area->get_buffer()->set_text (_route->comment());
946                 }
947                 ignore_comment_edit = false;
948         }
949 }
950
951 void
952 MixerStrip::set_mix_group (RouteGroup *rg)
953 {
954         _route->set_mix_group (rg, this);
955 }
956
957 void
958 MixerStrip::add_mix_group_to_menu (RouteGroup *rg, RadioMenuItem::Group* group)
959 {
960         using namespace Menu_Helpers;
961
962         MenuList& items = group_menu->items();
963
964         items.push_back (RadioMenuElem (*group, rg->name(), bind (mem_fun(*this, &MixerStrip::set_mix_group), rg)));
965
966         if (_route->mix_group() == rg) {
967                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
968         }
969 }
970
971 bool
972 MixerStrip::select_mix_group (GdkEventButton *ev)
973 {
974         using namespace Menu_Helpers;
975
976         if (group_menu == 0) {
977                 group_menu = new Menu;
978         } 
979         group_menu->set_name ("ArdourContextMenu");
980         MenuList& items = group_menu->items();
981         RadioMenuItem::Group group;
982
983         switch (ev->button) {
984         case 1:
985
986                 items.clear ();
987                 items.push_back (RadioMenuElem (group, _("No group"), bind (mem_fun(*this, &MixerStrip::set_mix_group), (RouteGroup *) 0)));
988
989                 _session.foreach_mix_group (bind (mem_fun (*this, &MixerStrip::add_mix_group_to_menu), &group));
990
991                 group_menu->popup (1, ev->time);
992                 break;
993
994         default:
995                 break;
996         }
997         
998         return true;
999 }       
1000
1001 void
1002 MixerStrip::mix_group_changed (void *ignored)
1003 {
1004         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::mix_group_changed), ignored));
1005         
1006         RouteGroup *rg = _route->mix_group();
1007         
1008         if (rg) {
1009                 group_label.set_text (rg->name());
1010         } else {
1011                 switch (_width) {
1012                 case Wide:
1013                         group_label.set_text (_("Grp"));
1014                         break;
1015                 case Narrow:
1016                         group_label.set_text (_("~G"));
1017                         break;
1018                 }
1019         }
1020 }
1021
1022
1023 void 
1024 MixerStrip::route_gui_changed (string what_changed, void* ignored)
1025 {
1026         ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed), what_changed, ignored));
1027         
1028         if (what_changed == "color") {
1029                 if (set_color_from_route () == 0) {
1030                         show_route_color ();
1031                 }
1032         }
1033 }
1034
1035 void
1036 MixerStrip::show_route_color ()
1037 {
1038         name_button.modify_bg (STATE_NORMAL, color());
1039         top_event_box.modify_bg (STATE_NORMAL, color());
1040         route_active_changed ();
1041 }
1042
1043 void
1044 MixerStrip::show_passthru_color ()
1045 {
1046         route_active_changed ();
1047 }
1048
1049 void
1050 MixerStrip::build_route_ops_menu ()
1051 {
1052         using namespace Menu_Helpers;
1053         route_ops_menu = new Menu;
1054         route_ops_menu->set_name ("ArdourContextMenu");
1055
1056         MenuList& items = route_ops_menu->items();
1057
1058         items.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template)));
1059         items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename)));
1060         rename_menu_item = &items.back();
1061         items.push_back (SeparatorElem());
1062         items.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active)));
1063         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1064         route_active_menu_item->set_active (_route->active());
1065         items.push_back (SeparatorElem());
1066         items.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity)));
1067         polarity_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1068         polarity_menu_item->set_active (_route->phase_invert());
1069         items.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1070         denormal_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
1071         denormal_menu_item->set_active (_route->denormal_protection());
1072
1073         if (!Profile->get_sae()) {
1074                 build_remote_control_menu ();
1075                 items.push_back (SeparatorElem());
1076                 items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
1077         }
1078
1079         items.push_back (SeparatorElem());
1080         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
1081 }
1082
1083 gint
1084 MixerStrip::name_button_button_press (GdkEventButton* ev)
1085 {
1086         if (ev->button == 1 || ev->button == 3) {
1087                 list_route_operations ();
1088                 /* do not allow rename if the track is record-enabled */
1089                 rename_menu_item->set_sensitive (!_route->record_enabled());
1090                 route_ops_menu->popup (1, ev->time);
1091         }
1092         return FALSE;
1093 }
1094
1095 void
1096 MixerStrip::list_route_operations ()
1097 {
1098         if (route_ops_menu == 0) {
1099                 build_route_ops_menu ();
1100         }
1101         
1102         refresh_remote_control_menu();
1103 }
1104
1105
1106 void
1107 MixerStrip::speed_adjustment_changed ()
1108 {
1109         /* since there is a usable speed adjustment, there has to be a diskstream */
1110         if (!ignore_speed_adjustment) {
1111                 get_diskstream()->set_speed (speed_adjustment.get_value());
1112         }
1113 }
1114
1115 void
1116 MixerStrip::speed_changed ()
1117 {
1118         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_speed_display));
1119 }
1120
1121 void
1122 MixerStrip::update_speed_display ()
1123 {
1124         float val;
1125         
1126         val = get_diskstream()->speed();
1127
1128         if (val != 1.0) {
1129                 speed_spinner.set_name ("MixerStripSpeedBaseNotOne");
1130         } else {
1131                 speed_spinner.set_name ("MixerStripSpeedBase");
1132         }
1133
1134         if (speed_adjustment.get_value() != val) {
1135                 ignore_speed_adjustment = true;
1136                 speed_adjustment.set_value (val);
1137                 ignore_speed_adjustment = false;
1138         }
1139 }                       
1140
1141
1142 void
1143 MixerStrip::set_selected (bool yn)
1144 {
1145         AxisView::set_selected (yn);
1146         if (_selected) {
1147                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1148                 global_frame.set_name ("MixerStripSelectedFrame");
1149         } else {
1150                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1151                 global_frame.set_name ("MixerStripFrame");
1152         }
1153         global_frame.queue_draw ();
1154 }
1155
1156 void
1157 MixerStrip::name_changed (void *src)
1158 {
1159         switch (_width) {
1160         case Wide:
1161                 RouteUI::name_changed (src);
1162                 break;
1163         case Narrow:
1164                 name_label.set_text (PBD::short_version (_route->name(), 5));
1165                 break;
1166         }
1167         if (_route->phase_invert()) {
1168                 name_label.set_text (X_("Ø ") + name_label.get_text());
1169         }
1170 }
1171
1172 void
1173 MixerStrip::width_clicked ()
1174 {
1175         switch (_width) {
1176         case Wide:
1177                 set_width (Narrow, this);
1178                 break;
1179         case Narrow:
1180                 set_width (Wide, this);
1181                 break;
1182         }
1183 }
1184
1185 void
1186 MixerStrip::hide_clicked ()
1187 {
1188         // LAME fix to reset the button status for when it is redisplayed (part 1)
1189         hide_button.set_sensitive(false);
1190         
1191         if (_embedded) {
1192                 Hiding(); /* EMIT_SIGNAL */
1193         } else {
1194                 _mixer.hide_strip (this);
1195         }
1196         
1197         // (part 2)
1198         hide_button.set_sensitive(true);
1199 }
1200
1201 void
1202 MixerStrip::set_embedded (bool yn)
1203 {
1204         _embedded = yn;
1205 }
1206
1207 void
1208 MixerStrip::map_frozen ()
1209 {
1210         ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen));
1211
1212         boost::shared_ptr<AudioTrack> at = audio_track();
1213
1214         if (at) {
1215                 switch (at->freeze_state()) {
1216                 case AudioTrack::Frozen:
1217                         pre_redirect_box.set_sensitive (false);
1218                         post_redirect_box.set_sensitive (false);
1219                         speed_spinner.set_sensitive (false);
1220                         break;
1221                 default:
1222                         pre_redirect_box.set_sensitive (true);
1223                         post_redirect_box.set_sensitive (true);
1224                         speed_spinner.set_sensitive (true);
1225                         // XXX need some way, maybe, to retoggle redirect editors
1226                         break;
1227                 }
1228         }
1229         
1230         hide_redirect_editors ();
1231 }
1232
1233 void
1234 MixerStrip::hide_redirect_editors ()
1235 {
1236         _route->foreach_redirect (this, &MixerStrip::hide_redirect_editor);
1237 }
1238
1239 void
1240 MixerStrip::hide_redirect_editor (boost::shared_ptr<Redirect> redirect)
1241 {
1242         void* gui = redirect->get_gui ();
1243         
1244         if (gui) {
1245                 static_cast<Gtk::Widget*>(gui)->hide ();
1246         }
1247 }
1248
1249 void
1250 MixerStrip::route_active_changed ()
1251 {
1252         RouteUI::route_active_changed ();
1253
1254         if (is_audio_track()) {
1255                 if (_route->active()) {
1256                         set_name ("AudioTrackStripBase");
1257                         gpm.set_meter_strip_name ("AudioTrackMetrics");
1258                 } else {
1259                         set_name ("AudioTrackStripBaseInactive");
1260                         gpm.set_meter_strip_name ("AudioTrackMetricsInactive");
1261                 }
1262                 gpm.set_fader_name ("AudioTrackFader");
1263         } else { // FIXME: assumed audio bus
1264                 if (_route->active()) {
1265                         set_name ("AudioBusStripBase");
1266                         gpm.set_meter_strip_name ("AudioBusMetrics");
1267                 } else {
1268                         set_name ("AudioBusStripBaseInactive");
1269                         gpm.set_meter_strip_name ("AudioBusMetricsInactive");
1270                 }
1271                 gpm.set_fader_name ("AudioBusFader");
1272         }
1273 }
1274
1275 RouteGroup*
1276 MixerStrip::mix_group() const
1277 {
1278         return _route->mix_group();
1279 }
1280
1281 void
1282 MixerStrip::engine_stopped ()
1283 {
1284 }
1285
1286 void
1287 MixerStrip::engine_running ()
1288 {
1289 }
1290
1291 void
1292 MixerStrip::meter_changed (void *src)
1293 {
1294
1295         ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed), src));
1296
1297         switch (_route->meter_point()) {
1298         case MeterInput:
1299                 meter_point_label.set_text (_("input"));
1300                 break;
1301                 
1302         case MeterPreFader:
1303                 meter_point_label.set_text (_("pre"));
1304                 break;
1305                 
1306         case MeterPostFader:
1307                 meter_point_label.set_text (_("post"));
1308                 break;
1309         }
1310         
1311         gpm.setup_meters ();
1312         // reset peak when meter point changes
1313         gpm.reset_peak_display();
1314         set_width (_width, this);
1315 }
1316