2 Copyright (C) 2006 Paul Davis
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.
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.
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.
29 #include <sigc++/bind.h>
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"
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>
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"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
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)
107 , color_mode_menu (0)
108 , gm (sess, true, 75, 20)
109 , _ignore_set_layer_display (false)
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);
116 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
122 RouteUI::set_route (rt);
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()));
129 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
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 ();
137 string str = gui_property ("height");
139 set_height (atoi (str));
141 set_height (preset_height (HeightNormal));
144 if (!_route->is_auditioner()) {
145 if (gui_property ("visible").empty()) {
146 set_gui_property ("visible", true);
149 set_gui_property ("visible", false);
153 update_solo_display ();
155 timestretch_rect = 0;
158 ignore_toggle = false;
160 route_group_button.set_name ("route button");
161 playlist_button.set_name ("route button");
162 automation_button.set_name ("route button");
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));
172 switch (track()->mode()) {
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>");
178 case ARDOUR::Destructive:
179 rec_enable_button->set_text (string());
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
184 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
185 controls_button_size_group->add_widget(*rec_enable_button);
187 if (is_midi_track()) {
188 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
189 gm.set_fader_name ("MidiTrackFader");
191 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
192 gm.set_fader_name ("AudioTrackFader");
195 rec_enable_button->set_sensitive (_session->writable());
197 /* set playlist button tip to the current playlist, and make it update when it changes */
198 update_playlist_tip ();
199 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
202 gm.set_fader_name ("AudioBusFader");
203 Gtk::Fixed *blank = manage(new Gtk::Fixed());
204 controls_button_size_group->add_widget(*blank);
205 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
211 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
212 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
213 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
214 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
216 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 controls_button_size_group->add_widget(*mute_button);
219 if (!_route->is_master()) {
220 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
221 controls_button_size_group->add_widget(*solo_button);
223 Gtk::Fixed *blank = manage(new Gtk::Fixed());
224 controls_button_size_group->add_widget(*blank);
225 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
229 if (!ARDOUR::Profile->get_trx()) {
230 controls_table.attach (route_group_button, 2, 3, 3, 4, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_button_size_group->add_widget(route_group_button);
232 controls_table.attach (gm.get_gain_slider(), 3, 6, 2, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
235 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
236 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
237 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
239 if (is_midi_track()) {
240 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
242 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
245 update_track_number_visibility();
248 if (!ARDOUR::Profile->get_trx()) {
249 controls_table.attach (automation_button, 1, 2, 3, 4, Gtk::SHRINK, Gtk::SHRINK);
250 controls_button_size_group->add_widget(automation_button);
253 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
254 controls_table.attach (playlist_button, 0, 1, 3, 4, Gtk::SHRINK, Gtk::SHRINK);
255 controls_button_size_group->add_widget(playlist_button);
260 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
261 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
265 str = gui_property ("layer-display");
267 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
270 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
271 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
273 /* pick up the correct freeze state */
278 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
279 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
281 PropertyList* plist = new PropertyList();
283 plist->add (ARDOUR::Properties::mute, true);
284 plist->add (ARDOUR::Properties::solo, true);
286 route_group_menu = new RouteGroupMenu (_session, plist);
288 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
290 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
293 RouteTimeAxisView::~RouteTimeAxisView ()
295 CatchDeletion (this);
297 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
301 delete playlist_action_menu;
302 playlist_action_menu = 0;
307 _automation_tracks.clear ();
309 delete route_group_menu;
313 RouteTimeAxisView::post_construct ()
315 /* map current state of the route */
317 update_diskstream_display ();
318 setup_processor_menu_and_curves ();
319 reset_processor_automation_curves ();
322 /** Set up the processor menu for the current set of processors, and
323 * display automation curves for any parameters which have data.
326 RouteTimeAxisView::setup_processor_menu_and_curves ()
328 _subplugin_menu_map.clear ();
329 subplugin_menu.items().clear ();
330 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
331 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
335 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
337 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
338 if (_route->route_group()) {
339 _route->route_group()->remove (_route);
345 r.push_back (route ());
347 route_group_menu->build (r);
348 route_group_menu->menu()->popup (ev->button, ev->time);
354 RouteTimeAxisView::playlist_changed ()
360 RouteTimeAxisView::label_view ()
362 string x = _route->name ();
363 if (x != name_label.get_text ()) {
364 name_label.set_text (x);
366 const int64_t track_number = _route->track_number ();
367 if (track_number == 0) {
368 number_label.set_text ("");
370 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
375 RouteTimeAxisView::update_track_number_visibility ()
377 bool show_label = _session->config.get_track_name_number();
379 if (_route && _route->is_master()) {
383 if (number_label.get_parent()) {
384 controls_table.remove (number_label); // XXX
387 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
388 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
389 number_label.set_size_request(3 + tnw, -1);
390 number_label.show ();
392 number_label.hide ();
397 RouteTimeAxisView::parameter_changed (string const & p)
399 if (p == "track-name-number") {
400 update_track_number_visibility();
405 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
407 if (what_changed.contains (ARDOUR::Properties::name)) {
413 RouteTimeAxisView::take_name_changed (void *src)
421 RouteTimeAxisView::playlist_click ()
423 build_playlist_menu ();
424 conditionally_add_to_selection ();
425 playlist_action_menu->popup (1, gtk_get_current_event_time());
429 RouteTimeAxisView::automation_click ()
431 conditionally_add_to_selection ();
432 build_automation_action_menu (false);
433 automation_action_menu->popup (1, gtk_get_current_event_time());
437 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
439 using namespace Menu_Helpers;
441 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
442 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
445 detach_menu (subplugin_menu);
447 _main_automation_menu_map.clear ();
448 delete automation_action_menu;
449 automation_action_menu = new Menu;
451 MenuList& items = automation_action_menu->items();
453 automation_action_menu->set_name ("ArdourContextMenu");
455 items.push_back (MenuElem (_("Show All Automation"),
456 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
458 items.push_back (MenuElem (_("Show Existing Automation"),
459 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
461 items.push_back (MenuElem (_("Hide All Automation"),
462 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
464 /* Attach the plugin submenu. It may have previously been used elsewhere,
465 so it was detached above
468 if (!subplugin_menu.items().empty()) {
469 items.push_back (SeparatorElem ());
470 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
471 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
476 RouteTimeAxisView::build_display_menu ()
478 using namespace Menu_Helpers;
482 TimeAxisView::build_display_menu ();
484 /* now fill it with our stuff */
486 MenuList& items = display_menu->items();
487 display_menu->set_name ("ArdourContextMenu");
489 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
491 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
493 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
495 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
497 items.push_back (SeparatorElem());
500 detach_menu (*_size_menu);
503 items.push_back (MenuElem (_("Height"), *_size_menu));
505 items.push_back (SeparatorElem());
507 if (!Profile->get_sae()) {
508 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
509 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
510 items.push_back (SeparatorElem());
513 // Hook for derived classes to add type specific stuff
514 append_extra_display_menu_items ();
518 Menu* layers_menu = manage (new Menu);
519 MenuList &layers_items = layers_menu->items();
520 layers_menu->set_name("ArdourContextMenu");
522 RadioMenuItem::Group layers_group;
524 /* Find out how many overlaid/stacked tracks we have in the selection */
528 TrackSelection const & s = _editor.get_selection().tracks;
529 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
530 StreamView* v = (*i)->view ();
535 switch (v->layer_display ()) {
546 /* We're not connecting to signal_toggled() here; in the case where these two items are
547 set to be in the `inconsistent' state, it seems that one or other will end up active
548 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
549 select the active one, no toggled signal is emitted so nothing happens.
552 _ignore_set_layer_display = true;
554 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
555 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
556 i->set_active (overlaid != 0 && stacked == 0);
557 i->set_inconsistent (overlaid != 0 && stacked != 0);
558 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
560 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
561 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
562 i->set_active (overlaid == 0 && stacked != 0);
563 i->set_inconsistent (overlaid != 0 && stacked != 0);
564 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
566 _ignore_set_layer_display = false;
568 items.push_back (MenuElem (_("Layers"), *layers_menu));
570 if (!Profile->get_sae()) {
572 Menu* alignment_menu = manage (new Menu);
573 MenuList& alignment_items = alignment_menu->items();
574 alignment_menu->set_name ("ArdourContextMenu");
576 RadioMenuItem::Group align_group;
578 /* Same verbose hacks as for the layering options above */
584 boost::shared_ptr<Track> first_track;
586 TrackSelection const & s = _editor.get_selection().tracks;
587 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
588 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
589 if (!r || !r->is_track ()) {
594 first_track = r->track();
597 switch (r->track()->alignment_choice()) {
601 switch (r->track()->alignment_style()) {
602 case ExistingMaterial:
610 case UseExistingMaterial:
626 inconsistent = false;
635 if (!inconsistent && first_track) {
637 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
638 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
639 i->set_active (automatic != 0 && existing == 0 && capture == 0);
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
642 switch (first_track->alignment_choice()) {
644 switch (first_track->alignment_style()) {
645 case ExistingMaterial:
646 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
649 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
657 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
658 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
659 i->set_active (existing != 0 && capture == 0 && automatic == 0);
660 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
662 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
663 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
664 i->set_active (existing == 0 && capture != 0 && automatic == 0);
665 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
667 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
673 Menu* mode_menu = manage (new Menu);
674 MenuList& mode_items = mode_menu->items ();
675 mode_menu->set_name ("ArdourContextMenu");
677 RadioMenuItem::Group mode_group;
683 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
684 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
685 if (!r || !r->is_track ()) {
689 switch (r->track()->mode()) {
702 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
703 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
704 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
705 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
706 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
708 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
709 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
710 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
711 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
712 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
714 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
715 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
716 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
717 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
718 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
720 items.push_back (MenuElem (_("Mode"), *mode_menu));
724 items.push_back (SeparatorElem());
726 build_playlist_menu ();
727 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
728 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
731 route_group_menu->detach ();
734 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
735 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
737 r.push_back (rtv->route ());
742 r.push_back (route ());
745 route_group_menu->build (r);
746 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
748 build_automation_action_menu (true);
749 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
751 items.push_back (SeparatorElem());
755 TrackSelection const & s = _editor.get_selection().tracks;
756 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
757 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
762 if (r->route()->active()) {
769 items.push_back (CheckMenuElem (_("Active")));
770 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
771 bool click_sets_active = true;
772 if (active > 0 && inactive == 0) {
773 i->set_active (true);
774 click_sets_active = false;
775 } else if (active > 0 && inactive > 0) {
776 i->set_inconsistent (true);
778 i->set_sensitive(! _session->transport_rolling());
779 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
781 items.push_back (SeparatorElem());
782 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
783 if (!Profile->get_sae()) {
784 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
786 items.push_front (SeparatorElem());
787 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
792 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
794 if (apply_to_selection) {
795 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
798 bool needs_bounce = false;
800 if (!track()->can_use_mode (mode, needs_bounce)) {
806 cerr << "would bounce this one\n";
811 track()->set_mode (mode);
813 rec_enable_button->remove ();
816 case ARDOUR::NonLayered:
818 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
819 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
821 case ARDOUR::Destructive:
822 rec_enable_button->set_text (string());
823 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
827 rec_enable_button->show_all ();
832 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
834 TimeAxisView::show_timestretch (start, end, layers, layer);
844 /* check that the time selection was made in our route, or our route group.
845 remember that route_group() == 0 implies the route is *not* in a edit group.
848 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
849 /* this doesn't apply to us */
853 /* ignore it if our edit group is not active */
855 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
860 if (timestretch_rect == 0) {
861 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
862 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
863 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
866 timestretch_rect->show ();
867 timestretch_rect->raise_to_top ();
869 double const x1 = start / _editor.get_current_zoom();
870 double const x2 = (end - 1) / _editor.get_current_zoom();
872 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
873 x2, current_height() * (layers - layer) / layers));
877 RouteTimeAxisView::hide_timestretch ()
879 TimeAxisView::hide_timestretch ();
881 if (timestretch_rect) {
882 timestretch_rect->hide ();
887 RouteTimeAxisView::show_selection (TimeSelection& ts)
891 /* ignore it if our edit group is not active or if the selection was started
892 in some other track or route group (remember that route_group() == 0 means
893 that the track is not in an route group).
896 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
897 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
903 TimeAxisView::show_selection (ts);
907 RouteTimeAxisView::set_height (uint32_t h)
910 bool height_changed = (height == 0) || (h != height);
913 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
916 gm.get_level_meter().setup_meters (gmlen, meter_width);
918 TimeAxisView::set_height (h);
921 _view->set_height ((double) current_height());
924 if (height >= preset_height (HeightNormal)) {
928 gm.get_gain_slider().show();
930 if (!_route || _route->is_monitor()) {
935 if (rec_enable_button)
936 rec_enable_button->show();
938 route_group_button.show();
939 automation_button.show();
941 if (is_track() && track()->mode() == ARDOUR::Normal) {
942 playlist_button.show();
949 gm.get_gain_slider().hide();
951 if (!_route || _route->is_monitor()) {
956 if (rec_enable_button)
957 rec_enable_button->show();
959 route_group_button.hide ();
960 automation_button.hide ();
962 if (is_track() && track()->mode() == ARDOUR::Normal) {
963 playlist_button.hide ();
968 if (height_changed && !no_redraw) {
969 /* only emit the signal if the height really changed */
975 RouteTimeAxisView::route_color_changed ()
978 _view->apply_color (color(), StreamView::RegionColor);
981 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
985 RouteTimeAxisView::reset_samples_per_pixel ()
987 set_samples_per_pixel (_editor.get_current_zoom());
991 RouteTimeAxisView::set_samples_per_pixel (double fpp)
996 speed = track()->speed();
1000 _view->set_samples_per_pixel (fpp * speed);
1003 TimeAxisView::set_samples_per_pixel (fpp * speed);
1007 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1009 if (!mitem->get_active()) {
1010 /* this is one of the two calls made when these radio menu items change status. this one
1011 is for the item that became inactive, and we want to ignore it.
1016 if (apply_to_selection) {
1017 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1020 track()->set_align_choice (choice);
1026 RouteTimeAxisView::rename_current_playlist ()
1028 ArdourPrompter prompter (true);
1031 boost::shared_ptr<Track> tr = track();
1032 if (!tr || tr->destructive()) {
1036 boost::shared_ptr<Playlist> pl = tr->playlist();
1041 prompter.set_title (_("Rename Playlist"));
1042 prompter.set_prompt (_("New name for playlist:"));
1043 prompter.set_initial_text (pl->name());
1044 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1045 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1047 switch (prompter.run ()) {
1048 case Gtk::RESPONSE_ACCEPT:
1049 prompter.get_result (name);
1050 if (name.length()) {
1051 pl->set_name (name);
1061 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1063 std::string ret (basename);
1065 std::string const group_string = "." + route_group()->name() + ".";
1067 // iterate through all playlists
1069 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1070 std::string tmp = (*i)->name();
1072 std::string::size_type idx = tmp.find(group_string);
1073 // find those which belong to this group
1074 if (idx != string::npos) {
1075 tmp = tmp.substr(idx + group_string.length());
1077 // and find the largest current number
1079 if (x > maxnumber) {
1088 snprintf (buf, sizeof(buf), "%d", maxnumber);
1090 ret = this->name() + "." + route_group()->name () + "." + buf;
1096 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1100 boost::shared_ptr<Track> tr = track ();
1101 if (!tr || tr->destructive()) {
1105 boost::shared_ptr<const Playlist> pl = tr->playlist();
1112 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1113 name = resolve_new_group_playlist_name(name, playlists_before_op);
1116 while (_session->playlists->by_name(name)) {
1117 name = Playlist::bump_name (name, *_session);
1120 // TODO: The prompter "new" button should be de-activated if the user
1121 // specifies a playlist name which already exists in the session.
1125 ArdourPrompter prompter (true);
1127 prompter.set_title (_("New Copy Playlist"));
1128 prompter.set_prompt (_("Name for new playlist:"));
1129 prompter.set_initial_text (name);
1130 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1131 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1132 prompter.show_all ();
1134 switch (prompter.run ()) {
1135 case Gtk::RESPONSE_ACCEPT:
1136 prompter.get_result (name);
1144 if (name.length()) {
1145 tr->use_copy_playlist ();
1146 tr->playlist()->set_name (name);
1151 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1155 boost::shared_ptr<Track> tr = track ();
1156 if (!tr || tr->destructive()) {
1160 boost::shared_ptr<const Playlist> pl = tr->playlist();
1167 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1168 name = resolve_new_group_playlist_name(name,playlists_before_op);
1171 while (_session->playlists->by_name(name)) {
1172 name = Playlist::bump_name (name, *_session);
1178 ArdourPrompter prompter (true);
1180 prompter.set_title (_("New Playlist"));
1181 prompter.set_prompt (_("Name for new playlist:"));
1182 prompter.set_initial_text (name);
1183 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1184 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1186 switch (prompter.run ()) {
1187 case Gtk::RESPONSE_ACCEPT:
1188 prompter.get_result (name);
1196 if (name.length()) {
1197 tr->use_new_playlist ();
1198 tr->playlist()->set_name (name);
1203 RouteTimeAxisView::clear_playlist ()
1205 boost::shared_ptr<Track> tr = track ();
1206 if (!tr || tr->destructive()) {
1210 boost::shared_ptr<Playlist> pl = tr->playlist();
1215 _editor.clear_playlist (pl);
1219 RouteTimeAxisView::speed_changed ()
1221 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1225 RouteTimeAxisView::update_diskstream_display ()
1235 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1237 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1239 /* special case: select/deselect all tracks */
1240 if (_editor.get_selection().selected (this)) {
1241 _editor.get_selection().clear_tracks ();
1243 _editor.select_all_tracks ();
1249 switch (ArdourKeyboard::selection_type (ev->state)) {
1250 case Selection::Toggle:
1251 _editor.get_selection().toggle (this);
1254 case Selection::Set:
1255 _editor.get_selection().set (this);
1258 case Selection::Extend:
1259 _editor.extend_selection_to_track (*this);
1262 case Selection::Add:
1263 _editor.get_selection().add (this);
1269 RouteTimeAxisView::set_selected_points (PointSelection& points)
1271 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1272 (*i)->set_selected_points (points);
1277 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1280 _view->set_selected_regionviews (regions);
1284 /** Add the selectable things that we have to a list.
1285 * @param results List to add things to.
1288 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1293 speed = track()->speed();
1296 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1297 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1299 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1300 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1303 /* pick up visible automation tracks */
1305 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1306 if (!(*i)->hidden()) {
1307 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1313 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1316 _view->get_inverted_selectables (sel, results);
1319 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1320 if (!(*i)->hidden()) {
1321 (*i)->get_inverted_selectables (sel, results);
1329 RouteTimeAxisView::route_group () const
1331 return _route->route_group();
1335 RouteTimeAxisView::name() const
1337 return _route->name();
1340 boost::shared_ptr<Playlist>
1341 RouteTimeAxisView::playlist () const
1343 boost::shared_ptr<Track> tr;
1345 if ((tr = track()) != 0) {
1346 return tr->playlist();
1348 return boost::shared_ptr<Playlist> ();
1353 RouteTimeAxisView::name_entry_changed ()
1355 TimeAxisView::name_entry_changed ();
1357 string x = name_entry->get_text ();
1359 if (x == _route->name()) {
1363 strip_whitespace_edges (x);
1365 if (x.length() == 0) {
1366 name_entry->set_text (_route->name());
1370 if (_session->route_name_internal (x)) {
1371 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1373 name_entry->grab_focus ();
1374 } else if (RouteUI::verify_new_route_name (x)) {
1375 _route->set_name (x);
1377 name_entry->grab_focus ();
1381 boost::shared_ptr<Region>
1382 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1384 boost::shared_ptr<Playlist> pl = playlist ();
1387 return pl->find_next_region (pos, point, dir);
1390 return boost::shared_ptr<Region> ();
1394 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1396 boost::shared_ptr<Playlist> pl = playlist ();
1399 return pl->find_next_region_boundary (pos, dir);
1406 RouteTimeAxisView::fade_range (TimeSelection& selection)
1408 boost::shared_ptr<Playlist> what_we_got;
1409 boost::shared_ptr<Track> tr = track ();
1410 boost::shared_ptr<Playlist> playlist;
1413 /* route is a bus, not a track */
1417 playlist = tr->playlist();
1419 TimeSelection time (selection);
1420 float const speed = tr->speed();
1421 if (speed != 1.0f) {
1422 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1423 (*i).start = session_frame_to_track_frame((*i).start, speed);
1424 (*i).end = session_frame_to_track_frame((*i).end, speed);
1428 playlist->clear_changes ();
1429 playlist->clear_owned_changes ();
1431 playlist->fade_range (time);
1433 vector<Command*> cmds;
1434 playlist->rdiff (cmds);
1435 _session->add_commands (cmds);
1436 _session->add_command (new StatefulDiffCommand (playlist));
1441 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1443 boost::shared_ptr<Playlist> what_we_got;
1444 boost::shared_ptr<Track> tr = track ();
1445 boost::shared_ptr<Playlist> playlist;
1448 /* route is a bus, not a track */
1452 playlist = tr->playlist();
1454 TimeSelection time (selection.time);
1455 float const speed = tr->speed();
1456 if (speed != 1.0f) {
1457 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1458 (*i).start = session_frame_to_track_frame((*i).start, speed);
1459 (*i).end = session_frame_to_track_frame((*i).end, speed);
1463 playlist->clear_changes ();
1464 playlist->clear_owned_changes ();
1468 if (playlist->cut (time) != 0) {
1469 if (Config->get_edit_mode() == Ripple)
1470 playlist->ripple(time.start(), -time.length(), NULL);
1471 // no need to exclude any regions from rippling here
1473 vector<Command*> cmds;
1474 playlist->rdiff (cmds);
1475 _session->add_commands (cmds);
1477 _session->add_command (new StatefulDiffCommand (playlist));
1482 if ((what_we_got = playlist->cut (time)) != 0) {
1483 _editor.get_cut_buffer().add (what_we_got);
1484 if (Config->get_edit_mode() == Ripple)
1485 playlist->ripple(time.start(), -time.length(), NULL);
1486 // no need to exclude any regions from rippling here
1488 vector<Command*> cmds;
1489 playlist->rdiff (cmds);
1490 _session->add_commands (cmds);
1492 _session->add_command (new StatefulDiffCommand (playlist));
1496 if ((what_we_got = playlist->copy (time)) != 0) {
1497 _editor.get_cut_buffer().add (what_we_got);
1502 if ((what_we_got = playlist->cut (time)) != 0) {
1503 if (Config->get_edit_mode() == Ripple)
1504 playlist->ripple(time.start(), -time.length(), NULL);
1505 // no need to exclude any regions from rippling here
1507 vector<Command*> cmds;
1508 playlist->rdiff (cmds);
1509 _session->add_commands (cmds);
1510 _session->add_command (new StatefulDiffCommand (playlist));
1511 what_we_got->release ();
1518 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1524 boost::shared_ptr<Playlist> pl = playlist ();
1525 PlaylistSelection::iterator p;
1527 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1529 if (p == selection.playlists.end()) {
1533 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1535 if (track()->speed() != 1.0f) {
1536 pos = session_frame_to_track_frame (pos, track()->speed());
1537 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1540 pl->clear_changes ();
1541 if (Config->get_edit_mode() == Ripple) {
1542 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1543 framecnt_t amount = extent.second - extent.first;
1544 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1546 pl->paste (*p, pos, times);
1548 vector<Command*> cmds;
1550 _session->add_commands (cmds);
1552 _session->add_command (new StatefulDiffCommand (pl));
1558 struct PlaylistSorter {
1559 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1560 return a->sort_id() < b->sort_id();
1565 RouteTimeAxisView::build_playlist_menu ()
1567 using namespace Menu_Helpers;
1573 delete playlist_action_menu;
1574 playlist_action_menu = new Menu;
1575 playlist_action_menu->set_name ("ArdourContextMenu");
1577 MenuList& playlist_items = playlist_action_menu->items();
1578 playlist_action_menu->set_name ("ArdourContextMenu");
1579 playlist_items.clear();
1581 RadioMenuItem::Group playlist_group;
1582 boost::shared_ptr<Track> tr = track ();
1584 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1586 /* sort the playlists */
1588 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1590 /* add the playlists to the menu */
1591 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1592 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1593 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1594 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1596 if (tr->playlist()->id() == (*i)->id()) {
1602 playlist_items.push_back (SeparatorElem());
1603 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1604 playlist_items.push_back (SeparatorElem());
1606 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1607 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1608 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1611 // Use a label which tells the user what is happening
1612 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1613 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1617 playlist_items.push_back (SeparatorElem());
1618 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1619 playlist_items.push_back (SeparatorElem());
1621 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1625 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1627 assert (is_track());
1629 // exit if we were triggered by deactivating the old playlist
1630 if (!item->get_active()) {
1634 boost::shared_ptr<Playlist> pl (wpl.lock());
1640 if (track()->playlist() == pl) {
1641 // exit when use_playlist is called by the creation of the playlist menu
1642 // or the playlist choice is unchanged
1646 track()->use_playlist (pl);
1648 RouteGroup* rg = route_group();
1650 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1651 std::string group_string = "." + rg->name() + ".";
1653 std::string take_name = pl->name();
1654 std::string::size_type idx = take_name.find(group_string);
1656 if (idx == std::string::npos)
1659 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1661 boost::shared_ptr<RouteList> rl (rg->route_list());
1663 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1664 if ((*i) == this->route()) {
1668 std::string playlist_name = (*i)->name()+group_string+take_name;
1670 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1675 if (track->freeze_state() == Track::Frozen) {
1676 /* Don't change playlists of frozen tracks */
1680 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1682 // No playlist for this track for this take yet, make it
1683 track->use_new_playlist();
1684 track->playlist()->set_name(playlist_name);
1686 track->use_playlist(ipl);
1693 RouteTimeAxisView::update_playlist_tip ()
1695 RouteGroup* rg = route_group ();
1696 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1697 string group_string = "." + rg->name() + ".";
1699 string take_name = track()->playlist()->name();
1700 string::size_type idx = take_name.find(group_string);
1702 if (idx != string::npos) {
1703 /* find the bit containing the take number / name */
1704 take_name = take_name.substr (idx + group_string.length());
1706 /* set the playlist button tooltip to the take name */
1707 ARDOUR_UI::instance()->set_tip (
1709 string_compose(_("Take: %1.%2"),
1710 Glib::Markup::escape_text(rg->name()),
1711 Glib::Markup::escape_text(take_name))
1718 /* set the playlist button tooltip to the playlist name */
1719 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1724 RouteTimeAxisView::show_playlist_selector ()
1726 _editor.playlist_selector().show_for (this);
1730 RouteTimeAxisView::map_frozen ()
1736 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1738 switch (track()->freeze_state()) {
1740 playlist_button.set_sensitive (false);
1741 rec_enable_button->set_sensitive (false);
1744 playlist_button.set_sensitive (true);
1745 rec_enable_button->set_sensitive (true);
1751 RouteTimeAxisView::color_handler ()
1753 //case cTimeStretchOutline:
1754 if (timestretch_rect) {
1755 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1757 //case cTimeStretchFill:
1758 if (timestretch_rect) {
1759 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1765 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1766 * Will add track if necessary.
1769 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1771 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1772 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1775 /* it doesn't exist yet, so we don't care about the button state: just add it */
1776 create_automation_child (param, true);
1779 bool yn = menu->get_active();
1780 bool changed = false;
1782 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1784 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1785 will have done that for us.
1788 if (changed && !no_redraw) {
1796 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1798 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1804 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1806 if (menu && !_hidden) {
1807 ignore_toggle = true;
1808 menu->set_active (false);
1809 ignore_toggle = false;
1812 if (_route && !no_redraw) {
1819 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1821 if (apply_to_selection) {
1822 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1826 /* Show our automation */
1828 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1829 i->second->set_marked_for_display (true);
1831 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1834 menu->set_active(true);
1839 /* Show processor automation */
1841 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1842 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1843 if ((*ii)->view == 0) {
1844 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1847 (*ii)->menu_item->set_active (true);
1860 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1862 if (apply_to_selection) {
1863 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1867 /* Show our automation */
1869 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1870 if (i->second->has_automation()) {
1871 i->second->set_marked_for_display (true);
1873 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1875 menu->set_active(true);
1880 /* Show processor automation */
1882 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1883 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1884 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1885 (*ii)->menu_item->set_active (true);
1897 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1899 if (apply_to_selection) {
1900 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1904 /* Hide our automation */
1906 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1907 i->second->set_marked_for_display (false);
1909 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1912 menu->set_active (false);
1916 /* Hide processor automation */
1918 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1919 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1920 (*ii)->menu_item->set_active (false);
1931 RouteTimeAxisView::region_view_added (RegionView* rv)
1933 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1934 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1935 boost::shared_ptr<AutomationTimeAxisView> atv;
1937 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1942 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1943 (*i)->add_ghost(rv);
1947 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1949 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1955 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1957 parent.remove_processor_automation_node (this);
1961 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1964 remove_child (pan->view);
1968 RouteTimeAxisView::ProcessorAutomationNode*
1969 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1971 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1973 if ((*i)->processor == processor) {
1975 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1976 if ((*ii)->what == what) {
1986 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1988 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1991 ProcessorAutomationNode* pan;
1993 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1994 /* session state may never have been saved with new plugin */
1995 error << _("programming error: ")
1996 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1997 processor->name(), what.type(), (int) what.channel(), what.id() )
2007 boost::shared_ptr<AutomationControl> control
2008 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2010 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2011 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2012 _editor, *this, false, parent_canvas,
2013 processor->describe_parameter (what), processor->name()));
2015 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2017 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2020 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2025 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2028 pan->menu_item->set_active (false);
2037 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2039 boost::shared_ptr<Processor> processor (p.lock ());
2041 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2042 /* The Amp processor is a special case and is dealt with separately */
2046 set<Evoral::Parameter> existing;
2048 processor->what_has_data (existing);
2050 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2052 Evoral::Parameter param (*i);
2053 boost::shared_ptr<AutomationLine> al;
2055 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2058 add_processor_automation_curve (processor, param);
2064 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2066 using namespace Menu_Helpers;
2070 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2072 _automation_tracks[param] = track;
2074 /* existing state overrides "show" argument */
2075 string s = track->gui_property ("visible");
2077 show = string_is_affirmative (s);
2080 /* this might or might not change the visibility status, so don't rely on it */
2081 track->set_marked_for_display (show);
2083 if (show && !no_redraw) {
2087 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2088 /* MIDI-related parameters are always in the menu, there's no
2089 reason to rebuild the menu just because we added a automation
2090 lane for one of them. But if we add a non-MIDI automation
2091 lane, then we need to invalidate the display menu.
2093 delete display_menu;
2099 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2101 boost::shared_ptr<Processor> processor (p.lock ());
2103 if (!processor || !processor->display_to_user ()) {
2107 /* we use this override to veto the Amp processor from the plugin menu,
2108 as its automation lane can be accessed using the special "Fader" menu
2112 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2116 using namespace Menu_Helpers;
2117 ProcessorAutomationInfo *rai;
2118 list<ProcessorAutomationInfo*>::iterator x;
2120 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2122 if (automatable.empty()) {
2126 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2127 if ((*x)->processor == processor) {
2132 if (x == processor_automation.end()) {
2134 rai = new ProcessorAutomationInfo (processor);
2135 processor_automation.push_back (rai);
2143 /* any older menu was deleted at the top of processors_changed()
2144 when we cleared the subplugin menu.
2147 rai->menu = manage (new Menu);
2148 MenuList& items = rai->menu->items();
2149 rai->menu->set_name ("ArdourContextMenu");
2153 std::set<Evoral::Parameter> has_visible_automation;
2154 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2156 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2158 ProcessorAutomationNode* pan;
2159 Gtk::CheckMenuItem* mitem;
2161 string name = processor->describe_parameter (*i);
2163 items.push_back (CheckMenuElem (name));
2164 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2166 _subplugin_menu_map[*i] = mitem;
2168 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2169 mitem->set_active(true);
2172 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2176 pan = new ProcessorAutomationNode (*i, mitem, *this);
2178 rai->lines.push_back (pan);
2182 pan->menu_item = mitem;
2186 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2189 /* add the menu for this processor, because the subplugin
2190 menu is always cleared at the top of processors_changed().
2191 this is the result of some poor design in gtkmm and/or
2195 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2200 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2201 RouteTimeAxisView::ProcessorAutomationNode* pan)
2203 bool showit = pan->menu_item->get_active();
2204 bool redraw = false;
2206 if (pan->view == 0 && showit) {
2207 add_processor_automation_curve (rai->processor, pan->what);
2211 if (pan->view && pan->view->set_marked_for_display (showit)) {
2215 if (redraw && !no_redraw) {
2221 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2223 if (c.type == RouteProcessorChange::MeterPointChange) {
2224 /* nothing to do if only the meter point has changed */
2228 using namespace Menu_Helpers;
2230 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2231 (*i)->valid = false;
2234 setup_processor_menu_and_curves ();
2236 bool deleted_processor_automation = false;
2238 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2240 list<ProcessorAutomationInfo*>::iterator tmp;
2248 processor_automation.erase (i);
2249 deleted_processor_automation = true;
2256 if (deleted_processor_automation && !no_redraw) {
2261 boost::shared_ptr<AutomationLine>
2262 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2264 ProcessorAutomationNode* pan;
2266 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2272 return boost::shared_ptr<AutomationLine>();
2276 RouteTimeAxisView::reset_processor_automation_curves ()
2278 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2284 RouteTimeAxisView::can_edit_name () const
2286 /* we do not allow track name changes if it is record enabled
2288 return !_route->record_enabled();
2292 RouteTimeAxisView::blink_rec_display (bool onoff)
2294 RouteUI::blink_rec_display (onoff);
2298 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2300 if (_ignore_set_layer_display) {
2304 if (apply_to_selection) {
2305 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2309 _view->set_layer_display (d);
2312 set_gui_property (X_("layer-display"), enum_2_string (d));
2317 RouteTimeAxisView::layer_display () const
2320 return _view->layer_display ();
2323 /* we don't know, since we don't have a _view, so just return something */
2329 boost::shared_ptr<AutomationTimeAxisView>
2330 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2332 AutomationTracks::iterator i = _automation_tracks.find(param);
2333 if (i != _automation_tracks.end()) {
2336 return boost::shared_ptr<AutomationTimeAxisView>();
2341 RouteTimeAxisView::fast_update ()
2343 gm.get_level_meter().update_meters ();
2347 RouteTimeAxisView::hide_meter ()
2350 gm.get_level_meter().hide_meters ();
2354 RouteTimeAxisView::show_meter ()
2360 RouteTimeAxisView::reset_meter ()
2362 if (Config->get_show_track_meters()) {
2363 int meter_width = 3;
2364 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2367 gm.get_level_meter().setup_meters (height - 9, meter_width);
2374 RouteTimeAxisView::clear_meter ()
2376 gm.get_level_meter().clear_meters ();
2380 RouteTimeAxisView::meter_changed ()
2382 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2384 if (_route && !no_redraw) {
2387 // reset peak when meter point changes
2388 gm.reset_peak_display();
2392 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2395 if (_route && !no_redraw) {
2401 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2403 using namespace Menu_Helpers;
2405 if (!_underlay_streams.empty()) {
2406 MenuList& parent_items = parent_menu->items();
2407 Menu* gs_menu = manage (new Menu);
2408 gs_menu->set_name ("ArdourContextMenu");
2409 MenuList& gs_items = gs_menu->items();
2411 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2413 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2414 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2415 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2421 RouteTimeAxisView::set_underlay_state()
2423 if (!underlay_xml_node) {
2427 XMLNodeList nlist = underlay_xml_node->children();
2428 XMLNodeConstIterator niter;
2429 XMLNode *child_node;
2431 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2432 child_node = *niter;
2434 if (child_node->name() != "Underlay") {
2438 XMLProperty* prop = child_node->property ("id");
2440 PBD::ID id (prop->value());
2442 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2445 add_underlay(v->view(), false);
2454 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2460 RouteTimeAxisView& other = v->trackview();
2462 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2463 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2464 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2468 _underlay_streams.push_back(v);
2469 other._underlay_mirrors.push_back(this);
2471 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2473 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2475 if (!underlay_xml_node) {
2476 underlay_xml_node = xml_node->add_child("Underlays");
2479 XMLNode* node = underlay_xml_node->add_child("Underlay");
2480 XMLProperty* prop = node->add_property("id");
2481 prop->set_value(v->trackview().route()->id().to_s());
2488 RouteTimeAxisView::remove_underlay (StreamView* v)
2494 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2495 RouteTimeAxisView& other = v->trackview();
2497 if (it != _underlay_streams.end()) {
2498 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2500 if (gm == other._underlay_mirrors.end()) {
2501 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2505 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2507 _underlay_streams.erase(it);
2508 other._underlay_mirrors.erase(gm);
2510 if (underlay_xml_node) {
2511 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2517 RouteTimeAxisView::set_button_names ()
2519 if (_route && _route->solo_safe()) {
2520 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2522 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2524 if (Config->get_solo_control_is_listen_control()) {
2525 switch (Config->get_listen_position()) {
2526 case AfterFaderListen:
2527 solo_button->set_text (_("A"));
2528 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2530 case PreFaderListen:
2531 solo_button->set_text (_("P"));
2532 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2536 solo_button->set_text (_("S"));
2537 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2539 mute_button->set_text (_("M"));
2543 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2545 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2546 if (i != _main_automation_menu_map.end()) {
2550 i = _subplugin_menu_map.find (param);
2551 if (i != _subplugin_menu_map.end()) {
2559 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2561 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2563 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2567 gain_track.reset (new AutomationTimeAxisView (_session,
2568 _route, _route->amp(), c, param,
2573 _route->amp()->describe_parameter(param)));
2576 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2579 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2583 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2585 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2587 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2591 mute_track.reset (new AutomationTimeAxisView (_session,
2592 _route, _route, c, param,
2597 _route->describe_parameter(param)));
2600 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2603 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2607 void add_region_to_list (RegionView* rv, RegionList* l)
2609 l->push_back (rv->region());
2613 RouteTimeAxisView::combine_regions ()
2615 /* as of may 2011, we do not offer uncombine for MIDI tracks
2618 if (!is_audio_track()) {
2626 RegionList selected_regions;
2627 boost::shared_ptr<Playlist> playlist = track()->playlist();
2629 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2631 if (selected_regions.size() < 2) {
2635 playlist->clear_changes ();
2636 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2638 _session->add_command (new StatefulDiffCommand (playlist));
2639 /* make the new region be selected */
2641 return _view->find_view (compound_region);
2645 RouteTimeAxisView::uncombine_regions ()
2647 /* as of may 2011, we do not offer uncombine for MIDI tracks
2649 if (!is_audio_track()) {
2657 RegionList selected_regions;
2658 boost::shared_ptr<Playlist> playlist = track()->playlist();
2660 /* have to grab selected regions first because the uncombine is going
2661 * to change that in the middle of the list traverse
2664 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2666 playlist->clear_changes ();
2668 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2669 playlist->uncombine (*i);
2672 _session->add_command (new StatefulDiffCommand (playlist));
2676 RouteTimeAxisView::state_id() const
2678 return string_compose ("rtav %1", _route->id().to_s());
2683 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2685 TimeAxisView::remove_child (c);
2687 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2689 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2690 if (i->second == a) {
2691 _automation_tracks.erase (i);