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);
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);
176 if (is_midi_track()) {
177 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
178 gm.set_fader_name ("MidiTrackFader");
180 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
181 gm.set_fader_name ("AudioTrackFader");
184 rec_enable_button->set_sensitive (_session->writable());
186 /* set playlist button tip to the current playlist, and make it update when it changes */
187 update_playlist_tip ();
188 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
191 gm.set_fader_name ("AudioBusFader");
192 Gtk::Fixed *blank = manage(new Gtk::Fixed());
193 controls_button_size_group->add_widget(*blank);
194 if (ARDOUR::Profile->get_mixbus() ) {
195 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
197 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
202 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
204 if (!ARDOUR::Profile->get_mixbus()) {
205 controls_meters_size_group->add_widget (gm.get_level_meter());
208 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
209 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
210 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
213 if (ARDOUR::Profile->get_mixbus()) {
214 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
216 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218 // mute button is always present, it is used to
219 // force the 'blank' placeholders to the proper size
220 controls_button_size_group->add_widget(*mute_button);
222 if (!_route->is_master()) {
223 if (ARDOUR::Profile->get_mixbus()) {
224 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
226 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
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_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
243 else if (!ARDOUR::Profile->get_trx()) {
244 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
245 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
248 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
249 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
250 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
252 mute_button->set_tweaks(ArdourButton::TrackHeader);
253 solo_button->set_tweaks(ArdourButton::TrackHeader);
254 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
255 playlist_button.set_tweaks(ArdourButton::TrackHeader);
256 automation_button.set_tweaks(ArdourButton::TrackHeader);
257 route_group_button.set_tweaks(ArdourButton::TrackHeader);
259 if (is_midi_track()) {
260 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
262 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
265 update_track_number_visibility();
268 if (ARDOUR::Profile->get_mixbus()) {
269 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
271 else if (!ARDOUR::Profile->get_trx()) {
272 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
275 if (is_track() && track()->mode() == ARDOUR::Normal) {
276 if (ARDOUR::Profile->get_mixbus()) {
277 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 else if (!ARDOUR::Profile->get_trx()) {
280 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
287 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
291 str = gui_property ("layer-display");
293 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
296 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
297 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
299 /* pick up the correct freeze state */
304 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
305 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
307 PropertyList* plist = new PropertyList();
309 plist->add (ARDOUR::Properties::mute, true);
310 plist->add (ARDOUR::Properties::solo, true);
312 route_group_menu = new RouteGroupMenu (_session, plist);
314 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
317 RouteTimeAxisView::~RouteTimeAxisView ()
319 CatchDeletion (this);
321 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
325 delete playlist_action_menu;
326 playlist_action_menu = 0;
331 _automation_tracks.clear ();
333 delete route_group_menu;
337 RouteTimeAxisView::post_construct ()
339 /* map current state of the route */
341 update_diskstream_display ();
342 setup_processor_menu_and_curves ();
343 reset_processor_automation_curves ();
346 /** Set up the processor menu for the current set of processors, and
347 * display automation curves for any parameters which have data.
350 RouteTimeAxisView::setup_processor_menu_and_curves ()
352 _subplugin_menu_map.clear ();
353 subplugin_menu.items().clear ();
354 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
355 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
359 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
361 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
362 if (_route->route_group()) {
363 _route->route_group()->remove (_route);
369 r.push_back (route ());
371 route_group_menu->build (r);
372 route_group_menu->menu()->popup (ev->button, ev->time);
378 RouteTimeAxisView::playlist_changed ()
384 RouteTimeAxisView::label_view ()
386 string x = _route->name ();
387 if (x != name_label.get_text ()) {
388 name_label.set_text (x);
390 const int64_t track_number = _route->track_number ();
391 if (track_number == 0) {
392 number_label.set_text ("");
394 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
399 RouteTimeAxisView::update_track_number_visibility ()
402 bool show_label = _session->config.get_track_name_number();
404 if (_route && _route->is_master()) {
408 if (number_label.get_parent()) {
409 controls_table.remove (number_label);
412 if (ARDOUR::Profile->get_mixbus()) {
413 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
415 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
417 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
418 // except the width of the number label is subtracted from the name-hbox, so we
419 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
420 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
422 number_label.set_size_request(tnw, -1);
423 number_label.show ();
424 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
426 number_label.hide ();
427 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
432 RouteTimeAxisView::parameter_changed (string const & p)
434 if (p == "track-name-number") {
435 update_track_number_visibility();
440 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
442 if (what_changed.contains (ARDOUR::Properties::name)) {
448 RouteTimeAxisView::take_name_changed (void *src)
456 RouteTimeAxisView::playlist_click ()
458 build_playlist_menu ();
459 conditionally_add_to_selection ();
460 playlist_action_menu->popup (1, gtk_get_current_event_time());
464 RouteTimeAxisView::automation_click ()
466 conditionally_add_to_selection ();
467 build_automation_action_menu (false);
468 automation_action_menu->popup (1, gtk_get_current_event_time());
472 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
474 using namespace Menu_Helpers;
476 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
477 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
480 detach_menu (subplugin_menu);
482 _main_automation_menu_map.clear ();
483 delete automation_action_menu;
484 automation_action_menu = new Menu;
486 MenuList& items = automation_action_menu->items();
488 automation_action_menu->set_name ("ArdourContextMenu");
490 items.push_back (MenuElem (_("Show All Automation"),
491 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
493 items.push_back (MenuElem (_("Show Existing Automation"),
494 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
496 items.push_back (MenuElem (_("Hide All Automation"),
497 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
499 /* Attach the plugin submenu. It may have previously been used elsewhere,
500 so it was detached above
503 if (!subplugin_menu.items().empty()) {
504 items.push_back (SeparatorElem ());
505 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
506 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
511 RouteTimeAxisView::build_display_menu ()
513 using namespace Menu_Helpers;
517 TimeAxisView::build_display_menu ();
519 /* now fill it with our stuff */
521 MenuList& items = display_menu->items();
522 display_menu->set_name ("ArdourContextMenu");
524 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
526 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
528 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
530 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
532 items.push_back (SeparatorElem());
535 detach_menu (*_size_menu);
538 items.push_back (MenuElem (_("Height"), *_size_menu));
540 items.push_back (SeparatorElem());
542 if (!Profile->get_sae()) {
543 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
544 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
545 items.push_back (SeparatorElem());
548 // Hook for derived classes to add type specific stuff
549 append_extra_display_menu_items ();
553 Menu* layers_menu = manage (new Menu);
554 MenuList &layers_items = layers_menu->items();
555 layers_menu->set_name("ArdourContextMenu");
557 RadioMenuItem::Group layers_group;
559 /* Find out how many overlaid/stacked tracks we have in the selection */
563 TrackSelection const & s = _editor.get_selection().tracks;
564 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
565 StreamView* v = (*i)->view ();
570 switch (v->layer_display ()) {
581 /* We're not connecting to signal_toggled() here; in the case where these two items are
582 set to be in the `inconsistent' state, it seems that one or other will end up active
583 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
584 select the active one, no toggled signal is emitted so nothing happens.
587 _ignore_set_layer_display = true;
589 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
590 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
591 i->set_active (overlaid != 0 && stacked == 0);
592 i->set_inconsistent (overlaid != 0 && stacked != 0);
593 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
595 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
596 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
597 i->set_active (overlaid == 0 && stacked != 0);
598 i->set_inconsistent (overlaid != 0 && stacked != 0);
599 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
601 _ignore_set_layer_display = false;
603 items.push_back (MenuElem (_("Layers"), *layers_menu));
605 if (!Profile->get_sae()) {
607 Menu* alignment_menu = manage (new Menu);
608 MenuList& alignment_items = alignment_menu->items();
609 alignment_menu->set_name ("ArdourContextMenu");
611 RadioMenuItem::Group align_group;
613 /* Same verbose hacks as for the layering options above */
619 boost::shared_ptr<Track> first_track;
621 TrackSelection const & s = _editor.get_selection().tracks;
622 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
623 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
624 if (!r || !r->is_track ()) {
629 first_track = r->track();
632 switch (r->track()->alignment_choice()) {
636 switch (r->track()->alignment_style()) {
637 case ExistingMaterial:
645 case UseExistingMaterial:
661 inconsistent = false;
670 if (!inconsistent && first_track) {
672 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
673 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
674 i->set_active (automatic != 0 && existing == 0 && capture == 0);
675 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
677 switch (first_track->alignment_choice()) {
679 switch (first_track->alignment_style()) {
680 case ExistingMaterial:
681 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
684 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
692 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
693 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
694 i->set_active (existing != 0 && capture == 0 && automatic == 0);
695 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
697 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
698 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
699 i->set_active (existing == 0 && capture != 0 && automatic == 0);
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
702 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
708 Menu* mode_menu = manage (new Menu);
709 MenuList& mode_items = mode_menu->items ();
710 mode_menu->set_name ("ArdourContextMenu");
712 RadioMenuItem::Group mode_group;
718 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
719 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
720 if (!r || !r->is_track ()) {
724 switch (r->track()->mode()) {
737 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
738 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
739 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
740 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
741 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
743 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
744 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
745 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
746 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
747 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
749 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
750 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
751 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
752 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
753 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
755 items.push_back (MenuElem (_("Mode"), *mode_menu));
759 items.push_back (SeparatorElem());
761 build_playlist_menu ();
762 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
763 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
766 route_group_menu->detach ();
769 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
770 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
772 r.push_back (rtv->route ());
777 r.push_back (route ());
780 route_group_menu->build (r);
781 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
783 build_automation_action_menu (true);
784 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
786 items.push_back (SeparatorElem());
790 TrackSelection const & s = _editor.get_selection().tracks;
791 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
792 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
797 if (r->route()->active()) {
804 items.push_back (CheckMenuElem (_("Active")));
805 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
806 bool click_sets_active = true;
807 if (active > 0 && inactive == 0) {
808 i->set_active (true);
809 click_sets_active = false;
810 } else if (active > 0 && inactive > 0) {
811 i->set_inconsistent (true);
813 i->set_sensitive(! _session->transport_rolling());
814 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
816 items.push_back (SeparatorElem());
817 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
818 if (!Profile->get_sae()) {
819 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
821 items.push_front (SeparatorElem());
822 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
827 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
829 if (apply_to_selection) {
830 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
833 bool needs_bounce = false;
835 if (!track()->can_use_mode (mode, needs_bounce)) {
841 cerr << "would bounce this one\n";
846 track()->set_mode (mode);
851 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
853 TimeAxisView::show_timestretch (start, end, layers, layer);
863 /* check that the time selection was made in our route, or our route group.
864 remember that route_group() == 0 implies the route is *not* in a edit group.
867 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
868 /* this doesn't apply to us */
872 /* ignore it if our edit group is not active */
874 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
879 if (timestretch_rect == 0) {
880 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
881 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_TimeStretchFill());
882 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_TimeStretchOutline());
885 timestretch_rect->show ();
886 timestretch_rect->raise_to_top ();
888 double const x1 = start / _editor.get_current_zoom();
889 double const x2 = (end - 1) / _editor.get_current_zoom();
891 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
892 x2, current_height() * (layers - layer) / layers));
896 RouteTimeAxisView::hide_timestretch ()
898 TimeAxisView::hide_timestretch ();
900 if (timestretch_rect) {
901 timestretch_rect->hide ();
906 RouteTimeAxisView::show_selection (TimeSelection& ts)
910 /* ignore it if our edit group is not active or if the selection was started
911 in some other track or route group (remember that route_group() == 0 means
912 that the track is not in an route group).
915 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
916 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
922 TimeAxisView::show_selection (ts);
926 RouteTimeAxisView::set_height (uint32_t h)
929 bool height_changed = (height == 0) || (h != height);
932 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
935 gm.get_level_meter().setup_meters (gmlen, meter_width);
937 TimeAxisView::set_height (h);
940 _view->set_height ((double) current_height());
943 if (height >= preset_height (HeightNormal)) {
947 gm.get_gain_slider().show();
949 if (!_route || _route->is_monitor()) {
954 if (rec_enable_button)
955 rec_enable_button->show();
957 route_group_button.show();
958 automation_button.show();
960 if (is_track() && track()->mode() == ARDOUR::Normal) {
961 playlist_button.show();
968 gm.get_gain_slider().hide();
970 if (!_route || _route->is_monitor()) {
975 if (rec_enable_button)
976 rec_enable_button->show();
978 route_group_button.hide ();
979 automation_button.hide ();
981 if (is_track() && track()->mode() == ARDOUR::Normal) {
982 playlist_button.hide ();
987 if (height_changed && !no_redraw) {
988 /* only emit the signal if the height really changed */
994 RouteTimeAxisView::route_color_changed ()
997 _view->apply_color (color(), StreamView::RegionColor);
1000 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1004 RouteTimeAxisView::reset_samples_per_pixel ()
1006 set_samples_per_pixel (_editor.get_current_zoom());
1010 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1015 speed = track()->speed();
1019 _view->set_samples_per_pixel (fpp * speed);
1022 TimeAxisView::set_samples_per_pixel (fpp * speed);
1026 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1028 if (!mitem->get_active()) {
1029 /* this is one of the two calls made when these radio menu items change status. this one
1030 is for the item that became inactive, and we want to ignore it.
1035 if (apply_to_selection) {
1036 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1039 track()->set_align_choice (choice);
1045 RouteTimeAxisView::rename_current_playlist ()
1047 ArdourPrompter prompter (true);
1050 boost::shared_ptr<Track> tr = track();
1051 if (!tr || tr->destructive()) {
1055 boost::shared_ptr<Playlist> pl = tr->playlist();
1060 prompter.set_title (_("Rename Playlist"));
1061 prompter.set_prompt (_("New name for playlist:"));
1062 prompter.set_initial_text (pl->name());
1063 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1064 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1066 switch (prompter.run ()) {
1067 case Gtk::RESPONSE_ACCEPT:
1068 prompter.get_result (name);
1069 if (name.length()) {
1070 pl->set_name (name);
1080 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1082 std::string ret (basename);
1084 std::string const group_string = "." + route_group()->name() + ".";
1086 // iterate through all playlists
1088 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1089 std::string tmp = (*i)->name();
1091 std::string::size_type idx = tmp.find(group_string);
1092 // find those which belong to this group
1093 if (idx != string::npos) {
1094 tmp = tmp.substr(idx + group_string.length());
1096 // and find the largest current number
1098 if (x > maxnumber) {
1107 snprintf (buf, sizeof(buf), "%d", maxnumber);
1109 ret = this->name() + "." + route_group()->name () + "." + buf;
1115 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1119 boost::shared_ptr<Track> tr = track ();
1120 if (!tr || tr->destructive()) {
1124 boost::shared_ptr<const Playlist> pl = tr->playlist();
1131 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1132 name = resolve_new_group_playlist_name(name, playlists_before_op);
1135 while (_session->playlists->by_name(name)) {
1136 name = Playlist::bump_name (name, *_session);
1139 // TODO: The prompter "new" button should be de-activated if the user
1140 // specifies a playlist name which already exists in the session.
1144 ArdourPrompter prompter (true);
1146 prompter.set_title (_("New Copy Playlist"));
1147 prompter.set_prompt (_("Name for new playlist:"));
1148 prompter.set_initial_text (name);
1149 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1150 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1151 prompter.show_all ();
1153 switch (prompter.run ()) {
1154 case Gtk::RESPONSE_ACCEPT:
1155 prompter.get_result (name);
1163 if (name.length()) {
1164 tr->use_copy_playlist ();
1165 tr->playlist()->set_name (name);
1170 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1174 boost::shared_ptr<Track> tr = track ();
1175 if (!tr || tr->destructive()) {
1179 boost::shared_ptr<const Playlist> pl = tr->playlist();
1186 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1187 name = resolve_new_group_playlist_name(name,playlists_before_op);
1190 while (_session->playlists->by_name(name)) {
1191 name = Playlist::bump_name (name, *_session);
1197 ArdourPrompter prompter (true);
1199 prompter.set_title (_("New Playlist"));
1200 prompter.set_prompt (_("Name for new playlist:"));
1201 prompter.set_initial_text (name);
1202 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1203 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1205 switch (prompter.run ()) {
1206 case Gtk::RESPONSE_ACCEPT:
1207 prompter.get_result (name);
1215 if (name.length()) {
1216 tr->use_new_playlist ();
1217 tr->playlist()->set_name (name);
1222 RouteTimeAxisView::clear_playlist ()
1224 boost::shared_ptr<Track> tr = track ();
1225 if (!tr || tr->destructive()) {
1229 boost::shared_ptr<Playlist> pl = tr->playlist();
1234 _editor.clear_playlist (pl);
1238 RouteTimeAxisView::speed_changed ()
1240 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1244 RouteTimeAxisView::update_diskstream_display ()
1254 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1256 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1258 /* special case: select/deselect all tracks */
1259 if (_editor.get_selection().selected (this)) {
1260 _editor.get_selection().clear_tracks ();
1262 _editor.select_all_tracks ();
1268 switch (ArdourKeyboard::selection_type (ev->state)) {
1269 case Selection::Toggle:
1270 _editor.get_selection().toggle (this);
1273 case Selection::Set:
1274 _editor.get_selection().set (this);
1277 case Selection::Extend:
1278 _editor.extend_selection_to_track (*this);
1281 case Selection::Add:
1282 _editor.get_selection().add (this);
1288 RouteTimeAxisView::set_selected_points (PointSelection& points)
1290 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1291 (*i)->set_selected_points (points);
1296 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1299 _view->set_selected_regionviews (regions);
1303 /** Add the selectable things that we have to a list.
1304 * @param results List to add things to.
1307 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1312 speed = track()->speed();
1315 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1316 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1318 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1319 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1322 /* pick up visible automation tracks */
1324 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1325 if (!(*i)->hidden()) {
1326 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1332 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1335 _view->get_inverted_selectables (sel, results);
1338 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1339 if (!(*i)->hidden()) {
1340 (*i)->get_inverted_selectables (sel, results);
1348 RouteTimeAxisView::route_group () const
1350 return _route->route_group();
1354 RouteTimeAxisView::name() const
1356 return _route->name();
1359 boost::shared_ptr<Playlist>
1360 RouteTimeAxisView::playlist () const
1362 boost::shared_ptr<Track> tr;
1364 if ((tr = track()) != 0) {
1365 return tr->playlist();
1367 return boost::shared_ptr<Playlist> ();
1372 RouteTimeAxisView::name_entry_changed ()
1374 TimeAxisView::name_entry_changed ();
1376 string x = name_entry->get_text ();
1378 if (x == _route->name()) {
1382 strip_whitespace_edges (x);
1384 if (x.length() == 0) {
1385 name_entry->set_text (_route->name());
1389 if (_session->route_name_internal (x)) {
1390 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1392 name_entry->grab_focus ();
1393 } else if (RouteUI::verify_new_route_name (x)) {
1394 _route->set_name (x);
1396 name_entry->grab_focus ();
1400 boost::shared_ptr<Region>
1401 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1403 boost::shared_ptr<Playlist> pl = playlist ();
1406 return pl->find_next_region (pos, point, dir);
1409 return boost::shared_ptr<Region> ();
1413 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1415 boost::shared_ptr<Playlist> pl = playlist ();
1418 return pl->find_next_region_boundary (pos, dir);
1425 RouteTimeAxisView::fade_range (TimeSelection& selection)
1427 boost::shared_ptr<Playlist> what_we_got;
1428 boost::shared_ptr<Track> tr = track ();
1429 boost::shared_ptr<Playlist> playlist;
1432 /* route is a bus, not a track */
1436 playlist = tr->playlist();
1438 TimeSelection time (selection);
1439 float const speed = tr->speed();
1440 if (speed != 1.0f) {
1441 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1442 (*i).start = session_frame_to_track_frame((*i).start, speed);
1443 (*i).end = session_frame_to_track_frame((*i).end, speed);
1447 playlist->clear_changes ();
1448 playlist->clear_owned_changes ();
1450 playlist->fade_range (time);
1452 vector<Command*> cmds;
1453 playlist->rdiff (cmds);
1454 _session->add_commands (cmds);
1455 _session->add_command (new StatefulDiffCommand (playlist));
1460 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1462 boost::shared_ptr<Playlist> what_we_got;
1463 boost::shared_ptr<Track> tr = track ();
1464 boost::shared_ptr<Playlist> playlist;
1467 /* route is a bus, not a track */
1471 playlist = tr->playlist();
1473 TimeSelection time (selection.time);
1474 float const speed = tr->speed();
1475 if (speed != 1.0f) {
1476 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1477 (*i).start = session_frame_to_track_frame((*i).start, speed);
1478 (*i).end = session_frame_to_track_frame((*i).end, speed);
1482 playlist->clear_changes ();
1483 playlist->clear_owned_changes ();
1487 if (playlist->cut (time) != 0) {
1488 if (Config->get_edit_mode() == Ripple)
1489 playlist->ripple(time.start(), -time.length(), NULL);
1490 // no need to exclude any regions from rippling here
1492 vector<Command*> cmds;
1493 playlist->rdiff (cmds);
1494 _session->add_commands (cmds);
1496 _session->add_command (new StatefulDiffCommand (playlist));
1501 if ((what_we_got = playlist->cut (time)) != 0) {
1502 _editor.get_cut_buffer().add (what_we_got);
1503 if (Config->get_edit_mode() == Ripple)
1504 playlist->ripple(time.start(), -time.length(), NULL);
1505 // no need to exclude any regions from rippling here
1507 vector<Command*> cmds;
1508 playlist->rdiff (cmds);
1509 _session->add_commands (cmds);
1511 _session->add_command (new StatefulDiffCommand (playlist));
1515 if ((what_we_got = playlist->copy (time)) != 0) {
1516 _editor.get_cut_buffer().add (what_we_got);
1521 if ((what_we_got = playlist->cut (time)) != 0) {
1522 if (Config->get_edit_mode() == Ripple)
1523 playlist->ripple(time.start(), -time.length(), NULL);
1524 // no need to exclude any regions from rippling here
1526 vector<Command*> cmds;
1527 playlist->rdiff (cmds);
1528 _session->add_commands (cmds);
1529 _session->add_command (new StatefulDiffCommand (playlist));
1530 what_we_got->release ();
1537 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1543 boost::shared_ptr<Playlist> pl = playlist ();
1544 PlaylistSelection::iterator p;
1546 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1548 if (p == selection.playlists.end()) {
1552 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1554 if (track()->speed() != 1.0f) {
1555 pos = session_frame_to_track_frame (pos, track()->speed());
1556 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1559 pl->clear_changes ();
1560 if (Config->get_edit_mode() == Ripple) {
1561 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1562 framecnt_t amount = extent.second - extent.first;
1563 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1565 pl->paste (*p, pos, times);
1567 vector<Command*> cmds;
1569 _session->add_commands (cmds);
1571 _session->add_command (new StatefulDiffCommand (pl));
1577 struct PlaylistSorter {
1578 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1579 return a->sort_id() < b->sort_id();
1584 RouteTimeAxisView::build_playlist_menu ()
1586 using namespace Menu_Helpers;
1592 delete playlist_action_menu;
1593 playlist_action_menu = new Menu;
1594 playlist_action_menu->set_name ("ArdourContextMenu");
1596 MenuList& playlist_items = playlist_action_menu->items();
1597 playlist_action_menu->set_name ("ArdourContextMenu");
1598 playlist_items.clear();
1600 RadioMenuItem::Group playlist_group;
1601 boost::shared_ptr<Track> tr = track ();
1603 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1605 /* sort the playlists */
1607 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1609 /* add the playlists to the menu */
1610 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1611 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1612 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1613 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1615 if (tr->playlist()->id() == (*i)->id()) {
1621 playlist_items.push_back (SeparatorElem());
1622 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1623 playlist_items.push_back (SeparatorElem());
1625 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1626 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1627 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1630 // Use a label which tells the user what is happening
1631 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1632 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1636 playlist_items.push_back (SeparatorElem());
1637 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1638 playlist_items.push_back (SeparatorElem());
1640 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1644 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1646 assert (is_track());
1648 // exit if we were triggered by deactivating the old playlist
1649 if (!item->get_active()) {
1653 boost::shared_ptr<Playlist> pl (wpl.lock());
1659 if (track()->playlist() == pl) {
1660 // exit when use_playlist is called by the creation of the playlist menu
1661 // or the playlist choice is unchanged
1665 track()->use_playlist (pl);
1667 RouteGroup* rg = route_group();
1669 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1670 std::string group_string = "." + rg->name() + ".";
1672 std::string take_name = pl->name();
1673 std::string::size_type idx = take_name.find(group_string);
1675 if (idx == std::string::npos)
1678 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1680 boost::shared_ptr<RouteList> rl (rg->route_list());
1682 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1683 if ((*i) == this->route()) {
1687 std::string playlist_name = (*i)->name()+group_string+take_name;
1689 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1694 if (track->freeze_state() == Track::Frozen) {
1695 /* Don't change playlists of frozen tracks */
1699 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1701 // No playlist for this track for this take yet, make it
1702 track->use_new_playlist();
1703 track->playlist()->set_name(playlist_name);
1705 track->use_playlist(ipl);
1712 RouteTimeAxisView::update_playlist_tip ()
1714 RouteGroup* rg = route_group ();
1715 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1716 string group_string = "." + rg->name() + ".";
1718 string take_name = track()->playlist()->name();
1719 string::size_type idx = take_name.find(group_string);
1721 if (idx != string::npos) {
1722 /* find the bit containing the take number / name */
1723 take_name = take_name.substr (idx + group_string.length());
1725 /* set the playlist button tooltip to the take name */
1726 ARDOUR_UI::instance()->set_tip (
1728 string_compose(_("Take: %1.%2"),
1729 Glib::Markup::escape_text(rg->name()),
1730 Glib::Markup::escape_text(take_name))
1737 /* set the playlist button tooltip to the playlist name */
1738 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1743 RouteTimeAxisView::show_playlist_selector ()
1745 _editor.playlist_selector().show_for (this);
1749 RouteTimeAxisView::map_frozen ()
1755 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1757 switch (track()->freeze_state()) {
1759 playlist_button.set_sensitive (false);
1760 rec_enable_button->set_sensitive (false);
1763 playlist_button.set_sensitive (true);
1764 rec_enable_button->set_sensitive (true);
1770 RouteTimeAxisView::color_handler ()
1772 //case cTimeStretchOutline:
1773 if (timestretch_rect) {
1774 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_TimeStretchOutline());
1776 //case cTimeStretchFill:
1777 if (timestretch_rect) {
1778 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_TimeStretchFill());
1784 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1785 * Will add track if necessary.
1788 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1790 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1791 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1794 /* it doesn't exist yet, so we don't care about the button state: just add it */
1795 create_automation_child (param, true);
1798 bool yn = menu->get_active();
1799 bool changed = false;
1801 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1803 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1804 will have done that for us.
1807 if (changed && !no_redraw) {
1815 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1817 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1823 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1825 if (menu && !_hidden) {
1826 ignore_toggle = true;
1827 menu->set_active (false);
1828 ignore_toggle = false;
1831 if (_route && !no_redraw) {
1838 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1840 if (apply_to_selection) {
1841 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1845 /* Show our automation */
1847 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1848 i->second->set_marked_for_display (true);
1850 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1853 menu->set_active(true);
1858 /* Show processor automation */
1860 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1861 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1862 if ((*ii)->view == 0) {
1863 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1866 (*ii)->menu_item->set_active (true);
1879 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1881 if (apply_to_selection) {
1882 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1886 /* Show our automation */
1888 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1889 if (i->second->has_automation()) {
1890 i->second->set_marked_for_display (true);
1892 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1894 menu->set_active(true);
1899 /* Show processor automation */
1901 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1902 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1903 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1904 (*ii)->menu_item->set_active (true);
1916 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1918 if (apply_to_selection) {
1919 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1923 /* Hide our automation */
1925 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1926 i->second->set_marked_for_display (false);
1928 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1931 menu->set_active (false);
1935 /* Hide processor automation */
1937 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1938 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1939 (*ii)->menu_item->set_active (false);
1950 RouteTimeAxisView::region_view_added (RegionView* rv)
1952 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1953 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1954 boost::shared_ptr<AutomationTimeAxisView> atv;
1956 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1961 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1962 (*i)->add_ghost(rv);
1966 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1968 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1974 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1976 parent.remove_processor_automation_node (this);
1980 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1983 remove_child (pan->view);
1987 RouteTimeAxisView::ProcessorAutomationNode*
1988 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1990 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1992 if ((*i)->processor == processor) {
1994 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1995 if ((*ii)->what == what) {
2005 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2007 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2010 ProcessorAutomationNode* pan;
2012 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2013 /* session state may never have been saved with new plugin */
2014 error << _("programming error: ")
2015 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2016 processor->name(), what.type(), (int) what.channel(), what.id() )
2026 boost::shared_ptr<AutomationControl> control
2027 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2029 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2030 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2031 _editor, *this, false, parent_canvas,
2032 processor->describe_parameter (what), processor->name()));
2034 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2036 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2039 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2044 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2047 pan->menu_item->set_active (false);
2056 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2058 boost::shared_ptr<Processor> processor (p.lock ());
2060 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2061 /* The Amp processor is a special case and is dealt with separately */
2065 set<Evoral::Parameter> existing;
2067 processor->what_has_data (existing);
2069 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2071 Evoral::Parameter param (*i);
2072 boost::shared_ptr<AutomationLine> al;
2074 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2077 add_processor_automation_curve (processor, param);
2083 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2085 using namespace Menu_Helpers;
2089 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2091 _automation_tracks[param] = track;
2093 /* existing state overrides "show" argument */
2094 string s = track->gui_property ("visible");
2096 show = string_is_affirmative (s);
2099 /* this might or might not change the visibility status, so don't rely on it */
2100 track->set_marked_for_display (show);
2102 if (show && !no_redraw) {
2106 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2107 /* MIDI-related parameters are always in the menu, there's no
2108 reason to rebuild the menu just because we added a automation
2109 lane for one of them. But if we add a non-MIDI automation
2110 lane, then we need to invalidate the display menu.
2112 delete display_menu;
2118 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2120 boost::shared_ptr<Processor> processor (p.lock ());
2122 if (!processor || !processor->display_to_user ()) {
2126 /* we use this override to veto the Amp processor from the plugin menu,
2127 as its automation lane can be accessed using the special "Fader" menu
2131 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2135 using namespace Menu_Helpers;
2136 ProcessorAutomationInfo *rai;
2137 list<ProcessorAutomationInfo*>::iterator x;
2139 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2141 if (automatable.empty()) {
2145 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2146 if ((*x)->processor == processor) {
2151 if (x == processor_automation.end()) {
2153 rai = new ProcessorAutomationInfo (processor);
2154 processor_automation.push_back (rai);
2162 /* any older menu was deleted at the top of processors_changed()
2163 when we cleared the subplugin menu.
2166 rai->menu = manage (new Menu);
2167 MenuList& items = rai->menu->items();
2168 rai->menu->set_name ("ArdourContextMenu");
2172 std::set<Evoral::Parameter> has_visible_automation;
2173 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2175 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2177 ProcessorAutomationNode* pan;
2178 Gtk::CheckMenuItem* mitem;
2180 string name = processor->describe_parameter (*i);
2182 items.push_back (CheckMenuElem (name));
2183 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2185 _subplugin_menu_map[*i] = mitem;
2187 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2188 mitem->set_active(true);
2191 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2195 pan = new ProcessorAutomationNode (*i, mitem, *this);
2197 rai->lines.push_back (pan);
2201 pan->menu_item = mitem;
2205 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2208 /* add the menu for this processor, because the subplugin
2209 menu is always cleared at the top of processors_changed().
2210 this is the result of some poor design in gtkmm and/or
2214 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2219 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2220 RouteTimeAxisView::ProcessorAutomationNode* pan)
2222 bool showit = pan->menu_item->get_active();
2223 bool redraw = false;
2225 if (pan->view == 0 && showit) {
2226 add_processor_automation_curve (rai->processor, pan->what);
2230 if (pan->view && pan->view->set_marked_for_display (showit)) {
2234 if (redraw && !no_redraw) {
2240 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2242 if (c.type == RouteProcessorChange::MeterPointChange) {
2243 /* nothing to do if only the meter point has changed */
2247 using namespace Menu_Helpers;
2249 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2250 (*i)->valid = false;
2253 setup_processor_menu_and_curves ();
2255 bool deleted_processor_automation = false;
2257 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2259 list<ProcessorAutomationInfo*>::iterator tmp;
2267 processor_automation.erase (i);
2268 deleted_processor_automation = true;
2275 if (deleted_processor_automation && !no_redraw) {
2280 boost::shared_ptr<AutomationLine>
2281 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2283 ProcessorAutomationNode* pan;
2285 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2291 return boost::shared_ptr<AutomationLine>();
2295 RouteTimeAxisView::reset_processor_automation_curves ()
2297 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2303 RouteTimeAxisView::can_edit_name () const
2305 /* we do not allow track name changes if it is record enabled
2307 return !_route->record_enabled();
2311 RouteTimeAxisView::blink_rec_display (bool onoff)
2313 RouteUI::blink_rec_display (onoff);
2317 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2319 if (_ignore_set_layer_display) {
2323 if (apply_to_selection) {
2324 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2328 _view->set_layer_display (d);
2331 set_gui_property (X_("layer-display"), enum_2_string (d));
2336 RouteTimeAxisView::layer_display () const
2339 return _view->layer_display ();
2342 /* we don't know, since we don't have a _view, so just return something */
2348 boost::shared_ptr<AutomationTimeAxisView>
2349 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2351 AutomationTracks::iterator i = _automation_tracks.find(param);
2352 if (i != _automation_tracks.end()) {
2355 return boost::shared_ptr<AutomationTimeAxisView>();
2360 RouteTimeAxisView::fast_update ()
2362 gm.get_level_meter().update_meters ();
2366 RouteTimeAxisView::hide_meter ()
2369 gm.get_level_meter().hide_meters ();
2373 RouteTimeAxisView::show_meter ()
2379 RouteTimeAxisView::reset_meter ()
2381 if (Config->get_show_track_meters()) {
2382 int meter_width = 3;
2383 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2386 gm.get_level_meter().setup_meters (height - 9, meter_width);
2393 RouteTimeAxisView::clear_meter ()
2395 gm.get_level_meter().clear_meters ();
2399 RouteTimeAxisView::meter_changed ()
2401 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2403 if (_route && !no_redraw) {
2406 // reset peak when meter point changes
2407 gm.reset_peak_display();
2411 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2414 if (_route && !no_redraw) {
2420 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2422 using namespace Menu_Helpers;
2424 if (!_underlay_streams.empty()) {
2425 MenuList& parent_items = parent_menu->items();
2426 Menu* gs_menu = manage (new Menu);
2427 gs_menu->set_name ("ArdourContextMenu");
2428 MenuList& gs_items = gs_menu->items();
2430 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2432 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2433 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2434 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2440 RouteTimeAxisView::set_underlay_state()
2442 if (!underlay_xml_node) {
2446 XMLNodeList nlist = underlay_xml_node->children();
2447 XMLNodeConstIterator niter;
2448 XMLNode *child_node;
2450 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2451 child_node = *niter;
2453 if (child_node->name() != "Underlay") {
2457 XMLProperty* prop = child_node->property ("id");
2459 PBD::ID id (prop->value());
2461 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2464 add_underlay(v->view(), false);
2473 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2479 RouteTimeAxisView& other = v->trackview();
2481 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2482 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2483 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2487 _underlay_streams.push_back(v);
2488 other._underlay_mirrors.push_back(this);
2490 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2492 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2494 if (!underlay_xml_node) {
2495 underlay_xml_node = xml_node->add_child("Underlays");
2498 XMLNode* node = underlay_xml_node->add_child("Underlay");
2499 XMLProperty* prop = node->add_property("id");
2500 prop->set_value(v->trackview().route()->id().to_s());
2507 RouteTimeAxisView::remove_underlay (StreamView* v)
2513 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2514 RouteTimeAxisView& other = v->trackview();
2516 if (it != _underlay_streams.end()) {
2517 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2519 if (gm == other._underlay_mirrors.end()) {
2520 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2524 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2526 _underlay_streams.erase(it);
2527 other._underlay_mirrors.erase(gm);
2529 if (underlay_xml_node) {
2530 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2536 RouteTimeAxisView::set_button_names ()
2538 if (_route && _route->solo_safe()) {
2539 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2541 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2543 if (Config->get_solo_control_is_listen_control()) {
2544 switch (Config->get_listen_position()) {
2545 case AfterFaderListen:
2546 solo_button->set_text (_("A"));
2547 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2549 case PreFaderListen:
2550 solo_button->set_text (_("P"));
2551 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2555 solo_button->set_text (_("S"));
2556 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2558 mute_button->set_text (_("M"));
2562 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2564 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2565 if (i != _main_automation_menu_map.end()) {
2569 i = _subplugin_menu_map.find (param);
2570 if (i != _subplugin_menu_map.end()) {
2578 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2580 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2582 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2586 gain_track.reset (new AutomationTimeAxisView (_session,
2587 _route, _route->amp(), c, param,
2592 _route->amp()->describe_parameter(param)));
2595 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2598 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2602 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2604 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2606 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2610 mute_track.reset (new AutomationTimeAxisView (_session,
2611 _route, _route, c, param,
2616 _route->describe_parameter(param)));
2619 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2622 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2626 void add_region_to_list (RegionView* rv, RegionList* l)
2628 l->push_back (rv->region());
2632 RouteTimeAxisView::combine_regions ()
2634 /* as of may 2011, we do not offer uncombine for MIDI tracks
2637 if (!is_audio_track()) {
2645 RegionList selected_regions;
2646 boost::shared_ptr<Playlist> playlist = track()->playlist();
2648 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2650 if (selected_regions.size() < 2) {
2654 playlist->clear_changes ();
2655 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2657 _session->add_command (new StatefulDiffCommand (playlist));
2658 /* make the new region be selected */
2660 return _view->find_view (compound_region);
2664 RouteTimeAxisView::uncombine_regions ()
2666 /* as of may 2011, we do not offer uncombine for MIDI tracks
2668 if (!is_audio_track()) {
2676 RegionList selected_regions;
2677 boost::shared_ptr<Playlist> playlist = track()->playlist();
2679 /* have to grab selected regions first because the uncombine is going
2680 * to change that in the middle of the list traverse
2683 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2685 playlist->clear_changes ();
2687 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2688 playlist->uncombine (*i);
2691 _session->add_command (new StatefulDiffCommand (playlist));
2695 RouteTimeAxisView::state_id() const
2697 return string_compose ("rtav %1", _route->id().to_s());
2702 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2704 TimeAxisView::remove_child (c);
2706 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2708 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2709 if (i->second == a) {
2710 _automation_tracks.erase (i);