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, 20)
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, 3, 4, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 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, 3, 4, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 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, 3, 4, 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, 3, 4, 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, 3, 4, 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, 3, 4, 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, 0, 0);
430 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
432 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
433 number_label.set_size_request(3 + tnw, -1);
434 number_label.show ();
436 number_label.hide ();
441 RouteTimeAxisView::parameter_changed (string const & p)
443 if (p == "track-name-number") {
444 update_track_number_visibility();
449 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
451 if (what_changed.contains (ARDOUR::Properties::name)) {
457 RouteTimeAxisView::take_name_changed (void *src)
465 RouteTimeAxisView::playlist_click ()
467 build_playlist_menu ();
468 conditionally_add_to_selection ();
469 playlist_action_menu->popup (1, gtk_get_current_event_time());
473 RouteTimeAxisView::automation_click ()
475 conditionally_add_to_selection ();
476 build_automation_action_menu (false);
477 automation_action_menu->popup (1, gtk_get_current_event_time());
481 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
483 using namespace Menu_Helpers;
485 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
486 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
489 detach_menu (subplugin_menu);
491 _main_automation_menu_map.clear ();
492 delete automation_action_menu;
493 automation_action_menu = new Menu;
495 MenuList& items = automation_action_menu->items();
497 automation_action_menu->set_name ("ArdourContextMenu");
499 items.push_back (MenuElem (_("Show All Automation"),
500 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
502 items.push_back (MenuElem (_("Show Existing Automation"),
503 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
505 items.push_back (MenuElem (_("Hide All Automation"),
506 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
508 /* Attach the plugin submenu. It may have previously been used elsewhere,
509 so it was detached above
512 if (!subplugin_menu.items().empty()) {
513 items.push_back (SeparatorElem ());
514 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
515 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
520 RouteTimeAxisView::build_display_menu ()
522 using namespace Menu_Helpers;
526 TimeAxisView::build_display_menu ();
528 /* now fill it with our stuff */
530 MenuList& items = display_menu->items();
531 display_menu->set_name ("ArdourContextMenu");
533 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
535 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
537 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
539 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
541 items.push_back (SeparatorElem());
544 detach_menu (*_size_menu);
547 items.push_back (MenuElem (_("Height"), *_size_menu));
549 items.push_back (SeparatorElem());
551 if (!Profile->get_sae()) {
552 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
553 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
554 items.push_back (SeparatorElem());
557 // Hook for derived classes to add type specific stuff
558 append_extra_display_menu_items ();
562 Menu* layers_menu = manage (new Menu);
563 MenuList &layers_items = layers_menu->items();
564 layers_menu->set_name("ArdourContextMenu");
566 RadioMenuItem::Group layers_group;
568 /* Find out how many overlaid/stacked tracks we have in the selection */
572 TrackSelection const & s = _editor.get_selection().tracks;
573 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
574 StreamView* v = (*i)->view ();
579 switch (v->layer_display ()) {
590 /* We're not connecting to signal_toggled() here; in the case where these two items are
591 set to be in the `inconsistent' state, it seems that one or other will end up active
592 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
593 select the active one, no toggled signal is emitted so nothing happens.
596 _ignore_set_layer_display = true;
598 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
599 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
600 i->set_active (overlaid != 0 && stacked == 0);
601 i->set_inconsistent (overlaid != 0 && stacked != 0);
602 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
604 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
605 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
606 i->set_active (overlaid == 0 && stacked != 0);
607 i->set_inconsistent (overlaid != 0 && stacked != 0);
608 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
610 _ignore_set_layer_display = false;
612 items.push_back (MenuElem (_("Layers"), *layers_menu));
614 if (!Profile->get_sae()) {
616 Menu* alignment_menu = manage (new Menu);
617 MenuList& alignment_items = alignment_menu->items();
618 alignment_menu->set_name ("ArdourContextMenu");
620 RadioMenuItem::Group align_group;
622 /* Same verbose hacks as for the layering options above */
628 boost::shared_ptr<Track> first_track;
630 TrackSelection const & s = _editor.get_selection().tracks;
631 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
632 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
633 if (!r || !r->is_track ()) {
638 first_track = r->track();
641 switch (r->track()->alignment_choice()) {
645 switch (r->track()->alignment_style()) {
646 case ExistingMaterial:
654 case UseExistingMaterial:
670 inconsistent = false;
679 if (!inconsistent && first_track) {
681 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
682 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
683 i->set_active (automatic != 0 && existing == 0 && capture == 0);
684 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
686 switch (first_track->alignment_choice()) {
688 switch (first_track->alignment_style()) {
689 case ExistingMaterial:
690 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
693 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
701 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
702 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
703 i->set_active (existing != 0 && capture == 0 && automatic == 0);
704 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
706 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
707 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
708 i->set_active (existing == 0 && capture != 0 && automatic == 0);
709 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
711 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
717 Menu* mode_menu = manage (new Menu);
718 MenuList& mode_items = mode_menu->items ();
719 mode_menu->set_name ("ArdourContextMenu");
721 RadioMenuItem::Group mode_group;
727 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
728 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
729 if (!r || !r->is_track ()) {
733 switch (r->track()->mode()) {
746 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
747 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
748 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
749 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
750 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
752 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
753 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
754 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
755 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
756 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
758 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
759 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
760 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
761 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
762 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
764 items.push_back (MenuElem (_("Mode"), *mode_menu));
768 items.push_back (SeparatorElem());
770 build_playlist_menu ();
771 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
772 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
775 route_group_menu->detach ();
778 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
779 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
781 r.push_back (rtv->route ());
786 r.push_back (route ());
789 route_group_menu->build (r);
790 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
792 build_automation_action_menu (true);
793 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
795 items.push_back (SeparatorElem());
799 TrackSelection const & s = _editor.get_selection().tracks;
800 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
801 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
806 if (r->route()->active()) {
813 items.push_back (CheckMenuElem (_("Active")));
814 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
815 bool click_sets_active = true;
816 if (active > 0 && inactive == 0) {
817 i->set_active (true);
818 click_sets_active = false;
819 } else if (active > 0 && inactive > 0) {
820 i->set_inconsistent (true);
822 i->set_sensitive(! _session->transport_rolling());
823 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
825 items.push_back (SeparatorElem());
826 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
827 if (!Profile->get_sae()) {
828 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
830 items.push_front (SeparatorElem());
831 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
836 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
838 if (apply_to_selection) {
839 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
842 bool needs_bounce = false;
844 if (!track()->can_use_mode (mode, needs_bounce)) {
850 cerr << "would bounce this one\n";
855 track()->set_mode (mode);
857 rec_enable_button->remove ();
860 case ARDOUR::NonLayered:
862 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
863 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
865 case ARDOUR::Destructive:
866 rec_enable_button->set_text (string());
867 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
871 rec_enable_button->show_all ();
876 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
878 TimeAxisView::show_timestretch (start, end, layers, layer);
888 /* check that the time selection was made in our route, or our route group.
889 remember that route_group() == 0 implies the route is *not* in a edit group.
892 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
893 /* this doesn't apply to us */
897 /* ignore it if our edit group is not active */
899 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
904 if (timestretch_rect == 0) {
905 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
906 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
907 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
910 timestretch_rect->show ();
911 timestretch_rect->raise_to_top ();
913 double const x1 = start / _editor.get_current_zoom();
914 double const x2 = (end - 1) / _editor.get_current_zoom();
916 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
917 x2, current_height() * (layers - layer) / layers));
921 RouteTimeAxisView::hide_timestretch ()
923 TimeAxisView::hide_timestretch ();
925 if (timestretch_rect) {
926 timestretch_rect->hide ();
931 RouteTimeAxisView::show_selection (TimeSelection& ts)
935 /* ignore it if our edit group is not active or if the selection was started
936 in some other track or route group (remember that route_group() == 0 means
937 that the track is not in an route group).
940 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
941 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
947 TimeAxisView::show_selection (ts);
951 RouteTimeAxisView::set_height (uint32_t h)
954 bool height_changed = (height == 0) || (h != height);
957 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
960 gm.get_level_meter().setup_meters (gmlen, meter_width);
962 TimeAxisView::set_height (h);
965 _view->set_height ((double) current_height());
968 if (height >= preset_height (HeightNormal)) {
972 gm.get_gain_slider().show();
974 if (!_route || _route->is_monitor()) {
979 if (rec_enable_button)
980 rec_enable_button->show();
982 route_group_button.show();
983 automation_button.show();
985 if (is_track() && track()->mode() == ARDOUR::Normal) {
986 playlist_button.show();
993 gm.get_gain_slider().hide();
995 if (!_route || _route->is_monitor()) {
1000 if (rec_enable_button)
1001 rec_enable_button->show();
1003 route_group_button.hide ();
1004 automation_button.hide ();
1006 if (is_track() && track()->mode() == ARDOUR::Normal) {
1007 playlist_button.hide ();
1012 if (height_changed && !no_redraw) {
1013 /* only emit the signal if the height really changed */
1019 RouteTimeAxisView::route_color_changed ()
1022 _view->apply_color (color(), StreamView::RegionColor);
1025 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1029 RouteTimeAxisView::reset_samples_per_pixel ()
1031 set_samples_per_pixel (_editor.get_current_zoom());
1035 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1040 speed = track()->speed();
1044 _view->set_samples_per_pixel (fpp * speed);
1047 TimeAxisView::set_samples_per_pixel (fpp * speed);
1051 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1053 if (!mitem->get_active()) {
1054 /* this is one of the two calls made when these radio menu items change status. this one
1055 is for the item that became inactive, and we want to ignore it.
1060 if (apply_to_selection) {
1061 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1064 track()->set_align_choice (choice);
1070 RouteTimeAxisView::rename_current_playlist ()
1072 ArdourPrompter prompter (true);
1075 boost::shared_ptr<Track> tr = track();
1076 if (!tr || tr->destructive()) {
1080 boost::shared_ptr<Playlist> pl = tr->playlist();
1085 prompter.set_title (_("Rename Playlist"));
1086 prompter.set_prompt (_("New name for playlist:"));
1087 prompter.set_initial_text (pl->name());
1088 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1089 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1091 switch (prompter.run ()) {
1092 case Gtk::RESPONSE_ACCEPT:
1093 prompter.get_result (name);
1094 if (name.length()) {
1095 pl->set_name (name);
1105 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1107 std::string ret (basename);
1109 std::string const group_string = "." + route_group()->name() + ".";
1111 // iterate through all playlists
1113 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1114 std::string tmp = (*i)->name();
1116 std::string::size_type idx = tmp.find(group_string);
1117 // find those which belong to this group
1118 if (idx != string::npos) {
1119 tmp = tmp.substr(idx + group_string.length());
1121 // and find the largest current number
1123 if (x > maxnumber) {
1132 snprintf (buf, sizeof(buf), "%d", maxnumber);
1134 ret = this->name() + "." + route_group()->name () + "." + buf;
1140 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1144 boost::shared_ptr<Track> tr = track ();
1145 if (!tr || tr->destructive()) {
1149 boost::shared_ptr<const Playlist> pl = tr->playlist();
1156 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1157 name = resolve_new_group_playlist_name(name, playlists_before_op);
1160 while (_session->playlists->by_name(name)) {
1161 name = Playlist::bump_name (name, *_session);
1164 // TODO: The prompter "new" button should be de-activated if the user
1165 // specifies a playlist name which already exists in the session.
1169 ArdourPrompter prompter (true);
1171 prompter.set_title (_("New Copy Playlist"));
1172 prompter.set_prompt (_("Name for new playlist:"));
1173 prompter.set_initial_text (name);
1174 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1175 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1176 prompter.show_all ();
1178 switch (prompter.run ()) {
1179 case Gtk::RESPONSE_ACCEPT:
1180 prompter.get_result (name);
1188 if (name.length()) {
1189 tr->use_copy_playlist ();
1190 tr->playlist()->set_name (name);
1195 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1199 boost::shared_ptr<Track> tr = track ();
1200 if (!tr || tr->destructive()) {
1204 boost::shared_ptr<const Playlist> pl = tr->playlist();
1211 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1212 name = resolve_new_group_playlist_name(name,playlists_before_op);
1215 while (_session->playlists->by_name(name)) {
1216 name = Playlist::bump_name (name, *_session);
1222 ArdourPrompter prompter (true);
1224 prompter.set_title (_("New Playlist"));
1225 prompter.set_prompt (_("Name for new playlist:"));
1226 prompter.set_initial_text (name);
1227 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1228 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1230 switch (prompter.run ()) {
1231 case Gtk::RESPONSE_ACCEPT:
1232 prompter.get_result (name);
1240 if (name.length()) {
1241 tr->use_new_playlist ();
1242 tr->playlist()->set_name (name);
1247 RouteTimeAxisView::clear_playlist ()
1249 boost::shared_ptr<Track> tr = track ();
1250 if (!tr || tr->destructive()) {
1254 boost::shared_ptr<Playlist> pl = tr->playlist();
1259 _editor.clear_playlist (pl);
1263 RouteTimeAxisView::speed_changed ()
1265 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1269 RouteTimeAxisView::update_diskstream_display ()
1279 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1281 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1283 /* special case: select/deselect all tracks */
1284 if (_editor.get_selection().selected (this)) {
1285 _editor.get_selection().clear_tracks ();
1287 _editor.select_all_tracks ();
1293 switch (ArdourKeyboard::selection_type (ev->state)) {
1294 case Selection::Toggle:
1295 _editor.get_selection().toggle (this);
1298 case Selection::Set:
1299 _editor.get_selection().set (this);
1302 case Selection::Extend:
1303 _editor.extend_selection_to_track (*this);
1306 case Selection::Add:
1307 _editor.get_selection().add (this);
1313 RouteTimeAxisView::set_selected_points (PointSelection& points)
1315 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1316 (*i)->set_selected_points (points);
1321 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1324 _view->set_selected_regionviews (regions);
1328 /** Add the selectable things that we have to a list.
1329 * @param results List to add things to.
1332 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1337 speed = track()->speed();
1340 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1341 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1343 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1344 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1347 /* pick up visible automation tracks */
1349 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1350 if (!(*i)->hidden()) {
1351 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1357 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1360 _view->get_inverted_selectables (sel, results);
1363 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1364 if (!(*i)->hidden()) {
1365 (*i)->get_inverted_selectables (sel, results);
1373 RouteTimeAxisView::route_group () const
1375 return _route->route_group();
1379 RouteTimeAxisView::name() const
1381 return _route->name();
1384 boost::shared_ptr<Playlist>
1385 RouteTimeAxisView::playlist () const
1387 boost::shared_ptr<Track> tr;
1389 if ((tr = track()) != 0) {
1390 return tr->playlist();
1392 return boost::shared_ptr<Playlist> ();
1397 RouteTimeAxisView::name_entry_changed ()
1399 TimeAxisView::name_entry_changed ();
1401 string x = name_entry->get_text ();
1403 if (x == _route->name()) {
1407 strip_whitespace_edges (x);
1409 if (x.length() == 0) {
1410 name_entry->set_text (_route->name());
1414 if (_session->route_name_internal (x)) {
1415 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1417 name_entry->grab_focus ();
1418 } else if (RouteUI::verify_new_route_name (x)) {
1419 _route->set_name (x);
1421 name_entry->grab_focus ();
1425 boost::shared_ptr<Region>
1426 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1428 boost::shared_ptr<Playlist> pl = playlist ();
1431 return pl->find_next_region (pos, point, dir);
1434 return boost::shared_ptr<Region> ();
1438 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1440 boost::shared_ptr<Playlist> pl = playlist ();
1443 return pl->find_next_region_boundary (pos, dir);
1450 RouteTimeAxisView::fade_range (TimeSelection& selection)
1452 boost::shared_ptr<Playlist> what_we_got;
1453 boost::shared_ptr<Track> tr = track ();
1454 boost::shared_ptr<Playlist> playlist;
1457 /* route is a bus, not a track */
1461 playlist = tr->playlist();
1463 TimeSelection time (selection);
1464 float const speed = tr->speed();
1465 if (speed != 1.0f) {
1466 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1467 (*i).start = session_frame_to_track_frame((*i).start, speed);
1468 (*i).end = session_frame_to_track_frame((*i).end, speed);
1472 playlist->clear_changes ();
1473 playlist->clear_owned_changes ();
1475 playlist->fade_range (time);
1477 vector<Command*> cmds;
1478 playlist->rdiff (cmds);
1479 _session->add_commands (cmds);
1480 _session->add_command (new StatefulDiffCommand (playlist));
1485 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1487 boost::shared_ptr<Playlist> what_we_got;
1488 boost::shared_ptr<Track> tr = track ();
1489 boost::shared_ptr<Playlist> playlist;
1492 /* route is a bus, not a track */
1496 playlist = tr->playlist();
1498 TimeSelection time (selection.time);
1499 float const speed = tr->speed();
1500 if (speed != 1.0f) {
1501 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1502 (*i).start = session_frame_to_track_frame((*i).start, speed);
1503 (*i).end = session_frame_to_track_frame((*i).end, speed);
1507 playlist->clear_changes ();
1508 playlist->clear_owned_changes ();
1512 if (playlist->cut (time) != 0) {
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));
1526 if ((what_we_got = playlist->cut (time)) != 0) {
1527 _editor.get_cut_buffer().add (what_we_got);
1528 if (Config->get_edit_mode() == Ripple)
1529 playlist->ripple(time.start(), -time.length(), NULL);
1530 // no need to exclude any regions from rippling here
1532 vector<Command*> cmds;
1533 playlist->rdiff (cmds);
1534 _session->add_commands (cmds);
1536 _session->add_command (new StatefulDiffCommand (playlist));
1540 if ((what_we_got = playlist->copy (time)) != 0) {
1541 _editor.get_cut_buffer().add (what_we_got);
1546 if ((what_we_got = playlist->cut (time)) != 0) {
1547 if (Config->get_edit_mode() == Ripple)
1548 playlist->ripple(time.start(), -time.length(), NULL);
1549 // no need to exclude any regions from rippling here
1551 vector<Command*> cmds;
1552 playlist->rdiff (cmds);
1553 _session->add_commands (cmds);
1554 _session->add_command (new StatefulDiffCommand (playlist));
1555 what_we_got->release ();
1562 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1568 boost::shared_ptr<Playlist> pl = playlist ();
1569 PlaylistSelection::iterator p;
1571 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1573 if (p == selection.playlists.end()) {
1577 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1579 if (track()->speed() != 1.0f) {
1580 pos = session_frame_to_track_frame (pos, track()->speed());
1581 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1584 pl->clear_changes ();
1585 if (Config->get_edit_mode() == Ripple) {
1586 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1587 framecnt_t amount = extent.second - extent.first;
1588 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1590 pl->paste (*p, pos, times);
1592 vector<Command*> cmds;
1594 _session->add_commands (cmds);
1596 _session->add_command (new StatefulDiffCommand (pl));
1602 struct PlaylistSorter {
1603 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1604 return a->sort_id() < b->sort_id();
1609 RouteTimeAxisView::build_playlist_menu ()
1611 using namespace Menu_Helpers;
1617 delete playlist_action_menu;
1618 playlist_action_menu = new Menu;
1619 playlist_action_menu->set_name ("ArdourContextMenu");
1621 MenuList& playlist_items = playlist_action_menu->items();
1622 playlist_action_menu->set_name ("ArdourContextMenu");
1623 playlist_items.clear();
1625 RadioMenuItem::Group playlist_group;
1626 boost::shared_ptr<Track> tr = track ();
1628 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1630 /* sort the playlists */
1632 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1634 /* add the playlists to the menu */
1635 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1636 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1637 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1638 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1640 if (tr->playlist()->id() == (*i)->id()) {
1646 playlist_items.push_back (SeparatorElem());
1647 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1648 playlist_items.push_back (SeparatorElem());
1650 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1651 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1652 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1655 // Use a label which tells the user what is happening
1656 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1657 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1661 playlist_items.push_back (SeparatorElem());
1662 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1663 playlist_items.push_back (SeparatorElem());
1665 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1669 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1671 assert (is_track());
1673 // exit if we were triggered by deactivating the old playlist
1674 if (!item->get_active()) {
1678 boost::shared_ptr<Playlist> pl (wpl.lock());
1684 if (track()->playlist() == pl) {
1685 // exit when use_playlist is called by the creation of the playlist menu
1686 // or the playlist choice is unchanged
1690 track()->use_playlist (pl);
1692 RouteGroup* rg = route_group();
1694 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1695 std::string group_string = "." + rg->name() + ".";
1697 std::string take_name = pl->name();
1698 std::string::size_type idx = take_name.find(group_string);
1700 if (idx == std::string::npos)
1703 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1705 boost::shared_ptr<RouteList> rl (rg->route_list());
1707 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1708 if ((*i) == this->route()) {
1712 std::string playlist_name = (*i)->name()+group_string+take_name;
1714 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1719 if (track->freeze_state() == Track::Frozen) {
1720 /* Don't change playlists of frozen tracks */
1724 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1726 // No playlist for this track for this take yet, make it
1727 track->use_new_playlist();
1728 track->playlist()->set_name(playlist_name);
1730 track->use_playlist(ipl);
1737 RouteTimeAxisView::update_playlist_tip ()
1739 RouteGroup* rg = route_group ();
1740 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1741 string group_string = "." + rg->name() + ".";
1743 string take_name = track()->playlist()->name();
1744 string::size_type idx = take_name.find(group_string);
1746 if (idx != string::npos) {
1747 /* find the bit containing the take number / name */
1748 take_name = take_name.substr (idx + group_string.length());
1750 /* set the playlist button tooltip to the take name */
1751 ARDOUR_UI::instance()->set_tip (
1753 string_compose(_("Take: %1.%2"),
1754 Glib::Markup::escape_text(rg->name()),
1755 Glib::Markup::escape_text(take_name))
1762 /* set the playlist button tooltip to the playlist name */
1763 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1768 RouteTimeAxisView::show_playlist_selector ()
1770 _editor.playlist_selector().show_for (this);
1774 RouteTimeAxisView::map_frozen ()
1780 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1782 switch (track()->freeze_state()) {
1784 playlist_button.set_sensitive (false);
1785 rec_enable_button->set_sensitive (false);
1788 playlist_button.set_sensitive (true);
1789 rec_enable_button->set_sensitive (true);
1795 RouteTimeAxisView::color_handler ()
1797 //case cTimeStretchOutline:
1798 if (timestretch_rect) {
1799 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1801 //case cTimeStretchFill:
1802 if (timestretch_rect) {
1803 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1809 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1810 * Will add track if necessary.
1813 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1815 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1816 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1819 /* it doesn't exist yet, so we don't care about the button state: just add it */
1820 create_automation_child (param, true);
1823 bool yn = menu->get_active();
1824 bool changed = false;
1826 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1828 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1829 will have done that for us.
1832 if (changed && !no_redraw) {
1840 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1842 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1848 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1850 if (menu && !_hidden) {
1851 ignore_toggle = true;
1852 menu->set_active (false);
1853 ignore_toggle = false;
1856 if (_route && !no_redraw) {
1863 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1865 if (apply_to_selection) {
1866 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1870 /* Show our automation */
1872 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1873 i->second->set_marked_for_display (true);
1875 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1878 menu->set_active(true);
1883 /* Show processor automation */
1885 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1886 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1887 if ((*ii)->view == 0) {
1888 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1891 (*ii)->menu_item->set_active (true);
1904 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1906 if (apply_to_selection) {
1907 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1911 /* Show our automation */
1913 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1914 if (i->second->has_automation()) {
1915 i->second->set_marked_for_display (true);
1917 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1919 menu->set_active(true);
1924 /* Show processor automation */
1926 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1927 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1928 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1929 (*ii)->menu_item->set_active (true);
1941 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1943 if (apply_to_selection) {
1944 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1948 /* Hide our automation */
1950 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1951 i->second->set_marked_for_display (false);
1953 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1956 menu->set_active (false);
1960 /* Hide processor automation */
1962 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1963 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1964 (*ii)->menu_item->set_active (false);
1975 RouteTimeAxisView::region_view_added (RegionView* rv)
1977 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1978 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1979 boost::shared_ptr<AutomationTimeAxisView> atv;
1981 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1986 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1987 (*i)->add_ghost(rv);
1991 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1993 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1999 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2001 parent.remove_processor_automation_node (this);
2005 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2008 remove_child (pan->view);
2012 RouteTimeAxisView::ProcessorAutomationNode*
2013 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2015 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2017 if ((*i)->processor == processor) {
2019 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2020 if ((*ii)->what == what) {
2030 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2032 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2035 ProcessorAutomationNode* pan;
2037 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2038 /* session state may never have been saved with new plugin */
2039 error << _("programming error: ")
2040 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2041 processor->name(), what.type(), (int) what.channel(), what.id() )
2051 boost::shared_ptr<AutomationControl> control
2052 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2054 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2055 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2056 _editor, *this, false, parent_canvas,
2057 processor->describe_parameter (what), processor->name()));
2059 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2061 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2064 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2069 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2072 pan->menu_item->set_active (false);
2081 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2083 boost::shared_ptr<Processor> processor (p.lock ());
2085 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2086 /* The Amp processor is a special case and is dealt with separately */
2090 set<Evoral::Parameter> existing;
2092 processor->what_has_data (existing);
2094 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2096 Evoral::Parameter param (*i);
2097 boost::shared_ptr<AutomationLine> al;
2099 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2102 add_processor_automation_curve (processor, param);
2108 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2110 using namespace Menu_Helpers;
2114 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2116 _automation_tracks[param] = track;
2118 /* existing state overrides "show" argument */
2119 string s = track->gui_property ("visible");
2121 show = string_is_affirmative (s);
2124 /* this might or might not change the visibility status, so don't rely on it */
2125 track->set_marked_for_display (show);
2127 if (show && !no_redraw) {
2131 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2132 /* MIDI-related parameters are always in the menu, there's no
2133 reason to rebuild the menu just because we added a automation
2134 lane for one of them. But if we add a non-MIDI automation
2135 lane, then we need to invalidate the display menu.
2137 delete display_menu;
2143 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2145 boost::shared_ptr<Processor> processor (p.lock ());
2147 if (!processor || !processor->display_to_user ()) {
2151 /* we use this override to veto the Amp processor from the plugin menu,
2152 as its automation lane can be accessed using the special "Fader" menu
2156 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2160 using namespace Menu_Helpers;
2161 ProcessorAutomationInfo *rai;
2162 list<ProcessorAutomationInfo*>::iterator x;
2164 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2166 if (automatable.empty()) {
2170 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2171 if ((*x)->processor == processor) {
2176 if (x == processor_automation.end()) {
2178 rai = new ProcessorAutomationInfo (processor);
2179 processor_automation.push_back (rai);
2187 /* any older menu was deleted at the top of processors_changed()
2188 when we cleared the subplugin menu.
2191 rai->menu = manage (new Menu);
2192 MenuList& items = rai->menu->items();
2193 rai->menu->set_name ("ArdourContextMenu");
2197 std::set<Evoral::Parameter> has_visible_automation;
2198 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2200 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2202 ProcessorAutomationNode* pan;
2203 Gtk::CheckMenuItem* mitem;
2205 string name = processor->describe_parameter (*i);
2207 items.push_back (CheckMenuElem (name));
2208 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2210 _subplugin_menu_map[*i] = mitem;
2212 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2213 mitem->set_active(true);
2216 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2220 pan = new ProcessorAutomationNode (*i, mitem, *this);
2222 rai->lines.push_back (pan);
2226 pan->menu_item = mitem;
2230 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2233 /* add the menu for this processor, because the subplugin
2234 menu is always cleared at the top of processors_changed().
2235 this is the result of some poor design in gtkmm and/or
2239 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2244 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2245 RouteTimeAxisView::ProcessorAutomationNode* pan)
2247 bool showit = pan->menu_item->get_active();
2248 bool redraw = false;
2250 if (pan->view == 0 && showit) {
2251 add_processor_automation_curve (rai->processor, pan->what);
2255 if (pan->view && pan->view->set_marked_for_display (showit)) {
2259 if (redraw && !no_redraw) {
2265 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2267 if (c.type == RouteProcessorChange::MeterPointChange) {
2268 /* nothing to do if only the meter point has changed */
2272 using namespace Menu_Helpers;
2274 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2275 (*i)->valid = false;
2278 setup_processor_menu_and_curves ();
2280 bool deleted_processor_automation = false;
2282 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2284 list<ProcessorAutomationInfo*>::iterator tmp;
2292 processor_automation.erase (i);
2293 deleted_processor_automation = true;
2300 if (deleted_processor_automation && !no_redraw) {
2305 boost::shared_ptr<AutomationLine>
2306 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2308 ProcessorAutomationNode* pan;
2310 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2316 return boost::shared_ptr<AutomationLine>();
2320 RouteTimeAxisView::reset_processor_automation_curves ()
2322 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2328 RouteTimeAxisView::can_edit_name () const
2330 /* we do not allow track name changes if it is record enabled
2332 return !_route->record_enabled();
2336 RouteTimeAxisView::blink_rec_display (bool onoff)
2338 RouteUI::blink_rec_display (onoff);
2342 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2344 if (_ignore_set_layer_display) {
2348 if (apply_to_selection) {
2349 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2353 _view->set_layer_display (d);
2356 set_gui_property (X_("layer-display"), enum_2_string (d));
2361 RouteTimeAxisView::layer_display () const
2364 return _view->layer_display ();
2367 /* we don't know, since we don't have a _view, so just return something */
2373 boost::shared_ptr<AutomationTimeAxisView>
2374 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2376 AutomationTracks::iterator i = _automation_tracks.find(param);
2377 if (i != _automation_tracks.end()) {
2380 return boost::shared_ptr<AutomationTimeAxisView>();
2385 RouteTimeAxisView::fast_update ()
2387 gm.get_level_meter().update_meters ();
2391 RouteTimeAxisView::hide_meter ()
2394 gm.get_level_meter().hide_meters ();
2398 RouteTimeAxisView::show_meter ()
2404 RouteTimeAxisView::reset_meter ()
2406 if (Config->get_show_track_meters()) {
2407 int meter_width = 3;
2408 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2411 gm.get_level_meter().setup_meters (height - 9, meter_width);
2418 RouteTimeAxisView::clear_meter ()
2420 gm.get_level_meter().clear_meters ();
2424 RouteTimeAxisView::meter_changed ()
2426 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2428 if (_route && !no_redraw) {
2431 // reset peak when meter point changes
2432 gm.reset_peak_display();
2436 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2439 if (_route && !no_redraw) {
2445 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2447 using namespace Menu_Helpers;
2449 if (!_underlay_streams.empty()) {
2450 MenuList& parent_items = parent_menu->items();
2451 Menu* gs_menu = manage (new Menu);
2452 gs_menu->set_name ("ArdourContextMenu");
2453 MenuList& gs_items = gs_menu->items();
2455 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2457 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2458 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2459 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2465 RouteTimeAxisView::set_underlay_state()
2467 if (!underlay_xml_node) {
2471 XMLNodeList nlist = underlay_xml_node->children();
2472 XMLNodeConstIterator niter;
2473 XMLNode *child_node;
2475 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2476 child_node = *niter;
2478 if (child_node->name() != "Underlay") {
2482 XMLProperty* prop = child_node->property ("id");
2484 PBD::ID id (prop->value());
2486 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2489 add_underlay(v->view(), false);
2498 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2504 RouteTimeAxisView& other = v->trackview();
2506 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2507 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2508 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2512 _underlay_streams.push_back(v);
2513 other._underlay_mirrors.push_back(this);
2515 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2517 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2519 if (!underlay_xml_node) {
2520 underlay_xml_node = xml_node->add_child("Underlays");
2523 XMLNode* node = underlay_xml_node->add_child("Underlay");
2524 XMLProperty* prop = node->add_property("id");
2525 prop->set_value(v->trackview().route()->id().to_s());
2532 RouteTimeAxisView::remove_underlay (StreamView* v)
2538 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2539 RouteTimeAxisView& other = v->trackview();
2541 if (it != _underlay_streams.end()) {
2542 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2544 if (gm == other._underlay_mirrors.end()) {
2545 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2549 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2551 _underlay_streams.erase(it);
2552 other._underlay_mirrors.erase(gm);
2554 if (underlay_xml_node) {
2555 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2561 RouteTimeAxisView::set_button_names ()
2563 if (_route && _route->solo_safe()) {
2564 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2566 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2568 if (Config->get_solo_control_is_listen_control()) {
2569 switch (Config->get_listen_position()) {
2570 case AfterFaderListen:
2571 solo_button->set_text (_("A"));
2572 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2574 case PreFaderListen:
2575 solo_button->set_text (_("P"));
2576 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2580 solo_button->set_text (_("S"));
2581 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2583 mute_button->set_text (_("M"));
2587 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2589 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2590 if (i != _main_automation_menu_map.end()) {
2594 i = _subplugin_menu_map.find (param);
2595 if (i != _subplugin_menu_map.end()) {
2603 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2605 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2607 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2611 gain_track.reset (new AutomationTimeAxisView (_session,
2612 _route, _route->amp(), c, param,
2617 _route->amp()->describe_parameter(param)));
2620 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2623 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2627 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2629 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2631 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2635 mute_track.reset (new AutomationTimeAxisView (_session,
2636 _route, _route, c, param,
2641 _route->describe_parameter(param)));
2644 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2647 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2651 void add_region_to_list (RegionView* rv, RegionList* l)
2653 l->push_back (rv->region());
2657 RouteTimeAxisView::combine_regions ()
2659 /* as of may 2011, we do not offer uncombine for MIDI tracks
2662 if (!is_audio_track()) {
2670 RegionList selected_regions;
2671 boost::shared_ptr<Playlist> playlist = track()->playlist();
2673 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2675 if (selected_regions.size() < 2) {
2679 playlist->clear_changes ();
2680 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2682 _session->add_command (new StatefulDiffCommand (playlist));
2683 /* make the new region be selected */
2685 return _view->find_view (compound_region);
2689 RouteTimeAxisView::uncombine_regions ()
2691 /* as of may 2011, we do not offer uncombine for MIDI tracks
2693 if (!is_audio_track()) {
2701 RegionList selected_regions;
2702 boost::shared_ptr<Playlist> playlist = track()->playlist();
2704 /* have to grab selected regions first because the uncombine is going
2705 * to change that in the middle of the list traverse
2708 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2710 playlist->clear_changes ();
2712 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2713 playlist->uncombine (*i);
2716 _session->add_command (new StatefulDiffCommand (playlist));
2720 RouteTimeAxisView::state_id() const
2722 return string_compose ("rtav %1", _route->id().to_s());
2727 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2729 TimeAxisView::remove_child (c);
2731 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2733 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2734 if (i->second == a) {
2735 _automation_tracks.erase (i);