2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("G"))
100 , playlist_button (_("P"))
101 , automation_button (_("A"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 75, 14)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("tracknumber label");
112 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
113 number_label.set_alignment(.5, .5);
114 number_label.set_fallthrough_to_parent (true);
115 track_number_v_size_group->add_widget(number_label);
117 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
121 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
123 RouteUI::set_route (rt);
125 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
126 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
127 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
130 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
133 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
134 gm.get_level_meter().set_no_show_all();
135 gm.get_level_meter().setup_meters(50, meter_width);
136 gm.update_gain_sensitive ();
138 string str = gui_property ("height");
140 set_height (atoi (str));
142 set_height (preset_height (HeightNormal));
145 if (!_route->is_auditioner()) {
146 if (gui_property ("visible").empty()) {
147 set_gui_property ("visible", true);
150 set_gui_property ("visible", false);
154 update_solo_display ();
156 timestretch_rect = 0;
159 ignore_toggle = false;
161 route_group_button.set_name ("route button");
162 playlist_button.set_name ("route button");
163 automation_button.set_name ("route button");
165 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
166 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
167 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 if (ARDOUR::Profile->get_mixbus()) {
172 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
174 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
176 controls_button_size_group->add_widget(*rec_enable_button);
178 if (is_midi_track()) {
179 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
180 gm.set_fader_name ("MidiTrackFader");
182 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
183 gm.set_fader_name ("AudioTrackFader");
186 rec_enable_button->set_sensitive (_session->writable());
188 /* set playlist button tip to the current playlist, and make it update when it changes */
189 update_playlist_tip ();
190 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
193 gm.set_fader_name ("AudioBusFader");
194 Gtk::Fixed *blank = manage(new Gtk::Fixed());
195 controls_button_size_group->add_widget(*blank);
196 if (ARDOUR::Profile->get_mixbus() ) {
197 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
199 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
206 if (!ARDOUR::Profile->get_mixbus()) {
207 controls_meters_size_group->add_widget (gm.get_level_meter());
210 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
211 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
213 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
215 if (ARDOUR::Profile->get_mixbus()) {
216 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
220 controls_button_size_group->add_widget(*mute_button);
221 track_number_v_size_group->add_widget(*mute_button);
223 if (!_route->is_master()) {
224 if (ARDOUR::Profile->get_mixbus()) {
225 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
227 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
229 controls_button_size_group->add_widget(*solo_button);
231 Gtk::Fixed *blank = manage(new Gtk::Fixed());
232 controls_button_size_group->add_widget(*blank);
233 if (ARDOUR::Profile->get_mixbus()) {
234 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 if (ARDOUR::Profile->get_mixbus()) {
242 controls_button_size_group->add_widget(route_group_button);
243 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
246 else if (!ARDOUR::Profile->get_trx()) {
247 controls_button_size_group->add_widget(route_group_button);
248 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
252 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
253 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
254 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
256 mute_button->set_tweaks(ArdourButton::Square);
257 solo_button->set_tweaks(ArdourButton::Square);
258 rec_enable_button->set_tweaks(ArdourButton::Square);
259 playlist_button.set_tweaks(ArdourButton::Square);
260 automation_button.set_tweaks(ArdourButton::Square);
261 route_group_button.set_tweaks(ArdourButton::Square);
263 if (is_midi_track()) {
264 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
266 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
269 update_track_number_visibility();
272 if (ARDOUR::Profile->get_mixbus()) {
273 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
274 controls_button_size_group->add_widget(automation_button);
276 else if (!ARDOUR::Profile->get_trx()) {
277 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 controls_button_size_group->add_widget(automation_button);
281 if (is_track() && track()->mode() == ARDOUR::Normal) {
282 if (ARDOUR::Profile->get_mixbus()) {
283 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
284 controls_button_size_group->add_widget(playlist_button);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
288 controls_button_size_group->add_widget(playlist_button);
294 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
295 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
299 str = gui_property ("layer-display");
301 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
304 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
305 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
307 /* pick up the correct freeze state */
312 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
313 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
315 PropertyList* plist = new PropertyList();
317 plist->add (ARDOUR::Properties::mute, true);
318 plist->add (ARDOUR::Properties::solo, true);
320 route_group_menu = new RouteGroupMenu (_session, plist);
322 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
325 RouteTimeAxisView::~RouteTimeAxisView ()
327 CatchDeletion (this);
329 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
333 delete playlist_action_menu;
334 playlist_action_menu = 0;
339 _automation_tracks.clear ();
341 delete route_group_menu;
345 RouteTimeAxisView::post_construct ()
347 /* map current state of the route */
349 update_diskstream_display ();
350 setup_processor_menu_and_curves ();
351 reset_processor_automation_curves ();
354 /** Set up the processor menu for the current set of processors, and
355 * display automation curves for any parameters which have data.
358 RouteTimeAxisView::setup_processor_menu_and_curves ()
360 _subplugin_menu_map.clear ();
361 subplugin_menu.items().clear ();
362 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
367 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
369 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
370 if (_route->route_group()) {
371 _route->route_group()->remove (_route);
377 r.push_back (route ());
379 route_group_menu->build (r);
380 route_group_menu->menu()->popup (ev->button, ev->time);
386 RouteTimeAxisView::playlist_changed ()
392 RouteTimeAxisView::label_view ()
394 string x = _route->name ();
395 if (x != name_label.get_text ()) {
396 name_label.set_text (x);
398 const int64_t track_number = _route->track_number ();
399 if (track_number == 0) {
400 number_label.set_text ("");
402 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
407 RouteTimeAxisView::update_track_number_visibility ()
410 bool show_label = _session->config.get_track_name_number();
412 if (_route && _route->is_master()) {
416 if (number_label.get_parent()) {
417 controls_table.remove (number_label);
420 if (ARDOUR::Profile->get_mixbus()) {
421 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
423 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
425 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
426 // except the width of the number label is subtracted from the name-hbox, so we
427 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
428 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
430 number_label.set_size_request(tnw, -1);
431 number_label.show ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
434 number_label.hide ();
435 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
440 RouteTimeAxisView::parameter_changed (string const & p)
442 if (p == "track-name-number") {
443 update_track_number_visibility();
448 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
450 if (what_changed.contains (ARDOUR::Properties::name)) {
456 RouteTimeAxisView::take_name_changed (void *src)
464 RouteTimeAxisView::playlist_click ()
466 build_playlist_menu ();
467 conditionally_add_to_selection ();
468 playlist_action_menu->popup (1, gtk_get_current_event_time());
472 RouteTimeAxisView::automation_click ()
474 conditionally_add_to_selection ();
475 build_automation_action_menu (false);
476 automation_action_menu->popup (1, gtk_get_current_event_time());
480 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
482 using namespace Menu_Helpers;
484 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
485 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
488 detach_menu (subplugin_menu);
490 _main_automation_menu_map.clear ();
491 delete automation_action_menu;
492 automation_action_menu = new Menu;
494 MenuList& items = automation_action_menu->items();
496 automation_action_menu->set_name ("ArdourContextMenu");
498 items.push_back (MenuElem (_("Show All Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
501 items.push_back (MenuElem (_("Show Existing Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
504 items.push_back (MenuElem (_("Hide All Automation"),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
507 /* Attach the plugin submenu. It may have previously been used elsewhere,
508 so it was detached above
511 if (!subplugin_menu.items().empty()) {
512 items.push_back (SeparatorElem ());
513 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
514 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
519 RouteTimeAxisView::build_display_menu ()
521 using namespace Menu_Helpers;
525 TimeAxisView::build_display_menu ();
527 /* now fill it with our stuff */
529 MenuList& items = display_menu->items();
530 display_menu->set_name ("ArdourContextMenu");
532 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
534 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
536 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
538 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
540 items.push_back (SeparatorElem());
543 detach_menu (*_size_menu);
546 items.push_back (MenuElem (_("Height"), *_size_menu));
548 items.push_back (SeparatorElem());
550 if (!Profile->get_sae()) {
551 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
552 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
553 items.push_back (SeparatorElem());
556 // Hook for derived classes to add type specific stuff
557 append_extra_display_menu_items ();
561 Menu* layers_menu = manage (new Menu);
562 MenuList &layers_items = layers_menu->items();
563 layers_menu->set_name("ArdourContextMenu");
565 RadioMenuItem::Group layers_group;
567 /* Find out how many overlaid/stacked tracks we have in the selection */
571 TrackSelection const & s = _editor.get_selection().tracks;
572 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
573 StreamView* v = (*i)->view ();
578 switch (v->layer_display ()) {
589 /* We're not connecting to signal_toggled() here; in the case where these two items are
590 set to be in the `inconsistent' state, it seems that one or other will end up active
591 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
592 select the active one, no toggled signal is emitted so nothing happens.
595 _ignore_set_layer_display = true;
597 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
598 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
599 i->set_active (overlaid != 0 && stacked == 0);
600 i->set_inconsistent (overlaid != 0 && stacked != 0);
601 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
603 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
604 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
605 i->set_active (overlaid == 0 && stacked != 0);
606 i->set_inconsistent (overlaid != 0 && stacked != 0);
607 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
609 _ignore_set_layer_display = false;
611 items.push_back (MenuElem (_("Layers"), *layers_menu));
613 if (!Profile->get_sae()) {
615 Menu* alignment_menu = manage (new Menu);
616 MenuList& alignment_items = alignment_menu->items();
617 alignment_menu->set_name ("ArdourContextMenu");
619 RadioMenuItem::Group align_group;
621 /* Same verbose hacks as for the layering options above */
627 boost::shared_ptr<Track> first_track;
629 TrackSelection const & s = _editor.get_selection().tracks;
630 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
631 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
632 if (!r || !r->is_track ()) {
637 first_track = r->track();
640 switch (r->track()->alignment_choice()) {
644 switch (r->track()->alignment_style()) {
645 case ExistingMaterial:
653 case UseExistingMaterial:
669 inconsistent = false;
678 if (!inconsistent && first_track) {
680 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
681 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
682 i->set_active (automatic != 0 && existing == 0 && capture == 0);
683 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
685 switch (first_track->alignment_choice()) {
687 switch (first_track->alignment_style()) {
688 case ExistingMaterial:
689 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
692 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
700 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
701 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
702 i->set_active (existing != 0 && capture == 0 && automatic == 0);
703 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
705 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
706 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
707 i->set_active (existing == 0 && capture != 0 && automatic == 0);
708 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
710 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
716 Menu* mode_menu = manage (new Menu);
717 MenuList& mode_items = mode_menu->items ();
718 mode_menu->set_name ("ArdourContextMenu");
720 RadioMenuItem::Group mode_group;
726 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
727 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
728 if (!r || !r->is_track ()) {
732 switch (r->track()->mode()) {
745 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
746 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
747 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
748 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
749 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
751 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
752 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
753 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
754 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
755 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
757 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
758 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
759 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
760 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
761 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
763 items.push_back (MenuElem (_("Mode"), *mode_menu));
767 items.push_back (SeparatorElem());
769 build_playlist_menu ();
770 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
771 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
774 route_group_menu->detach ();
777 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
778 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
780 r.push_back (rtv->route ());
785 r.push_back (route ());
788 route_group_menu->build (r);
789 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
791 build_automation_action_menu (true);
792 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
794 items.push_back (SeparatorElem());
798 TrackSelection const & s = _editor.get_selection().tracks;
799 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
800 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
805 if (r->route()->active()) {
812 items.push_back (CheckMenuElem (_("Active")));
813 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
814 bool click_sets_active = true;
815 if (active > 0 && inactive == 0) {
816 i->set_active (true);
817 click_sets_active = false;
818 } else if (active > 0 && inactive > 0) {
819 i->set_inconsistent (true);
821 i->set_sensitive(! _session->transport_rolling());
822 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
824 items.push_back (SeparatorElem());
825 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
826 if (!Profile->get_sae()) {
827 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
829 items.push_front (SeparatorElem());
830 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
835 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
837 if (apply_to_selection) {
838 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
841 bool needs_bounce = false;
843 if (!track()->can_use_mode (mode, needs_bounce)) {
849 cerr << "would bounce this one\n";
854 track()->set_mode (mode);
859 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
861 TimeAxisView::show_timestretch (start, end, layers, layer);
871 /* check that the time selection was made in our route, or our route group.
872 remember that route_group() == 0 implies the route is *not* in a edit group.
875 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
876 /* this doesn't apply to us */
880 /* ignore it if our edit group is not active */
882 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
887 if (timestretch_rect == 0) {
888 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
889 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
890 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
893 timestretch_rect->show ();
894 timestretch_rect->raise_to_top ();
896 double const x1 = start / _editor.get_current_zoom();
897 double const x2 = (end - 1) / _editor.get_current_zoom();
899 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
900 x2, current_height() * (layers - layer) / layers));
904 RouteTimeAxisView::hide_timestretch ()
906 TimeAxisView::hide_timestretch ();
908 if (timestretch_rect) {
909 timestretch_rect->hide ();
914 RouteTimeAxisView::show_selection (TimeSelection& ts)
918 /* ignore it if our edit group is not active or if the selection was started
919 in some other track or route group (remember that route_group() == 0 means
920 that the track is not in an route group).
923 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
924 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
930 TimeAxisView::show_selection (ts);
934 RouteTimeAxisView::set_height (uint32_t h)
937 bool height_changed = (height == 0) || (h != height);
940 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
943 gm.get_level_meter().setup_meters (gmlen, meter_width);
945 TimeAxisView::set_height (h);
948 _view->set_height ((double) current_height());
951 if (height >= preset_height (HeightNormal)) {
955 gm.get_gain_slider().show();
957 if (!_route || _route->is_monitor()) {
962 if (rec_enable_button)
963 rec_enable_button->show();
965 route_group_button.show();
966 automation_button.show();
968 if (is_track() && track()->mode() == ARDOUR::Normal) {
969 playlist_button.show();
976 gm.get_gain_slider().hide();
978 if (!_route || _route->is_monitor()) {
983 if (rec_enable_button)
984 rec_enable_button->show();
986 route_group_button.hide ();
987 automation_button.hide ();
989 if (is_track() && track()->mode() == ARDOUR::Normal) {
990 playlist_button.hide ();
995 if (height_changed && !no_redraw) {
996 /* only emit the signal if the height really changed */
1002 RouteTimeAxisView::route_color_changed ()
1005 _view->apply_color (color(), StreamView::RegionColor);
1008 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1012 RouteTimeAxisView::reset_samples_per_pixel ()
1014 set_samples_per_pixel (_editor.get_current_zoom());
1018 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1023 speed = track()->speed();
1027 _view->set_samples_per_pixel (fpp * speed);
1030 TimeAxisView::set_samples_per_pixel (fpp * speed);
1034 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1036 if (!mitem->get_active()) {
1037 /* this is one of the two calls made when these radio menu items change status. this one
1038 is for the item that became inactive, and we want to ignore it.
1043 if (apply_to_selection) {
1044 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1047 track()->set_align_choice (choice);
1053 RouteTimeAxisView::rename_current_playlist ()
1055 ArdourPrompter prompter (true);
1058 boost::shared_ptr<Track> tr = track();
1059 if (!tr || tr->destructive()) {
1063 boost::shared_ptr<Playlist> pl = tr->playlist();
1068 prompter.set_title (_("Rename Playlist"));
1069 prompter.set_prompt (_("New name for playlist:"));
1070 prompter.set_initial_text (pl->name());
1071 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1072 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1074 switch (prompter.run ()) {
1075 case Gtk::RESPONSE_ACCEPT:
1076 prompter.get_result (name);
1077 if (name.length()) {
1078 pl->set_name (name);
1088 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1090 std::string ret (basename);
1092 std::string const group_string = "." + route_group()->name() + ".";
1094 // iterate through all playlists
1096 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1097 std::string tmp = (*i)->name();
1099 std::string::size_type idx = tmp.find(group_string);
1100 // find those which belong to this group
1101 if (idx != string::npos) {
1102 tmp = tmp.substr(idx + group_string.length());
1104 // and find the largest current number
1106 if (x > maxnumber) {
1115 snprintf (buf, sizeof(buf), "%d", maxnumber);
1117 ret = this->name() + "." + route_group()->name () + "." + buf;
1123 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1127 boost::shared_ptr<Track> tr = track ();
1128 if (!tr || tr->destructive()) {
1132 boost::shared_ptr<const Playlist> pl = tr->playlist();
1139 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1140 name = resolve_new_group_playlist_name(name, playlists_before_op);
1143 while (_session->playlists->by_name(name)) {
1144 name = Playlist::bump_name (name, *_session);
1147 // TODO: The prompter "new" button should be de-activated if the user
1148 // specifies a playlist name which already exists in the session.
1152 ArdourPrompter prompter (true);
1154 prompter.set_title (_("New Copy Playlist"));
1155 prompter.set_prompt (_("Name for new playlist:"));
1156 prompter.set_initial_text (name);
1157 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1158 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1159 prompter.show_all ();
1161 switch (prompter.run ()) {
1162 case Gtk::RESPONSE_ACCEPT:
1163 prompter.get_result (name);
1171 if (name.length()) {
1172 tr->use_copy_playlist ();
1173 tr->playlist()->set_name (name);
1178 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1182 boost::shared_ptr<Track> tr = track ();
1183 if (!tr || tr->destructive()) {
1187 boost::shared_ptr<const Playlist> pl = tr->playlist();
1194 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1195 name = resolve_new_group_playlist_name(name,playlists_before_op);
1198 while (_session->playlists->by_name(name)) {
1199 name = Playlist::bump_name (name, *_session);
1205 ArdourPrompter prompter (true);
1207 prompter.set_title (_("New Playlist"));
1208 prompter.set_prompt (_("Name for new playlist:"));
1209 prompter.set_initial_text (name);
1210 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1211 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1213 switch (prompter.run ()) {
1214 case Gtk::RESPONSE_ACCEPT:
1215 prompter.get_result (name);
1223 if (name.length()) {
1224 tr->use_new_playlist ();
1225 tr->playlist()->set_name (name);
1230 RouteTimeAxisView::clear_playlist ()
1232 boost::shared_ptr<Track> tr = track ();
1233 if (!tr || tr->destructive()) {
1237 boost::shared_ptr<Playlist> pl = tr->playlist();
1242 _editor.clear_playlist (pl);
1246 RouteTimeAxisView::speed_changed ()
1248 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1252 RouteTimeAxisView::update_diskstream_display ()
1262 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1264 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1266 /* special case: select/deselect all tracks */
1267 if (_editor.get_selection().selected (this)) {
1268 _editor.get_selection().clear_tracks ();
1270 _editor.select_all_tracks ();
1276 switch (ArdourKeyboard::selection_type (ev->state)) {
1277 case Selection::Toggle:
1278 _editor.get_selection().toggle (this);
1281 case Selection::Set:
1282 _editor.get_selection().set (this);
1285 case Selection::Extend:
1286 _editor.extend_selection_to_track (*this);
1289 case Selection::Add:
1290 _editor.get_selection().add (this);
1296 RouteTimeAxisView::set_selected_points (PointSelection& points)
1298 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1299 (*i)->set_selected_points (points);
1304 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1307 _view->set_selected_regionviews (regions);
1311 /** Add the selectable things that we have to a list.
1312 * @param results List to add things to.
1315 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1320 speed = track()->speed();
1323 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1324 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1326 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1327 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1330 /* pick up visible automation tracks */
1332 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1333 if (!(*i)->hidden()) {
1334 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1340 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1343 _view->get_inverted_selectables (sel, results);
1346 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1347 if (!(*i)->hidden()) {
1348 (*i)->get_inverted_selectables (sel, results);
1356 RouteTimeAxisView::route_group () const
1358 return _route->route_group();
1362 RouteTimeAxisView::name() const
1364 return _route->name();
1367 boost::shared_ptr<Playlist>
1368 RouteTimeAxisView::playlist () const
1370 boost::shared_ptr<Track> tr;
1372 if ((tr = track()) != 0) {
1373 return tr->playlist();
1375 return boost::shared_ptr<Playlist> ();
1380 RouteTimeAxisView::name_entry_changed ()
1382 TimeAxisView::name_entry_changed ();
1384 string x = name_entry->get_text ();
1386 if (x == _route->name()) {
1390 strip_whitespace_edges (x);
1392 if (x.length() == 0) {
1393 name_entry->set_text (_route->name());
1397 if (_session->route_name_internal (x)) {
1398 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1400 name_entry->grab_focus ();
1401 } else if (RouteUI::verify_new_route_name (x)) {
1402 _route->set_name (x);
1404 name_entry->grab_focus ();
1408 boost::shared_ptr<Region>
1409 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1411 boost::shared_ptr<Playlist> pl = playlist ();
1414 return pl->find_next_region (pos, point, dir);
1417 return boost::shared_ptr<Region> ();
1421 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1423 boost::shared_ptr<Playlist> pl = playlist ();
1426 return pl->find_next_region_boundary (pos, dir);
1433 RouteTimeAxisView::fade_range (TimeSelection& selection)
1435 boost::shared_ptr<Playlist> what_we_got;
1436 boost::shared_ptr<Track> tr = track ();
1437 boost::shared_ptr<Playlist> playlist;
1440 /* route is a bus, not a track */
1444 playlist = tr->playlist();
1446 TimeSelection time (selection);
1447 float const speed = tr->speed();
1448 if (speed != 1.0f) {
1449 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1450 (*i).start = session_frame_to_track_frame((*i).start, speed);
1451 (*i).end = session_frame_to_track_frame((*i).end, speed);
1455 playlist->clear_changes ();
1456 playlist->clear_owned_changes ();
1458 playlist->fade_range (time);
1460 vector<Command*> cmds;
1461 playlist->rdiff (cmds);
1462 _session->add_commands (cmds);
1463 _session->add_command (new StatefulDiffCommand (playlist));
1468 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1470 boost::shared_ptr<Playlist> what_we_got;
1471 boost::shared_ptr<Track> tr = track ();
1472 boost::shared_ptr<Playlist> playlist;
1475 /* route is a bus, not a track */
1479 playlist = tr->playlist();
1481 TimeSelection time (selection.time);
1482 float const speed = tr->speed();
1483 if (speed != 1.0f) {
1484 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1485 (*i).start = session_frame_to_track_frame((*i).start, speed);
1486 (*i).end = session_frame_to_track_frame((*i).end, speed);
1490 playlist->clear_changes ();
1491 playlist->clear_owned_changes ();
1495 if (playlist->cut (time) != 0) {
1496 if (Config->get_edit_mode() == Ripple)
1497 playlist->ripple(time.start(), -time.length(), NULL);
1498 // no need to exclude any regions from rippling here
1500 vector<Command*> cmds;
1501 playlist->rdiff (cmds);
1502 _session->add_commands (cmds);
1504 _session->add_command (new StatefulDiffCommand (playlist));
1509 if ((what_we_got = playlist->cut (time)) != 0) {
1510 _editor.get_cut_buffer().add (what_we_got);
1511 if (Config->get_edit_mode() == Ripple)
1512 playlist->ripple(time.start(), -time.length(), NULL);
1513 // no need to exclude any regions from rippling here
1515 vector<Command*> cmds;
1516 playlist->rdiff (cmds);
1517 _session->add_commands (cmds);
1519 _session->add_command (new StatefulDiffCommand (playlist));
1523 if ((what_we_got = playlist->copy (time)) != 0) {
1524 _editor.get_cut_buffer().add (what_we_got);
1529 if ((what_we_got = playlist->cut (time)) != 0) {
1530 if (Config->get_edit_mode() == Ripple)
1531 playlist->ripple(time.start(), -time.length(), NULL);
1532 // no need to exclude any regions from rippling here
1534 vector<Command*> cmds;
1535 playlist->rdiff (cmds);
1536 _session->add_commands (cmds);
1537 _session->add_command (new StatefulDiffCommand (playlist));
1538 what_we_got->release ();
1545 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1551 boost::shared_ptr<Playlist> pl = playlist ();
1552 PlaylistSelection::iterator p;
1554 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1556 if (p == selection.playlists.end()) {
1560 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1562 if (track()->speed() != 1.0f) {
1563 pos = session_frame_to_track_frame (pos, track()->speed());
1564 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1567 pl->clear_changes ();
1568 if (Config->get_edit_mode() == Ripple) {
1569 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1570 framecnt_t amount = extent.second - extent.first;
1571 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1573 pl->paste (*p, pos, times);
1575 vector<Command*> cmds;
1577 _session->add_commands (cmds);
1579 _session->add_command (new StatefulDiffCommand (pl));
1585 struct PlaylistSorter {
1586 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1587 return a->sort_id() < b->sort_id();
1592 RouteTimeAxisView::build_playlist_menu ()
1594 using namespace Menu_Helpers;
1600 delete playlist_action_menu;
1601 playlist_action_menu = new Menu;
1602 playlist_action_menu->set_name ("ArdourContextMenu");
1604 MenuList& playlist_items = playlist_action_menu->items();
1605 playlist_action_menu->set_name ("ArdourContextMenu");
1606 playlist_items.clear();
1608 RadioMenuItem::Group playlist_group;
1609 boost::shared_ptr<Track> tr = track ();
1611 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1613 /* sort the playlists */
1615 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1617 /* add the playlists to the menu */
1618 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1619 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1620 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1621 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1623 if (tr->playlist()->id() == (*i)->id()) {
1629 playlist_items.push_back (SeparatorElem());
1630 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1631 playlist_items.push_back (SeparatorElem());
1633 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1634 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1635 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1638 // Use a label which tells the user what is happening
1639 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1640 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1644 playlist_items.push_back (SeparatorElem());
1645 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1646 playlist_items.push_back (SeparatorElem());
1648 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1652 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1654 assert (is_track());
1656 // exit if we were triggered by deactivating the old playlist
1657 if (!item->get_active()) {
1661 boost::shared_ptr<Playlist> pl (wpl.lock());
1667 if (track()->playlist() == pl) {
1668 // exit when use_playlist is called by the creation of the playlist menu
1669 // or the playlist choice is unchanged
1673 track()->use_playlist (pl);
1675 RouteGroup* rg = route_group();
1677 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1678 std::string group_string = "." + rg->name() + ".";
1680 std::string take_name = pl->name();
1681 std::string::size_type idx = take_name.find(group_string);
1683 if (idx == std::string::npos)
1686 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1688 boost::shared_ptr<RouteList> rl (rg->route_list());
1690 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1691 if ((*i) == this->route()) {
1695 std::string playlist_name = (*i)->name()+group_string+take_name;
1697 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1702 if (track->freeze_state() == Track::Frozen) {
1703 /* Don't change playlists of frozen tracks */
1707 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1709 // No playlist for this track for this take yet, make it
1710 track->use_new_playlist();
1711 track->playlist()->set_name(playlist_name);
1713 track->use_playlist(ipl);
1720 RouteTimeAxisView::update_playlist_tip ()
1722 RouteGroup* rg = route_group ();
1723 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1724 string group_string = "." + rg->name() + ".";
1726 string take_name = track()->playlist()->name();
1727 string::size_type idx = take_name.find(group_string);
1729 if (idx != string::npos) {
1730 /* find the bit containing the take number / name */
1731 take_name = take_name.substr (idx + group_string.length());
1733 /* set the playlist button tooltip to the take name */
1734 ARDOUR_UI::instance()->set_tip (
1736 string_compose(_("Take: %1.%2"),
1737 Glib::Markup::escape_text(rg->name()),
1738 Glib::Markup::escape_text(take_name))
1745 /* set the playlist button tooltip to the playlist name */
1746 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1751 RouteTimeAxisView::show_playlist_selector ()
1753 _editor.playlist_selector().show_for (this);
1757 RouteTimeAxisView::map_frozen ()
1763 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1765 switch (track()->freeze_state()) {
1767 playlist_button.set_sensitive (false);
1768 rec_enable_button->set_sensitive (false);
1771 playlist_button.set_sensitive (true);
1772 rec_enable_button->set_sensitive (true);
1778 RouteTimeAxisView::color_handler ()
1780 //case cTimeStretchOutline:
1781 if (timestretch_rect) {
1782 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1784 //case cTimeStretchFill:
1785 if (timestretch_rect) {
1786 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1792 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1793 * Will add track if necessary.
1796 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1798 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1799 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1802 /* it doesn't exist yet, so we don't care about the button state: just add it */
1803 create_automation_child (param, true);
1806 bool yn = menu->get_active();
1807 bool changed = false;
1809 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1811 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1812 will have done that for us.
1815 if (changed && !no_redraw) {
1823 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1825 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1831 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1833 if (menu && !_hidden) {
1834 ignore_toggle = true;
1835 menu->set_active (false);
1836 ignore_toggle = false;
1839 if (_route && !no_redraw) {
1846 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1848 if (apply_to_selection) {
1849 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1853 /* Show our automation */
1855 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1856 i->second->set_marked_for_display (true);
1858 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1861 menu->set_active(true);
1866 /* Show processor automation */
1868 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1869 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1870 if ((*ii)->view == 0) {
1871 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1874 (*ii)->menu_item->set_active (true);
1887 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1889 if (apply_to_selection) {
1890 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1894 /* Show our automation */
1896 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1897 if (i->second->has_automation()) {
1898 i->second->set_marked_for_display (true);
1900 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1902 menu->set_active(true);
1907 /* Show processor automation */
1909 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1910 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1911 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1912 (*ii)->menu_item->set_active (true);
1924 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1926 if (apply_to_selection) {
1927 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1931 /* Hide our automation */
1933 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1934 i->second->set_marked_for_display (false);
1936 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1939 menu->set_active (false);
1943 /* Hide processor automation */
1945 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1946 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1947 (*ii)->menu_item->set_active (false);
1958 RouteTimeAxisView::region_view_added (RegionView* rv)
1960 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1961 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1962 boost::shared_ptr<AutomationTimeAxisView> atv;
1964 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1969 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1970 (*i)->add_ghost(rv);
1974 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1976 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1982 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1984 parent.remove_processor_automation_node (this);
1988 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1991 remove_child (pan->view);
1995 RouteTimeAxisView::ProcessorAutomationNode*
1996 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1998 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2000 if ((*i)->processor == processor) {
2002 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2003 if ((*ii)->what == what) {
2013 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2015 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2018 ProcessorAutomationNode* pan;
2020 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2021 /* session state may never have been saved with new plugin */
2022 error << _("programming error: ")
2023 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2024 processor->name(), what.type(), (int) what.channel(), what.id() )
2034 boost::shared_ptr<AutomationControl> control
2035 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2037 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2038 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2039 _editor, *this, false, parent_canvas,
2040 processor->describe_parameter (what), processor->name()));
2042 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2044 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2047 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2052 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2055 pan->menu_item->set_active (false);
2064 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2066 boost::shared_ptr<Processor> processor (p.lock ());
2068 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2069 /* The Amp processor is a special case and is dealt with separately */
2073 set<Evoral::Parameter> existing;
2075 processor->what_has_data (existing);
2077 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2079 Evoral::Parameter param (*i);
2080 boost::shared_ptr<AutomationLine> al;
2082 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2085 add_processor_automation_curve (processor, param);
2091 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2093 using namespace Menu_Helpers;
2097 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2099 _automation_tracks[param] = track;
2101 /* existing state overrides "show" argument */
2102 string s = track->gui_property ("visible");
2104 show = string_is_affirmative (s);
2107 /* this might or might not change the visibility status, so don't rely on it */
2108 track->set_marked_for_display (show);
2110 if (show && !no_redraw) {
2114 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2115 /* MIDI-related parameters are always in the menu, there's no
2116 reason to rebuild the menu just because we added a automation
2117 lane for one of them. But if we add a non-MIDI automation
2118 lane, then we need to invalidate the display menu.
2120 delete display_menu;
2126 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2128 boost::shared_ptr<Processor> processor (p.lock ());
2130 if (!processor || !processor->display_to_user ()) {
2134 /* we use this override to veto the Amp processor from the plugin menu,
2135 as its automation lane can be accessed using the special "Fader" menu
2139 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2143 using namespace Menu_Helpers;
2144 ProcessorAutomationInfo *rai;
2145 list<ProcessorAutomationInfo*>::iterator x;
2147 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2149 if (automatable.empty()) {
2153 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2154 if ((*x)->processor == processor) {
2159 if (x == processor_automation.end()) {
2161 rai = new ProcessorAutomationInfo (processor);
2162 processor_automation.push_back (rai);
2170 /* any older menu was deleted at the top of processors_changed()
2171 when we cleared the subplugin menu.
2174 rai->menu = manage (new Menu);
2175 MenuList& items = rai->menu->items();
2176 rai->menu->set_name ("ArdourContextMenu");
2180 std::set<Evoral::Parameter> has_visible_automation;
2181 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2183 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2185 ProcessorAutomationNode* pan;
2186 Gtk::CheckMenuItem* mitem;
2188 string name = processor->describe_parameter (*i);
2190 items.push_back (CheckMenuElem (name));
2191 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2193 _subplugin_menu_map[*i] = mitem;
2195 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2196 mitem->set_active(true);
2199 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2203 pan = new ProcessorAutomationNode (*i, mitem, *this);
2205 rai->lines.push_back (pan);
2209 pan->menu_item = mitem;
2213 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2216 /* add the menu for this processor, because the subplugin
2217 menu is always cleared at the top of processors_changed().
2218 this is the result of some poor design in gtkmm and/or
2222 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2227 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2228 RouteTimeAxisView::ProcessorAutomationNode* pan)
2230 bool showit = pan->menu_item->get_active();
2231 bool redraw = false;
2233 if (pan->view == 0 && showit) {
2234 add_processor_automation_curve (rai->processor, pan->what);
2238 if (pan->view && pan->view->set_marked_for_display (showit)) {
2242 if (redraw && !no_redraw) {
2248 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2250 if (c.type == RouteProcessorChange::MeterPointChange) {
2251 /* nothing to do if only the meter point has changed */
2255 using namespace Menu_Helpers;
2257 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2258 (*i)->valid = false;
2261 setup_processor_menu_and_curves ();
2263 bool deleted_processor_automation = false;
2265 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2267 list<ProcessorAutomationInfo*>::iterator tmp;
2275 processor_automation.erase (i);
2276 deleted_processor_automation = true;
2283 if (deleted_processor_automation && !no_redraw) {
2288 boost::shared_ptr<AutomationLine>
2289 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2291 ProcessorAutomationNode* pan;
2293 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2299 return boost::shared_ptr<AutomationLine>();
2303 RouteTimeAxisView::reset_processor_automation_curves ()
2305 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2311 RouteTimeAxisView::can_edit_name () const
2313 /* we do not allow track name changes if it is record enabled
2315 return !_route->record_enabled();
2319 RouteTimeAxisView::blink_rec_display (bool onoff)
2321 RouteUI::blink_rec_display (onoff);
2325 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2327 if (_ignore_set_layer_display) {
2331 if (apply_to_selection) {
2332 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2336 _view->set_layer_display (d);
2339 set_gui_property (X_("layer-display"), enum_2_string (d));
2344 RouteTimeAxisView::layer_display () const
2347 return _view->layer_display ();
2350 /* we don't know, since we don't have a _view, so just return something */
2356 boost::shared_ptr<AutomationTimeAxisView>
2357 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2359 AutomationTracks::iterator i = _automation_tracks.find(param);
2360 if (i != _automation_tracks.end()) {
2363 return boost::shared_ptr<AutomationTimeAxisView>();
2368 RouteTimeAxisView::fast_update ()
2370 gm.get_level_meter().update_meters ();
2374 RouteTimeAxisView::hide_meter ()
2377 gm.get_level_meter().hide_meters ();
2381 RouteTimeAxisView::show_meter ()
2387 RouteTimeAxisView::reset_meter ()
2389 if (Config->get_show_track_meters()) {
2390 int meter_width = 3;
2391 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2394 gm.get_level_meter().setup_meters (height - 9, meter_width);
2401 RouteTimeAxisView::clear_meter ()
2403 gm.get_level_meter().clear_meters ();
2407 RouteTimeAxisView::meter_changed ()
2409 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2411 if (_route && !no_redraw) {
2414 // reset peak when meter point changes
2415 gm.reset_peak_display();
2419 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2422 if (_route && !no_redraw) {
2428 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2430 using namespace Menu_Helpers;
2432 if (!_underlay_streams.empty()) {
2433 MenuList& parent_items = parent_menu->items();
2434 Menu* gs_menu = manage (new Menu);
2435 gs_menu->set_name ("ArdourContextMenu");
2436 MenuList& gs_items = gs_menu->items();
2438 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2440 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2441 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2442 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2448 RouteTimeAxisView::set_underlay_state()
2450 if (!underlay_xml_node) {
2454 XMLNodeList nlist = underlay_xml_node->children();
2455 XMLNodeConstIterator niter;
2456 XMLNode *child_node;
2458 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2459 child_node = *niter;
2461 if (child_node->name() != "Underlay") {
2465 XMLProperty* prop = child_node->property ("id");
2467 PBD::ID id (prop->value());
2469 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2472 add_underlay(v->view(), false);
2481 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2487 RouteTimeAxisView& other = v->trackview();
2489 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2490 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2491 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2495 _underlay_streams.push_back(v);
2496 other._underlay_mirrors.push_back(this);
2498 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2500 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2502 if (!underlay_xml_node) {
2503 underlay_xml_node = xml_node->add_child("Underlays");
2506 XMLNode* node = underlay_xml_node->add_child("Underlay");
2507 XMLProperty* prop = node->add_property("id");
2508 prop->set_value(v->trackview().route()->id().to_s());
2515 RouteTimeAxisView::remove_underlay (StreamView* v)
2521 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2522 RouteTimeAxisView& other = v->trackview();
2524 if (it != _underlay_streams.end()) {
2525 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2527 if (gm == other._underlay_mirrors.end()) {
2528 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2532 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2534 _underlay_streams.erase(it);
2535 other._underlay_mirrors.erase(gm);
2537 if (underlay_xml_node) {
2538 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2544 RouteTimeAxisView::set_button_names ()
2546 if (_route && _route->solo_safe()) {
2547 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2549 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2551 if (Config->get_solo_control_is_listen_control()) {
2552 switch (Config->get_listen_position()) {
2553 case AfterFaderListen:
2554 solo_button->set_text (_("A"));
2555 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2557 case PreFaderListen:
2558 solo_button->set_text (_("P"));
2559 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2563 solo_button->set_text (_("S"));
2564 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2566 mute_button->set_text (_("M"));
2570 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2572 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2573 if (i != _main_automation_menu_map.end()) {
2577 i = _subplugin_menu_map.find (param);
2578 if (i != _subplugin_menu_map.end()) {
2586 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2588 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2590 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2594 gain_track.reset (new AutomationTimeAxisView (_session,
2595 _route, _route->amp(), c, param,
2600 _route->amp()->describe_parameter(param)));
2603 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2606 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2610 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2612 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2614 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2618 mute_track.reset (new AutomationTimeAxisView (_session,
2619 _route, _route, c, param,
2624 _route->describe_parameter(param)));
2627 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2630 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2634 void add_region_to_list (RegionView* rv, RegionList* l)
2636 l->push_back (rv->region());
2640 RouteTimeAxisView::combine_regions ()
2642 /* as of may 2011, we do not offer uncombine for MIDI tracks
2645 if (!is_audio_track()) {
2653 RegionList selected_regions;
2654 boost::shared_ptr<Playlist> playlist = track()->playlist();
2656 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2658 if (selected_regions.size() < 2) {
2662 playlist->clear_changes ();
2663 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2665 _session->add_command (new StatefulDiffCommand (playlist));
2666 /* make the new region be selected */
2668 return _view->find_view (compound_region);
2672 RouteTimeAxisView::uncombine_regions ()
2674 /* as of may 2011, we do not offer uncombine for MIDI tracks
2676 if (!is_audio_track()) {
2684 RegionList selected_regions;
2685 boost::shared_ptr<Playlist> playlist = track()->playlist();
2687 /* have to grab selected regions first because the uncombine is going
2688 * to change that in the middle of the list traverse
2691 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2693 playlist->clear_changes ();
2695 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2696 playlist->uncombine (*i);
2699 _session->add_command (new StatefulDiffCommand (playlist));
2703 RouteTimeAxisView::state_id() const
2705 return string_compose ("rtav %1", _route->id().to_s());
2710 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2712 TimeAxisView::remove_child (c);
2714 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2716 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2717 if (i->second == a) {
2718 _automation_tracks.erase (i);