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 <gtkmm/menu.h>
32 #include <gtkmm/menuitem.h>
33 #include <gtkmm/stock.h>
35 #include "pbd/error.h"
36 #include "pbd/stl_delete.h"
37 #include "pbd/whitespace.h"
38 #include "pbd/memento_command.h"
39 #include "pbd/enumwriter.h"
40 #include "pbd/stateful_diff_command.h"
42 #include "evoral/Parameter.hpp"
44 #include "ardour/amp.h"
45 #include "ardour/meter.h"
46 #include "ardour/event_type_map.h"
47 #include "ardour/pannable.h"
48 #include "ardour/panner.h"
49 #include "ardour/processor.h"
50 #include "ardour/profile.h"
51 #include "ardour/route_group.h"
52 #include "ardour/session.h"
53 #include "ardour/session_playlists.h"
54 #include "ardour/track.h"
56 #include "canvas/debug.h"
58 #include "gtkmm2ext/gtk_ui.h"
59 #include "gtkmm2ext/utils.h"
61 #include "widgets/ardour_button.h"
62 #include "widgets/prompter.h"
63 #include "widgets/tooltips.h"
65 #include "ardour_ui.h"
66 #include "audio_streamview.h"
68 #include "enums_convert.h"
69 #include "route_time_axis.h"
70 #include "automation_time_axis.h"
72 #include "gui_thread.h"
73 #include "item_counts.h"
75 #include "paste_context.h"
76 #include "playlist_selector.h"
77 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "streamview.h"
83 #include "ui_config.h"
85 #include "route_group_menu.h"
89 using namespace ARDOUR;
90 using namespace ArdourWidgets;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
98 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
100 , StripableTimeAxisView(ed, sess, canvas)
102 , button_table (3, 3)
103 , route_group_button (S_("RTAV|G"))
104 , playlist_button (S_("RTAV|P"))
105 , automation_button (S_("RTAV|A"))
106 , automation_action_menu (0)
107 , plugins_submenu_item (0)
108 , route_group_menu (0)
109 , playlist_action_menu (0)
111 , color_mode_menu (0)
112 , gm (sess, true, 75, 14)
113 , _ignore_set_layer_display (false)
114 , pan_automation_item(NULL)
116 number_label.set_name("tracknumber label");
117 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
118 number_label.set_alignment(.5, .5);
119 number_label.set_fallthrough_to_parent (true);
121 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
122 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
124 parameter_changed ("editor-stereo-only-meters");
128 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
130 if (what_changed.contains (ARDOUR::Properties::name)) {
136 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
138 RouteUI::set_route (rt);
139 StripableTimeAxisView::set_stripable (rt);
141 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
142 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
143 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
146 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
149 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
150 gm.get_level_meter().set_no_show_all();
151 gm.get_level_meter().setup_meters(50, meter_width);
152 gm.update_gain_sensitive ();
155 if (get_gui_property ("height", height)) {
158 set_height (preset_height (HeightNormal));
161 if (!_route->is_auditioner()) {
162 if (gui_property ("visible").empty()) {
163 set_gui_property ("visible", true);
166 set_gui_property ("visible", false);
169 timestretch_rect = 0;
172 ignore_toggle = false;
174 route_group_button.set_name ("route button");
175 playlist_button.set_name ("route button");
176 automation_button.set_name ("route button");
178 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
179 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
180 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
184 if (ARDOUR::Profile->get_mixbus()) {
185 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
187 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
190 if (is_midi_track()) {
191 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
192 gm.set_fader_name ("MidiTrackFader");
194 set_tooltip(*rec_enable_button, _("Record"));
195 gm.set_fader_name ("AudioTrackFader");
198 /* set playlist button tip to the current playlist, and make it update when it changes */
199 update_playlist_tip ();
200 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
203 gm.set_fader_name ("AudioBusFader");
204 Gtk::Fixed *blank = manage(new Gtk::Fixed());
205 controls_button_size_group->add_widget(*blank);
206 if (ARDOUR::Profile->get_mixbus() ) {
207 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
214 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
216 if (!ARDOUR::Profile->get_mixbus()) {
217 controls_meters_size_group->add_widget (gm.get_level_meter());
220 if (_route->is_master()) {
221 route_group_button.set_sensitive(false);
224 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
225 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
227 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
229 if (ARDOUR::Profile->get_mixbus()) {
230 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
232 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 // mute button is always present, it is used to
235 // force the 'blank' placeholders to the proper size
236 controls_button_size_group->add_widget(*mute_button);
238 if (!_route->is_master()) {
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
245 Gtk::Fixed *blank = manage(new Gtk::Fixed());
246 controls_button_size_group->add_widget(*blank);
247 if (ARDOUR::Profile->get_mixbus()) {
248 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
250 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
255 if (ARDOUR::Profile->get_mixbus()) {
256 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
257 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
259 else if (!ARDOUR::Profile->get_trx()) {
260 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
264 set_tooltip(*solo_button,_("Solo"));
265 set_tooltip(*mute_button,_("Mute"));
266 set_tooltip(route_group_button, _("Route Group"));
268 mute_button->set_tweaks(ArdourButton::TrackHeader);
269 solo_button->set_tweaks(ArdourButton::TrackHeader);
270 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
271 playlist_button.set_tweaks(ArdourButton::TrackHeader);
272 automation_button.set_tweaks(ArdourButton::TrackHeader);
273 route_group_button.set_tweaks(ArdourButton::TrackHeader);
275 if (is_midi_track()) {
276 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
278 set_tooltip(automation_button, _("Automation"));
281 update_track_number_visibility();
284 if (ARDOUR::Profile->get_mixbus()) {
285 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
287 else if (!ARDOUR::Profile->get_trx()) {
288 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
291 if (is_track() && track()->mode() == ARDOUR::Normal) {
292 if (ARDOUR::Profile->get_mixbus()) {
293 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
295 else if (!ARDOUR::Profile->get_trx()) {
296 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
302 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
306 LayerDisplay layer_display;
307 if (get_gui_property ("layer-display", layer_display)) {
308 set_layer_display (layer_display);
311 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
312 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
314 /* pick up the correct freeze state */
319 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
321 PropertyList* plist = new PropertyList();
323 plist->add (ARDOUR::Properties::group_mute, true);
324 plist->add (ARDOUR::Properties::group_solo, true);
326 route_group_menu = new RouteGroupMenu (_session, plist);
328 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
331 RouteTimeAxisView::~RouteTimeAxisView ()
333 cleanup_gui_properties ();
335 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
339 delete playlist_action_menu;
340 playlist_action_menu = 0;
345 _automation_tracks.clear ();
347 delete route_group_menu;
348 CatchDeletion (this);
352 RouteTimeAxisView::name() const
355 return _route->name();
361 RouteTimeAxisView::post_construct ()
363 /* map current state of the route */
365 update_diskstream_display ();
366 setup_processor_menu_and_curves ();
367 reset_processor_automation_curves ();
370 /** Set up the processor menu for the current set of processors, and
371 * display automation curves for any parameters which have data.
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
376 _subplugin_menu_map.clear ();
377 subplugin_menu.items().clear ();
378 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
383 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
385 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
386 if (_route->route_group()) {
387 _route->route_group()->remove (_route);
393 r.push_back (route ());
395 route_group_menu->build (r);
396 if (ev->button == 1) {
397 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
401 route_group_menu->menu()->popup (ev->button, ev->time);
408 RouteTimeAxisView::playlist_changed ()
414 RouteTimeAxisView::label_view ()
416 string x = _route->name ();
417 if (x != name_label.get_text ()) {
418 name_label.set_text (x);
420 const int64_t track_number = _route->track_number ();
421 if (track_number == 0) {
422 number_label.set_text ("");
424 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
429 RouteTimeAxisView::update_track_number_visibility ()
432 bool show_label = _session->config.get_track_name_number();
434 if (_route && _route->is_master()) {
438 if (number_label.get_parent()) {
439 controls_table.remove (number_label);
442 if (ARDOUR::Profile->get_mixbus()) {
443 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
445 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
448 // except the width of the number label is subtracted from the name-hbox, so we
449 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
450 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
452 number_label.set_size_request(tnw, -1);
453 number_label.show ();
455 number_label.hide ();
460 RouteTimeAxisView::parameter_changed (string const & p)
462 if (p == "track-name-number") {
463 update_track_number_visibility();
464 } else if (p == "editor-stereo-only-meters") {
465 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
466 gm.get_level_meter().set_max_audio_meter_count (2);
468 gm.get_level_meter().set_max_audio_meter_count (0);
474 RouteTimeAxisView::take_name_changed (void *src)
482 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
484 if (ev->button != 1) {
488 build_playlist_menu ();
489 conditionally_add_to_selection ();
490 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
496 RouteTimeAxisView::automation_click (GdkEventButton *ev)
498 if (ev->button != 1) {
502 conditionally_add_to_selection ();
503 build_automation_action_menu (false);
504 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
510 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
512 using namespace Menu_Helpers;
514 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
515 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
518 detach_menu (subplugin_menu);
520 _main_automation_menu_map.clear ();
521 delete automation_action_menu;
522 automation_action_menu = new Menu;
524 MenuList& items = automation_action_menu->items();
526 automation_action_menu->set_name ("ArdourContextMenu");
528 items.push_back (MenuElem (_("Show All Automation"),
529 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
531 items.push_back (MenuElem (_("Show Existing Automation"),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
534 items.push_back (MenuElem (_("Hide All Automation"),
535 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
537 /* Attach the plugin submenu. It may have previously been used elsewhere,
538 so it was detached above
541 bool single_track_selected = (!for_selection || _editor.get_selection().tracks.size() == 1);
543 if (!subplugin_menu.items().empty()) {
544 items.push_back (SeparatorElem ());
545 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
546 items.back().set_sensitive (single_track_selected);
549 /* Add any route automation */
552 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
553 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
554 gain_automation_item->set_active (single_track_selected &&
555 string_to<bool>(gain_track->gui_property ("visible")));
557 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
561 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
562 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
563 trim_automation_item->set_active (single_track_selected &&
564 string_to<bool>(trim_track->gui_property ("visible")));
566 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
570 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
571 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
572 mute_automation_item->set_active (single_track_selected &&
573 string_to<bool>(mute_track->gui_property ("visible")));
575 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
578 if (!pan_tracks.empty() && !ARDOUR::Profile->get_mixbus()) {
579 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
580 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
581 pan_automation_item->set_active (single_track_selected &&
582 string_to<bool>(pan_tracks.front()->gui_property ("visible")));
584 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
585 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
586 _main_automation_menu_map[*p] = pan_automation_item;
592 RouteTimeAxisView::build_display_menu ()
594 using namespace Menu_Helpers;
598 TimeAxisView::build_display_menu ();
600 /* now fill it with our stuff */
602 MenuList& items = display_menu->items();
603 display_menu->set_name ("ArdourContextMenu");
605 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
607 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
609 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
611 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
613 items.push_back (SeparatorElem());
616 detach_menu (*_size_menu);
619 items.push_back (MenuElem (_("Height"), *_size_menu));
620 items.push_back (SeparatorElem());
622 // Hook for derived classes to add type specific stuff
623 append_extra_display_menu_items ();
627 Menu* layers_menu = manage (new Menu);
628 MenuList &layers_items = layers_menu->items();
629 layers_menu->set_name("ArdourContextMenu");
631 RadioMenuItem::Group layers_group;
633 /* Find out how many overlaid/stacked tracks we have in the selection */
637 int unchangeable = 0;
638 TrackSelection const & s = _editor.get_selection().tracks;
640 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
641 StreamView* v = (*i)->view ();
646 if (v->can_change_layer_display()) {
647 switch (v->layer_display ()) {
661 /* We're not connecting to signal_toggled() here; in the case where these two items are
662 set to be in the `inconsistent' state, it seems that one or other will end up active
663 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
664 select the active one, no toggled signal is emitted so nothing happens.
667 _ignore_set_layer_display = true;
669 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
670 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
671 i->set_active (overlaid != 0 && stacked == 0);
672 i->set_inconsistent (overlaid != 0 && stacked != 0);
673 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
676 i->set_sensitive (false);
679 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
680 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
681 i->set_active (overlaid == 0 && stacked != 0);
682 i->set_inconsistent (overlaid != 0 && stacked != 0);
683 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
686 i->set_sensitive (false);
689 _ignore_set_layer_display = false;
691 items.push_back (MenuElem (_("Layers"), *layers_menu));
693 Menu* alignment_menu = manage (new Menu);
694 MenuList& alignment_items = alignment_menu->items();
695 alignment_menu->set_name ("ArdourContextMenu");
697 RadioMenuItem::Group align_group;
699 /* Same verbose hacks as for the layering options above */
705 boost::shared_ptr<Track> first_track;
707 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
708 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
709 if (!r || !r->is_track ()) {
714 first_track = r->track();
717 switch (r->track()->alignment_choice()) {
721 switch (r->track()->alignment_style()) {
722 case ExistingMaterial:
730 case UseExistingMaterial:
746 inconsistent = false;
753 if (!inconsistent && first_track) {
755 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
756 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
757 i->set_active (automatic != 0 && existing == 0 && capture == 0);
758 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
760 switch (first_track->alignment_choice()) {
762 switch (first_track->alignment_style()) {
763 case ExistingMaterial:
764 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
767 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
775 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
776 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
777 i->set_active (existing != 0 && capture == 0 && automatic == 0);
778 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
780 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
781 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
782 i->set_active (existing == 0 && capture != 0 && automatic == 0);
783 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
785 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
791 items.push_back (SeparatorElem());
793 build_playlist_menu ();
794 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
795 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
798 route_group_menu->detach ();
801 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
802 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
804 r.push_back (rtv->route ());
809 r.push_back (route ());
812 if (!_route->is_master()) {
813 route_group_menu->build (r);
814 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
817 build_automation_action_menu (true);
818 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
820 items.push_back (SeparatorElem());
824 TrackSelection const & s = _editor.get_selection().tracks;
825 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
826 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
831 if (r->route()->active()) {
838 items.push_back (CheckMenuElem (_("Active")));
839 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
840 bool click_sets_active = true;
841 if (active > 0 && inactive == 0) {
842 i->set_active (true);
843 click_sets_active = false;
844 } else if (active > 0 && inactive > 0) {
845 i->set_inconsistent (true);
847 i->set_sensitive(! _session->transport_rolling());
848 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
850 items.push_back (SeparatorElem());
851 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
852 if (_route && !_route->is_master()) {
853 items.push_back (SeparatorElem());
854 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
856 items.push_back (SeparatorElem());
857 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
861 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
863 TimeAxisView::show_timestretch (start, end, layers, layer);
873 /* check that the time selection was made in our route, or our route group.
874 remember that route_group() == 0 implies the route is *not* in a edit group.
877 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
878 /* this doesn't apply to us */
882 /* ignore it if our edit group is not active */
884 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
889 if (timestretch_rect == 0) {
890 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
891 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
892 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
895 timestretch_rect->show ();
896 timestretch_rect->raise_to_top ();
898 double const x1 = start / _editor.get_current_zoom();
899 double const x2 = (end - 1) / _editor.get_current_zoom();
901 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
902 x2, current_height() * (layers - layer) / layers));
906 RouteTimeAxisView::hide_timestretch ()
908 TimeAxisView::hide_timestretch ();
910 if (timestretch_rect) {
911 timestretch_rect->hide ();
916 RouteTimeAxisView::show_selection (TimeSelection& ts)
920 /* ignore it if our edit group is not active or if the selection was started
921 in some other track or route group (remember that route_group() == 0 means
922 that the track is not in an route group).
925 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
926 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
932 TimeAxisView::show_selection (ts);
936 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
939 bool height_changed = (height == 0) || (h != height);
942 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
945 gm.get_level_meter().setup_meters (gmlen, meter_width);
947 TimeAxisView::set_height (h, m);
950 _view->set_height ((double) current_height());
953 if (height >= preset_height (HeightNormal)) {
957 gm.get_gain_slider().show();
959 if (!_route || _route->is_monitor()) {
964 if (rec_enable_button)
965 rec_enable_button->show();
967 route_group_button.show();
968 automation_button.show();
970 if (is_track() && track()->mode() == ARDOUR::Normal) {
971 playlist_button.show();
978 gm.get_gain_slider().hide();
980 if (!_route || _route->is_monitor()) {
985 if (rec_enable_button)
986 rec_enable_button->show();
988 route_group_button.hide ();
989 automation_button.hide ();
991 if (is_track() && track()->mode() == ARDOUR::Normal) {
992 playlist_button.hide ();
997 if (height_changed && !no_redraw) {
998 /* only emit the signal if the height really changed */
1004 RouteTimeAxisView::route_color_changed ()
1006 using namespace ARDOUR_UI_UTILS;
1008 _view->apply_color (color(), StreamView::RegionColor);
1010 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1014 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1019 speed = track()->speed();
1023 _view->set_samples_per_pixel (fpp * speed);
1026 StripableTimeAxisView::set_samples_per_pixel (fpp * speed);
1030 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1032 if (!mitem->get_active()) {
1033 /* this is one of the two calls made when these radio menu items change status. this one
1034 is for the item that became inactive, and we want to ignore it.
1039 if (apply_to_selection) {
1040 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1043 track()->set_align_choice (choice);
1049 RouteTimeAxisView::rename_current_playlist ()
1051 Prompter prompter (true);
1054 boost::shared_ptr<Track> tr = track();
1055 if (!tr || tr->destructive()) {
1059 boost::shared_ptr<Playlist> pl = tr->playlist();
1064 prompter.set_title (_("Rename Playlist"));
1065 prompter.set_prompt (_("New name for playlist:"));
1066 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1067 prompter.set_initial_text (pl->name());
1068 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1071 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1074 prompter.get_result (name);
1075 if (name.length()) {
1076 if (_session->playlists->by_name (name)) {
1077 MessageDialog msg (_("Given playlist name is not unique."));
1079 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1081 pl->set_name (name);
1089 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1091 std::string ret (basename);
1093 std::string const group_string = "." + route_group()->name() + ".";
1095 // iterate through all playlists
1097 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1098 std::string tmp = (*i)->name();
1100 std::string::size_type idx = tmp.find(group_string);
1101 // find those which belong to this group
1102 if (idx != string::npos) {
1103 tmp = tmp.substr(idx + group_string.length());
1105 // and find the largest current number
1107 if (x > maxnumber) {
1116 snprintf (buf, sizeof(buf), "%d", maxnumber);
1118 ret = this->name() + "." + route_group()->name () + "." + buf;
1124 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1128 boost::shared_ptr<Track> tr = track ();
1129 if (!tr || tr->destructive()) {
1133 boost::shared_ptr<const Playlist> pl = tr->playlist();
1140 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1141 name = resolve_new_group_playlist_name(name,playlists_before_op);
1144 while (_session->playlists->by_name(name)) {
1145 name = Playlist::bump_name (name, *_session);
1149 // TODO: The prompter "new" button should be de-activated if the user
1150 // specifies a playlist name which already exists in the session.
1152 Prompter prompter (true);
1155 prompter.set_title (_("New Copy Playlist"));
1156 prompter.set_prompt (_("Name for playlist copy:"));
1158 prompter.set_title (_("New Playlist"));
1159 prompter.set_prompt (_("Name for new playlist:"));
1161 prompter.set_initial_text (name);
1162 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1163 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1164 prompter.show_all ();
1167 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1170 prompter.get_result (name);
1171 if (name.length()) {
1172 if (_session->playlists->by_name (name)) {
1173 MessageDialog msg (_("Given playlist name is not unique."));
1175 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1183 if (name.length()) {
1185 tr->use_copy_playlist ();
1187 tr->use_new_playlist ();
1189 tr->playlist()->set_name (name);
1194 RouteTimeAxisView::clear_playlist ()
1196 boost::shared_ptr<Track> tr = track ();
1197 if (!tr || tr->destructive()) {
1201 boost::shared_ptr<Playlist> pl = tr->playlist();
1206 _editor.clear_playlist (pl);
1210 RouteTimeAxisView::speed_changed ()
1212 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1216 RouteTimeAxisView::update_diskstream_display ()
1226 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1228 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1230 /* special case: select/deselect all tracks */
1232 _editor.begin_reversible_selection_op (X_("Selection Click"));
1234 if (_editor.get_selection().selected (this)) {
1235 _editor.get_selection().clear_tracks ();
1237 _editor.select_all_tracks ();
1240 _editor.commit_reversible_selection_op ();
1245 _editor.begin_reversible_selection_op (X_("Selection Click"));
1247 switch (ArdourKeyboard::selection_type (ev->state)) {
1248 case Selection::Toggle:
1249 _editor.get_selection().toggle (this);
1252 case Selection::Set:
1253 _editor.get_selection().set (this);
1256 case Selection::Extend:
1257 _editor.extend_selection_to_track (*this);
1260 case Selection::Add:
1261 _editor.get_selection().add (this);
1265 _editor.commit_reversible_selection_op ();
1267 _editor.set_selected_mixer_strip (*this);
1271 RouteTimeAxisView::set_selected_points (PointSelection& points)
1273 StripableTimeAxisView::set_selected_points (points);
1274 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1276 asv->set_selected_points (points);
1281 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1284 _view->set_selected_regionviews (regions);
1288 /** Add the selectable things that we have to a list.
1289 * @param results List to add things to.
1292 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1297 speed = track()->speed();
1300 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1301 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1303 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1304 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1307 /* pick up visible automation tracks */
1308 StripableTimeAxisView::get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1312 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1315 _view->get_inverted_selectables (sel, results);
1317 StripableTimeAxisView::get_inverted_selectables (sel, results);
1321 RouteTimeAxisView::route_group () const
1323 return _route->route_group();
1326 boost::shared_ptr<Playlist>
1327 RouteTimeAxisView::playlist () const
1329 boost::shared_ptr<Track> tr;
1331 if ((tr = track()) != 0) {
1332 return tr->playlist();
1334 return boost::shared_ptr<Playlist> ();
1339 RouteTimeAxisView::name_entry_changed (string const& str)
1341 if (str == _route->name()) {
1347 strip_whitespace_edges (x);
1353 if (_session->route_name_internal (x)) {
1354 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1356 } else if (RouteUI::verify_new_route_name (x)) {
1357 _route->set_name (x);
1364 boost::shared_ptr<Region>
1365 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1367 boost::shared_ptr<Playlist> pl = playlist ();
1370 return pl->find_next_region (pos, point, dir);
1373 return boost::shared_ptr<Region> ();
1377 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1379 boost::shared_ptr<Playlist> pl = playlist ();
1382 return pl->find_next_region_boundary (pos, dir);
1389 RouteTimeAxisView::fade_range (TimeSelection& selection)
1391 boost::shared_ptr<Playlist> what_we_got;
1392 boost::shared_ptr<Track> tr = track ();
1393 boost::shared_ptr<Playlist> playlist;
1396 /* route is a bus, not a track */
1400 playlist = tr->playlist();
1402 TimeSelection time (selection);
1403 float const speed = tr->speed();
1404 if (speed != 1.0f) {
1405 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1406 (*i).start = session_frame_to_track_frame((*i).start, speed);
1407 (*i).end = session_frame_to_track_frame((*i).end, speed);
1411 playlist->clear_changes ();
1412 playlist->clear_owned_changes ();
1414 playlist->fade_range (time);
1416 vector<Command*> cmds;
1417 playlist->rdiff (cmds);
1418 _session->add_commands (cmds);
1419 _session->add_command (new StatefulDiffCommand (playlist));
1424 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1426 boost::shared_ptr<Playlist> what_we_got;
1427 boost::shared_ptr<Track> tr = track ();
1428 boost::shared_ptr<Playlist> playlist;
1431 /* route is a bus, not a track */
1435 playlist = tr->playlist();
1437 TimeSelection time (selection.time);
1438 float const speed = tr->speed();
1439 if (speed != 1.0f) {
1440 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1441 (*i).start = session_frame_to_track_frame((*i).start, speed);
1442 (*i).end = session_frame_to_track_frame((*i).end, speed);
1446 playlist->clear_changes ();
1447 playlist->clear_owned_changes ();
1451 if (playlist->cut (time) != 0) {
1452 if (Config->get_edit_mode() == Ripple) {
1453 playlist->ripple(time.start(), -time.length(), NULL);
1455 // no need to exclude any regions from rippling here
1457 vector<Command*> cmds;
1458 playlist->rdiff (cmds);
1459 _session->add_commands (cmds);
1461 _session->add_command (new StatefulDiffCommand (playlist));
1466 if ((what_we_got = playlist->cut (time)) != 0) {
1467 _editor.get_cut_buffer().add (what_we_got);
1468 if (Config->get_edit_mode() == Ripple) {
1469 playlist->ripple(time.start(), -time.length(), NULL);
1471 // no need to exclude any regions from rippling here
1473 vector<Command*> cmds;
1474 playlist->rdiff (cmds);
1475 _session->add_commands (cmds);
1477 _session->add_command (new StatefulDiffCommand (playlist));
1481 if ((what_we_got = playlist->copy (time)) != 0) {
1482 _editor.get_cut_buffer().add (what_we_got);
1487 if ((what_we_got = playlist->cut (time)) != 0) {
1488 if (Config->get_edit_mode() == Ripple) {
1489 playlist->ripple(time.start(), -time.length(), NULL);
1491 // no need to exclude any regions from rippling here
1493 vector<Command*> cmds;
1494 playlist->rdiff (cmds);
1495 _session->add_commands (cmds);
1496 _session->add_command (new StatefulDiffCommand (playlist));
1497 what_we_got->release ();
1504 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1510 boost::shared_ptr<Playlist> pl = playlist ();
1511 const ARDOUR::DataType type = pl->data_type();
1512 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1514 if (p == selection.playlists.end()) {
1517 ctx.counts.increase_n_playlists(type);
1519 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1521 if (track()->speed() != 1.0f) {
1522 pos = session_frame_to_track_frame (pos, track()->speed());
1523 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1526 /* add multi-paste offset if applicable */
1527 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1528 const framecnt_t duration = extent.second - extent.first;
1529 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1531 pl->clear_changes ();
1532 pl->clear_owned_changes ();
1533 if (Config->get_edit_mode() == Ripple) {
1534 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1535 framecnt_t amount = extent.second - extent.first;
1536 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1538 pl->paste (*p, pos, ctx.times, sub_num);
1540 vector<Command*> cmds;
1542 _session->add_commands (cmds);
1544 _session->add_command (new StatefulDiffCommand (pl));
1550 struct PlaylistSorter {
1551 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1552 return a->sort_id() < b->sort_id();
1557 RouteTimeAxisView::build_playlist_menu ()
1559 using namespace Menu_Helpers;
1565 delete playlist_action_menu;
1566 playlist_action_menu = new Menu;
1567 playlist_action_menu->set_name ("ArdourContextMenu");
1569 MenuList& playlist_items = playlist_action_menu->items();
1570 playlist_action_menu->set_name ("ArdourContextMenu");
1571 playlist_items.clear();
1573 RadioMenuItem::Group playlist_group;
1574 boost::shared_ptr<Track> tr = track ();
1576 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1578 /* sort the playlists */
1580 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1582 /* add the playlists to the menu */
1583 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1584 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1585 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1586 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1588 if (tr->playlist()->id() == (*i)->id()) {
1593 playlist_items.push_back (SeparatorElem());
1594 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1595 playlist_items.push_back (SeparatorElem());
1597 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1598 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1599 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1602 // Use a label which tells the user what is happening
1603 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1604 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1608 playlist_items.push_back (SeparatorElem());
1609 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1610 playlist_items.push_back (SeparatorElem());
1612 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1616 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1618 assert (is_track());
1620 // exit if we were triggered by deactivating the old playlist
1621 if (!item->get_active()) {
1625 boost::shared_ptr<Playlist> pl (wpl.lock());
1631 if (track()->playlist() == pl) {
1632 // exit when use_playlist is called by the creation of the playlist menu
1633 // or the playlist choice is unchanged
1637 track()->use_playlist (pl);
1639 RouteGroup* rg = route_group();
1641 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1642 std::string group_string = "." + rg->name() + ".";
1644 std::string take_name = pl->name();
1645 std::string::size_type idx = take_name.find(group_string);
1647 if (idx == std::string::npos)
1650 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1652 boost::shared_ptr<RouteList> rl (rg->route_list());
1654 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1655 if ((*i) == this->route()) {
1659 std::string playlist_name = (*i)->name()+group_string+take_name;
1661 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1666 if (track->freeze_state() == Track::Frozen) {
1667 /* Don't change playlists of frozen tracks */
1671 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1673 // No playlist for this track for this take yet, make it
1674 track->use_new_playlist();
1675 track->playlist()->set_name(playlist_name);
1677 track->use_playlist(ipl);
1684 RouteTimeAxisView::update_playlist_tip ()
1686 RouteGroup* rg = route_group ();
1687 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1688 string group_string = "." + rg->name() + ".";
1690 string take_name = track()->playlist()->name();
1691 string::size_type idx = take_name.find(group_string);
1693 if (idx != string::npos) {
1694 /* find the bit containing the take number / name */
1695 take_name = take_name.substr (idx + group_string.length());
1697 /* set the playlist button tooltip to the take name */
1700 string_compose(_("Take: %1.%2"),
1701 Gtkmm2ext::markup_escape_text (rg->name()),
1702 Gtkmm2ext::markup_escape_text (take_name))
1709 /* set the playlist button tooltip to the playlist name */
1710 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1715 RouteTimeAxisView::show_playlist_selector ()
1717 _editor.playlist_selector().show_for (this);
1721 RouteTimeAxisView::map_frozen ()
1727 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1729 switch (track()->freeze_state()) {
1731 playlist_button.set_sensitive (false);
1734 playlist_button.set_sensitive (true);
1737 RouteUI::map_frozen ();
1741 RouteTimeAxisView::color_handler ()
1743 //case cTimeStretchOutline:
1744 if (timestretch_rect) {
1745 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1747 //case cTimeStretchFill:
1748 if (timestretch_rect) {
1749 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1755 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1756 * Will add track if necessary.
1759 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1761 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1762 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1765 /* it doesn't exist yet, so we don't care about the button state: just add it */
1766 create_automation_child (param, true);
1769 bool yn = menu->get_active();
1770 bool changed = false;
1772 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1774 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1775 will have done that for us.
1778 if (changed && !no_redraw) {
1786 RouteTimeAxisView::update_pan_track_visibility ()
1788 bool const showit = pan_automation_item->get_active();
1789 bool changed = false;
1791 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1792 if ((*i)->set_marked_for_display (showit)) {
1798 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1803 RouteTimeAxisView::ensure_pan_views (bool show)
1805 bool changed = false;
1806 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1808 (*i)->set_marked_for_display (false);
1811 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1815 if (!_route->panner()) {
1819 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1820 set<Evoral::Parameter>::iterator p;
1822 for (p = params.begin(); p != params.end(); ++p) {
1823 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1825 if (pan_control->parameter().type() == NullAutomation) {
1826 error << "Pan control has NULL automation type!" << endmsg;
1830 if (automation_child (pan_control->parameter ()).get () == 0) {
1832 /* we don't already have an AutomationTimeAxisView for this parameter */
1834 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1836 boost::shared_ptr<AutomationTimeAxisView> t (
1837 new AutomationTimeAxisView (_session,
1841 pan_control->parameter (),
1849 pan_tracks.push_back (t);
1850 add_automation_child (*p, t, show);
1852 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1859 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1861 if (apply_to_selection) {
1862 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1866 StripableTimeAxisView::show_all_automation ();
1868 /* Show processor automation */
1870 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1871 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1872 if ((*ii)->view == 0) {
1873 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1876 (*ii)->menu_item->set_active (true);
1889 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1891 if (apply_to_selection) {
1892 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1896 StripableTimeAxisView::show_existing_automation ();
1898 /* Show processor automation */
1899 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1900 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1901 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1902 (*ii)->menu_item->set_active (true);
1913 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1915 if (apply_to_selection) {
1916 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1919 StripableTimeAxisView::hide_all_automation ();
1921 /* Hide processor automation */
1922 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1923 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1924 (*ii)->menu_item->set_active (false);
1934 RouteTimeAxisView::region_view_added (RegionView* rv)
1936 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1937 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1938 boost::shared_ptr<AutomationTimeAxisView> atv;
1940 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1945 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1946 (*i)->add_ghost(rv);
1950 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1952 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1958 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1960 parent.remove_processor_automation_node (this);
1964 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1967 remove_child (pan->view);
1971 RouteTimeAxisView::ProcessorAutomationNode*
1972 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1974 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1976 if ((*i)->processor == processor) {
1978 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1979 if ((*ii)->what == what) {
1989 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1991 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1994 ProcessorAutomationNode* pan;
1996 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1997 /* session state may never have been saved with new plugin */
1998 error << _("programming error: ")
1999 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2000 processor->name(), what.type(), (int) what.channel(), what.id() )
2002 abort(); /*NOTREACHED*/
2010 boost::shared_ptr<AutomationControl> control
2011 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2013 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2014 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2015 _editor, *this, false, parent_canvas,
2016 processor->describe_parameter (what), processor->name()));
2018 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2020 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2023 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2028 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2031 pan->menu_item->set_active (false);
2040 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2042 boost::shared_ptr<Processor> processor (p.lock ());
2044 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2045 /* The Amp processor is a special case and is dealt with separately */
2049 set<Evoral::Parameter> existing;
2051 processor->what_has_data (existing);
2053 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2055 Evoral::Parameter param (*i);
2056 boost::shared_ptr<AutomationLine> al;
2058 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2061 add_processor_automation_curve (processor, param);
2067 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2069 boost::shared_ptr<Processor> processor (p.lock ());
2071 if (!processor || !processor->display_to_user ()) {
2075 /* we use this override to veto the Amp processor from the plugin menu,
2076 as its automation lane can be accessed using the special "Fader" menu
2080 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2084 using namespace Menu_Helpers;
2085 ProcessorAutomationInfo *rai;
2086 list<ProcessorAutomationInfo*>::iterator x;
2088 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2090 if (automatable.empty()) {
2094 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2095 if ((*x)->processor == processor) {
2100 if (x == processor_automation.end()) {
2101 rai = new ProcessorAutomationInfo (processor);
2102 processor_automation.push_back (rai);
2107 /* any older menu was deleted at the top of processors_changed()
2108 when we cleared the subplugin menu.
2111 rai->menu = manage (new Menu);
2112 MenuList& items = rai->menu->items();
2113 rai->menu->set_name ("ArdourContextMenu");
2117 std::set<Evoral::Parameter> has_visible_automation;
2118 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2120 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2122 ProcessorAutomationNode* pan;
2123 Gtk::CheckMenuItem* mitem;
2125 string name = processor->describe_parameter (*i);
2127 if (name == X_("hidden")) {
2131 items.push_back (CheckMenuElem (name));
2132 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2134 _subplugin_menu_map[*i] = mitem;
2136 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2137 mitem->set_active(true);
2140 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2144 pan = new ProcessorAutomationNode (*i, mitem, *this);
2146 rai->lines.push_back (pan);
2150 pan->menu_item = mitem;
2154 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2157 if (items.size() == 0) {
2161 /* add the menu for this processor, because the subplugin
2162 menu is always cleared at the top of processors_changed().
2163 this is the result of some poor design in gtkmm and/or
2167 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2172 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2173 RouteTimeAxisView::ProcessorAutomationNode* pan)
2175 bool showit = pan->menu_item->get_active();
2176 bool redraw = false;
2178 if (pan->view == 0 && showit) {
2179 add_processor_automation_curve (rai->processor, pan->what);
2183 if (pan->view && pan->view->set_marked_for_display (showit)) {
2187 if (redraw && !no_redraw) {
2193 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2195 if (c.type == RouteProcessorChange::MeterPointChange) {
2196 /* nothing to do if only the meter point has changed */
2200 using namespace Menu_Helpers;
2202 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2203 (*i)->valid = false;
2206 setup_processor_menu_and_curves ();
2208 bool deleted_processor_automation = false;
2210 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2212 list<ProcessorAutomationInfo*>::iterator tmp;
2220 processor_automation.erase (i);
2221 deleted_processor_automation = true;
2228 if (deleted_processor_automation && !no_redraw) {
2233 boost::shared_ptr<AutomationLine>
2234 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2236 ProcessorAutomationNode* pan;
2238 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2244 return boost::shared_ptr<AutomationLine>();
2248 RouteTimeAxisView::reset_processor_automation_curves ()
2250 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2256 RouteTimeAxisView::can_edit_name () const
2258 /* we do not allow track name changes if it is record enabled
2260 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2264 return !trk->rec_enable_control()->get_value();
2268 RouteTimeAxisView::blink_rec_display (bool onoff)
2270 RouteUI::blink_rec_display (onoff);
2274 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2276 if (_ignore_set_layer_display) {
2280 if (apply_to_selection) {
2281 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2285 _view->set_layer_display (d);
2288 set_gui_property (X_("layer-display"), d);
2293 RouteTimeAxisView::layer_display () const
2296 return _view->layer_display ();
2299 /* we don't know, since we don't have a _view, so just return something */
2304 RouteTimeAxisView::fast_update ()
2306 gm.get_level_meter().update_meters ();
2310 RouteTimeAxisView::hide_meter ()
2313 gm.get_level_meter().hide_meters ();
2317 RouteTimeAxisView::show_meter ()
2323 RouteTimeAxisView::reset_meter ()
2325 if (UIConfiguration::instance().get_show_track_meters()) {
2326 int meter_width = 3;
2327 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2330 gm.get_level_meter().setup_meters (height - 9, meter_width);
2337 RouteTimeAxisView::clear_meter ()
2339 gm.get_level_meter().clear_meters ();
2343 RouteTimeAxisView::meter_changed ()
2345 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2347 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2350 // reset peak when meter point changes
2351 gm.reset_peak_display();
2355 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2358 if (_route && !no_redraw) {
2364 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2366 using namespace Menu_Helpers;
2368 if (!_underlay_streams.empty()) {
2369 MenuList& parent_items = parent_menu->items();
2370 Menu* gs_menu = manage (new Menu);
2371 gs_menu->set_name ("ArdourContextMenu");
2372 MenuList& gs_items = gs_menu->items();
2374 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2376 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2377 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2378 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2384 RouteTimeAxisView::set_underlay_state()
2386 if (!underlay_xml_node) {
2390 XMLNodeList nlist = underlay_xml_node->children();
2391 XMLNodeConstIterator niter;
2392 XMLNode *child_node;
2394 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2395 child_node = *niter;
2397 if (child_node->name() != "Underlay") {
2401 XMLProperty const * prop = child_node->property ("id");
2403 PBD::ID id (prop->value());
2405 StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2408 add_underlay(v->view(), false);
2417 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2423 RouteTimeAxisView& other = v->trackview();
2425 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2426 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2427 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2428 abort(); /*NOTREACHED*/
2431 _underlay_streams.push_back(v);
2432 other._underlay_mirrors.push_back(this);
2434 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2436 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2438 if (!underlay_xml_node) {
2439 underlay_xml_node = xml_node->add_child("Underlays");
2442 XMLNode* node = underlay_xml_node->add_child("Underlay");
2443 XMLProperty const * prop = node->add_property("id");
2444 prop->set_value(v->trackview().route()->id().to_s());
2451 RouteTimeAxisView::remove_underlay (StreamView* v)
2457 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2458 RouteTimeAxisView& other = v->trackview();
2460 if (it != _underlay_streams.end()) {
2461 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2463 if (gm == other._underlay_mirrors.end()) {
2464 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2465 abort(); /*NOTREACHED*/
2468 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2470 _underlay_streams.erase(it);
2471 other._underlay_mirrors.erase(gm);
2473 if (underlay_xml_node) {
2474 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2480 RouteTimeAxisView::set_button_names ()
2482 if (_route && _route->solo_safe_control()->solo_safe()) {
2483 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2485 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2487 if (Config->get_solo_control_is_listen_control()) {
2488 switch (Config->get_listen_position()) {
2489 case AfterFaderListen:
2490 solo_button->set_text (S_("AfterFader|A"));
2491 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2493 case PreFaderListen:
2494 solo_button->set_text (S_("PreFader|P"));
2495 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2499 solo_button->set_text (S_("Solo|S"));
2500 set_tooltip (*solo_button, _("Solo"));
2502 mute_button->set_text (S_("Mute|M"));
2506 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2508 Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2513 ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2514 if (i != _subplugin_menu_map.end()) {
2522 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2524 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2526 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2530 gain_track.reset (new AutomationTimeAxisView (_session,
2531 _route, _route->amp(), c, param,
2536 _route->amp()->describe_parameter(param)));
2539 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2542 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2546 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2548 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2549 if (!c || ! _route->trim()->active()) {
2553 trim_track.reset (new AutomationTimeAxisView (_session,
2554 _route, _route->trim(), c, param,
2559 _route->trim()->describe_parameter(param)));
2562 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2565 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2569 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2571 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2573 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2577 mute_track.reset (new AutomationTimeAxisView (_session,
2578 _route, _route, c, param,
2583 _route->describe_parameter(param)));
2586 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2589 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2593 void add_region_to_list (RegionView* rv, RegionList* l)
2595 l->push_back (rv->region());
2599 RouteTimeAxisView::combine_regions ()
2601 /* as of may 2011, we do not offer uncombine for MIDI tracks
2604 if (!is_audio_track()) {
2612 RegionList selected_regions;
2613 boost::shared_ptr<Playlist> playlist = track()->playlist();
2615 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2617 if (selected_regions.size() < 2) {
2621 playlist->clear_changes ();
2622 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2624 _session->add_command (new StatefulDiffCommand (playlist));
2625 /* make the new region be selected */
2627 return _view->find_view (compound_region);
2631 RouteTimeAxisView::uncombine_regions ()
2633 /* as of may 2011, we do not offer uncombine for MIDI tracks
2635 if (!is_audio_track()) {
2643 RegionList selected_regions;
2644 boost::shared_ptr<Playlist> playlist = track()->playlist();
2646 /* have to grab selected regions first because the uncombine is going
2647 * to change that in the middle of the list traverse
2650 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2652 playlist->clear_changes ();
2654 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2655 playlist->uncombine (*i);
2658 _session->add_command (new StatefulDiffCommand (playlist));
2662 RouteTimeAxisView::state_id() const
2664 return string_compose ("rtav %1", _route->id().to_s());
2669 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2671 TimeAxisView::remove_child (c);
2673 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2675 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2676 if (i->second == a) {
2677 _automation_tracks.erase (i);
2685 RouteTimeAxisView::color () const
2687 return route_color ();
2691 RouteTimeAxisView::marked_for_display () const
2693 return !_route->presentation_info().hidden();
2697 RouteTimeAxisView::set_marked_for_display (bool yn)
2699 return RouteUI::mark_hidden (!yn);