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);
160 update_solo_display ();
162 timestretch_rect = 0;
165 ignore_toggle = false;
167 route_group_button.set_name ("route button");
168 playlist_button.set_name ("route button");
169 automation_button.set_name ("route button");
171 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
172 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
173 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
177 if (ARDOUR::Profile->get_mixbus()) {
178 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
180 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
183 if (is_midi_track()) {
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
199 Gtk::Fixed *blank = manage(new Gtk::Fixed());
200 controls_button_size_group->add_widget(*blank);
201 if (ARDOUR::Profile->get_mixbus() ) {
202 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
211 if (!ARDOUR::Profile->get_mixbus()) {
212 controls_meters_size_group->add_widget (gm.get_level_meter());
215 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
216 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
217 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
220 if (ARDOUR::Profile->get_mixbus()) {
221 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 // mute button is always present, it is used to
226 // force the 'blank' placeholders to the proper size
227 controls_button_size_group->add_widget(*mute_button);
229 if (!_route->is_master()) {
230 if (ARDOUR::Profile->get_mixbus()) {
231 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 Gtk::Fixed *blank = manage(new Gtk::Fixed());
237 controls_button_size_group->add_widget(*blank);
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 else if (!ARDOUR::Profile->get_trx()) {
251 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
255 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
256 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
257 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
259 mute_button->set_tweaks(ArdourButton::TrackHeader);
260 solo_button->set_tweaks(ArdourButton::TrackHeader);
261 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
262 playlist_button.set_tweaks(ArdourButton::TrackHeader);
263 automation_button.set_tweaks(ArdourButton::TrackHeader);
264 route_group_button.set_tweaks(ArdourButton::TrackHeader);
266 if (is_midi_track()) {
267 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
269 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
272 update_track_number_visibility();
275 if (ARDOUR::Profile->get_mixbus()) {
276 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 else if (!ARDOUR::Profile->get_trx()) {
279 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282 if (is_track() && track()->mode() == ARDOUR::Normal) {
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
294 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
298 str = gui_property ("layer-display");
300 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
303 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
304 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306 /* pick up the correct freeze state */
311 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
312 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314 PropertyList* plist = new PropertyList();
316 plist->add (ARDOUR::Properties::mute, true);
317 plist->add (ARDOUR::Properties::solo, true);
319 route_group_menu = new RouteGroupMenu (_session, plist);
321 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
324 RouteTimeAxisView::~RouteTimeAxisView ()
326 cleanup_gui_properties ();
328 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
332 delete playlist_action_menu;
333 playlist_action_menu = 0;
338 _automation_tracks.clear ();
340 delete route_group_menu;
341 CatchDeletion (this);
345 RouteTimeAxisView::post_construct ()
347 /* map current state of the route */
349 update_diskstream_display ();
350 setup_processor_menu_and_curves ();
351 reset_processor_automation_curves ();
354 /** Set up the processor menu for the current set of processors, and
355 * display automation curves for any parameters which have data.
358 RouteTimeAxisView::setup_processor_menu_and_curves ()
360 _subplugin_menu_map.clear ();
361 subplugin_menu.items().clear ();
362 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
367 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
369 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
370 if (_route->route_group()) {
371 _route->route_group()->remove (_route);
377 r.push_back (route ());
379 route_group_menu->build (r);
380 route_group_menu->menu()->popup (ev->button, ev->time);
386 RouteTimeAxisView::playlist_changed ()
392 RouteTimeAxisView::label_view ()
394 string x = _route->name ();
395 if (x != name_label.get_text ()) {
396 name_label.set_text (x);
398 const int64_t track_number = _route->track_number ();
399 if (track_number == 0) {
400 number_label.set_text ("");
402 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
407 RouteTimeAxisView::update_track_number_visibility ()
410 bool show_label = _session->config.get_track_name_number();
412 if (_route && _route->is_master()) {
416 if (number_label.get_parent()) {
417 controls_table.remove (number_label);
420 if (ARDOUR::Profile->get_mixbus()) {
421 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
425 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
426 // except the width of the number label is subtracted from the name-hbox, so we
427 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
428 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
430 number_label.set_size_request(tnw, -1);
431 number_label.show ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
434 number_label.hide ();
435 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
440 RouteTimeAxisView::parameter_changed (string const & p)
442 if (p == "track-name-number") {
443 update_track_number_visibility();
448 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
450 if (what_changed.contains (ARDOUR::Properties::name)) {
456 RouteTimeAxisView::take_name_changed (void *src)
464 RouteTimeAxisView::playlist_click ()
466 build_playlist_menu ();
467 conditionally_add_to_selection ();
468 playlist_action_menu->popup (1, gtk_get_current_event_time());
472 RouteTimeAxisView::automation_click ()
474 conditionally_add_to_selection ();
475 build_automation_action_menu (false);
476 automation_action_menu->popup (1, gtk_get_current_event_time());
480 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
482 using namespace Menu_Helpers;
484 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
485 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
488 detach_menu (subplugin_menu);
490 _main_automation_menu_map.clear ();
491 delete automation_action_menu;
492 automation_action_menu = new Menu;
494 MenuList& items = automation_action_menu->items();
496 automation_action_menu->set_name ("ArdourContextMenu");
498 items.push_back (MenuElem (_("Show All Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
501 items.push_back (MenuElem (_("Show Existing Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
504 items.push_back (MenuElem (_("Hide All Automation"),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
507 /* Attach the plugin submenu. It may have previously been used elsewhere,
508 so it was detached above
511 if (!subplugin_menu.items().empty()) {
512 items.push_back (SeparatorElem ());
513 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
514 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
517 /* Add any route automation */
520 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
521 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
522 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
523 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
525 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
529 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
530 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
531 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
532 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
534 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
537 if (!pan_tracks.empty()) {
538 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
539 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
540 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
541 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
543 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
544 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
545 _main_automation_menu_map[*p] = pan_automation_item;
551 RouteTimeAxisView::build_display_menu ()
553 using namespace Menu_Helpers;
557 TimeAxisView::build_display_menu ();
559 /* now fill it with our stuff */
561 MenuList& items = display_menu->items();
562 display_menu->set_name ("ArdourContextMenu");
564 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
566 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
568 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
570 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
572 items.push_back (SeparatorElem());
575 detach_menu (*_size_menu);
578 items.push_back (MenuElem (_("Height"), *_size_menu));
580 items.push_back (SeparatorElem());
582 if (!Profile->get_sae()) {
583 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
584 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
585 items.push_back (SeparatorElem());
588 // Hook for derived classes to add type specific stuff
589 append_extra_display_menu_items ();
593 Menu* layers_menu = manage (new Menu);
594 MenuList &layers_items = layers_menu->items();
595 layers_menu->set_name("ArdourContextMenu");
597 RadioMenuItem::Group layers_group;
599 /* Find out how many overlaid/stacked tracks we have in the selection */
603 TrackSelection const & s = _editor.get_selection().tracks;
604 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
605 StreamView* v = (*i)->view ();
610 switch (v->layer_display ()) {
621 /* We're not connecting to signal_toggled() here; in the case where these two items are
622 set to be in the `inconsistent' state, it seems that one or other will end up active
623 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
624 select the active one, no toggled signal is emitted so nothing happens.
627 _ignore_set_layer_display = true;
629 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
630 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
631 i->set_active (overlaid != 0 && stacked == 0);
632 i->set_inconsistent (overlaid != 0 && stacked != 0);
633 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
635 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
636 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
637 i->set_active (overlaid == 0 && stacked != 0);
638 i->set_inconsistent (overlaid != 0 && stacked != 0);
639 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
641 _ignore_set_layer_display = false;
643 items.push_back (MenuElem (_("Layers"), *layers_menu));
645 if (!Profile->get_sae()) {
647 Menu* alignment_menu = manage (new Menu);
648 MenuList& alignment_items = alignment_menu->items();
649 alignment_menu->set_name ("ArdourContextMenu");
651 RadioMenuItem::Group align_group;
653 /* Same verbose hacks as for the layering options above */
659 boost::shared_ptr<Track> first_track;
661 TrackSelection const & s = _editor.get_selection().tracks;
662 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
663 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
664 if (!r || !r->is_track ()) {
669 first_track = r->track();
672 switch (r->track()->alignment_choice()) {
676 switch (r->track()->alignment_style()) {
677 case ExistingMaterial:
685 case UseExistingMaterial:
701 inconsistent = false;
710 if (!inconsistent && first_track) {
712 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
713 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
714 i->set_active (automatic != 0 && existing == 0 && capture == 0);
715 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
717 switch (first_track->alignment_choice()) {
719 switch (first_track->alignment_style()) {
720 case ExistingMaterial:
721 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
724 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
732 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
733 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
734 i->set_active (existing != 0 && capture == 0 && automatic == 0);
735 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
737 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
738 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
739 i->set_active (existing == 0 && capture != 0 && automatic == 0);
740 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
742 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
748 Menu* mode_menu = manage (new Menu);
749 MenuList& mode_items = mode_menu->items ();
750 mode_menu->set_name ("ArdourContextMenu");
752 RadioMenuItem::Group mode_group;
758 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
759 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
760 if (!r || !r->is_track ()) {
764 switch (r->track()->mode()) {
777 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
778 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
779 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
780 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
781 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
783 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
784 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
785 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
786 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
787 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
789 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
790 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
791 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
792 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
793 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
795 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
799 items.push_back (SeparatorElem());
801 build_playlist_menu ();
802 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
803 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
806 route_group_menu->detach ();
809 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
812 r.push_back (rtv->route ());
817 r.push_back (route ());
820 route_group_menu->build (r);
821 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
823 build_automation_action_menu (true);
824 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
826 items.push_back (SeparatorElem());
830 TrackSelection const & s = _editor.get_selection().tracks;
831 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
832 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
837 if (r->route()->active()) {
844 items.push_back (CheckMenuElem (_("Active")));
845 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
846 bool click_sets_active = true;
847 if (active > 0 && inactive == 0) {
848 i->set_active (true);
849 click_sets_active = false;
850 } else if (active > 0 && inactive > 0) {
851 i->set_inconsistent (true);
853 i->set_sensitive(! _session->transport_rolling());
854 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
856 items.push_back (SeparatorElem());
857 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
858 if (!Profile->get_sae()) {
859 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
861 items.push_front (SeparatorElem());
862 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
867 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
869 if (apply_to_selection) {
870 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
873 bool needs_bounce = false;
875 if (!track()->can_use_mode (mode, needs_bounce)) {
881 cerr << "would bounce this one\n";
886 track()->set_mode (mode);
891 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
893 TimeAxisView::show_timestretch (start, end, layers, layer);
903 /* check that the time selection was made in our route, or our route group.
904 remember that route_group() == 0 implies the route is *not* in a edit group.
907 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
908 /* this doesn't apply to us */
912 /* ignore it if our edit group is not active */
914 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
919 if (timestretch_rect == 0) {
920 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
921 timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
922 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
925 timestretch_rect->show ();
926 timestretch_rect->raise_to_top ();
928 double const x1 = start / _editor.get_current_zoom();
929 double const x2 = (end - 1) / _editor.get_current_zoom();
931 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
932 x2, current_height() * (layers - layer) / layers));
936 RouteTimeAxisView::hide_timestretch ()
938 TimeAxisView::hide_timestretch ();
940 if (timestretch_rect) {
941 timestretch_rect->hide ();
946 RouteTimeAxisView::show_selection (TimeSelection& ts)
950 /* ignore it if our edit group is not active or if the selection was started
951 in some other track or route group (remember that route_group() == 0 means
952 that the track is not in an route group).
955 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
956 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
962 TimeAxisView::show_selection (ts);
966 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
969 bool height_changed = (height == 0) || (h != height);
972 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
975 gm.get_level_meter().setup_meters (gmlen, meter_width);
977 TimeAxisView::set_height (h, m);
980 _view->set_height ((double) current_height());
983 if (height >= preset_height (HeightNormal)) {
987 gm.get_gain_slider().show();
989 if (!_route || _route->is_monitor()) {
994 if (rec_enable_button)
995 rec_enable_button->show();
997 route_group_button.show();
998 automation_button.show();
1000 if (is_track() && track()->mode() == ARDOUR::Normal) {
1001 playlist_button.show();
1008 gm.get_gain_slider().hide();
1009 mute_button->show();
1010 if (!_route || _route->is_monitor()) {
1011 solo_button->hide();
1013 solo_button->show();
1015 if (rec_enable_button)
1016 rec_enable_button->show();
1018 route_group_button.hide ();
1019 automation_button.hide ();
1021 if (is_track() && track()->mode() == ARDOUR::Normal) {
1022 playlist_button.hide ();
1027 if (height_changed && !no_redraw) {
1028 /* only emit the signal if the height really changed */
1034 RouteTimeAxisView::route_color_changed ()
1037 _view->apply_color (color(), StreamView::RegionColor);
1040 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1044 RouteTimeAxisView::reset_samples_per_pixel ()
1046 set_samples_per_pixel (_editor.get_current_zoom());
1050 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1055 speed = track()->speed();
1059 _view->set_samples_per_pixel (fpp * speed);
1062 TimeAxisView::set_samples_per_pixel (fpp * speed);
1066 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1068 if (!mitem->get_active()) {
1069 /* this is one of the two calls made when these radio menu items change status. this one
1070 is for the item that became inactive, and we want to ignore it.
1075 if (apply_to_selection) {
1076 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1079 track()->set_align_choice (choice);
1085 RouteTimeAxisView::rename_current_playlist ()
1087 ArdourPrompter prompter (true);
1090 boost::shared_ptr<Track> tr = track();
1091 if (!tr || tr->destructive()) {
1095 boost::shared_ptr<Playlist> pl = tr->playlist();
1100 prompter.set_title (_("Rename Playlist"));
1101 prompter.set_prompt (_("New name for playlist:"));
1102 prompter.set_initial_text (pl->name());
1103 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1104 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1106 switch (prompter.run ()) {
1107 case Gtk::RESPONSE_ACCEPT:
1108 prompter.get_result (name);
1109 if (name.length()) {
1110 pl->set_name (name);
1120 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1122 std::string ret (basename);
1124 std::string const group_string = "." + route_group()->name() + ".";
1126 // iterate through all playlists
1128 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1129 std::string tmp = (*i)->name();
1131 std::string::size_type idx = tmp.find(group_string);
1132 // find those which belong to this group
1133 if (idx != string::npos) {
1134 tmp = tmp.substr(idx + group_string.length());
1136 // and find the largest current number
1138 if (x > maxnumber) {
1147 snprintf (buf, sizeof(buf), "%d", maxnumber);
1149 ret = this->name() + "." + route_group()->name () + "." + buf;
1155 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1159 boost::shared_ptr<Track> tr = track ();
1160 if (!tr || tr->destructive()) {
1164 boost::shared_ptr<const Playlist> pl = tr->playlist();
1171 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1172 name = resolve_new_group_playlist_name(name, playlists_before_op);
1175 while (_session->playlists->by_name(name)) {
1176 name = Playlist::bump_name (name, *_session);
1179 // TODO: The prompter "new" button should be de-activated if the user
1180 // specifies a playlist name which already exists in the session.
1184 ArdourPrompter prompter (true);
1186 prompter.set_title (_("New Copy Playlist"));
1187 prompter.set_prompt (_("Name for new playlist:"));
1188 prompter.set_initial_text (name);
1189 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1190 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1191 prompter.show_all ();
1193 switch (prompter.run ()) {
1194 case Gtk::RESPONSE_ACCEPT:
1195 prompter.get_result (name);
1203 if (name.length()) {
1204 tr->use_copy_playlist ();
1205 tr->playlist()->set_name (name);
1210 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1214 boost::shared_ptr<Track> tr = track ();
1215 if (!tr || tr->destructive()) {
1219 boost::shared_ptr<const Playlist> pl = tr->playlist();
1226 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1227 name = resolve_new_group_playlist_name(name,playlists_before_op);
1230 while (_session->playlists->by_name(name)) {
1231 name = Playlist::bump_name (name, *_session);
1237 ArdourPrompter prompter (true);
1239 prompter.set_title (_("New Playlist"));
1240 prompter.set_prompt (_("Name for new playlist:"));
1241 prompter.set_initial_text (name);
1242 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1243 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1245 switch (prompter.run ()) {
1246 case Gtk::RESPONSE_ACCEPT:
1247 prompter.get_result (name);
1255 if (name.length()) {
1256 tr->use_new_playlist ();
1257 tr->playlist()->set_name (name);
1262 RouteTimeAxisView::clear_playlist ()
1264 boost::shared_ptr<Track> tr = track ();
1265 if (!tr || tr->destructive()) {
1269 boost::shared_ptr<Playlist> pl = tr->playlist();
1274 _editor.clear_playlist (pl);
1278 RouteTimeAxisView::speed_changed ()
1280 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1284 RouteTimeAxisView::update_diskstream_display ()
1294 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1296 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1298 /* special case: select/deselect all tracks */
1300 _editor.begin_reversible_selection_op (X_("Selection Click"));
1302 if (_editor.get_selection().selected (this)) {
1303 _editor.get_selection().clear_tracks ();
1305 _editor.select_all_tracks ();
1308 _editor.commit_reversible_selection_op ();
1313 _editor.begin_reversible_selection_op (X_("Selection Click"));
1315 switch (ArdourKeyboard::selection_type (ev->state)) {
1316 case Selection::Toggle:
1317 _editor.get_selection().toggle (this);
1320 case Selection::Set:
1321 _editor.get_selection().set (this);
1324 case Selection::Extend:
1325 _editor.extend_selection_to_track (*this);
1328 case Selection::Add:
1329 _editor.get_selection().add (this);
1333 _editor.commit_reversible_selection_op ();
1337 RouteTimeAxisView::set_selected_points (PointSelection& points)
1339 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1340 (*i)->set_selected_points (points);
1345 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1348 _view->set_selected_regionviews (regions);
1352 /** Add the selectable things that we have to a list.
1353 * @param results List to add things to.
1356 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1361 speed = track()->speed();
1364 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1365 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1367 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1368 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1371 /* pick up visible automation tracks */
1373 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1374 if (!(*i)->hidden()) {
1375 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1381 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1384 _view->get_inverted_selectables (sel, results);
1387 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1388 if (!(*i)->hidden()) {
1389 (*i)->get_inverted_selectables (sel, results);
1397 RouteTimeAxisView::route_group () const
1399 return _route->route_group();
1403 RouteTimeAxisView::name() const
1405 return _route->name();
1408 boost::shared_ptr<Playlist>
1409 RouteTimeAxisView::playlist () const
1411 boost::shared_ptr<Track> tr;
1413 if ((tr = track()) != 0) {
1414 return tr->playlist();
1416 return boost::shared_ptr<Playlist> ();
1421 RouteTimeAxisView::name_entry_changed ()
1423 TimeAxisView::name_entry_changed ();
1425 string x = name_entry->get_text ();
1427 if (x == _route->name()) {
1431 strip_whitespace_edges (x);
1433 if (x.length() == 0) {
1434 name_entry->set_text (_route->name());
1438 if (_session->route_name_internal (x)) {
1439 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1441 name_entry->grab_focus ();
1442 } else if (RouteUI::verify_new_route_name (x)) {
1443 _route->set_name (x);
1445 name_entry->grab_focus ();
1449 boost::shared_ptr<Region>
1450 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1452 boost::shared_ptr<Playlist> pl = playlist ();
1455 return pl->find_next_region (pos, point, dir);
1458 return boost::shared_ptr<Region> ();
1462 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1464 boost::shared_ptr<Playlist> pl = playlist ();
1467 return pl->find_next_region_boundary (pos, dir);
1474 RouteTimeAxisView::fade_range (TimeSelection& selection)
1476 boost::shared_ptr<Playlist> what_we_got;
1477 boost::shared_ptr<Track> tr = track ();
1478 boost::shared_ptr<Playlist> playlist;
1481 /* route is a bus, not a track */
1485 playlist = tr->playlist();
1487 TimeSelection time (selection);
1488 float const speed = tr->speed();
1489 if (speed != 1.0f) {
1490 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1491 (*i).start = session_frame_to_track_frame((*i).start, speed);
1492 (*i).end = session_frame_to_track_frame((*i).end, speed);
1496 playlist->clear_changes ();
1497 playlist->clear_owned_changes ();
1499 playlist->fade_range (time);
1501 vector<Command*> cmds;
1502 playlist->rdiff (cmds);
1503 _session->add_commands (cmds);
1504 _session->add_command (new StatefulDiffCommand (playlist));
1509 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1511 boost::shared_ptr<Playlist> what_we_got;
1512 boost::shared_ptr<Track> tr = track ();
1513 boost::shared_ptr<Playlist> playlist;
1516 /* route is a bus, not a track */
1520 playlist = tr->playlist();
1522 TimeSelection time (selection.time);
1523 float const speed = tr->speed();
1524 if (speed != 1.0f) {
1525 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1526 (*i).start = session_frame_to_track_frame((*i).start, speed);
1527 (*i).end = session_frame_to_track_frame((*i).end, speed);
1531 playlist->clear_changes ();
1532 playlist->clear_owned_changes ();
1536 if (playlist->cut (time) != 0) {
1537 if (Config->get_edit_mode() == Ripple)
1538 playlist->ripple(time.start(), -time.length(), NULL);
1539 // no need to exclude any regions from rippling here
1541 vector<Command*> cmds;
1542 playlist->rdiff (cmds);
1543 _session->add_commands (cmds);
1545 _session->add_command (new StatefulDiffCommand (playlist));
1550 if ((what_we_got = playlist->cut (time)) != 0) {
1551 _editor.get_cut_buffer().add (what_we_got);
1552 if (Config->get_edit_mode() == Ripple)
1553 playlist->ripple(time.start(), -time.length(), NULL);
1554 // no need to exclude any regions from rippling here
1556 vector<Command*> cmds;
1557 playlist->rdiff (cmds);
1558 _session->add_commands (cmds);
1560 _session->add_command (new StatefulDiffCommand (playlist));
1564 if ((what_we_got = playlist->copy (time)) != 0) {
1565 _editor.get_cut_buffer().add (what_we_got);
1570 if ((what_we_got = playlist->cut (time)) != 0) {
1571 if (Config->get_edit_mode() == Ripple)
1572 playlist->ripple(time.start(), -time.length(), NULL);
1573 // no need to exclude any regions from rippling here
1575 vector<Command*> cmds;
1576 playlist->rdiff (cmds);
1577 _session->add_commands (cmds);
1578 _session->add_command (new StatefulDiffCommand (playlist));
1579 what_we_got->release ();
1586 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1592 boost::shared_ptr<Playlist> pl = playlist ();
1593 const ARDOUR::DataType type = pl->data_type();
1594 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1596 if (p == selection.playlists.end()) {
1599 ctx.counts.increase_n_playlists(type);
1601 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1603 if (track()->speed() != 1.0f) {
1604 pos = session_frame_to_track_frame (pos, track()->speed());
1605 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1608 /* add multi-paste offset if applicable */
1609 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1610 const framecnt_t duration = extent.second - extent.first;
1611 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1613 pl->clear_changes ();
1614 if (Config->get_edit_mode() == Ripple) {
1615 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1616 framecnt_t amount = extent.second - extent.first;
1617 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1619 pl->paste (*p, pos, ctx.times);
1621 vector<Command*> cmds;
1623 _session->add_commands (cmds);
1625 _session->add_command (new StatefulDiffCommand (pl));
1631 struct PlaylistSorter {
1632 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1633 return a->sort_id() < b->sort_id();
1638 RouteTimeAxisView::build_playlist_menu ()
1640 using namespace Menu_Helpers;
1646 delete playlist_action_menu;
1647 playlist_action_menu = new Menu;
1648 playlist_action_menu->set_name ("ArdourContextMenu");
1650 MenuList& playlist_items = playlist_action_menu->items();
1651 playlist_action_menu->set_name ("ArdourContextMenu");
1652 playlist_items.clear();
1654 RadioMenuItem::Group playlist_group;
1655 boost::shared_ptr<Track> tr = track ();
1657 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1659 /* sort the playlists */
1661 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1663 /* add the playlists to the menu */
1664 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1665 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1666 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1667 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1669 if (tr->playlist()->id() == (*i)->id()) {
1675 playlist_items.push_back (SeparatorElem());
1676 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1677 playlist_items.push_back (SeparatorElem());
1679 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1680 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1681 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1684 // Use a label which tells the user what is happening
1685 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1686 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1690 playlist_items.push_back (SeparatorElem());
1691 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1692 playlist_items.push_back (SeparatorElem());
1694 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1698 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1700 assert (is_track());
1702 // exit if we were triggered by deactivating the old playlist
1703 if (!item->get_active()) {
1707 boost::shared_ptr<Playlist> pl (wpl.lock());
1713 if (track()->playlist() == pl) {
1714 // exit when use_playlist is called by the creation of the playlist menu
1715 // or the playlist choice is unchanged
1719 track()->use_playlist (pl);
1721 RouteGroup* rg = route_group();
1723 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1724 std::string group_string = "." + rg->name() + ".";
1726 std::string take_name = pl->name();
1727 std::string::size_type idx = take_name.find(group_string);
1729 if (idx == std::string::npos)
1732 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1734 boost::shared_ptr<RouteList> rl (rg->route_list());
1736 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1737 if ((*i) == this->route()) {
1741 std::string playlist_name = (*i)->name()+group_string+take_name;
1743 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1748 if (track->freeze_state() == Track::Frozen) {
1749 /* Don't change playlists of frozen tracks */
1753 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1755 // No playlist for this track for this take yet, make it
1756 track->use_new_playlist();
1757 track->playlist()->set_name(playlist_name);
1759 track->use_playlist(ipl);
1766 RouteTimeAxisView::update_playlist_tip ()
1768 RouteGroup* rg = route_group ();
1769 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1770 string group_string = "." + rg->name() + ".";
1772 string take_name = track()->playlist()->name();
1773 string::size_type idx = take_name.find(group_string);
1775 if (idx != string::npos) {
1776 /* find the bit containing the take number / name */
1777 take_name = take_name.substr (idx + group_string.length());
1779 /* set the playlist button tooltip to the take name */
1780 ARDOUR_UI::instance()->set_tip (
1782 string_compose(_("Take: %1.%2"),
1783 Glib::Markup::escape_text(rg->name()),
1784 Glib::Markup::escape_text(take_name))
1791 /* set the playlist button tooltip to the playlist name */
1792 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1797 RouteTimeAxisView::show_playlist_selector ()
1799 _editor.playlist_selector().show_for (this);
1803 RouteTimeAxisView::map_frozen ()
1809 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1811 switch (track()->freeze_state()) {
1813 playlist_button.set_sensitive (false);
1814 rec_enable_button->set_sensitive (false);
1817 playlist_button.set_sensitive (true);
1818 rec_enable_button->set_sensitive (true);
1824 RouteTimeAxisView::color_handler ()
1826 //case cTimeStretchOutline:
1827 if (timestretch_rect) {
1828 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1830 //case cTimeStretchFill:
1831 if (timestretch_rect) {
1832 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1838 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1839 * Will add track if necessary.
1842 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1844 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1845 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1848 /* it doesn't exist yet, so we don't care about the button state: just add it */
1849 create_automation_child (param, true);
1852 bool yn = menu->get_active();
1853 bool changed = false;
1855 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1857 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1858 will have done that for us.
1861 if (changed && !no_redraw) {
1869 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1871 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1877 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1879 if (menu && !_hidden) {
1880 ignore_toggle = true;
1881 menu->set_active (false);
1882 ignore_toggle = false;
1885 if (_route && !no_redraw) {
1891 RouteTimeAxisView::update_gain_track_visibility ()
1893 bool const showit = gain_automation_item->get_active();
1895 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1896 gain_track->set_marked_for_display (showit);
1898 /* now trigger a redisplay */
1901 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1907 RouteTimeAxisView::update_mute_track_visibility ()
1909 bool const showit = mute_automation_item->get_active();
1911 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1912 mute_track->set_marked_for_display (showit);
1914 /* now trigger a redisplay */
1917 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1923 RouteTimeAxisView::update_pan_track_visibility ()
1925 bool const showit = pan_automation_item->get_active();
1926 bool changed = false;
1928 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1929 if ((*i)->set_marked_for_display (showit)) {
1935 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1940 RouteTimeAxisView::ensure_pan_views (bool show)
1942 bool changed = false;
1943 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1945 (*i)->set_marked_for_display (false);
1948 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1952 if (!_route->panner()) {
1956 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1957 set<Evoral::Parameter>::iterator p;
1959 for (p = params.begin(); p != params.end(); ++p) {
1960 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1962 if (pan_control->parameter().type() == NullAutomation) {
1963 error << "Pan control has NULL automation type!" << endmsg;
1967 if (automation_child (pan_control->parameter ()).get () == 0) {
1969 /* we don't already have an AutomationTimeAxisView for this parameter */
1971 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1973 boost::shared_ptr<AutomationTimeAxisView> t (
1974 new AutomationTimeAxisView (_session,
1978 pan_control->parameter (),
1986 pan_tracks.push_back (t);
1987 add_automation_child (*p, t, show);
1989 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1996 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1998 if (apply_to_selection) {
1999 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2003 /* Show our automation */
2005 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2006 i->second->set_marked_for_display (true);
2008 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2011 menu->set_active(true);
2016 /* Show processor automation */
2018 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2019 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2020 if ((*ii)->view == 0) {
2021 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2024 (*ii)->menu_item->set_active (true);
2037 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2039 if (apply_to_selection) {
2040 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2044 /* Show our automation */
2046 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2047 if (i->second->has_automation()) {
2048 i->second->set_marked_for_display (true);
2050 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2052 menu->set_active(true);
2057 /* Show processor automation */
2059 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2060 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2061 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2062 (*ii)->menu_item->set_active (true);
2074 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2076 if (apply_to_selection) {
2077 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2081 /* Hide our automation */
2083 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2084 i->second->set_marked_for_display (false);
2086 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2089 menu->set_active (false);
2093 /* Hide processor automation */
2095 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2096 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2097 (*ii)->menu_item->set_active (false);
2108 RouteTimeAxisView::region_view_added (RegionView* rv)
2110 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2111 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2112 boost::shared_ptr<AutomationTimeAxisView> atv;
2114 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2119 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2120 (*i)->add_ghost(rv);
2124 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2126 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2132 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2134 parent.remove_processor_automation_node (this);
2138 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2141 remove_child (pan->view);
2145 RouteTimeAxisView::ProcessorAutomationNode*
2146 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2148 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2150 if ((*i)->processor == processor) {
2152 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2153 if ((*ii)->what == what) {
2163 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2165 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2168 ProcessorAutomationNode* pan;
2170 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2171 /* session state may never have been saved with new plugin */
2172 error << _("programming error: ")
2173 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2174 processor->name(), what.type(), (int) what.channel(), what.id() )
2176 abort(); /*NOTREACHED*/
2184 boost::shared_ptr<AutomationControl> control
2185 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2187 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2188 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2189 _editor, *this, false, parent_canvas,
2190 processor->describe_parameter (what), processor->name()));
2192 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2194 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2197 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2202 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2205 pan->menu_item->set_active (false);
2214 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2216 boost::shared_ptr<Processor> processor (p.lock ());
2218 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2219 /* The Amp processor is a special case and is dealt with separately */
2223 set<Evoral::Parameter> existing;
2225 processor->what_has_data (existing);
2227 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2229 Evoral::Parameter param (*i);
2230 boost::shared_ptr<AutomationLine> al;
2232 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2235 add_processor_automation_curve (processor, param);
2241 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2243 using namespace Menu_Helpers;
2247 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2249 _automation_tracks[param] = track;
2251 /* existing state overrides "show" argument */
2252 string s = track->gui_property ("visible");
2254 show = string_is_affirmative (s);
2257 /* this might or might not change the visibility status, so don't rely on it */
2258 track->set_marked_for_display (show);
2260 if (show && !no_redraw) {
2264 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2265 /* MIDI-related parameters are always in the menu, there's no
2266 reason to rebuild the menu just because we added a automation
2267 lane for one of them. But if we add a non-MIDI automation
2268 lane, then we need to invalidate the display menu.
2270 delete display_menu;
2276 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2278 boost::shared_ptr<Processor> processor (p.lock ());
2280 if (!processor || !processor->display_to_user ()) {
2284 /* we use this override to veto the Amp processor from the plugin menu,
2285 as its automation lane can be accessed using the special "Fader" menu
2289 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2293 using namespace Menu_Helpers;
2294 ProcessorAutomationInfo *rai;
2295 list<ProcessorAutomationInfo*>::iterator x;
2297 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2299 if (automatable.empty()) {
2303 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2304 if ((*x)->processor == processor) {
2309 if (x == processor_automation.end()) {
2311 rai = new ProcessorAutomationInfo (processor);
2312 processor_automation.push_back (rai);
2320 /* any older menu was deleted at the top of processors_changed()
2321 when we cleared the subplugin menu.
2324 rai->menu = manage (new Menu);
2325 MenuList& items = rai->menu->items();
2326 rai->menu->set_name ("ArdourContextMenu");
2330 std::set<Evoral::Parameter> has_visible_automation;
2331 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2333 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2335 ProcessorAutomationNode* pan;
2336 Gtk::CheckMenuItem* mitem;
2338 string name = processor->describe_parameter (*i);
2340 items.push_back (CheckMenuElem (name));
2341 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2343 _subplugin_menu_map[*i] = mitem;
2345 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2346 mitem->set_active(true);
2349 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2353 pan = new ProcessorAutomationNode (*i, mitem, *this);
2355 rai->lines.push_back (pan);
2359 pan->menu_item = mitem;
2363 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2366 /* add the menu for this processor, because the subplugin
2367 menu is always cleared at the top of processors_changed().
2368 this is the result of some poor design in gtkmm and/or
2372 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2377 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2378 RouteTimeAxisView::ProcessorAutomationNode* pan)
2380 bool showit = pan->menu_item->get_active();
2381 bool redraw = false;
2383 if (pan->view == 0 && showit) {
2384 add_processor_automation_curve (rai->processor, pan->what);
2388 if (pan->view && pan->view->set_marked_for_display (showit)) {
2392 if (redraw && !no_redraw) {
2398 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2400 if (c.type == RouteProcessorChange::MeterPointChange) {
2401 /* nothing to do if only the meter point has changed */
2405 using namespace Menu_Helpers;
2407 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2408 (*i)->valid = false;
2411 setup_processor_menu_and_curves ();
2413 bool deleted_processor_automation = false;
2415 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2417 list<ProcessorAutomationInfo*>::iterator tmp;
2425 processor_automation.erase (i);
2426 deleted_processor_automation = true;
2433 if (deleted_processor_automation && !no_redraw) {
2438 boost::shared_ptr<AutomationLine>
2439 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2441 ProcessorAutomationNode* pan;
2443 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2449 return boost::shared_ptr<AutomationLine>();
2453 RouteTimeAxisView::reset_processor_automation_curves ()
2455 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2461 RouteTimeAxisView::can_edit_name () const
2463 /* we do not allow track name changes if it is record enabled
2465 return !_route->record_enabled();
2469 RouteTimeAxisView::blink_rec_display (bool onoff)
2471 RouteUI::blink_rec_display (onoff);
2475 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2477 if (_ignore_set_layer_display) {
2481 if (apply_to_selection) {
2482 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2486 _view->set_layer_display (d);
2489 set_gui_property (X_("layer-display"), enum_2_string (d));
2494 RouteTimeAxisView::layer_display () const
2497 return _view->layer_display ();
2500 /* we don't know, since we don't have a _view, so just return something */
2506 boost::shared_ptr<AutomationTimeAxisView>
2507 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2509 AutomationTracks::iterator i = _automation_tracks.find(param);
2510 if (i != _automation_tracks.end()) {
2513 return boost::shared_ptr<AutomationTimeAxisView>();
2518 RouteTimeAxisView::fast_update ()
2520 gm.get_level_meter().update_meters ();
2524 RouteTimeAxisView::hide_meter ()
2527 gm.get_level_meter().hide_meters ();
2531 RouteTimeAxisView::show_meter ()
2537 RouteTimeAxisView::reset_meter ()
2539 if (ARDOUR_UI::config()->get_show_track_meters()) {
2540 int meter_width = 3;
2541 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2544 gm.get_level_meter().setup_meters (height - 9, meter_width);
2551 RouteTimeAxisView::clear_meter ()
2553 gm.get_level_meter().clear_meters ();
2557 RouteTimeAxisView::meter_changed ()
2559 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2561 if (_route && !no_redraw) {
2564 // reset peak when meter point changes
2565 gm.reset_peak_display();
2569 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2572 if (_route && !no_redraw) {
2578 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2580 using namespace Menu_Helpers;
2582 if (!_underlay_streams.empty()) {
2583 MenuList& parent_items = parent_menu->items();
2584 Menu* gs_menu = manage (new Menu);
2585 gs_menu->set_name ("ArdourContextMenu");
2586 MenuList& gs_items = gs_menu->items();
2588 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2590 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2591 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2592 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2598 RouteTimeAxisView::set_underlay_state()
2600 if (!underlay_xml_node) {
2604 XMLNodeList nlist = underlay_xml_node->children();
2605 XMLNodeConstIterator niter;
2606 XMLNode *child_node;
2608 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2609 child_node = *niter;
2611 if (child_node->name() != "Underlay") {
2615 XMLProperty* prop = child_node->property ("id");
2617 PBD::ID id (prop->value());
2619 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2622 add_underlay(v->view(), false);
2631 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2637 RouteTimeAxisView& other = v->trackview();
2639 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2640 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2641 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2642 abort(); /*NOTREACHED*/
2645 _underlay_streams.push_back(v);
2646 other._underlay_mirrors.push_back(this);
2648 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2650 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2652 if (!underlay_xml_node) {
2653 underlay_xml_node = xml_node->add_child("Underlays");
2656 XMLNode* node = underlay_xml_node->add_child("Underlay");
2657 XMLProperty* prop = node->add_property("id");
2658 prop->set_value(v->trackview().route()->id().to_s());
2665 RouteTimeAxisView::remove_underlay (StreamView* v)
2671 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2672 RouteTimeAxisView& other = v->trackview();
2674 if (it != _underlay_streams.end()) {
2675 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2677 if (gm == other._underlay_mirrors.end()) {
2678 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2679 abort(); /*NOTREACHED*/
2682 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2684 _underlay_streams.erase(it);
2685 other._underlay_mirrors.erase(gm);
2687 if (underlay_xml_node) {
2688 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2694 RouteTimeAxisView::set_button_names ()
2696 if (_route && _route->solo_safe()) {
2697 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2699 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2701 if (Config->get_solo_control_is_listen_control()) {
2702 switch (Config->get_listen_position()) {
2703 case AfterFaderListen:
2704 solo_button->set_text (_("A"));
2705 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2707 case PreFaderListen:
2708 solo_button->set_text (_("P"));
2709 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2713 solo_button->set_text (_("S"));
2714 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2716 mute_button->set_text (_("M"));
2720 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2722 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2723 if (i != _main_automation_menu_map.end()) {
2727 i = _subplugin_menu_map.find (param);
2728 if (i != _subplugin_menu_map.end()) {
2736 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2738 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2740 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2744 gain_track.reset (new AutomationTimeAxisView (_session,
2745 _route, _route->amp(), c, param,
2750 _route->amp()->describe_parameter(param)));
2753 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2756 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2760 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2762 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2764 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2768 mute_track.reset (new AutomationTimeAxisView (_session,
2769 _route, _route, c, param,
2774 _route->describe_parameter(param)));
2777 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2780 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2784 void add_region_to_list (RegionView* rv, RegionList* l)
2786 l->push_back (rv->region());
2790 RouteTimeAxisView::combine_regions ()
2792 /* as of may 2011, we do not offer uncombine for MIDI tracks
2795 if (!is_audio_track()) {
2803 RegionList selected_regions;
2804 boost::shared_ptr<Playlist> playlist = track()->playlist();
2806 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2808 if (selected_regions.size() < 2) {
2812 playlist->clear_changes ();
2813 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2815 _session->add_command (new StatefulDiffCommand (playlist));
2816 /* make the new region be selected */
2818 return _view->find_view (compound_region);
2822 RouteTimeAxisView::uncombine_regions ()
2824 /* as of may 2011, we do not offer uncombine for MIDI tracks
2826 if (!is_audio_track()) {
2834 RegionList selected_regions;
2835 boost::shared_ptr<Playlist> playlist = track()->playlist();
2837 /* have to grab selected regions first because the uncombine is going
2838 * to change that in the middle of the list traverse
2841 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2843 playlist->clear_changes ();
2845 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2846 playlist->uncombine (*i);
2849 _session->add_command (new StatefulDiffCommand (playlist));
2853 RouteTimeAxisView::state_id() const
2855 return string_compose ("rtav %1", _route->id().to_s());
2860 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2862 TimeAxisView::remove_child (c);
2864 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2866 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2867 if (i->second == a) {
2868 _automation_tracks.erase (i);