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("route button");
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);
116 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
122 RouteUI::set_route (rt);
124 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
125 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
126 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
129 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
132 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133 gm.get_level_meter().set_no_show_all();
134 gm.get_level_meter().setup_meters(50, meter_width);
135 gm.update_gain_sensitive ();
137 string str = gui_property ("height");
139 set_height (atoi (str));
141 set_height (preset_height (HeightNormal));
144 if (!_route->is_auditioner()) {
145 if (gui_property ("visible").empty()) {
146 set_gui_property ("visible", true);
149 set_gui_property ("visible", false);
153 update_solo_display ();
155 timestretch_rect = 0;
158 ignore_toggle = false;
160 route_group_button.set_name ("route button");
161 playlist_button.set_name ("route button");
162 automation_button.set_name ("route button");
164 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
165 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
166 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
170 if (ARDOUR::Profile->get_mixbus()) {
171 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
173 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
175 controls_button_size_group->add_widget(*rec_enable_button);
177 if (is_midi_track()) {
178 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
179 gm.set_fader_name ("MidiTrackFader");
181 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
182 gm.set_fader_name ("AudioTrackFader");
185 rec_enable_button->set_sensitive (_session->writable());
187 /* set playlist button tip to the current playlist, and make it update when it changes */
188 update_playlist_tip ();
189 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
192 gm.set_fader_name ("AudioBusFader");
193 Gtk::Fixed *blank = manage(new Gtk::Fixed());
194 controls_button_size_group->add_widget(*blank);
195 if (ARDOUR::Profile->get_mixbus() ) {
196 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
198 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
203 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
205 if (!ARDOUR::Profile->get_mixbus()) {
206 controls_meters_size_group->add_widget (gm.get_level_meter());
209 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
210 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
214 if (ARDOUR::Profile->get_mixbus()) {
215 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
219 controls_button_size_group->add_widget(*mute_button);
221 if (!_route->is_master()) {
222 if (ARDOUR::Profile->get_mixbus()) {
223 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
227 controls_button_size_group->add_widget(*solo_button);
229 Gtk::Fixed *blank = manage(new Gtk::Fixed());
230 controls_button_size_group->add_widget(*blank);
231 if (ARDOUR::Profile->get_mixbus()) {
232 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_button_size_group->add_widget(route_group_button);
241 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
244 else if (!ARDOUR::Profile->get_trx()) {
245 controls_button_size_group->add_widget(route_group_button);
246 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
251 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
252 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
254 if (is_midi_track()) {
255 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
257 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
260 update_track_number_visibility();
263 if (ARDOUR::Profile->get_mixbus()) {
264 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
265 controls_button_size_group->add_widget(automation_button);
267 else if (!ARDOUR::Profile->get_trx()) {
268 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
269 controls_button_size_group->add_widget(automation_button);
272 if (is_track() && track()->mode() == ARDOUR::Normal) {
273 if (ARDOUR::Profile->get_mixbus()) {
274 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
275 controls_button_size_group->add_widget(playlist_button);
277 else if (!ARDOUR::Profile->get_trx()) {
278 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 controls_button_size_group->add_widget(playlist_button);
285 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
286 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
290 str = gui_property ("layer-display");
292 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
295 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
296 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
298 /* pick up the correct freeze state */
303 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
304 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
306 PropertyList* plist = new PropertyList();
308 plist->add (ARDOUR::Properties::mute, true);
309 plist->add (ARDOUR::Properties::solo, true);
311 route_group_menu = new RouteGroupMenu (_session, plist);
313 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
315 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
318 RouteTimeAxisView::~RouteTimeAxisView ()
320 CatchDeletion (this);
322 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
326 delete playlist_action_menu;
327 playlist_action_menu = 0;
332 _automation_tracks.clear ();
334 delete route_group_menu;
338 RouteTimeAxisView::post_construct ()
340 /* map current state of the route */
342 update_diskstream_display ();
343 setup_processor_menu_and_curves ();
344 reset_processor_automation_curves ();
347 /** Set up the processor menu for the current set of processors, and
348 * display automation curves for any parameters which have data.
351 RouteTimeAxisView::setup_processor_menu_and_curves ()
353 _subplugin_menu_map.clear ();
354 subplugin_menu.items().clear ();
355 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
356 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
360 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
362 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
363 if (_route->route_group()) {
364 _route->route_group()->remove (_route);
370 r.push_back (route ());
372 route_group_menu->build (r);
373 route_group_menu->menu()->popup (ev->button, ev->time);
379 RouteTimeAxisView::playlist_changed ()
385 RouteTimeAxisView::label_view ()
387 string x = _route->name ();
388 if (x != name_label.get_text ()) {
389 name_label.set_text (x);
391 const int64_t track_number = _route->track_number ();
392 if (track_number == 0) {
393 number_label.set_text ("");
395 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
400 RouteTimeAxisView::update_track_number_visibility ()
403 bool show_label = _session->config.get_track_name_number();
405 if (_route && _route->is_master()) {
409 if (number_label.get_parent()) {
410 controls_table.remove (number_label);
413 if (ARDOUR::Profile->get_mixbus()) {
414 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
416 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
418 const int tnw = 9 + std::max(2u, _session->track_number_decimals()) * number_label.char_pixel_width();
419 number_label.set_size_request(tnw, -1);
420 number_label.show ();
421 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, 0); // -2 = cellspacing
423 number_label.hide ();
424 name_hbox.set_size_request(TimeAxisView::name_width_px, 0);
429 RouteTimeAxisView::parameter_changed (string const & p)
431 if (p == "track-name-number") {
432 update_track_number_visibility();
437 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
439 if (what_changed.contains (ARDOUR::Properties::name)) {
445 RouteTimeAxisView::take_name_changed (void *src)
453 RouteTimeAxisView::playlist_click ()
455 build_playlist_menu ();
456 conditionally_add_to_selection ();
457 playlist_action_menu->popup (1, gtk_get_current_event_time());
461 RouteTimeAxisView::automation_click ()
463 conditionally_add_to_selection ();
464 build_automation_action_menu (false);
465 automation_action_menu->popup (1, gtk_get_current_event_time());
469 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
471 using namespace Menu_Helpers;
473 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
474 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
477 detach_menu (subplugin_menu);
479 _main_automation_menu_map.clear ();
480 delete automation_action_menu;
481 automation_action_menu = new Menu;
483 MenuList& items = automation_action_menu->items();
485 automation_action_menu->set_name ("ArdourContextMenu");
487 items.push_back (MenuElem (_("Show All Automation"),
488 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
490 items.push_back (MenuElem (_("Show Existing Automation"),
491 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
493 items.push_back (MenuElem (_("Hide All Automation"),
494 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
496 /* Attach the plugin submenu. It may have previously been used elsewhere,
497 so it was detached above
500 if (!subplugin_menu.items().empty()) {
501 items.push_back (SeparatorElem ());
502 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
503 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
508 RouteTimeAxisView::build_display_menu ()
510 using namespace Menu_Helpers;
514 TimeAxisView::build_display_menu ();
516 /* now fill it with our stuff */
518 MenuList& items = display_menu->items();
519 display_menu->set_name ("ArdourContextMenu");
521 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
523 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
525 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
527 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
529 items.push_back (SeparatorElem());
532 detach_menu (*_size_menu);
535 items.push_back (MenuElem (_("Height"), *_size_menu));
537 items.push_back (SeparatorElem());
539 if (!Profile->get_sae()) {
540 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
541 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
542 items.push_back (SeparatorElem());
545 // Hook for derived classes to add type specific stuff
546 append_extra_display_menu_items ();
550 Menu* layers_menu = manage (new Menu);
551 MenuList &layers_items = layers_menu->items();
552 layers_menu->set_name("ArdourContextMenu");
554 RadioMenuItem::Group layers_group;
556 /* Find out how many overlaid/stacked tracks we have in the selection */
560 TrackSelection const & s = _editor.get_selection().tracks;
561 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
562 StreamView* v = (*i)->view ();
567 switch (v->layer_display ()) {
578 /* We're not connecting to signal_toggled() here; in the case where these two items are
579 set to be in the `inconsistent' state, it seems that one or other will end up active
580 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
581 select the active one, no toggled signal is emitted so nothing happens.
584 _ignore_set_layer_display = true;
586 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
587 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
588 i->set_active (overlaid != 0 && stacked == 0);
589 i->set_inconsistent (overlaid != 0 && stacked != 0);
590 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
592 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
593 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
594 i->set_active (overlaid == 0 && stacked != 0);
595 i->set_inconsistent (overlaid != 0 && stacked != 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
598 _ignore_set_layer_display = false;
600 items.push_back (MenuElem (_("Layers"), *layers_menu));
602 if (!Profile->get_sae()) {
604 Menu* alignment_menu = manage (new Menu);
605 MenuList& alignment_items = alignment_menu->items();
606 alignment_menu->set_name ("ArdourContextMenu");
608 RadioMenuItem::Group align_group;
610 /* Same verbose hacks as for the layering options above */
616 boost::shared_ptr<Track> first_track;
618 TrackSelection const & s = _editor.get_selection().tracks;
619 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
620 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
621 if (!r || !r->is_track ()) {
626 first_track = r->track();
629 switch (r->track()->alignment_choice()) {
633 switch (r->track()->alignment_style()) {
634 case ExistingMaterial:
642 case UseExistingMaterial:
658 inconsistent = false;
667 if (!inconsistent && first_track) {
669 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
670 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
671 i->set_active (automatic != 0 && existing == 0 && capture == 0);
672 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
674 switch (first_track->alignment_choice()) {
676 switch (first_track->alignment_style()) {
677 case ExistingMaterial:
678 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
681 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
689 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
690 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
691 i->set_active (existing != 0 && capture == 0 && automatic == 0);
692 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
694 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
695 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
696 i->set_active (existing == 0 && capture != 0 && automatic == 0);
697 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
699 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
705 Menu* mode_menu = manage (new Menu);
706 MenuList& mode_items = mode_menu->items ();
707 mode_menu->set_name ("ArdourContextMenu");
709 RadioMenuItem::Group mode_group;
715 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
716 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
717 if (!r || !r->is_track ()) {
721 switch (r->track()->mode()) {
734 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
735 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
736 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
737 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
738 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
740 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
741 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
742 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
743 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
744 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
746 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
747 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
748 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
749 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
750 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
752 items.push_back (MenuElem (_("Mode"), *mode_menu));
756 items.push_back (SeparatorElem());
758 build_playlist_menu ();
759 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
760 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
763 route_group_menu->detach ();
766 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
767 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
769 r.push_back (rtv->route ());
774 r.push_back (route ());
777 route_group_menu->build (r);
778 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
780 build_automation_action_menu (true);
781 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
783 items.push_back (SeparatorElem());
787 TrackSelection const & s = _editor.get_selection().tracks;
788 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
789 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
794 if (r->route()->active()) {
801 items.push_back (CheckMenuElem (_("Active")));
802 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
803 bool click_sets_active = true;
804 if (active > 0 && inactive == 0) {
805 i->set_active (true);
806 click_sets_active = false;
807 } else if (active > 0 && inactive > 0) {
808 i->set_inconsistent (true);
810 i->set_sensitive(! _session->transport_rolling());
811 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
813 items.push_back (SeparatorElem());
814 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
815 if (!Profile->get_sae()) {
816 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
818 items.push_front (SeparatorElem());
819 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
824 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
826 if (apply_to_selection) {
827 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
830 bool needs_bounce = false;
832 if (!track()->can_use_mode (mode, needs_bounce)) {
838 cerr << "would bounce this one\n";
843 track()->set_mode (mode);
848 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
850 TimeAxisView::show_timestretch (start, end, layers, layer);
860 /* check that the time selection was made in our route, or our route group.
861 remember that route_group() == 0 implies the route is *not* in a edit group.
864 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
865 /* this doesn't apply to us */
869 /* ignore it if our edit group is not active */
871 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
876 if (timestretch_rect == 0) {
877 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
878 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
879 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
882 timestretch_rect->show ();
883 timestretch_rect->raise_to_top ();
885 double const x1 = start / _editor.get_current_zoom();
886 double const x2 = (end - 1) / _editor.get_current_zoom();
888 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
889 x2, current_height() * (layers - layer) / layers));
893 RouteTimeAxisView::hide_timestretch ()
895 TimeAxisView::hide_timestretch ();
897 if (timestretch_rect) {
898 timestretch_rect->hide ();
903 RouteTimeAxisView::show_selection (TimeSelection& ts)
907 /* ignore it if our edit group is not active or if the selection was started
908 in some other track or route group (remember that route_group() == 0 means
909 that the track is not in an route group).
912 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
913 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
919 TimeAxisView::show_selection (ts);
923 RouteTimeAxisView::set_height (uint32_t h)
926 bool height_changed = (height == 0) || (h != height);
929 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
932 gm.get_level_meter().setup_meters (gmlen, meter_width);
934 TimeAxisView::set_height (h);
937 _view->set_height ((double) current_height());
940 if (height >= preset_height (HeightNormal)) {
944 gm.get_gain_slider().show();
946 if (!_route || _route->is_monitor()) {
951 if (rec_enable_button)
952 rec_enable_button->show();
954 route_group_button.show();
955 automation_button.show();
957 if (is_track() && track()->mode() == ARDOUR::Normal) {
958 playlist_button.show();
965 gm.get_gain_slider().hide();
967 if (!_route || _route->is_monitor()) {
972 if (rec_enable_button)
973 rec_enable_button->show();
975 route_group_button.hide ();
976 automation_button.hide ();
978 if (is_track() && track()->mode() == ARDOUR::Normal) {
979 playlist_button.hide ();
984 if (height_changed && !no_redraw) {
985 /* only emit the signal if the height really changed */
991 RouteTimeAxisView::route_color_changed ()
994 _view->apply_color (color(), StreamView::RegionColor);
997 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1001 RouteTimeAxisView::reset_samples_per_pixel ()
1003 set_samples_per_pixel (_editor.get_current_zoom());
1007 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1012 speed = track()->speed();
1016 _view->set_samples_per_pixel (fpp * speed);
1019 TimeAxisView::set_samples_per_pixel (fpp * speed);
1023 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1025 if (!mitem->get_active()) {
1026 /* this is one of the two calls made when these radio menu items change status. this one
1027 is for the item that became inactive, and we want to ignore it.
1032 if (apply_to_selection) {
1033 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1036 track()->set_align_choice (choice);
1042 RouteTimeAxisView::rename_current_playlist ()
1044 ArdourPrompter prompter (true);
1047 boost::shared_ptr<Track> tr = track();
1048 if (!tr || tr->destructive()) {
1052 boost::shared_ptr<Playlist> pl = tr->playlist();
1057 prompter.set_title (_("Rename Playlist"));
1058 prompter.set_prompt (_("New name for playlist:"));
1059 prompter.set_initial_text (pl->name());
1060 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1061 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1063 switch (prompter.run ()) {
1064 case Gtk::RESPONSE_ACCEPT:
1065 prompter.get_result (name);
1066 if (name.length()) {
1067 pl->set_name (name);
1077 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1079 std::string ret (basename);
1081 std::string const group_string = "." + route_group()->name() + ".";
1083 // iterate through all playlists
1085 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1086 std::string tmp = (*i)->name();
1088 std::string::size_type idx = tmp.find(group_string);
1089 // find those which belong to this group
1090 if (idx != string::npos) {
1091 tmp = tmp.substr(idx + group_string.length());
1093 // and find the largest current number
1095 if (x > maxnumber) {
1104 snprintf (buf, sizeof(buf), "%d", maxnumber);
1106 ret = this->name() + "." + route_group()->name () + "." + buf;
1112 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1116 boost::shared_ptr<Track> tr = track ();
1117 if (!tr || tr->destructive()) {
1121 boost::shared_ptr<const Playlist> pl = tr->playlist();
1128 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1129 name = resolve_new_group_playlist_name(name, playlists_before_op);
1132 while (_session->playlists->by_name(name)) {
1133 name = Playlist::bump_name (name, *_session);
1136 // TODO: The prompter "new" button should be de-activated if the user
1137 // specifies a playlist name which already exists in the session.
1141 ArdourPrompter prompter (true);
1143 prompter.set_title (_("New Copy Playlist"));
1144 prompter.set_prompt (_("Name for new playlist:"));
1145 prompter.set_initial_text (name);
1146 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1147 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1148 prompter.show_all ();
1150 switch (prompter.run ()) {
1151 case Gtk::RESPONSE_ACCEPT:
1152 prompter.get_result (name);
1160 if (name.length()) {
1161 tr->use_copy_playlist ();
1162 tr->playlist()->set_name (name);
1167 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1171 boost::shared_ptr<Track> tr = track ();
1172 if (!tr || tr->destructive()) {
1176 boost::shared_ptr<const Playlist> pl = tr->playlist();
1183 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1184 name = resolve_new_group_playlist_name(name,playlists_before_op);
1187 while (_session->playlists->by_name(name)) {
1188 name = Playlist::bump_name (name, *_session);
1194 ArdourPrompter prompter (true);
1196 prompter.set_title (_("New Playlist"));
1197 prompter.set_prompt (_("Name for new playlist:"));
1198 prompter.set_initial_text (name);
1199 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1200 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1202 switch (prompter.run ()) {
1203 case Gtk::RESPONSE_ACCEPT:
1204 prompter.get_result (name);
1212 if (name.length()) {
1213 tr->use_new_playlist ();
1214 tr->playlist()->set_name (name);
1219 RouteTimeAxisView::clear_playlist ()
1221 boost::shared_ptr<Track> tr = track ();
1222 if (!tr || tr->destructive()) {
1226 boost::shared_ptr<Playlist> pl = tr->playlist();
1231 _editor.clear_playlist (pl);
1235 RouteTimeAxisView::speed_changed ()
1237 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1241 RouteTimeAxisView::update_diskstream_display ()
1251 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1253 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1255 /* special case: select/deselect all tracks */
1256 if (_editor.get_selection().selected (this)) {
1257 _editor.get_selection().clear_tracks ();
1259 _editor.select_all_tracks ();
1265 switch (ArdourKeyboard::selection_type (ev->state)) {
1266 case Selection::Toggle:
1267 _editor.get_selection().toggle (this);
1270 case Selection::Set:
1271 _editor.get_selection().set (this);
1274 case Selection::Extend:
1275 _editor.extend_selection_to_track (*this);
1278 case Selection::Add:
1279 _editor.get_selection().add (this);
1285 RouteTimeAxisView::set_selected_points (PointSelection& points)
1287 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1288 (*i)->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 (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1309 speed = track()->speed();
1312 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1313 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1315 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1316 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1319 /* pick up visible automation tracks */
1321 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1322 if (!(*i)->hidden()) {
1323 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1329 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1332 _view->get_inverted_selectables (sel, results);
1335 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1336 if (!(*i)->hidden()) {
1337 (*i)->get_inverted_selectables (sel, results);
1345 RouteTimeAxisView::route_group () const
1347 return _route->route_group();
1351 RouteTimeAxisView::name() const
1353 return _route->name();
1356 boost::shared_ptr<Playlist>
1357 RouteTimeAxisView::playlist () const
1359 boost::shared_ptr<Track> tr;
1361 if ((tr = track()) != 0) {
1362 return tr->playlist();
1364 return boost::shared_ptr<Playlist> ();
1369 RouteTimeAxisView::name_entry_changed ()
1371 TimeAxisView::name_entry_changed ();
1373 string x = name_entry->get_text ();
1375 if (x == _route->name()) {
1379 strip_whitespace_edges (x);
1381 if (x.length() == 0) {
1382 name_entry->set_text (_route->name());
1386 if (_session->route_name_internal (x)) {
1387 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1389 name_entry->grab_focus ();
1390 } else if (RouteUI::verify_new_route_name (x)) {
1391 _route->set_name (x);
1393 name_entry->grab_focus ();
1397 boost::shared_ptr<Region>
1398 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1400 boost::shared_ptr<Playlist> pl = playlist ();
1403 return pl->find_next_region (pos, point, dir);
1406 return boost::shared_ptr<Region> ();
1410 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1412 boost::shared_ptr<Playlist> pl = playlist ();
1415 return pl->find_next_region_boundary (pos, dir);
1422 RouteTimeAxisView::fade_range (TimeSelection& selection)
1424 boost::shared_ptr<Playlist> what_we_got;
1425 boost::shared_ptr<Track> tr = track ();
1426 boost::shared_ptr<Playlist> playlist;
1429 /* route is a bus, not a track */
1433 playlist = tr->playlist();
1435 TimeSelection time (selection);
1436 float const speed = tr->speed();
1437 if (speed != 1.0f) {
1438 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1439 (*i).start = session_frame_to_track_frame((*i).start, speed);
1440 (*i).end = session_frame_to_track_frame((*i).end, speed);
1444 playlist->clear_changes ();
1445 playlist->clear_owned_changes ();
1447 playlist->fade_range (time);
1449 vector<Command*> cmds;
1450 playlist->rdiff (cmds);
1451 _session->add_commands (cmds);
1452 _session->add_command (new StatefulDiffCommand (playlist));
1457 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1459 boost::shared_ptr<Playlist> what_we_got;
1460 boost::shared_ptr<Track> tr = track ();
1461 boost::shared_ptr<Playlist> playlist;
1464 /* route is a bus, not a track */
1468 playlist = tr->playlist();
1470 TimeSelection time (selection.time);
1471 float const speed = tr->speed();
1472 if (speed != 1.0f) {
1473 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1474 (*i).start = session_frame_to_track_frame((*i).start, speed);
1475 (*i).end = session_frame_to_track_frame((*i).end, speed);
1479 playlist->clear_changes ();
1480 playlist->clear_owned_changes ();
1484 if (playlist->cut (time) != 0) {
1485 if (Config->get_edit_mode() == Ripple)
1486 playlist->ripple(time.start(), -time.length(), NULL);
1487 // no need to exclude any regions from rippling here
1489 vector<Command*> cmds;
1490 playlist->rdiff (cmds);
1491 _session->add_commands (cmds);
1493 _session->add_command (new StatefulDiffCommand (playlist));
1498 if ((what_we_got = playlist->cut (time)) != 0) {
1499 _editor.get_cut_buffer().add (what_we_got);
1500 if (Config->get_edit_mode() == Ripple)
1501 playlist->ripple(time.start(), -time.length(), NULL);
1502 // no need to exclude any regions from rippling here
1504 vector<Command*> cmds;
1505 playlist->rdiff (cmds);
1506 _session->add_commands (cmds);
1508 _session->add_command (new StatefulDiffCommand (playlist));
1512 if ((what_we_got = playlist->copy (time)) != 0) {
1513 _editor.get_cut_buffer().add (what_we_got);
1518 if ((what_we_got = playlist->cut (time)) != 0) {
1519 if (Config->get_edit_mode() == Ripple)
1520 playlist->ripple(time.start(), -time.length(), NULL);
1521 // no need to exclude any regions from rippling here
1523 vector<Command*> cmds;
1524 playlist->rdiff (cmds);
1525 _session->add_commands (cmds);
1526 _session->add_command (new StatefulDiffCommand (playlist));
1527 what_we_got->release ();
1534 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1540 boost::shared_ptr<Playlist> pl = playlist ();
1541 PlaylistSelection::iterator p;
1543 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1545 if (p == selection.playlists.end()) {
1549 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1551 if (track()->speed() != 1.0f) {
1552 pos = session_frame_to_track_frame (pos, track()->speed());
1553 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1556 pl->clear_changes ();
1557 if (Config->get_edit_mode() == Ripple) {
1558 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1559 framecnt_t amount = extent.second - extent.first;
1560 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1562 pl->paste (*p, pos, times);
1564 vector<Command*> cmds;
1566 _session->add_commands (cmds);
1568 _session->add_command (new StatefulDiffCommand (pl));
1574 struct PlaylistSorter {
1575 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1576 return a->sort_id() < b->sort_id();
1581 RouteTimeAxisView::build_playlist_menu ()
1583 using namespace Menu_Helpers;
1589 delete playlist_action_menu;
1590 playlist_action_menu = new Menu;
1591 playlist_action_menu->set_name ("ArdourContextMenu");
1593 MenuList& playlist_items = playlist_action_menu->items();
1594 playlist_action_menu->set_name ("ArdourContextMenu");
1595 playlist_items.clear();
1597 RadioMenuItem::Group playlist_group;
1598 boost::shared_ptr<Track> tr = track ();
1600 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1602 /* sort the playlists */
1604 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1606 /* add the playlists to the menu */
1607 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1608 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1609 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1610 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1612 if (tr->playlist()->id() == (*i)->id()) {
1618 playlist_items.push_back (SeparatorElem());
1619 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1620 playlist_items.push_back (SeparatorElem());
1622 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1623 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1624 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1627 // Use a label which tells the user what is happening
1628 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1629 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1633 playlist_items.push_back (SeparatorElem());
1634 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1635 playlist_items.push_back (SeparatorElem());
1637 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1641 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1643 assert (is_track());
1645 // exit if we were triggered by deactivating the old playlist
1646 if (!item->get_active()) {
1650 boost::shared_ptr<Playlist> pl (wpl.lock());
1656 if (track()->playlist() == pl) {
1657 // exit when use_playlist is called by the creation of the playlist menu
1658 // or the playlist choice is unchanged
1662 track()->use_playlist (pl);
1664 RouteGroup* rg = route_group();
1666 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1667 std::string group_string = "." + rg->name() + ".";
1669 std::string take_name = pl->name();
1670 std::string::size_type idx = take_name.find(group_string);
1672 if (idx == std::string::npos)
1675 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1677 boost::shared_ptr<RouteList> rl (rg->route_list());
1679 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1680 if ((*i) == this->route()) {
1684 std::string playlist_name = (*i)->name()+group_string+take_name;
1686 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1691 if (track->freeze_state() == Track::Frozen) {
1692 /* Don't change playlists of frozen tracks */
1696 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1698 // No playlist for this track for this take yet, make it
1699 track->use_new_playlist();
1700 track->playlist()->set_name(playlist_name);
1702 track->use_playlist(ipl);
1709 RouteTimeAxisView::update_playlist_tip ()
1711 RouteGroup* rg = route_group ();
1712 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1713 string group_string = "." + rg->name() + ".";
1715 string take_name = track()->playlist()->name();
1716 string::size_type idx = take_name.find(group_string);
1718 if (idx != string::npos) {
1719 /* find the bit containing the take number / name */
1720 take_name = take_name.substr (idx + group_string.length());
1722 /* set the playlist button tooltip to the take name */
1723 ARDOUR_UI::instance()->set_tip (
1725 string_compose(_("Take: %1.%2"),
1726 Glib::Markup::escape_text(rg->name()),
1727 Glib::Markup::escape_text(take_name))
1734 /* set the playlist button tooltip to the playlist name */
1735 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1740 RouteTimeAxisView::show_playlist_selector ()
1742 _editor.playlist_selector().show_for (this);
1746 RouteTimeAxisView::map_frozen ()
1752 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1754 switch (track()->freeze_state()) {
1756 playlist_button.set_sensitive (false);
1757 rec_enable_button->set_sensitive (false);
1760 playlist_button.set_sensitive (true);
1761 rec_enable_button->set_sensitive (true);
1767 RouteTimeAxisView::color_handler ()
1769 //case cTimeStretchOutline:
1770 if (timestretch_rect) {
1771 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1773 //case cTimeStretchFill:
1774 if (timestretch_rect) {
1775 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1781 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1782 * Will add track if necessary.
1785 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1787 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1788 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1791 /* it doesn't exist yet, so we don't care about the button state: just add it */
1792 create_automation_child (param, true);
1795 bool yn = menu->get_active();
1796 bool changed = false;
1798 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1800 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1801 will have done that for us.
1804 if (changed && !no_redraw) {
1812 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1814 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1820 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1822 if (menu && !_hidden) {
1823 ignore_toggle = true;
1824 menu->set_active (false);
1825 ignore_toggle = false;
1828 if (_route && !no_redraw) {
1835 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1837 if (apply_to_selection) {
1838 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1842 /* Show our automation */
1844 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1845 i->second->set_marked_for_display (true);
1847 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1850 menu->set_active(true);
1855 /* Show processor automation */
1857 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1858 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1859 if ((*ii)->view == 0) {
1860 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1863 (*ii)->menu_item->set_active (true);
1876 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1878 if (apply_to_selection) {
1879 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1883 /* Show our automation */
1885 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1886 if (i->second->has_automation()) {
1887 i->second->set_marked_for_display (true);
1889 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1891 menu->set_active(true);
1896 /* Show processor automation */
1898 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1899 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1900 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1901 (*ii)->menu_item->set_active (true);
1913 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1915 if (apply_to_selection) {
1916 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1920 /* Hide our automation */
1922 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1923 i->second->set_marked_for_display (false);
1925 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1928 menu->set_active (false);
1932 /* Hide processor automation */
1934 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1935 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1936 (*ii)->menu_item->set_active (false);
1947 RouteTimeAxisView::region_view_added (RegionView* rv)
1949 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1950 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1951 boost::shared_ptr<AutomationTimeAxisView> atv;
1953 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1958 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1959 (*i)->add_ghost(rv);
1963 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1965 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1971 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1973 parent.remove_processor_automation_node (this);
1977 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1980 remove_child (pan->view);
1984 RouteTimeAxisView::ProcessorAutomationNode*
1985 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1987 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1989 if ((*i)->processor == processor) {
1991 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1992 if ((*ii)->what == what) {
2002 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2004 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2007 ProcessorAutomationNode* pan;
2009 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2010 /* session state may never have been saved with new plugin */
2011 error << _("programming error: ")
2012 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2013 processor->name(), what.type(), (int) what.channel(), what.id() )
2023 boost::shared_ptr<AutomationControl> control
2024 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2026 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2027 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2028 _editor, *this, false, parent_canvas,
2029 processor->describe_parameter (what), processor->name()));
2031 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2033 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2036 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2041 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2044 pan->menu_item->set_active (false);
2053 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2055 boost::shared_ptr<Processor> processor (p.lock ());
2057 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2058 /* The Amp processor is a special case and is dealt with separately */
2062 set<Evoral::Parameter> existing;
2064 processor->what_has_data (existing);
2066 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2068 Evoral::Parameter param (*i);
2069 boost::shared_ptr<AutomationLine> al;
2071 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2074 add_processor_automation_curve (processor, param);
2080 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2082 using namespace Menu_Helpers;
2086 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2088 _automation_tracks[param] = track;
2090 /* existing state overrides "show" argument */
2091 string s = track->gui_property ("visible");
2093 show = string_is_affirmative (s);
2096 /* this might or might not change the visibility status, so don't rely on it */
2097 track->set_marked_for_display (show);
2099 if (show && !no_redraw) {
2103 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2104 /* MIDI-related parameters are always in the menu, there's no
2105 reason to rebuild the menu just because we added a automation
2106 lane for one of them. But if we add a non-MIDI automation
2107 lane, then we need to invalidate the display menu.
2109 delete display_menu;
2115 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2117 boost::shared_ptr<Processor> processor (p.lock ());
2119 if (!processor || !processor->display_to_user ()) {
2123 /* we use this override to veto the Amp processor from the plugin menu,
2124 as its automation lane can be accessed using the special "Fader" menu
2128 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2132 using namespace Menu_Helpers;
2133 ProcessorAutomationInfo *rai;
2134 list<ProcessorAutomationInfo*>::iterator x;
2136 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2138 if (automatable.empty()) {
2142 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2143 if ((*x)->processor == processor) {
2148 if (x == processor_automation.end()) {
2150 rai = new ProcessorAutomationInfo (processor);
2151 processor_automation.push_back (rai);
2159 /* any older menu was deleted at the top of processors_changed()
2160 when we cleared the subplugin menu.
2163 rai->menu = manage (new Menu);
2164 MenuList& items = rai->menu->items();
2165 rai->menu->set_name ("ArdourContextMenu");
2169 std::set<Evoral::Parameter> has_visible_automation;
2170 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2172 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2174 ProcessorAutomationNode* pan;
2175 Gtk::CheckMenuItem* mitem;
2177 string name = processor->describe_parameter (*i);
2179 items.push_back (CheckMenuElem (name));
2180 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2182 _subplugin_menu_map[*i] = mitem;
2184 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2185 mitem->set_active(true);
2188 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2192 pan = new ProcessorAutomationNode (*i, mitem, *this);
2194 rai->lines.push_back (pan);
2198 pan->menu_item = mitem;
2202 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2205 /* add the menu for this processor, because the subplugin
2206 menu is always cleared at the top of processors_changed().
2207 this is the result of some poor design in gtkmm and/or
2211 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2216 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2217 RouteTimeAxisView::ProcessorAutomationNode* pan)
2219 bool showit = pan->menu_item->get_active();
2220 bool redraw = false;
2222 if (pan->view == 0 && showit) {
2223 add_processor_automation_curve (rai->processor, pan->what);
2227 if (pan->view && pan->view->set_marked_for_display (showit)) {
2231 if (redraw && !no_redraw) {
2237 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2239 if (c.type == RouteProcessorChange::MeterPointChange) {
2240 /* nothing to do if only the meter point has changed */
2244 using namespace Menu_Helpers;
2246 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2247 (*i)->valid = false;
2250 setup_processor_menu_and_curves ();
2252 bool deleted_processor_automation = false;
2254 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2256 list<ProcessorAutomationInfo*>::iterator tmp;
2264 processor_automation.erase (i);
2265 deleted_processor_automation = true;
2272 if (deleted_processor_automation && !no_redraw) {
2277 boost::shared_ptr<AutomationLine>
2278 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2280 ProcessorAutomationNode* pan;
2282 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2288 return boost::shared_ptr<AutomationLine>();
2292 RouteTimeAxisView::reset_processor_automation_curves ()
2294 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2300 RouteTimeAxisView::can_edit_name () const
2302 /* we do not allow track name changes if it is record enabled
2304 return !_route->record_enabled();
2308 RouteTimeAxisView::blink_rec_display (bool onoff)
2310 RouteUI::blink_rec_display (onoff);
2314 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2316 if (_ignore_set_layer_display) {
2320 if (apply_to_selection) {
2321 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2325 _view->set_layer_display (d);
2328 set_gui_property (X_("layer-display"), enum_2_string (d));
2333 RouteTimeAxisView::layer_display () const
2336 return _view->layer_display ();
2339 /* we don't know, since we don't have a _view, so just return something */
2345 boost::shared_ptr<AutomationTimeAxisView>
2346 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2348 AutomationTracks::iterator i = _automation_tracks.find(param);
2349 if (i != _automation_tracks.end()) {
2352 return boost::shared_ptr<AutomationTimeAxisView>();
2357 RouteTimeAxisView::fast_update ()
2359 gm.get_level_meter().update_meters ();
2363 RouteTimeAxisView::hide_meter ()
2366 gm.get_level_meter().hide_meters ();
2370 RouteTimeAxisView::show_meter ()
2376 RouteTimeAxisView::reset_meter ()
2378 if (Config->get_show_track_meters()) {
2379 int meter_width = 3;
2380 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2383 gm.get_level_meter().setup_meters (height - 9, meter_width);
2390 RouteTimeAxisView::clear_meter ()
2392 gm.get_level_meter().clear_meters ();
2396 RouteTimeAxisView::meter_changed ()
2398 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2400 if (_route && !no_redraw) {
2403 // reset peak when meter point changes
2404 gm.reset_peak_display();
2408 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2411 if (_route && !no_redraw) {
2417 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2419 using namespace Menu_Helpers;
2421 if (!_underlay_streams.empty()) {
2422 MenuList& parent_items = parent_menu->items();
2423 Menu* gs_menu = manage (new Menu);
2424 gs_menu->set_name ("ArdourContextMenu");
2425 MenuList& gs_items = gs_menu->items();
2427 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2429 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2430 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2431 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2437 RouteTimeAxisView::set_underlay_state()
2439 if (!underlay_xml_node) {
2443 XMLNodeList nlist = underlay_xml_node->children();
2444 XMLNodeConstIterator niter;
2445 XMLNode *child_node;
2447 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2448 child_node = *niter;
2450 if (child_node->name() != "Underlay") {
2454 XMLProperty* prop = child_node->property ("id");
2456 PBD::ID id (prop->value());
2458 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2461 add_underlay(v->view(), false);
2470 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2476 RouteTimeAxisView& other = v->trackview();
2478 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2479 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2480 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2484 _underlay_streams.push_back(v);
2485 other._underlay_mirrors.push_back(this);
2487 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2489 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2491 if (!underlay_xml_node) {
2492 underlay_xml_node = xml_node->add_child("Underlays");
2495 XMLNode* node = underlay_xml_node->add_child("Underlay");
2496 XMLProperty* prop = node->add_property("id");
2497 prop->set_value(v->trackview().route()->id().to_s());
2504 RouteTimeAxisView::remove_underlay (StreamView* v)
2510 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2511 RouteTimeAxisView& other = v->trackview();
2513 if (it != _underlay_streams.end()) {
2514 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2516 if (gm == other._underlay_mirrors.end()) {
2517 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2521 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2523 _underlay_streams.erase(it);
2524 other._underlay_mirrors.erase(gm);
2526 if (underlay_xml_node) {
2527 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2533 RouteTimeAxisView::set_button_names ()
2535 if (_route && _route->solo_safe()) {
2536 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2538 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2540 if (Config->get_solo_control_is_listen_control()) {
2541 switch (Config->get_listen_position()) {
2542 case AfterFaderListen:
2543 solo_button->set_text (_("A"));
2544 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2546 case PreFaderListen:
2547 solo_button->set_text (_("P"));
2548 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2552 solo_button->set_text (_("S"));
2553 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2555 mute_button->set_text (_("M"));
2559 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2561 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2562 if (i != _main_automation_menu_map.end()) {
2566 i = _subplugin_menu_map.find (param);
2567 if (i != _subplugin_menu_map.end()) {
2575 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2577 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2579 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2583 gain_track.reset (new AutomationTimeAxisView (_session,
2584 _route, _route->amp(), c, param,
2589 _route->amp()->describe_parameter(param)));
2592 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2595 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2599 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2601 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2603 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2607 mute_track.reset (new AutomationTimeAxisView (_session,
2608 _route, _route, c, param,
2613 _route->describe_parameter(param)));
2616 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2619 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2623 void add_region_to_list (RegionView* rv, RegionList* l)
2625 l->push_back (rv->region());
2629 RouteTimeAxisView::combine_regions ()
2631 /* as of may 2011, we do not offer uncombine for MIDI tracks
2634 if (!is_audio_track()) {
2642 RegionList selected_regions;
2643 boost::shared_ptr<Playlist> playlist = track()->playlist();
2645 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2647 if (selected_regions.size() < 2) {
2651 playlist->clear_changes ();
2652 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2654 _session->add_command (new StatefulDiffCommand (playlist));
2655 /* make the new region be selected */
2657 return _view->find_view (compound_region);
2661 RouteTimeAxisView::uncombine_regions ()
2663 /* as of may 2011, we do not offer uncombine for MIDI tracks
2665 if (!is_audio_track()) {
2673 RegionList selected_regions;
2674 boost::shared_ptr<Playlist> playlist = track()->playlist();
2676 /* have to grab selected regions first because the uncombine is going
2677 * to change that in the middle of the list traverse
2680 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2682 playlist->clear_changes ();
2684 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2685 playlist->uncombine (*i);
2688 _session->add_command (new StatefulDiffCommand (playlist));
2692 RouteTimeAxisView::state_id() const
2694 return string_compose ("rtav %1", _route->id().to_s());
2699 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2701 TimeAxisView::remove_child (c);
2703 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2705 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2706 if (i->second == a) {
2707 _automation_tracks.erase (i);