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 ()
378 bool show_label = _session->config.get_track_name_number();
380 if (_route && _route->is_master()) {
384 if (number_label.get_parent()) {
385 controls_table.remove (number_label); // XXX
388 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
389 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
390 number_label.set_size_request(3 + tnw, -1);
391 number_label.show ();
393 number_label.hide ();
398 RouteTimeAxisView::parameter_changed (string const & p)
400 if (p == "track-name-number") {
401 update_track_number_visibility();
406 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
408 if (what_changed.contains (ARDOUR::Properties::name)) {
414 RouteTimeAxisView::take_name_changed (void *src)
422 RouteTimeAxisView::playlist_click ()
424 build_playlist_menu ();
425 conditionally_add_to_selection ();
426 playlist_action_menu->popup (1, gtk_get_current_event_time());
430 RouteTimeAxisView::automation_click ()
432 conditionally_add_to_selection ();
433 build_automation_action_menu (false);
434 automation_action_menu->popup (1, gtk_get_current_event_time());
438 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
440 using namespace Menu_Helpers;
442 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
443 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
446 detach_menu (subplugin_menu);
448 _main_automation_menu_map.clear ();
449 delete automation_action_menu;
450 automation_action_menu = new Menu;
452 MenuList& items = automation_action_menu->items();
454 automation_action_menu->set_name ("ArdourContextMenu");
456 items.push_back (MenuElem (_("Show All Automation"),
457 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
459 items.push_back (MenuElem (_("Show Existing Automation"),
460 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
462 items.push_back (MenuElem (_("Hide All Automation"),
463 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
465 /* Attach the plugin submenu. It may have previously been used elsewhere,
466 so it was detached above
469 if (!subplugin_menu.items().empty()) {
470 items.push_back (SeparatorElem ());
471 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
472 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
477 RouteTimeAxisView::build_display_menu ()
479 using namespace Menu_Helpers;
483 TimeAxisView::build_display_menu ();
485 /* now fill it with our stuff */
487 MenuList& items = display_menu->items();
488 display_menu->set_name ("ArdourContextMenu");
490 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
492 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
494 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
496 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
498 items.push_back (SeparatorElem());
501 detach_menu (*_size_menu);
504 items.push_back (MenuElem (_("Height"), *_size_menu));
506 items.push_back (SeparatorElem());
508 if (!Profile->get_sae()) {
509 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
510 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
511 items.push_back (SeparatorElem());
514 // Hook for derived classes to add type specific stuff
515 append_extra_display_menu_items ();
519 Menu* layers_menu = manage (new Menu);
520 MenuList &layers_items = layers_menu->items();
521 layers_menu->set_name("ArdourContextMenu");
523 RadioMenuItem::Group layers_group;
525 /* Find out how many overlaid/stacked tracks we have in the selection */
529 TrackSelection const & s = _editor.get_selection().tracks;
530 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
531 StreamView* v = (*i)->view ();
536 switch (v->layer_display ()) {
547 /* We're not connecting to signal_toggled() here; in the case where these two items are
548 set to be in the `inconsistent' state, it seems that one or other will end up active
549 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
550 select the active one, no toggled signal is emitted so nothing happens.
553 _ignore_set_layer_display = true;
555 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
556 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
557 i->set_active (overlaid != 0 && stacked == 0);
558 i->set_inconsistent (overlaid != 0 && stacked != 0);
559 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
561 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
562 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
563 i->set_active (overlaid == 0 && stacked != 0);
564 i->set_inconsistent (overlaid != 0 && stacked != 0);
565 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
567 _ignore_set_layer_display = false;
569 items.push_back (MenuElem (_("Layers"), *layers_menu));
571 if (!Profile->get_sae()) {
573 Menu* alignment_menu = manage (new Menu);
574 MenuList& alignment_items = alignment_menu->items();
575 alignment_menu->set_name ("ArdourContextMenu");
577 RadioMenuItem::Group align_group;
579 /* Same verbose hacks as for the layering options above */
585 boost::shared_ptr<Track> first_track;
587 TrackSelection const & s = _editor.get_selection().tracks;
588 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
589 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
590 if (!r || !r->is_track ()) {
595 first_track = r->track();
598 switch (r->track()->alignment_choice()) {
602 switch (r->track()->alignment_style()) {
603 case ExistingMaterial:
611 case UseExistingMaterial:
627 inconsistent = false;
636 if (!inconsistent && first_track) {
638 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
639 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
640 i->set_active (automatic != 0 && existing == 0 && capture == 0);
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
643 switch (first_track->alignment_choice()) {
645 switch (first_track->alignment_style()) {
646 case ExistingMaterial:
647 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
650 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
658 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
659 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
660 i->set_active (existing != 0 && capture == 0 && automatic == 0);
661 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
663 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
664 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
665 i->set_active (existing == 0 && capture != 0 && automatic == 0);
666 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
668 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
674 Menu* mode_menu = manage (new Menu);
675 MenuList& mode_items = mode_menu->items ();
676 mode_menu->set_name ("ArdourContextMenu");
678 RadioMenuItem::Group mode_group;
684 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
685 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
686 if (!r || !r->is_track ()) {
690 switch (r->track()->mode()) {
703 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
704 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
705 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
706 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
707 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
709 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
710 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
711 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
712 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
713 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
715 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
716 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
717 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
718 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
719 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
721 items.push_back (MenuElem (_("Mode"), *mode_menu));
725 items.push_back (SeparatorElem());
727 build_playlist_menu ();
728 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
729 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
732 route_group_menu->detach ();
735 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
736 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
738 r.push_back (rtv->route ());
743 r.push_back (route ());
746 route_group_menu->build (r);
747 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
749 build_automation_action_menu (true);
750 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
752 items.push_back (SeparatorElem());
756 TrackSelection const & s = _editor.get_selection().tracks;
757 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
758 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
763 if (r->route()->active()) {
770 items.push_back (CheckMenuElem (_("Active")));
771 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
772 bool click_sets_active = true;
773 if (active > 0 && inactive == 0) {
774 i->set_active (true);
775 click_sets_active = false;
776 } else if (active > 0 && inactive > 0) {
777 i->set_inconsistent (true);
779 i->set_sensitive(! _session->transport_rolling());
780 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
782 items.push_back (SeparatorElem());
783 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
784 if (!Profile->get_sae()) {
785 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
787 items.push_front (SeparatorElem());
788 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
793 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
795 if (apply_to_selection) {
796 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
799 bool needs_bounce = false;
801 if (!track()->can_use_mode (mode, needs_bounce)) {
807 cerr << "would bounce this one\n";
812 track()->set_mode (mode);
814 rec_enable_button->remove ();
817 case ARDOUR::NonLayered:
819 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
820 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
822 case ARDOUR::Destructive:
823 rec_enable_button->set_text (string());
824 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
828 rec_enable_button->show_all ();
833 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
835 TimeAxisView::show_timestretch (start, end, layers, layer);
845 /* check that the time selection was made in our route, or our route group.
846 remember that route_group() == 0 implies the route is *not* in a edit group.
849 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
850 /* this doesn't apply to us */
854 /* ignore it if our edit group is not active */
856 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
861 if (timestretch_rect == 0) {
862 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
863 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
864 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
867 timestretch_rect->show ();
868 timestretch_rect->raise_to_top ();
870 double const x1 = start / _editor.get_current_zoom();
871 double const x2 = (end - 1) / _editor.get_current_zoom();
873 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
874 x2, current_height() * (layers - layer) / layers));
878 RouteTimeAxisView::hide_timestretch ()
880 TimeAxisView::hide_timestretch ();
882 if (timestretch_rect) {
883 timestretch_rect->hide ();
888 RouteTimeAxisView::show_selection (TimeSelection& ts)
892 /* ignore it if our edit group is not active or if the selection was started
893 in some other track or route group (remember that route_group() == 0 means
894 that the track is not in an route group).
897 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
898 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
904 TimeAxisView::show_selection (ts);
908 RouteTimeAxisView::set_height (uint32_t h)
911 bool height_changed = (height == 0) || (h != height);
914 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
917 gm.get_level_meter().setup_meters (gmlen, meter_width);
919 TimeAxisView::set_height (h);
922 _view->set_height ((double) current_height());
925 if (height >= preset_height (HeightNormal)) {
929 gm.get_gain_slider().show();
931 if (!_route || _route->is_monitor()) {
936 if (rec_enable_button)
937 rec_enable_button->show();
939 route_group_button.show();
940 automation_button.show();
942 if (is_track() && track()->mode() == ARDOUR::Normal) {
943 playlist_button.show();
950 gm.get_gain_slider().hide();
952 if (!_route || _route->is_monitor()) {
957 if (rec_enable_button)
958 rec_enable_button->show();
960 route_group_button.hide ();
961 automation_button.hide ();
963 if (is_track() && track()->mode() == ARDOUR::Normal) {
964 playlist_button.hide ();
969 if (height_changed && !no_redraw) {
970 /* only emit the signal if the height really changed */
976 RouteTimeAxisView::route_color_changed ()
979 _view->apply_color (color(), StreamView::RegionColor);
982 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
986 RouteTimeAxisView::reset_samples_per_pixel ()
988 set_samples_per_pixel (_editor.get_current_zoom());
992 RouteTimeAxisView::set_samples_per_pixel (double fpp)
997 speed = track()->speed();
1001 _view->set_samples_per_pixel (fpp * speed);
1004 TimeAxisView::set_samples_per_pixel (fpp * speed);
1008 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1010 if (!mitem->get_active()) {
1011 /* this is one of the two calls made when these radio menu items change status. this one
1012 is for the item that became inactive, and we want to ignore it.
1017 if (apply_to_selection) {
1018 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1021 track()->set_align_choice (choice);
1027 RouteTimeAxisView::rename_current_playlist ()
1029 ArdourPrompter prompter (true);
1032 boost::shared_ptr<Track> tr = track();
1033 if (!tr || tr->destructive()) {
1037 boost::shared_ptr<Playlist> pl = tr->playlist();
1042 prompter.set_title (_("Rename Playlist"));
1043 prompter.set_prompt (_("New name for playlist:"));
1044 prompter.set_initial_text (pl->name());
1045 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1046 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1048 switch (prompter.run ()) {
1049 case Gtk::RESPONSE_ACCEPT:
1050 prompter.get_result (name);
1051 if (name.length()) {
1052 pl->set_name (name);
1062 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1064 std::string ret (basename);
1066 std::string const group_string = "." + route_group()->name() + ".";
1068 // iterate through all playlists
1070 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1071 std::string tmp = (*i)->name();
1073 std::string::size_type idx = tmp.find(group_string);
1074 // find those which belong to this group
1075 if (idx != string::npos) {
1076 tmp = tmp.substr(idx + group_string.length());
1078 // and find the largest current number
1080 if (x > maxnumber) {
1089 snprintf (buf, sizeof(buf), "%d", maxnumber);
1091 ret = this->name() + "." + route_group()->name () + "." + buf;
1097 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1101 boost::shared_ptr<Track> tr = track ();
1102 if (!tr || tr->destructive()) {
1106 boost::shared_ptr<const Playlist> pl = tr->playlist();
1113 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1114 name = resolve_new_group_playlist_name(name, playlists_before_op);
1117 while (_session->playlists->by_name(name)) {
1118 name = Playlist::bump_name (name, *_session);
1121 // TODO: The prompter "new" button should be de-activated if the user
1122 // specifies a playlist name which already exists in the session.
1126 ArdourPrompter prompter (true);
1128 prompter.set_title (_("New Copy Playlist"));
1129 prompter.set_prompt (_("Name for new playlist:"));
1130 prompter.set_initial_text (name);
1131 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1132 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1133 prompter.show_all ();
1135 switch (prompter.run ()) {
1136 case Gtk::RESPONSE_ACCEPT:
1137 prompter.get_result (name);
1145 if (name.length()) {
1146 tr->use_copy_playlist ();
1147 tr->playlist()->set_name (name);
1152 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1156 boost::shared_ptr<Track> tr = track ();
1157 if (!tr || tr->destructive()) {
1161 boost::shared_ptr<const Playlist> pl = tr->playlist();
1168 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1169 name = resolve_new_group_playlist_name(name,playlists_before_op);
1172 while (_session->playlists->by_name(name)) {
1173 name = Playlist::bump_name (name, *_session);
1179 ArdourPrompter prompter (true);
1181 prompter.set_title (_("New Playlist"));
1182 prompter.set_prompt (_("Name for new playlist:"));
1183 prompter.set_initial_text (name);
1184 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1185 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1187 switch (prompter.run ()) {
1188 case Gtk::RESPONSE_ACCEPT:
1189 prompter.get_result (name);
1197 if (name.length()) {
1198 tr->use_new_playlist ();
1199 tr->playlist()->set_name (name);
1204 RouteTimeAxisView::clear_playlist ()
1206 boost::shared_ptr<Track> tr = track ();
1207 if (!tr || tr->destructive()) {
1211 boost::shared_ptr<Playlist> pl = tr->playlist();
1216 _editor.clear_playlist (pl);
1220 RouteTimeAxisView::speed_changed ()
1222 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1226 RouteTimeAxisView::update_diskstream_display ()
1236 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1238 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1240 /* special case: select/deselect all tracks */
1241 if (_editor.get_selection().selected (this)) {
1242 _editor.get_selection().clear_tracks ();
1244 _editor.select_all_tracks ();
1250 switch (ArdourKeyboard::selection_type (ev->state)) {
1251 case Selection::Toggle:
1252 _editor.get_selection().toggle (this);
1255 case Selection::Set:
1256 _editor.get_selection().set (this);
1259 case Selection::Extend:
1260 _editor.extend_selection_to_track (*this);
1263 case Selection::Add:
1264 _editor.get_selection().add (this);
1270 RouteTimeAxisView::set_selected_points (PointSelection& points)
1272 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1273 (*i)->set_selected_points (points);
1278 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1281 _view->set_selected_regionviews (regions);
1285 /** Add the selectable things that we have to a list.
1286 * @param results List to add things to.
1289 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1294 speed = track()->speed();
1297 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1298 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1300 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1301 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1304 /* pick up visible automation tracks */
1306 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1307 if (!(*i)->hidden()) {
1308 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1314 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1317 _view->get_inverted_selectables (sel, results);
1320 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1321 if (!(*i)->hidden()) {
1322 (*i)->get_inverted_selectables (sel, results);
1330 RouteTimeAxisView::route_group () const
1332 return _route->route_group();
1336 RouteTimeAxisView::name() const
1338 return _route->name();
1341 boost::shared_ptr<Playlist>
1342 RouteTimeAxisView::playlist () const
1344 boost::shared_ptr<Track> tr;
1346 if ((tr = track()) != 0) {
1347 return tr->playlist();
1349 return boost::shared_ptr<Playlist> ();
1354 RouteTimeAxisView::name_entry_changed ()
1356 TimeAxisView::name_entry_changed ();
1358 string x = name_entry->get_text ();
1360 if (x == _route->name()) {
1364 strip_whitespace_edges (x);
1366 if (x.length() == 0) {
1367 name_entry->set_text (_route->name());
1371 if (_session->route_name_internal (x)) {
1372 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1374 name_entry->grab_focus ();
1375 } else if (RouteUI::verify_new_route_name (x)) {
1376 _route->set_name (x);
1378 name_entry->grab_focus ();
1382 boost::shared_ptr<Region>
1383 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1385 boost::shared_ptr<Playlist> pl = playlist ();
1388 return pl->find_next_region (pos, point, dir);
1391 return boost::shared_ptr<Region> ();
1395 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1397 boost::shared_ptr<Playlist> pl = playlist ();
1400 return pl->find_next_region_boundary (pos, dir);
1407 RouteTimeAxisView::fade_range (TimeSelection& selection)
1409 boost::shared_ptr<Playlist> what_we_got;
1410 boost::shared_ptr<Track> tr = track ();
1411 boost::shared_ptr<Playlist> playlist;
1414 /* route is a bus, not a track */
1418 playlist = tr->playlist();
1420 TimeSelection time (selection);
1421 float const speed = tr->speed();
1422 if (speed != 1.0f) {
1423 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1424 (*i).start = session_frame_to_track_frame((*i).start, speed);
1425 (*i).end = session_frame_to_track_frame((*i).end, speed);
1429 playlist->clear_changes ();
1430 playlist->clear_owned_changes ();
1432 playlist->fade_range (time);
1434 vector<Command*> cmds;
1435 playlist->rdiff (cmds);
1436 _session->add_commands (cmds);
1437 _session->add_command (new StatefulDiffCommand (playlist));
1442 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1444 boost::shared_ptr<Playlist> what_we_got;
1445 boost::shared_ptr<Track> tr = track ();
1446 boost::shared_ptr<Playlist> playlist;
1449 /* route is a bus, not a track */
1453 playlist = tr->playlist();
1455 TimeSelection time (selection.time);
1456 float const speed = tr->speed();
1457 if (speed != 1.0f) {
1458 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1459 (*i).start = session_frame_to_track_frame((*i).start, speed);
1460 (*i).end = session_frame_to_track_frame((*i).end, speed);
1464 playlist->clear_changes ();
1465 playlist->clear_owned_changes ();
1469 if (playlist->cut (time) != 0) {
1470 if (Config->get_edit_mode() == Ripple)
1471 playlist->ripple(time.start(), -time.length(), NULL);
1472 // no need to exclude any regions from rippling here
1474 vector<Command*> cmds;
1475 playlist->rdiff (cmds);
1476 _session->add_commands (cmds);
1478 _session->add_command (new StatefulDiffCommand (playlist));
1483 if ((what_we_got = playlist->cut (time)) != 0) {
1484 _editor.get_cut_buffer().add (what_we_got);
1485 if (Config->get_edit_mode() == Ripple)
1486 playlist->ripple(time.start(), -time.length(), NULL);
1487 // no need to exclude any regions from rippling here
1489 vector<Command*> cmds;
1490 playlist->rdiff (cmds);
1491 _session->add_commands (cmds);
1493 _session->add_command (new StatefulDiffCommand (playlist));
1497 if ((what_we_got = playlist->copy (time)) != 0) {
1498 _editor.get_cut_buffer().add (what_we_got);
1503 if ((what_we_got = playlist->cut (time)) != 0) {
1504 if (Config->get_edit_mode() == Ripple)
1505 playlist->ripple(time.start(), -time.length(), NULL);
1506 // no need to exclude any regions from rippling here
1508 vector<Command*> cmds;
1509 playlist->rdiff (cmds);
1510 _session->add_commands (cmds);
1511 _session->add_command (new StatefulDiffCommand (playlist));
1512 what_we_got->release ();
1519 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1525 boost::shared_ptr<Playlist> pl = playlist ();
1526 PlaylistSelection::iterator p;
1528 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1530 if (p == selection.playlists.end()) {
1534 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1536 if (track()->speed() != 1.0f) {
1537 pos = session_frame_to_track_frame (pos, track()->speed());
1538 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1541 pl->clear_changes ();
1542 if (Config->get_edit_mode() == Ripple) {
1543 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1544 framecnt_t amount = extent.second - extent.first;
1545 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1547 pl->paste (*p, pos, times);
1549 vector<Command*> cmds;
1551 _session->add_commands (cmds);
1553 _session->add_command (new StatefulDiffCommand (pl));
1559 struct PlaylistSorter {
1560 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1561 return a->sort_id() < b->sort_id();
1566 RouteTimeAxisView::build_playlist_menu ()
1568 using namespace Menu_Helpers;
1574 delete playlist_action_menu;
1575 playlist_action_menu = new Menu;
1576 playlist_action_menu->set_name ("ArdourContextMenu");
1578 MenuList& playlist_items = playlist_action_menu->items();
1579 playlist_action_menu->set_name ("ArdourContextMenu");
1580 playlist_items.clear();
1582 RadioMenuItem::Group playlist_group;
1583 boost::shared_ptr<Track> tr = track ();
1585 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1587 /* sort the playlists */
1589 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1591 /* add the playlists to the menu */
1592 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1593 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1594 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1595 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1597 if (tr->playlist()->id() == (*i)->id()) {
1603 playlist_items.push_back (SeparatorElem());
1604 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1605 playlist_items.push_back (SeparatorElem());
1607 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1608 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1609 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1612 // Use a label which tells the user what is happening
1613 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1614 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1618 playlist_items.push_back (SeparatorElem());
1619 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1620 playlist_items.push_back (SeparatorElem());
1622 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1626 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1628 assert (is_track());
1630 // exit if we were triggered by deactivating the old playlist
1631 if (!item->get_active()) {
1635 boost::shared_ptr<Playlist> pl (wpl.lock());
1641 if (track()->playlist() == pl) {
1642 // exit when use_playlist is called by the creation of the playlist menu
1643 // or the playlist choice is unchanged
1647 track()->use_playlist (pl);
1649 RouteGroup* rg = route_group();
1651 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1652 std::string group_string = "." + rg->name() + ".";
1654 std::string take_name = pl->name();
1655 std::string::size_type idx = take_name.find(group_string);
1657 if (idx == std::string::npos)
1660 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1662 boost::shared_ptr<RouteList> rl (rg->route_list());
1664 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1665 if ((*i) == this->route()) {
1669 std::string playlist_name = (*i)->name()+group_string+take_name;
1671 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1676 if (track->freeze_state() == Track::Frozen) {
1677 /* Don't change playlists of frozen tracks */
1681 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1683 // No playlist for this track for this take yet, make it
1684 track->use_new_playlist();
1685 track->playlist()->set_name(playlist_name);
1687 track->use_playlist(ipl);
1694 RouteTimeAxisView::update_playlist_tip ()
1696 RouteGroup* rg = route_group ();
1697 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1698 string group_string = "." + rg->name() + ".";
1700 string take_name = track()->playlist()->name();
1701 string::size_type idx = take_name.find(group_string);
1703 if (idx != string::npos) {
1704 /* find the bit containing the take number / name */
1705 take_name = take_name.substr (idx + group_string.length());
1707 /* set the playlist button tooltip to the take name */
1708 ARDOUR_UI::instance()->set_tip (
1710 string_compose(_("Take: %1.%2"),
1711 Glib::Markup::escape_text(rg->name()),
1712 Glib::Markup::escape_text(take_name))
1719 /* set the playlist button tooltip to the playlist name */
1720 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1725 RouteTimeAxisView::show_playlist_selector ()
1727 _editor.playlist_selector().show_for (this);
1731 RouteTimeAxisView::map_frozen ()
1737 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1739 switch (track()->freeze_state()) {
1741 playlist_button.set_sensitive (false);
1742 rec_enable_button->set_sensitive (false);
1745 playlist_button.set_sensitive (true);
1746 rec_enable_button->set_sensitive (true);
1752 RouteTimeAxisView::color_handler ()
1754 //case cTimeStretchOutline:
1755 if (timestretch_rect) {
1756 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1758 //case cTimeStretchFill:
1759 if (timestretch_rect) {
1760 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1766 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1767 * Will add track if necessary.
1770 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1772 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1773 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1776 /* it doesn't exist yet, so we don't care about the button state: just add it */
1777 create_automation_child (param, true);
1780 bool yn = menu->get_active();
1781 bool changed = false;
1783 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1785 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1786 will have done that for us.
1789 if (changed && !no_redraw) {
1797 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1799 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1805 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1807 if (menu && !_hidden) {
1808 ignore_toggle = true;
1809 menu->set_active (false);
1810 ignore_toggle = false;
1813 if (_route && !no_redraw) {
1820 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1822 if (apply_to_selection) {
1823 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1827 /* Show our automation */
1829 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1830 i->second->set_marked_for_display (true);
1832 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1835 menu->set_active(true);
1840 /* Show processor automation */
1842 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1843 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1844 if ((*ii)->view == 0) {
1845 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1848 (*ii)->menu_item->set_active (true);
1861 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1863 if (apply_to_selection) {
1864 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1868 /* Show our automation */
1870 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1871 if (i->second->has_automation()) {
1872 i->second->set_marked_for_display (true);
1874 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1876 menu->set_active(true);
1881 /* Show processor automation */
1883 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1884 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1885 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1886 (*ii)->menu_item->set_active (true);
1898 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1900 if (apply_to_selection) {
1901 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1905 /* Hide our automation */
1907 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1908 i->second->set_marked_for_display (false);
1910 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1913 menu->set_active (false);
1917 /* Hide processor automation */
1919 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1920 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1921 (*ii)->menu_item->set_active (false);
1932 RouteTimeAxisView::region_view_added (RegionView* rv)
1934 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1935 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1936 boost::shared_ptr<AutomationTimeAxisView> atv;
1938 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1943 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1944 (*i)->add_ghost(rv);
1948 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1950 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1956 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1958 parent.remove_processor_automation_node (this);
1962 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1965 remove_child (pan->view);
1969 RouteTimeAxisView::ProcessorAutomationNode*
1970 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1972 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1974 if ((*i)->processor == processor) {
1976 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1977 if ((*ii)->what == what) {
1987 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1989 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1992 ProcessorAutomationNode* pan;
1994 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1995 /* session state may never have been saved with new plugin */
1996 error << _("programming error: ")
1997 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1998 processor->name(), what.type(), (int) what.channel(), what.id() )
2008 boost::shared_ptr<AutomationControl> control
2009 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2011 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2012 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2013 _editor, *this, false, parent_canvas,
2014 processor->describe_parameter (what), processor->name()));
2016 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2018 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2021 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2026 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2029 pan->menu_item->set_active (false);
2038 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2040 boost::shared_ptr<Processor> processor (p.lock ());
2042 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2043 /* The Amp processor is a special case and is dealt with separately */
2047 set<Evoral::Parameter> existing;
2049 processor->what_has_data (existing);
2051 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2053 Evoral::Parameter param (*i);
2054 boost::shared_ptr<AutomationLine> al;
2056 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2059 add_processor_automation_curve (processor, param);
2065 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2067 using namespace Menu_Helpers;
2071 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2073 _automation_tracks[param] = track;
2075 /* existing state overrides "show" argument */
2076 string s = track->gui_property ("visible");
2078 show = string_is_affirmative (s);
2081 /* this might or might not change the visibility status, so don't rely on it */
2082 track->set_marked_for_display (show);
2084 if (show && !no_redraw) {
2088 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2089 /* MIDI-related parameters are always in the menu, there's no
2090 reason to rebuild the menu just because we added a automation
2091 lane for one of them. But if we add a non-MIDI automation
2092 lane, then we need to invalidate the display menu.
2094 delete display_menu;
2100 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2102 boost::shared_ptr<Processor> processor (p.lock ());
2104 if (!processor || !processor->display_to_user ()) {
2108 /* we use this override to veto the Amp processor from the plugin menu,
2109 as its automation lane can be accessed using the special "Fader" menu
2113 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2117 using namespace Menu_Helpers;
2118 ProcessorAutomationInfo *rai;
2119 list<ProcessorAutomationInfo*>::iterator x;
2121 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2123 if (automatable.empty()) {
2127 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2128 if ((*x)->processor == processor) {
2133 if (x == processor_automation.end()) {
2135 rai = new ProcessorAutomationInfo (processor);
2136 processor_automation.push_back (rai);
2144 /* any older menu was deleted at the top of processors_changed()
2145 when we cleared the subplugin menu.
2148 rai->menu = manage (new Menu);
2149 MenuList& items = rai->menu->items();
2150 rai->menu->set_name ("ArdourContextMenu");
2154 std::set<Evoral::Parameter> has_visible_automation;
2155 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2157 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2159 ProcessorAutomationNode* pan;
2160 Gtk::CheckMenuItem* mitem;
2162 string name = processor->describe_parameter (*i);
2164 items.push_back (CheckMenuElem (name));
2165 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2167 _subplugin_menu_map[*i] = mitem;
2169 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2170 mitem->set_active(true);
2173 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2177 pan = new ProcessorAutomationNode (*i, mitem, *this);
2179 rai->lines.push_back (pan);
2183 pan->menu_item = mitem;
2187 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2190 /* add the menu for this processor, because the subplugin
2191 menu is always cleared at the top of processors_changed().
2192 this is the result of some poor design in gtkmm and/or
2196 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2201 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2202 RouteTimeAxisView::ProcessorAutomationNode* pan)
2204 bool showit = pan->menu_item->get_active();
2205 bool redraw = false;
2207 if (pan->view == 0 && showit) {
2208 add_processor_automation_curve (rai->processor, pan->what);
2212 if (pan->view && pan->view->set_marked_for_display (showit)) {
2216 if (redraw && !no_redraw) {
2222 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2224 if (c.type == RouteProcessorChange::MeterPointChange) {
2225 /* nothing to do if only the meter point has changed */
2229 using namespace Menu_Helpers;
2231 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2232 (*i)->valid = false;
2235 setup_processor_menu_and_curves ();
2237 bool deleted_processor_automation = false;
2239 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2241 list<ProcessorAutomationInfo*>::iterator tmp;
2249 processor_automation.erase (i);
2250 deleted_processor_automation = true;
2257 if (deleted_processor_automation && !no_redraw) {
2262 boost::shared_ptr<AutomationLine>
2263 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2265 ProcessorAutomationNode* pan;
2267 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2273 return boost::shared_ptr<AutomationLine>();
2277 RouteTimeAxisView::reset_processor_automation_curves ()
2279 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2285 RouteTimeAxisView::can_edit_name () const
2287 /* we do not allow track name changes if it is record enabled
2289 return !_route->record_enabled();
2293 RouteTimeAxisView::blink_rec_display (bool onoff)
2295 RouteUI::blink_rec_display (onoff);
2299 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2301 if (_ignore_set_layer_display) {
2305 if (apply_to_selection) {
2306 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2310 _view->set_layer_display (d);
2313 set_gui_property (X_("layer-display"), enum_2_string (d));
2318 RouteTimeAxisView::layer_display () const
2321 return _view->layer_display ();
2324 /* we don't know, since we don't have a _view, so just return something */
2330 boost::shared_ptr<AutomationTimeAxisView>
2331 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2333 AutomationTracks::iterator i = _automation_tracks.find(param);
2334 if (i != _automation_tracks.end()) {
2337 return boost::shared_ptr<AutomationTimeAxisView>();
2342 RouteTimeAxisView::fast_update ()
2344 gm.get_level_meter().update_meters ();
2348 RouteTimeAxisView::hide_meter ()
2351 gm.get_level_meter().hide_meters ();
2355 RouteTimeAxisView::show_meter ()
2361 RouteTimeAxisView::reset_meter ()
2363 if (Config->get_show_track_meters()) {
2364 int meter_width = 3;
2365 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2368 gm.get_level_meter().setup_meters (height - 9, meter_width);
2375 RouteTimeAxisView::clear_meter ()
2377 gm.get_level_meter().clear_meters ();
2381 RouteTimeAxisView::meter_changed ()
2383 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2385 if (_route && !no_redraw) {
2388 // reset peak when meter point changes
2389 gm.reset_peak_display();
2393 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2396 if (_route && !no_redraw) {
2402 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2404 using namespace Menu_Helpers;
2406 if (!_underlay_streams.empty()) {
2407 MenuList& parent_items = parent_menu->items();
2408 Menu* gs_menu = manage (new Menu);
2409 gs_menu->set_name ("ArdourContextMenu");
2410 MenuList& gs_items = gs_menu->items();
2412 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2414 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2415 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2416 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2422 RouteTimeAxisView::set_underlay_state()
2424 if (!underlay_xml_node) {
2428 XMLNodeList nlist = underlay_xml_node->children();
2429 XMLNodeConstIterator niter;
2430 XMLNode *child_node;
2432 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2433 child_node = *niter;
2435 if (child_node->name() != "Underlay") {
2439 XMLProperty* prop = child_node->property ("id");
2441 PBD::ID id (prop->value());
2443 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2446 add_underlay(v->view(), false);
2455 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2461 RouteTimeAxisView& other = v->trackview();
2463 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2464 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2465 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2469 _underlay_streams.push_back(v);
2470 other._underlay_mirrors.push_back(this);
2472 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2474 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2476 if (!underlay_xml_node) {
2477 underlay_xml_node = xml_node->add_child("Underlays");
2480 XMLNode* node = underlay_xml_node->add_child("Underlay");
2481 XMLProperty* prop = node->add_property("id");
2482 prop->set_value(v->trackview().route()->id().to_s());
2489 RouteTimeAxisView::remove_underlay (StreamView* v)
2495 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2496 RouteTimeAxisView& other = v->trackview();
2498 if (it != _underlay_streams.end()) {
2499 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2501 if (gm == other._underlay_mirrors.end()) {
2502 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2506 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2508 _underlay_streams.erase(it);
2509 other._underlay_mirrors.erase(gm);
2511 if (underlay_xml_node) {
2512 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2518 RouteTimeAxisView::set_button_names ()
2520 if (_route && _route->solo_safe()) {
2521 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2523 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2525 if (Config->get_solo_control_is_listen_control()) {
2526 switch (Config->get_listen_position()) {
2527 case AfterFaderListen:
2528 solo_button->set_text (_("A"));
2529 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2531 case PreFaderListen:
2532 solo_button->set_text (_("P"));
2533 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2537 solo_button->set_text (_("S"));
2538 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2540 mute_button->set_text (_("M"));
2544 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2546 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2547 if (i != _main_automation_menu_map.end()) {
2551 i = _subplugin_menu_map.find (param);
2552 if (i != _subplugin_menu_map.end()) {
2560 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2562 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2564 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2568 gain_track.reset (new AutomationTimeAxisView (_session,
2569 _route, _route->amp(), c, param,
2574 _route->amp()->describe_parameter(param)));
2577 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2580 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2584 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2586 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2588 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2592 mute_track.reset (new AutomationTimeAxisView (_session,
2593 _route, _route, c, param,
2598 _route->describe_parameter(param)));
2601 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2604 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2608 void add_region_to_list (RegionView* rv, RegionList* l)
2610 l->push_back (rv->region());
2614 RouteTimeAxisView::combine_regions ()
2616 /* as of may 2011, we do not offer uncombine for MIDI tracks
2619 if (!is_audio_track()) {
2627 RegionList selected_regions;
2628 boost::shared_ptr<Playlist> playlist = track()->playlist();
2630 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2632 if (selected_regions.size() < 2) {
2636 playlist->clear_changes ();
2637 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2639 _session->add_command (new StatefulDiffCommand (playlist));
2640 /* make the new region be selected */
2642 return _view->find_view (compound_region);
2646 RouteTimeAxisView::uncombine_regions ()
2648 /* as of may 2011, we do not offer uncombine for MIDI tracks
2650 if (!is_audio_track()) {
2658 RegionList selected_regions;
2659 boost::shared_ptr<Playlist> playlist = track()->playlist();
2661 /* have to grab selected regions first because the uncombine is going
2662 * to change that in the middle of the list traverse
2665 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2667 playlist->clear_changes ();
2669 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2670 playlist->uncombine (*i);
2673 _session->add_command (new StatefulDiffCommand (playlist));
2677 RouteTimeAxisView::state_id() const
2679 return string_compose ("rtav %1", _route->id().to_s());
2684 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2686 TimeAxisView::remove_child (c);
2688 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2690 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2691 if (i->second == a) {
2692 _automation_tracks.erase (i);