53124723ef19a5e2d23753ee237ac91d6d739685
[ardour.git] / gtk2_ardour / route_time_axis.cc
1 /*
2     Copyright (C) 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 <cstdlib>
20 #include <cmath>
21 #include <cassert>
22
23 #include <algorithm>
24 #include <string>
25 #include <vector>
26 #include <map>
27 #include <utility>
28
29 #include <sigc++/bind.h>
30
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
37
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
44
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
53
54 #include "evoral/Parameter.hpp"
55
56 #include "canvas/debug.h"
57
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
60 #include "debug.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
64 #include "enums.h"
65 #include "gui_thread.h"
66 #include "keyboard.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
69 #include "prompter.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
75 #include "utils.h"
76 #include "route_group_menu.h"
77
78 #include "ardour/track.h"
79
80 #include "i18n.h"
81
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
84 using namespace PBD;
85 using namespace Gtkmm2ext;
86 using namespace Gtk;
87 using namespace Editing;
88 using namespace std;
89 using std::list;
90
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
92         : AxisView(sess)
93         , RouteUI(sess)
94         , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
95         , _view (0)
96         , parent_canvas (canvas)
97         , no_redraw (false)
98         , button_table (3, 3)
99         , route_group_button (_("G"))
100         , playlist_button (_("P"))
101         , automation_button (_("A"))
102         , automation_action_menu (0)
103         , plugins_submenu_item (0)
104         , route_group_menu (0)
105         , playlist_action_menu (0)
106         , mode_menu (0)
107         , color_mode_menu (0)
108         , gm (sess, true, 75, 14)
109         , _ignore_set_layer_display (false)
110 {
111         number_label.set_name("route button");
112         number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
113         number_label.set_alignment(.5, .5);
114         number_label.set_fallthrough_to_parent (true);
115
116         sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
117 }
118
119 void
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 {
122         RouteUI::set_route (rt);
123
124         CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
125         CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
126         CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
127
128         int meter_width = 3;
129         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
130                 meter_width = 6;
131         }
132         gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133         gm.get_level_meter().set_no_show_all();
134         gm.get_level_meter().setup_meters(50, meter_width);
135         gm.update_gain_sensitive ();
136
137         string str = gui_property ("height");
138         if (!str.empty()) {
139                 set_height (atoi (str));
140         } else {
141                 set_height (preset_height (HeightNormal));
142         }
143
144         if (!_route->is_auditioner()) {
145                 if (gui_property ("visible").empty()) {
146                         set_gui_property ("visible", true);
147                 }
148         } else {
149                 set_gui_property ("visible", false);
150         }
151
152         mute_changed (0);
153         update_solo_display ();
154
155         timestretch_rect = 0;
156         no_redraw = false;
157
158         ignore_toggle = false;
159
160         route_group_button.set_name ("route button");
161         playlist_button.set_name ("route button");
162         automation_button.set_name ("route button");
163
164         route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
165         playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
166         automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
167
168         if (is_track()) {
169
170                 /* use icon */
171                 
172                 switch (track()->mode()) {
173                 case ARDOUR::Normal:
174                 case ARDOUR::NonLayered:
175                         rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
176                         rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
177                         break;
178                 case ARDOUR::Destructive:
179                         rec_enable_button->set_text (string());
180                         rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
181                         break;
182                 }
183
184                 if (ARDOUR::Profile->get_mixbus()) {
185                         controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
186                 } else {
187                         controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
188                 }
189                 controls_button_size_group->add_widget(*rec_enable_button);
190
191                 if (is_midi_track()) {
192                         ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
193                         gm.set_fader_name ("MidiTrackFader");
194                 } else {
195                         ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
196                         gm.set_fader_name ("AudioTrackFader");
197                 }
198
199                 rec_enable_button->set_sensitive (_session->writable());
200                 
201                 /* set playlist button tip to the current playlist, and make it update when it changes */
202                 update_playlist_tip ();
203                 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
204
205         } else {
206                 gm.set_fader_name ("AudioBusFader");
207                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208                 controls_button_size_group->add_widget(*blank);
209                 if (ARDOUR::Profile->get_mixbus() ) {
210                         controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
211                 } else {
212                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213                 }
214                 blank->show();
215         }
216
217         top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
218
219         if (!ARDOUR::Profile->get_mixbus()) {
220                 controls_meters_size_group->add_widget (gm.get_level_meter());
221         }
222
223         _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224         _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225         _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226         _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
227
228         if (ARDOUR::Profile->get_mixbus()) {
229                 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
230         } else {
231                 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
232         }
233         controls_button_size_group->add_widget(*mute_button);
234
235         if (!_route->is_master()) {
236                 if (ARDOUR::Profile->get_mixbus()) {
237                         controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
238                 } else {
239                         controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
240                 }
241                 controls_button_size_group->add_widget(*solo_button);
242         } else {
243                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
244                 controls_button_size_group->add_widget(*blank);
245                 if (ARDOUR::Profile->get_mixbus()) {
246                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247                 } else {
248                         controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249                 }
250                 blank->show();
251         }
252
253         if (ARDOUR::Profile->get_mixbus()) {
254                 controls_button_size_group->add_widget(route_group_button);
255                 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256                 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
257         }
258         else if (!ARDOUR::Profile->get_trx()) {
259                 controls_button_size_group->add_widget(route_group_button);
260                 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261                 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
262         }
263         
264         ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
265         ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
266         ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
267
268         if (is_midi_track()) {
269                 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
270         } else {
271                 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
272         }
273
274         update_track_number_visibility();
275         label_view ();
276
277         if (ARDOUR::Profile->get_mixbus()) {
278                 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279                 controls_button_size_group->add_widget(automation_button);
280         }
281         else if (!ARDOUR::Profile->get_trx()) {
282                 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283                 controls_button_size_group->add_widget(automation_button);
284         }
285
286         if (is_track() && track()->mode() == ARDOUR::Normal) {
287                 if (ARDOUR::Profile->get_mixbus()) {
288                         controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
289                         controls_button_size_group->add_widget(playlist_button);
290                 }
291                 else if (!ARDOUR::Profile->get_trx()) {
292                         controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293                         controls_button_size_group->add_widget(playlist_button);
294                 }
295         }
296
297         _y_position = -1;
298
299         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
300         _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
301
302         if (is_track()) {
303
304                 str = gui_property ("layer-display");
305                 if (!str.empty()) {
306                         set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
307                 }
308
309                 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
310                 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
311
312                 /* pick up the correct freeze state */
313                 map_frozen ();
314
315         } 
316
317         _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
318         ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
319
320         PropertyList* plist = new PropertyList();
321
322         plist->add (ARDOUR::Properties::mute, true);
323         plist->add (ARDOUR::Properties::solo, true);
324
325         route_group_menu = new RouteGroupMenu (_session, plist);
326
327         gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
328
329         gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
330 }
331
332 RouteTimeAxisView::~RouteTimeAxisView ()
333 {
334         CatchDeletion (this);
335
336         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
337                 delete *i;
338         }
339
340         delete playlist_action_menu;
341         playlist_action_menu = 0;
342
343         delete _view;
344         _view = 0;
345
346         _automation_tracks.clear ();
347
348         delete route_group_menu;
349 }
350
351 void
352 RouteTimeAxisView::post_construct ()
353 {
354         /* map current state of the route */
355
356         update_diskstream_display ();
357         setup_processor_menu_and_curves ();
358         reset_processor_automation_curves ();
359 }
360
361 /** Set up the processor menu for the current set of processors, and
362  *  display automation curves for any parameters which have data.
363  */
364 void
365 RouteTimeAxisView::setup_processor_menu_and_curves ()
366 {
367         _subplugin_menu_map.clear ();
368         subplugin_menu.items().clear ();
369         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
370         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
371 }
372
373 gint
374 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
375 {
376         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
377                 if (_route->route_group()) {
378                         _route->route_group()->remove (_route);
379                 }
380                 return false;
381         }
382
383         WeakRouteList r;
384         r.push_back (route ());
385
386         route_group_menu->build (r);
387         route_group_menu->menu()->popup (ev->button, ev->time);
388
389         return false;
390 }
391
392 void
393 RouteTimeAxisView::playlist_changed ()
394 {
395         label_view ();
396 }
397
398 void
399 RouteTimeAxisView::label_view ()
400 {
401         string x = _route->name ();
402         if (x != name_label.get_text ()) {
403                 name_label.set_text (x);
404         }
405         const int64_t track_number = _route->track_number ();
406         if (track_number == 0) {
407                 number_label.set_text ("");
408         } else {
409                 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
410         }
411 }
412
413 void
414 RouteTimeAxisView::update_track_number_visibility ()
415 {
416         DisplaySuspender ds;
417         bool show_label = _session->config.get_track_name_number();
418
419         if (_route && _route->is_master()) {
420                 show_label = false;
421         }
422
423         if (number_label.get_parent()) {
424                 controls_table.remove (number_label);
425         }
426         if (show_label) {
427                 if (ARDOUR::Profile->get_mixbus()) {
428                         controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
429                 } else {
430                         controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
431                 }
432                 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
433                 number_label.set_size_request(3 + tnw, -1);
434                 number_label.show ();
435                 // -5 = -2 for table border - 3 for padding ^^
436                 name_hbox.set_size_request(TimeAxisView::name_width_px - 5 - tnw, 0);
437         } else {
438                 number_label.hide ();
439                 name_hbox.set_size_request(TimeAxisView::name_width_px, 0);
440         }
441 }
442
443 void
444 RouteTimeAxisView::parameter_changed (string const & p)
445 {
446         if (p == "track-name-number") {
447                 update_track_number_visibility();
448         }
449 }
450
451 void
452 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
453 {
454         if (what_changed.contains (ARDOUR::Properties::name)) {
455                 label_view ();
456         }
457 }
458
459 void
460 RouteTimeAxisView::take_name_changed (void *src)
461 {
462         if (src != this) {
463                 label_view ();
464         }
465 }
466
467 void
468 RouteTimeAxisView::playlist_click ()
469 {
470         build_playlist_menu ();
471         conditionally_add_to_selection ();
472         playlist_action_menu->popup (1, gtk_get_current_event_time());
473 }
474
475 void
476 RouteTimeAxisView::automation_click ()
477 {
478         conditionally_add_to_selection ();
479         build_automation_action_menu (false);
480         automation_action_menu->popup (1, gtk_get_current_event_time());
481 }
482
483 void
484 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
485 {
486         using namespace Menu_Helpers;
487
488         /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
489            otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
490         */
491
492         detach_menu (subplugin_menu);
493
494         _main_automation_menu_map.clear ();
495         delete automation_action_menu;
496         automation_action_menu = new Menu;
497
498         MenuList& items = automation_action_menu->items();
499
500         automation_action_menu->set_name ("ArdourContextMenu");
501
502         items.push_back (MenuElem (_("Show All Automation"),
503                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
504
505         items.push_back (MenuElem (_("Show Existing Automation"),
506                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
507
508         items.push_back (MenuElem (_("Hide All Automation"),
509                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
510
511         /* Attach the plugin submenu. It may have previously been used elsewhere,
512            so it was detached above 
513         */
514         
515         if (!subplugin_menu.items().empty()) {
516                 items.push_back (SeparatorElem ());
517                 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
518                 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
519         }
520 }
521
522 void
523 RouteTimeAxisView::build_display_menu ()
524 {
525         using namespace Menu_Helpers;
526
527         /* prepare it */
528
529         TimeAxisView::build_display_menu ();
530
531         /* now fill it with our stuff */
532
533         MenuList& items = display_menu->items();
534         display_menu->set_name ("ArdourContextMenu");
535
536         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
537
538         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
539
540         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
541
542         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
543
544         items.push_back (SeparatorElem());
545
546         if (_size_menu) {
547                 detach_menu (*_size_menu);
548         }
549         build_size_menu ();
550         items.push_back (MenuElem (_("Height"), *_size_menu));
551
552         items.push_back (SeparatorElem());
553
554         if (!Profile->get_sae()) {
555                 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
556                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
557                 items.push_back (SeparatorElem());
558         }
559
560         // Hook for derived classes to add type specific stuff
561         append_extra_display_menu_items ();
562
563         if (is_track()) {
564
565                 Menu* layers_menu = manage (new Menu);
566                 MenuList &layers_items = layers_menu->items();
567                 layers_menu->set_name("ArdourContextMenu");
568
569                 RadioMenuItem::Group layers_group;
570
571                 /* Find out how many overlaid/stacked tracks we have in the selection */
572
573                 int overlaid = 0;
574                 int stacked = 0;
575                 TrackSelection const & s = _editor.get_selection().tracks;
576                 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
577                         StreamView* v = (*i)->view ();
578                         if (!v) {
579                                 continue;
580                         }
581
582                         switch (v->layer_display ()) {
583                         case Overlaid:
584                                 ++overlaid;
585                                 break;
586                         case Stacked:
587                         case Expanded:
588                                 ++stacked;
589                                 break;
590                         }
591                 }
592
593                 /* We're not connecting to signal_toggled() here; in the case where these two items are
594                    set to be in the `inconsistent' state, it seems that one or other will end up active
595                    as well as inconsistent (presumably due to the RadioMenuItem::Group).  Then when you
596                    select the active one, no toggled signal is emitted so nothing happens.
597                 */
598
599                 _ignore_set_layer_display = true;
600                 
601                 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
602                 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
603                 i->set_active (overlaid != 0 && stacked == 0);
604                 i->set_inconsistent (overlaid != 0 && stacked != 0);
605                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
606
607                 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
608                 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
609                 i->set_active (overlaid == 0 && stacked != 0);
610                 i->set_inconsistent (overlaid != 0 && stacked != 0);
611                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
612                 
613                 _ignore_set_layer_display = false;
614
615                 items.push_back (MenuElem (_("Layers"), *layers_menu));
616
617                 if (!Profile->get_sae()) {
618
619                         Menu* alignment_menu = manage (new Menu);
620                         MenuList& alignment_items = alignment_menu->items();
621                         alignment_menu->set_name ("ArdourContextMenu");
622
623                         RadioMenuItem::Group align_group;
624
625                         /* Same verbose hacks as for the layering options above */
626
627                         int existing = 0;
628                         int capture = 0;
629                         int automatic = 0;
630                         int styles = 0;
631                         boost::shared_ptr<Track> first_track;
632
633                         TrackSelection const & s = _editor.get_selection().tracks;
634                         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
635                                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
636                                 if (!r || !r->is_track ()) {
637                                         continue;
638                                 }
639
640                                 if (!first_track) {
641                                         first_track = r->track();
642                                 }
643
644                                 switch (r->track()->alignment_choice()) {
645                                 case Automatic:
646                                         ++automatic;
647                                         styles |= 0x1;
648                                         switch (r->track()->alignment_style()) {
649                                         case ExistingMaterial:
650                                                 ++existing;
651                                                 break;
652                                         case CaptureTime:
653                                                 ++capture;
654                                                 break;
655                                         }
656                                         break;
657                                 case UseExistingMaterial:
658                                         ++existing;
659                                         styles |= 0x2;
660                                         break;
661                                 case UseCaptureTime:
662                                         ++capture;
663                                         styles |= 0x4;
664                                         break;
665                                 }
666                         }
667
668                         bool inconsistent;
669                         switch (styles) {
670                         case 1:
671                         case 2:
672                         case 4:
673                                 inconsistent = false;
674                                 break;
675                         default:
676                                 inconsistent = true;
677                                 break;
678                         }
679
680                         RadioMenuItem* i;
681
682                         if (!inconsistent && first_track) {
683
684                                 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
685                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
686                                 i->set_active (automatic != 0 && existing == 0 && capture == 0);
687                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
688
689                                 switch (first_track->alignment_choice()) {
690                                 case Automatic:
691                                         switch (first_track->alignment_style()) {
692                                         case ExistingMaterial:
693                                                 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
694                                                 break;
695                                         case CaptureTime:
696                                                 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
697                                                 break;
698                                         }
699                                         break;
700                                 default:
701                                         break;
702                                 }
703
704                                 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
705                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
706                                 i->set_active (existing != 0 && capture == 0 && automatic == 0);
707                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
708
709                                 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
710                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
711                                 i->set_active (existing == 0 && capture != 0 && automatic == 0);
712                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
713
714                                 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
715
716                         } else {
717                                 /* show nothing */
718                         }
719
720                         Menu* mode_menu = manage (new Menu);
721                         MenuList& mode_items = mode_menu->items ();
722                         mode_menu->set_name ("ArdourContextMenu");
723
724                         RadioMenuItem::Group mode_group;
725
726                         int normal = 0;
727                         int tape = 0;
728                         int non_layered = 0;
729
730                         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
731                                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
732                                 if (!r || !r->is_track ()) {
733                                         continue;
734                                 }
735
736                                 switch (r->track()->mode()) {
737                                 case Normal:
738                                         ++normal;
739                                         break;
740                                 case Destructive:
741                                         ++tape;
742                                         break;
743                                 case NonLayered:
744                                         ++non_layered;
745                                         break;
746                                 }
747                         }
748
749                         mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
750                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
751                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
752                         i->set_active (normal != 0 && tape == 0 && non_layered == 0);
753                         i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
754
755                         mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
756                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
757                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
758                         i->set_active (normal == 0 && tape != 0 && non_layered == 0);
759                         i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
760
761                         mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
762                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
763                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
764                         i->set_active (normal == 0 && tape == 0 && non_layered != 0);
765                         i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
766
767                         items.push_back (MenuElem (_("Mode"), *mode_menu));
768                 }
769
770
771                 items.push_back (SeparatorElem());
772
773                 build_playlist_menu ();
774                 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
775                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
776         }
777
778         route_group_menu->detach ();
779         
780         WeakRouteList r;
781         for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
782                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
783                 if (rtv) {
784                         r.push_back (rtv->route ());
785                 }
786         }
787         
788         if (r.empty ()) {
789                 r.push_back (route ());
790         }
791
792         route_group_menu->build (r);
793         items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
794         
795         build_automation_action_menu (true);
796         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
797         
798         items.push_back (SeparatorElem());
799         
800         int active = 0;
801         int inactive = 0;
802         TrackSelection const & s = _editor.get_selection().tracks;
803         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
804                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
805                 if (!r) {
806                         continue;
807                 }
808
809                 if (r->route()->active()) {
810                         ++active;
811                 } else {
812                         ++inactive;
813                 }
814         }
815
816         items.push_back (CheckMenuElem (_("Active")));
817         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
818         bool click_sets_active = true;
819         if (active > 0 && inactive == 0) {
820                 i->set_active (true);
821                 click_sets_active = false;
822         } else if (active > 0 && inactive > 0) {
823                 i->set_inconsistent (true);
824         }
825         i->set_sensitive(! _session->transport_rolling());
826         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
827
828         items.push_back (SeparatorElem());
829         items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
830         if (!Profile->get_sae()) {
831                 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
832         } else {
833                 items.push_front (SeparatorElem());
834                 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
835         }
836 }
837
838 void
839 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
840 {
841         if (apply_to_selection) {
842                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
843         } else {
844
845                 bool needs_bounce = false;
846
847                 if (!track()->can_use_mode (mode, needs_bounce)) {
848
849                         if (!needs_bounce) {
850                                 /* cannot be done */
851                                 return;
852                         } else {
853                                 cerr << "would bounce this one\n";
854                                 return;
855                         }
856                 }
857
858                 track()->set_mode (mode);
859
860                 rec_enable_button->remove ();
861
862                 switch (mode) {
863                 case ARDOUR::NonLayered:
864                 case ARDOUR::Normal:
865                         rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
866                         rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
867                         break;
868                 case ARDOUR::Destructive:
869                         rec_enable_button->set_text (string());
870                         rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
871                         break;
872                 }
873
874                 rec_enable_button->show_all ();
875         }
876 }
877
878 void
879 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
880 {
881         TimeAxisView::show_timestretch (start, end, layers, layer);
882
883         hide_timestretch ();
884
885 #if 0
886         if (ts.empty()) {
887                 return;
888         }
889
890
891         /* check that the time selection was made in our route, or our route group.
892            remember that route_group() == 0 implies the route is *not* in a edit group.
893         */
894
895         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
896                 /* this doesn't apply to us */
897                 return;
898         }
899
900         /* ignore it if our edit group is not active */
901
902         if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
903                 return;
904         }
905 #endif
906
907         if (timestretch_rect == 0) {
908                 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
909                 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
910                 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
911         }
912
913         timestretch_rect->show ();
914         timestretch_rect->raise_to_top ();
915
916         double const x1 = start / _editor.get_current_zoom();
917         double const x2 = (end - 1) / _editor.get_current_zoom();
918
919         timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
920                                                    x2, current_height() * (layers - layer) / layers));
921 }
922
923 void
924 RouteTimeAxisView::hide_timestretch ()
925 {
926         TimeAxisView::hide_timestretch ();
927
928         if (timestretch_rect) {
929                 timestretch_rect->hide ();
930         }
931 }
932
933 void
934 RouteTimeAxisView::show_selection (TimeSelection& ts)
935 {
936
937 #if 0
938         /* ignore it if our edit group is not active or if the selection was started
939            in some other track or route group (remember that route_group() == 0 means
940            that the track is not in an route group).
941         */
942
943         if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
944             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
945                 hide_selection ();
946                 return;
947         }
948 #endif
949
950         TimeAxisView::show_selection (ts);
951 }
952
953 void
954 RouteTimeAxisView::set_height (uint32_t h)
955 {
956         int gmlen = h - 9;
957         bool height_changed = (height == 0) || (h != height);
958
959         int meter_width = 3;
960         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
961                 meter_width = 6;
962         }
963         gm.get_level_meter().setup_meters (gmlen, meter_width);
964
965         TimeAxisView::set_height (h);
966
967         if (_view) {
968                 _view->set_height ((double) current_height());
969         }
970
971         if (height >= preset_height (HeightNormal)) {
972
973                 reset_meter();
974
975                 gm.get_gain_slider().show();
976                 mute_button->show();
977                 if (!_route || _route->is_monitor()) {
978                         solo_button->hide();
979                 } else {
980                         solo_button->show();
981                 }
982                 if (rec_enable_button)
983                         rec_enable_button->show();
984
985                 route_group_button.show();
986                 automation_button.show();
987
988                 if (is_track() && track()->mode() == ARDOUR::Normal) {
989                         playlist_button.show();
990                 }
991
992         } else {
993
994                 reset_meter();
995
996                 gm.get_gain_slider().hide();
997                 mute_button->show();
998                 if (!_route || _route->is_monitor()) {
999                         solo_button->hide();
1000                 } else {
1001                         solo_button->show();
1002                 }
1003                 if (rec_enable_button)
1004                         rec_enable_button->show();
1005
1006                 route_group_button.hide ();
1007                 automation_button.hide ();
1008
1009                 if (is_track() && track()->mode() == ARDOUR::Normal) {
1010                         playlist_button.hide ();
1011                 }
1012
1013         }
1014
1015         if (height_changed && !no_redraw) {
1016                 /* only emit the signal if the height really changed */
1017                 request_redraw ();
1018         }
1019 }
1020
1021 void
1022 RouteTimeAxisView::route_color_changed ()
1023 {
1024         if (_view) {
1025                 _view->apply_color (color(), StreamView::RegionColor);
1026         }
1027
1028         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1029 }
1030
1031 void
1032 RouteTimeAxisView::reset_samples_per_pixel ()
1033 {
1034         set_samples_per_pixel (_editor.get_current_zoom());
1035 }
1036
1037 void
1038 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1039 {
1040         double speed = 1.0;
1041
1042         if (track()) {
1043                 speed = track()->speed();
1044         }
1045
1046         if (_view) {
1047                 _view->set_samples_per_pixel (fpp * speed);
1048         }
1049
1050         TimeAxisView::set_samples_per_pixel (fpp * speed);
1051 }
1052
1053 void
1054 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1055 {
1056         if (!mitem->get_active()) {
1057                 /* this is one of the two calls made when these radio menu items change status. this one
1058                    is for the item that became inactive, and we want to ignore it.
1059                 */
1060                 return;
1061         }
1062
1063         if (apply_to_selection) {
1064                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1065         } else {
1066                 if (track ()) {
1067                         track()->set_align_choice (choice);
1068                 }
1069         }
1070 }
1071
1072 void
1073 RouteTimeAxisView::rename_current_playlist ()
1074 {
1075         ArdourPrompter prompter (true);
1076         string name;
1077
1078         boost::shared_ptr<Track> tr = track();
1079         if (!tr || tr->destructive()) {
1080                 return;
1081         }
1082
1083         boost::shared_ptr<Playlist> pl = tr->playlist();
1084         if (!pl) {
1085                 return;
1086         }
1087
1088         prompter.set_title (_("Rename Playlist"));
1089         prompter.set_prompt (_("New name for playlist:"));
1090         prompter.set_initial_text (pl->name());
1091         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1092         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1093
1094         switch (prompter.run ()) {
1095         case Gtk::RESPONSE_ACCEPT:
1096                 prompter.get_result (name);
1097                 if (name.length()) {
1098                         pl->set_name (name);
1099                 }
1100                 break;
1101
1102         default:
1103                 break;
1104         }
1105 }
1106
1107 std::string
1108 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1109 {
1110         std::string ret (basename);
1111
1112         std::string const group_string = "." + route_group()->name() + ".";
1113
1114         // iterate through all playlists
1115         int maxnumber = 0;
1116         for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1117                 std::string tmp = (*i)->name();
1118
1119                 std::string::size_type idx = tmp.find(group_string);
1120                 // find those which belong to this group
1121                 if (idx != string::npos) {
1122                         tmp = tmp.substr(idx + group_string.length());
1123
1124                         // and find the largest current number
1125                         int x = atoi(tmp);
1126                         if (x > maxnumber) {
1127                                 maxnumber = x;
1128                         }
1129                 }
1130         }
1131
1132         maxnumber++;
1133
1134         char buf[32];
1135         snprintf (buf, sizeof(buf), "%d", maxnumber);
1136
1137         ret = this->name() + "." + route_group()->name () + "." + buf;
1138
1139         return ret;
1140 }
1141
1142 void
1143 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1144 {
1145         string name;
1146
1147         boost::shared_ptr<Track> tr = track ();
1148         if (!tr || tr->destructive()) {
1149                 return;
1150         }
1151
1152         boost::shared_ptr<const Playlist> pl = tr->playlist();
1153         if (!pl) {
1154                 return;
1155         }
1156
1157         name = pl->name();
1158
1159         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1160                 name = resolve_new_group_playlist_name(name, playlists_before_op);
1161         }
1162
1163         while (_session->playlists->by_name(name)) {
1164                 name = Playlist::bump_name (name, *_session);
1165         }
1166
1167         // TODO: The prompter "new" button should be de-activated if the user
1168         // specifies a playlist name which already exists in the session.
1169
1170         if (prompt) {
1171
1172                 ArdourPrompter prompter (true);
1173
1174                 prompter.set_title (_("New Copy Playlist"));
1175                 prompter.set_prompt (_("Name for new playlist:"));
1176                 prompter.set_initial_text (name);
1177                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1178                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1179                 prompter.show_all ();
1180
1181                 switch (prompter.run ()) {
1182                 case Gtk::RESPONSE_ACCEPT:
1183                         prompter.get_result (name);
1184                         break;
1185
1186                 default:
1187                         return;
1188                 }
1189         }
1190
1191         if (name.length()) {
1192                 tr->use_copy_playlist ();
1193                 tr->playlist()->set_name (name);
1194         }
1195 }
1196
1197 void
1198 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1199 {
1200         string name;
1201
1202         boost::shared_ptr<Track> tr = track ();
1203         if (!tr || tr->destructive()) {
1204                 return;
1205         }
1206
1207         boost::shared_ptr<const Playlist> pl = tr->playlist();
1208         if (!pl) {
1209                 return;
1210         }
1211
1212         name = pl->name();
1213
1214         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1215                 name = resolve_new_group_playlist_name(name,playlists_before_op);
1216         }
1217
1218         while (_session->playlists->by_name(name)) {
1219                 name = Playlist::bump_name (name, *_session);
1220         }
1221
1222
1223         if (prompt) {
1224
1225                 ArdourPrompter prompter (true);
1226
1227                 prompter.set_title (_("New Playlist"));
1228                 prompter.set_prompt (_("Name for new playlist:"));
1229                 prompter.set_initial_text (name);
1230                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1231                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1232
1233                 switch (prompter.run ()) {
1234                 case Gtk::RESPONSE_ACCEPT:
1235                         prompter.get_result (name);
1236                         break;
1237
1238                 default:
1239                         return;
1240                 }
1241         }
1242
1243         if (name.length()) {
1244                 tr->use_new_playlist ();
1245                 tr->playlist()->set_name (name);
1246         }
1247 }
1248
1249 void
1250 RouteTimeAxisView::clear_playlist ()
1251 {
1252         boost::shared_ptr<Track> tr = track ();
1253         if (!tr || tr->destructive()) {
1254                 return;
1255         }
1256
1257         boost::shared_ptr<Playlist> pl = tr->playlist();
1258         if (!pl) {
1259                 return;
1260         }
1261
1262         _editor.clear_playlist (pl);
1263 }
1264
1265 void
1266 RouteTimeAxisView::speed_changed ()
1267 {
1268         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1269 }
1270
1271 void
1272 RouteTimeAxisView::update_diskstream_display ()
1273 {
1274         if (!track()) {
1275                 return;
1276         }
1277
1278         map_frozen ();
1279 }
1280
1281 void
1282 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1283 {
1284         if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1285
1286                 /* special case: select/deselect all tracks */
1287                 if (_editor.get_selection().selected (this)) {
1288                         _editor.get_selection().clear_tracks ();
1289                 } else {
1290                         _editor.select_all_tracks ();
1291                 }
1292
1293                 return;
1294         }
1295
1296         switch (ArdourKeyboard::selection_type (ev->state)) {
1297         case Selection::Toggle:
1298                 _editor.get_selection().toggle (this);
1299                 break;
1300
1301         case Selection::Set:
1302                 _editor.get_selection().set (this);
1303                 break;
1304
1305         case Selection::Extend:
1306                 _editor.extend_selection_to_track (*this);
1307                 break;
1308
1309         case Selection::Add:
1310                 _editor.get_selection().add (this);
1311                 break;
1312         }
1313 }
1314
1315 void
1316 RouteTimeAxisView::set_selected_points (PointSelection& points)
1317 {
1318         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1319                 (*i)->set_selected_points (points);
1320         }
1321 }
1322
1323 void
1324 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1325 {
1326         if (_view) {
1327                 _view->set_selected_regionviews (regions);
1328         }
1329 }
1330
1331 /** Add the selectable things that we have to a list.
1332  * @param results List to add things to.
1333  */
1334 void
1335 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1336 {
1337         double speed = 1.0;
1338
1339         if (track() != 0) {
1340                 speed = track()->speed();
1341         }
1342
1343         framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1344         framepos_t const end_adjusted   = session_frame_to_track_frame(end, speed);
1345
1346         if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1347                 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1348         }
1349
1350         /* pick up visible automation tracks */
1351
1352         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1353                 if (!(*i)->hidden()) {
1354                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1355                 }
1356         }
1357 }
1358
1359 void
1360 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1361 {
1362         if (_view) {
1363                 _view->get_inverted_selectables (sel, results);
1364         }
1365
1366         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1367                 if (!(*i)->hidden()) {
1368                         (*i)->get_inverted_selectables (sel, results);
1369                 }
1370         }
1371
1372         return;
1373 }
1374
1375 RouteGroup*
1376 RouteTimeAxisView::route_group () const
1377 {
1378         return _route->route_group();
1379 }
1380
1381 string
1382 RouteTimeAxisView::name() const
1383 {
1384         return _route->name();
1385 }
1386
1387 boost::shared_ptr<Playlist>
1388 RouteTimeAxisView::playlist () const
1389 {
1390         boost::shared_ptr<Track> tr;
1391
1392         if ((tr = track()) != 0) {
1393                 return tr->playlist();
1394         } else {
1395                 return boost::shared_ptr<Playlist> ();
1396         }
1397 }
1398
1399 void
1400 RouteTimeAxisView::name_entry_changed ()
1401 {
1402         TimeAxisView::name_entry_changed ();
1403
1404         string x = name_entry->get_text ();
1405
1406         if (x == _route->name()) {
1407                 return;
1408         }
1409
1410         strip_whitespace_edges (x);
1411
1412         if (x.length() == 0) {
1413                 name_entry->set_text (_route->name());
1414                 return;
1415         }
1416
1417         if (_session->route_name_internal (x)) {
1418                 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1419                                                                     PROGRAM_NAME));
1420                 name_entry->grab_focus ();
1421         } else if (RouteUI::verify_new_route_name (x)) {
1422                 _route->set_name (x);
1423         } else {
1424                 name_entry->grab_focus ();
1425         }
1426 }
1427
1428 boost::shared_ptr<Region>
1429 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1430 {
1431         boost::shared_ptr<Playlist> pl = playlist ();
1432
1433         if (pl) {
1434                 return pl->find_next_region (pos, point, dir);
1435         }
1436
1437         return boost::shared_ptr<Region> ();
1438 }
1439
1440 framepos_t
1441 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1442 {
1443         boost::shared_ptr<Playlist> pl = playlist ();
1444
1445         if (pl) {
1446                 return pl->find_next_region_boundary (pos, dir);
1447         }
1448
1449         return -1;
1450 }
1451
1452 void
1453 RouteTimeAxisView::fade_range (TimeSelection& selection)
1454 {
1455         boost::shared_ptr<Playlist> what_we_got;
1456         boost::shared_ptr<Track> tr = track ();
1457         boost::shared_ptr<Playlist> playlist;
1458
1459         if (tr == 0) {
1460                 /* route is a bus, not a track */
1461                 return;
1462         }
1463
1464         playlist = tr->playlist();
1465
1466         TimeSelection time (selection);
1467         float const speed = tr->speed();
1468         if (speed != 1.0f) {
1469                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1470                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1471                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1472                 }
1473         }
1474
1475         playlist->clear_changes ();
1476         playlist->clear_owned_changes ();
1477
1478         playlist->fade_range (time);
1479
1480         vector<Command*> cmds;
1481         playlist->rdiff (cmds);
1482         _session->add_commands (cmds);
1483         _session->add_command (new StatefulDiffCommand (playlist));
1484
1485 }
1486
1487 void
1488 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1489 {
1490         boost::shared_ptr<Playlist> what_we_got;
1491         boost::shared_ptr<Track> tr = track ();
1492         boost::shared_ptr<Playlist> playlist;
1493
1494         if (tr == 0) {
1495                 /* route is a bus, not a track */
1496                 return;
1497         }
1498
1499         playlist = tr->playlist();
1500
1501         TimeSelection time (selection.time);
1502         float const speed = tr->speed();
1503         if (speed != 1.0f) {
1504                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1505                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1506                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1507                 }
1508         }
1509
1510         playlist->clear_changes ();
1511         playlist->clear_owned_changes ();
1512
1513         switch (op) {
1514         case Delete:
1515                 if (playlist->cut (time) != 0) {
1516                         if (Config->get_edit_mode() == Ripple)
1517                                 playlist->ripple(time.start(), -time.length(), NULL);
1518                                 // no need to exclude any regions from rippling here
1519
1520                         vector<Command*> cmds;
1521                         playlist->rdiff (cmds);
1522                         _session->add_commands (cmds);
1523                         
1524                         _session->add_command (new StatefulDiffCommand (playlist));
1525                 }
1526                 break;
1527                 
1528         case Cut:
1529                 if ((what_we_got = playlist->cut (time)) != 0) {
1530                         _editor.get_cut_buffer().add (what_we_got);
1531                         if (Config->get_edit_mode() == Ripple)
1532                                 playlist->ripple(time.start(), -time.length(), NULL);
1533                                 // no need to exclude any regions from rippling here
1534
1535                         vector<Command*> cmds;
1536                         playlist->rdiff (cmds);
1537                         _session->add_commands (cmds);
1538
1539                         _session->add_command (new StatefulDiffCommand (playlist));
1540                 }
1541                 break;
1542         case Copy:
1543                 if ((what_we_got = playlist->copy (time)) != 0) {
1544                         _editor.get_cut_buffer().add (what_we_got);
1545                 }
1546                 break;
1547
1548         case Clear:
1549                 if ((what_we_got = playlist->cut (time)) != 0) {
1550                         if (Config->get_edit_mode() == Ripple)
1551                                 playlist->ripple(time.start(), -time.length(), NULL);
1552                                 // no need to exclude any regions from rippling here
1553
1554                         vector<Command*> cmds;
1555                         playlist->rdiff (cmds);
1556                         _session->add_commands (cmds);
1557                         _session->add_command (new StatefulDiffCommand (playlist));
1558                         what_we_got->release ();
1559                 }
1560                 break;
1561         }
1562 }
1563
1564 bool
1565 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1566 {
1567         if (!is_track()) {
1568                 return false;
1569         }
1570
1571         boost::shared_ptr<Playlist> pl = playlist ();
1572         PlaylistSelection::iterator p;
1573
1574         for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1575
1576         if (p == selection.playlists.end()) {
1577                 return false;
1578         }
1579
1580         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1581
1582         if (track()->speed() != 1.0f) {
1583                 pos = session_frame_to_track_frame (pos, track()->speed());
1584                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1585         }
1586
1587         pl->clear_changes ();
1588         if (Config->get_edit_mode() == Ripple) {
1589                 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1590                 framecnt_t amount = extent.second - extent.first;
1591                 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1592         }
1593         pl->paste (*p, pos, times);
1594
1595         vector<Command*> cmds;
1596         pl->rdiff (cmds);
1597         _session->add_commands (cmds);
1598
1599         _session->add_command (new StatefulDiffCommand (pl));
1600
1601         return true;
1602 }
1603
1604
1605 struct PlaylistSorter {
1606     bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1607             return a->sort_id() < b->sort_id();
1608     }
1609 };
1610
1611 void
1612 RouteTimeAxisView::build_playlist_menu ()
1613 {
1614         using namespace Menu_Helpers;
1615
1616         if (!is_track()) {
1617                 return;
1618         }
1619
1620         delete playlist_action_menu;
1621         playlist_action_menu = new Menu;
1622         playlist_action_menu->set_name ("ArdourContextMenu");
1623
1624         MenuList& playlist_items = playlist_action_menu->items();
1625         playlist_action_menu->set_name ("ArdourContextMenu");
1626         playlist_items.clear();
1627
1628         RadioMenuItem::Group playlist_group;
1629         boost::shared_ptr<Track> tr = track ();
1630
1631         vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1632
1633         /* sort the playlists */
1634         PlaylistSorter cmp;
1635         sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1636
1637         /* add the playlists to the menu */
1638         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1639                 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1640                 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1641                 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1642
1643                 if (tr->playlist()->id() == (*i)->id()) {
1644                         item->set_active();
1645
1646                 }
1647         }
1648
1649         playlist_items.push_back (SeparatorElem());
1650         playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1651         playlist_items.push_back (SeparatorElem());
1652
1653         if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1654                 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1655                 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1656
1657         } else {
1658                 // Use a label which tells the user what is happening
1659                 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1660                 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1661
1662         }
1663
1664         playlist_items.push_back (SeparatorElem());
1665         playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1666         playlist_items.push_back (SeparatorElem());
1667
1668         playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1669 }
1670
1671 void
1672 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1673 {
1674         assert (is_track());
1675
1676         // exit if we were triggered by deactivating the old playlist
1677         if (!item->get_active()) {
1678                 return;
1679         }
1680
1681         boost::shared_ptr<Playlist> pl (wpl.lock());
1682
1683         if (!pl) {
1684                 return;
1685         }
1686
1687         if (track()->playlist() == pl) {
1688                 // exit when use_playlist is called by the creation of the playlist menu
1689                 // or the playlist choice is unchanged
1690                 return;
1691         }
1692
1693         track()->use_playlist (pl);
1694         
1695         RouteGroup* rg = route_group();
1696         
1697         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1698                 std::string group_string = "." + rg->name() + ".";
1699                 
1700                 std::string take_name = pl->name();
1701                 std::string::size_type idx = take_name.find(group_string);
1702                 
1703                 if (idx == std::string::npos)
1704                         return;
1705                 
1706                 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1707                 
1708                 boost::shared_ptr<RouteList> rl (rg->route_list());
1709                 
1710                 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1711                         if ((*i) == this->route()) {
1712                                 continue;
1713                         }
1714
1715                         std::string playlist_name = (*i)->name()+group_string+take_name;
1716                         
1717                         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1718                         if (!track) {
1719                                 continue;
1720                         }
1721
1722                         if (track->freeze_state() == Track::Frozen) {
1723                                 /* Don't change playlists of frozen tracks */
1724                                 continue;
1725                         }
1726                         
1727                         boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1728                         if (!ipl) {
1729                                 // No playlist for this track for this take yet, make it
1730                                 track->use_new_playlist();
1731                                 track->playlist()->set_name(playlist_name);
1732                         } else {
1733                                 track->use_playlist(ipl);
1734                         }
1735                 }
1736         }
1737 }
1738
1739 void
1740 RouteTimeAxisView::update_playlist_tip ()
1741 {
1742         RouteGroup* rg = route_group ();
1743         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1744                 string group_string = "." + rg->name() + ".";
1745                 
1746                 string take_name = track()->playlist()->name();
1747                 string::size_type idx = take_name.find(group_string);
1748                 
1749                 if (idx != string::npos) {
1750                         /* find the bit containing the take number / name */
1751                         take_name = take_name.substr (idx + group_string.length());
1752
1753                         /* set the playlist button tooltip to the take name */
1754                         ARDOUR_UI::instance()->set_tip (
1755                                 playlist_button,
1756                                 string_compose(_("Take: %1.%2"),
1757                                         Glib::Markup::escape_text(rg->name()),
1758                                         Glib::Markup::escape_text(take_name))
1759                                 );
1760                         
1761                         return;
1762                 }
1763         }
1764
1765         /* set the playlist button tooltip to the playlist name */
1766         ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1767 }
1768
1769
1770 void
1771 RouteTimeAxisView::show_playlist_selector ()
1772 {
1773         _editor.playlist_selector().show_for (this);
1774 }
1775
1776 void
1777 RouteTimeAxisView::map_frozen ()
1778 {
1779         if (!is_track()) {
1780                 return;
1781         }
1782
1783         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1784
1785         switch (track()->freeze_state()) {
1786         case Track::Frozen:
1787                 playlist_button.set_sensitive (false);
1788                 rec_enable_button->set_sensitive (false);
1789                 break;
1790         default:
1791                 playlist_button.set_sensitive (true);
1792                 rec_enable_button->set_sensitive (true);
1793                 break;
1794         }
1795 }
1796
1797 void
1798 RouteTimeAxisView::color_handler ()
1799 {
1800         //case cTimeStretchOutline:
1801         if (timestretch_rect) {
1802                 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1803         }
1804         //case cTimeStretchFill:
1805         if (timestretch_rect) {
1806                 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1807         }
1808
1809         reset_meter();
1810 }
1811
1812 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1813  *  Will add track if necessary.
1814  */
1815 void
1816 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1817 {
1818         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1819         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1820
1821         if (!track) {
1822                 /* it doesn't exist yet, so we don't care about the button state: just add it */
1823                 create_automation_child (param, true);
1824         } else {
1825                 assert (menu);
1826                 bool yn = menu->get_active();
1827                 bool changed = false;
1828
1829                 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1830
1831                         /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1832                            will have done that for us.
1833                         */
1834
1835                         if (changed && !no_redraw) {
1836                                 request_redraw ();
1837                         }
1838                 }
1839         }
1840 }
1841
1842 void
1843 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1844 {
1845         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1846
1847         if (!track) {
1848                 return;
1849         }
1850
1851         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1852
1853         if (menu && !_hidden) {
1854                 ignore_toggle = true;
1855                 menu->set_active (false);
1856                 ignore_toggle = false;
1857         }
1858
1859         if (_route && !no_redraw) {
1860                 request_redraw ();
1861         }
1862 }
1863
1864
1865 void
1866 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1867 {
1868         if (apply_to_selection) {
1869                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1870         } else {
1871                 no_redraw = true;
1872
1873                 /* Show our automation */
1874
1875                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1876                         i->second->set_marked_for_display (true);
1877
1878                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1879
1880                         if (menu) {
1881                                 menu->set_active(true);
1882                         }
1883                 }
1884
1885
1886                 /* Show processor automation */
1887
1888                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1889                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1890                                 if ((*ii)->view == 0) {
1891                                         add_processor_automation_curve ((*i)->processor, (*ii)->what);
1892                                 }
1893
1894                                 (*ii)->menu_item->set_active (true);
1895                         }
1896                 }
1897
1898                 no_redraw = false;
1899
1900                 /* Redraw */
1901
1902                 request_redraw ();
1903         }
1904 }
1905
1906 void
1907 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1908 {
1909         if (apply_to_selection) {
1910                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1911         } else {
1912                 no_redraw = true;
1913
1914                 /* Show our automation */
1915
1916                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1917                         if (i->second->has_automation()) {
1918                                 i->second->set_marked_for_display (true);
1919
1920                                 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1921                                 if (menu) {
1922                                         menu->set_active(true);
1923                                 }
1924                         }
1925                 }
1926
1927                 /* Show processor automation */
1928
1929                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1930                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1931                                 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1932                                         (*ii)->menu_item->set_active (true);
1933                                 }
1934                         }
1935                 }
1936
1937                 no_redraw = false;
1938
1939                 request_redraw ();
1940         }
1941 }
1942
1943 void
1944 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1945 {
1946         if (apply_to_selection) {
1947                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1948         } else {
1949                 no_redraw = true;
1950
1951                 /* Hide our automation */
1952
1953                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1954                         i->second->set_marked_for_display (false);
1955
1956                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1957
1958                         if (menu) {
1959                                 menu->set_active (false);
1960                         }
1961                 }
1962
1963                 /* Hide processor automation */
1964
1965                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1966                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1967                                 (*ii)->menu_item->set_active (false);
1968                         }
1969                 }
1970
1971                 no_redraw = false;
1972                 request_redraw ();
1973         }
1974 }
1975
1976
1977 void
1978 RouteTimeAxisView::region_view_added (RegionView* rv)
1979 {
1980         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1981         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1982                 boost::shared_ptr<AutomationTimeAxisView> atv;
1983
1984                 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1985                         atv->add_ghost(rv);
1986                 }
1987         }
1988
1989         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1990                 (*i)->add_ghost(rv);
1991         }
1992 }
1993
1994 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1995 {
1996         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1997                 delete *i;
1998         }
1999 }
2000
2001
2002 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2003 {
2004         parent.remove_processor_automation_node (this);
2005 }
2006
2007 void
2008 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2009 {
2010         if (pan->view) {
2011                 remove_child (pan->view);
2012         }
2013 }
2014
2015 RouteTimeAxisView::ProcessorAutomationNode*
2016 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2017 {
2018         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2019
2020                 if ((*i)->processor == processor) {
2021
2022                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2023                                 if ((*ii)->what == what) {
2024                                         return *ii;
2025                                 }
2026                         }
2027                 }
2028         }
2029
2030         return 0;
2031 }
2032
2033 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2034 void
2035 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2036 {
2037         string name;
2038         ProcessorAutomationNode* pan;
2039
2040         if ((pan = find_processor_automation_node (processor, what)) == 0) {
2041                 /* session state may never have been saved with new plugin */
2042                 error << _("programming error: ")
2043                       << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2044                                          processor->name(), what.type(), (int) what.channel(), what.id() )
2045                       << endmsg;
2046                 /*NOTREACHED*/
2047                 return;
2048         }
2049
2050         if (pan->view) {
2051                 return;
2052         }
2053
2054         boost::shared_ptr<AutomationControl> control
2055                 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2056         
2057         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2058                 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2059                                             _editor, *this, false, parent_canvas, 
2060                                             processor->describe_parameter (what), processor->name()));
2061
2062         pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2063
2064         add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2065
2066         if (_view) {
2067                 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2068         }
2069 }
2070
2071 void
2072 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2073 {
2074         if (!_hidden) {
2075                 pan->menu_item->set_active (false);
2076         }
2077
2078         if (!no_redraw) {
2079                 request_redraw ();
2080         }
2081 }
2082
2083 void
2084 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2085 {
2086         boost::shared_ptr<Processor> processor (p.lock ());
2087
2088         if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2089                 /* The Amp processor is a special case and is dealt with separately */
2090                 return;
2091         }
2092
2093         set<Evoral::Parameter> existing;
2094
2095         processor->what_has_data (existing);
2096
2097         for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2098                 
2099                 Evoral::Parameter param (*i);
2100                 boost::shared_ptr<AutomationLine> al;
2101
2102                 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2103                         al->queue_reset ();
2104                 } else {
2105                         add_processor_automation_curve (processor, param);
2106                 }
2107         }
2108 }
2109
2110 void
2111 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2112 {
2113         using namespace Menu_Helpers;
2114
2115         add_child (track);
2116
2117         track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2118
2119         _automation_tracks[param] = track;
2120
2121         /* existing state overrides "show" argument */
2122         string s = track->gui_property ("visible");
2123         if (!s.empty()) { 
2124                 show = string_is_affirmative (s);
2125         }
2126
2127         /* this might or might not change the visibility status, so don't rely on it */
2128         track->set_marked_for_display (show);
2129
2130         if (show && !no_redraw) {
2131                 request_redraw ();
2132         }
2133
2134         if (!EventTypeMap::instance().is_midi_parameter(param)) {
2135                 /* MIDI-related parameters are always in the menu, there's no
2136                    reason to rebuild the menu just because we added a automation
2137                    lane for one of them. But if we add a non-MIDI automation
2138                    lane, then we need to invalidate the display menu.
2139                 */
2140                 delete display_menu;
2141                 display_menu = 0;
2142         }
2143 }
2144
2145 void
2146 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2147 {
2148         boost::shared_ptr<Processor> processor (p.lock ());
2149
2150         if (!processor || !processor->display_to_user ()) {
2151                 return;
2152         }
2153
2154         /* we use this override to veto the Amp processor from the plugin menu,
2155            as its automation lane can be accessed using the special "Fader" menu
2156            option
2157         */
2158
2159         if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2160                 return;
2161         }
2162
2163         using namespace Menu_Helpers;
2164         ProcessorAutomationInfo *rai;
2165         list<ProcessorAutomationInfo*>::iterator x;
2166
2167         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2168
2169         if (automatable.empty()) {
2170                 return;
2171         }
2172
2173         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2174                 if ((*x)->processor == processor) {
2175                         break;
2176                 }
2177         }
2178
2179         if (x == processor_automation.end()) {
2180
2181                 rai = new ProcessorAutomationInfo (processor);
2182                 processor_automation.push_back (rai);
2183
2184         } else {
2185
2186                 rai = *x;
2187
2188         }
2189
2190         /* any older menu was deleted at the top of processors_changed()
2191            when we cleared the subplugin menu.
2192         */
2193
2194         rai->menu = manage (new Menu);
2195         MenuList& items = rai->menu->items();
2196         rai->menu->set_name ("ArdourContextMenu");
2197
2198         items.clear ();
2199
2200         std::set<Evoral::Parameter> has_visible_automation;
2201         AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2202
2203         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2204
2205                 ProcessorAutomationNode* pan;
2206                 Gtk::CheckMenuItem* mitem;
2207
2208                 string name = processor->describe_parameter (*i);
2209
2210                 items.push_back (CheckMenuElem (name));
2211                 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2212                 
2213                 _subplugin_menu_map[*i] = mitem;
2214
2215                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2216                         mitem->set_active(true);
2217                 }
2218
2219                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2220
2221                         /* new item */
2222
2223                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2224
2225                         rai->lines.push_back (pan);
2226
2227                 } else {
2228
2229                         pan->menu_item = mitem;
2230
2231                 }
2232
2233                 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2234         }
2235
2236         /* add the menu for this processor, because the subplugin
2237            menu is always cleared at the top of processors_changed().
2238            this is the result of some poor design in gtkmm and/or
2239            GTK+.
2240         */
2241
2242         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2243         rai->valid = true;
2244 }
2245
2246 void
2247 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2248                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2249 {
2250         bool showit = pan->menu_item->get_active();
2251         bool redraw = false;
2252
2253         if (pan->view == 0 && showit) {
2254                 add_processor_automation_curve (rai->processor, pan->what);
2255                 redraw = true;
2256         }
2257
2258         if (pan->view && pan->view->set_marked_for_display (showit)) {
2259                 redraw = true;
2260         }
2261         
2262         if (redraw && !no_redraw) {
2263                 request_redraw ();
2264         }
2265 }
2266
2267 void
2268 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2269 {
2270         if (c.type == RouteProcessorChange::MeterPointChange) {
2271                 /* nothing to do if only the meter point has changed */
2272                 return;
2273         }
2274
2275         using namespace Menu_Helpers;
2276
2277         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2278                 (*i)->valid = false;
2279         }
2280
2281         setup_processor_menu_and_curves ();
2282
2283         bool deleted_processor_automation = false;
2284
2285         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2286
2287                 list<ProcessorAutomationInfo*>::iterator tmp;
2288
2289                 tmp = i;
2290                 ++tmp;
2291
2292                 if (!(*i)->valid) {
2293
2294                         delete *i;
2295                         processor_automation.erase (i);
2296                         deleted_processor_automation = true;
2297
2298                 }
2299
2300                 i = tmp;
2301         }
2302
2303         if (deleted_processor_automation && !no_redraw) {
2304                 request_redraw ();
2305         }
2306 }
2307
2308 boost::shared_ptr<AutomationLine>
2309 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2310 {
2311         ProcessorAutomationNode* pan;
2312
2313         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2314                 if (pan->view) {
2315                         pan->view->line();
2316                 }
2317         }
2318
2319         return boost::shared_ptr<AutomationLine>();
2320 }
2321
2322 void
2323 RouteTimeAxisView::reset_processor_automation_curves ()
2324 {
2325         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2326                 (*i)->reset();
2327         }
2328 }
2329
2330 bool
2331 RouteTimeAxisView::can_edit_name () const
2332 {
2333         /* we do not allow track name changes if it is record enabled
2334          */
2335         return !_route->record_enabled();
2336 }
2337
2338 void
2339 RouteTimeAxisView::blink_rec_display (bool onoff)
2340 {
2341         RouteUI::blink_rec_display (onoff);
2342 }
2343
2344 void
2345 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2346 {
2347         if (_ignore_set_layer_display) {
2348                 return;
2349         }
2350         
2351         if (apply_to_selection) {
2352                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2353         } else {
2354
2355                 if (_view) {
2356                         _view->set_layer_display (d);
2357                 }
2358
2359                 set_gui_property (X_("layer-display"), enum_2_string (d));
2360         }
2361 }
2362
2363 LayerDisplay
2364 RouteTimeAxisView::layer_display () const
2365 {
2366         if (_view) {
2367                 return _view->layer_display ();
2368         }
2369
2370         /* we don't know, since we don't have a _view, so just return something */
2371         return Overlaid;
2372 }
2373
2374
2375
2376 boost::shared_ptr<AutomationTimeAxisView>
2377 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2378 {
2379         AutomationTracks::iterator i = _automation_tracks.find(param);
2380         if (i != _automation_tracks.end()) {
2381                 return i->second;
2382         } else {
2383                 return boost::shared_ptr<AutomationTimeAxisView>();
2384         }
2385 }
2386
2387 void
2388 RouteTimeAxisView::fast_update ()
2389 {
2390         gm.get_level_meter().update_meters ();
2391 }
2392
2393 void
2394 RouteTimeAxisView::hide_meter ()
2395 {
2396         clear_meter ();
2397         gm.get_level_meter().hide_meters ();
2398 }
2399
2400 void
2401 RouteTimeAxisView::show_meter ()
2402 {
2403         reset_meter ();
2404 }
2405
2406 void
2407 RouteTimeAxisView::reset_meter ()
2408 {
2409         if (Config->get_show_track_meters()) {
2410                 int meter_width = 3;
2411                 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2412                         meter_width = 6;
2413                 }
2414                 gm.get_level_meter().setup_meters (height - 9, meter_width);
2415         } else {
2416                 hide_meter ();
2417         }
2418 }
2419
2420 void
2421 RouteTimeAxisView::clear_meter ()
2422 {
2423         gm.get_level_meter().clear_meters ();
2424 }
2425
2426 void
2427 RouteTimeAxisView::meter_changed ()
2428 {
2429         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2430         reset_meter();
2431         if (_route && !no_redraw) {
2432                 request_redraw ();
2433         }
2434         // reset peak when meter point changes
2435         gm.reset_peak_display();
2436 }
2437
2438 void
2439 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2440 {
2441         reset_meter ();
2442         if (_route && !no_redraw) {
2443                 request_redraw ();
2444         }
2445 }
2446
2447 void
2448 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2449 {
2450         using namespace Menu_Helpers;
2451
2452         if (!_underlay_streams.empty()) {
2453                 MenuList& parent_items = parent_menu->items();
2454                 Menu* gs_menu = manage (new Menu);
2455                 gs_menu->set_name ("ArdourContextMenu");
2456                 MenuList& gs_items = gs_menu->items();
2457
2458                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2459
2460                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2461                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2462                                                     sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2463                 }
2464         }
2465 }
2466
2467 bool
2468 RouteTimeAxisView::set_underlay_state()
2469 {
2470         if (!underlay_xml_node) {
2471                 return false;
2472         }
2473
2474         XMLNodeList nlist = underlay_xml_node->children();
2475         XMLNodeConstIterator niter;
2476         XMLNode *child_node;
2477
2478         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2479                 child_node = *niter;
2480
2481                 if (child_node->name() != "Underlay") {
2482                         continue;
2483                 }
2484
2485                 XMLProperty* prop = child_node->property ("id");
2486                 if (prop) {
2487                         PBD::ID id (prop->value());
2488
2489                         RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2490
2491                         if (v) {
2492                                 add_underlay(v->view(), false);
2493                         }
2494                 }
2495         }
2496
2497         return false;
2498 }
2499
2500 void
2501 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2502 {
2503         if (!v) {
2504                 return;
2505         }
2506
2507         RouteTimeAxisView& other = v->trackview();
2508
2509         if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2510                 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2511                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2512                         /*NOTREACHED*/
2513                 }
2514
2515                 _underlay_streams.push_back(v);
2516                 other._underlay_mirrors.push_back(this);
2517
2518                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2519
2520 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2521                 if (update_xml) {
2522                         if (!underlay_xml_node) {
2523                                 underlay_xml_node = xml_node->add_child("Underlays");
2524                         }
2525
2526                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2527                         XMLProperty* prop = node->add_property("id");
2528                         prop->set_value(v->trackview().route()->id().to_s());
2529                 }
2530 #endif
2531         }
2532 }
2533
2534 void
2535 RouteTimeAxisView::remove_underlay (StreamView* v)
2536 {
2537         if (!v) {
2538                 return;
2539         }
2540
2541         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2542         RouteTimeAxisView& other = v->trackview();
2543
2544         if (it != _underlay_streams.end()) {
2545                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2546
2547                 if (gm == other._underlay_mirrors.end()) {
2548                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2549                         /*NOTREACHED*/
2550                 }
2551
2552                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2553
2554                 _underlay_streams.erase(it);
2555                 other._underlay_mirrors.erase(gm);
2556
2557                 if (underlay_xml_node) {
2558                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2559                 }
2560         }
2561 }
2562
2563 void
2564 RouteTimeAxisView::set_button_names ()
2565 {
2566         if (_route && _route->solo_safe()) {
2567                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2568         } else {
2569                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2570         }
2571         if (Config->get_solo_control_is_listen_control()) {
2572                 switch (Config->get_listen_position()) {
2573                         case AfterFaderListen:
2574                                 solo_button->set_text (_("A"));
2575                                 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2576                                 break;
2577                         case PreFaderListen:
2578                                 solo_button->set_text (_("P"));
2579                                 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2580                         break;
2581                 }
2582         } else {
2583                 solo_button->set_text (_("S"));
2584                 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2585         }
2586         mute_button->set_text (_("M"));
2587 }
2588
2589 Gtk::CheckMenuItem*
2590 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2591 {
2592         ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2593         if (i != _main_automation_menu_map.end()) {
2594                 return i->second;
2595         }
2596
2597         i = _subplugin_menu_map.find (param);
2598         if (i != _subplugin_menu_map.end()) {
2599                 return i->second;
2600         }
2601
2602         return 0;
2603 }
2604
2605 void
2606 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2607 {
2608         boost::shared_ptr<AutomationControl> c = _route->gain_control();
2609         if (!c) {
2610                 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2611                 return;
2612         }
2613
2614         gain_track.reset (new AutomationTimeAxisView (_session,
2615                                                       _route, _route->amp(), c, param,
2616                                                       _editor,
2617                                                       *this,
2618                                                       false,
2619                                                       parent_canvas,
2620                                                       _route->amp()->describe_parameter(param)));
2621
2622         if (_view) {
2623                 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2624         }
2625
2626         add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2627 }
2628
2629 void
2630 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2631 {
2632         boost::shared_ptr<AutomationControl> c = _route->mute_control();
2633         if (!c) {
2634                 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2635                 return;
2636         }
2637
2638         mute_track.reset (new AutomationTimeAxisView (_session,
2639                                                       _route, _route, c, param,
2640                                                       _editor,
2641                                                       *this,
2642                                                       false,
2643                                                       parent_canvas,
2644                                                       _route->describe_parameter(param)));
2645
2646         if (_view) {
2647                 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2648         }
2649
2650         add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2651 }
2652
2653 static
2654 void add_region_to_list (RegionView* rv, RegionList* l)
2655 {
2656         l->push_back (rv->region());
2657 }
2658
2659 RegionView*
2660 RouteTimeAxisView::combine_regions ()
2661 {
2662         /* as of may 2011, we do not offer uncombine for MIDI tracks
2663          */
2664
2665         if (!is_audio_track()) {
2666                 return 0;
2667         }
2668
2669         if (!_view) {
2670                 return 0;
2671         }
2672
2673         RegionList selected_regions;
2674         boost::shared_ptr<Playlist> playlist = track()->playlist();
2675
2676         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2677
2678         if (selected_regions.size() < 2) {
2679                 return 0;
2680         }
2681
2682         playlist->clear_changes ();
2683         boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2684
2685         _session->add_command (new StatefulDiffCommand (playlist));
2686         /* make the new region be selected */
2687
2688         return _view->find_view (compound_region);
2689 }
2690
2691 void
2692 RouteTimeAxisView::uncombine_regions ()
2693 {
2694         /* as of may 2011, we do not offer uncombine for MIDI tracks
2695          */
2696         if (!is_audio_track()) {
2697                 return;
2698         }
2699
2700         if (!_view) {
2701                 return;
2702         }
2703
2704         RegionList selected_regions;
2705         boost::shared_ptr<Playlist> playlist = track()->playlist();
2706
2707         /* have to grab selected regions first because the uncombine is going
2708          * to change that in the middle of the list traverse
2709          */
2710
2711         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2712
2713         playlist->clear_changes ();
2714
2715         for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2716                 playlist->uncombine (*i);
2717         }
2718
2719         _session->add_command (new StatefulDiffCommand (playlist));
2720 }
2721
2722 string
2723 RouteTimeAxisView::state_id() const
2724 {
2725         return string_compose ("rtav %1", _route->id().to_s());
2726 }
2727
2728
2729 void
2730 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2731 {
2732         TimeAxisView::remove_child (c);
2733         
2734         boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2735         if (a) {
2736                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2737                         if (i->second == a) {
2738                                 _automation_tracks.erase (i);
2739                                 return;
2740                         }
2741                 }
2742         }
2743 }