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/pannable.h"
49 #include "ardour/panner.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
56 #include "evoral/Parameter.hpp"
58 #include "canvas/debug.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
63 #include "global_signals.h"
64 #include "route_time_axis.h"
65 #include "automation_time_axis.h"
67 #include "gui_thread.h"
68 #include "item_counts.h"
70 #include "paste_context.h"
71 #include "playlist_selector.h"
72 #include "point_selection.h"
74 #include "public_editor.h"
75 #include "region_view.h"
76 #include "rgb_macros.h"
77 #include "selection.h"
78 #include "streamview.h"
80 #include "route_group_menu.h"
82 #include "ardour/track.h"
86 using namespace ARDOUR;
87 using namespace ARDOUR_UI_UTILS;
89 using namespace Gtkmm2ext;
91 using namespace Editing;
95 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
98 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
100 , parent_canvas (canvas)
102 , button_table (3, 3)
103 , route_group_button (_("G"))
104 , playlist_button (_("P"))
105 , automation_button (_("A"))
106 , automation_action_menu (0)
107 , plugins_submenu_item (0)
108 , route_group_menu (0)
109 , playlist_action_menu (0)
111 , color_mode_menu (0)
112 , gm (sess, true, 75, 14)
113 , _ignore_set_layer_display (false)
114 , gain_automation_item(NULL)
115 , mute_automation_item(NULL)
116 , pan_automation_item(NULL)
118 number_label.set_name("tracknumber label");
119 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
120 number_label.set_alignment(.5, .5);
121 number_label.set_fallthrough_to_parent (true);
123 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
129 RouteUI::set_route (rt);
131 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
132 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
133 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
136 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
139 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
140 gm.get_level_meter().set_no_show_all();
141 gm.get_level_meter().setup_meters(50, meter_width);
142 gm.update_gain_sensitive ();
144 string str = gui_property ("height");
146 set_height (atoi (str));
148 set_height (preset_height (HeightNormal));
151 if (!_route->is_auditioner()) {
152 if (gui_property ("visible").empty()) {
153 set_gui_property ("visible", true);
156 set_gui_property ("visible", false);
159 timestretch_rect = 0;
162 ignore_toggle = false;
164 route_group_button.set_name ("route button");
165 playlist_button.set_name ("route button");
166 automation_button.set_name ("route button");
168 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
169 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
170 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
174 if (ARDOUR::Profile->get_mixbus()) {
175 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
177 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
180 if (is_midi_track()) {
181 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
182 gm.set_fader_name ("MidiTrackFader");
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
185 gm.set_fader_name ("AudioTrackFader");
188 rec_enable_button->set_sensitive (_session->writable());
190 /* set playlist button tip to the current playlist, and make it update when it changes */
191 update_playlist_tip ();
192 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
195 gm.set_fader_name ("AudioBusFader");
196 Gtk::Fixed *blank = manage(new Gtk::Fixed());
197 controls_button_size_group->add_widget(*blank);
198 if (ARDOUR::Profile->get_mixbus() ) {
199 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
201 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
206 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
208 if (!ARDOUR::Profile->get_mixbus()) {
209 controls_meters_size_group->add_widget (gm.get_level_meter());
212 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
213 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
214 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
215 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
217 if (ARDOUR::Profile->get_mixbus()) {
218 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
220 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
222 // mute button is always present, it is used to
223 // force the 'blank' placeholders to the proper size
224 controls_button_size_group->add_widget(*mute_button);
226 if (!_route->is_master()) {
227 if (ARDOUR::Profile->get_mixbus()) {
228 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
230 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 Gtk::Fixed *blank = manage(new Gtk::Fixed());
234 controls_button_size_group->add_widget(*blank);
235 if (ARDOUR::Profile->get_mixbus()) {
236 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
238 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
243 if (ARDOUR::Profile->get_mixbus()) {
244 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
245 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
247 else if (!ARDOUR::Profile->get_trx()) {
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::TrackHeader);
257 solo_button->set_tweaks(ArdourButton::TrackHeader);
258 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
259 playlist_button.set_tweaks(ArdourButton::TrackHeader);
260 automation_button.set_tweaks(ArdourButton::TrackHeader);
261 route_group_button.set_tweaks(ArdourButton::TrackHeader);
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);
275 else if (!ARDOUR::Profile->get_trx()) {
276 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 if (is_track() && track()->mode() == ARDOUR::Normal) {
280 if (ARDOUR::Profile->get_mixbus()) {
281 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283 else if (!ARDOUR::Profile->get_trx()) {
284 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
291 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
295 str = gui_property ("layer-display");
297 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
300 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
301 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
303 /* pick up the correct freeze state */
308 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
309 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
311 PropertyList* plist = new PropertyList();
313 plist->add (ARDOUR::Properties::mute, true);
314 plist->add (ARDOUR::Properties::solo, true);
316 route_group_menu = new RouteGroupMenu (_session, plist);
318 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
321 RouteTimeAxisView::~RouteTimeAxisView ()
323 cleanup_gui_properties ();
325 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
329 delete playlist_action_menu;
330 playlist_action_menu = 0;
335 _automation_tracks.clear ();
337 delete route_group_menu;
338 CatchDeletion (this);
342 RouteTimeAxisView::post_construct ()
344 /* map current state of the route */
346 update_diskstream_display ();
347 setup_processor_menu_and_curves ();
348 reset_processor_automation_curves ();
351 /** Set up the processor menu for the current set of processors, and
352 * display automation curves for any parameters which have data.
355 RouteTimeAxisView::setup_processor_menu_and_curves ()
357 _subplugin_menu_map.clear ();
358 subplugin_menu.items().clear ();
359 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
360 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
364 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
366 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
367 if (_route->route_group()) {
368 _route->route_group()->remove (_route);
374 r.push_back (route ());
376 route_group_menu->build (r);
377 route_group_menu->menu()->popup (ev->button, ev->time);
383 RouteTimeAxisView::playlist_changed ()
389 RouteTimeAxisView::label_view ()
391 string x = _route->name ();
392 if (x != name_label.get_text ()) {
393 name_label.set_text (x);
395 const int64_t track_number = _route->track_number ();
396 if (track_number == 0) {
397 number_label.set_text ("");
399 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
404 RouteTimeAxisView::update_track_number_visibility ()
407 bool show_label = _session->config.get_track_name_number();
409 if (_route && _route->is_master()) {
413 if (number_label.get_parent()) {
414 controls_table.remove (number_label);
417 if (ARDOUR::Profile->get_mixbus()) {
418 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
420 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
422 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
423 // except the width of the number label is subtracted from the name-hbox, so we
424 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
425 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
427 number_label.set_size_request(tnw, -1);
428 number_label.show ();
429 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
431 number_label.hide ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
437 RouteTimeAxisView::parameter_changed (string const & p)
439 if (p == "track-name-number") {
440 update_track_number_visibility();
445 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
447 if (what_changed.contains (ARDOUR::Properties::name)) {
453 RouteTimeAxisView::take_name_changed (void *src)
461 RouteTimeAxisView::playlist_click ()
463 build_playlist_menu ();
464 conditionally_add_to_selection ();
465 playlist_action_menu->popup (1, gtk_get_current_event_time());
469 RouteTimeAxisView::automation_click ()
471 conditionally_add_to_selection ();
472 build_automation_action_menu (false);
473 automation_action_menu->popup (1, gtk_get_current_event_time());
477 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
479 using namespace Menu_Helpers;
481 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
482 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
485 detach_menu (subplugin_menu);
487 _main_automation_menu_map.clear ();
488 delete automation_action_menu;
489 automation_action_menu = new Menu;
491 MenuList& items = automation_action_menu->items();
493 automation_action_menu->set_name ("ArdourContextMenu");
495 items.push_back (MenuElem (_("Show All Automation"),
496 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
498 items.push_back (MenuElem (_("Show Existing Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
501 items.push_back (MenuElem (_("Hide All Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
504 /* Attach the plugin submenu. It may have previously been used elsewhere,
505 so it was detached above
508 if (!subplugin_menu.items().empty()) {
509 items.push_back (SeparatorElem ());
510 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
511 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
514 /* Add any route automation */
517 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
518 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
519 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
520 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
522 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
526 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
527 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
528 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
529 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
531 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
534 if (!pan_tracks.empty()) {
535 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
536 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
537 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
538 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
540 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
541 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
542 _main_automation_menu_map[*p] = pan_automation_item;
548 RouteTimeAxisView::build_display_menu ()
550 using namespace Menu_Helpers;
554 TimeAxisView::build_display_menu ();
556 /* now fill it with our stuff */
558 MenuList& items = display_menu->items();
559 display_menu->set_name ("ArdourContextMenu");
561 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
563 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
565 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
567 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
569 items.push_back (SeparatorElem());
572 detach_menu (*_size_menu);
575 items.push_back (MenuElem (_("Height"), *_size_menu));
577 items.push_back (SeparatorElem());
579 if (!Profile->get_sae()) {
580 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
581 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
582 items.push_back (SeparatorElem());
585 // Hook for derived classes to add type specific stuff
586 append_extra_display_menu_items ();
590 Menu* layers_menu = manage (new Menu);
591 MenuList &layers_items = layers_menu->items();
592 layers_menu->set_name("ArdourContextMenu");
594 RadioMenuItem::Group layers_group;
596 /* Find out how many overlaid/stacked tracks we have in the selection */
600 TrackSelection const & s = _editor.get_selection().tracks;
601 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
602 StreamView* v = (*i)->view ();
607 switch (v->layer_display ()) {
618 /* We're not connecting to signal_toggled() here; in the case where these two items are
619 set to be in the `inconsistent' state, it seems that one or other will end up active
620 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
621 select the active one, no toggled signal is emitted so nothing happens.
624 _ignore_set_layer_display = true;
626 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
627 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
628 i->set_active (overlaid != 0 && stacked == 0);
629 i->set_inconsistent (overlaid != 0 && stacked != 0);
630 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
632 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
633 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
634 i->set_active (overlaid == 0 && stacked != 0);
635 i->set_inconsistent (overlaid != 0 && stacked != 0);
636 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
638 _ignore_set_layer_display = false;
640 items.push_back (MenuElem (_("Layers"), *layers_menu));
642 if (!Profile->get_sae()) {
644 Menu* alignment_menu = manage (new Menu);
645 MenuList& alignment_items = alignment_menu->items();
646 alignment_menu->set_name ("ArdourContextMenu");
648 RadioMenuItem::Group align_group;
650 /* Same verbose hacks as for the layering options above */
656 boost::shared_ptr<Track> first_track;
658 TrackSelection const & s = _editor.get_selection().tracks;
659 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
660 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
661 if (!r || !r->is_track ()) {
666 first_track = r->track();
669 switch (r->track()->alignment_choice()) {
673 switch (r->track()->alignment_style()) {
674 case ExistingMaterial:
682 case UseExistingMaterial:
698 inconsistent = false;
707 if (!inconsistent && first_track) {
709 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
710 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
711 i->set_active (automatic != 0 && existing == 0 && capture == 0);
712 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
714 switch (first_track->alignment_choice()) {
716 switch (first_track->alignment_style()) {
717 case ExistingMaterial:
718 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
721 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
729 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
730 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
731 i->set_active (existing != 0 && capture == 0 && automatic == 0);
732 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
734 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
735 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
736 i->set_active (existing == 0 && capture != 0 && automatic == 0);
737 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
739 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
745 Menu* mode_menu = manage (new Menu);
746 MenuList& mode_items = mode_menu->items ();
747 mode_menu->set_name ("ArdourContextMenu");
749 RadioMenuItem::Group mode_group;
755 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
756 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
757 if (!r || !r->is_track ()) {
761 switch (r->track()->mode()) {
774 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
775 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
776 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
777 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
778 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
780 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
781 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
782 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
783 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
784 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
786 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
787 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
789 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
790 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
792 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
796 items.push_back (SeparatorElem());
798 build_playlist_menu ();
799 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
800 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
803 route_group_menu->detach ();
806 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
807 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
809 r.push_back (rtv->route ());
814 r.push_back (route ());
817 route_group_menu->build (r);
818 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
820 build_automation_action_menu (true);
821 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
823 items.push_back (SeparatorElem());
827 TrackSelection const & s = _editor.get_selection().tracks;
828 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
829 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
834 if (r->route()->active()) {
841 items.push_back (CheckMenuElem (_("Active")));
842 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
843 bool click_sets_active = true;
844 if (active > 0 && inactive == 0) {
845 i->set_active (true);
846 click_sets_active = false;
847 } else if (active > 0 && inactive > 0) {
848 i->set_inconsistent (true);
850 i->set_sensitive(! _session->transport_rolling());
851 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
853 items.push_back (SeparatorElem());
854 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
855 if (!Profile->get_sae()) {
856 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
858 items.push_front (SeparatorElem());
859 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
864 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
866 if (apply_to_selection) {
867 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
870 bool needs_bounce = false;
872 if (!track()->can_use_mode (mode, needs_bounce)) {
878 cerr << "would bounce this one\n";
883 track()->set_mode (mode);
888 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
890 TimeAxisView::show_timestretch (start, end, layers, layer);
900 /* check that the time selection was made in our route, or our route group.
901 remember that route_group() == 0 implies the route is *not* in a edit group.
904 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
905 /* this doesn't apply to us */
909 /* ignore it if our edit group is not active */
911 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
916 if (timestretch_rect == 0) {
917 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
918 timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
919 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
922 timestretch_rect->show ();
923 timestretch_rect->raise_to_top ();
925 double const x1 = start / _editor.get_current_zoom();
926 double const x2 = (end - 1) / _editor.get_current_zoom();
928 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
929 x2, current_height() * (layers - layer) / layers));
933 RouteTimeAxisView::hide_timestretch ()
935 TimeAxisView::hide_timestretch ();
937 if (timestretch_rect) {
938 timestretch_rect->hide ();
943 RouteTimeAxisView::show_selection (TimeSelection& ts)
947 /* ignore it if our edit group is not active or if the selection was started
948 in some other track or route group (remember that route_group() == 0 means
949 that the track is not in an route group).
952 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
953 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
959 TimeAxisView::show_selection (ts);
963 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
966 bool height_changed = (height == 0) || (h != height);
969 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
972 gm.get_level_meter().setup_meters (gmlen, meter_width);
974 TimeAxisView::set_height (h, m);
977 _view->set_height ((double) current_height());
980 if (height >= preset_height (HeightNormal)) {
984 gm.get_gain_slider().show();
986 if (!_route || _route->is_monitor()) {
991 if (rec_enable_button)
992 rec_enable_button->show();
994 route_group_button.show();
995 automation_button.show();
997 if (is_track() && track()->mode() == ARDOUR::Normal) {
998 playlist_button.show();
1005 gm.get_gain_slider().hide();
1006 mute_button->show();
1007 if (!_route || _route->is_monitor()) {
1008 solo_button->hide();
1010 solo_button->show();
1012 if (rec_enable_button)
1013 rec_enable_button->show();
1015 route_group_button.hide ();
1016 automation_button.hide ();
1018 if (is_track() && track()->mode() == ARDOUR::Normal) {
1019 playlist_button.hide ();
1024 if (height_changed && !no_redraw) {
1025 /* only emit the signal if the height really changed */
1031 RouteTimeAxisView::route_color_changed ()
1034 _view->apply_color (color(), StreamView::RegionColor);
1037 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1041 RouteTimeAxisView::reset_samples_per_pixel ()
1043 set_samples_per_pixel (_editor.get_current_zoom());
1047 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1052 speed = track()->speed();
1056 _view->set_samples_per_pixel (fpp * speed);
1059 TimeAxisView::set_samples_per_pixel (fpp * speed);
1063 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1065 if (!mitem->get_active()) {
1066 /* this is one of the two calls made when these radio menu items change status. this one
1067 is for the item that became inactive, and we want to ignore it.
1072 if (apply_to_selection) {
1073 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1076 track()->set_align_choice (choice);
1082 RouteTimeAxisView::rename_current_playlist ()
1084 ArdourPrompter prompter (true);
1087 boost::shared_ptr<Track> tr = track();
1088 if (!tr || tr->destructive()) {
1092 boost::shared_ptr<Playlist> pl = tr->playlist();
1097 prompter.set_title (_("Rename Playlist"));
1098 prompter.set_prompt (_("New name for playlist:"));
1099 prompter.set_initial_text (pl->name());
1100 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1101 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1103 switch (prompter.run ()) {
1104 case Gtk::RESPONSE_ACCEPT:
1105 prompter.get_result (name);
1106 if (name.length()) {
1107 pl->set_name (name);
1117 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1119 std::string ret (basename);
1121 std::string const group_string = "." + route_group()->name() + ".";
1123 // iterate through all playlists
1125 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1126 std::string tmp = (*i)->name();
1128 std::string::size_type idx = tmp.find(group_string);
1129 // find those which belong to this group
1130 if (idx != string::npos) {
1131 tmp = tmp.substr(idx + group_string.length());
1133 // and find the largest current number
1135 if (x > maxnumber) {
1144 snprintf (buf, sizeof(buf), "%d", maxnumber);
1146 ret = this->name() + "." + route_group()->name () + "." + buf;
1152 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1156 boost::shared_ptr<Track> tr = track ();
1157 if (!tr || tr->destructive()) {
1161 boost::shared_ptr<const Playlist> pl = tr->playlist();
1168 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1169 name = resolve_new_group_playlist_name(name, playlists_before_op);
1172 while (_session->playlists->by_name(name)) {
1173 name = Playlist::bump_name (name, *_session);
1176 // TODO: The prompter "new" button should be de-activated if the user
1177 // specifies a playlist name which already exists in the session.
1181 ArdourPrompter prompter (true);
1183 prompter.set_title (_("New Copy Playlist"));
1184 prompter.set_prompt (_("Name for new playlist:"));
1185 prompter.set_initial_text (name);
1186 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1187 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1188 prompter.show_all ();
1190 switch (prompter.run ()) {
1191 case Gtk::RESPONSE_ACCEPT:
1192 prompter.get_result (name);
1200 if (name.length()) {
1201 tr->use_copy_playlist ();
1202 tr->playlist()->set_name (name);
1207 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1211 boost::shared_ptr<Track> tr = track ();
1212 if (!tr || tr->destructive()) {
1216 boost::shared_ptr<const Playlist> pl = tr->playlist();
1223 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1224 name = resolve_new_group_playlist_name(name,playlists_before_op);
1227 while (_session->playlists->by_name(name)) {
1228 name = Playlist::bump_name (name, *_session);
1234 ArdourPrompter prompter (true);
1236 prompter.set_title (_("New Playlist"));
1237 prompter.set_prompt (_("Name for new playlist:"));
1238 prompter.set_initial_text (name);
1239 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1240 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1242 switch (prompter.run ()) {
1243 case Gtk::RESPONSE_ACCEPT:
1244 prompter.get_result (name);
1252 if (name.length()) {
1253 tr->use_new_playlist ();
1254 tr->playlist()->set_name (name);
1259 RouteTimeAxisView::clear_playlist ()
1261 boost::shared_ptr<Track> tr = track ();
1262 if (!tr || tr->destructive()) {
1266 boost::shared_ptr<Playlist> pl = tr->playlist();
1271 _editor.clear_playlist (pl);
1275 RouteTimeAxisView::speed_changed ()
1277 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1281 RouteTimeAxisView::update_diskstream_display ()
1291 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1293 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1295 /* special case: select/deselect all tracks */
1297 _editor.begin_reversible_selection_op (X_("Selection Click"));
1299 if (_editor.get_selection().selected (this)) {
1300 _editor.get_selection().clear_tracks ();
1302 _editor.select_all_tracks ();
1305 _editor.commit_reversible_selection_op ();
1310 _editor.begin_reversible_selection_op (X_("Selection Click"));
1312 switch (ArdourKeyboard::selection_type (ev->state)) {
1313 case Selection::Toggle:
1314 _editor.get_selection().toggle (this);
1317 case Selection::Set:
1318 _editor.get_selection().set (this);
1321 case Selection::Extend:
1322 _editor.extend_selection_to_track (*this);
1325 case Selection::Add:
1326 _editor.get_selection().add (this);
1330 _editor.commit_reversible_selection_op ();
1334 RouteTimeAxisView::set_selected_points (PointSelection& points)
1336 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1337 (*i)->set_selected_points (points);
1342 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1345 _view->set_selected_regionviews (regions);
1349 /** Add the selectable things that we have to a list.
1350 * @param results List to add things to.
1353 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1358 speed = track()->speed();
1361 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1362 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1364 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1365 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1368 /* pick up visible automation tracks */
1370 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1371 if (!(*i)->hidden()) {
1372 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1378 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1381 _view->get_inverted_selectables (sel, results);
1384 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1385 if (!(*i)->hidden()) {
1386 (*i)->get_inverted_selectables (sel, results);
1394 RouteTimeAxisView::route_group () const
1396 return _route->route_group();
1400 RouteTimeAxisView::name() const
1402 return _route->name();
1405 boost::shared_ptr<Playlist>
1406 RouteTimeAxisView::playlist () const
1408 boost::shared_ptr<Track> tr;
1410 if ((tr = track()) != 0) {
1411 return tr->playlist();
1413 return boost::shared_ptr<Playlist> ();
1418 RouteTimeAxisView::name_entry_changed ()
1420 TimeAxisView::name_entry_changed ();
1422 string x = name_entry->get_text ();
1424 if (x == _route->name()) {
1428 strip_whitespace_edges (x);
1430 if (x.length() == 0) {
1431 name_entry->set_text (_route->name());
1435 if (_session->route_name_internal (x)) {
1436 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1438 name_entry->grab_focus ();
1439 } else if (RouteUI::verify_new_route_name (x)) {
1440 _route->set_name (x);
1442 name_entry->grab_focus ();
1446 boost::shared_ptr<Region>
1447 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1449 boost::shared_ptr<Playlist> pl = playlist ();
1452 return pl->find_next_region (pos, point, dir);
1455 return boost::shared_ptr<Region> ();
1459 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1461 boost::shared_ptr<Playlist> pl = playlist ();
1464 return pl->find_next_region_boundary (pos, dir);
1471 RouteTimeAxisView::fade_range (TimeSelection& selection)
1473 boost::shared_ptr<Playlist> what_we_got;
1474 boost::shared_ptr<Track> tr = track ();
1475 boost::shared_ptr<Playlist> playlist;
1478 /* route is a bus, not a track */
1482 playlist = tr->playlist();
1484 TimeSelection time (selection);
1485 float const speed = tr->speed();
1486 if (speed != 1.0f) {
1487 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1488 (*i).start = session_frame_to_track_frame((*i).start, speed);
1489 (*i).end = session_frame_to_track_frame((*i).end, speed);
1493 playlist->clear_changes ();
1494 playlist->clear_owned_changes ();
1496 playlist->fade_range (time);
1498 vector<Command*> cmds;
1499 playlist->rdiff (cmds);
1500 _session->add_commands (cmds);
1501 _session->add_command (new StatefulDiffCommand (playlist));
1506 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1508 boost::shared_ptr<Playlist> what_we_got;
1509 boost::shared_ptr<Track> tr = track ();
1510 boost::shared_ptr<Playlist> playlist;
1513 /* route is a bus, not a track */
1517 playlist = tr->playlist();
1519 TimeSelection time (selection.time);
1520 float const speed = tr->speed();
1521 if (speed != 1.0f) {
1522 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1523 (*i).start = session_frame_to_track_frame((*i).start, speed);
1524 (*i).end = session_frame_to_track_frame((*i).end, speed);
1528 playlist->clear_changes ();
1529 playlist->clear_owned_changes ();
1533 if (playlist->cut (time) != 0) {
1534 if (Config->get_edit_mode() == Ripple)
1535 playlist->ripple(time.start(), -time.length(), NULL);
1536 // no need to exclude any regions from rippling here
1538 vector<Command*> cmds;
1539 playlist->rdiff (cmds);
1540 _session->add_commands (cmds);
1542 _session->add_command (new StatefulDiffCommand (playlist));
1547 if ((what_we_got = playlist->cut (time)) != 0) {
1548 _editor.get_cut_buffer().add (what_we_got);
1549 if (Config->get_edit_mode() == Ripple)
1550 playlist->ripple(time.start(), -time.length(), NULL);
1551 // no need to exclude any regions from rippling here
1553 vector<Command*> cmds;
1554 playlist->rdiff (cmds);
1555 _session->add_commands (cmds);
1557 _session->add_command (new StatefulDiffCommand (playlist));
1561 if ((what_we_got = playlist->copy (time)) != 0) {
1562 _editor.get_cut_buffer().add (what_we_got);
1567 if ((what_we_got = playlist->cut (time)) != 0) {
1568 if (Config->get_edit_mode() == Ripple)
1569 playlist->ripple(time.start(), -time.length(), NULL);
1570 // no need to exclude any regions from rippling here
1572 vector<Command*> cmds;
1573 playlist->rdiff (cmds);
1574 _session->add_commands (cmds);
1575 _session->add_command (new StatefulDiffCommand (playlist));
1576 what_we_got->release ();
1583 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1589 boost::shared_ptr<Playlist> pl = playlist ();
1590 const ARDOUR::DataType type = pl->data_type();
1591 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1593 if (p == selection.playlists.end()) {
1596 ctx.counts.increase_n_playlists(type);
1598 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1600 if (track()->speed() != 1.0f) {
1601 pos = session_frame_to_track_frame (pos, track()->speed());
1602 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1605 /* add multi-paste offset if applicable */
1606 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1607 const framecnt_t duration = extent.second - extent.first;
1608 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1610 pl->clear_changes ();
1611 if (Config->get_edit_mode() == Ripple) {
1612 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1613 framecnt_t amount = extent.second - extent.first;
1614 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1616 pl->paste (*p, pos, ctx.times);
1618 vector<Command*> cmds;
1620 _session->add_commands (cmds);
1622 _session->add_command (new StatefulDiffCommand (pl));
1628 struct PlaylistSorter {
1629 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1630 return a->sort_id() < b->sort_id();
1635 RouteTimeAxisView::build_playlist_menu ()
1637 using namespace Menu_Helpers;
1643 delete playlist_action_menu;
1644 playlist_action_menu = new Menu;
1645 playlist_action_menu->set_name ("ArdourContextMenu");
1647 MenuList& playlist_items = playlist_action_menu->items();
1648 playlist_action_menu->set_name ("ArdourContextMenu");
1649 playlist_items.clear();
1651 RadioMenuItem::Group playlist_group;
1652 boost::shared_ptr<Track> tr = track ();
1654 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1656 /* sort the playlists */
1658 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1660 /* add the playlists to the menu */
1661 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1662 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1663 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1664 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1666 if (tr->playlist()->id() == (*i)->id()) {
1672 playlist_items.push_back (SeparatorElem());
1673 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1674 playlist_items.push_back (SeparatorElem());
1676 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1677 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1678 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1681 // Use a label which tells the user what is happening
1682 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1683 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1687 playlist_items.push_back (SeparatorElem());
1688 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1689 playlist_items.push_back (SeparatorElem());
1691 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1695 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1697 assert (is_track());
1699 // exit if we were triggered by deactivating the old playlist
1700 if (!item->get_active()) {
1704 boost::shared_ptr<Playlist> pl (wpl.lock());
1710 if (track()->playlist() == pl) {
1711 // exit when use_playlist is called by the creation of the playlist menu
1712 // or the playlist choice is unchanged
1716 track()->use_playlist (pl);
1718 RouteGroup* rg = route_group();
1720 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1721 std::string group_string = "." + rg->name() + ".";
1723 std::string take_name = pl->name();
1724 std::string::size_type idx = take_name.find(group_string);
1726 if (idx == std::string::npos)
1729 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1731 boost::shared_ptr<RouteList> rl (rg->route_list());
1733 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1734 if ((*i) == this->route()) {
1738 std::string playlist_name = (*i)->name()+group_string+take_name;
1740 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1745 if (track->freeze_state() == Track::Frozen) {
1746 /* Don't change playlists of frozen tracks */
1750 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1752 // No playlist for this track for this take yet, make it
1753 track->use_new_playlist();
1754 track->playlist()->set_name(playlist_name);
1756 track->use_playlist(ipl);
1763 RouteTimeAxisView::update_playlist_tip ()
1765 RouteGroup* rg = route_group ();
1766 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1767 string group_string = "." + rg->name() + ".";
1769 string take_name = track()->playlist()->name();
1770 string::size_type idx = take_name.find(group_string);
1772 if (idx != string::npos) {
1773 /* find the bit containing the take number / name */
1774 take_name = take_name.substr (idx + group_string.length());
1776 /* set the playlist button tooltip to the take name */
1777 ARDOUR_UI::instance()->set_tip (
1779 string_compose(_("Take: %1.%2"),
1780 Glib::Markup::escape_text(rg->name()),
1781 Glib::Markup::escape_text(take_name))
1788 /* set the playlist button tooltip to the playlist name */
1789 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1794 RouteTimeAxisView::show_playlist_selector ()
1796 _editor.playlist_selector().show_for (this);
1800 RouteTimeAxisView::map_frozen ()
1806 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1808 switch (track()->freeze_state()) {
1810 playlist_button.set_sensitive (false);
1811 rec_enable_button->set_sensitive (false);
1814 playlist_button.set_sensitive (true);
1815 rec_enable_button->set_sensitive (true);
1821 RouteTimeAxisView::color_handler ()
1823 //case cTimeStretchOutline:
1824 if (timestretch_rect) {
1825 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1827 //case cTimeStretchFill:
1828 if (timestretch_rect) {
1829 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1835 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1836 * Will add track if necessary.
1839 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1841 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1842 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1845 /* it doesn't exist yet, so we don't care about the button state: just add it */
1846 create_automation_child (param, true);
1849 bool yn = menu->get_active();
1850 bool changed = false;
1852 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1854 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1855 will have done that for us.
1858 if (changed && !no_redraw) {
1866 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1868 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1874 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1876 if (menu && !_hidden) {
1877 ignore_toggle = true;
1878 menu->set_active (false);
1879 ignore_toggle = false;
1882 if (_route && !no_redraw) {
1888 RouteTimeAxisView::update_gain_track_visibility ()
1890 bool const showit = gain_automation_item->get_active();
1892 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1893 gain_track->set_marked_for_display (showit);
1895 /* now trigger a redisplay */
1898 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1904 RouteTimeAxisView::update_mute_track_visibility ()
1906 bool const showit = mute_automation_item->get_active();
1908 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1909 mute_track->set_marked_for_display (showit);
1911 /* now trigger a redisplay */
1914 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1920 RouteTimeAxisView::update_pan_track_visibility ()
1922 bool const showit = pan_automation_item->get_active();
1923 bool changed = false;
1925 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1926 if ((*i)->set_marked_for_display (showit)) {
1932 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1937 RouteTimeAxisView::ensure_pan_views (bool show)
1939 bool changed = false;
1940 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1942 (*i)->set_marked_for_display (false);
1945 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1949 if (!_route->panner()) {
1953 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1954 set<Evoral::Parameter>::iterator p;
1956 for (p = params.begin(); p != params.end(); ++p) {
1957 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1959 if (pan_control->parameter().type() == NullAutomation) {
1960 error << "Pan control has NULL automation type!" << endmsg;
1964 if (automation_child (pan_control->parameter ()).get () == 0) {
1966 /* we don't already have an AutomationTimeAxisView for this parameter */
1968 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1970 boost::shared_ptr<AutomationTimeAxisView> t (
1971 new AutomationTimeAxisView (_session,
1975 pan_control->parameter (),
1983 pan_tracks.push_back (t);
1984 add_automation_child (*p, t, show);
1986 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1993 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1995 if (apply_to_selection) {
1996 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2000 /* Show our automation */
2002 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2003 i->second->set_marked_for_display (true);
2005 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2008 menu->set_active(true);
2013 /* Show processor automation */
2015 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2016 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2017 if ((*ii)->view == 0) {
2018 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2021 (*ii)->menu_item->set_active (true);
2034 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2036 if (apply_to_selection) {
2037 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2041 /* Show our automation */
2043 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2044 if (i->second->has_automation()) {
2045 i->second->set_marked_for_display (true);
2047 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2049 menu->set_active(true);
2054 /* Show processor automation */
2056 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2057 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2058 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2059 (*ii)->menu_item->set_active (true);
2071 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2073 if (apply_to_selection) {
2074 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2078 /* Hide our automation */
2080 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2081 i->second->set_marked_for_display (false);
2083 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2086 menu->set_active (false);
2090 /* Hide processor automation */
2092 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2093 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2094 (*ii)->menu_item->set_active (false);
2105 RouteTimeAxisView::region_view_added (RegionView* rv)
2107 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2108 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2109 boost::shared_ptr<AutomationTimeAxisView> atv;
2111 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2116 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2117 (*i)->add_ghost(rv);
2121 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2123 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2129 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2131 parent.remove_processor_automation_node (this);
2135 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2138 remove_child (pan->view);
2142 RouteTimeAxisView::ProcessorAutomationNode*
2143 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2145 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2147 if ((*i)->processor == processor) {
2149 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2150 if ((*ii)->what == what) {
2160 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2162 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2165 ProcessorAutomationNode* pan;
2167 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2168 /* session state may never have been saved with new plugin */
2169 error << _("programming error: ")
2170 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2171 processor->name(), what.type(), (int) what.channel(), what.id() )
2173 abort(); /*NOTREACHED*/
2181 boost::shared_ptr<AutomationControl> control
2182 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2184 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2185 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2186 _editor, *this, false, parent_canvas,
2187 processor->describe_parameter (what), processor->name()));
2189 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2191 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2194 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2199 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2202 pan->menu_item->set_active (false);
2211 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2213 boost::shared_ptr<Processor> processor (p.lock ());
2215 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2216 /* The Amp processor is a special case and is dealt with separately */
2220 set<Evoral::Parameter> existing;
2222 processor->what_has_data (existing);
2224 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2226 Evoral::Parameter param (*i);
2227 boost::shared_ptr<AutomationLine> al;
2229 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2232 add_processor_automation_curve (processor, param);
2238 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2240 using namespace Menu_Helpers;
2244 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2246 _automation_tracks[param] = track;
2248 /* existing state overrides "show" argument */
2249 string s = track->gui_property ("visible");
2251 show = string_is_affirmative (s);
2254 /* this might or might not change the visibility status, so don't rely on it */
2255 track->set_marked_for_display (show);
2257 if (show && !no_redraw) {
2261 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2262 /* MIDI-related parameters are always in the menu, there's no
2263 reason to rebuild the menu just because we added a automation
2264 lane for one of them. But if we add a non-MIDI automation
2265 lane, then we need to invalidate the display menu.
2267 delete display_menu;
2273 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2275 boost::shared_ptr<Processor> processor (p.lock ());
2277 if (!processor || !processor->display_to_user ()) {
2281 /* we use this override to veto the Amp processor from the plugin menu,
2282 as its automation lane can be accessed using the special "Fader" menu
2286 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2290 using namespace Menu_Helpers;
2291 ProcessorAutomationInfo *rai;
2292 list<ProcessorAutomationInfo*>::iterator x;
2294 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2296 if (automatable.empty()) {
2300 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2301 if ((*x)->processor == processor) {
2306 if (x == processor_automation.end()) {
2308 rai = new ProcessorAutomationInfo (processor);
2309 processor_automation.push_back (rai);
2317 /* any older menu was deleted at the top of processors_changed()
2318 when we cleared the subplugin menu.
2321 rai->menu = manage (new Menu);
2322 MenuList& items = rai->menu->items();
2323 rai->menu->set_name ("ArdourContextMenu");
2327 std::set<Evoral::Parameter> has_visible_automation;
2328 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2330 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2332 ProcessorAutomationNode* pan;
2333 Gtk::CheckMenuItem* mitem;
2335 string name = processor->describe_parameter (*i);
2337 items.push_back (CheckMenuElem (name));
2338 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2340 _subplugin_menu_map[*i] = mitem;
2342 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2343 mitem->set_active(true);
2346 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2350 pan = new ProcessorAutomationNode (*i, mitem, *this);
2352 rai->lines.push_back (pan);
2356 pan->menu_item = mitem;
2360 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2363 /* add the menu for this processor, because the subplugin
2364 menu is always cleared at the top of processors_changed().
2365 this is the result of some poor design in gtkmm and/or
2369 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2374 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2375 RouteTimeAxisView::ProcessorAutomationNode* pan)
2377 bool showit = pan->menu_item->get_active();
2378 bool redraw = false;
2380 if (pan->view == 0 && showit) {
2381 add_processor_automation_curve (rai->processor, pan->what);
2385 if (pan->view && pan->view->set_marked_for_display (showit)) {
2389 if (redraw && !no_redraw) {
2395 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2397 if (c.type == RouteProcessorChange::MeterPointChange) {
2398 /* nothing to do if only the meter point has changed */
2402 using namespace Menu_Helpers;
2404 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2405 (*i)->valid = false;
2408 setup_processor_menu_and_curves ();
2410 bool deleted_processor_automation = false;
2412 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2414 list<ProcessorAutomationInfo*>::iterator tmp;
2422 processor_automation.erase (i);
2423 deleted_processor_automation = true;
2430 if (deleted_processor_automation && !no_redraw) {
2435 boost::shared_ptr<AutomationLine>
2436 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2438 ProcessorAutomationNode* pan;
2440 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2446 return boost::shared_ptr<AutomationLine>();
2450 RouteTimeAxisView::reset_processor_automation_curves ()
2452 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2458 RouteTimeAxisView::can_edit_name () const
2460 /* we do not allow track name changes if it is record enabled
2462 return !_route->record_enabled();
2466 RouteTimeAxisView::blink_rec_display (bool onoff)
2468 RouteUI::blink_rec_display (onoff);
2472 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2474 if (_ignore_set_layer_display) {
2478 if (apply_to_selection) {
2479 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2483 _view->set_layer_display (d);
2486 set_gui_property (X_("layer-display"), enum_2_string (d));
2491 RouteTimeAxisView::layer_display () const
2494 return _view->layer_display ();
2497 /* we don't know, since we don't have a _view, so just return something */
2503 boost::shared_ptr<AutomationTimeAxisView>
2504 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2506 AutomationTracks::iterator i = _automation_tracks.find(param);
2507 if (i != _automation_tracks.end()) {
2510 return boost::shared_ptr<AutomationTimeAxisView>();
2515 RouteTimeAxisView::fast_update ()
2517 gm.get_level_meter().update_meters ();
2521 RouteTimeAxisView::hide_meter ()
2524 gm.get_level_meter().hide_meters ();
2528 RouteTimeAxisView::show_meter ()
2534 RouteTimeAxisView::reset_meter ()
2536 if (ARDOUR_UI::config()->get_show_track_meters()) {
2537 int meter_width = 3;
2538 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2541 gm.get_level_meter().setup_meters (height - 9, meter_width);
2548 RouteTimeAxisView::clear_meter ()
2550 gm.get_level_meter().clear_meters ();
2554 RouteTimeAxisView::meter_changed ()
2556 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2558 if (_route && !no_redraw) {
2561 // reset peak when meter point changes
2562 gm.reset_peak_display();
2566 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2569 if (_route && !no_redraw) {
2575 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2577 using namespace Menu_Helpers;
2579 if (!_underlay_streams.empty()) {
2580 MenuList& parent_items = parent_menu->items();
2581 Menu* gs_menu = manage (new Menu);
2582 gs_menu->set_name ("ArdourContextMenu");
2583 MenuList& gs_items = gs_menu->items();
2585 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2587 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2588 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2589 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2595 RouteTimeAxisView::set_underlay_state()
2597 if (!underlay_xml_node) {
2601 XMLNodeList nlist = underlay_xml_node->children();
2602 XMLNodeConstIterator niter;
2603 XMLNode *child_node;
2605 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2606 child_node = *niter;
2608 if (child_node->name() != "Underlay") {
2612 XMLProperty* prop = child_node->property ("id");
2614 PBD::ID id (prop->value());
2616 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2619 add_underlay(v->view(), false);
2628 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2634 RouteTimeAxisView& other = v->trackview();
2636 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2637 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2638 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2639 abort(); /*NOTREACHED*/
2642 _underlay_streams.push_back(v);
2643 other._underlay_mirrors.push_back(this);
2645 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2647 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2649 if (!underlay_xml_node) {
2650 underlay_xml_node = xml_node->add_child("Underlays");
2653 XMLNode* node = underlay_xml_node->add_child("Underlay");
2654 XMLProperty* prop = node->add_property("id");
2655 prop->set_value(v->trackview().route()->id().to_s());
2662 RouteTimeAxisView::remove_underlay (StreamView* v)
2668 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2669 RouteTimeAxisView& other = v->trackview();
2671 if (it != _underlay_streams.end()) {
2672 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2674 if (gm == other._underlay_mirrors.end()) {
2675 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2676 abort(); /*NOTREACHED*/
2679 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2681 _underlay_streams.erase(it);
2682 other._underlay_mirrors.erase(gm);
2684 if (underlay_xml_node) {
2685 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2691 RouteTimeAxisView::set_button_names ()
2693 if (_route && _route->solo_safe()) {
2694 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2696 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2698 if (Config->get_solo_control_is_listen_control()) {
2699 switch (Config->get_listen_position()) {
2700 case AfterFaderListen:
2701 solo_button->set_text (_("A"));
2702 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2704 case PreFaderListen:
2705 solo_button->set_text (_("P"));
2706 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2710 solo_button->set_text (_("S"));
2711 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2713 mute_button->set_text (_("M"));
2717 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2719 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2720 if (i != _main_automation_menu_map.end()) {
2724 i = _subplugin_menu_map.find (param);
2725 if (i != _subplugin_menu_map.end()) {
2733 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2735 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2737 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2741 gain_track.reset (new AutomationTimeAxisView (_session,
2742 _route, _route->amp(), c, param,
2747 _route->amp()->describe_parameter(param)));
2750 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2753 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2757 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2759 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2761 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2765 mute_track.reset (new AutomationTimeAxisView (_session,
2766 _route, _route, c, param,
2771 _route->describe_parameter(param)));
2774 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2777 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2781 void add_region_to_list (RegionView* rv, RegionList* l)
2783 l->push_back (rv->region());
2787 RouteTimeAxisView::combine_regions ()
2789 /* as of may 2011, we do not offer uncombine for MIDI tracks
2792 if (!is_audio_track()) {
2800 RegionList selected_regions;
2801 boost::shared_ptr<Playlist> playlist = track()->playlist();
2803 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2805 if (selected_regions.size() < 2) {
2809 playlist->clear_changes ();
2810 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2812 _session->add_command (new StatefulDiffCommand (playlist));
2813 /* make the new region be selected */
2815 return _view->find_view (compound_region);
2819 RouteTimeAxisView::uncombine_regions ()
2821 /* as of may 2011, we do not offer uncombine for MIDI tracks
2823 if (!is_audio_track()) {
2831 RegionList selected_regions;
2832 boost::shared_ptr<Playlist> playlist = track()->playlist();
2834 /* have to grab selected regions first because the uncombine is going
2835 * to change that in the middle of the list traverse
2838 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2840 playlist->clear_changes ();
2842 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2843 playlist->uncombine (*i);
2846 _session->add_command (new StatefulDiffCommand (playlist));
2850 RouteTimeAxisView::state_id() const
2852 return string_compose ("rtav %1", _route->id().to_s());
2857 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2859 TimeAxisView::remove_child (c);
2861 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2863 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2864 if (i->second == a) {
2865 _automation_tracks.erase (i);