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 = 9 + std::max(2u, _session->track_number_decimals()) * number_label.char_pixel_width();
433 number_label.set_size_request(tnw, -1);
434 number_label.show ();
435 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, 0); // -2 = cellspacing
437 number_label.hide ();
438 name_hbox.set_size_request(TimeAxisView::name_width_px, 0);
443 RouteTimeAxisView::parameter_changed (string const & p)
445 if (p == "track-name-number") {
446 update_track_number_visibility();
451 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
453 if (what_changed.contains (ARDOUR::Properties::name)) {
459 RouteTimeAxisView::take_name_changed (void *src)
467 RouteTimeAxisView::playlist_click ()
469 build_playlist_menu ();
470 conditionally_add_to_selection ();
471 playlist_action_menu->popup (1, gtk_get_current_event_time());
475 RouteTimeAxisView::automation_click ()
477 conditionally_add_to_selection ();
478 build_automation_action_menu (false);
479 automation_action_menu->popup (1, gtk_get_current_event_time());
483 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
485 using namespace Menu_Helpers;
487 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
488 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
491 detach_menu (subplugin_menu);
493 _main_automation_menu_map.clear ();
494 delete automation_action_menu;
495 automation_action_menu = new Menu;
497 MenuList& items = automation_action_menu->items();
499 automation_action_menu->set_name ("ArdourContextMenu");
501 items.push_back (MenuElem (_("Show All Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
504 items.push_back (MenuElem (_("Show Existing Automation"),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
507 items.push_back (MenuElem (_("Hide All Automation"),
508 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
510 /* Attach the plugin submenu. It may have previously been used elsewhere,
511 so it was detached above
514 if (!subplugin_menu.items().empty()) {
515 items.push_back (SeparatorElem ());
516 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
517 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
522 RouteTimeAxisView::build_display_menu ()
524 using namespace Menu_Helpers;
528 TimeAxisView::build_display_menu ();
530 /* now fill it with our stuff */
532 MenuList& items = display_menu->items();
533 display_menu->set_name ("ArdourContextMenu");
535 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
537 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
539 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
541 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
543 items.push_back (SeparatorElem());
546 detach_menu (*_size_menu);
549 items.push_back (MenuElem (_("Height"), *_size_menu));
551 items.push_back (SeparatorElem());
553 if (!Profile->get_sae()) {
554 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
555 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
556 items.push_back (SeparatorElem());
559 // Hook for derived classes to add type specific stuff
560 append_extra_display_menu_items ();
564 Menu* layers_menu = manage (new Menu);
565 MenuList &layers_items = layers_menu->items();
566 layers_menu->set_name("ArdourContextMenu");
568 RadioMenuItem::Group layers_group;
570 /* Find out how many overlaid/stacked tracks we have in the selection */
574 TrackSelection const & s = _editor.get_selection().tracks;
575 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
576 StreamView* v = (*i)->view ();
581 switch (v->layer_display ()) {
592 /* We're not connecting to signal_toggled() here; in the case where these two items are
593 set to be in the `inconsistent' state, it seems that one or other will end up active
594 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
595 select the active one, no toggled signal is emitted so nothing happens.
598 _ignore_set_layer_display = true;
600 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
601 RadioMenuItem* 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), Overlaid, true));
606 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
607 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
608 i->set_active (overlaid == 0 && stacked != 0);
609 i->set_inconsistent (overlaid != 0 && stacked != 0);
610 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
612 _ignore_set_layer_display = false;
614 items.push_back (MenuElem (_("Layers"), *layers_menu));
616 if (!Profile->get_sae()) {
618 Menu* alignment_menu = manage (new Menu);
619 MenuList& alignment_items = alignment_menu->items();
620 alignment_menu->set_name ("ArdourContextMenu");
622 RadioMenuItem::Group align_group;
624 /* Same verbose hacks as for the layering options above */
630 boost::shared_ptr<Track> first_track;
632 TrackSelection const & s = _editor.get_selection().tracks;
633 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
634 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
635 if (!r || !r->is_track ()) {
640 first_track = r->track();
643 switch (r->track()->alignment_choice()) {
647 switch (r->track()->alignment_style()) {
648 case ExistingMaterial:
656 case UseExistingMaterial:
672 inconsistent = false;
681 if (!inconsistent && first_track) {
683 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
684 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
685 i->set_active (automatic != 0 && existing == 0 && capture == 0);
686 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
688 switch (first_track->alignment_choice()) {
690 switch (first_track->alignment_style()) {
691 case ExistingMaterial:
692 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
695 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
703 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
704 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
705 i->set_active (existing != 0 && capture == 0 && automatic == 0);
706 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
708 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
709 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
710 i->set_active (existing == 0 && capture != 0 && automatic == 0);
711 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
713 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
719 Menu* mode_menu = manage (new Menu);
720 MenuList& mode_items = mode_menu->items ();
721 mode_menu->set_name ("ArdourContextMenu");
723 RadioMenuItem::Group mode_group;
729 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
730 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
731 if (!r || !r->is_track ()) {
735 switch (r->track()->mode()) {
748 mode_items.push_back (RadioMenuElem (mode_group, _("Normal 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::Normal, true));
751 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
752 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
754 mode_items.push_back (RadioMenuElem (mode_group, _("Tape 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::Destructive, true));
757 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
758 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
760 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
761 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
762 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
763 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
764 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
766 items.push_back (MenuElem (_("Mode"), *mode_menu));
770 items.push_back (SeparatorElem());
772 build_playlist_menu ();
773 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
774 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
777 route_group_menu->detach ();
780 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
781 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
783 r.push_back (rtv->route ());
788 r.push_back (route ());
791 route_group_menu->build (r);
792 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
794 build_automation_action_menu (true);
795 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
797 items.push_back (SeparatorElem());
801 TrackSelection const & s = _editor.get_selection().tracks;
802 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
803 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
808 if (r->route()->active()) {
815 items.push_back (CheckMenuElem (_("Active")));
816 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
817 bool click_sets_active = true;
818 if (active > 0 && inactive == 0) {
819 i->set_active (true);
820 click_sets_active = false;
821 } else if (active > 0 && inactive > 0) {
822 i->set_inconsistent (true);
824 i->set_sensitive(! _session->transport_rolling());
825 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
827 items.push_back (SeparatorElem());
828 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
829 if (!Profile->get_sae()) {
830 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
832 items.push_front (SeparatorElem());
833 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
838 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
840 if (apply_to_selection) {
841 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
844 bool needs_bounce = false;
846 if (!track()->can_use_mode (mode, needs_bounce)) {
852 cerr << "would bounce this one\n";
857 track()->set_mode (mode);
859 rec_enable_button->remove ();
862 case ARDOUR::NonLayered:
864 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
865 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
867 case ARDOUR::Destructive:
868 rec_enable_button->set_text (string());
869 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
873 rec_enable_button->show_all ();
878 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
880 TimeAxisView::show_timestretch (start, end, layers, layer);
890 /* check that the time selection was made in our route, or our route group.
891 remember that route_group() == 0 implies the route is *not* in a edit group.
894 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
895 /* this doesn't apply to us */
899 /* ignore it if our edit group is not active */
901 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
906 if (timestretch_rect == 0) {
907 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
908 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
909 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
912 timestretch_rect->show ();
913 timestretch_rect->raise_to_top ();
915 double const x1 = start / _editor.get_current_zoom();
916 double const x2 = (end - 1) / _editor.get_current_zoom();
918 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
919 x2, current_height() * (layers - layer) / layers));
923 RouteTimeAxisView::hide_timestretch ()
925 TimeAxisView::hide_timestretch ();
927 if (timestretch_rect) {
928 timestretch_rect->hide ();
933 RouteTimeAxisView::show_selection (TimeSelection& ts)
937 /* ignore it if our edit group is not active or if the selection was started
938 in some other track or route group (remember that route_group() == 0 means
939 that the track is not in an route group).
942 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
943 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
949 TimeAxisView::show_selection (ts);
953 RouteTimeAxisView::set_height (uint32_t h)
956 bool height_changed = (height == 0) || (h != height);
959 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
962 gm.get_level_meter().setup_meters (gmlen, meter_width);
964 TimeAxisView::set_height (h);
967 _view->set_height ((double) current_height());
970 if (height >= preset_height (HeightNormal)) {
974 gm.get_gain_slider().show();
976 if (!_route || _route->is_monitor()) {
981 if (rec_enable_button)
982 rec_enable_button->show();
984 route_group_button.show();
985 automation_button.show();
987 if (is_track() && track()->mode() == ARDOUR::Normal) {
988 playlist_button.show();
995 gm.get_gain_slider().hide();
997 if (!_route || _route->is_monitor()) {
1000 solo_button->show();
1002 if (rec_enable_button)
1003 rec_enable_button->show();
1005 route_group_button.hide ();
1006 automation_button.hide ();
1008 if (is_track() && track()->mode() == ARDOUR::Normal) {
1009 playlist_button.hide ();
1014 if (height_changed && !no_redraw) {
1015 /* only emit the signal if the height really changed */
1021 RouteTimeAxisView::route_color_changed ()
1024 _view->apply_color (color(), StreamView::RegionColor);
1027 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1031 RouteTimeAxisView::reset_samples_per_pixel ()
1033 set_samples_per_pixel (_editor.get_current_zoom());
1037 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1042 speed = track()->speed();
1046 _view->set_samples_per_pixel (fpp * speed);
1049 TimeAxisView::set_samples_per_pixel (fpp * speed);
1053 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1055 if (!mitem->get_active()) {
1056 /* this is one of the two calls made when these radio menu items change status. this one
1057 is for the item that became inactive, and we want to ignore it.
1062 if (apply_to_selection) {
1063 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1066 track()->set_align_choice (choice);
1072 RouteTimeAxisView::rename_current_playlist ()
1074 ArdourPrompter prompter (true);
1077 boost::shared_ptr<Track> tr = track();
1078 if (!tr || tr->destructive()) {
1082 boost::shared_ptr<Playlist> pl = tr->playlist();
1087 prompter.set_title (_("Rename Playlist"));
1088 prompter.set_prompt (_("New name for playlist:"));
1089 prompter.set_initial_text (pl->name());
1090 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1091 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1093 switch (prompter.run ()) {
1094 case Gtk::RESPONSE_ACCEPT:
1095 prompter.get_result (name);
1096 if (name.length()) {
1097 pl->set_name (name);
1107 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1109 std::string ret (basename);
1111 std::string const group_string = "." + route_group()->name() + ".";
1113 // iterate through all playlists
1115 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1116 std::string tmp = (*i)->name();
1118 std::string::size_type idx = tmp.find(group_string);
1119 // find those which belong to this group
1120 if (idx != string::npos) {
1121 tmp = tmp.substr(idx + group_string.length());
1123 // and find the largest current number
1125 if (x > maxnumber) {
1134 snprintf (buf, sizeof(buf), "%d", maxnumber);
1136 ret = this->name() + "." + route_group()->name () + "." + buf;
1142 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1146 boost::shared_ptr<Track> tr = track ();
1147 if (!tr || tr->destructive()) {
1151 boost::shared_ptr<const Playlist> pl = tr->playlist();
1158 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1159 name = resolve_new_group_playlist_name(name, playlists_before_op);
1162 while (_session->playlists->by_name(name)) {
1163 name = Playlist::bump_name (name, *_session);
1166 // TODO: The prompter "new" button should be de-activated if the user
1167 // specifies a playlist name which already exists in the session.
1171 ArdourPrompter prompter (true);
1173 prompter.set_title (_("New Copy Playlist"));
1174 prompter.set_prompt (_("Name for new playlist:"));
1175 prompter.set_initial_text (name);
1176 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1177 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1178 prompter.show_all ();
1180 switch (prompter.run ()) {
1181 case Gtk::RESPONSE_ACCEPT:
1182 prompter.get_result (name);
1190 if (name.length()) {
1191 tr->use_copy_playlist ();
1192 tr->playlist()->set_name (name);
1197 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1201 boost::shared_ptr<Track> tr = track ();
1202 if (!tr || tr->destructive()) {
1206 boost::shared_ptr<const Playlist> pl = tr->playlist();
1213 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1214 name = resolve_new_group_playlist_name(name,playlists_before_op);
1217 while (_session->playlists->by_name(name)) {
1218 name = Playlist::bump_name (name, *_session);
1224 ArdourPrompter prompter (true);
1226 prompter.set_title (_("New Playlist"));
1227 prompter.set_prompt (_("Name for new playlist:"));
1228 prompter.set_initial_text (name);
1229 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1230 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1232 switch (prompter.run ()) {
1233 case Gtk::RESPONSE_ACCEPT:
1234 prompter.get_result (name);
1242 if (name.length()) {
1243 tr->use_new_playlist ();
1244 tr->playlist()->set_name (name);
1249 RouteTimeAxisView::clear_playlist ()
1251 boost::shared_ptr<Track> tr = track ();
1252 if (!tr || tr->destructive()) {
1256 boost::shared_ptr<Playlist> pl = tr->playlist();
1261 _editor.clear_playlist (pl);
1265 RouteTimeAxisView::speed_changed ()
1267 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1271 RouteTimeAxisView::update_diskstream_display ()
1281 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1283 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1285 /* special case: select/deselect all tracks */
1286 if (_editor.get_selection().selected (this)) {
1287 _editor.get_selection().clear_tracks ();
1289 _editor.select_all_tracks ();
1295 switch (ArdourKeyboard::selection_type (ev->state)) {
1296 case Selection::Toggle:
1297 _editor.get_selection().toggle (this);
1300 case Selection::Set:
1301 _editor.get_selection().set (this);
1304 case Selection::Extend:
1305 _editor.extend_selection_to_track (*this);
1308 case Selection::Add:
1309 _editor.get_selection().add (this);
1315 RouteTimeAxisView::set_selected_points (PointSelection& points)
1317 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1318 (*i)->set_selected_points (points);
1323 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1326 _view->set_selected_regionviews (regions);
1330 /** Add the selectable things that we have to a list.
1331 * @param results List to add things to.
1334 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1339 speed = track()->speed();
1342 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1343 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1345 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1346 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1349 /* pick up visible automation tracks */
1351 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1352 if (!(*i)->hidden()) {
1353 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1359 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1362 _view->get_inverted_selectables (sel, results);
1365 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1366 if (!(*i)->hidden()) {
1367 (*i)->get_inverted_selectables (sel, results);
1375 RouteTimeAxisView::route_group () const
1377 return _route->route_group();
1381 RouteTimeAxisView::name() const
1383 return _route->name();
1386 boost::shared_ptr<Playlist>
1387 RouteTimeAxisView::playlist () const
1389 boost::shared_ptr<Track> tr;
1391 if ((tr = track()) != 0) {
1392 return tr->playlist();
1394 return boost::shared_ptr<Playlist> ();
1399 RouteTimeAxisView::name_entry_changed ()
1401 TimeAxisView::name_entry_changed ();
1403 string x = name_entry->get_text ();
1405 if (x == _route->name()) {
1409 strip_whitespace_edges (x);
1411 if (x.length() == 0) {
1412 name_entry->set_text (_route->name());
1416 if (_session->route_name_internal (x)) {
1417 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1419 name_entry->grab_focus ();
1420 } else if (RouteUI::verify_new_route_name (x)) {
1421 _route->set_name (x);
1423 name_entry->grab_focus ();
1427 boost::shared_ptr<Region>
1428 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1430 boost::shared_ptr<Playlist> pl = playlist ();
1433 return pl->find_next_region (pos, point, dir);
1436 return boost::shared_ptr<Region> ();
1440 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1442 boost::shared_ptr<Playlist> pl = playlist ();
1445 return pl->find_next_region_boundary (pos, dir);
1452 RouteTimeAxisView::fade_range (TimeSelection& selection)
1454 boost::shared_ptr<Playlist> what_we_got;
1455 boost::shared_ptr<Track> tr = track ();
1456 boost::shared_ptr<Playlist> playlist;
1459 /* route is a bus, not a track */
1463 playlist = tr->playlist();
1465 TimeSelection time (selection);
1466 float const speed = tr->speed();
1467 if (speed != 1.0f) {
1468 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1469 (*i).start = session_frame_to_track_frame((*i).start, speed);
1470 (*i).end = session_frame_to_track_frame((*i).end, speed);
1474 playlist->clear_changes ();
1475 playlist->clear_owned_changes ();
1477 playlist->fade_range (time);
1479 vector<Command*> cmds;
1480 playlist->rdiff (cmds);
1481 _session->add_commands (cmds);
1482 _session->add_command (new StatefulDiffCommand (playlist));
1487 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1489 boost::shared_ptr<Playlist> what_we_got;
1490 boost::shared_ptr<Track> tr = track ();
1491 boost::shared_ptr<Playlist> playlist;
1494 /* route is a bus, not a track */
1498 playlist = tr->playlist();
1500 TimeSelection time (selection.time);
1501 float const speed = tr->speed();
1502 if (speed != 1.0f) {
1503 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1504 (*i).start = session_frame_to_track_frame((*i).start, speed);
1505 (*i).end = session_frame_to_track_frame((*i).end, speed);
1509 playlist->clear_changes ();
1510 playlist->clear_owned_changes ();
1514 if (playlist->cut (time) != 0) {
1515 if (Config->get_edit_mode() == Ripple)
1516 playlist->ripple(time.start(), -time.length(), NULL);
1517 // no need to exclude any regions from rippling here
1519 vector<Command*> cmds;
1520 playlist->rdiff (cmds);
1521 _session->add_commands (cmds);
1523 _session->add_command (new StatefulDiffCommand (playlist));
1528 if ((what_we_got = playlist->cut (time)) != 0) {
1529 _editor.get_cut_buffer().add (what_we_got);
1530 if (Config->get_edit_mode() == Ripple)
1531 playlist->ripple(time.start(), -time.length(), NULL);
1532 // no need to exclude any regions from rippling here
1534 vector<Command*> cmds;
1535 playlist->rdiff (cmds);
1536 _session->add_commands (cmds);
1538 _session->add_command (new StatefulDiffCommand (playlist));
1542 if ((what_we_got = playlist->copy (time)) != 0) {
1543 _editor.get_cut_buffer().add (what_we_got);
1548 if ((what_we_got = playlist->cut (time)) != 0) {
1549 if (Config->get_edit_mode() == Ripple)
1550 playlist->ripple(time.start(), -time.length(), NULL);
1551 // no need to exclude any regions from rippling here
1553 vector<Command*> cmds;
1554 playlist->rdiff (cmds);
1555 _session->add_commands (cmds);
1556 _session->add_command (new StatefulDiffCommand (playlist));
1557 what_we_got->release ();
1564 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1570 boost::shared_ptr<Playlist> pl = playlist ();
1571 PlaylistSelection::iterator p;
1573 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1575 if (p == selection.playlists.end()) {
1579 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1581 if (track()->speed() != 1.0f) {
1582 pos = session_frame_to_track_frame (pos, track()->speed());
1583 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1586 pl->clear_changes ();
1587 if (Config->get_edit_mode() == Ripple) {
1588 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1589 framecnt_t amount = extent.second - extent.first;
1590 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1592 pl->paste (*p, pos, times);
1594 vector<Command*> cmds;
1596 _session->add_commands (cmds);
1598 _session->add_command (new StatefulDiffCommand (pl));
1604 struct PlaylistSorter {
1605 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1606 return a->sort_id() < b->sort_id();
1611 RouteTimeAxisView::build_playlist_menu ()
1613 using namespace Menu_Helpers;
1619 delete playlist_action_menu;
1620 playlist_action_menu = new Menu;
1621 playlist_action_menu->set_name ("ArdourContextMenu");
1623 MenuList& playlist_items = playlist_action_menu->items();
1624 playlist_action_menu->set_name ("ArdourContextMenu");
1625 playlist_items.clear();
1627 RadioMenuItem::Group playlist_group;
1628 boost::shared_ptr<Track> tr = track ();
1630 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1632 /* sort the playlists */
1634 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1636 /* add the playlists to the menu */
1637 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1638 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1639 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1640 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1642 if (tr->playlist()->id() == (*i)->id()) {
1648 playlist_items.push_back (SeparatorElem());
1649 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1650 playlist_items.push_back (SeparatorElem());
1652 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1653 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1654 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1657 // Use a label which tells the user what is happening
1658 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1659 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1663 playlist_items.push_back (SeparatorElem());
1664 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1665 playlist_items.push_back (SeparatorElem());
1667 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1671 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1673 assert (is_track());
1675 // exit if we were triggered by deactivating the old playlist
1676 if (!item->get_active()) {
1680 boost::shared_ptr<Playlist> pl (wpl.lock());
1686 if (track()->playlist() == pl) {
1687 // exit when use_playlist is called by the creation of the playlist menu
1688 // or the playlist choice is unchanged
1692 track()->use_playlist (pl);
1694 RouteGroup* rg = route_group();
1696 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1697 std::string group_string = "." + rg->name() + ".";
1699 std::string take_name = pl->name();
1700 std::string::size_type idx = take_name.find(group_string);
1702 if (idx == std::string::npos)
1705 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1707 boost::shared_ptr<RouteList> rl (rg->route_list());
1709 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1710 if ((*i) == this->route()) {
1714 std::string playlist_name = (*i)->name()+group_string+take_name;
1716 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1721 if (track->freeze_state() == Track::Frozen) {
1722 /* Don't change playlists of frozen tracks */
1726 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1728 // No playlist for this track for this take yet, make it
1729 track->use_new_playlist();
1730 track->playlist()->set_name(playlist_name);
1732 track->use_playlist(ipl);
1739 RouteTimeAxisView::update_playlist_tip ()
1741 RouteGroup* rg = route_group ();
1742 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1743 string group_string = "." + rg->name() + ".";
1745 string take_name = track()->playlist()->name();
1746 string::size_type idx = take_name.find(group_string);
1748 if (idx != string::npos) {
1749 /* find the bit containing the take number / name */
1750 take_name = take_name.substr (idx + group_string.length());
1752 /* set the playlist button tooltip to the take name */
1753 ARDOUR_UI::instance()->set_tip (
1755 string_compose(_("Take: %1.%2"),
1756 Glib::Markup::escape_text(rg->name()),
1757 Glib::Markup::escape_text(take_name))
1764 /* set the playlist button tooltip to the playlist name */
1765 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1770 RouteTimeAxisView::show_playlist_selector ()
1772 _editor.playlist_selector().show_for (this);
1776 RouteTimeAxisView::map_frozen ()
1782 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1784 switch (track()->freeze_state()) {
1786 playlist_button.set_sensitive (false);
1787 rec_enable_button->set_sensitive (false);
1790 playlist_button.set_sensitive (true);
1791 rec_enable_button->set_sensitive (true);
1797 RouteTimeAxisView::color_handler ()
1799 //case cTimeStretchOutline:
1800 if (timestretch_rect) {
1801 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1803 //case cTimeStretchFill:
1804 if (timestretch_rect) {
1805 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1811 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1812 * Will add track if necessary.
1815 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1817 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1818 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1821 /* it doesn't exist yet, so we don't care about the button state: just add it */
1822 create_automation_child (param, true);
1825 bool yn = menu->get_active();
1826 bool changed = false;
1828 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1830 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1831 will have done that for us.
1834 if (changed && !no_redraw) {
1842 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1844 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1850 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1852 if (menu && !_hidden) {
1853 ignore_toggle = true;
1854 menu->set_active (false);
1855 ignore_toggle = false;
1858 if (_route && !no_redraw) {
1865 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1867 if (apply_to_selection) {
1868 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1872 /* Show our automation */
1874 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1875 i->second->set_marked_for_display (true);
1877 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1880 menu->set_active(true);
1885 /* Show processor automation */
1887 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1888 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1889 if ((*ii)->view == 0) {
1890 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1893 (*ii)->menu_item->set_active (true);
1906 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1908 if (apply_to_selection) {
1909 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1913 /* Show our automation */
1915 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1916 if (i->second->has_automation()) {
1917 i->second->set_marked_for_display (true);
1919 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1921 menu->set_active(true);
1926 /* Show processor automation */
1928 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1929 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1930 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1931 (*ii)->menu_item->set_active (true);
1943 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1945 if (apply_to_selection) {
1946 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1950 /* Hide our automation */
1952 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1953 i->second->set_marked_for_display (false);
1955 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1958 menu->set_active (false);
1962 /* Hide processor automation */
1964 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1965 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1966 (*ii)->menu_item->set_active (false);
1977 RouteTimeAxisView::region_view_added (RegionView* rv)
1979 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1980 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1981 boost::shared_ptr<AutomationTimeAxisView> atv;
1983 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1988 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1989 (*i)->add_ghost(rv);
1993 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1995 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2001 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2003 parent.remove_processor_automation_node (this);
2007 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2010 remove_child (pan->view);
2014 RouteTimeAxisView::ProcessorAutomationNode*
2015 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2017 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2019 if ((*i)->processor == processor) {
2021 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2022 if ((*ii)->what == what) {
2032 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2034 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2037 ProcessorAutomationNode* pan;
2039 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2040 /* session state may never have been saved with new plugin */
2041 error << _("programming error: ")
2042 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2043 processor->name(), what.type(), (int) what.channel(), what.id() )
2053 boost::shared_ptr<AutomationControl> control
2054 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2056 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2057 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2058 _editor, *this, false, parent_canvas,
2059 processor->describe_parameter (what), processor->name()));
2061 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2063 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2066 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2071 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2074 pan->menu_item->set_active (false);
2083 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2085 boost::shared_ptr<Processor> processor (p.lock ());
2087 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2088 /* The Amp processor is a special case and is dealt with separately */
2092 set<Evoral::Parameter> existing;
2094 processor->what_has_data (existing);
2096 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2098 Evoral::Parameter param (*i);
2099 boost::shared_ptr<AutomationLine> al;
2101 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2104 add_processor_automation_curve (processor, param);
2110 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2112 using namespace Menu_Helpers;
2116 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2118 _automation_tracks[param] = track;
2120 /* existing state overrides "show" argument */
2121 string s = track->gui_property ("visible");
2123 show = string_is_affirmative (s);
2126 /* this might or might not change the visibility status, so don't rely on it */
2127 track->set_marked_for_display (show);
2129 if (show && !no_redraw) {
2133 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2134 /* MIDI-related parameters are always in the menu, there's no
2135 reason to rebuild the menu just because we added a automation
2136 lane for one of them. But if we add a non-MIDI automation
2137 lane, then we need to invalidate the display menu.
2139 delete display_menu;
2145 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2147 boost::shared_ptr<Processor> processor (p.lock ());
2149 if (!processor || !processor->display_to_user ()) {
2153 /* we use this override to veto the Amp processor from the plugin menu,
2154 as its automation lane can be accessed using the special "Fader" menu
2158 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2162 using namespace Menu_Helpers;
2163 ProcessorAutomationInfo *rai;
2164 list<ProcessorAutomationInfo*>::iterator x;
2166 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2168 if (automatable.empty()) {
2172 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2173 if ((*x)->processor == processor) {
2178 if (x == processor_automation.end()) {
2180 rai = new ProcessorAutomationInfo (processor);
2181 processor_automation.push_back (rai);
2189 /* any older menu was deleted at the top of processors_changed()
2190 when we cleared the subplugin menu.
2193 rai->menu = manage (new Menu);
2194 MenuList& items = rai->menu->items();
2195 rai->menu->set_name ("ArdourContextMenu");
2199 std::set<Evoral::Parameter> has_visible_automation;
2200 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2202 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2204 ProcessorAutomationNode* pan;
2205 Gtk::CheckMenuItem* mitem;
2207 string name = processor->describe_parameter (*i);
2209 items.push_back (CheckMenuElem (name));
2210 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2212 _subplugin_menu_map[*i] = mitem;
2214 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2215 mitem->set_active(true);
2218 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2222 pan = new ProcessorAutomationNode (*i, mitem, *this);
2224 rai->lines.push_back (pan);
2228 pan->menu_item = mitem;
2232 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2235 /* add the menu for this processor, because the subplugin
2236 menu is always cleared at the top of processors_changed().
2237 this is the result of some poor design in gtkmm and/or
2241 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2246 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2247 RouteTimeAxisView::ProcessorAutomationNode* pan)
2249 bool showit = pan->menu_item->get_active();
2250 bool redraw = false;
2252 if (pan->view == 0 && showit) {
2253 add_processor_automation_curve (rai->processor, pan->what);
2257 if (pan->view && pan->view->set_marked_for_display (showit)) {
2261 if (redraw && !no_redraw) {
2267 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2269 if (c.type == RouteProcessorChange::MeterPointChange) {
2270 /* nothing to do if only the meter point has changed */
2274 using namespace Menu_Helpers;
2276 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2277 (*i)->valid = false;
2280 setup_processor_menu_and_curves ();
2282 bool deleted_processor_automation = false;
2284 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2286 list<ProcessorAutomationInfo*>::iterator tmp;
2294 processor_automation.erase (i);
2295 deleted_processor_automation = true;
2302 if (deleted_processor_automation && !no_redraw) {
2307 boost::shared_ptr<AutomationLine>
2308 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2310 ProcessorAutomationNode* pan;
2312 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2318 return boost::shared_ptr<AutomationLine>();
2322 RouteTimeAxisView::reset_processor_automation_curves ()
2324 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2330 RouteTimeAxisView::can_edit_name () const
2332 /* we do not allow track name changes if it is record enabled
2334 return !_route->record_enabled();
2338 RouteTimeAxisView::blink_rec_display (bool onoff)
2340 RouteUI::blink_rec_display (onoff);
2344 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2346 if (_ignore_set_layer_display) {
2350 if (apply_to_selection) {
2351 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2355 _view->set_layer_display (d);
2358 set_gui_property (X_("layer-display"), enum_2_string (d));
2363 RouteTimeAxisView::layer_display () const
2366 return _view->layer_display ();
2369 /* we don't know, since we don't have a _view, so just return something */
2375 boost::shared_ptr<AutomationTimeAxisView>
2376 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2378 AutomationTracks::iterator i = _automation_tracks.find(param);
2379 if (i != _automation_tracks.end()) {
2382 return boost::shared_ptr<AutomationTimeAxisView>();
2387 RouteTimeAxisView::fast_update ()
2389 gm.get_level_meter().update_meters ();
2393 RouteTimeAxisView::hide_meter ()
2396 gm.get_level_meter().hide_meters ();
2400 RouteTimeAxisView::show_meter ()
2406 RouteTimeAxisView::reset_meter ()
2408 if (Config->get_show_track_meters()) {
2409 int meter_width = 3;
2410 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2413 gm.get_level_meter().setup_meters (height - 9, meter_width);
2420 RouteTimeAxisView::clear_meter ()
2422 gm.get_level_meter().clear_meters ();
2426 RouteTimeAxisView::meter_changed ()
2428 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2430 if (_route && !no_redraw) {
2433 // reset peak when meter point changes
2434 gm.reset_peak_display();
2438 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2441 if (_route && !no_redraw) {
2447 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2449 using namespace Menu_Helpers;
2451 if (!_underlay_streams.empty()) {
2452 MenuList& parent_items = parent_menu->items();
2453 Menu* gs_menu = manage (new Menu);
2454 gs_menu->set_name ("ArdourContextMenu");
2455 MenuList& gs_items = gs_menu->items();
2457 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2459 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2460 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2461 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2467 RouteTimeAxisView::set_underlay_state()
2469 if (!underlay_xml_node) {
2473 XMLNodeList nlist = underlay_xml_node->children();
2474 XMLNodeConstIterator niter;
2475 XMLNode *child_node;
2477 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2478 child_node = *niter;
2480 if (child_node->name() != "Underlay") {
2484 XMLProperty* prop = child_node->property ("id");
2486 PBD::ID id (prop->value());
2488 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2491 add_underlay(v->view(), false);
2500 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2506 RouteTimeAxisView& other = v->trackview();
2508 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2509 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2510 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2514 _underlay_streams.push_back(v);
2515 other._underlay_mirrors.push_back(this);
2517 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2519 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2521 if (!underlay_xml_node) {
2522 underlay_xml_node = xml_node->add_child("Underlays");
2525 XMLNode* node = underlay_xml_node->add_child("Underlay");
2526 XMLProperty* prop = node->add_property("id");
2527 prop->set_value(v->trackview().route()->id().to_s());
2534 RouteTimeAxisView::remove_underlay (StreamView* v)
2540 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2541 RouteTimeAxisView& other = v->trackview();
2543 if (it != _underlay_streams.end()) {
2544 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2546 if (gm == other._underlay_mirrors.end()) {
2547 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2551 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2553 _underlay_streams.erase(it);
2554 other._underlay_mirrors.erase(gm);
2556 if (underlay_xml_node) {
2557 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2563 RouteTimeAxisView::set_button_names ()
2565 if (_route && _route->solo_safe()) {
2566 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2568 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2570 if (Config->get_solo_control_is_listen_control()) {
2571 switch (Config->get_listen_position()) {
2572 case AfterFaderListen:
2573 solo_button->set_text (_("A"));
2574 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2576 case PreFaderListen:
2577 solo_button->set_text (_("P"));
2578 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2582 solo_button->set_text (_("S"));
2583 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2585 mute_button->set_text (_("M"));
2589 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2591 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2592 if (i != _main_automation_menu_map.end()) {
2596 i = _subplugin_menu_map.find (param);
2597 if (i != _subplugin_menu_map.end()) {
2605 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2607 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2609 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2613 gain_track.reset (new AutomationTimeAxisView (_session,
2614 _route, _route->amp(), c, param,
2619 _route->amp()->describe_parameter(param)));
2622 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2625 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2629 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2631 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2633 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2637 mute_track.reset (new AutomationTimeAxisView (_session,
2638 _route, _route, c, param,
2643 _route->describe_parameter(param)));
2646 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2649 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2653 void add_region_to_list (RegionView* rv, RegionList* l)
2655 l->push_back (rv->region());
2659 RouteTimeAxisView::combine_regions ()
2661 /* as of may 2011, we do not offer uncombine for MIDI tracks
2664 if (!is_audio_track()) {
2672 RegionList selected_regions;
2673 boost::shared_ptr<Playlist> playlist = track()->playlist();
2675 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2677 if (selected_regions.size() < 2) {
2681 playlist->clear_changes ();
2682 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2684 _session->add_command (new StatefulDiffCommand (playlist));
2685 /* make the new region be selected */
2687 return _view->find_view (compound_region);
2691 RouteTimeAxisView::uncombine_regions ()
2693 /* as of may 2011, we do not offer uncombine for MIDI tracks
2695 if (!is_audio_track()) {
2703 RegionList selected_regions;
2704 boost::shared_ptr<Playlist> playlist = track()->playlist();
2706 /* have to grab selected regions first because the uncombine is going
2707 * to change that in the middle of the list traverse
2710 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2712 playlist->clear_changes ();
2714 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2715 playlist->uncombine (*i);
2718 _session->add_command (new StatefulDiffCommand (playlist));
2722 RouteTimeAxisView::state_id() const
2724 return string_compose ("rtav %1", _route->id().to_s());
2729 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2731 TimeAxisView::remove_child (c);
2733 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2735 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2736 if (i->second == a) {
2737 _automation_tracks.erase (i);