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 (S_("RTAV|G"))
104 , playlist_button (S_("RTAV|P"))
105 , automation_button (S_("RTAV|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 items.push_front (SeparatorElem());
856 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
860 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
862 if (apply_to_selection) {
863 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
866 bool needs_bounce = false;
868 if (!track()->can_use_mode (mode, needs_bounce)) {
874 cerr << "would bounce this one\n";
879 track()->set_mode (mode);
884 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
886 TimeAxisView::show_timestretch (start, end, layers, layer);
896 /* check that the time selection was made in our route, or our route group.
897 remember that route_group() == 0 implies the route is *not* in a edit group.
900 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
901 /* this doesn't apply to us */
905 /* ignore it if our edit group is not active */
907 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
912 if (timestretch_rect == 0) {
913 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
914 timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
915 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
918 timestretch_rect->show ();
919 timestretch_rect->raise_to_top ();
921 double const x1 = start / _editor.get_current_zoom();
922 double const x2 = (end - 1) / _editor.get_current_zoom();
924 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
925 x2, current_height() * (layers - layer) / layers));
929 RouteTimeAxisView::hide_timestretch ()
931 TimeAxisView::hide_timestretch ();
933 if (timestretch_rect) {
934 timestretch_rect->hide ();
939 RouteTimeAxisView::show_selection (TimeSelection& ts)
943 /* ignore it if our edit group is not active or if the selection was started
944 in some other track or route group (remember that route_group() == 0 means
945 that the track is not in an route group).
948 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
949 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
955 TimeAxisView::show_selection (ts);
959 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
962 bool height_changed = (height == 0) || (h != height);
965 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
968 gm.get_level_meter().setup_meters (gmlen, meter_width);
970 TimeAxisView::set_height (h, m);
973 _view->set_height ((double) current_height());
976 if (height >= preset_height (HeightNormal)) {
980 gm.get_gain_slider().show();
982 if (!_route || _route->is_monitor()) {
987 if (rec_enable_button)
988 rec_enable_button->show();
990 route_group_button.show();
991 automation_button.show();
993 if (is_track() && track()->mode() == ARDOUR::Normal) {
994 playlist_button.show();
1001 gm.get_gain_slider().hide();
1002 mute_button->show();
1003 if (!_route || _route->is_monitor()) {
1004 solo_button->hide();
1006 solo_button->show();
1008 if (rec_enable_button)
1009 rec_enable_button->show();
1011 route_group_button.hide ();
1012 automation_button.hide ();
1014 if (is_track() && track()->mode() == ARDOUR::Normal) {
1015 playlist_button.hide ();
1020 if (height_changed && !no_redraw) {
1021 /* only emit the signal if the height really changed */
1027 RouteTimeAxisView::route_color_changed ()
1030 _view->apply_color (color(), StreamView::RegionColor);
1033 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1037 RouteTimeAxisView::reset_samples_per_pixel ()
1039 set_samples_per_pixel (_editor.get_current_zoom());
1043 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1048 speed = track()->speed();
1052 _view->set_samples_per_pixel (fpp * speed);
1055 TimeAxisView::set_samples_per_pixel (fpp * speed);
1059 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1061 if (!mitem->get_active()) {
1062 /* this is one of the two calls made when these radio menu items change status. this one
1063 is for the item that became inactive, and we want to ignore it.
1068 if (apply_to_selection) {
1069 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1072 track()->set_align_choice (choice);
1078 RouteTimeAxisView::rename_current_playlist ()
1080 ArdourPrompter prompter (true);
1083 boost::shared_ptr<Track> tr = track();
1084 if (!tr || tr->destructive()) {
1088 boost::shared_ptr<Playlist> pl = tr->playlist();
1093 prompter.set_title (_("Rename Playlist"));
1094 prompter.set_prompt (_("New name for playlist:"));
1095 prompter.set_initial_text (pl->name());
1096 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1097 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1099 switch (prompter.run ()) {
1100 case Gtk::RESPONSE_ACCEPT:
1101 prompter.get_result (name);
1102 if (name.length()) {
1103 pl->set_name (name);
1113 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1115 std::string ret (basename);
1117 std::string const group_string = "." + route_group()->name() + ".";
1119 // iterate through all playlists
1121 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1122 std::string tmp = (*i)->name();
1124 std::string::size_type idx = tmp.find(group_string);
1125 // find those which belong to this group
1126 if (idx != string::npos) {
1127 tmp = tmp.substr(idx + group_string.length());
1129 // and find the largest current number
1131 if (x > maxnumber) {
1140 snprintf (buf, sizeof(buf), "%d", maxnumber);
1142 ret = this->name() + "." + route_group()->name () + "." + buf;
1148 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1152 boost::shared_ptr<Track> tr = track ();
1153 if (!tr || tr->destructive()) {
1157 boost::shared_ptr<const Playlist> pl = tr->playlist();
1164 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1165 name = resolve_new_group_playlist_name(name, playlists_before_op);
1168 while (_session->playlists->by_name(name)) {
1169 name = Playlist::bump_name (name, *_session);
1172 // TODO: The prompter "new" button should be de-activated if the user
1173 // specifies a playlist name which already exists in the session.
1177 ArdourPrompter prompter (true);
1179 prompter.set_title (_("New Copy Playlist"));
1180 prompter.set_prompt (_("Name for new playlist:"));
1181 prompter.set_initial_text (name);
1182 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1183 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1184 prompter.show_all ();
1186 switch (prompter.run ()) {
1187 case Gtk::RESPONSE_ACCEPT:
1188 prompter.get_result (name);
1196 if (name.length()) {
1197 tr->use_copy_playlist ();
1198 tr->playlist()->set_name (name);
1203 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1207 boost::shared_ptr<Track> tr = track ();
1208 if (!tr || tr->destructive()) {
1212 boost::shared_ptr<const Playlist> pl = tr->playlist();
1219 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1220 name = resolve_new_group_playlist_name(name,playlists_before_op);
1223 while (_session->playlists->by_name(name)) {
1224 name = Playlist::bump_name (name, *_session);
1230 ArdourPrompter prompter (true);
1232 prompter.set_title (_("New Playlist"));
1233 prompter.set_prompt (_("Name for new playlist:"));
1234 prompter.set_initial_text (name);
1235 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1236 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1238 switch (prompter.run ()) {
1239 case Gtk::RESPONSE_ACCEPT:
1240 prompter.get_result (name);
1248 if (name.length()) {
1249 tr->use_new_playlist ();
1250 tr->playlist()->set_name (name);
1255 RouteTimeAxisView::clear_playlist ()
1257 boost::shared_ptr<Track> tr = track ();
1258 if (!tr || tr->destructive()) {
1262 boost::shared_ptr<Playlist> pl = tr->playlist();
1267 _editor.clear_playlist (pl);
1271 RouteTimeAxisView::speed_changed ()
1273 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1277 RouteTimeAxisView::update_diskstream_display ()
1287 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1289 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1291 /* special case: select/deselect all tracks */
1293 _editor.begin_reversible_selection_op (X_("Selection Click"));
1295 if (_editor.get_selection().selected (this)) {
1296 _editor.get_selection().clear_tracks ();
1298 _editor.select_all_tracks ();
1301 _editor.commit_reversible_selection_op ();
1306 _editor.begin_reversible_selection_op (X_("Selection Click"));
1308 switch (ArdourKeyboard::selection_type (ev->state)) {
1309 case Selection::Toggle:
1310 _editor.get_selection().toggle (this);
1313 case Selection::Set:
1314 _editor.get_selection().set (this);
1317 case Selection::Extend:
1318 _editor.extend_selection_to_track (*this);
1321 case Selection::Add:
1322 _editor.get_selection().add (this);
1326 _editor.commit_reversible_selection_op ();
1330 RouteTimeAxisView::set_selected_points (PointSelection& points)
1332 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1333 (*i)->set_selected_points (points);
1338 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1341 _view->set_selected_regionviews (regions);
1345 /** Add the selectable things that we have to a list.
1346 * @param results List to add things to.
1349 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1354 speed = track()->speed();
1357 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1358 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1360 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1361 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1364 /* pick up visible automation tracks */
1366 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1367 if (!(*i)->hidden()) {
1368 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1374 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1377 _view->get_inverted_selectables (sel, results);
1380 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1381 if (!(*i)->hidden()) {
1382 (*i)->get_inverted_selectables (sel, results);
1390 RouteTimeAxisView::route_group () const
1392 return _route->route_group();
1396 RouteTimeAxisView::name() const
1398 return _route->name();
1401 boost::shared_ptr<Playlist>
1402 RouteTimeAxisView::playlist () const
1404 boost::shared_ptr<Track> tr;
1406 if ((tr = track()) != 0) {
1407 return tr->playlist();
1409 return boost::shared_ptr<Playlist> ();
1414 RouteTimeAxisView::name_entry_changed ()
1416 TimeAxisView::name_entry_changed ();
1418 string x = name_entry->get_text ();
1420 if (x == _route->name()) {
1424 strip_whitespace_edges (x);
1426 if (x.length() == 0) {
1427 name_entry->set_text (_route->name());
1431 if (_session->route_name_internal (x)) {
1432 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1434 name_entry->grab_focus ();
1435 } else if (RouteUI::verify_new_route_name (x)) {
1436 _route->set_name (x);
1438 name_entry->grab_focus ();
1442 boost::shared_ptr<Region>
1443 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1445 boost::shared_ptr<Playlist> pl = playlist ();
1448 return pl->find_next_region (pos, point, dir);
1451 return boost::shared_ptr<Region> ();
1455 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1457 boost::shared_ptr<Playlist> pl = playlist ();
1460 return pl->find_next_region_boundary (pos, dir);
1467 RouteTimeAxisView::fade_range (TimeSelection& selection)
1469 boost::shared_ptr<Playlist> what_we_got;
1470 boost::shared_ptr<Track> tr = track ();
1471 boost::shared_ptr<Playlist> playlist;
1474 /* route is a bus, not a track */
1478 playlist = tr->playlist();
1480 TimeSelection time (selection);
1481 float const speed = tr->speed();
1482 if (speed != 1.0f) {
1483 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1484 (*i).start = session_frame_to_track_frame((*i).start, speed);
1485 (*i).end = session_frame_to_track_frame((*i).end, speed);
1489 playlist->clear_changes ();
1490 playlist->clear_owned_changes ();
1492 playlist->fade_range (time);
1494 vector<Command*> cmds;
1495 playlist->rdiff (cmds);
1496 _session->add_commands (cmds);
1497 _session->add_command (new StatefulDiffCommand (playlist));
1502 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1504 boost::shared_ptr<Playlist> what_we_got;
1505 boost::shared_ptr<Track> tr = track ();
1506 boost::shared_ptr<Playlist> playlist;
1509 /* route is a bus, not a track */
1513 playlist = tr->playlist();
1515 TimeSelection time (selection.time);
1516 float const speed = tr->speed();
1517 if (speed != 1.0f) {
1518 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1519 (*i).start = session_frame_to_track_frame((*i).start, speed);
1520 (*i).end = session_frame_to_track_frame((*i).end, speed);
1524 playlist->clear_changes ();
1525 playlist->clear_owned_changes ();
1529 if (playlist->cut (time) != 0) {
1530 if (Config->get_edit_mode() == Ripple)
1531 playlist->ripple(time.start(), -time.length(), NULL);
1532 // no need to exclude any regions from rippling here
1534 vector<Command*> cmds;
1535 playlist->rdiff (cmds);
1536 _session->add_commands (cmds);
1538 _session->add_command (new StatefulDiffCommand (playlist));
1543 if ((what_we_got = playlist->cut (time)) != 0) {
1544 _editor.get_cut_buffer().add (what_we_got);
1545 if (Config->get_edit_mode() == Ripple)
1546 playlist->ripple(time.start(), -time.length(), NULL);
1547 // no need to exclude any regions from rippling here
1549 vector<Command*> cmds;
1550 playlist->rdiff (cmds);
1551 _session->add_commands (cmds);
1553 _session->add_command (new StatefulDiffCommand (playlist));
1557 if ((what_we_got = playlist->copy (time)) != 0) {
1558 _editor.get_cut_buffer().add (what_we_got);
1563 if ((what_we_got = playlist->cut (time)) != 0) {
1564 if (Config->get_edit_mode() == Ripple)
1565 playlist->ripple(time.start(), -time.length(), NULL);
1566 // no need to exclude any regions from rippling here
1568 vector<Command*> cmds;
1569 playlist->rdiff (cmds);
1570 _session->add_commands (cmds);
1571 _session->add_command (new StatefulDiffCommand (playlist));
1572 what_we_got->release ();
1579 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1585 boost::shared_ptr<Playlist> pl = playlist ();
1586 const ARDOUR::DataType type = pl->data_type();
1587 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1589 if (p == selection.playlists.end()) {
1592 ctx.counts.increase_n_playlists(type);
1594 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1596 if (track()->speed() != 1.0f) {
1597 pos = session_frame_to_track_frame (pos, track()->speed());
1598 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1601 /* add multi-paste offset if applicable */
1602 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1603 const framecnt_t duration = extent.second - extent.first;
1604 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1606 pl->clear_changes ();
1607 if (Config->get_edit_mode() == Ripple) {
1608 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1609 framecnt_t amount = extent.second - extent.first;
1610 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1612 pl->paste (*p, pos, ctx.times);
1614 vector<Command*> cmds;
1616 _session->add_commands (cmds);
1618 _session->add_command (new StatefulDiffCommand (pl));
1624 struct PlaylistSorter {
1625 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1626 return a->sort_id() < b->sort_id();
1631 RouteTimeAxisView::build_playlist_menu ()
1633 using namespace Menu_Helpers;
1639 delete playlist_action_menu;
1640 playlist_action_menu = new Menu;
1641 playlist_action_menu->set_name ("ArdourContextMenu");
1643 MenuList& playlist_items = playlist_action_menu->items();
1644 playlist_action_menu->set_name ("ArdourContextMenu");
1645 playlist_items.clear();
1647 RadioMenuItem::Group playlist_group;
1648 boost::shared_ptr<Track> tr = track ();
1650 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1652 /* sort the playlists */
1654 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1656 /* add the playlists to the menu */
1657 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1658 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1659 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1660 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1662 if (tr->playlist()->id() == (*i)->id()) {
1668 playlist_items.push_back (SeparatorElem());
1669 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1670 playlist_items.push_back (SeparatorElem());
1672 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1673 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1674 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1677 // Use a label which tells the user what is happening
1678 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1679 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1683 playlist_items.push_back (SeparatorElem());
1684 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1685 playlist_items.push_back (SeparatorElem());
1687 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1691 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1693 assert (is_track());
1695 // exit if we were triggered by deactivating the old playlist
1696 if (!item->get_active()) {
1700 boost::shared_ptr<Playlist> pl (wpl.lock());
1706 if (track()->playlist() == pl) {
1707 // exit when use_playlist is called by the creation of the playlist menu
1708 // or the playlist choice is unchanged
1712 track()->use_playlist (pl);
1714 RouteGroup* rg = route_group();
1716 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1717 std::string group_string = "." + rg->name() + ".";
1719 std::string take_name = pl->name();
1720 std::string::size_type idx = take_name.find(group_string);
1722 if (idx == std::string::npos)
1725 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1727 boost::shared_ptr<RouteList> rl (rg->route_list());
1729 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1730 if ((*i) == this->route()) {
1734 std::string playlist_name = (*i)->name()+group_string+take_name;
1736 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1741 if (track->freeze_state() == Track::Frozen) {
1742 /* Don't change playlists of frozen tracks */
1746 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1748 // No playlist for this track for this take yet, make it
1749 track->use_new_playlist();
1750 track->playlist()->set_name(playlist_name);
1752 track->use_playlist(ipl);
1759 RouteTimeAxisView::update_playlist_tip ()
1761 RouteGroup* rg = route_group ();
1762 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1763 string group_string = "." + rg->name() + ".";
1765 string take_name = track()->playlist()->name();
1766 string::size_type idx = take_name.find(group_string);
1768 if (idx != string::npos) {
1769 /* find the bit containing the take number / name */
1770 take_name = take_name.substr (idx + group_string.length());
1772 /* set the playlist button tooltip to the take name */
1773 ARDOUR_UI::instance()->set_tip (
1775 string_compose(_("Take: %1.%2"),
1776 Glib::Markup::escape_text(rg->name()),
1777 Glib::Markup::escape_text(take_name))
1784 /* set the playlist button tooltip to the playlist name */
1785 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1790 RouteTimeAxisView::show_playlist_selector ()
1792 _editor.playlist_selector().show_for (this);
1796 RouteTimeAxisView::map_frozen ()
1802 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1804 switch (track()->freeze_state()) {
1806 playlist_button.set_sensitive (false);
1807 rec_enable_button->set_sensitive (false);
1810 playlist_button.set_sensitive (true);
1811 rec_enable_button->set_sensitive (true);
1817 RouteTimeAxisView::color_handler ()
1819 //case cTimeStretchOutline:
1820 if (timestretch_rect) {
1821 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1823 //case cTimeStretchFill:
1824 if (timestretch_rect) {
1825 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1831 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1832 * Will add track if necessary.
1835 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1837 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1838 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1841 /* it doesn't exist yet, so we don't care about the button state: just add it */
1842 create_automation_child (param, true);
1845 bool yn = menu->get_active();
1846 bool changed = false;
1848 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1850 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1851 will have done that for us.
1854 if (changed && !no_redraw) {
1862 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1864 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1870 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1872 if (menu && !_hidden) {
1873 ignore_toggle = true;
1874 menu->set_active (false);
1875 ignore_toggle = false;
1878 if (_route && !no_redraw) {
1884 RouteTimeAxisView::update_gain_track_visibility ()
1886 bool const showit = gain_automation_item->get_active();
1888 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1889 gain_track->set_marked_for_display (showit);
1891 /* now trigger a redisplay */
1894 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1900 RouteTimeAxisView::update_mute_track_visibility ()
1902 bool const showit = mute_automation_item->get_active();
1904 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1905 mute_track->set_marked_for_display (showit);
1907 /* now trigger a redisplay */
1910 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1916 RouteTimeAxisView::update_pan_track_visibility ()
1918 bool const showit = pan_automation_item->get_active();
1919 bool changed = false;
1921 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1922 if ((*i)->set_marked_for_display (showit)) {
1928 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1933 RouteTimeAxisView::ensure_pan_views (bool show)
1935 bool changed = false;
1936 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1938 (*i)->set_marked_for_display (false);
1941 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1945 if (!_route->panner()) {
1949 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1950 set<Evoral::Parameter>::iterator p;
1952 for (p = params.begin(); p != params.end(); ++p) {
1953 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1955 if (pan_control->parameter().type() == NullAutomation) {
1956 error << "Pan control has NULL automation type!" << endmsg;
1960 if (automation_child (pan_control->parameter ()).get () == 0) {
1962 /* we don't already have an AutomationTimeAxisView for this parameter */
1964 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1966 boost::shared_ptr<AutomationTimeAxisView> t (
1967 new AutomationTimeAxisView (_session,
1971 pan_control->parameter (),
1979 pan_tracks.push_back (t);
1980 add_automation_child (*p, t, show);
1982 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1989 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1991 if (apply_to_selection) {
1992 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1996 /* Show our automation */
1998 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1999 i->second->set_marked_for_display (true);
2001 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2004 menu->set_active(true);
2009 /* Show processor automation */
2011 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2012 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2013 if ((*ii)->view == 0) {
2014 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2017 (*ii)->menu_item->set_active (true);
2030 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2032 if (apply_to_selection) {
2033 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2037 /* Show our automation */
2039 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2040 if (i->second->has_automation()) {
2041 i->second->set_marked_for_display (true);
2043 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2045 menu->set_active(true);
2050 /* Show processor automation */
2052 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2053 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2054 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2055 (*ii)->menu_item->set_active (true);
2067 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2069 if (apply_to_selection) {
2070 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2074 /* Hide our automation */
2076 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2077 i->second->set_marked_for_display (false);
2079 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2082 menu->set_active (false);
2086 /* Hide processor automation */
2088 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2089 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2090 (*ii)->menu_item->set_active (false);
2101 RouteTimeAxisView::region_view_added (RegionView* rv)
2103 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2104 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2105 boost::shared_ptr<AutomationTimeAxisView> atv;
2107 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2112 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2113 (*i)->add_ghost(rv);
2117 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2119 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2125 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2127 parent.remove_processor_automation_node (this);
2131 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2134 remove_child (pan->view);
2138 RouteTimeAxisView::ProcessorAutomationNode*
2139 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2141 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2143 if ((*i)->processor == processor) {
2145 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2146 if ((*ii)->what == what) {
2156 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2158 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2161 ProcessorAutomationNode* pan;
2163 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2164 /* session state may never have been saved with new plugin */
2165 error << _("programming error: ")
2166 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2167 processor->name(), what.type(), (int) what.channel(), what.id() )
2169 abort(); /*NOTREACHED*/
2177 boost::shared_ptr<AutomationControl> control
2178 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2180 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2181 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2182 _editor, *this, false, parent_canvas,
2183 processor->describe_parameter (what), processor->name()));
2185 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2187 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2190 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2195 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2198 pan->menu_item->set_active (false);
2207 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2209 boost::shared_ptr<Processor> processor (p.lock ());
2211 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2212 /* The Amp processor is a special case and is dealt with separately */
2216 set<Evoral::Parameter> existing;
2218 processor->what_has_data (existing);
2220 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2222 Evoral::Parameter param (*i);
2223 boost::shared_ptr<AutomationLine> al;
2225 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2228 add_processor_automation_curve (processor, param);
2234 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2236 using namespace Menu_Helpers;
2240 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2242 _automation_tracks[param] = track;
2244 /* existing state overrides "show" argument */
2245 string s = track->gui_property ("visible");
2247 show = string_is_affirmative (s);
2250 /* this might or might not change the visibility status, so don't rely on it */
2251 track->set_marked_for_display (show);
2253 if (show && !no_redraw) {
2257 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2258 /* MIDI-related parameters are always in the menu, there's no
2259 reason to rebuild the menu just because we added a automation
2260 lane for one of them. But if we add a non-MIDI automation
2261 lane, then we need to invalidate the display menu.
2263 delete display_menu;
2269 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2271 boost::shared_ptr<Processor> processor (p.lock ());
2273 if (!processor || !processor->display_to_user ()) {
2277 /* we use this override to veto the Amp processor from the plugin menu,
2278 as its automation lane can be accessed using the special "Fader" menu
2282 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2286 using namespace Menu_Helpers;
2287 ProcessorAutomationInfo *rai;
2288 list<ProcessorAutomationInfo*>::iterator x;
2290 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2292 if (automatable.empty()) {
2296 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2297 if ((*x)->processor == processor) {
2302 if (x == processor_automation.end()) {
2304 rai = new ProcessorAutomationInfo (processor);
2305 processor_automation.push_back (rai);
2313 /* any older menu was deleted at the top of processors_changed()
2314 when we cleared the subplugin menu.
2317 rai->menu = manage (new Menu);
2318 MenuList& items = rai->menu->items();
2319 rai->menu->set_name ("ArdourContextMenu");
2323 std::set<Evoral::Parameter> has_visible_automation;
2324 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2326 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2328 ProcessorAutomationNode* pan;
2329 Gtk::CheckMenuItem* mitem;
2331 string name = processor->describe_parameter (*i);
2333 items.push_back (CheckMenuElem (name));
2334 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2336 _subplugin_menu_map[*i] = mitem;
2338 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2339 mitem->set_active(true);
2342 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2346 pan = new ProcessorAutomationNode (*i, mitem, *this);
2348 rai->lines.push_back (pan);
2352 pan->menu_item = mitem;
2356 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2359 /* add the menu for this processor, because the subplugin
2360 menu is always cleared at the top of processors_changed().
2361 this is the result of some poor design in gtkmm and/or
2365 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2370 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2371 RouteTimeAxisView::ProcessorAutomationNode* pan)
2373 bool showit = pan->menu_item->get_active();
2374 bool redraw = false;
2376 if (pan->view == 0 && showit) {
2377 add_processor_automation_curve (rai->processor, pan->what);
2381 if (pan->view && pan->view->set_marked_for_display (showit)) {
2385 if (redraw && !no_redraw) {
2391 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2393 if (c.type == RouteProcessorChange::MeterPointChange) {
2394 /* nothing to do if only the meter point has changed */
2398 using namespace Menu_Helpers;
2400 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2401 (*i)->valid = false;
2404 setup_processor_menu_and_curves ();
2406 bool deleted_processor_automation = false;
2408 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2410 list<ProcessorAutomationInfo*>::iterator tmp;
2418 processor_automation.erase (i);
2419 deleted_processor_automation = true;
2426 if (deleted_processor_automation && !no_redraw) {
2431 boost::shared_ptr<AutomationLine>
2432 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2434 ProcessorAutomationNode* pan;
2436 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2442 return boost::shared_ptr<AutomationLine>();
2446 RouteTimeAxisView::reset_processor_automation_curves ()
2448 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2454 RouteTimeAxisView::can_edit_name () const
2456 /* we do not allow track name changes if it is record enabled
2458 return !_route->record_enabled();
2462 RouteTimeAxisView::blink_rec_display (bool onoff)
2464 RouteUI::blink_rec_display (onoff);
2468 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2470 if (_ignore_set_layer_display) {
2474 if (apply_to_selection) {
2475 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2479 _view->set_layer_display (d);
2482 set_gui_property (X_("layer-display"), enum_2_string (d));
2487 RouteTimeAxisView::layer_display () const
2490 return _view->layer_display ();
2493 /* we don't know, since we don't have a _view, so just return something */
2499 boost::shared_ptr<AutomationTimeAxisView>
2500 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2502 AutomationTracks::iterator i = _automation_tracks.find(param);
2503 if (i != _automation_tracks.end()) {
2506 return boost::shared_ptr<AutomationTimeAxisView>();
2511 RouteTimeAxisView::fast_update ()
2513 gm.get_level_meter().update_meters ();
2517 RouteTimeAxisView::hide_meter ()
2520 gm.get_level_meter().hide_meters ();
2524 RouteTimeAxisView::show_meter ()
2530 RouteTimeAxisView::reset_meter ()
2532 if (ARDOUR_UI::config()->get_show_track_meters()) {
2533 int meter_width = 3;
2534 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2537 gm.get_level_meter().setup_meters (height - 9, meter_width);
2544 RouteTimeAxisView::clear_meter ()
2546 gm.get_level_meter().clear_meters ();
2550 RouteTimeAxisView::meter_changed ()
2552 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2554 if (_route && !no_redraw) {
2557 // reset peak when meter point changes
2558 gm.reset_peak_display();
2562 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2565 if (_route && !no_redraw) {
2571 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2573 using namespace Menu_Helpers;
2575 if (!_underlay_streams.empty()) {
2576 MenuList& parent_items = parent_menu->items();
2577 Menu* gs_menu = manage (new Menu);
2578 gs_menu->set_name ("ArdourContextMenu");
2579 MenuList& gs_items = gs_menu->items();
2581 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2583 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2584 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2585 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2591 RouteTimeAxisView::set_underlay_state()
2593 if (!underlay_xml_node) {
2597 XMLNodeList nlist = underlay_xml_node->children();
2598 XMLNodeConstIterator niter;
2599 XMLNode *child_node;
2601 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2602 child_node = *niter;
2604 if (child_node->name() != "Underlay") {
2608 XMLProperty* prop = child_node->property ("id");
2610 PBD::ID id (prop->value());
2612 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2615 add_underlay(v->view(), false);
2624 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2630 RouteTimeAxisView& other = v->trackview();
2632 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2633 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2634 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2635 abort(); /*NOTREACHED*/
2638 _underlay_streams.push_back(v);
2639 other._underlay_mirrors.push_back(this);
2641 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2643 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2645 if (!underlay_xml_node) {
2646 underlay_xml_node = xml_node->add_child("Underlays");
2649 XMLNode* node = underlay_xml_node->add_child("Underlay");
2650 XMLProperty* prop = node->add_property("id");
2651 prop->set_value(v->trackview().route()->id().to_s());
2658 RouteTimeAxisView::remove_underlay (StreamView* v)
2664 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2665 RouteTimeAxisView& other = v->trackview();
2667 if (it != _underlay_streams.end()) {
2668 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2670 if (gm == other._underlay_mirrors.end()) {
2671 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2672 abort(); /*NOTREACHED*/
2675 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2677 _underlay_streams.erase(it);
2678 other._underlay_mirrors.erase(gm);
2680 if (underlay_xml_node) {
2681 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2687 RouteTimeAxisView::set_button_names ()
2689 if (_route && _route->solo_safe()) {
2690 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2692 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2694 if (Config->get_solo_control_is_listen_control()) {
2695 switch (Config->get_listen_position()) {
2696 case AfterFaderListen:
2697 solo_button->set_text (S_("AfterFader|A"));
2698 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2700 case PreFaderListen:
2701 solo_button->set_text (S_("PreFader|P"));
2702 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2706 solo_button->set_text (S_("Solo|S"));
2707 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2709 mute_button->set_text (S_("Mute|M"));
2713 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2715 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2716 if (i != _main_automation_menu_map.end()) {
2720 i = _subplugin_menu_map.find (param);
2721 if (i != _subplugin_menu_map.end()) {
2729 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2731 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2733 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2737 gain_track.reset (new AutomationTimeAxisView (_session,
2738 _route, _route->amp(), c, param,
2743 _route->amp()->describe_parameter(param)));
2746 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2749 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2753 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2755 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2757 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2761 mute_track.reset (new AutomationTimeAxisView (_session,
2762 _route, _route, c, param,
2767 _route->describe_parameter(param)));
2770 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2773 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2777 void add_region_to_list (RegionView* rv, RegionList* l)
2779 l->push_back (rv->region());
2783 RouteTimeAxisView::combine_regions ()
2785 /* as of may 2011, we do not offer uncombine for MIDI tracks
2788 if (!is_audio_track()) {
2796 RegionList selected_regions;
2797 boost::shared_ptr<Playlist> playlist = track()->playlist();
2799 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2801 if (selected_regions.size() < 2) {
2805 playlist->clear_changes ();
2806 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2808 _session->add_command (new StatefulDiffCommand (playlist));
2809 /* make the new region be selected */
2811 return _view->find_view (compound_region);
2815 RouteTimeAxisView::uncombine_regions ()
2817 /* as of may 2011, we do not offer uncombine for MIDI tracks
2819 if (!is_audio_track()) {
2827 RegionList selected_regions;
2828 boost::shared_ptr<Playlist> playlist = track()->playlist();
2830 /* have to grab selected regions first because the uncombine is going
2831 * to change that in the middle of the list traverse
2834 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2836 playlist->clear_changes ();
2838 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2839 playlist->uncombine (*i);
2842 _session->add_command (new StatefulDiffCommand (playlist));
2846 RouteTimeAxisView::state_id() const
2848 return string_compose ("rtav %1", _route->id().to_s());
2853 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2855 TimeAxisView::remove_child (c);
2857 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2859 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2860 if (i->second == a) {
2861 _automation_tracks.erase (i);