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 CatchDeletion (this);
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;
344 RouteTimeAxisView::post_construct ()
346 /* map current state of the route */
348 update_diskstream_display ();
349 setup_processor_menu_and_curves ();
350 reset_processor_automation_curves ();
353 /** Set up the processor menu for the current set of processors, and
354 * display automation curves for any parameters which have data.
357 RouteTimeAxisView::setup_processor_menu_and_curves ()
359 _subplugin_menu_map.clear ();
360 subplugin_menu.items().clear ();
361 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
362 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
366 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
368 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
369 if (_route->route_group()) {
370 _route->route_group()->remove (_route);
376 r.push_back (route ());
378 route_group_menu->build (r);
379 route_group_menu->menu()->popup (ev->button, ev->time);
385 RouteTimeAxisView::playlist_changed ()
391 RouteTimeAxisView::label_view ()
393 string x = _route->name ();
394 if (x != name_label.get_text ()) {
395 name_label.set_text (x);
397 const int64_t track_number = _route->track_number ();
398 if (track_number == 0) {
399 number_label.set_text ("");
401 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
406 RouteTimeAxisView::update_track_number_visibility ()
409 bool show_label = _session->config.get_track_name_number();
411 if (_route && _route->is_master()) {
415 if (number_label.get_parent()) {
416 controls_table.remove (number_label);
419 if (ARDOUR::Profile->get_mixbus()) {
420 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
422 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
424 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
425 // except the width of the number label is subtracted from the name-hbox, so we
426 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
427 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
429 number_label.set_size_request(tnw, -1);
430 number_label.show ();
431 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
433 number_label.hide ();
434 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
439 RouteTimeAxisView::parameter_changed (string const & p)
441 if (p == "track-name-number") {
442 update_track_number_visibility();
447 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
449 if (what_changed.contains (ARDOUR::Properties::name)) {
455 RouteTimeAxisView::take_name_changed (void *src)
463 RouteTimeAxisView::playlist_click ()
465 build_playlist_menu ();
466 conditionally_add_to_selection ();
467 playlist_action_menu->popup (1, gtk_get_current_event_time());
471 RouteTimeAxisView::automation_click ()
473 conditionally_add_to_selection ();
474 build_automation_action_menu (false);
475 automation_action_menu->popup (1, gtk_get_current_event_time());
479 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
481 using namespace Menu_Helpers;
483 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
484 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
487 detach_menu (subplugin_menu);
489 _main_automation_menu_map.clear ();
490 delete automation_action_menu;
491 automation_action_menu = new Menu;
493 MenuList& items = automation_action_menu->items();
495 automation_action_menu->set_name ("ArdourContextMenu");
497 items.push_back (MenuElem (_("Show All Automation"),
498 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
500 items.push_back (MenuElem (_("Show Existing Automation"),
501 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
503 items.push_back (MenuElem (_("Hide All Automation"),
504 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
506 /* Attach the plugin submenu. It may have previously been used elsewhere,
507 so it was detached above
510 if (!subplugin_menu.items().empty()) {
511 items.push_back (SeparatorElem ());
512 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
513 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
516 /* Add any route automation */
519 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
520 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
521 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
522 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
524 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
528 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
529 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
530 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
531 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
533 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
536 if (!pan_tracks.empty()) {
537 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
538 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
539 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
540 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
542 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
543 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
544 _main_automation_menu_map[*p] = pan_automation_item;
550 RouteTimeAxisView::build_display_menu ()
552 using namespace Menu_Helpers;
556 TimeAxisView::build_display_menu ();
558 /* now fill it with our stuff */
560 MenuList& items = display_menu->items();
561 display_menu->set_name ("ArdourContextMenu");
563 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
565 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
567 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
569 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
571 items.push_back (SeparatorElem());
574 detach_menu (*_size_menu);
577 items.push_back (MenuElem (_("Height"), *_size_menu));
579 items.push_back (SeparatorElem());
581 if (!Profile->get_sae()) {
582 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
583 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
584 items.push_back (SeparatorElem());
587 // Hook for derived classes to add type specific stuff
588 append_extra_display_menu_items ();
592 Menu* layers_menu = manage (new Menu);
593 MenuList &layers_items = layers_menu->items();
594 layers_menu->set_name("ArdourContextMenu");
596 RadioMenuItem::Group layers_group;
598 /* Find out how many overlaid/stacked tracks we have in the selection */
602 TrackSelection const & s = _editor.get_selection().tracks;
603 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
604 StreamView* v = (*i)->view ();
609 switch (v->layer_display ()) {
620 /* We're not connecting to signal_toggled() here; in the case where these two items are
621 set to be in the `inconsistent' state, it seems that one or other will end up active
622 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
623 select the active one, no toggled signal is emitted so nothing happens.
626 _ignore_set_layer_display = true;
628 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
629 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
630 i->set_active (overlaid != 0 && stacked == 0);
631 i->set_inconsistent (overlaid != 0 && stacked != 0);
632 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
634 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
635 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
636 i->set_active (overlaid == 0 && stacked != 0);
637 i->set_inconsistent (overlaid != 0 && stacked != 0);
638 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
640 _ignore_set_layer_display = false;
642 items.push_back (MenuElem (_("Layers"), *layers_menu));
644 if (!Profile->get_sae()) {
646 Menu* alignment_menu = manage (new Menu);
647 MenuList& alignment_items = alignment_menu->items();
648 alignment_menu->set_name ("ArdourContextMenu");
650 RadioMenuItem::Group align_group;
652 /* Same verbose hacks as for the layering options above */
658 boost::shared_ptr<Track> first_track;
660 TrackSelection const & s = _editor.get_selection().tracks;
661 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
662 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
663 if (!r || !r->is_track ()) {
668 first_track = r->track();
671 switch (r->track()->alignment_choice()) {
675 switch (r->track()->alignment_style()) {
676 case ExistingMaterial:
684 case UseExistingMaterial:
700 inconsistent = false;
709 if (!inconsistent && first_track) {
711 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
712 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
713 i->set_active (automatic != 0 && existing == 0 && capture == 0);
714 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
716 switch (first_track->alignment_choice()) {
718 switch (first_track->alignment_style()) {
719 case ExistingMaterial:
720 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
723 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
731 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
732 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
733 i->set_active (existing != 0 && capture == 0 && automatic == 0);
734 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
736 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
737 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
738 i->set_active (existing == 0 && capture != 0 && automatic == 0);
739 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
741 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
747 Menu* mode_menu = manage (new Menu);
748 MenuList& mode_items = mode_menu->items ();
749 mode_menu->set_name ("ArdourContextMenu");
751 RadioMenuItem::Group mode_group;
757 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
758 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
759 if (!r || !r->is_track ()) {
763 switch (r->track()->mode()) {
776 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
777 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
778 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
779 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
780 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
782 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
783 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
784 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
785 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
786 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
788 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
789 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
790 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
791 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
792 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
794 items.push_back (MenuElem (_("Mode"), *mode_menu));
798 items.push_back (SeparatorElem());
800 build_playlist_menu ();
801 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
802 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
805 route_group_menu->detach ();
808 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
809 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
811 r.push_back (rtv->route ());
816 r.push_back (route ());
819 route_group_menu->build (r);
820 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
822 build_automation_action_menu (true);
823 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
825 items.push_back (SeparatorElem());
829 TrackSelection const & s = _editor.get_selection().tracks;
830 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
831 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
836 if (r->route()->active()) {
843 items.push_back (CheckMenuElem (_("Active")));
844 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
845 bool click_sets_active = true;
846 if (active > 0 && inactive == 0) {
847 i->set_active (true);
848 click_sets_active = false;
849 } else if (active > 0 && inactive > 0) {
850 i->set_inconsistent (true);
852 i->set_sensitive(! _session->transport_rolling());
853 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
855 items.push_back (SeparatorElem());
856 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
857 if (!Profile->get_sae()) {
858 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
860 items.push_front (SeparatorElem());
861 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
866 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
868 if (apply_to_selection) {
869 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
872 bool needs_bounce = false;
874 if (!track()->can_use_mode (mode, needs_bounce)) {
880 cerr << "would bounce this one\n";
885 track()->set_mode (mode);
890 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
892 TimeAxisView::show_timestretch (start, end, layers, layer);
902 /* check that the time selection was made in our route, or our route group.
903 remember that route_group() == 0 implies the route is *not* in a edit group.
906 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
907 /* this doesn't apply to us */
911 /* ignore it if our edit group is not active */
913 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
918 if (timestretch_rect == 0) {
919 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
920 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_TimeStretchFill());
921 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_TimeStretchOutline());
924 timestretch_rect->show ();
925 timestretch_rect->raise_to_top ();
927 double const x1 = start / _editor.get_current_zoom();
928 double const x2 = (end - 1) / _editor.get_current_zoom();
930 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
931 x2, current_height() * (layers - layer) / layers));
935 RouteTimeAxisView::hide_timestretch ()
937 TimeAxisView::hide_timestretch ();
939 if (timestretch_rect) {
940 timestretch_rect->hide ();
945 RouteTimeAxisView::show_selection (TimeSelection& ts)
949 /* ignore it if our edit group is not active or if the selection was started
950 in some other track or route group (remember that route_group() == 0 means
951 that the track is not in an route group).
954 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
955 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
961 TimeAxisView::show_selection (ts);
965 RouteTimeAxisView::set_height (uint32_t h)
968 bool height_changed = (height == 0) || (h != height);
971 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
974 gm.get_level_meter().setup_meters (gmlen, meter_width);
976 TimeAxisView::set_height (h);
979 _view->set_height ((double) current_height());
982 if (height >= preset_height (HeightNormal)) {
986 gm.get_gain_slider().show();
988 if (!_route || _route->is_monitor()) {
993 if (rec_enable_button)
994 rec_enable_button->show();
996 route_group_button.show();
997 automation_button.show();
999 if (is_track() && track()->mode() == ARDOUR::Normal) {
1000 playlist_button.show();
1007 gm.get_gain_slider().hide();
1008 mute_button->show();
1009 if (!_route || _route->is_monitor()) {
1010 solo_button->hide();
1012 solo_button->show();
1014 if (rec_enable_button)
1015 rec_enable_button->show();
1017 route_group_button.hide ();
1018 automation_button.hide ();
1020 if (is_track() && track()->mode() == ARDOUR::Normal) {
1021 playlist_button.hide ();
1026 if (height_changed && !no_redraw) {
1027 /* only emit the signal if the height really changed */
1033 RouteTimeAxisView::route_color_changed ()
1036 _view->apply_color (color(), StreamView::RegionColor);
1039 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1043 RouteTimeAxisView::reset_samples_per_pixel ()
1045 set_samples_per_pixel (_editor.get_current_zoom());
1049 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1054 speed = track()->speed();
1058 _view->set_samples_per_pixel (fpp * speed);
1061 TimeAxisView::set_samples_per_pixel (fpp * speed);
1065 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1067 if (!mitem->get_active()) {
1068 /* this is one of the two calls made when these radio menu items change status. this one
1069 is for the item that became inactive, and we want to ignore it.
1074 if (apply_to_selection) {
1075 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1078 track()->set_align_choice (choice);
1084 RouteTimeAxisView::rename_current_playlist ()
1086 ArdourPrompter prompter (true);
1089 boost::shared_ptr<Track> tr = track();
1090 if (!tr || tr->destructive()) {
1094 boost::shared_ptr<Playlist> pl = tr->playlist();
1099 prompter.set_title (_("Rename Playlist"));
1100 prompter.set_prompt (_("New name for playlist:"));
1101 prompter.set_initial_text (pl->name());
1102 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1103 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1105 switch (prompter.run ()) {
1106 case Gtk::RESPONSE_ACCEPT:
1107 prompter.get_result (name);
1108 if (name.length()) {
1109 pl->set_name (name);
1119 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1121 std::string ret (basename);
1123 std::string const group_string = "." + route_group()->name() + ".";
1125 // iterate through all playlists
1127 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1128 std::string tmp = (*i)->name();
1130 std::string::size_type idx = tmp.find(group_string);
1131 // find those which belong to this group
1132 if (idx != string::npos) {
1133 tmp = tmp.substr(idx + group_string.length());
1135 // and find the largest current number
1137 if (x > maxnumber) {
1146 snprintf (buf, sizeof(buf), "%d", maxnumber);
1148 ret = this->name() + "." + route_group()->name () + "." + buf;
1154 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1158 boost::shared_ptr<Track> tr = track ();
1159 if (!tr || tr->destructive()) {
1163 boost::shared_ptr<const Playlist> pl = tr->playlist();
1170 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1171 name = resolve_new_group_playlist_name(name, playlists_before_op);
1174 while (_session->playlists->by_name(name)) {
1175 name = Playlist::bump_name (name, *_session);
1178 // TODO: The prompter "new" button should be de-activated if the user
1179 // specifies a playlist name which already exists in the session.
1183 ArdourPrompter prompter (true);
1185 prompter.set_title (_("New Copy Playlist"));
1186 prompter.set_prompt (_("Name for new playlist:"));
1187 prompter.set_initial_text (name);
1188 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1189 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1190 prompter.show_all ();
1192 switch (prompter.run ()) {
1193 case Gtk::RESPONSE_ACCEPT:
1194 prompter.get_result (name);
1202 if (name.length()) {
1203 tr->use_copy_playlist ();
1204 tr->playlist()->set_name (name);
1209 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1213 boost::shared_ptr<Track> tr = track ();
1214 if (!tr || tr->destructive()) {
1218 boost::shared_ptr<const Playlist> pl = tr->playlist();
1225 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1226 name = resolve_new_group_playlist_name(name,playlists_before_op);
1229 while (_session->playlists->by_name(name)) {
1230 name = Playlist::bump_name (name, *_session);
1236 ArdourPrompter prompter (true);
1238 prompter.set_title (_("New Playlist"));
1239 prompter.set_prompt (_("Name for new playlist:"));
1240 prompter.set_initial_text (name);
1241 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1242 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1244 switch (prompter.run ()) {
1245 case Gtk::RESPONSE_ACCEPT:
1246 prompter.get_result (name);
1254 if (name.length()) {
1255 tr->use_new_playlist ();
1256 tr->playlist()->set_name (name);
1261 RouteTimeAxisView::clear_playlist ()
1263 boost::shared_ptr<Track> tr = track ();
1264 if (!tr || tr->destructive()) {
1268 boost::shared_ptr<Playlist> pl = tr->playlist();
1273 _editor.clear_playlist (pl);
1277 RouteTimeAxisView::speed_changed ()
1279 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1283 RouteTimeAxisView::update_diskstream_display ()
1293 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1295 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1297 /* special case: select/deselect all tracks */
1298 if (_editor.get_selection().selected (this)) {
1299 _editor.get_selection().clear_tracks ();
1301 _editor.select_all_tracks ();
1307 switch (ArdourKeyboard::selection_type (ev->state)) {
1308 case Selection::Toggle:
1309 _editor.get_selection().toggle (this);
1312 case Selection::Set:
1313 _editor.get_selection().set (this);
1316 case Selection::Extend:
1317 _editor.extend_selection_to_track (*this);
1320 case Selection::Add:
1321 _editor.get_selection().add (this);
1327 RouteTimeAxisView::set_selected_points (PointSelection& points)
1329 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1330 (*i)->set_selected_points (points);
1335 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1338 _view->set_selected_regionviews (regions);
1342 /** Add the selectable things that we have to a list.
1343 * @param results List to add things to.
1346 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1351 speed = track()->speed();
1354 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1355 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1357 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1358 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1361 /* pick up visible automation tracks */
1363 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1364 if (!(*i)->hidden()) {
1365 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1371 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1374 _view->get_inverted_selectables (sel, results);
1377 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1378 if (!(*i)->hidden()) {
1379 (*i)->get_inverted_selectables (sel, results);
1387 RouteTimeAxisView::route_group () const
1389 return _route->route_group();
1393 RouteTimeAxisView::name() const
1395 return _route->name();
1398 boost::shared_ptr<Playlist>
1399 RouteTimeAxisView::playlist () const
1401 boost::shared_ptr<Track> tr;
1403 if ((tr = track()) != 0) {
1404 return tr->playlist();
1406 return boost::shared_ptr<Playlist> ();
1411 RouteTimeAxisView::name_entry_changed ()
1413 TimeAxisView::name_entry_changed ();
1415 string x = name_entry->get_text ();
1417 if (x == _route->name()) {
1421 strip_whitespace_edges (x);
1423 if (x.length() == 0) {
1424 name_entry->set_text (_route->name());
1428 if (_session->route_name_internal (x)) {
1429 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1431 name_entry->grab_focus ();
1432 } else if (RouteUI::verify_new_route_name (x)) {
1433 _route->set_name (x);
1435 name_entry->grab_focus ();
1439 boost::shared_ptr<Region>
1440 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1442 boost::shared_ptr<Playlist> pl = playlist ();
1445 return pl->find_next_region (pos, point, dir);
1448 return boost::shared_ptr<Region> ();
1452 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1454 boost::shared_ptr<Playlist> pl = playlist ();
1457 return pl->find_next_region_boundary (pos, dir);
1464 RouteTimeAxisView::fade_range (TimeSelection& selection)
1466 boost::shared_ptr<Playlist> what_we_got;
1467 boost::shared_ptr<Track> tr = track ();
1468 boost::shared_ptr<Playlist> playlist;
1471 /* route is a bus, not a track */
1475 playlist = tr->playlist();
1477 TimeSelection time (selection);
1478 float const speed = tr->speed();
1479 if (speed != 1.0f) {
1480 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1481 (*i).start = session_frame_to_track_frame((*i).start, speed);
1482 (*i).end = session_frame_to_track_frame((*i).end, speed);
1486 playlist->clear_changes ();
1487 playlist->clear_owned_changes ();
1489 playlist->fade_range (time);
1491 vector<Command*> cmds;
1492 playlist->rdiff (cmds);
1493 _session->add_commands (cmds);
1494 _session->add_command (new StatefulDiffCommand (playlist));
1499 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1501 boost::shared_ptr<Playlist> what_we_got;
1502 boost::shared_ptr<Track> tr = track ();
1503 boost::shared_ptr<Playlist> playlist;
1506 /* route is a bus, not a track */
1510 playlist = tr->playlist();
1512 TimeSelection time (selection.time);
1513 float const speed = tr->speed();
1514 if (speed != 1.0f) {
1515 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1516 (*i).start = session_frame_to_track_frame((*i).start, speed);
1517 (*i).end = session_frame_to_track_frame((*i).end, speed);
1521 playlist->clear_changes ();
1522 playlist->clear_owned_changes ();
1526 if (playlist->cut (time) != 0) {
1527 if (Config->get_edit_mode() == Ripple)
1528 playlist->ripple(time.start(), -time.length(), NULL);
1529 // no need to exclude any regions from rippling here
1531 vector<Command*> cmds;
1532 playlist->rdiff (cmds);
1533 _session->add_commands (cmds);
1535 _session->add_command (new StatefulDiffCommand (playlist));
1540 if ((what_we_got = playlist->cut (time)) != 0) {
1541 _editor.get_cut_buffer().add (what_we_got);
1542 if (Config->get_edit_mode() == Ripple)
1543 playlist->ripple(time.start(), -time.length(), NULL);
1544 // no need to exclude any regions from rippling here
1546 vector<Command*> cmds;
1547 playlist->rdiff (cmds);
1548 _session->add_commands (cmds);
1550 _session->add_command (new StatefulDiffCommand (playlist));
1554 if ((what_we_got = playlist->copy (time)) != 0) {
1555 _editor.get_cut_buffer().add (what_we_got);
1560 if ((what_we_got = playlist->cut (time)) != 0) {
1561 if (Config->get_edit_mode() == Ripple)
1562 playlist->ripple(time.start(), -time.length(), NULL);
1563 // no need to exclude any regions from rippling here
1565 vector<Command*> cmds;
1566 playlist->rdiff (cmds);
1567 _session->add_commands (cmds);
1568 _session->add_command (new StatefulDiffCommand (playlist));
1569 what_we_got->release ();
1576 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1582 boost::shared_ptr<Playlist> pl = playlist ();
1583 const ARDOUR::DataType type = pl->data_type();
1584 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1586 if (p == selection.playlists.end()) {
1589 ctx.counts.increase_n_playlists(type);
1591 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1593 if (track()->speed() != 1.0f) {
1594 pos = session_frame_to_track_frame (pos, track()->speed());
1595 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1598 /* add multi-paste offset if applicable */
1599 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1600 const framecnt_t duration = extent.second - extent.first;
1601 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1603 pl->clear_changes ();
1604 if (Config->get_edit_mode() == Ripple) {
1605 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1606 framecnt_t amount = extent.second - extent.first;
1607 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1609 pl->paste (*p, pos, ctx.times);
1611 vector<Command*> cmds;
1613 _session->add_commands (cmds);
1615 _session->add_command (new StatefulDiffCommand (pl));
1621 struct PlaylistSorter {
1622 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1623 return a->sort_id() < b->sort_id();
1628 RouteTimeAxisView::build_playlist_menu ()
1630 using namespace Menu_Helpers;
1636 delete playlist_action_menu;
1637 playlist_action_menu = new Menu;
1638 playlist_action_menu->set_name ("ArdourContextMenu");
1640 MenuList& playlist_items = playlist_action_menu->items();
1641 playlist_action_menu->set_name ("ArdourContextMenu");
1642 playlist_items.clear();
1644 RadioMenuItem::Group playlist_group;
1645 boost::shared_ptr<Track> tr = track ();
1647 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1649 /* sort the playlists */
1651 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1653 /* add the playlists to the menu */
1654 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1655 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1656 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1657 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1659 if (tr->playlist()->id() == (*i)->id()) {
1665 playlist_items.push_back (SeparatorElem());
1666 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1667 playlist_items.push_back (SeparatorElem());
1669 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1670 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1671 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1674 // Use a label which tells the user what is happening
1675 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1676 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1680 playlist_items.push_back (SeparatorElem());
1681 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1682 playlist_items.push_back (SeparatorElem());
1684 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1688 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1690 assert (is_track());
1692 // exit if we were triggered by deactivating the old playlist
1693 if (!item->get_active()) {
1697 boost::shared_ptr<Playlist> pl (wpl.lock());
1703 if (track()->playlist() == pl) {
1704 // exit when use_playlist is called by the creation of the playlist menu
1705 // or the playlist choice is unchanged
1709 track()->use_playlist (pl);
1711 RouteGroup* rg = route_group();
1713 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1714 std::string group_string = "." + rg->name() + ".";
1716 std::string take_name = pl->name();
1717 std::string::size_type idx = take_name.find(group_string);
1719 if (idx == std::string::npos)
1722 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1724 boost::shared_ptr<RouteList> rl (rg->route_list());
1726 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1727 if ((*i) == this->route()) {
1731 std::string playlist_name = (*i)->name()+group_string+take_name;
1733 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1738 if (track->freeze_state() == Track::Frozen) {
1739 /* Don't change playlists of frozen tracks */
1743 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1745 // No playlist for this track for this take yet, make it
1746 track->use_new_playlist();
1747 track->playlist()->set_name(playlist_name);
1749 track->use_playlist(ipl);
1756 RouteTimeAxisView::update_playlist_tip ()
1758 RouteGroup* rg = route_group ();
1759 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1760 string group_string = "." + rg->name() + ".";
1762 string take_name = track()->playlist()->name();
1763 string::size_type idx = take_name.find(group_string);
1765 if (idx != string::npos) {
1766 /* find the bit containing the take number / name */
1767 take_name = take_name.substr (idx + group_string.length());
1769 /* set the playlist button tooltip to the take name */
1770 ARDOUR_UI::instance()->set_tip (
1772 string_compose(_("Take: %1.%2"),
1773 Glib::Markup::escape_text(rg->name()),
1774 Glib::Markup::escape_text(take_name))
1781 /* set the playlist button tooltip to the playlist name */
1782 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1787 RouteTimeAxisView::show_playlist_selector ()
1789 _editor.playlist_selector().show_for (this);
1793 RouteTimeAxisView::map_frozen ()
1799 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1801 switch (track()->freeze_state()) {
1803 playlist_button.set_sensitive (false);
1804 rec_enable_button->set_sensitive (false);
1807 playlist_button.set_sensitive (true);
1808 rec_enable_button->set_sensitive (true);
1814 RouteTimeAxisView::color_handler ()
1816 //case cTimeStretchOutline:
1817 if (timestretch_rect) {
1818 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_TimeStretchOutline());
1820 //case cTimeStretchFill:
1821 if (timestretch_rect) {
1822 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_TimeStretchFill());
1828 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1829 * Will add track if necessary.
1832 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1834 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1835 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1838 /* it doesn't exist yet, so we don't care about the button state: just add it */
1839 create_automation_child (param, true);
1842 bool yn = menu->get_active();
1843 bool changed = false;
1845 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1847 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1848 will have done that for us.
1851 if (changed && !no_redraw) {
1859 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1861 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1867 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1869 if (menu && !_hidden) {
1870 ignore_toggle = true;
1871 menu->set_active (false);
1872 ignore_toggle = false;
1875 if (_route && !no_redraw) {
1881 RouteTimeAxisView::update_gain_track_visibility ()
1883 bool const showit = gain_automation_item->get_active();
1885 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1886 gain_track->set_marked_for_display (showit);
1888 /* now trigger a redisplay */
1891 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1897 RouteTimeAxisView::update_mute_track_visibility ()
1899 bool const showit = mute_automation_item->get_active();
1901 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1902 mute_track->set_marked_for_display (showit);
1904 /* now trigger a redisplay */
1907 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1913 RouteTimeAxisView::update_pan_track_visibility ()
1915 bool const showit = pan_automation_item->get_active();
1916 bool changed = false;
1918 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1919 if ((*i)->set_marked_for_display (showit)) {
1925 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1930 RouteTimeAxisView::ensure_pan_views (bool show)
1932 bool changed = false;
1933 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1935 (*i)->set_marked_for_display (false);
1938 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1942 if (!_route->panner()) {
1946 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1947 set<Evoral::Parameter>::iterator p;
1949 for (p = params.begin(); p != params.end(); ++p) {
1950 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1952 if (pan_control->parameter().type() == NullAutomation) {
1953 error << "Pan control has NULL automation type!" << endmsg;
1957 if (automation_child (pan_control->parameter ()).get () == 0) {
1959 /* we don't already have an AutomationTimeAxisView for this parameter */
1961 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1963 boost::shared_ptr<AutomationTimeAxisView> t (
1964 new AutomationTimeAxisView (_session,
1968 pan_control->parameter (),
1976 pan_tracks.push_back (t);
1977 add_automation_child (*p, t, show);
1979 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1986 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1988 if (apply_to_selection) {
1989 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1993 /* Show our automation */
1995 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1996 i->second->set_marked_for_display (true);
1998 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2001 menu->set_active(true);
2006 /* Show processor automation */
2008 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2009 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2010 if ((*ii)->view == 0) {
2011 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2014 (*ii)->menu_item->set_active (true);
2027 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2029 if (apply_to_selection) {
2030 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2034 /* Show our automation */
2036 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2037 if (i->second->has_automation()) {
2038 i->second->set_marked_for_display (true);
2040 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2042 menu->set_active(true);
2047 /* Show processor automation */
2049 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2050 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2051 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2052 (*ii)->menu_item->set_active (true);
2064 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2066 if (apply_to_selection) {
2067 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2071 /* Hide our automation */
2073 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2074 i->second->set_marked_for_display (false);
2076 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2079 menu->set_active (false);
2083 /* Hide processor automation */
2085 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2086 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2087 (*ii)->menu_item->set_active (false);
2098 RouteTimeAxisView::region_view_added (RegionView* rv)
2100 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2101 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2102 boost::shared_ptr<AutomationTimeAxisView> atv;
2104 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2109 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2110 (*i)->add_ghost(rv);
2114 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2116 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2122 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2124 parent.remove_processor_automation_node (this);
2128 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2131 remove_child (pan->view);
2135 RouteTimeAxisView::ProcessorAutomationNode*
2136 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2138 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2140 if ((*i)->processor == processor) {
2142 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2143 if ((*ii)->what == what) {
2153 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2155 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2158 ProcessorAutomationNode* pan;
2160 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2161 /* session state may never have been saved with new plugin */
2162 error << _("programming error: ")
2163 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2164 processor->name(), what.type(), (int) what.channel(), what.id() )
2166 abort(); /*NOTREACHED*/
2174 boost::shared_ptr<AutomationControl> control
2175 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2177 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2178 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2179 _editor, *this, false, parent_canvas,
2180 processor->describe_parameter (what), processor->name()));
2182 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2184 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2187 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2192 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2195 pan->menu_item->set_active (false);
2204 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2206 boost::shared_ptr<Processor> processor (p.lock ());
2208 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2209 /* The Amp processor is a special case and is dealt with separately */
2213 set<Evoral::Parameter> existing;
2215 processor->what_has_data (existing);
2217 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2219 Evoral::Parameter param (*i);
2220 boost::shared_ptr<AutomationLine> al;
2222 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2225 add_processor_automation_curve (processor, param);
2231 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2233 using namespace Menu_Helpers;
2237 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2239 _automation_tracks[param] = track;
2241 /* existing state overrides "show" argument */
2242 string s = track->gui_property ("visible");
2244 show = string_is_affirmative (s);
2247 /* this might or might not change the visibility status, so don't rely on it */
2248 track->set_marked_for_display (show);
2250 if (show && !no_redraw) {
2254 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2255 /* MIDI-related parameters are always in the menu, there's no
2256 reason to rebuild the menu just because we added a automation
2257 lane for one of them. But if we add a non-MIDI automation
2258 lane, then we need to invalidate the display menu.
2260 delete display_menu;
2266 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2268 boost::shared_ptr<Processor> processor (p.lock ());
2270 if (!processor || !processor->display_to_user ()) {
2274 /* we use this override to veto the Amp processor from the plugin menu,
2275 as its automation lane can be accessed using the special "Fader" menu
2279 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2283 using namespace Menu_Helpers;
2284 ProcessorAutomationInfo *rai;
2285 list<ProcessorAutomationInfo*>::iterator x;
2287 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2289 if (automatable.empty()) {
2293 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2294 if ((*x)->processor == processor) {
2299 if (x == processor_automation.end()) {
2301 rai = new ProcessorAutomationInfo (processor);
2302 processor_automation.push_back (rai);
2310 /* any older menu was deleted at the top of processors_changed()
2311 when we cleared the subplugin menu.
2314 rai->menu = manage (new Menu);
2315 MenuList& items = rai->menu->items();
2316 rai->menu->set_name ("ArdourContextMenu");
2320 std::set<Evoral::Parameter> has_visible_automation;
2321 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2323 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2325 ProcessorAutomationNode* pan;
2326 Gtk::CheckMenuItem* mitem;
2328 string name = processor->describe_parameter (*i);
2330 items.push_back (CheckMenuElem (name));
2331 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2333 _subplugin_menu_map[*i] = mitem;
2335 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2336 mitem->set_active(true);
2339 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2343 pan = new ProcessorAutomationNode (*i, mitem, *this);
2345 rai->lines.push_back (pan);
2349 pan->menu_item = mitem;
2353 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2356 /* add the menu for this processor, because the subplugin
2357 menu is always cleared at the top of processors_changed().
2358 this is the result of some poor design in gtkmm and/or
2362 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2367 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2368 RouteTimeAxisView::ProcessorAutomationNode* pan)
2370 bool showit = pan->menu_item->get_active();
2371 bool redraw = false;
2373 if (pan->view == 0 && showit) {
2374 add_processor_automation_curve (rai->processor, pan->what);
2378 if (pan->view && pan->view->set_marked_for_display (showit)) {
2382 if (redraw && !no_redraw) {
2388 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2390 if (c.type == RouteProcessorChange::MeterPointChange) {
2391 /* nothing to do if only the meter point has changed */
2395 using namespace Menu_Helpers;
2397 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2398 (*i)->valid = false;
2401 setup_processor_menu_and_curves ();
2403 bool deleted_processor_automation = false;
2405 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2407 list<ProcessorAutomationInfo*>::iterator tmp;
2415 processor_automation.erase (i);
2416 deleted_processor_automation = true;
2423 if (deleted_processor_automation && !no_redraw) {
2428 boost::shared_ptr<AutomationLine>
2429 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2431 ProcessorAutomationNode* pan;
2433 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2439 return boost::shared_ptr<AutomationLine>();
2443 RouteTimeAxisView::reset_processor_automation_curves ()
2445 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2451 RouteTimeAxisView::can_edit_name () const
2453 /* we do not allow track name changes if it is record enabled
2455 return !_route->record_enabled();
2459 RouteTimeAxisView::blink_rec_display (bool onoff)
2461 RouteUI::blink_rec_display (onoff);
2465 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2467 if (_ignore_set_layer_display) {
2471 if (apply_to_selection) {
2472 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2476 _view->set_layer_display (d);
2479 set_gui_property (X_("layer-display"), enum_2_string (d));
2484 RouteTimeAxisView::layer_display () const
2487 return _view->layer_display ();
2490 /* we don't know, since we don't have a _view, so just return something */
2496 boost::shared_ptr<AutomationTimeAxisView>
2497 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2499 AutomationTracks::iterator i = _automation_tracks.find(param);
2500 if (i != _automation_tracks.end()) {
2503 return boost::shared_ptr<AutomationTimeAxisView>();
2508 RouteTimeAxisView::fast_update ()
2510 gm.get_level_meter().update_meters ();
2514 RouteTimeAxisView::hide_meter ()
2517 gm.get_level_meter().hide_meters ();
2521 RouteTimeAxisView::show_meter ()
2527 RouteTimeAxisView::reset_meter ()
2529 if (Config->get_show_track_meters()) {
2530 int meter_width = 3;
2531 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2534 gm.get_level_meter().setup_meters (height - 9, meter_width);
2541 RouteTimeAxisView::clear_meter ()
2543 gm.get_level_meter().clear_meters ();
2547 RouteTimeAxisView::meter_changed ()
2549 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2551 if (_route && !no_redraw) {
2554 // reset peak when meter point changes
2555 gm.reset_peak_display();
2559 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2562 if (_route && !no_redraw) {
2568 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2570 using namespace Menu_Helpers;
2572 if (!_underlay_streams.empty()) {
2573 MenuList& parent_items = parent_menu->items();
2574 Menu* gs_menu = manage (new Menu);
2575 gs_menu->set_name ("ArdourContextMenu");
2576 MenuList& gs_items = gs_menu->items();
2578 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2580 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2581 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2582 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2588 RouteTimeAxisView::set_underlay_state()
2590 if (!underlay_xml_node) {
2594 XMLNodeList nlist = underlay_xml_node->children();
2595 XMLNodeConstIterator niter;
2596 XMLNode *child_node;
2598 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2599 child_node = *niter;
2601 if (child_node->name() != "Underlay") {
2605 XMLProperty* prop = child_node->property ("id");
2607 PBD::ID id (prop->value());
2609 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2612 add_underlay(v->view(), false);
2621 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2627 RouteTimeAxisView& other = v->trackview();
2629 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2630 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2631 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2632 abort(); /*NOTREACHED*/
2635 _underlay_streams.push_back(v);
2636 other._underlay_mirrors.push_back(this);
2638 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2640 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2642 if (!underlay_xml_node) {
2643 underlay_xml_node = xml_node->add_child("Underlays");
2646 XMLNode* node = underlay_xml_node->add_child("Underlay");
2647 XMLProperty* prop = node->add_property("id");
2648 prop->set_value(v->trackview().route()->id().to_s());
2655 RouteTimeAxisView::remove_underlay (StreamView* v)
2661 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2662 RouteTimeAxisView& other = v->trackview();
2664 if (it != _underlay_streams.end()) {
2665 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2667 if (gm == other._underlay_mirrors.end()) {
2668 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2669 abort(); /*NOTREACHED*/
2672 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2674 _underlay_streams.erase(it);
2675 other._underlay_mirrors.erase(gm);
2677 if (underlay_xml_node) {
2678 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2684 RouteTimeAxisView::set_button_names ()
2686 if (_route && _route->solo_safe()) {
2687 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2689 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2691 if (Config->get_solo_control_is_listen_control()) {
2692 switch (Config->get_listen_position()) {
2693 case AfterFaderListen:
2694 solo_button->set_text (_("A"));
2695 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2697 case PreFaderListen:
2698 solo_button->set_text (_("P"));
2699 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2703 solo_button->set_text (_("S"));
2704 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2706 mute_button->set_text (_("M"));
2710 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2712 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2713 if (i != _main_automation_menu_map.end()) {
2717 i = _subplugin_menu_map.find (param);
2718 if (i != _subplugin_menu_map.end()) {
2726 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2728 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2730 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2734 gain_track.reset (new AutomationTimeAxisView (_session,
2735 _route, _route->amp(), c, param,
2740 _route->amp()->describe_parameter(param)));
2743 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2746 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2750 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2752 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2754 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2758 mute_track.reset (new AutomationTimeAxisView (_session,
2759 _route, _route, c, param,
2764 _route->describe_parameter(param)));
2767 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2770 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2774 void add_region_to_list (RegionView* rv, RegionList* l)
2776 l->push_back (rv->region());
2780 RouteTimeAxisView::combine_regions ()
2782 /* as of may 2011, we do not offer uncombine for MIDI tracks
2785 if (!is_audio_track()) {
2793 RegionList selected_regions;
2794 boost::shared_ptr<Playlist> playlist = track()->playlist();
2796 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2798 if (selected_regions.size() < 2) {
2802 playlist->clear_changes ();
2803 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2805 _session->add_command (new StatefulDiffCommand (playlist));
2806 /* make the new region be selected */
2808 return _view->find_view (compound_region);
2812 RouteTimeAxisView::uncombine_regions ()
2814 /* as of may 2011, we do not offer uncombine for MIDI tracks
2816 if (!is_audio_track()) {
2824 RegionList selected_regions;
2825 boost::shared_ptr<Playlist> playlist = track()->playlist();
2827 /* have to grab selected regions first because the uncombine is going
2828 * to change that in the middle of the list traverse
2831 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2833 playlist->clear_changes ();
2835 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2836 playlist->uncombine (*i);
2839 _session->add_command (new StatefulDiffCommand (playlist));
2843 RouteTimeAxisView::state_id() const
2845 return string_compose ("rtav %1", _route->id().to_s());
2850 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2852 TimeAxisView::remove_child (c);
2854 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2856 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2857 if (i->second == a) {
2858 _automation_tracks.erase (i);