2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("G"))
100 , playlist_button (_("P"))
101 , automation_button (_("A"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 75, 14)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("tracknumber label");
112 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
113 number_label.set_alignment(.5, .5);
114 number_label.set_fallthrough_to_parent (true);
115 track_number_v_size_group->add_widget(number_label);
117 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
121 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
123 RouteUI::set_route (rt);
125 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
126 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
127 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
130 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
133 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
134 gm.get_level_meter().set_no_show_all();
135 gm.get_level_meter().setup_meters(50, meter_width);
136 gm.update_gain_sensitive ();
138 string str = gui_property ("height");
140 set_height (atoi (str));
142 set_height (preset_height (HeightNormal));
145 if (!_route->is_auditioner()) {
146 if (gui_property ("visible").empty()) {
147 set_gui_property ("visible", true);
150 set_gui_property ("visible", false);
154 update_solo_display ();
156 timestretch_rect = 0;
159 ignore_toggle = false;
161 route_group_button.set_name ("route button");
162 playlist_button.set_name ("route button");
163 automation_button.set_name ("route button");
165 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
166 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
167 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 if (ARDOUR::Profile->get_mixbus()) {
172 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
174 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
176 controls_button_size_group->add_widget(*rec_enable_button);
178 if (is_midi_track()) {
179 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
180 gm.set_fader_name ("MidiTrackFader");
182 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
183 gm.set_fader_name ("AudioTrackFader");
186 rec_enable_button->set_sensitive (_session->writable());
188 /* set playlist button tip to the current playlist, and make it update when it changes */
189 update_playlist_tip ();
190 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
193 gm.set_fader_name ("AudioBusFader");
194 Gtk::Fixed *blank = manage(new Gtk::Fixed());
195 controls_button_size_group->add_widget(*blank);
196 if (ARDOUR::Profile->get_mixbus() ) {
197 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
199 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
206 if (!ARDOUR::Profile->get_mixbus()) {
207 controls_meters_size_group->add_widget (gm.get_level_meter());
210 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
211 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
213 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
215 if (ARDOUR::Profile->get_mixbus()) {
216 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
220 controls_button_size_group->add_widget(*mute_button);
221 track_number_v_size_group->add_widget(*mute_button);
223 if (!_route->is_master()) {
224 if (ARDOUR::Profile->get_mixbus()) {
225 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
227 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
229 controls_button_size_group->add_widget(*solo_button);
231 Gtk::Fixed *blank = manage(new Gtk::Fixed());
232 controls_button_size_group->add_widget(*blank);
233 if (ARDOUR::Profile->get_mixbus()) {
234 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 if (ARDOUR::Profile->get_mixbus()) {
242 controls_button_size_group->add_widget(route_group_button);
243 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
246 else if (!ARDOUR::Profile->get_trx()) {
247 controls_button_size_group->add_widget(route_group_button);
248 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
252 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
253 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
254 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
256 mute_button->set_tweaks(ArdourButton::Square);
257 solo_button->set_tweaks(ArdourButton::Square);
258 rec_enable_button->set_tweaks(ArdourButton::Square);
259 playlist_button.set_tweaks(ArdourButton::Square);
260 automation_button.set_tweaks(ArdourButton::Square);
261 route_group_button.set_tweaks(ArdourButton::Square);
263 if (is_midi_track()) {
264 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
266 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
269 update_track_number_visibility();
272 if (ARDOUR::Profile->get_mixbus()) {
273 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
274 controls_button_size_group->add_widget(automation_button);
276 else if (!ARDOUR::Profile->get_trx()) {
277 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 controls_button_size_group->add_widget(automation_button);
281 if (is_track() && track()->mode() == ARDOUR::Normal) {
282 if (ARDOUR::Profile->get_mixbus()) {
283 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
284 controls_button_size_group->add_widget(playlist_button);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
288 controls_button_size_group->add_widget(playlist_button);
294 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
295 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
299 str = gui_property ("layer-display");
301 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
304 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
305 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
307 /* pick up the correct freeze state */
312 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
313 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
315 PropertyList* plist = new PropertyList();
317 plist->add (ARDOUR::Properties::mute, true);
318 plist->add (ARDOUR::Properties::solo, true);
320 route_group_menu = new RouteGroupMenu (_session, plist);
322 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
324 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
327 RouteTimeAxisView::~RouteTimeAxisView ()
329 CatchDeletion (this);
331 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
335 delete playlist_action_menu;
336 playlist_action_menu = 0;
341 _automation_tracks.clear ();
343 delete route_group_menu;
347 RouteTimeAxisView::post_construct ()
349 /* map current state of the route */
351 update_diskstream_display ();
352 setup_processor_menu_and_curves ();
353 reset_processor_automation_curves ();
356 /** Set up the processor menu for the current set of processors, and
357 * display automation curves for any parameters which have data.
360 RouteTimeAxisView::setup_processor_menu_and_curves ()
362 _subplugin_menu_map.clear ();
363 subplugin_menu.items().clear ();
364 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
365 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
369 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
371 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
372 if (_route->route_group()) {
373 _route->route_group()->remove (_route);
379 r.push_back (route ());
381 route_group_menu->build (r);
382 route_group_menu->menu()->popup (ev->button, ev->time);
388 RouteTimeAxisView::playlist_changed ()
394 RouteTimeAxisView::label_view ()
396 string x = _route->name ();
397 if (x != name_label.get_text ()) {
398 name_label.set_text (x);
400 const int64_t track_number = _route->track_number ();
401 if (track_number == 0) {
402 number_label.set_text ("");
404 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
409 RouteTimeAxisView::update_track_number_visibility ()
412 bool show_label = _session->config.get_track_name_number();
414 if (_route && _route->is_master()) {
418 if (number_label.get_parent()) {
419 controls_table.remove (number_label);
422 if (ARDOUR::Profile->get_mixbus()) {
423 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
425 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
427 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
428 // except the width of the number label is subtracted from the name-hbox, so we
429 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
430 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
432 number_label.set_size_request(tnw, -1);
433 number_label.show ();
434 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
436 number_label.hide ();
437 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
442 RouteTimeAxisView::parameter_changed (string const & p)
444 if (p == "track-name-number") {
445 update_track_number_visibility();
450 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
452 if (what_changed.contains (ARDOUR::Properties::name)) {
458 RouteTimeAxisView::take_name_changed (void *src)
466 RouteTimeAxisView::playlist_click ()
468 build_playlist_menu ();
469 conditionally_add_to_selection ();
470 playlist_action_menu->popup (1, gtk_get_current_event_time());
474 RouteTimeAxisView::automation_click ()
476 conditionally_add_to_selection ();
477 build_automation_action_menu (false);
478 automation_action_menu->popup (1, gtk_get_current_event_time());
482 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
484 using namespace Menu_Helpers;
486 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
487 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
490 detach_menu (subplugin_menu);
492 _main_automation_menu_map.clear ();
493 delete automation_action_menu;
494 automation_action_menu = new Menu;
496 MenuList& items = automation_action_menu->items();
498 automation_action_menu->set_name ("ArdourContextMenu");
500 items.push_back (MenuElem (_("Show All Automation"),
501 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
503 items.push_back (MenuElem (_("Show Existing Automation"),
504 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
506 items.push_back (MenuElem (_("Hide All Automation"),
507 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
509 /* Attach the plugin submenu. It may have previously been used elsewhere,
510 so it was detached above
513 if (!subplugin_menu.items().empty()) {
514 items.push_back (SeparatorElem ());
515 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
516 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
521 RouteTimeAxisView::build_display_menu ()
523 using namespace Menu_Helpers;
527 TimeAxisView::build_display_menu ();
529 /* now fill it with our stuff */
531 MenuList& items = display_menu->items();
532 display_menu->set_name ("ArdourContextMenu");
534 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
536 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
538 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
540 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
542 items.push_back (SeparatorElem());
545 detach_menu (*_size_menu);
548 items.push_back (MenuElem (_("Height"), *_size_menu));
550 items.push_back (SeparatorElem());
552 if (!Profile->get_sae()) {
553 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
554 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
555 items.push_back (SeparatorElem());
558 // Hook for derived classes to add type specific stuff
559 append_extra_display_menu_items ();
563 Menu* layers_menu = manage (new Menu);
564 MenuList &layers_items = layers_menu->items();
565 layers_menu->set_name("ArdourContextMenu");
567 RadioMenuItem::Group layers_group;
569 /* Find out how many overlaid/stacked tracks we have in the selection */
573 TrackSelection const & s = _editor.get_selection().tracks;
574 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
575 StreamView* v = (*i)->view ();
580 switch (v->layer_display ()) {
591 /* We're not connecting to signal_toggled() here; in the case where these two items are
592 set to be in the `inconsistent' state, it seems that one or other will end up active
593 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
594 select the active one, no toggled signal is emitted so nothing happens.
597 _ignore_set_layer_display = true;
599 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
600 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
601 i->set_active (overlaid != 0 && stacked == 0);
602 i->set_inconsistent (overlaid != 0 && stacked != 0);
603 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
605 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
606 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
607 i->set_active (overlaid == 0 && stacked != 0);
608 i->set_inconsistent (overlaid != 0 && stacked != 0);
609 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
611 _ignore_set_layer_display = false;
613 items.push_back (MenuElem (_("Layers"), *layers_menu));
615 if (!Profile->get_sae()) {
617 Menu* alignment_menu = manage (new Menu);
618 MenuList& alignment_items = alignment_menu->items();
619 alignment_menu->set_name ("ArdourContextMenu");
621 RadioMenuItem::Group align_group;
623 /* Same verbose hacks as for the layering options above */
629 boost::shared_ptr<Track> first_track;
631 TrackSelection const & s = _editor.get_selection().tracks;
632 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
633 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
634 if (!r || !r->is_track ()) {
639 first_track = r->track();
642 switch (r->track()->alignment_choice()) {
646 switch (r->track()->alignment_style()) {
647 case ExistingMaterial:
655 case UseExistingMaterial:
671 inconsistent = false;
680 if (!inconsistent && first_track) {
682 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
683 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
684 i->set_active (automatic != 0 && existing == 0 && capture == 0);
685 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
687 switch (first_track->alignment_choice()) {
689 switch (first_track->alignment_style()) {
690 case ExistingMaterial:
691 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
694 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
702 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
703 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
704 i->set_active (existing != 0 && capture == 0 && automatic == 0);
705 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
707 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
708 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
709 i->set_active (existing == 0 && capture != 0 && automatic == 0);
710 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
712 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
718 Menu* mode_menu = manage (new Menu);
719 MenuList& mode_items = mode_menu->items ();
720 mode_menu->set_name ("ArdourContextMenu");
722 RadioMenuItem::Group mode_group;
728 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
729 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
730 if (!r || !r->is_track ()) {
734 switch (r->track()->mode()) {
747 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
748 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
749 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
750 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
751 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
753 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
754 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
755 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
756 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
757 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
759 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
760 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
761 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
762 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
763 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
765 items.push_back (MenuElem (_("Mode"), *mode_menu));
769 items.push_back (SeparatorElem());
771 build_playlist_menu ();
772 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
773 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
776 route_group_menu->detach ();
779 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
780 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
782 r.push_back (rtv->route ());
787 r.push_back (route ());
790 route_group_menu->build (r);
791 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
793 build_automation_action_menu (true);
794 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
796 items.push_back (SeparatorElem());
800 TrackSelection const & s = _editor.get_selection().tracks;
801 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
802 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
807 if (r->route()->active()) {
814 items.push_back (CheckMenuElem (_("Active")));
815 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
816 bool click_sets_active = true;
817 if (active > 0 && inactive == 0) {
818 i->set_active (true);
819 click_sets_active = false;
820 } else if (active > 0 && inactive > 0) {
821 i->set_inconsistent (true);
823 i->set_sensitive(! _session->transport_rolling());
824 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
826 items.push_back (SeparatorElem());
827 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
828 if (!Profile->get_sae()) {
829 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
831 items.push_front (SeparatorElem());
832 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
837 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
839 if (apply_to_selection) {
840 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
843 bool needs_bounce = false;
845 if (!track()->can_use_mode (mode, needs_bounce)) {
851 cerr << "would bounce this one\n";
856 track()->set_mode (mode);
861 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
863 TimeAxisView::show_timestretch (start, end, layers, layer);
873 /* check that the time selection was made in our route, or our route group.
874 remember that route_group() == 0 implies the route is *not* in a edit group.
877 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
878 /* this doesn't apply to us */
882 /* ignore it if our edit group is not active */
884 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
889 if (timestretch_rect == 0) {
890 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
891 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
892 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
895 timestretch_rect->show ();
896 timestretch_rect->raise_to_top ();
898 double const x1 = start / _editor.get_current_zoom();
899 double const x2 = (end - 1) / _editor.get_current_zoom();
901 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
902 x2, current_height() * (layers - layer) / layers));
906 RouteTimeAxisView::hide_timestretch ()
908 TimeAxisView::hide_timestretch ();
910 if (timestretch_rect) {
911 timestretch_rect->hide ();
916 RouteTimeAxisView::show_selection (TimeSelection& ts)
920 /* ignore it if our edit group is not active or if the selection was started
921 in some other track or route group (remember that route_group() == 0 means
922 that the track is not in an route group).
925 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
926 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
932 TimeAxisView::show_selection (ts);
936 RouteTimeAxisView::set_height (uint32_t h)
939 bool height_changed = (height == 0) || (h != height);
942 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
945 gm.get_level_meter().setup_meters (gmlen, meter_width);
947 TimeAxisView::set_height (h);
950 _view->set_height ((double) current_height());
953 if (height >= preset_height (HeightNormal)) {
957 gm.get_gain_slider().show();
959 if (!_route || _route->is_monitor()) {
964 if (rec_enable_button)
965 rec_enable_button->show();
967 route_group_button.show();
968 automation_button.show();
970 if (is_track() && track()->mode() == ARDOUR::Normal) {
971 playlist_button.show();
978 gm.get_gain_slider().hide();
980 if (!_route || _route->is_monitor()) {
985 if (rec_enable_button)
986 rec_enable_button->show();
988 route_group_button.hide ();
989 automation_button.hide ();
991 if (is_track() && track()->mode() == ARDOUR::Normal) {
992 playlist_button.hide ();
997 if (height_changed && !no_redraw) {
998 /* only emit the signal if the height really changed */
1004 RouteTimeAxisView::route_color_changed ()
1007 _view->apply_color (color(), StreamView::RegionColor);
1010 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1014 RouteTimeAxisView::reset_samples_per_pixel ()
1016 set_samples_per_pixel (_editor.get_current_zoom());
1020 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1025 speed = track()->speed();
1029 _view->set_samples_per_pixel (fpp * speed);
1032 TimeAxisView::set_samples_per_pixel (fpp * speed);
1036 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1038 if (!mitem->get_active()) {
1039 /* this is one of the two calls made when these radio menu items change status. this one
1040 is for the item that became inactive, and we want to ignore it.
1045 if (apply_to_selection) {
1046 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1049 track()->set_align_choice (choice);
1055 RouteTimeAxisView::rename_current_playlist ()
1057 ArdourPrompter prompter (true);
1060 boost::shared_ptr<Track> tr = track();
1061 if (!tr || tr->destructive()) {
1065 boost::shared_ptr<Playlist> pl = tr->playlist();
1070 prompter.set_title (_("Rename Playlist"));
1071 prompter.set_prompt (_("New name for playlist:"));
1072 prompter.set_initial_text (pl->name());
1073 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1074 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1076 switch (prompter.run ()) {
1077 case Gtk::RESPONSE_ACCEPT:
1078 prompter.get_result (name);
1079 if (name.length()) {
1080 pl->set_name (name);
1090 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1092 std::string ret (basename);
1094 std::string const group_string = "." + route_group()->name() + ".";
1096 // iterate through all playlists
1098 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1099 std::string tmp = (*i)->name();
1101 std::string::size_type idx = tmp.find(group_string);
1102 // find those which belong to this group
1103 if (idx != string::npos) {
1104 tmp = tmp.substr(idx + group_string.length());
1106 // and find the largest current number
1108 if (x > maxnumber) {
1117 snprintf (buf, sizeof(buf), "%d", maxnumber);
1119 ret = this->name() + "." + route_group()->name () + "." + buf;
1125 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1129 boost::shared_ptr<Track> tr = track ();
1130 if (!tr || tr->destructive()) {
1134 boost::shared_ptr<const Playlist> pl = tr->playlist();
1141 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1142 name = resolve_new_group_playlist_name(name, playlists_before_op);
1145 while (_session->playlists->by_name(name)) {
1146 name = Playlist::bump_name (name, *_session);
1149 // TODO: The prompter "new" button should be de-activated if the user
1150 // specifies a playlist name which already exists in the session.
1154 ArdourPrompter prompter (true);
1156 prompter.set_title (_("New Copy Playlist"));
1157 prompter.set_prompt (_("Name for new playlist:"));
1158 prompter.set_initial_text (name);
1159 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1160 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1161 prompter.show_all ();
1163 switch (prompter.run ()) {
1164 case Gtk::RESPONSE_ACCEPT:
1165 prompter.get_result (name);
1173 if (name.length()) {
1174 tr->use_copy_playlist ();
1175 tr->playlist()->set_name (name);
1180 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1184 boost::shared_ptr<Track> tr = track ();
1185 if (!tr || tr->destructive()) {
1189 boost::shared_ptr<const Playlist> pl = tr->playlist();
1196 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1197 name = resolve_new_group_playlist_name(name,playlists_before_op);
1200 while (_session->playlists->by_name(name)) {
1201 name = Playlist::bump_name (name, *_session);
1207 ArdourPrompter prompter (true);
1209 prompter.set_title (_("New Playlist"));
1210 prompter.set_prompt (_("Name for new playlist:"));
1211 prompter.set_initial_text (name);
1212 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1213 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1215 switch (prompter.run ()) {
1216 case Gtk::RESPONSE_ACCEPT:
1217 prompter.get_result (name);
1225 if (name.length()) {
1226 tr->use_new_playlist ();
1227 tr->playlist()->set_name (name);
1232 RouteTimeAxisView::clear_playlist ()
1234 boost::shared_ptr<Track> tr = track ();
1235 if (!tr || tr->destructive()) {
1239 boost::shared_ptr<Playlist> pl = tr->playlist();
1244 _editor.clear_playlist (pl);
1248 RouteTimeAxisView::speed_changed ()
1250 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1254 RouteTimeAxisView::update_diskstream_display ()
1264 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1266 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1268 /* special case: select/deselect all tracks */
1269 if (_editor.get_selection().selected (this)) {
1270 _editor.get_selection().clear_tracks ();
1272 _editor.select_all_tracks ();
1278 switch (ArdourKeyboard::selection_type (ev->state)) {
1279 case Selection::Toggle:
1280 _editor.get_selection().toggle (this);
1283 case Selection::Set:
1284 _editor.get_selection().set (this);
1287 case Selection::Extend:
1288 _editor.extend_selection_to_track (*this);
1291 case Selection::Add:
1292 _editor.get_selection().add (this);
1298 RouteTimeAxisView::set_selected_points (PointSelection& points)
1300 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1301 (*i)->set_selected_points (points);
1306 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1309 _view->set_selected_regionviews (regions);
1313 /** Add the selectable things that we have to a list.
1314 * @param results List to add things to.
1317 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1322 speed = track()->speed();
1325 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1326 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1328 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1329 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1332 /* pick up visible automation tracks */
1334 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1335 if (!(*i)->hidden()) {
1336 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1342 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1345 _view->get_inverted_selectables (sel, results);
1348 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1349 if (!(*i)->hidden()) {
1350 (*i)->get_inverted_selectables (sel, results);
1358 RouteTimeAxisView::route_group () const
1360 return _route->route_group();
1364 RouteTimeAxisView::name() const
1366 return _route->name();
1369 boost::shared_ptr<Playlist>
1370 RouteTimeAxisView::playlist () const
1372 boost::shared_ptr<Track> tr;
1374 if ((tr = track()) != 0) {
1375 return tr->playlist();
1377 return boost::shared_ptr<Playlist> ();
1382 RouteTimeAxisView::name_entry_changed ()
1384 TimeAxisView::name_entry_changed ();
1386 string x = name_entry->get_text ();
1388 if (x == _route->name()) {
1392 strip_whitespace_edges (x);
1394 if (x.length() == 0) {
1395 name_entry->set_text (_route->name());
1399 if (_session->route_name_internal (x)) {
1400 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1402 name_entry->grab_focus ();
1403 } else if (RouteUI::verify_new_route_name (x)) {
1404 _route->set_name (x);
1406 name_entry->grab_focus ();
1410 boost::shared_ptr<Region>
1411 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1413 boost::shared_ptr<Playlist> pl = playlist ();
1416 return pl->find_next_region (pos, point, dir);
1419 return boost::shared_ptr<Region> ();
1423 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1425 boost::shared_ptr<Playlist> pl = playlist ();
1428 return pl->find_next_region_boundary (pos, dir);
1435 RouteTimeAxisView::fade_range (TimeSelection& selection)
1437 boost::shared_ptr<Playlist> what_we_got;
1438 boost::shared_ptr<Track> tr = track ();
1439 boost::shared_ptr<Playlist> playlist;
1442 /* route is a bus, not a track */
1446 playlist = tr->playlist();
1448 TimeSelection time (selection);
1449 float const speed = tr->speed();
1450 if (speed != 1.0f) {
1451 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1452 (*i).start = session_frame_to_track_frame((*i).start, speed);
1453 (*i).end = session_frame_to_track_frame((*i).end, speed);
1457 playlist->clear_changes ();
1458 playlist->clear_owned_changes ();
1460 playlist->fade_range (time);
1462 vector<Command*> cmds;
1463 playlist->rdiff (cmds);
1464 _session->add_commands (cmds);
1465 _session->add_command (new StatefulDiffCommand (playlist));
1470 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1472 boost::shared_ptr<Playlist> what_we_got;
1473 boost::shared_ptr<Track> tr = track ();
1474 boost::shared_ptr<Playlist> playlist;
1477 /* route is a bus, not a track */
1481 playlist = tr->playlist();
1483 TimeSelection time (selection.time);
1484 float const speed = tr->speed();
1485 if (speed != 1.0f) {
1486 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1487 (*i).start = session_frame_to_track_frame((*i).start, speed);
1488 (*i).end = session_frame_to_track_frame((*i).end, speed);
1492 playlist->clear_changes ();
1493 playlist->clear_owned_changes ();
1497 if (playlist->cut (time) != 0) {
1498 if (Config->get_edit_mode() == Ripple)
1499 playlist->ripple(time.start(), -time.length(), NULL);
1500 // no need to exclude any regions from rippling here
1502 vector<Command*> cmds;
1503 playlist->rdiff (cmds);
1504 _session->add_commands (cmds);
1506 _session->add_command (new StatefulDiffCommand (playlist));
1511 if ((what_we_got = playlist->cut (time)) != 0) {
1512 _editor.get_cut_buffer().add (what_we_got);
1513 if (Config->get_edit_mode() == Ripple)
1514 playlist->ripple(time.start(), -time.length(), NULL);
1515 // no need to exclude any regions from rippling here
1517 vector<Command*> cmds;
1518 playlist->rdiff (cmds);
1519 _session->add_commands (cmds);
1521 _session->add_command (new StatefulDiffCommand (playlist));
1525 if ((what_we_got = playlist->copy (time)) != 0) {
1526 _editor.get_cut_buffer().add (what_we_got);
1531 if ((what_we_got = playlist->cut (time)) != 0) {
1532 if (Config->get_edit_mode() == Ripple)
1533 playlist->ripple(time.start(), -time.length(), NULL);
1534 // no need to exclude any regions from rippling here
1536 vector<Command*> cmds;
1537 playlist->rdiff (cmds);
1538 _session->add_commands (cmds);
1539 _session->add_command (new StatefulDiffCommand (playlist));
1540 what_we_got->release ();
1547 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1553 boost::shared_ptr<Playlist> pl = playlist ();
1554 PlaylistSelection::iterator p;
1556 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1558 if (p == selection.playlists.end()) {
1562 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1564 if (track()->speed() != 1.0f) {
1565 pos = session_frame_to_track_frame (pos, track()->speed());
1566 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1569 pl->clear_changes ();
1570 if (Config->get_edit_mode() == Ripple) {
1571 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1572 framecnt_t amount = extent.second - extent.first;
1573 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1575 pl->paste (*p, pos, times);
1577 vector<Command*> cmds;
1579 _session->add_commands (cmds);
1581 _session->add_command (new StatefulDiffCommand (pl));
1587 struct PlaylistSorter {
1588 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1589 return a->sort_id() < b->sort_id();
1594 RouteTimeAxisView::build_playlist_menu ()
1596 using namespace Menu_Helpers;
1602 delete playlist_action_menu;
1603 playlist_action_menu = new Menu;
1604 playlist_action_menu->set_name ("ArdourContextMenu");
1606 MenuList& playlist_items = playlist_action_menu->items();
1607 playlist_action_menu->set_name ("ArdourContextMenu");
1608 playlist_items.clear();
1610 RadioMenuItem::Group playlist_group;
1611 boost::shared_ptr<Track> tr = track ();
1613 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1615 /* sort the playlists */
1617 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1619 /* add the playlists to the menu */
1620 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1621 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1622 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1623 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1625 if (tr->playlist()->id() == (*i)->id()) {
1631 playlist_items.push_back (SeparatorElem());
1632 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1633 playlist_items.push_back (SeparatorElem());
1635 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1636 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1637 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1640 // Use a label which tells the user what is happening
1641 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1642 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1646 playlist_items.push_back (SeparatorElem());
1647 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1648 playlist_items.push_back (SeparatorElem());
1650 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1654 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1656 assert (is_track());
1658 // exit if we were triggered by deactivating the old playlist
1659 if (!item->get_active()) {
1663 boost::shared_ptr<Playlist> pl (wpl.lock());
1669 if (track()->playlist() == pl) {
1670 // exit when use_playlist is called by the creation of the playlist menu
1671 // or the playlist choice is unchanged
1675 track()->use_playlist (pl);
1677 RouteGroup* rg = route_group();
1679 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1680 std::string group_string = "." + rg->name() + ".";
1682 std::string take_name = pl->name();
1683 std::string::size_type idx = take_name.find(group_string);
1685 if (idx == std::string::npos)
1688 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1690 boost::shared_ptr<RouteList> rl (rg->route_list());
1692 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1693 if ((*i) == this->route()) {
1697 std::string playlist_name = (*i)->name()+group_string+take_name;
1699 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1704 if (track->freeze_state() == Track::Frozen) {
1705 /* Don't change playlists of frozen tracks */
1709 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1711 // No playlist for this track for this take yet, make it
1712 track->use_new_playlist();
1713 track->playlist()->set_name(playlist_name);
1715 track->use_playlist(ipl);
1722 RouteTimeAxisView::update_playlist_tip ()
1724 RouteGroup* rg = route_group ();
1725 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1726 string group_string = "." + rg->name() + ".";
1728 string take_name = track()->playlist()->name();
1729 string::size_type idx = take_name.find(group_string);
1731 if (idx != string::npos) {
1732 /* find the bit containing the take number / name */
1733 take_name = take_name.substr (idx + group_string.length());
1735 /* set the playlist button tooltip to the take name */
1736 ARDOUR_UI::instance()->set_tip (
1738 string_compose(_("Take: %1.%2"),
1739 Glib::Markup::escape_text(rg->name()),
1740 Glib::Markup::escape_text(take_name))
1747 /* set the playlist button tooltip to the playlist name */
1748 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1753 RouteTimeAxisView::show_playlist_selector ()
1755 _editor.playlist_selector().show_for (this);
1759 RouteTimeAxisView::map_frozen ()
1765 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1767 switch (track()->freeze_state()) {
1769 playlist_button.set_sensitive (false);
1770 rec_enable_button->set_sensitive (false);
1773 playlist_button.set_sensitive (true);
1774 rec_enable_button->set_sensitive (true);
1780 RouteTimeAxisView::color_handler ()
1782 //case cTimeStretchOutline:
1783 if (timestretch_rect) {
1784 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1786 //case cTimeStretchFill:
1787 if (timestretch_rect) {
1788 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1794 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1795 * Will add track if necessary.
1798 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1800 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1801 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1804 /* it doesn't exist yet, so we don't care about the button state: just add it */
1805 create_automation_child (param, true);
1808 bool yn = menu->get_active();
1809 bool changed = false;
1811 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1813 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1814 will have done that for us.
1817 if (changed && !no_redraw) {
1825 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1827 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1833 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1835 if (menu && !_hidden) {
1836 ignore_toggle = true;
1837 menu->set_active (false);
1838 ignore_toggle = false;
1841 if (_route && !no_redraw) {
1848 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1850 if (apply_to_selection) {
1851 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1855 /* Show our automation */
1857 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1858 i->second->set_marked_for_display (true);
1860 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1863 menu->set_active(true);
1868 /* Show processor automation */
1870 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1871 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1872 if ((*ii)->view == 0) {
1873 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1876 (*ii)->menu_item->set_active (true);
1889 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1891 if (apply_to_selection) {
1892 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1896 /* Show our automation */
1898 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1899 if (i->second->has_automation()) {
1900 i->second->set_marked_for_display (true);
1902 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1904 menu->set_active(true);
1909 /* Show processor automation */
1911 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1912 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1913 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1914 (*ii)->menu_item->set_active (true);
1926 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1928 if (apply_to_selection) {
1929 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1933 /* Hide our automation */
1935 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1936 i->second->set_marked_for_display (false);
1938 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1941 menu->set_active (false);
1945 /* Hide processor automation */
1947 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1948 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1949 (*ii)->menu_item->set_active (false);
1960 RouteTimeAxisView::region_view_added (RegionView* rv)
1962 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1963 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1964 boost::shared_ptr<AutomationTimeAxisView> atv;
1966 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1971 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1972 (*i)->add_ghost(rv);
1976 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1978 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1984 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1986 parent.remove_processor_automation_node (this);
1990 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1993 remove_child (pan->view);
1997 RouteTimeAxisView::ProcessorAutomationNode*
1998 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2000 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2002 if ((*i)->processor == processor) {
2004 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2005 if ((*ii)->what == what) {
2015 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2017 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2020 ProcessorAutomationNode* pan;
2022 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2023 /* session state may never have been saved with new plugin */
2024 error << _("programming error: ")
2025 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2026 processor->name(), what.type(), (int) what.channel(), what.id() )
2036 boost::shared_ptr<AutomationControl> control
2037 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2039 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2040 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2041 _editor, *this, false, parent_canvas,
2042 processor->describe_parameter (what), processor->name()));
2044 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2046 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2049 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2054 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2057 pan->menu_item->set_active (false);
2066 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2068 boost::shared_ptr<Processor> processor (p.lock ());
2070 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2071 /* The Amp processor is a special case and is dealt with separately */
2075 set<Evoral::Parameter> existing;
2077 processor->what_has_data (existing);
2079 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2081 Evoral::Parameter param (*i);
2082 boost::shared_ptr<AutomationLine> al;
2084 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2087 add_processor_automation_curve (processor, param);
2093 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2095 using namespace Menu_Helpers;
2099 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2101 _automation_tracks[param] = track;
2103 /* existing state overrides "show" argument */
2104 string s = track->gui_property ("visible");
2106 show = string_is_affirmative (s);
2109 /* this might or might not change the visibility status, so don't rely on it */
2110 track->set_marked_for_display (show);
2112 if (show && !no_redraw) {
2116 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2117 /* MIDI-related parameters are always in the menu, there's no
2118 reason to rebuild the menu just because we added a automation
2119 lane for one of them. But if we add a non-MIDI automation
2120 lane, then we need to invalidate the display menu.
2122 delete display_menu;
2128 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2130 boost::shared_ptr<Processor> processor (p.lock ());
2132 if (!processor || !processor->display_to_user ()) {
2136 /* we use this override to veto the Amp processor from the plugin menu,
2137 as its automation lane can be accessed using the special "Fader" menu
2141 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2145 using namespace Menu_Helpers;
2146 ProcessorAutomationInfo *rai;
2147 list<ProcessorAutomationInfo*>::iterator x;
2149 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2151 if (automatable.empty()) {
2155 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2156 if ((*x)->processor == processor) {
2161 if (x == processor_automation.end()) {
2163 rai = new ProcessorAutomationInfo (processor);
2164 processor_automation.push_back (rai);
2172 /* any older menu was deleted at the top of processors_changed()
2173 when we cleared the subplugin menu.
2176 rai->menu = manage (new Menu);
2177 MenuList& items = rai->menu->items();
2178 rai->menu->set_name ("ArdourContextMenu");
2182 std::set<Evoral::Parameter> has_visible_automation;
2183 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2185 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2187 ProcessorAutomationNode* pan;
2188 Gtk::CheckMenuItem* mitem;
2190 string name = processor->describe_parameter (*i);
2192 items.push_back (CheckMenuElem (name));
2193 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2195 _subplugin_menu_map[*i] = mitem;
2197 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2198 mitem->set_active(true);
2201 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2205 pan = new ProcessorAutomationNode (*i, mitem, *this);
2207 rai->lines.push_back (pan);
2211 pan->menu_item = mitem;
2215 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2218 /* add the menu for this processor, because the subplugin
2219 menu is always cleared at the top of processors_changed().
2220 this is the result of some poor design in gtkmm and/or
2224 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2229 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2230 RouteTimeAxisView::ProcessorAutomationNode* pan)
2232 bool showit = pan->menu_item->get_active();
2233 bool redraw = false;
2235 if (pan->view == 0 && showit) {
2236 add_processor_automation_curve (rai->processor, pan->what);
2240 if (pan->view && pan->view->set_marked_for_display (showit)) {
2244 if (redraw && !no_redraw) {
2250 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2252 if (c.type == RouteProcessorChange::MeterPointChange) {
2253 /* nothing to do if only the meter point has changed */
2257 using namespace Menu_Helpers;
2259 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2260 (*i)->valid = false;
2263 setup_processor_menu_and_curves ();
2265 bool deleted_processor_automation = false;
2267 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2269 list<ProcessorAutomationInfo*>::iterator tmp;
2277 processor_automation.erase (i);
2278 deleted_processor_automation = true;
2285 if (deleted_processor_automation && !no_redraw) {
2290 boost::shared_ptr<AutomationLine>
2291 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2293 ProcessorAutomationNode* pan;
2295 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2301 return boost::shared_ptr<AutomationLine>();
2305 RouteTimeAxisView::reset_processor_automation_curves ()
2307 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2313 RouteTimeAxisView::can_edit_name () const
2315 /* we do not allow track name changes if it is record enabled
2317 return !_route->record_enabled();
2321 RouteTimeAxisView::blink_rec_display (bool onoff)
2323 RouteUI::blink_rec_display (onoff);
2327 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2329 if (_ignore_set_layer_display) {
2333 if (apply_to_selection) {
2334 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2338 _view->set_layer_display (d);
2341 set_gui_property (X_("layer-display"), enum_2_string (d));
2346 RouteTimeAxisView::layer_display () const
2349 return _view->layer_display ();
2352 /* we don't know, since we don't have a _view, so just return something */
2358 boost::shared_ptr<AutomationTimeAxisView>
2359 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2361 AutomationTracks::iterator i = _automation_tracks.find(param);
2362 if (i != _automation_tracks.end()) {
2365 return boost::shared_ptr<AutomationTimeAxisView>();
2370 RouteTimeAxisView::fast_update ()
2372 gm.get_level_meter().update_meters ();
2376 RouteTimeAxisView::hide_meter ()
2379 gm.get_level_meter().hide_meters ();
2383 RouteTimeAxisView::show_meter ()
2389 RouteTimeAxisView::reset_meter ()
2391 if (Config->get_show_track_meters()) {
2392 int meter_width = 3;
2393 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2396 gm.get_level_meter().setup_meters (height - 9, meter_width);
2403 RouteTimeAxisView::clear_meter ()
2405 gm.get_level_meter().clear_meters ();
2409 RouteTimeAxisView::meter_changed ()
2411 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2413 if (_route && !no_redraw) {
2416 // reset peak when meter point changes
2417 gm.reset_peak_display();
2421 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2424 if (_route && !no_redraw) {
2430 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2432 using namespace Menu_Helpers;
2434 if (!_underlay_streams.empty()) {
2435 MenuList& parent_items = parent_menu->items();
2436 Menu* gs_menu = manage (new Menu);
2437 gs_menu->set_name ("ArdourContextMenu");
2438 MenuList& gs_items = gs_menu->items();
2440 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2442 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2443 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2444 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2450 RouteTimeAxisView::set_underlay_state()
2452 if (!underlay_xml_node) {
2456 XMLNodeList nlist = underlay_xml_node->children();
2457 XMLNodeConstIterator niter;
2458 XMLNode *child_node;
2460 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2461 child_node = *niter;
2463 if (child_node->name() != "Underlay") {
2467 XMLProperty* prop = child_node->property ("id");
2469 PBD::ID id (prop->value());
2471 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2474 add_underlay(v->view(), false);
2483 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2489 RouteTimeAxisView& other = v->trackview();
2491 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2492 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2493 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2497 _underlay_streams.push_back(v);
2498 other._underlay_mirrors.push_back(this);
2500 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2502 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2504 if (!underlay_xml_node) {
2505 underlay_xml_node = xml_node->add_child("Underlays");
2508 XMLNode* node = underlay_xml_node->add_child("Underlay");
2509 XMLProperty* prop = node->add_property("id");
2510 prop->set_value(v->trackview().route()->id().to_s());
2517 RouteTimeAxisView::remove_underlay (StreamView* v)
2523 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2524 RouteTimeAxisView& other = v->trackview();
2526 if (it != _underlay_streams.end()) {
2527 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2529 if (gm == other._underlay_mirrors.end()) {
2530 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2534 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2536 _underlay_streams.erase(it);
2537 other._underlay_mirrors.erase(gm);
2539 if (underlay_xml_node) {
2540 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2546 RouteTimeAxisView::set_button_names ()
2548 if (_route && _route->solo_safe()) {
2549 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2551 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2553 if (Config->get_solo_control_is_listen_control()) {
2554 switch (Config->get_listen_position()) {
2555 case AfterFaderListen:
2556 solo_button->set_text (_("A"));
2557 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2559 case PreFaderListen:
2560 solo_button->set_text (_("P"));
2561 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2565 solo_button->set_text (_("S"));
2566 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2568 mute_button->set_text (_("M"));
2572 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2574 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2575 if (i != _main_automation_menu_map.end()) {
2579 i = _subplugin_menu_map.find (param);
2580 if (i != _subplugin_menu_map.end()) {
2588 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2590 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2592 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2596 gain_track.reset (new AutomationTimeAxisView (_session,
2597 _route, _route->amp(), c, param,
2602 _route->amp()->describe_parameter(param)));
2605 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2608 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2612 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2614 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2616 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2620 mute_track.reset (new AutomationTimeAxisView (_session,
2621 _route, _route, c, param,
2626 _route->describe_parameter(param)));
2629 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2632 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2636 void add_region_to_list (RegionView* rv, RegionList* l)
2638 l->push_back (rv->region());
2642 RouteTimeAxisView::combine_regions ()
2644 /* as of may 2011, we do not offer uncombine for MIDI tracks
2647 if (!is_audio_track()) {
2655 RegionList selected_regions;
2656 boost::shared_ptr<Playlist> playlist = track()->playlist();
2658 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2660 if (selected_regions.size() < 2) {
2664 playlist->clear_changes ();
2665 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2667 _session->add_command (new StatefulDiffCommand (playlist));
2668 /* make the new region be selected */
2670 return _view->find_view (compound_region);
2674 RouteTimeAxisView::uncombine_regions ()
2676 /* as of may 2011, we do not offer uncombine for MIDI tracks
2678 if (!is_audio_track()) {
2686 RegionList selected_regions;
2687 boost::shared_ptr<Playlist> playlist = track()->playlist();
2689 /* have to grab selected regions first because the uncombine is going
2690 * to change that in the middle of the list traverse
2693 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2695 playlist->clear_changes ();
2697 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2698 playlist->uncombine (*i);
2701 _session->add_command (new StatefulDiffCommand (playlist));
2705 RouteTimeAxisView::state_id() const
2707 return string_compose ("rtav %1", _route->id().to_s());
2712 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2714 TimeAxisView::remove_child (c);
2716 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2718 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2719 if (i->second == a) {
2720 _automation_tracks.erase (i);