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));
172 switch (track()->mode()) {
174 case ARDOUR::NonLayered:
175 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
176 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
178 case ARDOUR::Destructive:
179 rec_enable_button->set_text (string());
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
184 if (ARDOUR::Profile->get_mixbus()) {
185 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
187 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
189 controls_button_size_group->add_widget(*rec_enable_button);
191 if (is_midi_track()) {
192 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
193 gm.set_fader_name ("MidiTrackFader");
195 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
196 gm.set_fader_name ("AudioTrackFader");
199 rec_enable_button->set_sensitive (_session->writable());
201 /* set playlist button tip to the current playlist, and make it update when it changes */
202 update_playlist_tip ();
203 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
206 gm.set_fader_name ("AudioBusFader");
207 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208 controls_button_size_group->add_widget(*blank);
209 if (ARDOUR::Profile->get_mixbus() ) {
210 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
212 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
219 if (!ARDOUR::Profile->get_mixbus()) {
220 controls_meters_size_group->add_widget (gm.get_level_meter());
223 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 controls_button_size_group->add_widget(*mute_button);
235 if (!_route->is_master()) {
236 if (ARDOUR::Profile->get_mixbus()) {
237 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
239 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_button_size_group->add_widget(*solo_button);
243 Gtk::Fixed *blank = manage(new Gtk::Fixed());
244 controls_button_size_group->add_widget(*blank);
245 if (ARDOUR::Profile->get_mixbus()) {
246 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
253 if (ARDOUR::Profile->get_mixbus()) {
254 controls_button_size_group->add_widget(route_group_button);
255 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
258 else if (!ARDOUR::Profile->get_trx()) {
259 controls_button_size_group->add_widget(route_group_button);
260 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
264 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
265 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
266 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
268 if (is_midi_track()) {
269 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
271 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
274 update_track_number_visibility();
277 if (ARDOUR::Profile->get_mixbus()) {
278 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 controls_button_size_group->add_widget(automation_button);
281 else if (!ARDOUR::Profile->get_trx()) {
282 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283 controls_button_size_group->add_widget(automation_button);
286 if (is_track() && track()->mode() == ARDOUR::Normal) {
287 if (ARDOUR::Profile->get_mixbus()) {
288 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
289 controls_button_size_group->add_widget(playlist_button);
291 else if (!ARDOUR::Profile->get_trx()) {
292 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 controls_button_size_group->add_widget(playlist_button);
299 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
300 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
304 str = gui_property ("layer-display");
306 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
309 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
310 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
312 /* pick up the correct freeze state */
317 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
318 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
320 PropertyList* plist = new PropertyList();
322 plist->add (ARDOUR::Properties::mute, true);
323 plist->add (ARDOUR::Properties::solo, true);
325 route_group_menu = new RouteGroupMenu (_session, plist);
327 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
329 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
332 RouteTimeAxisView::~RouteTimeAxisView ()
334 CatchDeletion (this);
336 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
340 delete playlist_action_menu;
341 playlist_action_menu = 0;
346 _automation_tracks.clear ();
348 delete route_group_menu;
352 RouteTimeAxisView::post_construct ()
354 /* map current state of the route */
356 update_diskstream_display ();
357 setup_processor_menu_and_curves ();
358 reset_processor_automation_curves ();
361 /** Set up the processor menu for the current set of processors, and
362 * display automation curves for any parameters which have data.
365 RouteTimeAxisView::setup_processor_menu_and_curves ()
367 _subplugin_menu_map.clear ();
368 subplugin_menu.items().clear ();
369 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
370 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
374 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
376 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
377 if (_route->route_group()) {
378 _route->route_group()->remove (_route);
384 r.push_back (route ());
386 route_group_menu->build (r);
387 route_group_menu->menu()->popup (ev->button, ev->time);
393 RouteTimeAxisView::playlist_changed ()
399 RouteTimeAxisView::label_view ()
401 string x = _route->name ();
402 if (x != name_label.get_text ()) {
403 name_label.set_text (x);
405 const int64_t track_number = _route->track_number ();
406 if (track_number == 0) {
407 number_label.set_text ("");
409 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
414 RouteTimeAxisView::update_track_number_visibility ()
417 bool show_label = _session->config.get_track_name_number();
419 if (_route && _route->is_master()) {
423 if (number_label.get_parent()) {
424 controls_table.remove (number_label);
427 if (ARDOUR::Profile->get_mixbus()) {
428 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
430 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
432 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
433 number_label.set_size_request(3 + tnw, -1);
434 number_label.show ();
435 // -5 = -2 for table border - 3 for padding ^^
436 name_hbox.set_size_request(TimeAxisView::name_width_px - 5 - tnw, 0);
438 number_label.hide ();
439 name_hbox.set_size_request(TimeAxisView::name_width_px, 0);
444 RouteTimeAxisView::parameter_changed (string const & p)
446 if (p == "track-name-number") {
447 update_track_number_visibility();
452 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
454 if (what_changed.contains (ARDOUR::Properties::name)) {
460 RouteTimeAxisView::take_name_changed (void *src)
468 RouteTimeAxisView::playlist_click ()
470 build_playlist_menu ();
471 conditionally_add_to_selection ();
472 playlist_action_menu->popup (1, gtk_get_current_event_time());
476 RouteTimeAxisView::automation_click ()
478 conditionally_add_to_selection ();
479 build_automation_action_menu (false);
480 automation_action_menu->popup (1, gtk_get_current_event_time());
484 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
486 using namespace Menu_Helpers;
488 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
489 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
492 detach_menu (subplugin_menu);
494 _main_automation_menu_map.clear ();
495 delete automation_action_menu;
496 automation_action_menu = new Menu;
498 MenuList& items = automation_action_menu->items();
500 automation_action_menu->set_name ("ArdourContextMenu");
502 items.push_back (MenuElem (_("Show All Automation"),
503 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
505 items.push_back (MenuElem (_("Show Existing Automation"),
506 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
508 items.push_back (MenuElem (_("Hide All Automation"),
509 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
511 /* Attach the plugin submenu. It may have previously been used elsewhere,
512 so it was detached above
515 if (!subplugin_menu.items().empty()) {
516 items.push_back (SeparatorElem ());
517 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
518 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
523 RouteTimeAxisView::build_display_menu ()
525 using namespace Menu_Helpers;
529 TimeAxisView::build_display_menu ();
531 /* now fill it with our stuff */
533 MenuList& items = display_menu->items();
534 display_menu->set_name ("ArdourContextMenu");
536 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
538 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
540 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
542 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
544 items.push_back (SeparatorElem());
547 detach_menu (*_size_menu);
550 items.push_back (MenuElem (_("Height"), *_size_menu));
552 items.push_back (SeparatorElem());
554 if (!Profile->get_sae()) {
555 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
556 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
557 items.push_back (SeparatorElem());
560 // Hook for derived classes to add type specific stuff
561 append_extra_display_menu_items ();
565 Menu* layers_menu = manage (new Menu);
566 MenuList &layers_items = layers_menu->items();
567 layers_menu->set_name("ArdourContextMenu");
569 RadioMenuItem::Group layers_group;
571 /* Find out how many overlaid/stacked tracks we have in the selection */
575 TrackSelection const & s = _editor.get_selection().tracks;
576 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
577 StreamView* v = (*i)->view ();
582 switch (v->layer_display ()) {
593 /* We're not connecting to signal_toggled() here; in the case where these two items are
594 set to be in the `inconsistent' state, it seems that one or other will end up active
595 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
596 select the active one, no toggled signal is emitted so nothing happens.
599 _ignore_set_layer_display = true;
601 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
602 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
603 i->set_active (overlaid != 0 && stacked == 0);
604 i->set_inconsistent (overlaid != 0 && stacked != 0);
605 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
607 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
608 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
609 i->set_active (overlaid == 0 && stacked != 0);
610 i->set_inconsistent (overlaid != 0 && stacked != 0);
611 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
613 _ignore_set_layer_display = false;
615 items.push_back (MenuElem (_("Layers"), *layers_menu));
617 if (!Profile->get_sae()) {
619 Menu* alignment_menu = manage (new Menu);
620 MenuList& alignment_items = alignment_menu->items();
621 alignment_menu->set_name ("ArdourContextMenu");
623 RadioMenuItem::Group align_group;
625 /* Same verbose hacks as for the layering options above */
631 boost::shared_ptr<Track> first_track;
633 TrackSelection const & s = _editor.get_selection().tracks;
634 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
635 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
636 if (!r || !r->is_track ()) {
641 first_track = r->track();
644 switch (r->track()->alignment_choice()) {
648 switch (r->track()->alignment_style()) {
649 case ExistingMaterial:
657 case UseExistingMaterial:
673 inconsistent = false;
682 if (!inconsistent && first_track) {
684 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
685 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
686 i->set_active (automatic != 0 && existing == 0 && capture == 0);
687 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
689 switch (first_track->alignment_choice()) {
691 switch (first_track->alignment_style()) {
692 case ExistingMaterial:
693 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
696 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
704 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
705 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
706 i->set_active (existing != 0 && capture == 0 && automatic == 0);
707 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
709 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
710 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
711 i->set_active (existing == 0 && capture != 0 && automatic == 0);
712 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
714 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
720 Menu* mode_menu = manage (new Menu);
721 MenuList& mode_items = mode_menu->items ();
722 mode_menu->set_name ("ArdourContextMenu");
724 RadioMenuItem::Group mode_group;
730 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
731 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
732 if (!r || !r->is_track ()) {
736 switch (r->track()->mode()) {
749 mode_items.push_back (RadioMenuElem (mode_group, _("Normal 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::Normal, true));
752 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
753 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
755 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
756 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
757 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
758 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
759 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
761 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
762 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
763 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
764 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
765 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
767 items.push_back (MenuElem (_("Mode"), *mode_menu));
771 items.push_back (SeparatorElem());
773 build_playlist_menu ();
774 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
775 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
778 route_group_menu->detach ();
781 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
784 r.push_back (rtv->route ());
789 r.push_back (route ());
792 route_group_menu->build (r);
793 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
795 build_automation_action_menu (true);
796 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
798 items.push_back (SeparatorElem());
802 TrackSelection const & s = _editor.get_selection().tracks;
803 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
804 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
809 if (r->route()->active()) {
816 items.push_back (CheckMenuElem (_("Active")));
817 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
818 bool click_sets_active = true;
819 if (active > 0 && inactive == 0) {
820 i->set_active (true);
821 click_sets_active = false;
822 } else if (active > 0 && inactive > 0) {
823 i->set_inconsistent (true);
825 i->set_sensitive(! _session->transport_rolling());
826 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
828 items.push_back (SeparatorElem());
829 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
830 if (!Profile->get_sae()) {
831 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
833 items.push_front (SeparatorElem());
834 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
839 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
841 if (apply_to_selection) {
842 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
845 bool needs_bounce = false;
847 if (!track()->can_use_mode (mode, needs_bounce)) {
853 cerr << "would bounce this one\n";
858 track()->set_mode (mode);
860 rec_enable_button->remove ();
863 case ARDOUR::NonLayered:
865 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
866 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
868 case ARDOUR::Destructive:
869 rec_enable_button->set_text (string());
870 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
874 rec_enable_button->show_all ();
879 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
881 TimeAxisView::show_timestretch (start, end, layers, layer);
891 /* check that the time selection was made in our route, or our route group.
892 remember that route_group() == 0 implies the route is *not* in a edit group.
895 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
896 /* this doesn't apply to us */
900 /* ignore it if our edit group is not active */
902 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
907 if (timestretch_rect == 0) {
908 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
909 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
910 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
913 timestretch_rect->show ();
914 timestretch_rect->raise_to_top ();
916 double const x1 = start / _editor.get_current_zoom();
917 double const x2 = (end - 1) / _editor.get_current_zoom();
919 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
920 x2, current_height() * (layers - layer) / layers));
924 RouteTimeAxisView::hide_timestretch ()
926 TimeAxisView::hide_timestretch ();
928 if (timestretch_rect) {
929 timestretch_rect->hide ();
934 RouteTimeAxisView::show_selection (TimeSelection& ts)
938 /* ignore it if our edit group is not active or if the selection was started
939 in some other track or route group (remember that route_group() == 0 means
940 that the track is not in an route group).
943 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
944 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
950 TimeAxisView::show_selection (ts);
954 RouteTimeAxisView::set_height (uint32_t h)
957 bool height_changed = (height == 0) || (h != height);
960 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
963 gm.get_level_meter().setup_meters (gmlen, meter_width);
965 TimeAxisView::set_height (h);
968 _view->set_height ((double) current_height());
971 if (height >= preset_height (HeightNormal)) {
975 gm.get_gain_slider().show();
977 if (!_route || _route->is_monitor()) {
982 if (rec_enable_button)
983 rec_enable_button->show();
985 route_group_button.show();
986 automation_button.show();
988 if (is_track() && track()->mode() == ARDOUR::Normal) {
989 playlist_button.show();
996 gm.get_gain_slider().hide();
998 if (!_route || _route->is_monitor()) {
1001 solo_button->show();
1003 if (rec_enable_button)
1004 rec_enable_button->show();
1006 route_group_button.hide ();
1007 automation_button.hide ();
1009 if (is_track() && track()->mode() == ARDOUR::Normal) {
1010 playlist_button.hide ();
1015 if (height_changed && !no_redraw) {
1016 /* only emit the signal if the height really changed */
1022 RouteTimeAxisView::route_color_changed ()
1025 _view->apply_color (color(), StreamView::RegionColor);
1028 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1032 RouteTimeAxisView::reset_samples_per_pixel ()
1034 set_samples_per_pixel (_editor.get_current_zoom());
1038 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1043 speed = track()->speed();
1047 _view->set_samples_per_pixel (fpp * speed);
1050 TimeAxisView::set_samples_per_pixel (fpp * speed);
1054 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1056 if (!mitem->get_active()) {
1057 /* this is one of the two calls made when these radio menu items change status. this one
1058 is for the item that became inactive, and we want to ignore it.
1063 if (apply_to_selection) {
1064 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1067 track()->set_align_choice (choice);
1073 RouteTimeAxisView::rename_current_playlist ()
1075 ArdourPrompter prompter (true);
1078 boost::shared_ptr<Track> tr = track();
1079 if (!tr || tr->destructive()) {
1083 boost::shared_ptr<Playlist> pl = tr->playlist();
1088 prompter.set_title (_("Rename Playlist"));
1089 prompter.set_prompt (_("New name for playlist:"));
1090 prompter.set_initial_text (pl->name());
1091 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1092 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1094 switch (prompter.run ()) {
1095 case Gtk::RESPONSE_ACCEPT:
1096 prompter.get_result (name);
1097 if (name.length()) {
1098 pl->set_name (name);
1108 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1110 std::string ret (basename);
1112 std::string const group_string = "." + route_group()->name() + ".";
1114 // iterate through all playlists
1116 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1117 std::string tmp = (*i)->name();
1119 std::string::size_type idx = tmp.find(group_string);
1120 // find those which belong to this group
1121 if (idx != string::npos) {
1122 tmp = tmp.substr(idx + group_string.length());
1124 // and find the largest current number
1126 if (x > maxnumber) {
1135 snprintf (buf, sizeof(buf), "%d", maxnumber);
1137 ret = this->name() + "." + route_group()->name () + "." + buf;
1143 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1147 boost::shared_ptr<Track> tr = track ();
1148 if (!tr || tr->destructive()) {
1152 boost::shared_ptr<const Playlist> pl = tr->playlist();
1159 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1160 name = resolve_new_group_playlist_name(name, playlists_before_op);
1163 while (_session->playlists->by_name(name)) {
1164 name = Playlist::bump_name (name, *_session);
1167 // TODO: The prompter "new" button should be de-activated if the user
1168 // specifies a playlist name which already exists in the session.
1172 ArdourPrompter prompter (true);
1174 prompter.set_title (_("New Copy Playlist"));
1175 prompter.set_prompt (_("Name for new playlist:"));
1176 prompter.set_initial_text (name);
1177 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1178 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1179 prompter.show_all ();
1181 switch (prompter.run ()) {
1182 case Gtk::RESPONSE_ACCEPT:
1183 prompter.get_result (name);
1191 if (name.length()) {
1192 tr->use_copy_playlist ();
1193 tr->playlist()->set_name (name);
1198 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1202 boost::shared_ptr<Track> tr = track ();
1203 if (!tr || tr->destructive()) {
1207 boost::shared_ptr<const Playlist> pl = tr->playlist();
1214 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1215 name = resolve_new_group_playlist_name(name,playlists_before_op);
1218 while (_session->playlists->by_name(name)) {
1219 name = Playlist::bump_name (name, *_session);
1225 ArdourPrompter prompter (true);
1227 prompter.set_title (_("New Playlist"));
1228 prompter.set_prompt (_("Name for new playlist:"));
1229 prompter.set_initial_text (name);
1230 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1231 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1233 switch (prompter.run ()) {
1234 case Gtk::RESPONSE_ACCEPT:
1235 prompter.get_result (name);
1243 if (name.length()) {
1244 tr->use_new_playlist ();
1245 tr->playlist()->set_name (name);
1250 RouteTimeAxisView::clear_playlist ()
1252 boost::shared_ptr<Track> tr = track ();
1253 if (!tr || tr->destructive()) {
1257 boost::shared_ptr<Playlist> pl = tr->playlist();
1262 _editor.clear_playlist (pl);
1266 RouteTimeAxisView::speed_changed ()
1268 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1272 RouteTimeAxisView::update_diskstream_display ()
1282 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1284 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1286 /* special case: select/deselect all tracks */
1287 if (_editor.get_selection().selected (this)) {
1288 _editor.get_selection().clear_tracks ();
1290 _editor.select_all_tracks ();
1296 switch (ArdourKeyboard::selection_type (ev->state)) {
1297 case Selection::Toggle:
1298 _editor.get_selection().toggle (this);
1301 case Selection::Set:
1302 _editor.get_selection().set (this);
1305 case Selection::Extend:
1306 _editor.extend_selection_to_track (*this);
1309 case Selection::Add:
1310 _editor.get_selection().add (this);
1316 RouteTimeAxisView::set_selected_points (PointSelection& points)
1318 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1319 (*i)->set_selected_points (points);
1324 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1327 _view->set_selected_regionviews (regions);
1331 /** Add the selectable things that we have to a list.
1332 * @param results List to add things to.
1335 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1340 speed = track()->speed();
1343 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1344 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1346 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1347 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1350 /* pick up visible automation tracks */
1352 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1353 if (!(*i)->hidden()) {
1354 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1360 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1363 _view->get_inverted_selectables (sel, results);
1366 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1367 if (!(*i)->hidden()) {
1368 (*i)->get_inverted_selectables (sel, results);
1376 RouteTimeAxisView::route_group () const
1378 return _route->route_group();
1382 RouteTimeAxisView::name() const
1384 return _route->name();
1387 boost::shared_ptr<Playlist>
1388 RouteTimeAxisView::playlist () const
1390 boost::shared_ptr<Track> tr;
1392 if ((tr = track()) != 0) {
1393 return tr->playlist();
1395 return boost::shared_ptr<Playlist> ();
1400 RouteTimeAxisView::name_entry_changed ()
1402 TimeAxisView::name_entry_changed ();
1404 string x = name_entry->get_text ();
1406 if (x == _route->name()) {
1410 strip_whitespace_edges (x);
1412 if (x.length() == 0) {
1413 name_entry->set_text (_route->name());
1417 if (_session->route_name_internal (x)) {
1418 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1420 name_entry->grab_focus ();
1421 } else if (RouteUI::verify_new_route_name (x)) {
1422 _route->set_name (x);
1424 name_entry->grab_focus ();
1428 boost::shared_ptr<Region>
1429 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1431 boost::shared_ptr<Playlist> pl = playlist ();
1434 return pl->find_next_region (pos, point, dir);
1437 return boost::shared_ptr<Region> ();
1441 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1443 boost::shared_ptr<Playlist> pl = playlist ();
1446 return pl->find_next_region_boundary (pos, dir);
1453 RouteTimeAxisView::fade_range (TimeSelection& selection)
1455 boost::shared_ptr<Playlist> what_we_got;
1456 boost::shared_ptr<Track> tr = track ();
1457 boost::shared_ptr<Playlist> playlist;
1460 /* route is a bus, not a track */
1464 playlist = tr->playlist();
1466 TimeSelection time (selection);
1467 float const speed = tr->speed();
1468 if (speed != 1.0f) {
1469 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1470 (*i).start = session_frame_to_track_frame((*i).start, speed);
1471 (*i).end = session_frame_to_track_frame((*i).end, speed);
1475 playlist->clear_changes ();
1476 playlist->clear_owned_changes ();
1478 playlist->fade_range (time);
1480 vector<Command*> cmds;
1481 playlist->rdiff (cmds);
1482 _session->add_commands (cmds);
1483 _session->add_command (new StatefulDiffCommand (playlist));
1488 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1490 boost::shared_ptr<Playlist> what_we_got;
1491 boost::shared_ptr<Track> tr = track ();
1492 boost::shared_ptr<Playlist> playlist;
1495 /* route is a bus, not a track */
1499 playlist = tr->playlist();
1501 TimeSelection time (selection.time);
1502 float const speed = tr->speed();
1503 if (speed != 1.0f) {
1504 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1505 (*i).start = session_frame_to_track_frame((*i).start, speed);
1506 (*i).end = session_frame_to_track_frame((*i).end, speed);
1510 playlist->clear_changes ();
1511 playlist->clear_owned_changes ();
1515 if (playlist->cut (time) != 0) {
1516 if (Config->get_edit_mode() == Ripple)
1517 playlist->ripple(time.start(), -time.length(), NULL);
1518 // no need to exclude any regions from rippling here
1520 vector<Command*> cmds;
1521 playlist->rdiff (cmds);
1522 _session->add_commands (cmds);
1524 _session->add_command (new StatefulDiffCommand (playlist));
1529 if ((what_we_got = playlist->cut (time)) != 0) {
1530 _editor.get_cut_buffer().add (what_we_got);
1531 if (Config->get_edit_mode() == Ripple)
1532 playlist->ripple(time.start(), -time.length(), NULL);
1533 // no need to exclude any regions from rippling here
1535 vector<Command*> cmds;
1536 playlist->rdiff (cmds);
1537 _session->add_commands (cmds);
1539 _session->add_command (new StatefulDiffCommand (playlist));
1543 if ((what_we_got = playlist->copy (time)) != 0) {
1544 _editor.get_cut_buffer().add (what_we_got);
1549 if ((what_we_got = playlist->cut (time)) != 0) {
1550 if (Config->get_edit_mode() == Ripple)
1551 playlist->ripple(time.start(), -time.length(), NULL);
1552 // no need to exclude any regions from rippling here
1554 vector<Command*> cmds;
1555 playlist->rdiff (cmds);
1556 _session->add_commands (cmds);
1557 _session->add_command (new StatefulDiffCommand (playlist));
1558 what_we_got->release ();
1565 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1571 boost::shared_ptr<Playlist> pl = playlist ();
1572 PlaylistSelection::iterator p;
1574 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1576 if (p == selection.playlists.end()) {
1580 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1582 if (track()->speed() != 1.0f) {
1583 pos = session_frame_to_track_frame (pos, track()->speed());
1584 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1587 pl->clear_changes ();
1588 if (Config->get_edit_mode() == Ripple) {
1589 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1590 framecnt_t amount = extent.second - extent.first;
1591 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1593 pl->paste (*p, pos, times);
1595 vector<Command*> cmds;
1597 _session->add_commands (cmds);
1599 _session->add_command (new StatefulDiffCommand (pl));
1605 struct PlaylistSorter {
1606 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1607 return a->sort_id() < b->sort_id();
1612 RouteTimeAxisView::build_playlist_menu ()
1614 using namespace Menu_Helpers;
1620 delete playlist_action_menu;
1621 playlist_action_menu = new Menu;
1622 playlist_action_menu->set_name ("ArdourContextMenu");
1624 MenuList& playlist_items = playlist_action_menu->items();
1625 playlist_action_menu->set_name ("ArdourContextMenu");
1626 playlist_items.clear();
1628 RadioMenuItem::Group playlist_group;
1629 boost::shared_ptr<Track> tr = track ();
1631 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1633 /* sort the playlists */
1635 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1637 /* add the playlists to the menu */
1638 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1639 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1640 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1641 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1643 if (tr->playlist()->id() == (*i)->id()) {
1649 playlist_items.push_back (SeparatorElem());
1650 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1651 playlist_items.push_back (SeparatorElem());
1653 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1654 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1655 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1658 // Use a label which tells the user what is happening
1659 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1660 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1664 playlist_items.push_back (SeparatorElem());
1665 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1666 playlist_items.push_back (SeparatorElem());
1668 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1672 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1674 assert (is_track());
1676 // exit if we were triggered by deactivating the old playlist
1677 if (!item->get_active()) {
1681 boost::shared_ptr<Playlist> pl (wpl.lock());
1687 if (track()->playlist() == pl) {
1688 // exit when use_playlist is called by the creation of the playlist menu
1689 // or the playlist choice is unchanged
1693 track()->use_playlist (pl);
1695 RouteGroup* rg = route_group();
1697 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1698 std::string group_string = "." + rg->name() + ".";
1700 std::string take_name = pl->name();
1701 std::string::size_type idx = take_name.find(group_string);
1703 if (idx == std::string::npos)
1706 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1708 boost::shared_ptr<RouteList> rl (rg->route_list());
1710 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1711 if ((*i) == this->route()) {
1715 std::string playlist_name = (*i)->name()+group_string+take_name;
1717 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1722 if (track->freeze_state() == Track::Frozen) {
1723 /* Don't change playlists of frozen tracks */
1727 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1729 // No playlist for this track for this take yet, make it
1730 track->use_new_playlist();
1731 track->playlist()->set_name(playlist_name);
1733 track->use_playlist(ipl);
1740 RouteTimeAxisView::update_playlist_tip ()
1742 RouteGroup* rg = route_group ();
1743 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1744 string group_string = "." + rg->name() + ".";
1746 string take_name = track()->playlist()->name();
1747 string::size_type idx = take_name.find(group_string);
1749 if (idx != string::npos) {
1750 /* find the bit containing the take number / name */
1751 take_name = take_name.substr (idx + group_string.length());
1753 /* set the playlist button tooltip to the take name */
1754 ARDOUR_UI::instance()->set_tip (
1756 string_compose(_("Take: %1.%2"),
1757 Glib::Markup::escape_text(rg->name()),
1758 Glib::Markup::escape_text(take_name))
1765 /* set the playlist button tooltip to the playlist name */
1766 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1771 RouteTimeAxisView::show_playlist_selector ()
1773 _editor.playlist_selector().show_for (this);
1777 RouteTimeAxisView::map_frozen ()
1783 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1785 switch (track()->freeze_state()) {
1787 playlist_button.set_sensitive (false);
1788 rec_enable_button->set_sensitive (false);
1791 playlist_button.set_sensitive (true);
1792 rec_enable_button->set_sensitive (true);
1798 RouteTimeAxisView::color_handler ()
1800 //case cTimeStretchOutline:
1801 if (timestretch_rect) {
1802 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1804 //case cTimeStretchFill:
1805 if (timestretch_rect) {
1806 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1812 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1813 * Will add track if necessary.
1816 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1818 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1819 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1822 /* it doesn't exist yet, so we don't care about the button state: just add it */
1823 create_automation_child (param, true);
1826 bool yn = menu->get_active();
1827 bool changed = false;
1829 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1831 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1832 will have done that for us.
1835 if (changed && !no_redraw) {
1843 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1845 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1851 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1853 if (menu && !_hidden) {
1854 ignore_toggle = true;
1855 menu->set_active (false);
1856 ignore_toggle = false;
1859 if (_route && !no_redraw) {
1866 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1868 if (apply_to_selection) {
1869 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1873 /* Show our automation */
1875 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1876 i->second->set_marked_for_display (true);
1878 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1881 menu->set_active(true);
1886 /* Show processor automation */
1888 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1889 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1890 if ((*ii)->view == 0) {
1891 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1894 (*ii)->menu_item->set_active (true);
1907 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1909 if (apply_to_selection) {
1910 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1914 /* Show our automation */
1916 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1917 if (i->second->has_automation()) {
1918 i->second->set_marked_for_display (true);
1920 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1922 menu->set_active(true);
1927 /* Show processor automation */
1929 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1930 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1931 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1932 (*ii)->menu_item->set_active (true);
1944 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1946 if (apply_to_selection) {
1947 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1951 /* Hide our automation */
1953 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1954 i->second->set_marked_for_display (false);
1956 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1959 menu->set_active (false);
1963 /* Hide processor automation */
1965 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1966 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1967 (*ii)->menu_item->set_active (false);
1978 RouteTimeAxisView::region_view_added (RegionView* rv)
1980 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1981 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1982 boost::shared_ptr<AutomationTimeAxisView> atv;
1984 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1989 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1990 (*i)->add_ghost(rv);
1994 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1996 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2002 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2004 parent.remove_processor_automation_node (this);
2008 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2011 remove_child (pan->view);
2015 RouteTimeAxisView::ProcessorAutomationNode*
2016 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2018 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2020 if ((*i)->processor == processor) {
2022 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2023 if ((*ii)->what == what) {
2033 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2035 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2038 ProcessorAutomationNode* pan;
2040 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2041 /* session state may never have been saved with new plugin */
2042 error << _("programming error: ")
2043 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2044 processor->name(), what.type(), (int) what.channel(), what.id() )
2054 boost::shared_ptr<AutomationControl> control
2055 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2057 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2058 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2059 _editor, *this, false, parent_canvas,
2060 processor->describe_parameter (what), processor->name()));
2062 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2064 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2067 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2072 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2075 pan->menu_item->set_active (false);
2084 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2086 boost::shared_ptr<Processor> processor (p.lock ());
2088 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2089 /* The Amp processor is a special case and is dealt with separately */
2093 set<Evoral::Parameter> existing;
2095 processor->what_has_data (existing);
2097 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2099 Evoral::Parameter param (*i);
2100 boost::shared_ptr<AutomationLine> al;
2102 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2105 add_processor_automation_curve (processor, param);
2111 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2113 using namespace Menu_Helpers;
2117 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2119 _automation_tracks[param] = track;
2121 /* existing state overrides "show" argument */
2122 string s = track->gui_property ("visible");
2124 show = string_is_affirmative (s);
2127 /* this might or might not change the visibility status, so don't rely on it */
2128 track->set_marked_for_display (show);
2130 if (show && !no_redraw) {
2134 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2135 /* MIDI-related parameters are always in the menu, there's no
2136 reason to rebuild the menu just because we added a automation
2137 lane for one of them. But if we add a non-MIDI automation
2138 lane, then we need to invalidate the display menu.
2140 delete display_menu;
2146 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2148 boost::shared_ptr<Processor> processor (p.lock ());
2150 if (!processor || !processor->display_to_user ()) {
2154 /* we use this override to veto the Amp processor from the plugin menu,
2155 as its automation lane can be accessed using the special "Fader" menu
2159 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2163 using namespace Menu_Helpers;
2164 ProcessorAutomationInfo *rai;
2165 list<ProcessorAutomationInfo*>::iterator x;
2167 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2169 if (automatable.empty()) {
2173 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2174 if ((*x)->processor == processor) {
2179 if (x == processor_automation.end()) {
2181 rai = new ProcessorAutomationInfo (processor);
2182 processor_automation.push_back (rai);
2190 /* any older menu was deleted at the top of processors_changed()
2191 when we cleared the subplugin menu.
2194 rai->menu = manage (new Menu);
2195 MenuList& items = rai->menu->items();
2196 rai->menu->set_name ("ArdourContextMenu");
2200 std::set<Evoral::Parameter> has_visible_automation;
2201 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2203 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2205 ProcessorAutomationNode* pan;
2206 Gtk::CheckMenuItem* mitem;
2208 string name = processor->describe_parameter (*i);
2210 items.push_back (CheckMenuElem (name));
2211 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2213 _subplugin_menu_map[*i] = mitem;
2215 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2216 mitem->set_active(true);
2219 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2223 pan = new ProcessorAutomationNode (*i, mitem, *this);
2225 rai->lines.push_back (pan);
2229 pan->menu_item = mitem;
2233 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2236 /* add the menu for this processor, because the subplugin
2237 menu is always cleared at the top of processors_changed().
2238 this is the result of some poor design in gtkmm and/or
2242 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2247 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2248 RouteTimeAxisView::ProcessorAutomationNode* pan)
2250 bool showit = pan->menu_item->get_active();
2251 bool redraw = false;
2253 if (pan->view == 0 && showit) {
2254 add_processor_automation_curve (rai->processor, pan->what);
2258 if (pan->view && pan->view->set_marked_for_display (showit)) {
2262 if (redraw && !no_redraw) {
2268 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2270 if (c.type == RouteProcessorChange::MeterPointChange) {
2271 /* nothing to do if only the meter point has changed */
2275 using namespace Menu_Helpers;
2277 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2278 (*i)->valid = false;
2281 setup_processor_menu_and_curves ();
2283 bool deleted_processor_automation = false;
2285 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2287 list<ProcessorAutomationInfo*>::iterator tmp;
2295 processor_automation.erase (i);
2296 deleted_processor_automation = true;
2303 if (deleted_processor_automation && !no_redraw) {
2308 boost::shared_ptr<AutomationLine>
2309 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2311 ProcessorAutomationNode* pan;
2313 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2319 return boost::shared_ptr<AutomationLine>();
2323 RouteTimeAxisView::reset_processor_automation_curves ()
2325 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2331 RouteTimeAxisView::can_edit_name () const
2333 /* we do not allow track name changes if it is record enabled
2335 return !_route->record_enabled();
2339 RouteTimeAxisView::blink_rec_display (bool onoff)
2341 RouteUI::blink_rec_display (onoff);
2345 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2347 if (_ignore_set_layer_display) {
2351 if (apply_to_selection) {
2352 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2356 _view->set_layer_display (d);
2359 set_gui_property (X_("layer-display"), enum_2_string (d));
2364 RouteTimeAxisView::layer_display () const
2367 return _view->layer_display ();
2370 /* we don't know, since we don't have a _view, so just return something */
2376 boost::shared_ptr<AutomationTimeAxisView>
2377 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2379 AutomationTracks::iterator i = _automation_tracks.find(param);
2380 if (i != _automation_tracks.end()) {
2383 return boost::shared_ptr<AutomationTimeAxisView>();
2388 RouteTimeAxisView::fast_update ()
2390 gm.get_level_meter().update_meters ();
2394 RouteTimeAxisView::hide_meter ()
2397 gm.get_level_meter().hide_meters ();
2401 RouteTimeAxisView::show_meter ()
2407 RouteTimeAxisView::reset_meter ()
2409 if (Config->get_show_track_meters()) {
2410 int meter_width = 3;
2411 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2414 gm.get_level_meter().setup_meters (height - 9, meter_width);
2421 RouteTimeAxisView::clear_meter ()
2423 gm.get_level_meter().clear_meters ();
2427 RouteTimeAxisView::meter_changed ()
2429 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2431 if (_route && !no_redraw) {
2434 // reset peak when meter point changes
2435 gm.reset_peak_display();
2439 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2442 if (_route && !no_redraw) {
2448 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2450 using namespace Menu_Helpers;
2452 if (!_underlay_streams.empty()) {
2453 MenuList& parent_items = parent_menu->items();
2454 Menu* gs_menu = manage (new Menu);
2455 gs_menu->set_name ("ArdourContextMenu");
2456 MenuList& gs_items = gs_menu->items();
2458 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2460 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2461 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2462 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2468 RouteTimeAxisView::set_underlay_state()
2470 if (!underlay_xml_node) {
2474 XMLNodeList nlist = underlay_xml_node->children();
2475 XMLNodeConstIterator niter;
2476 XMLNode *child_node;
2478 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2479 child_node = *niter;
2481 if (child_node->name() != "Underlay") {
2485 XMLProperty* prop = child_node->property ("id");
2487 PBD::ID id (prop->value());
2489 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2492 add_underlay(v->view(), false);
2501 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2507 RouteTimeAxisView& other = v->trackview();
2509 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2510 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2511 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2515 _underlay_streams.push_back(v);
2516 other._underlay_mirrors.push_back(this);
2518 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2520 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2522 if (!underlay_xml_node) {
2523 underlay_xml_node = xml_node->add_child("Underlays");
2526 XMLNode* node = underlay_xml_node->add_child("Underlay");
2527 XMLProperty* prop = node->add_property("id");
2528 prop->set_value(v->trackview().route()->id().to_s());
2535 RouteTimeAxisView::remove_underlay (StreamView* v)
2541 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2542 RouteTimeAxisView& other = v->trackview();
2544 if (it != _underlay_streams.end()) {
2545 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2547 if (gm == other._underlay_mirrors.end()) {
2548 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2552 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2554 _underlay_streams.erase(it);
2555 other._underlay_mirrors.erase(gm);
2557 if (underlay_xml_node) {
2558 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2564 RouteTimeAxisView::set_button_names ()
2566 if (_route && _route->solo_safe()) {
2567 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2569 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2571 if (Config->get_solo_control_is_listen_control()) {
2572 switch (Config->get_listen_position()) {
2573 case AfterFaderListen:
2574 solo_button->set_text (_("A"));
2575 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2577 case PreFaderListen:
2578 solo_button->set_text (_("P"));
2579 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2583 solo_button->set_text (_("S"));
2584 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2586 mute_button->set_text (_("M"));
2590 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2592 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2593 if (i != _main_automation_menu_map.end()) {
2597 i = _subplugin_menu_map.find (param);
2598 if (i != _subplugin_menu_map.end()) {
2606 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2608 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2610 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2614 gain_track.reset (new AutomationTimeAxisView (_session,
2615 _route, _route->amp(), c, param,
2620 _route->amp()->describe_parameter(param)));
2623 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2626 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2630 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2632 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2634 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2638 mute_track.reset (new AutomationTimeAxisView (_session,
2639 _route, _route, c, param,
2644 _route->describe_parameter(param)));
2647 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2650 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2654 void add_region_to_list (RegionView* rv, RegionList* l)
2656 l->push_back (rv->region());
2660 RouteTimeAxisView::combine_regions ()
2662 /* as of may 2011, we do not offer uncombine for MIDI tracks
2665 if (!is_audio_track()) {
2673 RegionList selected_regions;
2674 boost::shared_ptr<Playlist> playlist = track()->playlist();
2676 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2678 if (selected_regions.size() < 2) {
2682 playlist->clear_changes ();
2683 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2685 _session->add_command (new StatefulDiffCommand (playlist));
2686 /* make the new region be selected */
2688 return _view->find_view (compound_region);
2692 RouteTimeAxisView::uncombine_regions ()
2694 /* as of may 2011, we do not offer uncombine for MIDI tracks
2696 if (!is_audio_track()) {
2704 RegionList selected_regions;
2705 boost::shared_ptr<Playlist> playlist = track()->playlist();
2707 /* have to grab selected regions first because the uncombine is going
2708 * to change that in the middle of the list traverse
2711 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2713 playlist->clear_changes ();
2715 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2716 playlist->uncombine (*i);
2719 _session->add_command (new StatefulDiffCommand (playlist));
2723 RouteTimeAxisView::state_id() const
2725 return string_compose ("rtav %1", _route->id().to_s());
2730 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2732 TimeAxisView::remove_child (c);
2734 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2736 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2737 if (i->second == a) {
2738 _automation_tracks.erase (i);