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.hpp"
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 else if (!ARDOUR::Profile->get_trx()) {
270 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
271 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
274 set_tooltip(*solo_button,_("Solo"));
275 set_tooltip(*mute_button,_("Mute"));
276 set_tooltip(route_group_button, _("Route Group"));
278 mute_button->set_tweaks(ArdourButton::TrackHeader);
279 solo_button->set_tweaks(ArdourButton::TrackHeader);
280 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
281 playlist_button.set_tweaks(ArdourButton::TrackHeader);
282 automation_button.set_tweaks(ArdourButton::TrackHeader);
283 route_group_button.set_tweaks(ArdourButton::TrackHeader);
285 if (is_midi_track()) {
286 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
288 set_tooltip(automation_button, _("Automation"));
291 update_track_number_visibility();
294 if (ARDOUR::Profile->get_mixbus()) {
295 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
297 else if (!ARDOUR::Profile->get_trx()) {
298 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
301 if (is_track() && track()->mode() == ARDOUR::Normal) {
302 if (ARDOUR::Profile->get_mixbus()) {
303 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
305 else if (!ARDOUR::Profile->get_trx()) {
306 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
312 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
316 LayerDisplay layer_display;
317 if (get_gui_property ("layer-display", layer_display)) {
318 set_layer_display (layer_display);
321 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
322 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
324 /* pick up the correct freeze state */
329 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
331 PropertyList* plist = new PropertyList();
333 plist->add (ARDOUR::Properties::group_mute, true);
334 plist->add (ARDOUR::Properties::group_solo, true);
336 route_group_menu = new RouteGroupMenu (_session, plist);
338 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
341 RouteTimeAxisView::~RouteTimeAxisView ()
343 cleanup_gui_properties ();
345 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
349 delete playlist_action_menu;
350 delete automation_action_menu;
355 _automation_tracks.clear ();
357 delete route_group_menu;
358 CatchDeletion (this);
362 RouteTimeAxisView::name() const
365 return _route->name();
371 RouteTimeAxisView::post_construct ()
373 /* map current state of the route */
375 update_diskstream_display ();
376 setup_processor_menu_and_curves ();
377 reset_processor_automation_curves ();
380 /** Set up the processor menu for the current set of processors, and
381 * display automation curves for any parameters which have data.
384 RouteTimeAxisView::setup_processor_menu_and_curves ()
386 _subplugin_menu_map.clear ();
387 subplugin_menu.items().clear ();
388 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
389 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
393 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
395 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
396 if (_route->route_group()) {
397 _route->route_group()->remove (_route);
403 r.push_back (route ());
405 route_group_menu->build (r);
406 if (ev->button == 1) {
407 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
411 route_group_menu->menu()->popup (ev->button, ev->time);
418 RouteTimeAxisView::playlist_changed ()
424 RouteTimeAxisView::label_view ()
426 string x = _route->name ();
427 if (x != name_label.get_text ()) {
428 name_label.set_text (x);
430 const int64_t track_number = _route->track_number ();
431 if (track_number == 0) {
432 number_label.set_text ("");
434 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
439 RouteTimeAxisView::update_track_number_visibility ()
442 bool show_label = _session->config.get_track_name_number();
444 if (_route && _route->is_master()) {
448 if (number_label.get_parent()) {
449 controls_table.remove (number_label);
452 if (ARDOUR::Profile->get_mixbus()) {
453 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
455 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
457 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
458 // except the width of the number label is subtracted from the name-hbox, so we
459 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
460 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
462 number_label.set_size_request(tnw, -1);
463 number_label.show ();
465 number_label.hide ();
470 RouteTimeAxisView::parameter_changed (string const & p)
472 if (p == "track-name-number") {
473 update_track_number_visibility();
474 } else if (p == "editor-stereo-only-meters") {
475 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
476 gm.get_level_meter().set_max_audio_meter_count (2);
478 gm.get_level_meter().set_max_audio_meter_count (0);
484 RouteTimeAxisView::take_name_changed (void *src)
492 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
494 if (ev->button != 1) {
498 build_playlist_menu ();
499 conditionally_add_to_selection ();
500 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
506 RouteTimeAxisView::automation_click (GdkEventButton *ev)
508 if (ev->button != 1) {
512 conditionally_add_to_selection ();
513 build_automation_action_menu (false);
514 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
520 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
522 using namespace Menu_Helpers;
524 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
525 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
528 detach_menu (subplugin_menu);
530 _main_automation_menu_map.clear ();
531 delete automation_action_menu;
532 automation_action_menu = new Menu;
534 MenuList& items = automation_action_menu->items();
536 automation_action_menu->set_name ("ArdourContextMenu");
538 items.push_back (MenuElem (_("Show All Automation"),
539 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
541 items.push_back (MenuElem (_("Show Existing Automation"),
542 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
544 items.push_back (MenuElem (_("Hide All Automation"),
545 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
547 /* Attach the plugin submenu. It may have previously been used elsewhere,
548 so it was detached above
551 bool single_track_selected = (!for_selection || _editor.get_selection().tracks.size() == 1);
553 if (!subplugin_menu.items().empty()) {
554 items.push_back (SeparatorElem ());
555 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
556 items.back().set_sensitive (single_track_selected);
559 /* Add any route automation */
562 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
563 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
564 gain_automation_item->set_active (single_track_selected &&
565 string_to<bool>(gain_track->gui_property ("visible")));
567 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
571 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
572 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
573 trim_automation_item->set_active (single_track_selected &&
574 string_to<bool>(trim_track->gui_property ("visible")));
576 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
580 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
581 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
582 mute_automation_item->set_active (single_track_selected &&
583 string_to<bool>(mute_track->gui_property ("visible")));
585 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
588 if (!pan_tracks.empty() && !ARDOUR::Profile->get_mixbus()) {
589 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
590 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
591 pan_automation_item->set_active (single_track_selected &&
592 string_to<bool>(pan_tracks.front()->gui_property ("visible")));
594 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
595 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
596 _main_automation_menu_map[*p] = pan_automation_item;
602 RouteTimeAxisView::build_display_menu ()
604 using namespace Menu_Helpers;
608 TimeAxisView::build_display_menu ();
610 /* now fill it with our stuff */
612 MenuList& items = display_menu->items();
613 display_menu->set_name ("ArdourContextMenu");
615 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
617 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
619 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
621 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
623 items.push_back (SeparatorElem());
626 detach_menu (*_size_menu);
629 items.push_back (MenuElem (_("Height"), *_size_menu));
630 items.push_back (SeparatorElem());
632 // Hook for derived classes to add type specific stuff
633 append_extra_display_menu_items ();
637 Menu* layers_menu = manage (new Menu);
638 MenuList &layers_items = layers_menu->items();
639 layers_menu->set_name("ArdourContextMenu");
641 RadioMenuItem::Group layers_group;
643 /* Find out how many overlaid/stacked tracks we have in the selection */
647 int unchangeable = 0;
648 TrackSelection const & s = _editor.get_selection().tracks;
650 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
651 StreamView* v = (*i)->view ();
656 if (v->can_change_layer_display()) {
657 switch (v->layer_display ()) {
671 /* We're not connecting to signal_toggled() here; in the case where these two items are
672 set to be in the `inconsistent' state, it seems that one or other will end up active
673 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
674 select the active one, no toggled signal is emitted so nothing happens.
677 _ignore_set_layer_display = true;
679 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
680 RadioMenuItem* 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), Overlaid, true));
686 i->set_sensitive (false);
689 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
690 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
691 i->set_active (overlaid == 0 && stacked != 0);
692 i->set_inconsistent (overlaid != 0 && stacked != 0);
693 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
696 i->set_sensitive (false);
699 _ignore_set_layer_display = false;
701 items.push_back (MenuElem (_("Layers"), *layers_menu));
703 Menu* alignment_menu = manage (new Menu);
704 MenuList& alignment_items = alignment_menu->items();
705 alignment_menu->set_name ("ArdourContextMenu");
707 RadioMenuItem::Group align_group;
709 /* Same verbose hacks as for the layering options above */
715 boost::shared_ptr<Track> first_track;
717 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
718 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
719 if (!r || !r->is_track ()) {
724 first_track = r->track();
727 switch (r->track()->alignment_choice()) {
731 switch (r->track()->alignment_style()) {
732 case ExistingMaterial:
740 case UseExistingMaterial:
756 inconsistent = false;
763 if (!inconsistent && first_track) {
765 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
766 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
767 i->set_active (automatic != 0 && existing == 0 && capture == 0);
768 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
770 switch (first_track->alignment_choice()) {
772 switch (first_track->alignment_style()) {
773 case ExistingMaterial:
774 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
777 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
785 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
786 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
787 i->set_active (existing != 0 && capture == 0 && automatic == 0);
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
790 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
791 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
792 i->set_active (existing == 0 && capture != 0 && automatic == 0);
793 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
795 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
799 delete alignment_menu;
802 items.push_back (SeparatorElem());
804 build_playlist_menu ();
805 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
806 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
809 if (!is_midi_track () && _route->the_instrument ()) {
811 items.push_back (MenuElem (_("Patch Selector..."),
812 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
813 items.push_back (SeparatorElem());
816 route_group_menu->detach ();
819 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
822 r.push_back (rtv->route ());
827 r.push_back (route ());
830 if (!_route->is_master()) {
831 route_group_menu->build (r);
832 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
835 build_automation_action_menu (true);
836 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
838 items.push_back (SeparatorElem());
842 TrackSelection const & s = _editor.get_selection().tracks;
843 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
844 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
849 if (r->route()->active()) {
856 items.push_back (CheckMenuElem (_("Active")));
857 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
858 bool click_sets_active = true;
859 if (active > 0 && inactive == 0) {
860 i->set_active (true);
861 click_sets_active = false;
862 } else if (active > 0 && inactive > 0) {
863 i->set_inconsistent (true);
865 i->set_sensitive(! _session->transport_rolling());
866 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
868 items.push_back (SeparatorElem());
869 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
870 if (_route && !_route->is_master()) {
871 items.push_back (SeparatorElem());
872 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
874 items.push_back (SeparatorElem());
875 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
879 RouteTimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
881 TimeAxisView::show_timestretch (start, end, layers, layer);
891 /* check that the time selection was made in our route, or our route group.
892 remember that route_group() == 0 implies the route is *not* in a edit group.
895 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
896 /* this doesn't apply to us */
900 /* ignore it if our edit group is not active */
902 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
907 if (timestretch_rect == 0) {
908 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
909 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
910 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
913 timestretch_rect->show ();
914 timestretch_rect->raise_to_top ();
916 double const x1 = start / _editor.get_current_zoom();
917 double const x2 = (end - 1) / _editor.get_current_zoom();
919 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
920 x2, current_height() * (layers - layer) / layers));
924 RouteTimeAxisView::hide_timestretch ()
926 TimeAxisView::hide_timestretch ();
928 if (timestretch_rect) {
929 timestretch_rect->hide ();
934 RouteTimeAxisView::show_selection (TimeSelection& ts)
938 /* ignore it if our edit group is not active or if the selection was started
939 in some other track or route group (remember that route_group() == 0 means
940 that the track is not in an route group).
943 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
944 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
950 TimeAxisView::show_selection (ts);
954 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
957 bool height_changed = (height == 0) || (h != height);
960 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
963 gm.get_level_meter().setup_meters (gmlen, meter_width);
965 TimeAxisView::set_height (h, m);
968 _view->set_height ((double) current_height());
971 if (height >= preset_height (HeightNormal)) {
975 gm.get_gain_slider().show();
977 if (!_route || _route->is_monitor()) {
982 if (rec_enable_button)
983 rec_enable_button->show();
985 route_group_button.show();
986 automation_button.show();
988 if (is_track() && track()->mode() == ARDOUR::Normal) {
989 playlist_button.show();
996 gm.get_gain_slider().hide();
998 if (!_route || _route->is_monitor()) {
1001 solo_button->show();
1003 if (rec_enable_button)
1004 rec_enable_button->show();
1006 route_group_button.hide ();
1007 automation_button.hide ();
1009 if (is_track() && track()->mode() == ARDOUR::Normal) {
1010 playlist_button.hide ();
1015 if (height_changed && !no_redraw) {
1016 /* only emit the signal if the height really changed */
1022 RouteTimeAxisView::route_color_changed ()
1024 using namespace ARDOUR_UI_UTILS;
1026 _view->apply_color (color(), StreamView::RegionColor);
1028 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1032 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1035 _view->set_samples_per_pixel (fpp);
1038 StripableTimeAxisView::set_samples_per_pixel (fpp);
1042 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1044 if (!mitem->get_active()) {
1045 /* this is one of the two calls made when these radio menu items change status. this one
1046 is for the item that became inactive, and we want to ignore it.
1051 if (apply_to_selection) {
1052 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1055 track()->set_align_choice (choice);
1061 RouteTimeAxisView::rename_current_playlist ()
1063 Prompter prompter (true);
1066 boost::shared_ptr<Track> tr = track();
1067 if (!tr || tr->destructive()) {
1071 boost::shared_ptr<Playlist> pl = tr->playlist();
1076 prompter.set_title (_("Rename Playlist"));
1077 prompter.set_prompt (_("New name for playlist:"));
1078 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1079 prompter.set_initial_text (pl->name());
1080 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1083 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1086 prompter.get_result (name);
1087 if (name.length()) {
1088 if (_session->playlists()->by_name (name)) {
1089 MessageDialog msg (_("Given playlist name is not unique."));
1091 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1093 pl->set_name (name);
1101 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1103 std::string ret (basename);
1105 std::string const group_string = "." + route_group()->name() + ".";
1107 // iterate through all playlists
1109 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1110 std::string tmp = (*i)->name();
1112 std::string::size_type idx = tmp.find(group_string);
1113 // find those which belong to this group
1114 if (idx != string::npos) {
1115 tmp = tmp.substr(idx + group_string.length());
1117 // and find the largest current number
1119 if (x > maxnumber) {
1128 snprintf (buf, sizeof(buf), "%d", maxnumber);
1130 ret = this->name() + "." + route_group()->name () + "." + buf;
1136 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1140 boost::shared_ptr<Track> tr = track ();
1141 if (!tr || tr->destructive()) {
1145 boost::shared_ptr<const Playlist> pl = tr->playlist();
1152 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1153 name = resolve_new_group_playlist_name(name,playlists_before_op);
1156 while (_session->playlists()->by_name(name)) {
1157 name = Playlist::bump_name (name, *_session);
1161 // TODO: The prompter "new" button should be de-activated if the user
1162 // specifies a playlist name which already exists in the session.
1164 Prompter prompter (true);
1167 prompter.set_title (_("New Copy Playlist"));
1168 prompter.set_prompt (_("Name for playlist copy:"));
1170 prompter.set_title (_("New Playlist"));
1171 prompter.set_prompt (_("Name for new playlist:"));
1173 prompter.set_initial_text (name);
1174 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1175 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1176 prompter.show_all ();
1179 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1182 prompter.get_result (name);
1183 if (name.length()) {
1184 if (_session->playlists()->by_name (name)) {
1185 MessageDialog msg (_("Given playlist name is not unique."));
1187 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1195 if (name.length()) {
1197 tr->use_copy_playlist ();
1199 tr->use_default_new_playlist ();
1201 tr->playlist()->set_name (name);
1206 RouteTimeAxisView::clear_playlist ()
1208 boost::shared_ptr<Track> tr = track ();
1209 if (!tr || tr->destructive()) {
1213 boost::shared_ptr<Playlist> pl = tr->playlist();
1218 _editor.clear_playlist (pl);
1222 RouteTimeAxisView::speed_changed ()
1224 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1228 RouteTimeAxisView::update_diskstream_display ()
1238 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1240 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1242 /* special case: select/deselect all tracks */
1244 _editor.begin_reversible_selection_op (X_("Selection Click"));
1246 if (_editor.get_selection().selected (this)) {
1247 _editor.get_selection().clear_tracks ();
1249 _editor.select_all_tracks ();
1252 _editor.commit_reversible_selection_op ();
1257 _editor.begin_reversible_selection_op (X_("Selection Click"));
1259 switch (ArdourKeyboard::selection_type (ev->state)) {
1260 case Selection::Toggle:
1261 _editor.get_selection().toggle (this);
1264 case Selection::Set:
1265 _editor.get_selection().set (this);
1268 case Selection::Extend:
1269 _editor.extend_selection_to_track (*this);
1272 case Selection::Add:
1273 _editor.get_selection().add (this);
1277 _editor.commit_reversible_selection_op ();
1279 _editor.set_selected_mixer_strip (*this);
1283 RouteTimeAxisView::set_selected_points (PointSelection& points)
1285 StripableTimeAxisView::set_selected_points (points);
1286 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1288 asv->set_selected_points (points);
1293 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1296 _view->set_selected_regionviews (regions);
1300 /** Add the selectable things that we have to a list.
1301 * @param results List to add things to.
1304 RouteTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1306 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1307 _view->get_selectables (start, end, top, bot, results, within);
1310 /* pick up visible automation tracks */
1311 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1312 if (!(*i)->hidden()) {
1313 (*i)->get_selectables (start, end, top, bot, results, within);
1319 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1322 _view->get_inverted_selectables (sel, results);
1324 StripableTimeAxisView::get_inverted_selectables (sel, results);
1328 RouteTimeAxisView::route_group () const
1330 return _route->route_group();
1333 boost::shared_ptr<Playlist>
1334 RouteTimeAxisView::playlist () const
1336 boost::shared_ptr<Track> tr;
1338 if ((tr = track()) != 0) {
1339 return tr->playlist();
1341 return boost::shared_ptr<Playlist> ();
1346 RouteTimeAxisView::name_entry_changed (string const& str)
1348 if (str == _route->name()) {
1354 strip_whitespace_edges (x);
1360 if (_session->route_name_internal (x)) {
1361 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1363 } else if (RouteUI::verify_new_route_name (x)) {
1364 _route->set_name (x);
1371 boost::shared_ptr<Region>
1372 RouteTimeAxisView::find_next_region (samplepos_t pos, RegionPoint point, int32_t dir)
1374 boost::shared_ptr<Playlist> pl = playlist ();
1377 return pl->find_next_region (pos, point, dir);
1380 return boost::shared_ptr<Region> ();
1384 RouteTimeAxisView::find_next_region_boundary (samplepos_t pos, int32_t dir)
1386 boost::shared_ptr<Playlist> pl = playlist ();
1389 return pl->find_next_region_boundary (pos, dir);
1396 RouteTimeAxisView::fade_range (TimeSelection& selection)
1398 boost::shared_ptr<Playlist> what_we_got;
1399 boost::shared_ptr<Track> tr = track ();
1400 boost::shared_ptr<Playlist> playlist;
1403 /* route is a bus, not a track */
1407 playlist = tr->playlist();
1409 TimeSelection time (selection);
1411 playlist->clear_changes ();
1412 playlist->clear_owned_changes ();
1414 playlist->fade_range (time);
1416 vector<Command*> cmds;
1417 playlist->rdiff (cmds);
1418 _session->add_commands (cmds);
1419 _session->add_command (new StatefulDiffCommand (playlist));
1424 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1426 boost::shared_ptr<Playlist> what_we_got;
1427 boost::shared_ptr<Track> tr = track ();
1428 boost::shared_ptr<Playlist> playlist;
1431 /* route is a bus, not a track */
1435 playlist = tr->playlist();
1437 TimeSelection time (selection.time);
1439 playlist->clear_changes ();
1440 playlist->clear_owned_changes ();
1444 if (playlist->cut (time) != 0) {
1445 if (Config->get_edit_mode() == Ripple) {
1446 playlist->ripple(time.start(), -time.length(), NULL);
1448 // no need to exclude any regions from rippling here
1450 vector<Command*> cmds;
1451 playlist->rdiff (cmds);
1452 _session->add_commands (cmds);
1454 _session->add_command (new StatefulDiffCommand (playlist));
1459 if ((what_we_got = playlist->cut (time)) != 0) {
1460 _editor.get_cut_buffer().add (what_we_got);
1461 if (Config->get_edit_mode() == Ripple) {
1462 playlist->ripple(time.start(), -time.length(), NULL);
1464 // no need to exclude any regions from rippling here
1466 vector<Command*> cmds;
1467 playlist->rdiff (cmds);
1468 _session->add_commands (cmds);
1470 _session->add_command (new StatefulDiffCommand (playlist));
1474 if ((what_we_got = playlist->copy (time)) != 0) {
1475 _editor.get_cut_buffer().add (what_we_got);
1480 if ((what_we_got = playlist->cut (time)) != 0) {
1481 if (Config->get_edit_mode() == Ripple) {
1482 playlist->ripple(time.start(), -time.length(), NULL);
1484 // no need to exclude any regions from rippling here
1486 vector<Command*> cmds;
1487 playlist->rdiff (cmds);
1488 _session->add_commands (cmds);
1489 _session->add_command (new StatefulDiffCommand (playlist));
1490 what_we_got->release ();
1497 RouteTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1503 boost::shared_ptr<Playlist> pl = playlist ();
1504 const ARDOUR::DataType type = pl->data_type();
1505 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1507 if (p == selection.playlists.end()) {
1510 ctx.counts.increase_n_playlists(type);
1512 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1514 /* add multi-paste offset if applicable */
1515 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent();
1516 const samplecnt_t duration = extent.second - extent.first;
1517 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1519 pl->clear_changes ();
1520 pl->clear_owned_changes ();
1521 if (Config->get_edit_mode() == Ripple) {
1522 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent_with_endspace();
1523 samplecnt_t amount = extent.second - extent.first;
1524 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1526 pl->paste (*p, pos, ctx.times, sub_num);
1528 vector<Command*> cmds;
1530 _session->add_commands (cmds);
1532 _session->add_command (new StatefulDiffCommand (pl));
1538 struct PlaylistSorter {
1539 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1540 return a->sort_id() < b->sort_id();
1545 RouteTimeAxisView::build_playlist_menu ()
1547 using namespace Menu_Helpers;
1553 delete playlist_action_menu;
1554 playlist_action_menu = new Menu;
1555 playlist_action_menu->set_name ("ArdourContextMenu");
1557 MenuList& playlist_items = playlist_action_menu->items();
1558 playlist_action_menu->set_name ("ArdourContextMenu");
1559 playlist_items.clear();
1561 RadioMenuItem::Group playlist_group;
1562 boost::shared_ptr<Track> tr = track ();
1564 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists()->playlists_for_track (tr);
1566 /* sort the playlists */
1568 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1570 /* add the playlists to the menu */
1571 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1572 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1573 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1574 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1576 if (tr->playlist()->id() == (*i)->id()) {
1581 playlist_items.push_back (SeparatorElem());
1582 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1583 playlist_items.push_back (SeparatorElem());
1585 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1586 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1587 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1590 // Use a label which tells the user what is happening
1591 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1592 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1596 playlist_items.push_back (SeparatorElem());
1597 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1598 playlist_items.push_back (SeparatorElem());
1600 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1604 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1606 assert (is_track());
1608 // exit if we were triggered by deactivating the old playlist
1609 if (!item->get_active()) {
1613 boost::shared_ptr<Playlist> pl (wpl.lock());
1619 if (track()->playlist() == pl) {
1620 // exit when use_playlist is called by the creation of the playlist menu
1621 // or the playlist choice is unchanged
1625 track()->use_playlist (track()->data_type(), pl);
1627 RouteGroup* rg = route_group();
1629 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1630 std::string group_string = "." + rg->name() + ".";
1632 std::string take_name = pl->name();
1633 std::string::size_type idx = take_name.find(group_string);
1635 if (idx == std::string::npos)
1638 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1640 boost::shared_ptr<RouteList> rl (rg->route_list());
1642 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1643 if ((*i) == this->route()) {
1647 std::string playlist_name = (*i)->name()+group_string+take_name;
1649 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1654 if (track->freeze_state() == Track::Frozen) {
1655 /* Don't change playlists of frozen tracks */
1659 boost::shared_ptr<Playlist> ipl = session()->playlists()->by_name(playlist_name);
1661 // No playlist for this track for this take yet, make it
1662 track->use_default_new_playlist();
1663 track->playlist()->set_name(playlist_name);
1665 track->use_playlist(track->data_type(), ipl);
1672 RouteTimeAxisView::update_playlist_tip ()
1674 RouteGroup* rg = route_group ();
1675 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1676 string group_string = "." + rg->name() + ".";
1678 string take_name = track()->playlist()->name();
1679 string::size_type idx = take_name.find(group_string);
1681 if (idx != string::npos) {
1682 /* find the bit containing the take number / name */
1683 take_name = take_name.substr (idx + group_string.length());
1685 /* set the playlist button tooltip to the take name */
1688 string_compose(_("Take: %1.%2"),
1689 Gtkmm2ext::markup_escape_text (rg->name()),
1690 Gtkmm2ext::markup_escape_text (take_name))
1697 /* set the playlist button tooltip to the playlist name */
1698 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1703 RouteTimeAxisView::show_playlist_selector ()
1705 _editor.playlist_selector().show_for (this);
1709 RouteTimeAxisView::map_frozen ()
1715 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1717 switch (track()->freeze_state()) {
1719 playlist_button.set_sensitive (false);
1722 playlist_button.set_sensitive (true);
1725 RouteUI::map_frozen ();
1729 RouteTimeAxisView::color_handler ()
1731 //case cTimeStretchOutline:
1732 if (timestretch_rect) {
1733 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1735 //case cTimeStretchFill:
1736 if (timestretch_rect) {
1737 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1743 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1744 * Will add track if necessary.
1747 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1749 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1750 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1753 /* it doesn't exist yet, so we don't care about the button state: just add it */
1754 create_automation_child (param, true);
1757 bool yn = menu->get_active();
1758 bool changed = false;
1760 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1762 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1763 will have done that for us.
1766 if (changed && !no_redraw) {
1774 RouteTimeAxisView::update_pan_track_visibility ()
1776 bool const showit = pan_automation_item->get_active();
1777 bool changed = false;
1779 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1780 if ((*i)->set_marked_for_display (showit)) {
1786 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1791 RouteTimeAxisView::ensure_pan_views (bool show)
1793 bool changed = false;
1794 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1796 (*i)->set_marked_for_display (false);
1799 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1803 if (!_route->panner()) {
1807 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1808 set<Evoral::Parameter>::iterator p;
1810 for (p = params.begin(); p != params.end(); ++p) {
1811 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1813 if (pan_control->parameter().type() == NullAutomation) {
1814 error << "Pan control has NULL automation type!" << endmsg;
1818 if (automation_child (pan_control->parameter ()).get () == 0) {
1820 /* we don't already have an AutomationTimeAxisView for this parameter */
1822 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1824 boost::shared_ptr<AutomationTimeAxisView> t (
1825 new AutomationTimeAxisView (_session,
1829 pan_control->parameter (),
1837 pan_tracks.push_back (t);
1838 add_automation_child (*p, t, show);
1840 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1847 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1849 if (apply_to_selection) {
1850 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1854 StripableTimeAxisView::show_all_automation ();
1856 /* Show processor automation */
1858 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1859 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1860 if ((*ii)->view == 0) {
1861 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1864 (*ii)->menu_item->set_active (true);
1877 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1879 if (apply_to_selection) {
1880 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1884 StripableTimeAxisView::show_existing_automation ();
1886 /* Show processor automation */
1887 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1888 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1889 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1890 (*ii)->menu_item->set_active (true);
1901 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1903 if (apply_to_selection) {
1904 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1907 StripableTimeAxisView::hide_all_automation ();
1909 /* Hide processor automation */
1910 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1911 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1912 (*ii)->menu_item->set_active (false);
1922 RouteTimeAxisView::region_view_added (RegionView* rv)
1924 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1925 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1926 boost::shared_ptr<AutomationTimeAxisView> atv;
1928 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1933 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1934 (*i)->add_ghost(rv);
1938 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1940 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1946 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1948 parent.remove_processor_automation_node (this);
1952 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1955 remove_child (pan->view);
1959 RouteTimeAxisView::ProcessorAutomationNode*
1960 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1962 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1964 if ((*i)->processor == processor) {
1966 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1967 if ((*ii)->what == what) {
1977 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1979 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1982 ProcessorAutomationNode* pan;
1984 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1985 /* session state may never have been saved with new plugin */
1986 error << _("programming error: ")
1987 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1988 processor->name(), what.type(), (int) what.channel(), what.id() )
1990 abort(); /*NOTREACHED*/
1998 boost::shared_ptr<AutomationControl> control
1999 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2001 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2002 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2003 _editor, *this, false, parent_canvas,
2004 processor->describe_parameter (what), processor->name()));
2006 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2008 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2011 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2016 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2019 pan->menu_item->set_active (false);
2028 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2030 boost::shared_ptr<Processor> processor (p.lock ());
2032 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2033 /* The Amp processor is a special case and is dealt with separately */
2037 set<Evoral::Parameter> existing;
2039 processor->what_has_data (existing);
2041 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2043 Evoral::Parameter param (*i);
2044 boost::shared_ptr<AutomationLine> al;
2046 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2047 #warning NOT REACHED -- bug?
2050 add_processor_automation_curve (processor, param);
2056 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2058 boost::shared_ptr<Processor> processor (p.lock ());
2060 if (!processor || !processor->display_to_user ()) {
2064 /* we use this override to veto the Amp processor from the plugin menu,
2065 as its automation lane can be accessed using the special "Fader" menu
2069 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2073 using namespace Menu_Helpers;
2074 ProcessorAutomationInfo *rai;
2075 list<ProcessorAutomationInfo*>::iterator x;
2077 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2079 if (automatable.empty()) {
2083 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2084 if ((*x)->processor == processor) {
2089 if (x == processor_automation.end()) {
2090 rai = new ProcessorAutomationInfo (processor);
2091 processor_automation.push_back (rai);
2096 /* any older menu was deleted at the top of processors_changed()
2097 when we cleared the subplugin menu.
2100 rai->menu = manage (new Menu);
2101 MenuList& items = rai->menu->items();
2102 rai->menu->set_name ("ArdourContextMenu");
2106 std::set<Evoral::Parameter> has_visible_automation;
2107 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2109 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2111 ProcessorAutomationNode* pan;
2112 Gtk::CheckMenuItem* mitem;
2114 string name = processor->describe_parameter (*i);
2116 if (name == X_("hidden")) {
2120 items.push_back (CheckMenuElem (name));
2121 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2123 _subplugin_menu_map[*i] = mitem;
2125 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2126 mitem->set_active(true);
2129 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2133 pan = new ProcessorAutomationNode (*i, mitem, *this);
2135 rai->lines.push_back (pan);
2139 pan->menu_item = mitem;
2143 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2146 if (items.size() == 0) {
2150 /* add the menu for this processor, because the subplugin
2151 menu is always cleared at the top of processors_changed().
2152 this is the result of some poor design in gtkmm and/or
2156 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2161 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2162 RouteTimeAxisView::ProcessorAutomationNode* pan)
2164 bool showit = pan->menu_item->get_active();
2165 bool redraw = false;
2167 if (pan->view == 0 && showit) {
2168 add_processor_automation_curve (rai->processor, pan->what);
2172 if (pan->view && pan->view->set_marked_for_display (showit)) {
2176 if (redraw && !no_redraw) {
2182 RouteTimeAxisView::reread_midnam ()
2184 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument ());
2186 bool rv = pi->plugin ()->read_midnam();
2188 if (rv && patch_change_dialog ()) {
2189 patch_change_dialog ()->refresh ();
2194 RouteTimeAxisView::drop_instrument_ref ()
2196 midnam_connection.drop_connections ();
2200 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2203 boost::shared_ptr<Processor> the_instrument (_route->the_instrument());
2204 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (the_instrument);
2205 if (pi && pi->plugin ()->has_midnam ()) {
2206 midnam_connection.drop_connections ();
2207 the_instrument->DropReferences.connect (midnam_connection, invalidator (*this),
2208 boost::bind (&RouteTimeAxisView::drop_instrument_ref, this),
2210 pi->plugin()->UpdateMidnam.connect (midnam_connection, invalidator (*this),
2211 boost::bind (&RouteTimeAxisView::reread_midnam, this),
2218 if (c.type == RouteProcessorChange::MeterPointChange) {
2219 /* nothing to do if only the meter point has changed */
2223 using namespace Menu_Helpers;
2225 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2226 (*i)->valid = false;
2229 setup_processor_menu_and_curves ();
2231 bool deleted_processor_automation = false;
2233 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2235 list<ProcessorAutomationInfo*>::iterator tmp;
2243 processor_automation.erase (i);
2244 deleted_processor_automation = true;
2251 if (deleted_processor_automation && !no_redraw) {
2256 boost::shared_ptr<AutomationLine>
2257 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2259 ProcessorAutomationNode* pan;
2261 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2267 return boost::shared_ptr<AutomationLine>();
2271 RouteTimeAxisView::reset_processor_automation_curves ()
2273 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2279 RouteTimeAxisView::can_edit_name () const
2281 /* we do not allow track name changes if it is record enabled
2283 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2287 return !trk->rec_enable_control()->get_value();
2291 RouteTimeAxisView::blink_rec_display (bool onoff)
2293 RouteUI::blink_rec_display (onoff);
2297 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2299 if (_ignore_set_layer_display) {
2303 if (apply_to_selection) {
2304 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2308 _view->set_layer_display (d);
2311 set_gui_property (X_("layer-display"), d);
2316 RouteTimeAxisView::layer_display () const
2319 return _view->layer_display ();
2322 /* we don't know, since we don't have a _view, so just return something */
2327 RouteTimeAxisView::fast_update ()
2329 gm.get_level_meter().update_meters ();
2333 RouteTimeAxisView::hide_meter ()
2336 gm.get_level_meter().hide_meters ();
2340 RouteTimeAxisView::show_meter ()
2346 RouteTimeAxisView::reset_meter ()
2348 if (UIConfiguration::instance().get_show_track_meters()) {
2349 int meter_width = 3;
2350 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2353 gm.get_level_meter().setup_meters (height - 9, meter_width);
2360 RouteTimeAxisView::clear_meter ()
2362 gm.get_level_meter().clear_meters ();
2366 RouteTimeAxisView::meter_changed ()
2368 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2370 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2373 // reset peak when meter point changes
2374 gm.reset_peak_display();
2378 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2381 if (_route && !no_redraw) {
2387 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2389 using namespace Menu_Helpers;
2391 if (!_underlay_streams.empty()) {
2392 MenuList& parent_items = parent_menu->items();
2393 Menu* gs_menu = manage (new Menu);
2394 gs_menu->set_name ("ArdourContextMenu");
2395 MenuList& gs_items = gs_menu->items();
2397 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2399 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2400 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2401 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2407 RouteTimeAxisView::set_underlay_state()
2409 if (!underlay_xml_node) {
2413 XMLNodeList nlist = underlay_xml_node->children();
2414 XMLNodeConstIterator niter;
2415 XMLNode *child_node;
2417 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2418 child_node = *niter;
2420 if (child_node->name() != "Underlay") {
2424 XMLProperty const * prop = child_node->property ("id");
2426 PBD::ID id (prop->value());
2428 StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2431 add_underlay(v->view(), false);
2440 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2446 RouteTimeAxisView& other = v->trackview();
2448 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2449 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2450 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2451 abort(); /*NOTREACHED*/
2454 _underlay_streams.push_back(v);
2455 other._underlay_mirrors.push_back(this);
2457 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2459 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2461 if (!underlay_xml_node) {
2462 underlay_xml_node = xml_node->add_child("Underlays");
2465 XMLNode* node = underlay_xml_node->add_child("Underlay");
2466 XMLProperty const * prop = node->add_property("id");
2467 prop->set_value(v->trackview().route()->id().to_s());
2474 RouteTimeAxisView::remove_underlay (StreamView* v)
2480 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2481 RouteTimeAxisView& other = v->trackview();
2483 if (it != _underlay_streams.end()) {
2484 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2486 if (gm == other._underlay_mirrors.end()) {
2487 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2488 abort(); /*NOTREACHED*/
2491 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2493 _underlay_streams.erase(it);
2494 other._underlay_mirrors.erase(gm);
2496 if (underlay_xml_node) {
2497 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2503 RouteTimeAxisView::set_button_names ()
2505 if (_route && _route->solo_safe_control()->solo_safe()) {
2506 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2508 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2510 if (Config->get_solo_control_is_listen_control()) {
2511 switch (Config->get_listen_position()) {
2512 case AfterFaderListen:
2513 solo_button->set_text (S_("AfterFader|A"));
2514 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2516 case PreFaderListen:
2517 solo_button->set_text (S_("PreFader|P"));
2518 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2522 solo_button->set_text (S_("Solo|S"));
2523 set_tooltip (*solo_button, _("Solo"));
2525 mute_button->set_text (S_("Mute|M"));
2529 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2531 Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2536 ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2537 if (i != _subplugin_menu_map.end()) {
2545 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2547 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2549 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2553 gain_track.reset (new AutomationTimeAxisView (_session,
2554 _route, _route->amp(), c, param,
2559 _route->amp()->describe_parameter(param)));
2562 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2565 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2569 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2571 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2572 if (!c || ! _route->trim()->active()) {
2576 trim_track.reset (new AutomationTimeAxisView (_session,
2577 _route, _route->trim(), c, param,
2582 _route->trim()->describe_parameter(param)));
2585 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2588 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2592 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2594 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2596 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2600 mute_track.reset (new AutomationTimeAxisView (_session,
2601 _route, _route, c, param,
2606 _route->describe_parameter(param)));
2609 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2612 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2616 void add_region_to_list (RegionView* rv, RegionList* l)
2618 l->push_back (rv->region());
2622 RouteTimeAxisView::combine_regions ()
2624 /* as of may 2011, we do not offer uncombine for MIDI tracks
2627 if (!is_audio_track()) {
2635 RegionList selected_regions;
2636 boost::shared_ptr<Playlist> playlist = track()->playlist();
2638 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2640 if (selected_regions.size() < 2) {
2644 playlist->clear_changes ();
2645 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2647 _session->add_command (new StatefulDiffCommand (playlist));
2648 /* make the new region be selected */
2650 return _view->find_view (compound_region);
2654 RouteTimeAxisView::uncombine_regions ()
2656 /* as of may 2011, we do not offer uncombine for MIDI tracks
2658 if (!is_audio_track()) {
2666 RegionList selected_regions;
2667 boost::shared_ptr<Playlist> playlist = track()->playlist();
2669 /* have to grab selected regions first because the uncombine is going
2670 * to change that in the middle of the list traverse
2673 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2675 playlist->clear_changes ();
2677 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2678 playlist->uncombine (*i);
2681 _session->add_command (new StatefulDiffCommand (playlist));
2685 RouteTimeAxisView::state_id() const
2687 return string_compose ("rtav %1", _route->id().to_s());
2692 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2694 TimeAxisView::remove_child (c);
2696 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2698 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2699 if (i->second == a) {
2700 _automation_tracks.erase (i);
2708 RouteTimeAxisView::color () const
2710 return route_color ();
2714 RouteTimeAxisView::marked_for_display () const
2716 return !_route->presentation_info().hidden();
2720 RouteTimeAxisView::set_marked_for_display (bool yn)
2722 return RouteUI::mark_hidden (!yn);