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/plugin_insert.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
55 #include "ardour/track.h"
57 #include "canvas/debug.h"
59 #include "gtkmm2ext/gtk_ui.h"
60 #include "gtkmm2ext/utils.h"
62 #include "widgets/ardour_button.h"
63 #include "widgets/prompter.h"
64 #include "widgets/tooltips.h"
66 #include "ardour_ui.h"
67 #include "audio_streamview.h"
69 #include "enums_convert.h"
70 #include "route_time_axis.h"
71 #include "automation_time_axis.h"
73 #include "gui_thread.h"
74 #include "item_counts.h"
76 #include "paste_context.h"
77 #include "patch_change_widget.h"
78 #include "playlist_selector.h"
79 #include "point_selection.h"
80 #include "public_editor.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "streamview.h"
85 #include "ui_config.h"
87 #include "route_group_menu.h"
91 using namespace ARDOUR;
92 using namespace ArdourWidgets;
94 using namespace Gtkmm2ext;
96 using namespace Editing;
100 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
102 , StripableTimeAxisView(ed, sess, canvas)
104 , button_table (3, 3)
105 , route_group_button (S_("RTAV|G"))
106 , playlist_button (S_("RTAV|P"))
107 , automation_button (S_("RTAV|A"))
108 , automation_action_menu (0)
109 , plugins_submenu_item (0)
110 , route_group_menu (0)
111 , playlist_action_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 delete automation_action_menu;
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));
789 delete alignment_menu;
792 items.push_back (SeparatorElem());
794 build_playlist_menu ();
795 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
796 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
799 if (!is_midi_track () && _route->the_instrument ()) {
801 items.push_back (MenuElem (_("Patch Selector..."),
802 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
803 items.push_back (SeparatorElem());
806 route_group_menu->detach ();
809 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
812 r.push_back (rtv->route ());
817 r.push_back (route ());
820 if (!_route->is_master()) {
821 route_group_menu->build (r);
822 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
825 build_automation_action_menu (true);
826 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
828 items.push_back (SeparatorElem());
832 TrackSelection const & s = _editor.get_selection().tracks;
833 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
834 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
839 if (r->route()->active()) {
846 items.push_back (CheckMenuElem (_("Active")));
847 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
848 bool click_sets_active = true;
849 if (active > 0 && inactive == 0) {
850 i->set_active (true);
851 click_sets_active = false;
852 } else if (active > 0 && inactive > 0) {
853 i->set_inconsistent (true);
855 i->set_sensitive(! _session->transport_rolling());
856 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
858 items.push_back (SeparatorElem());
859 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
860 if (_route && !_route->is_master()) {
861 items.push_back (SeparatorElem());
862 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
864 items.push_back (SeparatorElem());
865 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
869 RouteTimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
871 TimeAxisView::show_timestretch (start, end, layers, layer);
881 /* check that the time selection was made in our route, or our route group.
882 remember that route_group() == 0 implies the route is *not* in a edit group.
885 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
886 /* this doesn't apply to us */
890 /* ignore it if our edit group is not active */
892 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
897 if (timestretch_rect == 0) {
898 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
899 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
900 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
903 timestretch_rect->show ();
904 timestretch_rect->raise_to_top ();
906 double const x1 = start / _editor.get_current_zoom();
907 double const x2 = (end - 1) / _editor.get_current_zoom();
909 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
910 x2, current_height() * (layers - layer) / layers));
914 RouteTimeAxisView::hide_timestretch ()
916 TimeAxisView::hide_timestretch ();
918 if (timestretch_rect) {
919 timestretch_rect->hide ();
924 RouteTimeAxisView::show_selection (TimeSelection& ts)
928 /* ignore it if our edit group is not active or if the selection was started
929 in some other track or route group (remember that route_group() == 0 means
930 that the track is not in an route group).
933 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
934 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
940 TimeAxisView::show_selection (ts);
944 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
947 bool height_changed = (height == 0) || (h != height);
950 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
953 gm.get_level_meter().setup_meters (gmlen, meter_width);
955 TimeAxisView::set_height (h, m);
958 _view->set_height ((double) current_height());
961 if (height >= preset_height (HeightNormal)) {
965 gm.get_gain_slider().show();
967 if (!_route || _route->is_monitor()) {
972 if (rec_enable_button)
973 rec_enable_button->show();
975 route_group_button.show();
976 automation_button.show();
978 if (is_track() && track()->mode() == ARDOUR::Normal) {
979 playlist_button.show();
986 gm.get_gain_slider().hide();
988 if (!_route || _route->is_monitor()) {
993 if (rec_enable_button)
994 rec_enable_button->show();
996 route_group_button.hide ();
997 automation_button.hide ();
999 if (is_track() && track()->mode() == ARDOUR::Normal) {
1000 playlist_button.hide ();
1005 if (height_changed && !no_redraw) {
1006 /* only emit the signal if the height really changed */
1012 RouteTimeAxisView::route_color_changed ()
1014 using namespace ARDOUR_UI_UTILS;
1016 _view->apply_color (color(), StreamView::RegionColor);
1018 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1022 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1025 _view->set_samples_per_pixel (fpp);
1028 StripableTimeAxisView::set_samples_per_pixel (fpp);
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 Prompter 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.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1069 prompter.set_initial_text (pl->name());
1070 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1073 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1076 prompter.get_result (name);
1077 if (name.length()) {
1078 if (_session->playlists()->by_name (name)) {
1079 MessageDialog msg (_("Given playlist name is not unique."));
1081 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1083 pl->set_name (name);
1091 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1093 std::string ret (basename);
1095 std::string const group_string = "." + route_group()->name() + ".";
1097 // iterate through all playlists
1099 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1100 std::string tmp = (*i)->name();
1102 std::string::size_type idx = tmp.find(group_string);
1103 // find those which belong to this group
1104 if (idx != string::npos) {
1105 tmp = tmp.substr(idx + group_string.length());
1107 // and find the largest current number
1109 if (x > maxnumber) {
1118 snprintf (buf, sizeof(buf), "%d", maxnumber);
1120 ret = this->name() + "." + route_group()->name () + "." + buf;
1126 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1130 boost::shared_ptr<Track> tr = track ();
1131 if (!tr || tr->destructive()) {
1135 boost::shared_ptr<const Playlist> pl = tr->playlist();
1142 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1143 name = resolve_new_group_playlist_name(name,playlists_before_op);
1146 while (_session->playlists()->by_name(name)) {
1147 name = Playlist::bump_name (name, *_session);
1151 // TODO: The prompter "new" button should be de-activated if the user
1152 // specifies a playlist name which already exists in the session.
1154 Prompter prompter (true);
1157 prompter.set_title (_("New Copy Playlist"));
1158 prompter.set_prompt (_("Name for playlist copy:"));
1160 prompter.set_title (_("New Playlist"));
1161 prompter.set_prompt (_("Name for new playlist:"));
1163 prompter.set_initial_text (name);
1164 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1165 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1166 prompter.show_all ();
1169 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1172 prompter.get_result (name);
1173 if (name.length()) {
1174 if (_session->playlists()->by_name (name)) {
1175 MessageDialog msg (_("Given playlist name is not unique."));
1177 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1185 if (name.length()) {
1187 tr->use_copy_playlist ();
1189 tr->use_default_new_playlist ();
1191 tr->playlist()->set_name (name);
1196 RouteTimeAxisView::clear_playlist ()
1198 boost::shared_ptr<Track> tr = track ();
1199 if (!tr || tr->destructive()) {
1203 boost::shared_ptr<Playlist> pl = tr->playlist();
1208 _editor.clear_playlist (pl);
1212 RouteTimeAxisView::speed_changed ()
1214 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1218 RouteTimeAxisView::update_diskstream_display ()
1228 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1230 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1232 /* special case: select/deselect all tracks */
1234 _editor.begin_reversible_selection_op (X_("Selection Click"));
1236 if (_editor.get_selection().selected (this)) {
1237 _editor.get_selection().clear_tracks ();
1239 _editor.select_all_tracks ();
1242 _editor.commit_reversible_selection_op ();
1247 _editor.begin_reversible_selection_op (X_("Selection Click"));
1249 switch (ArdourKeyboard::selection_type (ev->state)) {
1250 case Selection::Toggle:
1251 _editor.get_selection().toggle (this);
1254 case Selection::Set:
1255 _editor.get_selection().set (this);
1258 case Selection::Extend:
1259 _editor.extend_selection_to_track (*this);
1262 case Selection::Add:
1263 _editor.get_selection().add (this);
1267 _editor.commit_reversible_selection_op ();
1269 _editor.set_selected_mixer_strip (*this);
1273 RouteTimeAxisView::set_selected_points (PointSelection& points)
1275 StripableTimeAxisView::set_selected_points (points);
1276 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1278 asv->set_selected_points (points);
1283 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1286 _view->set_selected_regionviews (regions);
1290 /** Add the selectable things that we have to a list.
1291 * @param results List to add things to.
1294 RouteTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1296 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1297 _view->get_selectables (start, end, top, bot, results, within);
1300 /* pick up visible automation tracks */
1301 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1302 if (!(*i)->hidden()) {
1303 (*i)->get_selectables (start, end, top, bot, results, within);
1309 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1312 _view->get_inverted_selectables (sel, results);
1314 StripableTimeAxisView::get_inverted_selectables (sel, results);
1318 RouteTimeAxisView::route_group () const
1320 return _route->route_group();
1323 boost::shared_ptr<Playlist>
1324 RouteTimeAxisView::playlist () const
1326 boost::shared_ptr<Track> tr;
1328 if ((tr = track()) != 0) {
1329 return tr->playlist();
1331 return boost::shared_ptr<Playlist> ();
1336 RouteTimeAxisView::name_entry_changed (string const& str)
1338 if (str == _route->name()) {
1344 strip_whitespace_edges (x);
1350 if (_session->route_name_internal (x)) {
1351 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1353 } else if (RouteUI::verify_new_route_name (x)) {
1354 _route->set_name (x);
1361 boost::shared_ptr<Region>
1362 RouteTimeAxisView::find_next_region (samplepos_t pos, RegionPoint point, int32_t dir)
1364 boost::shared_ptr<Playlist> pl = playlist ();
1367 return pl->find_next_region (pos, point, dir);
1370 return boost::shared_ptr<Region> ();
1374 RouteTimeAxisView::find_next_region_boundary (samplepos_t pos, int32_t dir)
1376 boost::shared_ptr<Playlist> pl = playlist ();
1379 return pl->find_next_region_boundary (pos, dir);
1386 RouteTimeAxisView::fade_range (TimeSelection& selection)
1388 boost::shared_ptr<Playlist> what_we_got;
1389 boost::shared_ptr<Track> tr = track ();
1390 boost::shared_ptr<Playlist> playlist;
1393 /* route is a bus, not a track */
1397 playlist = tr->playlist();
1399 TimeSelection time (selection);
1401 playlist->clear_changes ();
1402 playlist->clear_owned_changes ();
1404 playlist->fade_range (time);
1406 vector<Command*> cmds;
1407 playlist->rdiff (cmds);
1408 _session->add_commands (cmds);
1409 _session->add_command (new StatefulDiffCommand (playlist));
1414 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1416 boost::shared_ptr<Playlist> what_we_got;
1417 boost::shared_ptr<Track> tr = track ();
1418 boost::shared_ptr<Playlist> playlist;
1421 /* route is a bus, not a track */
1425 playlist = tr->playlist();
1427 TimeSelection time (selection.time);
1429 playlist->clear_changes ();
1430 playlist->clear_owned_changes ();
1434 if (playlist->cut (time) != 0) {
1435 if (Config->get_edit_mode() == Ripple) {
1436 playlist->ripple(time.start(), -time.length(), NULL);
1438 // no need to exclude any regions from rippling here
1440 vector<Command*> cmds;
1441 playlist->rdiff (cmds);
1442 _session->add_commands (cmds);
1444 _session->add_command (new StatefulDiffCommand (playlist));
1449 if ((what_we_got = playlist->cut (time)) != 0) {
1450 _editor.get_cut_buffer().add (what_we_got);
1451 if (Config->get_edit_mode() == Ripple) {
1452 playlist->ripple(time.start(), -time.length(), NULL);
1454 // no need to exclude any regions from rippling here
1456 vector<Command*> cmds;
1457 playlist->rdiff (cmds);
1458 _session->add_commands (cmds);
1460 _session->add_command (new StatefulDiffCommand (playlist));
1464 if ((what_we_got = playlist->copy (time)) != 0) {
1465 _editor.get_cut_buffer().add (what_we_got);
1470 if ((what_we_got = playlist->cut (time)) != 0) {
1471 if (Config->get_edit_mode() == Ripple) {
1472 playlist->ripple(time.start(), -time.length(), NULL);
1474 // no need to exclude any regions from rippling here
1476 vector<Command*> cmds;
1477 playlist->rdiff (cmds);
1478 _session->add_commands (cmds);
1479 _session->add_command (new StatefulDiffCommand (playlist));
1480 what_we_got->release ();
1487 RouteTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1493 boost::shared_ptr<Playlist> pl = playlist ();
1494 const ARDOUR::DataType type = pl->data_type();
1495 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1497 if (p == selection.playlists.end()) {
1500 ctx.counts.increase_n_playlists(type);
1502 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1504 /* add multi-paste offset if applicable */
1505 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent();
1506 const samplecnt_t duration = extent.second - extent.first;
1507 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1509 pl->clear_changes ();
1510 pl->clear_owned_changes ();
1511 if (Config->get_edit_mode() == Ripple) {
1512 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent_with_endspace();
1513 samplecnt_t amount = extent.second - extent.first;
1514 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1516 pl->paste (*p, pos, ctx.times, sub_num);
1518 vector<Command*> cmds;
1520 _session->add_commands (cmds);
1522 _session->add_command (new StatefulDiffCommand (pl));
1528 struct PlaylistSorter {
1529 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1530 return a->sort_id() < b->sort_id();
1535 RouteTimeAxisView::build_playlist_menu ()
1537 using namespace Menu_Helpers;
1543 delete playlist_action_menu;
1544 playlist_action_menu = new Menu;
1545 playlist_action_menu->set_name ("ArdourContextMenu");
1547 MenuList& playlist_items = playlist_action_menu->items();
1548 playlist_action_menu->set_name ("ArdourContextMenu");
1549 playlist_items.clear();
1551 RadioMenuItem::Group playlist_group;
1552 boost::shared_ptr<Track> tr = track ();
1554 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists()->playlists_for_track (tr);
1556 /* sort the playlists */
1558 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1560 /* add the playlists to the menu */
1561 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1562 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1563 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1564 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1566 if (tr->playlist()->id() == (*i)->id()) {
1571 playlist_items.push_back (SeparatorElem());
1572 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1573 playlist_items.push_back (SeparatorElem());
1575 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1576 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1577 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1580 // Use a label which tells the user what is happening
1581 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1582 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1586 playlist_items.push_back (SeparatorElem());
1587 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1588 playlist_items.push_back (SeparatorElem());
1590 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1594 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1596 assert (is_track());
1598 // exit if we were triggered by deactivating the old playlist
1599 if (!item->get_active()) {
1603 boost::shared_ptr<Playlist> pl (wpl.lock());
1609 if (track()->playlist() == pl) {
1610 // exit when use_playlist is called by the creation of the playlist menu
1611 // or the playlist choice is unchanged
1615 track()->use_playlist (track()->data_type(), pl);
1617 RouteGroup* rg = route_group();
1619 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1620 std::string group_string = "." + rg->name() + ".";
1622 std::string take_name = pl->name();
1623 std::string::size_type idx = take_name.find(group_string);
1625 if (idx == std::string::npos)
1628 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1630 boost::shared_ptr<RouteList> rl (rg->route_list());
1632 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1633 if ((*i) == this->route()) {
1637 std::string playlist_name = (*i)->name()+group_string+take_name;
1639 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1644 if (track->freeze_state() == Track::Frozen) {
1645 /* Don't change playlists of frozen tracks */
1649 boost::shared_ptr<Playlist> ipl = session()->playlists()->by_name(playlist_name);
1651 // No playlist for this track for this take yet, make it
1652 track->use_default_new_playlist();
1653 track->playlist()->set_name(playlist_name);
1655 track->use_playlist(track->data_type(), ipl);
1662 RouteTimeAxisView::update_playlist_tip ()
1664 RouteGroup* rg = route_group ();
1665 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1666 string group_string = "." + rg->name() + ".";
1668 string take_name = track()->playlist()->name();
1669 string::size_type idx = take_name.find(group_string);
1671 if (idx != string::npos) {
1672 /* find the bit containing the take number / name */
1673 take_name = take_name.substr (idx + group_string.length());
1675 /* set the playlist button tooltip to the take name */
1678 string_compose(_("Take: %1.%2"),
1679 Gtkmm2ext::markup_escape_text (rg->name()),
1680 Gtkmm2ext::markup_escape_text (take_name))
1687 /* set the playlist button tooltip to the playlist name */
1688 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1693 RouteTimeAxisView::show_playlist_selector ()
1695 _editor.playlist_selector().show_for (this);
1699 RouteTimeAxisView::map_frozen ()
1705 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1707 switch (track()->freeze_state()) {
1709 playlist_button.set_sensitive (false);
1712 playlist_button.set_sensitive (true);
1715 RouteUI::map_frozen ();
1719 RouteTimeAxisView::color_handler ()
1721 //case cTimeStretchOutline:
1722 if (timestretch_rect) {
1723 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1725 //case cTimeStretchFill:
1726 if (timestretch_rect) {
1727 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1733 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1734 * Will add track if necessary.
1737 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1739 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1740 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1743 /* it doesn't exist yet, so we don't care about the button state: just add it */
1744 create_automation_child (param, true);
1747 bool yn = menu->get_active();
1748 bool changed = false;
1750 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1752 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1753 will have done that for us.
1756 if (changed && !no_redraw) {
1764 RouteTimeAxisView::update_pan_track_visibility ()
1766 bool const showit = pan_automation_item->get_active();
1767 bool changed = false;
1769 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1770 if ((*i)->set_marked_for_display (showit)) {
1776 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1781 RouteTimeAxisView::ensure_pan_views (bool show)
1783 bool changed = false;
1784 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1786 (*i)->set_marked_for_display (false);
1789 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1793 if (!_route->panner()) {
1797 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1798 set<Evoral::Parameter>::iterator p;
1800 for (p = params.begin(); p != params.end(); ++p) {
1801 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1803 if (pan_control->parameter().type() == NullAutomation) {
1804 error << "Pan control has NULL automation type!" << endmsg;
1808 if (automation_child (pan_control->parameter ()).get () == 0) {
1810 /* we don't already have an AutomationTimeAxisView for this parameter */
1812 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1814 boost::shared_ptr<AutomationTimeAxisView> t (
1815 new AutomationTimeAxisView (_session,
1819 pan_control->parameter (),
1827 pan_tracks.push_back (t);
1828 add_automation_child (*p, t, show);
1830 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1837 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1839 if (apply_to_selection) {
1840 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1844 StripableTimeAxisView::show_all_automation ();
1846 /* Show processor automation */
1848 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1849 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1850 if ((*ii)->view == 0) {
1851 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1854 (*ii)->menu_item->set_active (true);
1867 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1869 if (apply_to_selection) {
1870 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1874 StripableTimeAxisView::show_existing_automation ();
1876 /* Show processor automation */
1877 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1878 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1879 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1880 (*ii)->menu_item->set_active (true);
1891 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1893 if (apply_to_selection) {
1894 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1897 StripableTimeAxisView::hide_all_automation ();
1899 /* Hide processor automation */
1900 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1901 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1902 (*ii)->menu_item->set_active (false);
1912 RouteTimeAxisView::region_view_added (RegionView* rv)
1914 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1915 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1916 boost::shared_ptr<AutomationTimeAxisView> atv;
1918 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1923 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1924 (*i)->add_ghost(rv);
1928 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1930 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1936 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1938 parent.remove_processor_automation_node (this);
1942 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1945 remove_child (pan->view);
1949 RouteTimeAxisView::ProcessorAutomationNode*
1950 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1952 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1954 if ((*i)->processor == processor) {
1956 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1957 if ((*ii)->what == what) {
1967 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1969 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1972 ProcessorAutomationNode* pan;
1974 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1975 /* session state may never have been saved with new plugin */
1976 error << _("programming error: ")
1977 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1978 processor->name(), what.type(), (int) what.channel(), what.id() )
1980 abort(); /*NOTREACHED*/
1988 boost::shared_ptr<AutomationControl> control
1989 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1991 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1992 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1993 _editor, *this, false, parent_canvas,
1994 processor->describe_parameter (what), processor->name()));
1996 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1998 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2001 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2006 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2009 pan->menu_item->set_active (false);
2018 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2020 boost::shared_ptr<Processor> processor (p.lock ());
2022 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2023 /* The Amp processor is a special case and is dealt with separately */
2027 set<Evoral::Parameter> existing;
2029 processor->what_has_data (existing);
2031 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2033 Evoral::Parameter param (*i);
2034 boost::shared_ptr<AutomationLine> al;
2036 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2039 add_processor_automation_curve (processor, param);
2045 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2047 boost::shared_ptr<Processor> processor (p.lock ());
2049 if (!processor || !processor->display_to_user ()) {
2053 /* we use this override to veto the Amp processor from the plugin menu,
2054 as its automation lane can be accessed using the special "Fader" menu
2058 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2062 using namespace Menu_Helpers;
2063 ProcessorAutomationInfo *rai;
2064 list<ProcessorAutomationInfo*>::iterator x;
2066 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2068 if (automatable.empty()) {
2072 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2073 if ((*x)->processor == processor) {
2078 if (x == processor_automation.end()) {
2079 rai = new ProcessorAutomationInfo (processor);
2080 processor_automation.push_back (rai);
2085 /* any older menu was deleted at the top of processors_changed()
2086 when we cleared the subplugin menu.
2089 rai->menu = manage (new Menu);
2090 MenuList& items = rai->menu->items();
2091 rai->menu->set_name ("ArdourContextMenu");
2095 std::set<Evoral::Parameter> has_visible_automation;
2096 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2098 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2100 ProcessorAutomationNode* pan;
2101 Gtk::CheckMenuItem* mitem;
2103 string name = processor->describe_parameter (*i);
2105 if (name == X_("hidden")) {
2109 items.push_back (CheckMenuElem (name));
2110 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2112 _subplugin_menu_map[*i] = mitem;
2114 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2115 mitem->set_active(true);
2118 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2122 pan = new ProcessorAutomationNode (*i, mitem, *this);
2124 rai->lines.push_back (pan);
2128 pan->menu_item = mitem;
2132 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2135 if (items.size() == 0) {
2139 /* add the menu for this processor, because the subplugin
2140 menu is always cleared at the top of processors_changed().
2141 this is the result of some poor design in gtkmm and/or
2145 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2150 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2151 RouteTimeAxisView::ProcessorAutomationNode* pan)
2153 bool showit = pan->menu_item->get_active();
2154 bool redraw = false;
2156 if (pan->view == 0 && showit) {
2157 add_processor_automation_curve (rai->processor, pan->what);
2161 if (pan->view && pan->view->set_marked_for_display (showit)) {
2165 if (redraw && !no_redraw) {
2171 RouteTimeAxisView::reread_midnam ()
2173 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument ());
2175 bool rv = pi->plugin ()->read_midnam();
2177 if (rv && patch_change_dialog ()) {
2178 patch_change_dialog ()->refresh ();
2183 RouteTimeAxisView::drop_instrument_ref ()
2185 midnam_connection.drop_connections ();
2189 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2192 boost::shared_ptr<Processor> the_instrument (_route->the_instrument());
2193 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (the_instrument);
2194 if (pi && pi->plugin ()->has_midnam ()) {
2195 midnam_connection.drop_connections ();
2196 the_instrument->DropReferences.connect (midnam_connection, invalidator (*this),
2197 boost::bind (&RouteTimeAxisView::drop_instrument_ref, this),
2199 pi->plugin()->UpdateMidnam.connect (midnam_connection, invalidator (*this),
2200 boost::bind (&RouteTimeAxisView::reread_midnam, this),
2207 if (c.type == RouteProcessorChange::MeterPointChange) {
2208 /* nothing to do if only the meter point has changed */
2212 using namespace Menu_Helpers;
2214 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2215 (*i)->valid = false;
2218 setup_processor_menu_and_curves ();
2220 bool deleted_processor_automation = false;
2222 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2224 list<ProcessorAutomationInfo*>::iterator tmp;
2232 processor_automation.erase (i);
2233 deleted_processor_automation = true;
2240 if (deleted_processor_automation && !no_redraw) {
2245 boost::shared_ptr<AutomationLine>
2246 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2248 ProcessorAutomationNode* pan;
2250 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2256 return boost::shared_ptr<AutomationLine>();
2260 RouteTimeAxisView::reset_processor_automation_curves ()
2262 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2268 RouteTimeAxisView::can_edit_name () const
2270 /* we do not allow track name changes if it is record enabled
2272 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2276 return !trk->rec_enable_control()->get_value();
2280 RouteTimeAxisView::blink_rec_display (bool onoff)
2282 RouteUI::blink_rec_display (onoff);
2286 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2288 if (_ignore_set_layer_display) {
2292 if (apply_to_selection) {
2293 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2297 _view->set_layer_display (d);
2300 set_gui_property (X_("layer-display"), d);
2305 RouteTimeAxisView::layer_display () const
2308 return _view->layer_display ();
2311 /* we don't know, since we don't have a _view, so just return something */
2316 RouteTimeAxisView::fast_update ()
2318 gm.get_level_meter().update_meters ();
2322 RouteTimeAxisView::hide_meter ()
2325 gm.get_level_meter().hide_meters ();
2329 RouteTimeAxisView::show_meter ()
2335 RouteTimeAxisView::reset_meter ()
2337 if (UIConfiguration::instance().get_show_track_meters()) {
2338 int meter_width = 3;
2339 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2342 gm.get_level_meter().setup_meters (height - 9, meter_width);
2349 RouteTimeAxisView::clear_meter ()
2351 gm.get_level_meter().clear_meters ();
2355 RouteTimeAxisView::meter_changed ()
2357 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2359 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2362 // reset peak when meter point changes
2363 gm.reset_peak_display();
2367 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2370 if (_route && !no_redraw) {
2376 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2378 using namespace Menu_Helpers;
2380 if (!_underlay_streams.empty()) {
2381 MenuList& parent_items = parent_menu->items();
2382 Menu* gs_menu = manage (new Menu);
2383 gs_menu->set_name ("ArdourContextMenu");
2384 MenuList& gs_items = gs_menu->items();
2386 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2388 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2389 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2390 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2396 RouteTimeAxisView::set_underlay_state()
2398 if (!underlay_xml_node) {
2402 XMLNodeList nlist = underlay_xml_node->children();
2403 XMLNodeConstIterator niter;
2404 XMLNode *child_node;
2406 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2407 child_node = *niter;
2409 if (child_node->name() != "Underlay") {
2413 XMLProperty const * prop = child_node->property ("id");
2415 PBD::ID id (prop->value());
2417 StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2420 add_underlay(v->view(), false);
2429 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2435 RouteTimeAxisView& other = v->trackview();
2437 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2438 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2439 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2440 abort(); /*NOTREACHED*/
2443 _underlay_streams.push_back(v);
2444 other._underlay_mirrors.push_back(this);
2446 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2448 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2450 if (!underlay_xml_node) {
2451 underlay_xml_node = xml_node->add_child("Underlays");
2454 XMLNode* node = underlay_xml_node->add_child("Underlay");
2455 XMLProperty const * prop = node->add_property("id");
2456 prop->set_value(v->trackview().route()->id().to_s());
2463 RouteTimeAxisView::remove_underlay (StreamView* v)
2469 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2470 RouteTimeAxisView& other = v->trackview();
2472 if (it != _underlay_streams.end()) {
2473 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2475 if (gm == other._underlay_mirrors.end()) {
2476 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2477 abort(); /*NOTREACHED*/
2480 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2482 _underlay_streams.erase(it);
2483 other._underlay_mirrors.erase(gm);
2485 if (underlay_xml_node) {
2486 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2492 RouteTimeAxisView::set_button_names ()
2494 if (_route && _route->solo_safe_control()->solo_safe()) {
2495 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2497 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2499 if (Config->get_solo_control_is_listen_control()) {
2500 switch (Config->get_listen_position()) {
2501 case AfterFaderListen:
2502 solo_button->set_text (S_("AfterFader|A"));
2503 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2505 case PreFaderListen:
2506 solo_button->set_text (S_("PreFader|P"));
2507 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2511 solo_button->set_text (S_("Solo|S"));
2512 set_tooltip (*solo_button, _("Solo"));
2514 mute_button->set_text (S_("Mute|M"));
2518 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2520 Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2525 ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2526 if (i != _subplugin_menu_map.end()) {
2534 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2536 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2538 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2542 gain_track.reset (new AutomationTimeAxisView (_session,
2543 _route, _route->amp(), c, param,
2548 _route->amp()->describe_parameter(param)));
2551 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2554 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2558 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2560 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2561 if (!c || ! _route->trim()->active()) {
2565 trim_track.reset (new AutomationTimeAxisView (_session,
2566 _route, _route->trim(), c, param,
2571 _route->trim()->describe_parameter(param)));
2574 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2577 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2581 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2583 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2585 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2589 mute_track.reset (new AutomationTimeAxisView (_session,
2590 _route, _route, c, param,
2595 _route->describe_parameter(param)));
2598 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2601 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2605 void add_region_to_list (RegionView* rv, RegionList* l)
2607 l->push_back (rv->region());
2611 RouteTimeAxisView::combine_regions ()
2613 /* as of may 2011, we do not offer uncombine for MIDI tracks
2616 if (!is_audio_track()) {
2624 RegionList selected_regions;
2625 boost::shared_ptr<Playlist> playlist = track()->playlist();
2627 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2629 if (selected_regions.size() < 2) {
2633 playlist->clear_changes ();
2634 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2636 _session->add_command (new StatefulDiffCommand (playlist));
2637 /* make the new region be selected */
2639 return _view->find_view (compound_region);
2643 RouteTimeAxisView::uncombine_regions ()
2645 /* as of may 2011, we do not offer uncombine for MIDI tracks
2647 if (!is_audio_track()) {
2655 RegionList selected_regions;
2656 boost::shared_ptr<Playlist> playlist = track()->playlist();
2658 /* have to grab selected regions first because the uncombine is going
2659 * to change that in the middle of the list traverse
2662 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2664 playlist->clear_changes ();
2666 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2667 playlist->uncombine (*i);
2670 _session->add_command (new StatefulDiffCommand (playlist));
2674 RouteTimeAxisView::state_id() const
2676 return string_compose ("rtav %1", _route->id().to_s());
2681 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2683 TimeAxisView::remove_child (c);
2685 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2687 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2688 if (i->second == a) {
2689 _automation_tracks.erase (i);
2697 RouteTimeAxisView::color () const
2699 return route_color ();
2703 RouteTimeAxisView::marked_for_display () const
2705 return !_route->presentation_info().hidden();
2709 RouteTimeAxisView::set_marked_for_display (bool yn)
2711 return RouteUI::mark_hidden (!yn);