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