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, 14)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("tracknumber label");
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));
170 if (ARDOUR::Profile->get_mixbus()) {
171 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
173 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
175 controls_button_size_group->add_widget(*rec_enable_button);
177 if (is_midi_track()) {
178 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
179 gm.set_fader_name ("MidiTrackFader");
181 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
182 gm.set_fader_name ("AudioTrackFader");
185 rec_enable_button->set_sensitive (_session->writable());
187 /* set playlist button tip to the current playlist, and make it update when it changes */
188 update_playlist_tip ();
189 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
192 gm.set_fader_name ("AudioBusFader");
193 Gtk::Fixed *blank = manage(new Gtk::Fixed());
194 controls_button_size_group->add_widget(*blank);
195 if (ARDOUR::Profile->get_mixbus() ) {
196 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
198 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
203 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
205 if (!ARDOUR::Profile->get_mixbus()) {
206 controls_meters_size_group->add_widget (gm.get_level_meter());
209 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
210 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
214 if (ARDOUR::Profile->get_mixbus()) {
215 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
219 controls_button_size_group->add_widget(*mute_button);
221 if (!_route->is_master()) {
222 if (ARDOUR::Profile->get_mixbus()) {
223 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
227 controls_button_size_group->add_widget(*solo_button);
229 Gtk::Fixed *blank = manage(new Gtk::Fixed());
230 controls_button_size_group->add_widget(*blank);
231 if (ARDOUR::Profile->get_mixbus()) {
232 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_button_size_group->add_widget(route_group_button);
241 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
244 else if (!ARDOUR::Profile->get_trx()) {
245 controls_button_size_group->add_widget(route_group_button);
246 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
251 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
252 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
254 mute_button->set_tweaks(ArdourButton::Square);
255 solo_button->set_tweaks(ArdourButton::Square);
256 rec_enable_button->set_tweaks(ArdourButton::Square);
257 playlist_button.set_tweaks(ArdourButton::Square);
258 automation_button.set_tweaks(ArdourButton::Square);
259 route_group_button.set_tweaks(ArdourButton::Square);
261 if (is_midi_track()) {
262 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
264 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
267 update_track_number_visibility();
270 if (ARDOUR::Profile->get_mixbus()) {
271 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
272 controls_button_size_group->add_widget(automation_button);
274 else if (!ARDOUR::Profile->get_trx()) {
275 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
276 controls_button_size_group->add_widget(automation_button);
279 if (is_track() && track()->mode() == ARDOUR::Normal) {
280 if (ARDOUR::Profile->get_mixbus()) {
281 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282 controls_button_size_group->add_widget(playlist_button);
284 else if (!ARDOUR::Profile->get_trx()) {
285 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 controls_button_size_group->add_widget(playlist_button);
292 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
293 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
297 str = gui_property ("layer-display");
299 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
302 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
303 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
305 /* pick up the correct freeze state */
310 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
311 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
313 PropertyList* plist = new PropertyList();
315 plist->add (ARDOUR::Properties::mute, true);
316 plist->add (ARDOUR::Properties::solo, true);
318 route_group_menu = new RouteGroupMenu (_session, plist);
320 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
323 RouteTimeAxisView::~RouteTimeAxisView ()
325 CatchDeletion (this);
327 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
331 delete playlist_action_menu;
332 playlist_action_menu = 0;
337 _automation_tracks.clear ();
339 delete route_group_menu;
343 RouteTimeAxisView::post_construct ()
345 /* map current state of the route */
347 update_diskstream_display ();
348 setup_processor_menu_and_curves ();
349 reset_processor_automation_curves ();
352 /** Set up the processor menu for the current set of processors, and
353 * display automation curves for any parameters which have data.
356 RouteTimeAxisView::setup_processor_menu_and_curves ()
358 _subplugin_menu_map.clear ();
359 subplugin_menu.items().clear ();
360 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
361 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
365 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
367 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
368 if (_route->route_group()) {
369 _route->route_group()->remove (_route);
375 r.push_back (route ());
377 route_group_menu->build (r);
378 route_group_menu->menu()->popup (ev->button, ev->time);
384 RouteTimeAxisView::playlist_changed ()
390 RouteTimeAxisView::label_view ()
392 string x = _route->name ();
393 if (x != name_label.get_text ()) {
394 name_label.set_text (x);
396 const int64_t track_number = _route->track_number ();
397 if (track_number == 0) {
398 number_label.set_text ("");
400 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
405 RouteTimeAxisView::update_track_number_visibility ()
408 bool show_label = _session->config.get_track_name_number();
410 if (_route && _route->is_master()) {
414 if (number_label.get_parent()) {
415 controls_table.remove (number_label);
418 if (ARDOUR::Profile->get_mixbus()) {
419 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
421 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
424 // except the width of the number label is subtracted from the name-hbox, so we
425 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
426 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
428 number_label.set_size_request(tnw, -1);
429 number_label.show ();
430 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
432 number_label.hide ();
433 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
438 RouteTimeAxisView::parameter_changed (string const & p)
440 if (p == "track-name-number") {
441 update_track_number_visibility();
446 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
448 if (what_changed.contains (ARDOUR::Properties::name)) {
454 RouteTimeAxisView::take_name_changed (void *src)
462 RouteTimeAxisView::playlist_click ()
464 build_playlist_menu ();
465 conditionally_add_to_selection ();
466 playlist_action_menu->popup (1, gtk_get_current_event_time());
470 RouteTimeAxisView::automation_click ()
472 conditionally_add_to_selection ();
473 build_automation_action_menu (false);
474 automation_action_menu->popup (1, gtk_get_current_event_time());
478 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
480 using namespace Menu_Helpers;
482 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
483 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
486 detach_menu (subplugin_menu);
488 _main_automation_menu_map.clear ();
489 delete automation_action_menu;
490 automation_action_menu = new Menu;
492 MenuList& items = automation_action_menu->items();
494 automation_action_menu->set_name ("ArdourContextMenu");
496 items.push_back (MenuElem (_("Show All Automation"),
497 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
499 items.push_back (MenuElem (_("Show Existing Automation"),
500 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
502 items.push_back (MenuElem (_("Hide All Automation"),
503 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
505 /* Attach the plugin submenu. It may have previously been used elsewhere,
506 so it was detached above
509 if (!subplugin_menu.items().empty()) {
510 items.push_back (SeparatorElem ());
511 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
512 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
517 RouteTimeAxisView::build_display_menu ()
519 using namespace Menu_Helpers;
523 TimeAxisView::build_display_menu ();
525 /* now fill it with our stuff */
527 MenuList& items = display_menu->items();
528 display_menu->set_name ("ArdourContextMenu");
530 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
532 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
534 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
536 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
538 items.push_back (SeparatorElem());
541 detach_menu (*_size_menu);
544 items.push_back (MenuElem (_("Height"), *_size_menu));
546 items.push_back (SeparatorElem());
548 if (!Profile->get_sae()) {
549 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
550 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
551 items.push_back (SeparatorElem());
554 // Hook for derived classes to add type specific stuff
555 append_extra_display_menu_items ();
559 Menu* layers_menu = manage (new Menu);
560 MenuList &layers_items = layers_menu->items();
561 layers_menu->set_name("ArdourContextMenu");
563 RadioMenuItem::Group layers_group;
565 /* Find out how many overlaid/stacked tracks we have in the selection */
569 TrackSelection const & s = _editor.get_selection().tracks;
570 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
571 StreamView* v = (*i)->view ();
576 switch (v->layer_display ()) {
587 /* We're not connecting to signal_toggled() here; in the case where these two items are
588 set to be in the `inconsistent' state, it seems that one or other will end up active
589 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
590 select the active one, no toggled signal is emitted so nothing happens.
593 _ignore_set_layer_display = true;
595 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
596 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
597 i->set_active (overlaid != 0 && stacked == 0);
598 i->set_inconsistent (overlaid != 0 && stacked != 0);
599 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
601 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
602 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
603 i->set_active (overlaid == 0 && stacked != 0);
604 i->set_inconsistent (overlaid != 0 && stacked != 0);
605 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
607 _ignore_set_layer_display = false;
609 items.push_back (MenuElem (_("Layers"), *layers_menu));
611 if (!Profile->get_sae()) {
613 Menu* alignment_menu = manage (new Menu);
614 MenuList& alignment_items = alignment_menu->items();
615 alignment_menu->set_name ("ArdourContextMenu");
617 RadioMenuItem::Group align_group;
619 /* Same verbose hacks as for the layering options above */
625 boost::shared_ptr<Track> first_track;
627 TrackSelection const & s = _editor.get_selection().tracks;
628 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
629 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
630 if (!r || !r->is_track ()) {
635 first_track = r->track();
638 switch (r->track()->alignment_choice()) {
642 switch (r->track()->alignment_style()) {
643 case ExistingMaterial:
651 case UseExistingMaterial:
667 inconsistent = false;
676 if (!inconsistent && first_track) {
678 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
679 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
680 i->set_active (automatic != 0 && existing == 0 && capture == 0);
681 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
683 switch (first_track->alignment_choice()) {
685 switch (first_track->alignment_style()) {
686 case ExistingMaterial:
687 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
690 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
698 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
699 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
700 i->set_active (existing != 0 && capture == 0 && automatic == 0);
701 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
703 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
704 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
705 i->set_active (existing == 0 && capture != 0 && automatic == 0);
706 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
708 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
714 Menu* mode_menu = manage (new Menu);
715 MenuList& mode_items = mode_menu->items ();
716 mode_menu->set_name ("ArdourContextMenu");
718 RadioMenuItem::Group mode_group;
724 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
725 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
726 if (!r || !r->is_track ()) {
730 switch (r->track()->mode()) {
743 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
744 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
745 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
746 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
747 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
749 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
750 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
751 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
752 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
753 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
755 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
756 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
757 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
758 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
759 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
761 items.push_back (MenuElem (_("Mode"), *mode_menu));
765 items.push_back (SeparatorElem());
767 build_playlist_menu ();
768 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
769 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
772 route_group_menu->detach ();
775 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
776 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
778 r.push_back (rtv->route ());
783 r.push_back (route ());
786 route_group_menu->build (r);
787 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
789 build_automation_action_menu (true);
790 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
792 items.push_back (SeparatorElem());
796 TrackSelection const & s = _editor.get_selection().tracks;
797 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
798 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
803 if (r->route()->active()) {
810 items.push_back (CheckMenuElem (_("Active")));
811 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
812 bool click_sets_active = true;
813 if (active > 0 && inactive == 0) {
814 i->set_active (true);
815 click_sets_active = false;
816 } else if (active > 0 && inactive > 0) {
817 i->set_inconsistent (true);
819 i->set_sensitive(! _session->transport_rolling());
820 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
822 items.push_back (SeparatorElem());
823 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
824 if (!Profile->get_sae()) {
825 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
827 items.push_front (SeparatorElem());
828 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
833 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
835 if (apply_to_selection) {
836 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
839 bool needs_bounce = false;
841 if (!track()->can_use_mode (mode, needs_bounce)) {
847 cerr << "would bounce this one\n";
852 track()->set_mode (mode);
857 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
859 TimeAxisView::show_timestretch (start, end, layers, layer);
869 /* check that the time selection was made in our route, or our route group.
870 remember that route_group() == 0 implies the route is *not* in a edit group.
873 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
874 /* this doesn't apply to us */
878 /* ignore it if our edit group is not active */
880 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
885 if (timestretch_rect == 0) {
886 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
887 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
888 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
891 timestretch_rect->show ();
892 timestretch_rect->raise_to_top ();
894 double const x1 = start / _editor.get_current_zoom();
895 double const x2 = (end - 1) / _editor.get_current_zoom();
897 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
898 x2, current_height() * (layers - layer) / layers));
902 RouteTimeAxisView::hide_timestretch ()
904 TimeAxisView::hide_timestretch ();
906 if (timestretch_rect) {
907 timestretch_rect->hide ();
912 RouteTimeAxisView::show_selection (TimeSelection& ts)
916 /* ignore it if our edit group is not active or if the selection was started
917 in some other track or route group (remember that route_group() == 0 means
918 that the track is not in an route group).
921 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
922 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
928 TimeAxisView::show_selection (ts);
932 RouteTimeAxisView::set_height (uint32_t h)
935 bool height_changed = (height == 0) || (h != height);
938 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
941 gm.get_level_meter().setup_meters (gmlen, meter_width);
943 TimeAxisView::set_height (h);
946 _view->set_height ((double) current_height());
949 if (height >= preset_height (HeightNormal)) {
953 gm.get_gain_slider().show();
955 if (!_route || _route->is_monitor()) {
960 if (rec_enable_button)
961 rec_enable_button->show();
963 route_group_button.show();
964 automation_button.show();
966 if (is_track() && track()->mode() == ARDOUR::Normal) {
967 playlist_button.show();
974 gm.get_gain_slider().hide();
976 if (!_route || _route->is_monitor()) {
981 if (rec_enable_button)
982 rec_enable_button->show();
984 route_group_button.hide ();
985 automation_button.hide ();
987 if (is_track() && track()->mode() == ARDOUR::Normal) {
988 playlist_button.hide ();
993 if (height_changed && !no_redraw) {
994 /* only emit the signal if the height really changed */
1000 RouteTimeAxisView::route_color_changed ()
1003 _view->apply_color (color(), StreamView::RegionColor);
1006 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1010 RouteTimeAxisView::reset_samples_per_pixel ()
1012 set_samples_per_pixel (_editor.get_current_zoom());
1016 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1021 speed = track()->speed();
1025 _view->set_samples_per_pixel (fpp * speed);
1028 TimeAxisView::set_samples_per_pixel (fpp * speed);
1032 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1034 if (!mitem->get_active()) {
1035 /* this is one of the two calls made when these radio menu items change status. this one
1036 is for the item that became inactive, and we want to ignore it.
1041 if (apply_to_selection) {
1042 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1045 track()->set_align_choice (choice);
1051 RouteTimeAxisView::rename_current_playlist ()
1053 ArdourPrompter prompter (true);
1056 boost::shared_ptr<Track> tr = track();
1057 if (!tr || tr->destructive()) {
1061 boost::shared_ptr<Playlist> pl = tr->playlist();
1066 prompter.set_title (_("Rename Playlist"));
1067 prompter.set_prompt (_("New name for playlist:"));
1068 prompter.set_initial_text (pl->name());
1069 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1070 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1072 switch (prompter.run ()) {
1073 case Gtk::RESPONSE_ACCEPT:
1074 prompter.get_result (name);
1075 if (name.length()) {
1076 pl->set_name (name);
1086 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1088 std::string ret (basename);
1090 std::string const group_string = "." + route_group()->name() + ".";
1092 // iterate through all playlists
1094 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1095 std::string tmp = (*i)->name();
1097 std::string::size_type idx = tmp.find(group_string);
1098 // find those which belong to this group
1099 if (idx != string::npos) {
1100 tmp = tmp.substr(idx + group_string.length());
1102 // and find the largest current number
1104 if (x > maxnumber) {
1113 snprintf (buf, sizeof(buf), "%d", maxnumber);
1115 ret = this->name() + "." + route_group()->name () + "." + buf;
1121 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1125 boost::shared_ptr<Track> tr = track ();
1126 if (!tr || tr->destructive()) {
1130 boost::shared_ptr<const Playlist> pl = tr->playlist();
1137 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1138 name = resolve_new_group_playlist_name(name, playlists_before_op);
1141 while (_session->playlists->by_name(name)) {
1142 name = Playlist::bump_name (name, *_session);
1145 // TODO: The prompter "new" button should be de-activated if the user
1146 // specifies a playlist name which already exists in the session.
1150 ArdourPrompter prompter (true);
1152 prompter.set_title (_("New Copy Playlist"));
1153 prompter.set_prompt (_("Name for new playlist:"));
1154 prompter.set_initial_text (name);
1155 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1156 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1157 prompter.show_all ();
1159 switch (prompter.run ()) {
1160 case Gtk::RESPONSE_ACCEPT:
1161 prompter.get_result (name);
1169 if (name.length()) {
1170 tr->use_copy_playlist ();
1171 tr->playlist()->set_name (name);
1176 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1180 boost::shared_ptr<Track> tr = track ();
1181 if (!tr || tr->destructive()) {
1185 boost::shared_ptr<const Playlist> pl = tr->playlist();
1192 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1193 name = resolve_new_group_playlist_name(name,playlists_before_op);
1196 while (_session->playlists->by_name(name)) {
1197 name = Playlist::bump_name (name, *_session);
1203 ArdourPrompter prompter (true);
1205 prompter.set_title (_("New Playlist"));
1206 prompter.set_prompt (_("Name for new playlist:"));
1207 prompter.set_initial_text (name);
1208 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1209 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1211 switch (prompter.run ()) {
1212 case Gtk::RESPONSE_ACCEPT:
1213 prompter.get_result (name);
1221 if (name.length()) {
1222 tr->use_new_playlist ();
1223 tr->playlist()->set_name (name);
1228 RouteTimeAxisView::clear_playlist ()
1230 boost::shared_ptr<Track> tr = track ();
1231 if (!tr || tr->destructive()) {
1235 boost::shared_ptr<Playlist> pl = tr->playlist();
1240 _editor.clear_playlist (pl);
1244 RouteTimeAxisView::speed_changed ()
1246 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1250 RouteTimeAxisView::update_diskstream_display ()
1260 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1262 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1264 /* special case: select/deselect all tracks */
1265 if (_editor.get_selection().selected (this)) {
1266 _editor.get_selection().clear_tracks ();
1268 _editor.select_all_tracks ();
1274 switch (ArdourKeyboard::selection_type (ev->state)) {
1275 case Selection::Toggle:
1276 _editor.get_selection().toggle (this);
1279 case Selection::Set:
1280 _editor.get_selection().set (this);
1283 case Selection::Extend:
1284 _editor.extend_selection_to_track (*this);
1287 case Selection::Add:
1288 _editor.get_selection().add (this);
1294 RouteTimeAxisView::set_selected_points (PointSelection& points)
1296 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1297 (*i)->set_selected_points (points);
1302 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1305 _view->set_selected_regionviews (regions);
1309 /** Add the selectable things that we have to a list.
1310 * @param results List to add things to.
1313 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1318 speed = track()->speed();
1321 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1322 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1324 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1325 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1328 /* pick up visible automation tracks */
1330 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1331 if (!(*i)->hidden()) {
1332 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1338 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1341 _view->get_inverted_selectables (sel, results);
1344 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1345 if (!(*i)->hidden()) {
1346 (*i)->get_inverted_selectables (sel, results);
1354 RouteTimeAxisView::route_group () const
1356 return _route->route_group();
1360 RouteTimeAxisView::name() const
1362 return _route->name();
1365 boost::shared_ptr<Playlist>
1366 RouteTimeAxisView::playlist () const
1368 boost::shared_ptr<Track> tr;
1370 if ((tr = track()) != 0) {
1371 return tr->playlist();
1373 return boost::shared_ptr<Playlist> ();
1378 RouteTimeAxisView::name_entry_changed ()
1380 TimeAxisView::name_entry_changed ();
1382 string x = name_entry->get_text ();
1384 if (x == _route->name()) {
1388 strip_whitespace_edges (x);
1390 if (x.length() == 0) {
1391 name_entry->set_text (_route->name());
1395 if (_session->route_name_internal (x)) {
1396 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1398 name_entry->grab_focus ();
1399 } else if (RouteUI::verify_new_route_name (x)) {
1400 _route->set_name (x);
1402 name_entry->grab_focus ();
1406 boost::shared_ptr<Region>
1407 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1409 boost::shared_ptr<Playlist> pl = playlist ();
1412 return pl->find_next_region (pos, point, dir);
1415 return boost::shared_ptr<Region> ();
1419 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1421 boost::shared_ptr<Playlist> pl = playlist ();
1424 return pl->find_next_region_boundary (pos, dir);
1431 RouteTimeAxisView::fade_range (TimeSelection& selection)
1433 boost::shared_ptr<Playlist> what_we_got;
1434 boost::shared_ptr<Track> tr = track ();
1435 boost::shared_ptr<Playlist> playlist;
1438 /* route is a bus, not a track */
1442 playlist = tr->playlist();
1444 TimeSelection time (selection);
1445 float const speed = tr->speed();
1446 if (speed != 1.0f) {
1447 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1448 (*i).start = session_frame_to_track_frame((*i).start, speed);
1449 (*i).end = session_frame_to_track_frame((*i).end, speed);
1453 playlist->clear_changes ();
1454 playlist->clear_owned_changes ();
1456 playlist->fade_range (time);
1458 vector<Command*> cmds;
1459 playlist->rdiff (cmds);
1460 _session->add_commands (cmds);
1461 _session->add_command (new StatefulDiffCommand (playlist));
1466 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1468 boost::shared_ptr<Playlist> what_we_got;
1469 boost::shared_ptr<Track> tr = track ();
1470 boost::shared_ptr<Playlist> playlist;
1473 /* route is a bus, not a track */
1477 playlist = tr->playlist();
1479 TimeSelection time (selection.time);
1480 float const speed = tr->speed();
1481 if (speed != 1.0f) {
1482 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1483 (*i).start = session_frame_to_track_frame((*i).start, speed);
1484 (*i).end = session_frame_to_track_frame((*i).end, speed);
1488 playlist->clear_changes ();
1489 playlist->clear_owned_changes ();
1493 if (playlist->cut (time) != 0) {
1494 if (Config->get_edit_mode() == Ripple)
1495 playlist->ripple(time.start(), -time.length(), NULL);
1496 // no need to exclude any regions from rippling here
1498 vector<Command*> cmds;
1499 playlist->rdiff (cmds);
1500 _session->add_commands (cmds);
1502 _session->add_command (new StatefulDiffCommand (playlist));
1507 if ((what_we_got = playlist->cut (time)) != 0) {
1508 _editor.get_cut_buffer().add (what_we_got);
1509 if (Config->get_edit_mode() == Ripple)
1510 playlist->ripple(time.start(), -time.length(), NULL);
1511 // no need to exclude any regions from rippling here
1513 vector<Command*> cmds;
1514 playlist->rdiff (cmds);
1515 _session->add_commands (cmds);
1517 _session->add_command (new StatefulDiffCommand (playlist));
1521 if ((what_we_got = playlist->copy (time)) != 0) {
1522 _editor.get_cut_buffer().add (what_we_got);
1527 if ((what_we_got = playlist->cut (time)) != 0) {
1528 if (Config->get_edit_mode() == Ripple)
1529 playlist->ripple(time.start(), -time.length(), NULL);
1530 // no need to exclude any regions from rippling here
1532 vector<Command*> cmds;
1533 playlist->rdiff (cmds);
1534 _session->add_commands (cmds);
1535 _session->add_command (new StatefulDiffCommand (playlist));
1536 what_we_got->release ();
1543 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1549 boost::shared_ptr<Playlist> pl = playlist ();
1550 PlaylistSelection::iterator p;
1552 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1554 if (p == selection.playlists.end()) {
1558 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1560 if (track()->speed() != 1.0f) {
1561 pos = session_frame_to_track_frame (pos, track()->speed());
1562 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1565 pl->clear_changes ();
1566 if (Config->get_edit_mode() == Ripple) {
1567 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1568 framecnt_t amount = extent.second - extent.first;
1569 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1571 pl->paste (*p, pos, times);
1573 vector<Command*> cmds;
1575 _session->add_commands (cmds);
1577 _session->add_command (new StatefulDiffCommand (pl));
1583 struct PlaylistSorter {
1584 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1585 return a->sort_id() < b->sort_id();
1590 RouteTimeAxisView::build_playlist_menu ()
1592 using namespace Menu_Helpers;
1598 delete playlist_action_menu;
1599 playlist_action_menu = new Menu;
1600 playlist_action_menu->set_name ("ArdourContextMenu");
1602 MenuList& playlist_items = playlist_action_menu->items();
1603 playlist_action_menu->set_name ("ArdourContextMenu");
1604 playlist_items.clear();
1606 RadioMenuItem::Group playlist_group;
1607 boost::shared_ptr<Track> tr = track ();
1609 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1611 /* sort the playlists */
1613 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1615 /* add the playlists to the menu */
1616 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1617 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1618 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1619 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1621 if (tr->playlist()->id() == (*i)->id()) {
1627 playlist_items.push_back (SeparatorElem());
1628 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1629 playlist_items.push_back (SeparatorElem());
1631 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1632 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1633 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1636 // Use a label which tells the user what is happening
1637 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1638 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1642 playlist_items.push_back (SeparatorElem());
1643 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1644 playlist_items.push_back (SeparatorElem());
1646 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1650 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1652 assert (is_track());
1654 // exit if we were triggered by deactivating the old playlist
1655 if (!item->get_active()) {
1659 boost::shared_ptr<Playlist> pl (wpl.lock());
1665 if (track()->playlist() == pl) {
1666 // exit when use_playlist is called by the creation of the playlist menu
1667 // or the playlist choice is unchanged
1671 track()->use_playlist (pl);
1673 RouteGroup* rg = route_group();
1675 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1676 std::string group_string = "." + rg->name() + ".";
1678 std::string take_name = pl->name();
1679 std::string::size_type idx = take_name.find(group_string);
1681 if (idx == std::string::npos)
1684 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1686 boost::shared_ptr<RouteList> rl (rg->route_list());
1688 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1689 if ((*i) == this->route()) {
1693 std::string playlist_name = (*i)->name()+group_string+take_name;
1695 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1700 if (track->freeze_state() == Track::Frozen) {
1701 /* Don't change playlists of frozen tracks */
1705 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1707 // No playlist for this track for this take yet, make it
1708 track->use_new_playlist();
1709 track->playlist()->set_name(playlist_name);
1711 track->use_playlist(ipl);
1718 RouteTimeAxisView::update_playlist_tip ()
1720 RouteGroup* rg = route_group ();
1721 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1722 string group_string = "." + rg->name() + ".";
1724 string take_name = track()->playlist()->name();
1725 string::size_type idx = take_name.find(group_string);
1727 if (idx != string::npos) {
1728 /* find the bit containing the take number / name */
1729 take_name = take_name.substr (idx + group_string.length());
1731 /* set the playlist button tooltip to the take name */
1732 ARDOUR_UI::instance()->set_tip (
1734 string_compose(_("Take: %1.%2"),
1735 Glib::Markup::escape_text(rg->name()),
1736 Glib::Markup::escape_text(take_name))
1743 /* set the playlist button tooltip to the playlist name */
1744 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1749 RouteTimeAxisView::show_playlist_selector ()
1751 _editor.playlist_selector().show_for (this);
1755 RouteTimeAxisView::map_frozen ()
1761 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1763 switch (track()->freeze_state()) {
1765 playlist_button.set_sensitive (false);
1766 rec_enable_button->set_sensitive (false);
1769 playlist_button.set_sensitive (true);
1770 rec_enable_button->set_sensitive (true);
1776 RouteTimeAxisView::color_handler ()
1778 //case cTimeStretchOutline:
1779 if (timestretch_rect) {
1780 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1782 //case cTimeStretchFill:
1783 if (timestretch_rect) {
1784 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1790 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1791 * Will add track if necessary.
1794 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1796 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1797 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1800 /* it doesn't exist yet, so we don't care about the button state: just add it */
1801 create_automation_child (param, true);
1804 bool yn = menu->get_active();
1805 bool changed = false;
1807 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1809 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1810 will have done that for us.
1813 if (changed && !no_redraw) {
1821 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1823 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1829 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1831 if (menu && !_hidden) {
1832 ignore_toggle = true;
1833 menu->set_active (false);
1834 ignore_toggle = false;
1837 if (_route && !no_redraw) {
1844 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1846 if (apply_to_selection) {
1847 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1851 /* Show our automation */
1853 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1854 i->second->set_marked_for_display (true);
1856 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1859 menu->set_active(true);
1864 /* Show processor automation */
1866 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1867 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1868 if ((*ii)->view == 0) {
1869 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1872 (*ii)->menu_item->set_active (true);
1885 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1887 if (apply_to_selection) {
1888 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1892 /* Show our automation */
1894 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1895 if (i->second->has_automation()) {
1896 i->second->set_marked_for_display (true);
1898 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1900 menu->set_active(true);
1905 /* Show processor automation */
1907 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1908 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1909 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1910 (*ii)->menu_item->set_active (true);
1922 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1924 if (apply_to_selection) {
1925 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1929 /* Hide our automation */
1931 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1932 i->second->set_marked_for_display (false);
1934 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1937 menu->set_active (false);
1941 /* Hide processor automation */
1943 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1944 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1945 (*ii)->menu_item->set_active (false);
1956 RouteTimeAxisView::region_view_added (RegionView* rv)
1958 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1959 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1960 boost::shared_ptr<AutomationTimeAxisView> atv;
1962 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1967 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1968 (*i)->add_ghost(rv);
1972 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1974 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1980 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1982 parent.remove_processor_automation_node (this);
1986 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1989 remove_child (pan->view);
1993 RouteTimeAxisView::ProcessorAutomationNode*
1994 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1996 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1998 if ((*i)->processor == processor) {
2000 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2001 if ((*ii)->what == what) {
2011 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2013 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2016 ProcessorAutomationNode* pan;
2018 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2019 /* session state may never have been saved with new plugin */
2020 error << _("programming error: ")
2021 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2022 processor->name(), what.type(), (int) what.channel(), what.id() )
2032 boost::shared_ptr<AutomationControl> control
2033 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2035 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2036 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2037 _editor, *this, false, parent_canvas,
2038 processor->describe_parameter (what), processor->name()));
2040 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2042 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2045 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2050 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2053 pan->menu_item->set_active (false);
2062 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2064 boost::shared_ptr<Processor> processor (p.lock ());
2066 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2067 /* The Amp processor is a special case and is dealt with separately */
2071 set<Evoral::Parameter> existing;
2073 processor->what_has_data (existing);
2075 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2077 Evoral::Parameter param (*i);
2078 boost::shared_ptr<AutomationLine> al;
2080 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2083 add_processor_automation_curve (processor, param);
2089 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2091 using namespace Menu_Helpers;
2095 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2097 _automation_tracks[param] = track;
2099 /* existing state overrides "show" argument */
2100 string s = track->gui_property ("visible");
2102 show = string_is_affirmative (s);
2105 /* this might or might not change the visibility status, so don't rely on it */
2106 track->set_marked_for_display (show);
2108 if (show && !no_redraw) {
2112 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2113 /* MIDI-related parameters are always in the menu, there's no
2114 reason to rebuild the menu just because we added a automation
2115 lane for one of them. But if we add a non-MIDI automation
2116 lane, then we need to invalidate the display menu.
2118 delete display_menu;
2124 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2126 boost::shared_ptr<Processor> processor (p.lock ());
2128 if (!processor || !processor->display_to_user ()) {
2132 /* we use this override to veto the Amp processor from the plugin menu,
2133 as its automation lane can be accessed using the special "Fader" menu
2137 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2141 using namespace Menu_Helpers;
2142 ProcessorAutomationInfo *rai;
2143 list<ProcessorAutomationInfo*>::iterator x;
2145 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2147 if (automatable.empty()) {
2151 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2152 if ((*x)->processor == processor) {
2157 if (x == processor_automation.end()) {
2159 rai = new ProcessorAutomationInfo (processor);
2160 processor_automation.push_back (rai);
2168 /* any older menu was deleted at the top of processors_changed()
2169 when we cleared the subplugin menu.
2172 rai->menu = manage (new Menu);
2173 MenuList& items = rai->menu->items();
2174 rai->menu->set_name ("ArdourContextMenu");
2178 std::set<Evoral::Parameter> has_visible_automation;
2179 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2181 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2183 ProcessorAutomationNode* pan;
2184 Gtk::CheckMenuItem* mitem;
2186 string name = processor->describe_parameter (*i);
2188 items.push_back (CheckMenuElem (name));
2189 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2191 _subplugin_menu_map[*i] = mitem;
2193 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2194 mitem->set_active(true);
2197 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2201 pan = new ProcessorAutomationNode (*i, mitem, *this);
2203 rai->lines.push_back (pan);
2207 pan->menu_item = mitem;
2211 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2214 /* add the menu for this processor, because the subplugin
2215 menu is always cleared at the top of processors_changed().
2216 this is the result of some poor design in gtkmm and/or
2220 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2225 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2226 RouteTimeAxisView::ProcessorAutomationNode* pan)
2228 bool showit = pan->menu_item->get_active();
2229 bool redraw = false;
2231 if (pan->view == 0 && showit) {
2232 add_processor_automation_curve (rai->processor, pan->what);
2236 if (pan->view && pan->view->set_marked_for_display (showit)) {
2240 if (redraw && !no_redraw) {
2246 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2248 if (c.type == RouteProcessorChange::MeterPointChange) {
2249 /* nothing to do if only the meter point has changed */
2253 using namespace Menu_Helpers;
2255 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2256 (*i)->valid = false;
2259 setup_processor_menu_and_curves ();
2261 bool deleted_processor_automation = false;
2263 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2265 list<ProcessorAutomationInfo*>::iterator tmp;
2273 processor_automation.erase (i);
2274 deleted_processor_automation = true;
2281 if (deleted_processor_automation && !no_redraw) {
2286 boost::shared_ptr<AutomationLine>
2287 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2289 ProcessorAutomationNode* pan;
2291 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2297 return boost::shared_ptr<AutomationLine>();
2301 RouteTimeAxisView::reset_processor_automation_curves ()
2303 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2309 RouteTimeAxisView::can_edit_name () const
2311 /* we do not allow track name changes if it is record enabled
2313 return !_route->record_enabled();
2317 RouteTimeAxisView::blink_rec_display (bool onoff)
2319 RouteUI::blink_rec_display (onoff);
2323 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2325 if (_ignore_set_layer_display) {
2329 if (apply_to_selection) {
2330 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2334 _view->set_layer_display (d);
2337 set_gui_property (X_("layer-display"), enum_2_string (d));
2342 RouteTimeAxisView::layer_display () const
2345 return _view->layer_display ();
2348 /* we don't know, since we don't have a _view, so just return something */
2354 boost::shared_ptr<AutomationTimeAxisView>
2355 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2357 AutomationTracks::iterator i = _automation_tracks.find(param);
2358 if (i != _automation_tracks.end()) {
2361 return boost::shared_ptr<AutomationTimeAxisView>();
2366 RouteTimeAxisView::fast_update ()
2368 gm.get_level_meter().update_meters ();
2372 RouteTimeAxisView::hide_meter ()
2375 gm.get_level_meter().hide_meters ();
2379 RouteTimeAxisView::show_meter ()
2385 RouteTimeAxisView::reset_meter ()
2387 if (Config->get_show_track_meters()) {
2388 int meter_width = 3;
2389 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2392 gm.get_level_meter().setup_meters (height - 9, meter_width);
2399 RouteTimeAxisView::clear_meter ()
2401 gm.get_level_meter().clear_meters ();
2405 RouteTimeAxisView::meter_changed ()
2407 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2409 if (_route && !no_redraw) {
2412 // reset peak when meter point changes
2413 gm.reset_peak_display();
2417 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2420 if (_route && !no_redraw) {
2426 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2428 using namespace Menu_Helpers;
2430 if (!_underlay_streams.empty()) {
2431 MenuList& parent_items = parent_menu->items();
2432 Menu* gs_menu = manage (new Menu);
2433 gs_menu->set_name ("ArdourContextMenu");
2434 MenuList& gs_items = gs_menu->items();
2436 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2438 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2439 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2440 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2446 RouteTimeAxisView::set_underlay_state()
2448 if (!underlay_xml_node) {
2452 XMLNodeList nlist = underlay_xml_node->children();
2453 XMLNodeConstIterator niter;
2454 XMLNode *child_node;
2456 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2457 child_node = *niter;
2459 if (child_node->name() != "Underlay") {
2463 XMLProperty* prop = child_node->property ("id");
2465 PBD::ID id (prop->value());
2467 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2470 add_underlay(v->view(), false);
2479 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2485 RouteTimeAxisView& other = v->trackview();
2487 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2488 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2489 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2493 _underlay_streams.push_back(v);
2494 other._underlay_mirrors.push_back(this);
2496 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2498 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2500 if (!underlay_xml_node) {
2501 underlay_xml_node = xml_node->add_child("Underlays");
2504 XMLNode* node = underlay_xml_node->add_child("Underlay");
2505 XMLProperty* prop = node->add_property("id");
2506 prop->set_value(v->trackview().route()->id().to_s());
2513 RouteTimeAxisView::remove_underlay (StreamView* v)
2519 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2520 RouteTimeAxisView& other = v->trackview();
2522 if (it != _underlay_streams.end()) {
2523 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2525 if (gm == other._underlay_mirrors.end()) {
2526 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2530 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2532 _underlay_streams.erase(it);
2533 other._underlay_mirrors.erase(gm);
2535 if (underlay_xml_node) {
2536 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2542 RouteTimeAxisView::set_button_names ()
2544 if (_route && _route->solo_safe()) {
2545 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2547 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2549 if (Config->get_solo_control_is_listen_control()) {
2550 switch (Config->get_listen_position()) {
2551 case AfterFaderListen:
2552 solo_button->set_text (_("A"));
2553 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2555 case PreFaderListen:
2556 solo_button->set_text (_("P"));
2557 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2561 solo_button->set_text (_("S"));
2562 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2564 mute_button->set_text (_("M"));
2568 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2570 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2571 if (i != _main_automation_menu_map.end()) {
2575 i = _subplugin_menu_map.find (param);
2576 if (i != _subplugin_menu_map.end()) {
2584 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2586 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2588 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2592 gain_track.reset (new AutomationTimeAxisView (_session,
2593 _route, _route->amp(), c, param,
2598 _route->amp()->describe_parameter(param)));
2601 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2604 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2608 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2610 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2612 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2616 mute_track.reset (new AutomationTimeAxisView (_session,
2617 _route, _route, c, param,
2622 _route->describe_parameter(param)));
2625 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2628 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2632 void add_region_to_list (RegionView* rv, RegionList* l)
2634 l->push_back (rv->region());
2638 RouteTimeAxisView::combine_regions ()
2640 /* as of may 2011, we do not offer uncombine for MIDI tracks
2643 if (!is_audio_track()) {
2651 RegionList selected_regions;
2652 boost::shared_ptr<Playlist> playlist = track()->playlist();
2654 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2656 if (selected_regions.size() < 2) {
2660 playlist->clear_changes ();
2661 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2663 _session->add_command (new StatefulDiffCommand (playlist));
2664 /* make the new region be selected */
2666 return _view->find_view (compound_region);
2670 RouteTimeAxisView::uncombine_regions ()
2672 /* as of may 2011, we do not offer uncombine for MIDI tracks
2674 if (!is_audio_track()) {
2682 RegionList selected_regions;
2683 boost::shared_ptr<Playlist> playlist = track()->playlist();
2685 /* have to grab selected regions first because the uncombine is going
2686 * to change that in the middle of the list traverse
2689 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2691 playlist->clear_changes ();
2693 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2694 playlist->uncombine (*i);
2697 _session->add_command (new StatefulDiffCommand (playlist));
2701 RouteTimeAxisView::state_id() const
2703 return string_compose ("rtav %1", _route->id().to_s());
2708 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2710 TimeAxisView::remove_child (c);
2712 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2714 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2715 if (i->second == a) {
2716 _automation_tracks.erase (i);