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 mute_button->set_tweaks(ArdourButton::Square);
255 solo_button->set_tweaks(ArdourButton::Square);
256 rec_enable_button->set_tweaks(ArdourButton::Square);
257 playlist_button.set_tweaks(ArdourButton::Square);
258 automation_button.set_tweaks(ArdourButton::Square);
259 route_group_button.set_tweaks(ArdourButton::Square);
260 number_label.set_tweaks(ArdourButton::Square);
262 if (is_midi_track()) {
263 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
265 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
268 update_track_number_visibility();
271 if (ARDOUR::Profile->get_mixbus()) {
272 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
273 controls_button_size_group->add_widget(automation_button);
275 else if (!ARDOUR::Profile->get_trx()) {
276 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
277 controls_button_size_group->add_widget(automation_button);
280 if (is_track() && track()->mode() == ARDOUR::Normal) {
281 if (ARDOUR::Profile->get_mixbus()) {
282 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283 controls_button_size_group->add_widget(playlist_button);
285 else if (!ARDOUR::Profile->get_trx()) {
286 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
287 controls_button_size_group->add_widget(playlist_button);
293 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
294 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
298 str = gui_property ("layer-display");
300 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
303 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
304 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306 /* pick up the correct freeze state */
311 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
312 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314 PropertyList* plist = new PropertyList();
316 plist->add (ARDOUR::Properties::mute, true);
317 plist->add (ARDOUR::Properties::solo, true);
319 route_group_menu = new RouteGroupMenu (_session, plist);
321 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
323 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
326 RouteTimeAxisView::~RouteTimeAxisView ()
328 CatchDeletion (this);
330 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
334 delete playlist_action_menu;
335 playlist_action_menu = 0;
340 _automation_tracks.clear ();
342 delete route_group_menu;
346 RouteTimeAxisView::post_construct ()
348 /* map current state of the route */
350 update_diskstream_display ();
351 setup_processor_menu_and_curves ();
352 reset_processor_automation_curves ();
355 /** Set up the processor menu for the current set of processors, and
356 * display automation curves for any parameters which have data.
359 RouteTimeAxisView::setup_processor_menu_and_curves ()
361 _subplugin_menu_map.clear ();
362 subplugin_menu.items().clear ();
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
364 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
368 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
370 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
371 if (_route->route_group()) {
372 _route->route_group()->remove (_route);
378 r.push_back (route ());
380 route_group_menu->build (r);
381 route_group_menu->menu()->popup (ev->button, ev->time);
387 RouteTimeAxisView::playlist_changed ()
393 RouteTimeAxisView::label_view ()
395 string x = _route->name ();
396 if (x != name_label.get_text ()) {
397 name_label.set_text (x);
399 const int64_t track_number = _route->track_number ();
400 if (track_number == 0) {
401 number_label.set_text ("");
403 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
408 RouteTimeAxisView::update_track_number_visibility ()
411 bool show_label = _session->config.get_track_name_number();
413 if (_route && _route->is_master()) {
417 if (number_label.get_parent()) {
418 controls_table.remove (number_label);
421 if (ARDOUR::Profile->get_mixbus()) {
422 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
424 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
426 const int tnw = 9 + std::max(2u, _session->track_number_decimals()) * number_label.char_pixel_width();
427 number_label.set_size_request(tnw, -1);
428 number_label.show ();
429 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, 0); // -2 = cellspacing
431 number_label.hide ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px, 0);
437 RouteTimeAxisView::parameter_changed (string const & p)
439 if (p == "track-name-number") {
440 update_track_number_visibility();
445 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
447 if (what_changed.contains (ARDOUR::Properties::name)) {
453 RouteTimeAxisView::take_name_changed (void *src)
461 RouteTimeAxisView::playlist_click ()
463 build_playlist_menu ();
464 conditionally_add_to_selection ();
465 playlist_action_menu->popup (1, gtk_get_current_event_time());
469 RouteTimeAxisView::automation_click ()
471 conditionally_add_to_selection ();
472 build_automation_action_menu (false);
473 automation_action_menu->popup (1, gtk_get_current_event_time());
477 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
479 using namespace Menu_Helpers;
481 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
482 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
485 detach_menu (subplugin_menu);
487 _main_automation_menu_map.clear ();
488 delete automation_action_menu;
489 automation_action_menu = new Menu;
491 MenuList& items = automation_action_menu->items();
493 automation_action_menu->set_name ("ArdourContextMenu");
495 items.push_back (MenuElem (_("Show All Automation"),
496 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
498 items.push_back (MenuElem (_("Show Existing Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
501 items.push_back (MenuElem (_("Hide All Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
504 /* Attach the plugin submenu. It may have previously been used elsewhere,
505 so it was detached above
508 if (!subplugin_menu.items().empty()) {
509 items.push_back (SeparatorElem ());
510 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
511 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
516 RouteTimeAxisView::build_display_menu ()
518 using namespace Menu_Helpers;
522 TimeAxisView::build_display_menu ();
524 /* now fill it with our stuff */
526 MenuList& items = display_menu->items();
527 display_menu->set_name ("ArdourContextMenu");
529 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
531 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
533 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
535 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
537 items.push_back (SeparatorElem());
540 detach_menu (*_size_menu);
543 items.push_back (MenuElem (_("Height"), *_size_menu));
545 items.push_back (SeparatorElem());
547 if (!Profile->get_sae()) {
548 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
549 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
550 items.push_back (SeparatorElem());
553 // Hook for derived classes to add type specific stuff
554 append_extra_display_menu_items ();
558 Menu* layers_menu = manage (new Menu);
559 MenuList &layers_items = layers_menu->items();
560 layers_menu->set_name("ArdourContextMenu");
562 RadioMenuItem::Group layers_group;
564 /* Find out how many overlaid/stacked tracks we have in the selection */
568 TrackSelection const & s = _editor.get_selection().tracks;
569 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
570 StreamView* v = (*i)->view ();
575 switch (v->layer_display ()) {
586 /* We're not connecting to signal_toggled() here; in the case where these two items are
587 set to be in the `inconsistent' state, it seems that one or other will end up active
588 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
589 select the active one, no toggled signal is emitted so nothing happens.
592 _ignore_set_layer_display = true;
594 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
595 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
596 i->set_active (overlaid != 0 && stacked == 0);
597 i->set_inconsistent (overlaid != 0 && stacked != 0);
598 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
600 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
601 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
602 i->set_active (overlaid == 0 && stacked != 0);
603 i->set_inconsistent (overlaid != 0 && stacked != 0);
604 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
606 _ignore_set_layer_display = false;
608 items.push_back (MenuElem (_("Layers"), *layers_menu));
610 if (!Profile->get_sae()) {
612 Menu* alignment_menu = manage (new Menu);
613 MenuList& alignment_items = alignment_menu->items();
614 alignment_menu->set_name ("ArdourContextMenu");
616 RadioMenuItem::Group align_group;
618 /* Same verbose hacks as for the layering options above */
624 boost::shared_ptr<Track> first_track;
626 TrackSelection const & s = _editor.get_selection().tracks;
627 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
628 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
629 if (!r || !r->is_track ()) {
634 first_track = r->track();
637 switch (r->track()->alignment_choice()) {
641 switch (r->track()->alignment_style()) {
642 case ExistingMaterial:
650 case UseExistingMaterial:
666 inconsistent = false;
675 if (!inconsistent && first_track) {
677 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
678 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
679 i->set_active (automatic != 0 && existing == 0 && capture == 0);
680 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
682 switch (first_track->alignment_choice()) {
684 switch (first_track->alignment_style()) {
685 case ExistingMaterial:
686 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
689 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
697 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
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, UseExistingMaterial, true));
702 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
703 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
704 i->set_active (existing == 0 && capture != 0 && automatic == 0);
705 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
707 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
713 Menu* mode_menu = manage (new Menu);
714 MenuList& mode_items = mode_menu->items ();
715 mode_menu->set_name ("ArdourContextMenu");
717 RadioMenuItem::Group mode_group;
723 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
724 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
725 if (!r || !r->is_track ()) {
729 switch (r->track()->mode()) {
742 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
743 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
744 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
745 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
746 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
748 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
749 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
750 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
751 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
752 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
754 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
755 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
756 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
757 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
758 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
760 items.push_back (MenuElem (_("Mode"), *mode_menu));
764 items.push_back (SeparatorElem());
766 build_playlist_menu ();
767 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
768 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
771 route_group_menu->detach ();
774 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
775 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
777 r.push_back (rtv->route ());
782 r.push_back (route ());
785 route_group_menu->build (r);
786 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
788 build_automation_action_menu (true);
789 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
791 items.push_back (SeparatorElem());
795 TrackSelection const & s = _editor.get_selection().tracks;
796 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
797 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
802 if (r->route()->active()) {
809 items.push_back (CheckMenuElem (_("Active")));
810 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
811 bool click_sets_active = true;
812 if (active > 0 && inactive == 0) {
813 i->set_active (true);
814 click_sets_active = false;
815 } else if (active > 0 && inactive > 0) {
816 i->set_inconsistent (true);
818 i->set_sensitive(! _session->transport_rolling());
819 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
821 items.push_back (SeparatorElem());
822 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
823 if (!Profile->get_sae()) {
824 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
826 items.push_front (SeparatorElem());
827 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
832 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
834 if (apply_to_selection) {
835 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
838 bool needs_bounce = false;
840 if (!track()->can_use_mode (mode, needs_bounce)) {
846 cerr << "would bounce this one\n";
851 track()->set_mode (mode);
856 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
858 TimeAxisView::show_timestretch (start, end, layers, layer);
868 /* check that the time selection was made in our route, or our route group.
869 remember that route_group() == 0 implies the route is *not* in a edit group.
872 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
873 /* this doesn't apply to us */
877 /* ignore it if our edit group is not active */
879 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
884 if (timestretch_rect == 0) {
885 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
886 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
887 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
890 timestretch_rect->show ();
891 timestretch_rect->raise_to_top ();
893 double const x1 = start / _editor.get_current_zoom();
894 double const x2 = (end - 1) / _editor.get_current_zoom();
896 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
897 x2, current_height() * (layers - layer) / layers));
901 RouteTimeAxisView::hide_timestretch ()
903 TimeAxisView::hide_timestretch ();
905 if (timestretch_rect) {
906 timestretch_rect->hide ();
911 RouteTimeAxisView::show_selection (TimeSelection& ts)
915 /* ignore it if our edit group is not active or if the selection was started
916 in some other track or route group (remember that route_group() == 0 means
917 that the track is not in an route group).
920 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
921 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
927 TimeAxisView::show_selection (ts);
931 RouteTimeAxisView::set_height (uint32_t h)
934 bool height_changed = (height == 0) || (h != height);
937 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
940 gm.get_level_meter().setup_meters (gmlen, meter_width);
942 TimeAxisView::set_height (h);
945 _view->set_height ((double) current_height());
948 if (height >= preset_height (HeightNormal)) {
952 gm.get_gain_slider().show();
954 if (!_route || _route->is_monitor()) {
959 if (rec_enable_button)
960 rec_enable_button->show();
962 route_group_button.show();
963 automation_button.show();
965 if (is_track() && track()->mode() == ARDOUR::Normal) {
966 playlist_button.show();
973 gm.get_gain_slider().hide();
975 if (!_route || _route->is_monitor()) {
980 if (rec_enable_button)
981 rec_enable_button->show();
983 route_group_button.hide ();
984 automation_button.hide ();
986 if (is_track() && track()->mode() == ARDOUR::Normal) {
987 playlist_button.hide ();
992 if (height_changed && !no_redraw) {
993 /* only emit the signal if the height really changed */
999 RouteTimeAxisView::route_color_changed ()
1002 _view->apply_color (color(), StreamView::RegionColor);
1005 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1009 RouteTimeAxisView::reset_samples_per_pixel ()
1011 set_samples_per_pixel (_editor.get_current_zoom());
1015 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1020 speed = track()->speed();
1024 _view->set_samples_per_pixel (fpp * speed);
1027 TimeAxisView::set_samples_per_pixel (fpp * speed);
1031 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1033 if (!mitem->get_active()) {
1034 /* this is one of the two calls made when these radio menu items change status. this one
1035 is for the item that became inactive, and we want to ignore it.
1040 if (apply_to_selection) {
1041 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1044 track()->set_align_choice (choice);
1050 RouteTimeAxisView::rename_current_playlist ()
1052 ArdourPrompter prompter (true);
1055 boost::shared_ptr<Track> tr = track();
1056 if (!tr || tr->destructive()) {
1060 boost::shared_ptr<Playlist> pl = tr->playlist();
1065 prompter.set_title (_("Rename Playlist"));
1066 prompter.set_prompt (_("New name for playlist:"));
1067 prompter.set_initial_text (pl->name());
1068 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1069 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1071 switch (prompter.run ()) {
1072 case Gtk::RESPONSE_ACCEPT:
1073 prompter.get_result (name);
1074 if (name.length()) {
1075 pl->set_name (name);
1085 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1087 std::string ret (basename);
1089 std::string const group_string = "." + route_group()->name() + ".";
1091 // iterate through all playlists
1093 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1094 std::string tmp = (*i)->name();
1096 std::string::size_type idx = tmp.find(group_string);
1097 // find those which belong to this group
1098 if (idx != string::npos) {
1099 tmp = tmp.substr(idx + group_string.length());
1101 // and find the largest current number
1103 if (x > maxnumber) {
1112 snprintf (buf, sizeof(buf), "%d", maxnumber);
1114 ret = this->name() + "." + route_group()->name () + "." + buf;
1120 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1124 boost::shared_ptr<Track> tr = track ();
1125 if (!tr || tr->destructive()) {
1129 boost::shared_ptr<const Playlist> pl = tr->playlist();
1136 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1137 name = resolve_new_group_playlist_name(name, playlists_before_op);
1140 while (_session->playlists->by_name(name)) {
1141 name = Playlist::bump_name (name, *_session);
1144 // TODO: The prompter "new" button should be de-activated if the user
1145 // specifies a playlist name which already exists in the session.
1149 ArdourPrompter prompter (true);
1151 prompter.set_title (_("New Copy Playlist"));
1152 prompter.set_prompt (_("Name for new playlist:"));
1153 prompter.set_initial_text (name);
1154 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1155 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1156 prompter.show_all ();
1158 switch (prompter.run ()) {
1159 case Gtk::RESPONSE_ACCEPT:
1160 prompter.get_result (name);
1168 if (name.length()) {
1169 tr->use_copy_playlist ();
1170 tr->playlist()->set_name (name);
1175 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1179 boost::shared_ptr<Track> tr = track ();
1180 if (!tr || tr->destructive()) {
1184 boost::shared_ptr<const Playlist> pl = tr->playlist();
1191 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1192 name = resolve_new_group_playlist_name(name,playlists_before_op);
1195 while (_session->playlists->by_name(name)) {
1196 name = Playlist::bump_name (name, *_session);
1202 ArdourPrompter prompter (true);
1204 prompter.set_title (_("New Playlist"));
1205 prompter.set_prompt (_("Name for new playlist:"));
1206 prompter.set_initial_text (name);
1207 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1208 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1210 switch (prompter.run ()) {
1211 case Gtk::RESPONSE_ACCEPT:
1212 prompter.get_result (name);
1220 if (name.length()) {
1221 tr->use_new_playlist ();
1222 tr->playlist()->set_name (name);
1227 RouteTimeAxisView::clear_playlist ()
1229 boost::shared_ptr<Track> tr = track ();
1230 if (!tr || tr->destructive()) {
1234 boost::shared_ptr<Playlist> pl = tr->playlist();
1239 _editor.clear_playlist (pl);
1243 RouteTimeAxisView::speed_changed ()
1245 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1249 RouteTimeAxisView::update_diskstream_display ()
1259 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1261 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1263 /* special case: select/deselect all tracks */
1264 if (_editor.get_selection().selected (this)) {
1265 _editor.get_selection().clear_tracks ();
1267 _editor.select_all_tracks ();
1273 switch (ArdourKeyboard::selection_type (ev->state)) {
1274 case Selection::Toggle:
1275 _editor.get_selection().toggle (this);
1278 case Selection::Set:
1279 _editor.get_selection().set (this);
1282 case Selection::Extend:
1283 _editor.extend_selection_to_track (*this);
1286 case Selection::Add:
1287 _editor.get_selection().add (this);
1293 RouteTimeAxisView::set_selected_points (PointSelection& points)
1295 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1296 (*i)->set_selected_points (points);
1301 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1304 _view->set_selected_regionviews (regions);
1308 /** Add the selectable things that we have to a list.
1309 * @param results List to add things to.
1312 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1317 speed = track()->speed();
1320 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1321 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1323 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1324 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1327 /* pick up visible automation tracks */
1329 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1330 if (!(*i)->hidden()) {
1331 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1337 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1340 _view->get_inverted_selectables (sel, results);
1343 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1344 if (!(*i)->hidden()) {
1345 (*i)->get_inverted_selectables (sel, results);
1353 RouteTimeAxisView::route_group () const
1355 return _route->route_group();
1359 RouteTimeAxisView::name() const
1361 return _route->name();
1364 boost::shared_ptr<Playlist>
1365 RouteTimeAxisView::playlist () const
1367 boost::shared_ptr<Track> tr;
1369 if ((tr = track()) != 0) {
1370 return tr->playlist();
1372 return boost::shared_ptr<Playlist> ();
1377 RouteTimeAxisView::name_entry_changed ()
1379 TimeAxisView::name_entry_changed ();
1381 string x = name_entry->get_text ();
1383 if (x == _route->name()) {
1387 strip_whitespace_edges (x);
1389 if (x.length() == 0) {
1390 name_entry->set_text (_route->name());
1394 if (_session->route_name_internal (x)) {
1395 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1397 name_entry->grab_focus ();
1398 } else if (RouteUI::verify_new_route_name (x)) {
1399 _route->set_name (x);
1401 name_entry->grab_focus ();
1405 boost::shared_ptr<Region>
1406 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1408 boost::shared_ptr<Playlist> pl = playlist ();
1411 return pl->find_next_region (pos, point, dir);
1414 return boost::shared_ptr<Region> ();
1418 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1420 boost::shared_ptr<Playlist> pl = playlist ();
1423 return pl->find_next_region_boundary (pos, dir);
1430 RouteTimeAxisView::fade_range (TimeSelection& selection)
1432 boost::shared_ptr<Playlist> what_we_got;
1433 boost::shared_ptr<Track> tr = track ();
1434 boost::shared_ptr<Playlist> playlist;
1437 /* route is a bus, not a track */
1441 playlist = tr->playlist();
1443 TimeSelection time (selection);
1444 float const speed = tr->speed();
1445 if (speed != 1.0f) {
1446 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1447 (*i).start = session_frame_to_track_frame((*i).start, speed);
1448 (*i).end = session_frame_to_track_frame((*i).end, speed);
1452 playlist->clear_changes ();
1453 playlist->clear_owned_changes ();
1455 playlist->fade_range (time);
1457 vector<Command*> cmds;
1458 playlist->rdiff (cmds);
1459 _session->add_commands (cmds);
1460 _session->add_command (new StatefulDiffCommand (playlist));
1465 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1467 boost::shared_ptr<Playlist> what_we_got;
1468 boost::shared_ptr<Track> tr = track ();
1469 boost::shared_ptr<Playlist> playlist;
1472 /* route is a bus, not a track */
1476 playlist = tr->playlist();
1478 TimeSelection time (selection.time);
1479 float const speed = tr->speed();
1480 if (speed != 1.0f) {
1481 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1482 (*i).start = session_frame_to_track_frame((*i).start, speed);
1483 (*i).end = session_frame_to_track_frame((*i).end, speed);
1487 playlist->clear_changes ();
1488 playlist->clear_owned_changes ();
1492 if (playlist->cut (time) != 0) {
1493 if (Config->get_edit_mode() == Ripple)
1494 playlist->ripple(time.start(), -time.length(), NULL);
1495 // no need to exclude any regions from rippling here
1497 vector<Command*> cmds;
1498 playlist->rdiff (cmds);
1499 _session->add_commands (cmds);
1501 _session->add_command (new StatefulDiffCommand (playlist));
1506 if ((what_we_got = playlist->cut (time)) != 0) {
1507 _editor.get_cut_buffer().add (what_we_got);
1508 if (Config->get_edit_mode() == Ripple)
1509 playlist->ripple(time.start(), -time.length(), NULL);
1510 // no need to exclude any regions from rippling here
1512 vector<Command*> cmds;
1513 playlist->rdiff (cmds);
1514 _session->add_commands (cmds);
1516 _session->add_command (new StatefulDiffCommand (playlist));
1520 if ((what_we_got = playlist->copy (time)) != 0) {
1521 _editor.get_cut_buffer().add (what_we_got);
1526 if ((what_we_got = playlist->cut (time)) != 0) {
1527 if (Config->get_edit_mode() == Ripple)
1528 playlist->ripple(time.start(), -time.length(), NULL);
1529 // no need to exclude any regions from rippling here
1531 vector<Command*> cmds;
1532 playlist->rdiff (cmds);
1533 _session->add_commands (cmds);
1534 _session->add_command (new StatefulDiffCommand (playlist));
1535 what_we_got->release ();
1542 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1548 boost::shared_ptr<Playlist> pl = playlist ();
1549 PlaylistSelection::iterator p;
1551 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1553 if (p == selection.playlists.end()) {
1557 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1559 if (track()->speed() != 1.0f) {
1560 pos = session_frame_to_track_frame (pos, track()->speed());
1561 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1564 pl->clear_changes ();
1565 if (Config->get_edit_mode() == Ripple) {
1566 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1567 framecnt_t amount = extent.second - extent.first;
1568 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1570 pl->paste (*p, pos, times);
1572 vector<Command*> cmds;
1574 _session->add_commands (cmds);
1576 _session->add_command (new StatefulDiffCommand (pl));
1582 struct PlaylistSorter {
1583 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1584 return a->sort_id() < b->sort_id();
1589 RouteTimeAxisView::build_playlist_menu ()
1591 using namespace Menu_Helpers;
1597 delete playlist_action_menu;
1598 playlist_action_menu = new Menu;
1599 playlist_action_menu->set_name ("ArdourContextMenu");
1601 MenuList& playlist_items = playlist_action_menu->items();
1602 playlist_action_menu->set_name ("ArdourContextMenu");
1603 playlist_items.clear();
1605 RadioMenuItem::Group playlist_group;
1606 boost::shared_ptr<Track> tr = track ();
1608 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1610 /* sort the playlists */
1612 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1614 /* add the playlists to the menu */
1615 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1616 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1617 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1618 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1620 if (tr->playlist()->id() == (*i)->id()) {
1626 playlist_items.push_back (SeparatorElem());
1627 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1628 playlist_items.push_back (SeparatorElem());
1630 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1631 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1632 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1635 // Use a label which tells the user what is happening
1636 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1637 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1641 playlist_items.push_back (SeparatorElem());
1642 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1643 playlist_items.push_back (SeparatorElem());
1645 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1649 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1651 assert (is_track());
1653 // exit if we were triggered by deactivating the old playlist
1654 if (!item->get_active()) {
1658 boost::shared_ptr<Playlist> pl (wpl.lock());
1664 if (track()->playlist() == pl) {
1665 // exit when use_playlist is called by the creation of the playlist menu
1666 // or the playlist choice is unchanged
1670 track()->use_playlist (pl);
1672 RouteGroup* rg = route_group();
1674 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1675 std::string group_string = "." + rg->name() + ".";
1677 std::string take_name = pl->name();
1678 std::string::size_type idx = take_name.find(group_string);
1680 if (idx == std::string::npos)
1683 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1685 boost::shared_ptr<RouteList> rl (rg->route_list());
1687 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1688 if ((*i) == this->route()) {
1692 std::string playlist_name = (*i)->name()+group_string+take_name;
1694 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1699 if (track->freeze_state() == Track::Frozen) {
1700 /* Don't change playlists of frozen tracks */
1704 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1706 // No playlist for this track for this take yet, make it
1707 track->use_new_playlist();
1708 track->playlist()->set_name(playlist_name);
1710 track->use_playlist(ipl);
1717 RouteTimeAxisView::update_playlist_tip ()
1719 RouteGroup* rg = route_group ();
1720 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1721 string group_string = "." + rg->name() + ".";
1723 string take_name = track()->playlist()->name();
1724 string::size_type idx = take_name.find(group_string);
1726 if (idx != string::npos) {
1727 /* find the bit containing the take number / name */
1728 take_name = take_name.substr (idx + group_string.length());
1730 /* set the playlist button tooltip to the take name */
1731 ARDOUR_UI::instance()->set_tip (
1733 string_compose(_("Take: %1.%2"),
1734 Glib::Markup::escape_text(rg->name()),
1735 Glib::Markup::escape_text(take_name))
1742 /* set the playlist button tooltip to the playlist name */
1743 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1748 RouteTimeAxisView::show_playlist_selector ()
1750 _editor.playlist_selector().show_for (this);
1754 RouteTimeAxisView::map_frozen ()
1760 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1762 switch (track()->freeze_state()) {
1764 playlist_button.set_sensitive (false);
1765 rec_enable_button->set_sensitive (false);
1768 playlist_button.set_sensitive (true);
1769 rec_enable_button->set_sensitive (true);
1775 RouteTimeAxisView::color_handler ()
1777 //case cTimeStretchOutline:
1778 if (timestretch_rect) {
1779 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1781 //case cTimeStretchFill:
1782 if (timestretch_rect) {
1783 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1789 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1790 * Will add track if necessary.
1793 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1795 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1796 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1799 /* it doesn't exist yet, so we don't care about the button state: just add it */
1800 create_automation_child (param, true);
1803 bool yn = menu->get_active();
1804 bool changed = false;
1806 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1808 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1809 will have done that for us.
1812 if (changed && !no_redraw) {
1820 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1822 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1828 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1830 if (menu && !_hidden) {
1831 ignore_toggle = true;
1832 menu->set_active (false);
1833 ignore_toggle = false;
1836 if (_route && !no_redraw) {
1843 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1845 if (apply_to_selection) {
1846 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1850 /* Show our automation */
1852 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1853 i->second->set_marked_for_display (true);
1855 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1858 menu->set_active(true);
1863 /* Show processor automation */
1865 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1866 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1867 if ((*ii)->view == 0) {
1868 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1871 (*ii)->menu_item->set_active (true);
1884 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1886 if (apply_to_selection) {
1887 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1891 /* Show our automation */
1893 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1894 if (i->second->has_automation()) {
1895 i->second->set_marked_for_display (true);
1897 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1899 menu->set_active(true);
1904 /* Show processor automation */
1906 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1907 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1908 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1909 (*ii)->menu_item->set_active (true);
1921 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1923 if (apply_to_selection) {
1924 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1928 /* Hide our automation */
1930 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1931 i->second->set_marked_for_display (false);
1933 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1936 menu->set_active (false);
1940 /* Hide processor automation */
1942 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1943 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1944 (*ii)->menu_item->set_active (false);
1955 RouteTimeAxisView::region_view_added (RegionView* rv)
1957 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1958 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1959 boost::shared_ptr<AutomationTimeAxisView> atv;
1961 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1966 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1967 (*i)->add_ghost(rv);
1971 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1973 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1979 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1981 parent.remove_processor_automation_node (this);
1985 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1988 remove_child (pan->view);
1992 RouteTimeAxisView::ProcessorAutomationNode*
1993 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1995 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1997 if ((*i)->processor == processor) {
1999 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2000 if ((*ii)->what == what) {
2010 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2012 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2015 ProcessorAutomationNode* pan;
2017 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2018 /* session state may never have been saved with new plugin */
2019 error << _("programming error: ")
2020 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2021 processor->name(), what.type(), (int) what.channel(), what.id() )
2031 boost::shared_ptr<AutomationControl> control
2032 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2034 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2035 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2036 _editor, *this, false, parent_canvas,
2037 processor->describe_parameter (what), processor->name()));
2039 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2041 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2044 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2049 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2052 pan->menu_item->set_active (false);
2061 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2063 boost::shared_ptr<Processor> processor (p.lock ());
2065 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2066 /* The Amp processor is a special case and is dealt with separately */
2070 set<Evoral::Parameter> existing;
2072 processor->what_has_data (existing);
2074 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2076 Evoral::Parameter param (*i);
2077 boost::shared_ptr<AutomationLine> al;
2079 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2082 add_processor_automation_curve (processor, param);
2088 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2090 using namespace Menu_Helpers;
2094 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2096 _automation_tracks[param] = track;
2098 /* existing state overrides "show" argument */
2099 string s = track->gui_property ("visible");
2101 show = string_is_affirmative (s);
2104 /* this might or might not change the visibility status, so don't rely on it */
2105 track->set_marked_for_display (show);
2107 if (show && !no_redraw) {
2111 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2112 /* MIDI-related parameters are always in the menu, there's no
2113 reason to rebuild the menu just because we added a automation
2114 lane for one of them. But if we add a non-MIDI automation
2115 lane, then we need to invalidate the display menu.
2117 delete display_menu;
2123 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2125 boost::shared_ptr<Processor> processor (p.lock ());
2127 if (!processor || !processor->display_to_user ()) {
2131 /* we use this override to veto the Amp processor from the plugin menu,
2132 as its automation lane can be accessed using the special "Fader" menu
2136 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2140 using namespace Menu_Helpers;
2141 ProcessorAutomationInfo *rai;
2142 list<ProcessorAutomationInfo*>::iterator x;
2144 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2146 if (automatable.empty()) {
2150 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2151 if ((*x)->processor == processor) {
2156 if (x == processor_automation.end()) {
2158 rai = new ProcessorAutomationInfo (processor);
2159 processor_automation.push_back (rai);
2167 /* any older menu was deleted at the top of processors_changed()
2168 when we cleared the subplugin menu.
2171 rai->menu = manage (new Menu);
2172 MenuList& items = rai->menu->items();
2173 rai->menu->set_name ("ArdourContextMenu");
2177 std::set<Evoral::Parameter> has_visible_automation;
2178 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2180 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2182 ProcessorAutomationNode* pan;
2183 Gtk::CheckMenuItem* mitem;
2185 string name = processor->describe_parameter (*i);
2187 items.push_back (CheckMenuElem (name));
2188 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2190 _subplugin_menu_map[*i] = mitem;
2192 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2193 mitem->set_active(true);
2196 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2200 pan = new ProcessorAutomationNode (*i, mitem, *this);
2202 rai->lines.push_back (pan);
2206 pan->menu_item = mitem;
2210 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2213 /* add the menu for this processor, because the subplugin
2214 menu is always cleared at the top of processors_changed().
2215 this is the result of some poor design in gtkmm and/or
2219 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2224 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2225 RouteTimeAxisView::ProcessorAutomationNode* pan)
2227 bool showit = pan->menu_item->get_active();
2228 bool redraw = false;
2230 if (pan->view == 0 && showit) {
2231 add_processor_automation_curve (rai->processor, pan->what);
2235 if (pan->view && pan->view->set_marked_for_display (showit)) {
2239 if (redraw && !no_redraw) {
2245 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2247 if (c.type == RouteProcessorChange::MeterPointChange) {
2248 /* nothing to do if only the meter point has changed */
2252 using namespace Menu_Helpers;
2254 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2255 (*i)->valid = false;
2258 setup_processor_menu_and_curves ();
2260 bool deleted_processor_automation = false;
2262 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2264 list<ProcessorAutomationInfo*>::iterator tmp;
2272 processor_automation.erase (i);
2273 deleted_processor_automation = true;
2280 if (deleted_processor_automation && !no_redraw) {
2285 boost::shared_ptr<AutomationLine>
2286 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2288 ProcessorAutomationNode* pan;
2290 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2296 return boost::shared_ptr<AutomationLine>();
2300 RouteTimeAxisView::reset_processor_automation_curves ()
2302 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2308 RouteTimeAxisView::can_edit_name () const
2310 /* we do not allow track name changes if it is record enabled
2312 return !_route->record_enabled();
2316 RouteTimeAxisView::blink_rec_display (bool onoff)
2318 RouteUI::blink_rec_display (onoff);
2322 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2324 if (_ignore_set_layer_display) {
2328 if (apply_to_selection) {
2329 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2333 _view->set_layer_display (d);
2336 set_gui_property (X_("layer-display"), enum_2_string (d));
2341 RouteTimeAxisView::layer_display () const
2344 return _view->layer_display ();
2347 /* we don't know, since we don't have a _view, so just return something */
2353 boost::shared_ptr<AutomationTimeAxisView>
2354 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2356 AutomationTracks::iterator i = _automation_tracks.find(param);
2357 if (i != _automation_tracks.end()) {
2360 return boost::shared_ptr<AutomationTimeAxisView>();
2365 RouteTimeAxisView::fast_update ()
2367 gm.get_level_meter().update_meters ();
2371 RouteTimeAxisView::hide_meter ()
2374 gm.get_level_meter().hide_meters ();
2378 RouteTimeAxisView::show_meter ()
2384 RouteTimeAxisView::reset_meter ()
2386 if (Config->get_show_track_meters()) {
2387 int meter_width = 3;
2388 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2391 gm.get_level_meter().setup_meters (height - 9, meter_width);
2398 RouteTimeAxisView::clear_meter ()
2400 gm.get_level_meter().clear_meters ();
2404 RouteTimeAxisView::meter_changed ()
2406 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2408 if (_route && !no_redraw) {
2411 // reset peak when meter point changes
2412 gm.reset_peak_display();
2416 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2419 if (_route && !no_redraw) {
2425 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2427 using namespace Menu_Helpers;
2429 if (!_underlay_streams.empty()) {
2430 MenuList& parent_items = parent_menu->items();
2431 Menu* gs_menu = manage (new Menu);
2432 gs_menu->set_name ("ArdourContextMenu");
2433 MenuList& gs_items = gs_menu->items();
2435 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2437 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2438 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2439 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2445 RouteTimeAxisView::set_underlay_state()
2447 if (!underlay_xml_node) {
2451 XMLNodeList nlist = underlay_xml_node->children();
2452 XMLNodeConstIterator niter;
2453 XMLNode *child_node;
2455 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2456 child_node = *niter;
2458 if (child_node->name() != "Underlay") {
2462 XMLProperty* prop = child_node->property ("id");
2464 PBD::ID id (prop->value());
2466 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2469 add_underlay(v->view(), false);
2478 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2484 RouteTimeAxisView& other = v->trackview();
2486 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2487 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2488 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2492 _underlay_streams.push_back(v);
2493 other._underlay_mirrors.push_back(this);
2495 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2497 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2499 if (!underlay_xml_node) {
2500 underlay_xml_node = xml_node->add_child("Underlays");
2503 XMLNode* node = underlay_xml_node->add_child("Underlay");
2504 XMLProperty* prop = node->add_property("id");
2505 prop->set_value(v->trackview().route()->id().to_s());
2512 RouteTimeAxisView::remove_underlay (StreamView* v)
2518 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2519 RouteTimeAxisView& other = v->trackview();
2521 if (it != _underlay_streams.end()) {
2522 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2524 if (gm == other._underlay_mirrors.end()) {
2525 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2529 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2531 _underlay_streams.erase(it);
2532 other._underlay_mirrors.erase(gm);
2534 if (underlay_xml_node) {
2535 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2541 RouteTimeAxisView::set_button_names ()
2543 if (_route && _route->solo_safe()) {
2544 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2546 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2548 if (Config->get_solo_control_is_listen_control()) {
2549 switch (Config->get_listen_position()) {
2550 case AfterFaderListen:
2551 solo_button->set_text (_("A"));
2552 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2554 case PreFaderListen:
2555 solo_button->set_text (_("P"));
2556 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2560 solo_button->set_text (_("S"));
2561 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2563 mute_button->set_text (_("M"));
2567 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2569 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2570 if (i != _main_automation_menu_map.end()) {
2574 i = _subplugin_menu_map.find (param);
2575 if (i != _subplugin_menu_map.end()) {
2583 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2585 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2587 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2591 gain_track.reset (new AutomationTimeAxisView (_session,
2592 _route, _route->amp(), c, param,
2597 _route->amp()->describe_parameter(param)));
2600 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2603 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2607 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2609 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2611 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2615 mute_track.reset (new AutomationTimeAxisView (_session,
2616 _route, _route, c, param,
2621 _route->describe_parameter(param)));
2624 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2627 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2631 void add_region_to_list (RegionView* rv, RegionList* l)
2633 l->push_back (rv->region());
2637 RouteTimeAxisView::combine_regions ()
2639 /* as of may 2011, we do not offer uncombine for MIDI tracks
2642 if (!is_audio_track()) {
2650 RegionList selected_regions;
2651 boost::shared_ptr<Playlist> playlist = track()->playlist();
2653 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2655 if (selected_regions.size() < 2) {
2659 playlist->clear_changes ();
2660 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2662 _session->add_command (new StatefulDiffCommand (playlist));
2663 /* make the new region be selected */
2665 return _view->find_view (compound_region);
2669 RouteTimeAxisView::uncombine_regions ()
2671 /* as of may 2011, we do not offer uncombine for MIDI tracks
2673 if (!is_audio_track()) {
2681 RegionList selected_regions;
2682 boost::shared_ptr<Playlist> playlist = track()->playlist();
2684 /* have to grab selected regions first because the uncombine is going
2685 * to change that in the middle of the list traverse
2688 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2690 playlist->clear_changes ();
2692 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2693 playlist->uncombine (*i);
2696 _session->add_command (new StatefulDiffCommand (playlist));
2700 RouteTimeAxisView::state_id() const
2702 return string_compose ("rtav %1", _route->id().to_s());
2707 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2709 TimeAxisView::remove_child (c);
2711 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2713 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2714 if (i->second == a) {
2715 _automation_tracks.erase (i);