2 * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
3 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
4 * Copyright (C) 2007-2017 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2007 Doug McLain <doug@nostar.net>
6 * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
7 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
8 * Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
9 * Copyright (C) 2014-2017 Ben Loftis <ben@harrisonconsoles.com>
10 * Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
11 * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
12 * Copyright (C) 2016 Julien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
39 #include <sigc++/bind.h>
41 #include <gtkmm/menu.h>
42 #include <gtkmm/menuitem.h>
43 #include <gtkmm/stock.h>
45 #include "pbd/error.h"
46 #include "pbd/stl_delete.h"
47 #include "pbd/whitespace.h"
48 #include "pbd/memento_command.h"
49 #include "pbd/enumwriter.h"
50 #include "pbd/stateful_diff_command.h"
52 #include "evoral/Parameter.h"
54 #include "ardour/amp.h"
55 #include "ardour/meter.h"
56 #include "ardour/event_type_map.h"
57 #include "ardour/pannable.h"
58 #include "ardour/panner.h"
59 #include "ardour/plugin_insert.h"
60 #include "ardour/processor.h"
61 #include "ardour/profile.h"
62 #include "ardour/route_group.h"
63 #include "ardour/session.h"
64 #include "ardour/session_playlists.h"
65 #include "ardour/track.h"
67 #include "canvas/debug.h"
69 #include "gtkmm2ext/gtk_ui.h"
70 #include "gtkmm2ext/utils.h"
72 #include "widgets/ardour_button.h"
73 #include "widgets/prompter.h"
74 #include "widgets/tooltips.h"
76 #include "ardour_ui.h"
77 #include "audio_streamview.h"
79 #include "enums_convert.h"
80 #include "route_time_axis.h"
81 #include "automation_time_axis.h"
83 #include "gui_thread.h"
84 #include "item_counts.h"
86 #include "paste_context.h"
87 #include "patch_change_widget.h"
88 #include "playlist_selector.h"
89 #include "point_selection.h"
90 #include "public_editor.h"
91 #include "region_view.h"
92 #include "rgb_macros.h"
93 #include "selection.h"
94 #include "streamview.h"
95 #include "ui_config.h"
97 #include "route_group_menu.h"
101 using namespace ARDOUR;
102 using namespace ArdourWidgets;
104 using namespace Gtkmm2ext;
106 using namespace Editing;
110 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
112 , StripableTimeAxisView(ed, sess, canvas)
114 , button_table (3, 3)
115 , route_group_button (S_("RTAV|G"))
116 , playlist_button (S_("RTAV|P"))
117 , automation_button (S_("RTAV|A"))
118 , automation_action_menu (0)
119 , plugins_submenu_item (0)
120 , route_group_menu (0)
121 , playlist_action_menu (0)
122 , gm (sess, true, 75, 14)
123 , _ignore_set_layer_display (false)
124 , pan_automation_item(NULL)
126 number_label.set_name("tracknumber label");
127 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
128 number_label.set_alignment(.5, .5);
129 number_label.set_fallthrough_to_parent (true);
131 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
132 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
134 parameter_changed ("editor-stereo-only-meters");
138 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
140 if (what_changed.contains (ARDOUR::Properties::name)) {
146 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
148 RouteUI::set_route (rt);
149 StripableTimeAxisView::set_stripable (rt);
151 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
152 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
153 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
156 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
159 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
160 gm.get_level_meter().set_no_show_all();
161 gm.get_level_meter().setup_meters(50, meter_width);
162 gm.update_gain_sensitive ();
165 if (get_gui_property ("height", height)) {
168 set_height (preset_height (HeightNormal));
171 if (!_route->is_auditioner()) {
172 if (gui_property ("visible").empty()) {
173 set_gui_property ("visible", true);
176 set_gui_property ("visible", false);
179 timestretch_rect = 0;
182 ignore_toggle = false;
184 route_group_button.set_name ("route button");
185 playlist_button.set_name ("route button");
186 automation_button.set_name ("route button");
188 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
189 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
190 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
194 if (ARDOUR::Profile->get_mixbus()) {
195 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
197 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
200 if (is_midi_track()) {
201 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
202 gm.set_fader_name ("MidiTrackFader");
204 set_tooltip(*rec_enable_button, _("Record"));
205 gm.set_fader_name ("AudioTrackFader");
208 /* set playlist button tip to the current playlist, and make it update when it changes */
209 update_playlist_tip ();
210 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
213 gm.set_fader_name ("AudioBusFader");
214 Gtk::Fixed *blank = manage(new Gtk::Fixed());
215 controls_button_size_group->add_widget(*blank);
216 if (ARDOUR::Profile->get_mixbus() ) {
217 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
219 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
224 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
226 if (!ARDOUR::Profile->get_mixbus()) {
227 controls_meters_size_group->add_widget (gm.get_level_meter());
230 if (_route->is_master()) {
231 route_group_button.set_sensitive(false);
234 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
235 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
236 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
237 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 // mute button is always present, it is used to
245 // force the 'blank' placeholders to the proper size
246 controls_button_size_group->add_widget(*mute_button);
248 if (!_route->is_master()) {
249 if (ARDOUR::Profile->get_mixbus()) {
250 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
255 Gtk::Fixed *blank = manage(new Gtk::Fixed());
256 controls_button_size_group->add_widget(*blank);
257 if (ARDOUR::Profile->get_mixbus()) {
258 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
265 if (ARDOUR::Profile->get_mixbus()) {
266 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
267 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
269 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
270 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
273 set_tooltip(*solo_button,_("Solo"));
274 set_tooltip(*mute_button,_("Mute"));
275 set_tooltip(route_group_button, _("Route Group"));
277 mute_button->set_tweaks(ArdourButton::TrackHeader);
278 solo_button->set_tweaks(ArdourButton::TrackHeader);
279 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
280 playlist_button.set_tweaks(ArdourButton::TrackHeader);
281 automation_button.set_tweaks(ArdourButton::TrackHeader);
282 route_group_button.set_tweaks(ArdourButton::TrackHeader);
284 if (is_midi_track()) {
285 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
287 set_tooltip(automation_button, _("Automation"));
290 update_track_number_visibility();
293 if (ARDOUR::Profile->get_mixbus()) {
294 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
296 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
299 if (is_track() && track()->mode() == ARDOUR::Normal) {
300 if (ARDOUR::Profile->get_mixbus()) {
301 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
303 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
309 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
313 LayerDisplay layer_display;
314 if (get_gui_property ("layer-display", layer_display)) {
315 set_layer_display (layer_display);
318 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
319 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
321 /* pick up the correct freeze state */
326 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
328 PropertyList* plist = new PropertyList();
330 plist->add (ARDOUR::Properties::group_mute, true);
331 plist->add (ARDOUR::Properties::group_solo, true);
333 route_group_menu = new RouteGroupMenu (_session, plist);
335 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
338 RouteTimeAxisView::~RouteTimeAxisView ()
340 cleanup_gui_properties ();
342 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
346 delete playlist_action_menu;
347 delete automation_action_menu;
352 _automation_tracks.clear ();
354 delete route_group_menu;
355 CatchDeletion (this);
359 RouteTimeAxisView::name() const
362 return _route->name();
368 RouteTimeAxisView::post_construct ()
370 /* map current state of the route */
372 update_diskstream_display ();
373 setup_processor_menu_and_curves ();
374 reset_processor_automation_curves ();
377 /** Set up the processor menu for the current set of processors, and
378 * display automation curves for any parameters which have data.
381 RouteTimeAxisView::setup_processor_menu_and_curves ()
383 _subplugin_menu_map.clear ();
384 subplugin_menu.items().clear ();
385 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
386 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
390 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
392 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
393 if (_route->route_group()) {
394 _route->route_group()->remove (_route);
400 r.push_back (route ());
402 route_group_menu->build (r);
403 if (ev->button == 1) {
404 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
408 route_group_menu->menu()->popup (ev->button, ev->time);
415 RouteTimeAxisView::playlist_changed ()
421 RouteTimeAxisView::label_view ()
423 string x = _route->name ();
424 if (x != name_label.get_text ()) {
425 name_label.set_text (x);
427 const int64_t track_number = _route->track_number ();
428 if (track_number == 0) {
429 number_label.set_text ("");
431 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
436 RouteTimeAxisView::update_track_number_visibility ()
439 bool show_label = _session->config.get_track_name_number();
441 if (_route && _route->is_master()) {
445 if (number_label.get_parent()) {
446 controls_table.remove (number_label);
449 if (ARDOUR::Profile->get_mixbus()) {
450 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
452 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
454 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
455 // except the width of the number label is subtracted from the name-hbox, so we
456 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
457 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
459 number_label.set_size_request(tnw, -1);
460 number_label.show ();
462 number_label.hide ();
467 RouteTimeAxisView::parameter_changed (string const & p)
469 if (p == "track-name-number") {
470 update_track_number_visibility();
471 } else if (p == "editor-stereo-only-meters") {
472 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
473 gm.get_level_meter().set_max_audio_meter_count (2);
475 gm.get_level_meter().set_max_audio_meter_count (0);
481 RouteTimeAxisView::take_name_changed (void *src)
489 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
491 if (ev->button != 1) {
495 build_playlist_menu ();
496 conditionally_add_to_selection ();
497 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
503 RouteTimeAxisView::automation_click (GdkEventButton *ev)
505 if (ev->button != 1) {
509 conditionally_add_to_selection ();
510 build_automation_action_menu (false);
511 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
517 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
519 using namespace Menu_Helpers;
521 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
522 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
525 detach_menu (subplugin_menu);
527 _main_automation_menu_map.clear ();
528 delete automation_action_menu;
529 automation_action_menu = new Menu;
531 MenuList& items = automation_action_menu->items();
533 automation_action_menu->set_name ("ArdourContextMenu");
535 items.push_back (MenuElem (_("Show All Automation"),
536 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
538 items.push_back (MenuElem (_("Show Existing Automation"),
539 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
541 items.push_back (MenuElem (_("Hide All Automation"),
542 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
544 /* Attach the plugin submenu. It may have previously been used elsewhere,
545 so it was detached above
548 bool single_track_selected = (!for_selection || _editor.get_selection().tracks.size() == 1);
550 if (!subplugin_menu.items().empty()) {
551 items.push_back (SeparatorElem ());
552 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
553 items.back().set_sensitive (single_track_selected);
556 /* Add any route automation */
559 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
560 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 gain_automation_item->set_active (single_track_selected &&
562 string_to<bool>(gain_track->gui_property ("visible")));
564 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
568 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
569 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
570 trim_automation_item->set_active (single_track_selected &&
571 string_to<bool>(trim_track->gui_property ("visible")));
573 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
577 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
578 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
579 mute_automation_item->set_active (single_track_selected &&
580 string_to<bool>(mute_track->gui_property ("visible")));
582 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
585 if (!pan_tracks.empty() && !ARDOUR::Profile->get_mixbus()) {
586 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
587 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
588 pan_automation_item->set_active (single_track_selected &&
589 string_to<bool>(pan_tracks.front()->gui_property ("visible")));
591 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
592 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
593 _main_automation_menu_map[*p] = pan_automation_item;
599 RouteTimeAxisView::build_display_menu ()
601 using namespace Menu_Helpers;
605 TimeAxisView::build_display_menu ();
607 /* now fill it with our stuff */
609 MenuList& items = display_menu->items();
610 display_menu->set_name ("ArdourContextMenu");
612 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
614 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
616 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
618 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
620 items.push_back (SeparatorElem());
623 detach_menu (*_size_menu);
626 items.push_back (MenuElem (_("Height"), *_size_menu));
627 items.push_back (SeparatorElem());
629 // Hook for derived classes to add type specific stuff
630 append_extra_display_menu_items ();
634 Menu* layers_menu = manage (new Menu);
635 MenuList &layers_items = layers_menu->items();
636 layers_menu->set_name("ArdourContextMenu");
638 RadioMenuItem::Group layers_group;
640 /* Find out how many overlaid/stacked tracks we have in the selection */
644 int unchangeable = 0;
645 TrackSelection const & s = _editor.get_selection().tracks;
647 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
648 StreamView* v = (*i)->view ();
653 if (v->can_change_layer_display()) {
654 switch (v->layer_display ()) {
668 /* We're not connecting to signal_toggled() here; in the case where these two items are
669 set to be in the `inconsistent' state, it seems that one or other will end up active
670 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
671 select the active one, no toggled signal is emitted so nothing happens.
674 _ignore_set_layer_display = true;
676 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
677 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
678 i->set_active (overlaid != 0 && stacked == 0);
679 i->set_inconsistent (overlaid != 0 && stacked != 0);
680 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
683 i->set_sensitive (false);
686 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
687 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
688 i->set_active (overlaid == 0 && stacked != 0);
689 i->set_inconsistent (overlaid != 0 && stacked != 0);
690 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
693 i->set_sensitive (false);
696 _ignore_set_layer_display = false;
698 items.push_back (MenuElem (_("Layers"), *layers_menu));
700 Menu* alignment_menu = manage (new Menu);
701 MenuList& alignment_items = alignment_menu->items();
702 alignment_menu->set_name ("ArdourContextMenu");
704 RadioMenuItem::Group align_group;
706 /* Same verbose hacks as for the layering options above */
712 boost::shared_ptr<Track> first_track;
714 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
715 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
716 if (!r || !r->is_track ()) {
721 first_track = r->track();
724 switch (r->track()->alignment_choice()) {
728 switch (r->track()->alignment_style()) {
729 case ExistingMaterial:
737 case UseExistingMaterial:
753 inconsistent = false;
760 if (!inconsistent && first_track) {
762 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
763 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
764 i->set_active (automatic != 0 && existing == 0 && capture == 0);
765 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
767 switch (first_track->alignment_choice()) {
769 switch (first_track->alignment_style()) {
770 case ExistingMaterial:
771 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
774 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
782 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
783 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
784 i->set_active (existing != 0 && capture == 0 && automatic == 0);
785 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
787 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
788 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
789 i->set_active (existing == 0 && capture != 0 && automatic == 0);
790 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
792 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
796 delete alignment_menu;
799 items.push_back (SeparatorElem());
801 build_playlist_menu ();
802 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
803 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
806 if (!is_midi_track () && _route->the_instrument ()) {
808 items.push_back (MenuElem (_("Patch Selector..."),
809 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
810 items.push_back (SeparatorElem());
813 route_group_menu->detach ();
816 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
817 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
819 r.push_back (rtv->route ());
824 r.push_back (route ());
827 if (!_route->is_master()) {
828 route_group_menu->build (r);
829 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
832 build_automation_action_menu (true);
833 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
835 items.push_back (SeparatorElem());
839 TrackSelection const & s = _editor.get_selection().tracks;
840 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
841 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
846 if (r->route()->active()) {
853 items.push_back (CheckMenuElem (_("Active")));
854 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
855 bool click_sets_active = true;
856 if (active > 0 && inactive == 0) {
857 i->set_active (true);
858 click_sets_active = false;
859 } else if (active > 0 && inactive > 0) {
860 i->set_inconsistent (true);
862 i->set_sensitive(! _session->transport_rolling());
863 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
865 items.push_back (SeparatorElem());
866 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
867 if (_route && !_route->is_master()) {
868 items.push_back (SeparatorElem());
869 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
871 items.push_back (SeparatorElem());
872 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
876 RouteTimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
878 TimeAxisView::show_timestretch (start, end, layers, layer);
888 /* check that the time selection was made in our route, or our route group.
889 remember that route_group() == 0 implies the route is *not* in a edit group.
892 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
893 /* this doesn't apply to us */
897 /* ignore it if our edit group is not active */
899 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
904 if (timestretch_rect == 0) {
905 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
906 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
907 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
910 timestretch_rect->show ();
911 timestretch_rect->raise_to_top ();
913 double const x1 = start / _editor.get_current_zoom();
914 double const x2 = (end - 1) / _editor.get_current_zoom();
916 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
917 x2, current_height() * (layers - layer) / layers));
921 RouteTimeAxisView::hide_timestretch ()
923 TimeAxisView::hide_timestretch ();
925 if (timestretch_rect) {
926 timestretch_rect->hide ();
931 RouteTimeAxisView::show_selection (TimeSelection& ts)
935 /* ignore it if our edit group is not active or if the selection was started
936 in some other track or route group (remember that route_group() == 0 means
937 that the track is not in an route group).
940 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
941 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
947 TimeAxisView::show_selection (ts);
951 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
954 bool height_changed = (height == 0) || (h != height);
957 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
960 gm.get_level_meter().setup_meters (gmlen, meter_width);
962 TimeAxisView::set_height (h, m);
965 _view->set_height ((double) current_height());
968 if (height >= preset_height (HeightNormal)) {
972 gm.get_gain_slider().show();
974 if (!_route || _route->is_monitor()) {
979 if (rec_enable_button)
980 rec_enable_button->show();
982 route_group_button.show();
983 automation_button.show();
985 if (is_track() && track()->mode() == ARDOUR::Normal) {
986 playlist_button.show();
993 gm.get_gain_slider().hide();
995 if (!_route || _route->is_monitor()) {
1000 if (rec_enable_button)
1001 rec_enable_button->show();
1003 route_group_button.hide ();
1004 automation_button.hide ();
1006 if (is_track() && track()->mode() == ARDOUR::Normal) {
1007 playlist_button.hide ();
1012 if (height_changed && !no_redraw) {
1013 /* only emit the signal if the height really changed */
1019 RouteTimeAxisView::route_color_changed ()
1021 using namespace ARDOUR_UI_UTILS;
1023 _view->apply_color (color(), StreamView::RegionColor);
1025 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1029 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1032 _view->set_samples_per_pixel (fpp);
1035 StripableTimeAxisView::set_samples_per_pixel (fpp);
1039 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1041 if (!mitem->get_active()) {
1042 /* this is one of the two calls made when these radio menu items change status. this one
1043 is for the item that became inactive, and we want to ignore it.
1048 if (apply_to_selection) {
1049 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1052 track()->set_align_choice (choice);
1058 RouteTimeAxisView::rename_current_playlist ()
1060 Prompter prompter (true);
1063 boost::shared_ptr<Track> tr = track();
1064 if (!tr || tr->destructive()) {
1068 boost::shared_ptr<Playlist> pl = tr->playlist();
1073 prompter.set_title (_("Rename Playlist"));
1074 prompter.set_prompt (_("New name for playlist:"));
1075 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1076 prompter.set_initial_text (pl->name());
1077 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1080 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1083 prompter.get_result (name);
1084 if (name.length()) {
1085 if (_session->playlists()->by_name (name)) {
1086 MessageDialog msg (_("Given playlist name is not unique."));
1088 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1090 pl->set_name (name);
1098 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1100 std::string ret (basename);
1102 std::string const group_string = "." + route_group()->name() + ".";
1104 // iterate through all playlists
1106 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1107 std::string tmp = (*i)->name();
1109 std::string::size_type idx = tmp.find(group_string);
1110 // find those which belong to this group
1111 if (idx != string::npos) {
1112 tmp = tmp.substr(idx + group_string.length());
1114 // and find the largest current number
1116 if (x > maxnumber) {
1125 snprintf (buf, sizeof(buf), "%d", maxnumber);
1127 ret = this->name() + "." + route_group()->name () + "." + buf;
1133 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1137 boost::shared_ptr<Track> tr = track ();
1138 if (!tr || tr->destructive()) {
1142 boost::shared_ptr<const Playlist> pl = tr->playlist();
1149 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1150 name = resolve_new_group_playlist_name(name,playlists_before_op);
1153 while (_session->playlists()->by_name(name)) {
1154 name = Playlist::bump_name (name, *_session);
1158 // TODO: The prompter "new" button should be de-activated if the user
1159 // specifies a playlist name which already exists in the session.
1161 Prompter prompter (true);
1164 prompter.set_title (_("New Copy Playlist"));
1165 prompter.set_prompt (_("Name for playlist copy:"));
1167 prompter.set_title (_("New Playlist"));
1168 prompter.set_prompt (_("Name for new playlist:"));
1170 prompter.set_initial_text (name);
1171 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1172 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1173 prompter.show_all ();
1176 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1179 prompter.get_result (name);
1180 if (name.length()) {
1181 if (_session->playlists()->by_name (name)) {
1182 MessageDialog msg (_("Given playlist name is not unique."));
1184 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1192 if (name.length()) {
1194 tr->use_copy_playlist ();
1196 tr->use_default_new_playlist ();
1198 tr->playlist()->set_name (name);
1203 RouteTimeAxisView::clear_playlist ()
1205 boost::shared_ptr<Track> tr = track ();
1206 if (!tr || tr->destructive()) {
1210 boost::shared_ptr<Playlist> pl = tr->playlist();
1215 _editor.clear_playlist (pl);
1219 RouteTimeAxisView::speed_changed ()
1221 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1225 RouteTimeAxisView::update_diskstream_display ()
1235 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1237 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1239 /* special case: select/deselect all tracks */
1241 _editor.begin_reversible_selection_op (X_("Selection Click"));
1243 if (_editor.get_selection().selected (this)) {
1244 _editor.get_selection().clear_tracks ();
1246 _editor.select_all_tracks ();
1249 _editor.commit_reversible_selection_op ();
1254 _editor.begin_reversible_selection_op (X_("Selection Click"));
1256 switch (ArdourKeyboard::selection_type (ev->state)) {
1257 case Selection::Toggle:
1258 _editor.get_selection().toggle (this);
1261 case Selection::Set:
1262 _editor.get_selection().set (this);
1265 case Selection::Extend:
1266 _editor.extend_selection_to_track (*this);
1269 case Selection::Add:
1270 _editor.get_selection().add (this);
1274 _editor.commit_reversible_selection_op ();
1276 _editor.set_selected_mixer_strip (*this);
1280 RouteTimeAxisView::set_selected_points (PointSelection& points)
1282 StripableTimeAxisView::set_selected_points (points);
1283 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1285 asv->set_selected_points (points);
1290 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1293 _view->set_selected_regionviews (regions);
1297 /** Add the selectable things that we have to a list.
1298 * @param results List to add things to.
1301 RouteTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1303 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1304 _view->get_selectables (start, end, top, bot, results, within);
1307 /* pick up visible automation tracks */
1308 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1309 if (!(*i)->hidden()) {
1310 (*i)->get_selectables (start, end, top, bot, results, within);
1316 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1319 _view->get_inverted_selectables (sel, results);
1321 StripableTimeAxisView::get_inverted_selectables (sel, results);
1325 RouteTimeAxisView::route_group () const
1327 return _route->route_group();
1330 boost::shared_ptr<Playlist>
1331 RouteTimeAxisView::playlist () const
1333 boost::shared_ptr<Track> tr;
1335 if ((tr = track()) != 0) {
1336 return tr->playlist();
1338 return boost::shared_ptr<Playlist> ();
1343 RouteTimeAxisView::name_entry_changed (string const& str)
1345 if (str == _route->name()) {
1351 strip_whitespace_edges (x);
1357 if (_session->route_name_internal (x)) {
1358 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1360 } else if (RouteUI::verify_new_route_name (x)) {
1361 _route->set_name (x);
1368 boost::shared_ptr<Region>
1369 RouteTimeAxisView::find_next_region (samplepos_t pos, RegionPoint point, int32_t dir)
1371 boost::shared_ptr<Playlist> pl = playlist ();
1374 return pl->find_next_region (pos, point, dir);
1377 return boost::shared_ptr<Region> ();
1381 RouteTimeAxisView::find_next_region_boundary (samplepos_t pos, int32_t dir)
1383 boost::shared_ptr<Playlist> pl = playlist ();
1386 return pl->find_next_region_boundary (pos, dir);
1393 RouteTimeAxisView::fade_range (TimeSelection& selection)
1395 boost::shared_ptr<Playlist> what_we_got;
1396 boost::shared_ptr<Track> tr = track ();
1397 boost::shared_ptr<Playlist> playlist;
1400 /* route is a bus, not a track */
1404 playlist = tr->playlist();
1406 TimeSelection time (selection);
1408 playlist->clear_changes ();
1409 playlist->clear_owned_changes ();
1411 playlist->fade_range (time);
1413 vector<Command*> cmds;
1414 playlist->rdiff (cmds);
1415 _session->add_commands (cmds);
1416 _session->add_command (new StatefulDiffCommand (playlist));
1421 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1423 boost::shared_ptr<Playlist> what_we_got;
1424 boost::shared_ptr<Track> tr = track ();
1425 boost::shared_ptr<Playlist> playlist;
1428 /* route is a bus, not a track */
1432 playlist = tr->playlist();
1434 TimeSelection time (selection.time);
1436 playlist->clear_changes ();
1437 playlist->clear_owned_changes ();
1441 if (playlist->cut (time) != 0) {
1442 if (Config->get_edit_mode() == Ripple) {
1443 playlist->ripple(time.start(), -time.length(), NULL);
1445 // no need to exclude any regions from rippling here
1447 vector<Command*> cmds;
1448 playlist->rdiff (cmds);
1449 _session->add_commands (cmds);
1451 _session->add_command (new StatefulDiffCommand (playlist));
1456 if ((what_we_got = playlist->cut (time)) != 0) {
1457 _editor.get_cut_buffer().add (what_we_got);
1458 if (Config->get_edit_mode() == Ripple) {
1459 playlist->ripple(time.start(), -time.length(), NULL);
1461 // no need to exclude any regions from rippling here
1463 vector<Command*> cmds;
1464 playlist->rdiff (cmds);
1465 _session->add_commands (cmds);
1467 _session->add_command (new StatefulDiffCommand (playlist));
1471 if ((what_we_got = playlist->copy (time)) != 0) {
1472 _editor.get_cut_buffer().add (what_we_got);
1477 if ((what_we_got = playlist->cut (time)) != 0) {
1478 if (Config->get_edit_mode() == Ripple) {
1479 playlist->ripple(time.start(), -time.length(), NULL);
1481 // no need to exclude any regions from rippling here
1483 vector<Command*> cmds;
1484 playlist->rdiff (cmds);
1485 _session->add_commands (cmds);
1486 _session->add_command (new StatefulDiffCommand (playlist));
1487 what_we_got->release ();
1494 RouteTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1500 boost::shared_ptr<Playlist> pl = playlist ();
1501 const ARDOUR::DataType type = pl->data_type();
1502 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1504 if (p == selection.playlists.end()) {
1507 ctx.counts.increase_n_playlists(type);
1509 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1511 /* add multi-paste offset if applicable */
1512 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent();
1513 const samplecnt_t duration = extent.second - extent.first;
1514 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1516 pl->clear_changes ();
1517 pl->clear_owned_changes ();
1518 if (Config->get_edit_mode() == Ripple) {
1519 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent_with_endspace();
1520 samplecnt_t amount = extent.second - extent.first;
1521 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1523 pl->paste (*p, pos, ctx.times, sub_num);
1525 vector<Command*> cmds;
1527 _session->add_commands (cmds);
1529 _session->add_command (new StatefulDiffCommand (pl));
1535 struct PlaylistSorter {
1536 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1537 return a->sort_id() < b->sort_id();
1542 RouteTimeAxisView::build_playlist_menu ()
1544 using namespace Menu_Helpers;
1550 delete playlist_action_menu;
1551 playlist_action_menu = new Menu;
1552 playlist_action_menu->set_name ("ArdourContextMenu");
1554 MenuList& playlist_items = playlist_action_menu->items();
1555 playlist_action_menu->set_name ("ArdourContextMenu");
1556 playlist_items.clear();
1558 RadioMenuItem::Group playlist_group;
1559 boost::shared_ptr<Track> tr = track ();
1561 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists()->playlists_for_track (tr);
1563 /* sort the playlists */
1565 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1567 /* add the playlists to the menu */
1568 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1569 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1570 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1571 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1573 if (tr->playlist()->id() == (*i)->id()) {
1578 playlist_items.push_back (SeparatorElem());
1579 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1580 playlist_items.push_back (SeparatorElem());
1582 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1583 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1584 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1587 // Use a label which tells the user what is happening
1588 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1589 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1593 playlist_items.push_back (SeparatorElem());
1594 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1595 playlist_items.push_back (SeparatorElem());
1597 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1601 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1603 assert (is_track());
1605 // exit if we were triggered by deactivating the old playlist
1606 if (!item->get_active()) {
1610 boost::shared_ptr<Playlist> pl (wpl.lock());
1616 if (track()->playlist() == pl) {
1617 // exit when use_playlist is called by the creation of the playlist menu
1618 // or the playlist choice is unchanged
1622 track()->use_playlist (track()->data_type(), pl);
1624 RouteGroup* rg = route_group();
1626 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1627 std::string group_string = "." + rg->name() + ".";
1629 std::string take_name = pl->name();
1630 std::string::size_type idx = take_name.find(group_string);
1632 if (idx == std::string::npos)
1635 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1637 boost::shared_ptr<RouteList> rl (rg->route_list());
1639 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1640 if ((*i) == this->route()) {
1644 std::string playlist_name = (*i)->name()+group_string+take_name;
1646 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1651 if (track->freeze_state() == Track::Frozen) {
1652 /* Don't change playlists of frozen tracks */
1656 boost::shared_ptr<Playlist> ipl = session()->playlists()->by_name(playlist_name);
1658 // No playlist for this track for this take yet, make it
1659 track->use_default_new_playlist();
1660 track->playlist()->set_name(playlist_name);
1662 track->use_playlist(track->data_type(), ipl);
1669 RouteTimeAxisView::update_playlist_tip ()
1671 RouteGroup* rg = route_group ();
1672 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1673 string group_string = "." + rg->name() + ".";
1675 string take_name = track()->playlist()->name();
1676 string::size_type idx = take_name.find(group_string);
1678 if (idx != string::npos) {
1679 /* find the bit containing the take number / name */
1680 take_name = take_name.substr (idx + group_string.length());
1682 /* set the playlist button tooltip to the take name */
1685 string_compose(_("Take: %1.%2"),
1686 Gtkmm2ext::markup_escape_text (rg->name()),
1687 Gtkmm2ext::markup_escape_text (take_name))
1694 /* set the playlist button tooltip to the playlist name */
1695 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1700 RouteTimeAxisView::show_playlist_selector ()
1702 _editor.playlist_selector().show_for (this);
1706 RouteTimeAxisView::map_frozen ()
1712 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1714 switch (track()->freeze_state()) {
1716 playlist_button.set_sensitive (false);
1719 playlist_button.set_sensitive (true);
1722 RouteUI::map_frozen ();
1726 RouteTimeAxisView::color_handler ()
1728 //case cTimeStretchOutline:
1729 if (timestretch_rect) {
1730 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1732 //case cTimeStretchFill:
1733 if (timestretch_rect) {
1734 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1740 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1741 * Will add track if necessary.
1744 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1746 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1747 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1750 /* it doesn't exist yet, so we don't care about the button state: just add it */
1751 create_automation_child (param, true);
1754 bool yn = menu->get_active();
1755 bool changed = false;
1757 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1759 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1760 will have done that for us.
1763 if (changed && !no_redraw) {
1771 RouteTimeAxisView::update_pan_track_visibility ()
1773 bool const showit = pan_automation_item->get_active();
1774 bool changed = false;
1776 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1777 if ((*i)->set_marked_for_display (showit)) {
1783 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1788 RouteTimeAxisView::ensure_pan_views (bool show)
1790 bool changed = false;
1791 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1793 (*i)->set_marked_for_display (false);
1796 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1800 if (!_route->panner()) {
1804 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1805 set<Evoral::Parameter>::iterator p;
1807 for (p = params.begin(); p != params.end(); ++p) {
1808 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1810 if (pan_control->parameter().type() == NullAutomation) {
1811 error << "Pan control has NULL automation type!" << endmsg;
1815 if (automation_child (pan_control->parameter ()).get () == 0) {
1817 /* we don't already have an AutomationTimeAxisView for this parameter */
1819 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1821 boost::shared_ptr<AutomationTimeAxisView> t (
1822 new AutomationTimeAxisView (_session,
1826 pan_control->parameter (),
1834 pan_tracks.push_back (t);
1835 add_automation_child (*p, t, show);
1837 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1844 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1846 if (apply_to_selection) {
1847 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1851 StripableTimeAxisView::show_all_automation ();
1853 /* Show processor automation */
1855 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1856 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1857 if ((*ii)->view == 0) {
1858 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1861 (*ii)->menu_item->set_active (true);
1874 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1876 if (apply_to_selection) {
1877 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1881 StripableTimeAxisView::show_existing_automation ();
1883 /* Show processor automation */
1884 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1885 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1886 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1887 (*ii)->menu_item->set_active (true);
1898 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1900 if (apply_to_selection) {
1901 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1904 StripableTimeAxisView::hide_all_automation ();
1906 /* Hide processor automation */
1907 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1908 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1909 (*ii)->menu_item->set_active (false);
1919 RouteTimeAxisView::region_view_added (RegionView* rv)
1921 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1922 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1923 boost::shared_ptr<AutomationTimeAxisView> atv;
1925 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1930 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1931 (*i)->add_ghost(rv);
1935 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1937 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1943 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1945 parent.remove_processor_automation_node (this);
1949 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1952 remove_child (pan->view);
1956 RouteTimeAxisView::ProcessorAutomationNode*
1957 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1959 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1961 if ((*i)->processor == processor) {
1963 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1964 if ((*ii)->what == what) {
1974 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1976 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1979 ProcessorAutomationNode* pan;
1981 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1982 /* session state may never have been saved with new plugin */
1983 error << _("programming error: ")
1984 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1985 processor->name(), what.type(), (int) what.channel(), what.id() )
1987 abort(); /*NOTREACHED*/
1995 boost::shared_ptr<AutomationControl> control
1996 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1998 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1999 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2000 _editor, *this, false, parent_canvas,
2001 processor->describe_parameter (what), processor->name()));
2003 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2005 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2008 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2013 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2016 pan->menu_item->set_active (false);
2025 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2027 boost::shared_ptr<Processor> processor (p.lock ());
2029 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2030 /* The Amp processor is a special case and is dealt with separately */
2034 set<Evoral::Parameter> existing;
2036 processor->what_has_data (existing);
2038 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2040 Evoral::Parameter param (*i);
2041 boost::shared_ptr<AutomationLine> al;
2043 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2044 #warning NOT REACHED -- bug?
2047 add_processor_automation_curve (processor, param);
2053 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2055 boost::shared_ptr<Processor> processor (p.lock ());
2057 if (!processor || !processor->display_to_user ()) {
2061 /* we use this override to veto the Amp processor from the plugin menu,
2062 as its automation lane can be accessed using the special "Fader" menu
2066 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2070 using namespace Menu_Helpers;
2071 ProcessorAutomationInfo *rai;
2072 list<ProcessorAutomationInfo*>::iterator x;
2074 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2076 if (automatable.empty()) {
2080 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2081 if ((*x)->processor == processor) {
2086 if (x == processor_automation.end()) {
2087 rai = new ProcessorAutomationInfo (processor);
2088 processor_automation.push_back (rai);
2093 /* any older menu was deleted at the top of processors_changed()
2094 when we cleared the subplugin menu.
2097 rai->menu = manage (new Menu);
2098 MenuList& items = rai->menu->items();
2099 rai->menu->set_name ("ArdourContextMenu");
2103 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2105 ProcessorAutomationNode* pan;
2106 Gtk::CheckMenuItem* mitem;
2108 string name = processor->describe_parameter (*i);
2110 if (name == X_("hidden")) {
2114 items.push_back (CheckMenuElem (name));
2115 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2117 _subplugin_menu_map[*i] = mitem;
2119 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2123 pan = new ProcessorAutomationNode (*i, mitem, *this);
2125 rai->lines.push_back (pan);
2129 pan->menu_item = mitem;
2133 boost::shared_ptr<AutomationTimeAxisView> atav = automation_child (*i);
2135 if (atav && atav->get_gui_property ("visible", visible)) {
2136 mitem->set_active(true);
2139 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2142 if (items.size() == 0) {
2146 /* add the menu for this processor, because the subplugin
2147 menu is always cleared at the top of processors_changed().
2148 this is the result of some poor design in gtkmm and/or
2152 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2157 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2158 RouteTimeAxisView::ProcessorAutomationNode* pan)
2160 bool showit = pan->menu_item->get_active();
2161 bool redraw = false;
2163 if (pan->view == 0 && showit) {
2164 add_processor_automation_curve (rai->processor, pan->what);
2168 if (pan->view && pan->view->set_marked_for_display (showit)) {
2172 if (redraw && !no_redraw) {
2178 RouteTimeAxisView::reread_midnam ()
2180 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument ());
2182 bool rv = pi->plugin ()->read_midnam();
2184 if (rv && patch_change_dialog ()) {
2185 patch_change_dialog ()->refresh ();
2190 RouteTimeAxisView::drop_instrument_ref ()
2192 midnam_connection.drop_connections ();
2196 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2199 boost::shared_ptr<Processor> the_instrument (_route->the_instrument());
2200 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (the_instrument);
2201 if (pi && pi->plugin ()->has_midnam ()) {
2202 midnam_connection.drop_connections ();
2203 the_instrument->DropReferences.connect (midnam_connection, invalidator (*this),
2204 boost::bind (&RouteTimeAxisView::drop_instrument_ref, this),
2206 pi->plugin()->UpdateMidnam.connect (midnam_connection, invalidator (*this),
2207 boost::bind (&RouteTimeAxisView::reread_midnam, this),
2214 if (c.type == RouteProcessorChange::MeterPointChange) {
2215 /* nothing to do if only the meter point has changed */
2219 using namespace Menu_Helpers;
2221 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2222 (*i)->valid = false;
2225 setup_processor_menu_and_curves ();
2227 bool deleted_processor_automation = false;
2229 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2231 list<ProcessorAutomationInfo*>::iterator tmp;
2239 processor_automation.erase (i);
2240 deleted_processor_automation = true;
2247 if (deleted_processor_automation && !no_redraw) {
2252 boost::shared_ptr<AutomationLine>
2253 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2255 ProcessorAutomationNode* pan;
2257 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2263 return boost::shared_ptr<AutomationLine>();
2267 RouteTimeAxisView::reset_processor_automation_curves ()
2269 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2275 RouteTimeAxisView::can_edit_name () const
2277 /* we do not allow track name changes if it is record enabled
2279 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2283 return !trk->rec_enable_control()->get_value();
2287 RouteTimeAxisView::blink_rec_display (bool onoff)
2289 RouteUI::blink_rec_display (onoff);
2293 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2295 if (_ignore_set_layer_display) {
2299 if (apply_to_selection) {
2300 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2304 _view->set_layer_display (d);
2307 set_gui_property (X_("layer-display"), d);
2312 RouteTimeAxisView::layer_display () const
2315 return _view->layer_display ();
2318 /* we don't know, since we don't have a _view, so just return something */
2323 RouteTimeAxisView::fast_update ()
2325 gm.get_level_meter().update_meters ();
2329 RouteTimeAxisView::hide_meter ()
2332 gm.get_level_meter().hide_meters ();
2336 RouteTimeAxisView::show_meter ()
2342 RouteTimeAxisView::reset_meter ()
2344 if (UIConfiguration::instance().get_show_track_meters()) {
2345 int meter_width = 3;
2346 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2349 gm.get_level_meter().setup_meters (height - 9, meter_width);
2356 RouteTimeAxisView::clear_meter ()
2358 gm.get_level_meter().clear_meters ();
2362 RouteTimeAxisView::meter_changed ()
2364 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2366 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2369 // reset peak when meter point changes
2370 gm.reset_peak_display();
2374 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2377 if (_route && !no_redraw) {
2383 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2385 using namespace Menu_Helpers;
2387 if (!_underlay_streams.empty()) {
2388 MenuList& parent_items = parent_menu->items();
2389 Menu* gs_menu = manage (new Menu);
2390 gs_menu->set_name ("ArdourContextMenu");
2391 MenuList& gs_items = gs_menu->items();
2393 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2395 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2396 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2397 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2403 RouteTimeAxisView::set_underlay_state()
2405 if (!underlay_xml_node) {
2409 XMLNodeList nlist = underlay_xml_node->children();
2410 XMLNodeConstIterator niter;
2411 XMLNode *child_node;
2413 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2414 child_node = *niter;
2416 if (child_node->name() != "Underlay") {
2420 XMLProperty const * prop = child_node->property ("id");
2422 PBD::ID id (prop->value());
2424 StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2427 add_underlay(v->view(), false);
2436 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2442 RouteTimeAxisView& other = v->trackview();
2444 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2445 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2446 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2447 abort(); /*NOTREACHED*/
2450 _underlay_streams.push_back(v);
2451 other._underlay_mirrors.push_back(this);
2453 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2455 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2457 if (!underlay_xml_node) {
2458 underlay_xml_node = xml_node->add_child("Underlays");
2461 XMLNode* node = underlay_xml_node->add_child("Underlay");
2462 XMLProperty const * prop = node->add_property("id");
2463 prop->set_value(v->trackview().route()->id().to_s());
2470 RouteTimeAxisView::remove_underlay (StreamView* v)
2476 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2477 RouteTimeAxisView& other = v->trackview();
2479 if (it != _underlay_streams.end()) {
2480 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2482 if (gm == other._underlay_mirrors.end()) {
2483 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2484 abort(); /*NOTREACHED*/
2487 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2489 _underlay_streams.erase(it);
2490 other._underlay_mirrors.erase(gm);
2492 if (underlay_xml_node) {
2493 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2499 RouteTimeAxisView::set_button_names ()
2501 if (_route && _route->solo_safe_control()->solo_safe()) {
2502 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2504 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2506 if (Config->get_solo_control_is_listen_control()) {
2507 switch (Config->get_listen_position()) {
2508 case AfterFaderListen:
2509 solo_button->set_text (S_("AfterFader|A"));
2510 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2512 case PreFaderListen:
2513 solo_button->set_text (S_("PreFader|P"));
2514 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2518 solo_button->set_text (S_("Solo|S"));
2519 set_tooltip (*solo_button, _("Solo"));
2521 mute_button->set_text (S_("Mute|M"));
2525 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2527 Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2532 ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2533 if (i != _subplugin_menu_map.end()) {
2541 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2543 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2545 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2549 gain_track.reset (new AutomationTimeAxisView (_session,
2550 _route, _route->amp(), c, param,
2555 _route->amp()->describe_parameter(param)));
2558 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2561 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2565 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2567 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2568 if (!c || ! _route->trim()->active()) {
2572 trim_track.reset (new AutomationTimeAxisView (_session,
2573 _route, _route->trim(), c, param,
2578 _route->trim()->describe_parameter(param)));
2581 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2584 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2588 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2590 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2592 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2596 mute_track.reset (new AutomationTimeAxisView (_session,
2597 _route, _route, c, param,
2602 _route->describe_parameter(param)));
2605 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2608 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2612 void add_region_to_list (RegionView* rv, RegionList* l)
2614 l->push_back (rv->region());
2618 RouteTimeAxisView::combine_regions ()
2620 /* as of may 2011, we do not offer uncombine for MIDI tracks
2623 if (!is_audio_track()) {
2631 RegionList selected_regions;
2632 boost::shared_ptr<Playlist> playlist = track()->playlist();
2634 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2636 if (selected_regions.size() < 2) {
2640 playlist->clear_changes ();
2641 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2643 _session->add_command (new StatefulDiffCommand (playlist));
2644 /* make the new region be selected */
2646 return _view->find_view (compound_region);
2650 RouteTimeAxisView::uncombine_regions ()
2652 /* as of may 2011, we do not offer uncombine for MIDI tracks
2654 if (!is_audio_track()) {
2662 RegionList selected_regions;
2663 boost::shared_ptr<Playlist> playlist = track()->playlist();
2665 /* have to grab selected regions first because the uncombine is going
2666 * to change that in the middle of the list traverse
2669 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2671 playlist->clear_changes ();
2673 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2674 playlist->uncombine (*i);
2677 _session->add_command (new StatefulDiffCommand (playlist));
2681 RouteTimeAxisView::state_id() const
2683 return string_compose ("rtav %1", _route->id().to_s());
2688 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2690 TimeAxisView::remove_child (c);
2692 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2694 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2695 if (i->second == a) {
2696 _automation_tracks.erase (i);
2704 RouteTimeAxisView::color () const
2706 return route_color ();
2710 RouteTimeAxisView::marked_for_display () const
2712 return !_route->presentation_info().hidden();
2716 RouteTimeAxisView::set_marked_for_display (bool yn)
2718 return RouteUI::mark_hidden (!yn);