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 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
330 delete playlist_action_menu;
331 playlist_action_menu = 0;
336 _automation_tracks.clear ();
338 delete route_group_menu;
342 RouteTimeAxisView::post_construct ()
344 /* map current state of the route */
346 update_diskstream_display ();
347 setup_processor_menu_and_curves ();
348 reset_processor_automation_curves ();
351 /** Set up the processor menu for the current set of processors, and
352 * display automation curves for any parameters which have data.
355 RouteTimeAxisView::setup_processor_menu_and_curves ()
357 _subplugin_menu_map.clear ();
358 subplugin_menu.items().clear ();
359 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
360 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
364 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
366 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
367 if (_route->route_group()) {
368 _route->route_group()->remove (_route);
374 r.push_back (route ());
376 route_group_menu->build (r);
377 route_group_menu->menu()->popup (ev->button, ev->time);
383 RouteTimeAxisView::playlist_changed ()
389 RouteTimeAxisView::label_view ()
391 string x = _route->name ();
392 if (x != name_label.get_text ()) {
393 name_label.set_text (x);
395 const int64_t track_number = _route->track_number ();
396 if (track_number == 0) {
397 number_label.set_text ("");
399 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
404 RouteTimeAxisView::update_track_number_visibility ()
407 bool show_label = _session->config.get_track_name_number();
409 if (_route && _route->is_master()) {
413 if (number_label.get_parent()) {
414 controls_table.remove (number_label);
417 if (ARDOUR::Profile->get_mixbus()) {
418 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
420 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
422 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
423 // except the width of the number label is subtracted from the name-hbox, so we
424 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
425 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
427 number_label.set_size_request(tnw, -1);
428 number_label.show ();
429 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
431 number_label.hide ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
437 RouteTimeAxisView::parameter_changed (string const & p)
439 if (p == "track-name-number") {
440 update_track_number_visibility();
445 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
447 if (what_changed.contains (ARDOUR::Properties::name)) {
453 RouteTimeAxisView::take_name_changed (void *src)
461 RouteTimeAxisView::playlist_click ()
463 build_playlist_menu ();
464 conditionally_add_to_selection ();
465 playlist_action_menu->popup (1, gtk_get_current_event_time());
469 RouteTimeAxisView::automation_click ()
471 conditionally_add_to_selection ();
472 build_automation_action_menu (false);
473 automation_action_menu->popup (1, gtk_get_current_event_time());
477 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
479 using namespace Menu_Helpers;
481 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
482 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
485 detach_menu (subplugin_menu);
487 _main_automation_menu_map.clear ();
488 delete automation_action_menu;
489 automation_action_menu = new Menu;
491 MenuList& items = automation_action_menu->items();
493 automation_action_menu->set_name ("ArdourContextMenu");
495 items.push_back (MenuElem (_("Show All Automation"),
496 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
498 items.push_back (MenuElem (_("Show Existing Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
501 items.push_back (MenuElem (_("Hide All Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
504 /* Attach the plugin submenu. It may have previously been used elsewhere,
505 so it was detached above
508 if (!subplugin_menu.items().empty()) {
509 items.push_back (SeparatorElem ());
510 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
511 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
514 /* Add any route automation */
517 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
518 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
519 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
520 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
522 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
526 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
527 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
528 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
529 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
531 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
534 if (!pan_tracks.empty()) {
535 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
536 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
537 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
538 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
540 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
541 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
542 _main_automation_menu_map[*p] = pan_automation_item;
548 RouteTimeAxisView::build_display_menu ()
550 using namespace Menu_Helpers;
554 TimeAxisView::build_display_menu ();
556 /* now fill it with our stuff */
558 MenuList& items = display_menu->items();
559 display_menu->set_name ("ArdourContextMenu");
561 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
563 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
565 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
567 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
569 items.push_back (SeparatorElem());
572 detach_menu (*_size_menu);
575 items.push_back (MenuElem (_("Height"), *_size_menu));
577 items.push_back (SeparatorElem());
579 if (!Profile->get_sae()) {
580 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
581 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
582 items.push_back (SeparatorElem());
585 // Hook for derived classes to add type specific stuff
586 append_extra_display_menu_items ();
590 Menu* layers_menu = manage (new Menu);
591 MenuList &layers_items = layers_menu->items();
592 layers_menu->set_name("ArdourContextMenu");
594 RadioMenuItem::Group layers_group;
596 /* Find out how many overlaid/stacked tracks we have in the selection */
600 TrackSelection const & s = _editor.get_selection().tracks;
601 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
602 StreamView* v = (*i)->view ();
607 switch (v->layer_display ()) {
618 /* We're not connecting to signal_toggled() here; in the case where these two items are
619 set to be in the `inconsistent' state, it seems that one or other will end up active
620 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
621 select the active one, no toggled signal is emitted so nothing happens.
624 _ignore_set_layer_display = true;
626 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
627 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
628 i->set_active (overlaid != 0 && stacked == 0);
629 i->set_inconsistent (overlaid != 0 && stacked != 0);
630 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
632 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
633 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
634 i->set_active (overlaid == 0 && stacked != 0);
635 i->set_inconsistent (overlaid != 0 && stacked != 0);
636 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
638 _ignore_set_layer_display = false;
640 items.push_back (MenuElem (_("Layers"), *layers_menu));
642 if (!Profile->get_sae()) {
644 Menu* alignment_menu = manage (new Menu);
645 MenuList& alignment_items = alignment_menu->items();
646 alignment_menu->set_name ("ArdourContextMenu");
648 RadioMenuItem::Group align_group;
650 /* Same verbose hacks as for the layering options above */
656 boost::shared_ptr<Track> first_track;
658 TrackSelection const & s = _editor.get_selection().tracks;
659 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
660 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
661 if (!r || !r->is_track ()) {
666 first_track = r->track();
669 switch (r->track()->alignment_choice()) {
673 switch (r->track()->alignment_style()) {
674 case ExistingMaterial:
682 case UseExistingMaterial:
698 inconsistent = false;
707 if (!inconsistent && first_track) {
709 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
710 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
711 i->set_active (automatic != 0 && existing == 0 && capture == 0);
712 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
714 switch (first_track->alignment_choice()) {
716 switch (first_track->alignment_style()) {
717 case ExistingMaterial:
718 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
721 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
729 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
730 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
731 i->set_active (existing != 0 && capture == 0 && automatic == 0);
732 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
734 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
735 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
736 i->set_active (existing == 0 && capture != 0 && automatic == 0);
737 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
739 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
745 Menu* mode_menu = manage (new Menu);
746 MenuList& mode_items = mode_menu->items ();
747 mode_menu->set_name ("ArdourContextMenu");
749 RadioMenuItem::Group mode_group;
755 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
756 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
757 if (!r || !r->is_track ()) {
761 switch (r->track()->mode()) {
774 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
775 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
776 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
777 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
778 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
780 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
781 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
782 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
783 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
784 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
786 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
787 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
789 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
790 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
792 items.push_back (MenuElem (_("Mode"), *mode_menu));
796 items.push_back (SeparatorElem());
798 build_playlist_menu ();
799 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
800 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
803 route_group_menu->detach ();
806 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
807 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
809 r.push_back (rtv->route ());
814 r.push_back (route ());
817 route_group_menu->build (r);
818 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
820 build_automation_action_menu (true);
821 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
823 items.push_back (SeparatorElem());
827 TrackSelection const & s = _editor.get_selection().tracks;
828 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
829 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
834 if (r->route()->active()) {
841 items.push_back (CheckMenuElem (_("Active")));
842 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
843 bool click_sets_active = true;
844 if (active > 0 && inactive == 0) {
845 i->set_active (true);
846 click_sets_active = false;
847 } else if (active > 0 && inactive > 0) {
848 i->set_inconsistent (true);
850 i->set_sensitive(! _session->transport_rolling());
851 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
853 items.push_back (SeparatorElem());
854 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
855 if (!Profile->get_sae()) {
856 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
858 items.push_front (SeparatorElem());
859 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
864 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
866 if (apply_to_selection) {
867 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
870 bool needs_bounce = false;
872 if (!track()->can_use_mode (mode, needs_bounce)) {
878 cerr << "would bounce this one\n";
883 track()->set_mode (mode);
888 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
890 TimeAxisView::show_timestretch (start, end, layers, layer);
900 /* check that the time selection was made in our route, or our route group.
901 remember that route_group() == 0 implies the route is *not* in a edit group.
904 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
905 /* this doesn't apply to us */
909 /* ignore it if our edit group is not active */
911 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
916 if (timestretch_rect == 0) {
917 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
918 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
919 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
922 timestretch_rect->show ();
923 timestretch_rect->raise_to_top ();
925 double const x1 = start / _editor.get_current_zoom();
926 double const x2 = (end - 1) / _editor.get_current_zoom();
928 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
929 x2, current_height() * (layers - layer) / layers));
933 RouteTimeAxisView::hide_timestretch ()
935 TimeAxisView::hide_timestretch ();
937 if (timestretch_rect) {
938 timestretch_rect->hide ();
943 RouteTimeAxisView::show_selection (TimeSelection& ts)
947 /* ignore it if our edit group is not active or if the selection was started
948 in some other track or route group (remember that route_group() == 0 means
949 that the track is not in an route group).
952 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
953 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
959 TimeAxisView::show_selection (ts);
963 RouteTimeAxisView::set_height (uint32_t h)
966 bool height_changed = (height == 0) || (h != height);
969 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
972 gm.get_level_meter().setup_meters (gmlen, meter_width);
974 TimeAxisView::set_height (h);
977 _view->set_height ((double) current_height());
980 if (height >= preset_height (HeightNormal)) {
984 gm.get_gain_slider().show();
986 if (!_route || _route->is_monitor()) {
991 if (rec_enable_button)
992 rec_enable_button->show();
994 route_group_button.show();
995 automation_button.show();
997 if (is_track() && track()->mode() == ARDOUR::Normal) {
998 playlist_button.show();
1005 gm.get_gain_slider().hide();
1006 mute_button->show();
1007 if (!_route || _route->is_monitor()) {
1008 solo_button->hide();
1010 solo_button->show();
1012 if (rec_enable_button)
1013 rec_enable_button->show();
1015 route_group_button.hide ();
1016 automation_button.hide ();
1018 if (is_track() && track()->mode() == ARDOUR::Normal) {
1019 playlist_button.hide ();
1024 if (height_changed && !no_redraw) {
1025 /* only emit the signal if the height really changed */
1031 RouteTimeAxisView::route_color_changed ()
1034 _view->apply_color (color(), StreamView::RegionColor);
1037 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1041 RouteTimeAxisView::reset_samples_per_pixel ()
1043 set_samples_per_pixel (_editor.get_current_zoom());
1047 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1052 speed = track()->speed();
1056 _view->set_samples_per_pixel (fpp * speed);
1059 TimeAxisView::set_samples_per_pixel (fpp * speed);
1063 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1065 if (!mitem->get_active()) {
1066 /* this is one of the two calls made when these radio menu items change status. this one
1067 is for the item that became inactive, and we want to ignore it.
1072 if (apply_to_selection) {
1073 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1076 track()->set_align_choice (choice);
1082 RouteTimeAxisView::rename_current_playlist ()
1084 ArdourPrompter prompter (true);
1087 boost::shared_ptr<Track> tr = track();
1088 if (!tr || tr->destructive()) {
1092 boost::shared_ptr<Playlist> pl = tr->playlist();
1097 prompter.set_title (_("Rename Playlist"));
1098 prompter.set_prompt (_("New name for playlist:"));
1099 prompter.set_initial_text (pl->name());
1100 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1101 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1103 switch (prompter.run ()) {
1104 case Gtk::RESPONSE_ACCEPT:
1105 prompter.get_result (name);
1106 if (name.length()) {
1107 pl->set_name (name);
1117 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1119 std::string ret (basename);
1121 std::string const group_string = "." + route_group()->name() + ".";
1123 // iterate through all playlists
1125 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1126 std::string tmp = (*i)->name();
1128 std::string::size_type idx = tmp.find(group_string);
1129 // find those which belong to this group
1130 if (idx != string::npos) {
1131 tmp = tmp.substr(idx + group_string.length());
1133 // and find the largest current number
1135 if (x > maxnumber) {
1144 snprintf (buf, sizeof(buf), "%d", maxnumber);
1146 ret = this->name() + "." + route_group()->name () + "." + buf;
1152 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1156 boost::shared_ptr<Track> tr = track ();
1157 if (!tr || tr->destructive()) {
1161 boost::shared_ptr<const Playlist> pl = tr->playlist();
1168 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1169 name = resolve_new_group_playlist_name(name, playlists_before_op);
1172 while (_session->playlists->by_name(name)) {
1173 name = Playlist::bump_name (name, *_session);
1176 // TODO: The prompter "new" button should be de-activated if the user
1177 // specifies a playlist name which already exists in the session.
1181 ArdourPrompter prompter (true);
1183 prompter.set_title (_("New Copy Playlist"));
1184 prompter.set_prompt (_("Name for new playlist:"));
1185 prompter.set_initial_text (name);
1186 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1187 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1188 prompter.show_all ();
1190 switch (prompter.run ()) {
1191 case Gtk::RESPONSE_ACCEPT:
1192 prompter.get_result (name);
1200 if (name.length()) {
1201 tr->use_copy_playlist ();
1202 tr->playlist()->set_name (name);
1207 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1211 boost::shared_ptr<Track> tr = track ();
1212 if (!tr || tr->destructive()) {
1216 boost::shared_ptr<const Playlist> pl = tr->playlist();
1223 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1224 name = resolve_new_group_playlist_name(name,playlists_before_op);
1227 while (_session->playlists->by_name(name)) {
1228 name = Playlist::bump_name (name, *_session);
1234 ArdourPrompter prompter (true);
1236 prompter.set_title (_("New Playlist"));
1237 prompter.set_prompt (_("Name for new playlist:"));
1238 prompter.set_initial_text (name);
1239 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1240 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1242 switch (prompter.run ()) {
1243 case Gtk::RESPONSE_ACCEPT:
1244 prompter.get_result (name);
1252 if (name.length()) {
1253 tr->use_new_playlist ();
1254 tr->playlist()->set_name (name);
1259 RouteTimeAxisView::clear_playlist ()
1261 boost::shared_ptr<Track> tr = track ();
1262 if (!tr || tr->destructive()) {
1266 boost::shared_ptr<Playlist> pl = tr->playlist();
1271 _editor.clear_playlist (pl);
1275 RouteTimeAxisView::speed_changed ()
1277 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1281 RouteTimeAxisView::update_diskstream_display ()
1291 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1293 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1295 /* special case: select/deselect all tracks */
1296 if (_editor.get_selection().selected (this)) {
1297 _editor.get_selection().clear_tracks ();
1299 _editor.select_all_tracks ();
1305 switch (ArdourKeyboard::selection_type (ev->state)) {
1306 case Selection::Toggle:
1307 _editor.get_selection().toggle (this);
1310 case Selection::Set:
1311 _editor.get_selection().set (this);
1314 case Selection::Extend:
1315 _editor.extend_selection_to_track (*this);
1318 case Selection::Add:
1319 _editor.get_selection().add (this);
1325 RouteTimeAxisView::set_selected_points (PointSelection& points)
1327 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1328 (*i)->set_selected_points (points);
1333 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1336 _view->set_selected_regionviews (regions);
1340 /** Add the selectable things that we have to a list.
1341 * @param results List to add things to.
1344 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1349 speed = track()->speed();
1352 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1353 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1355 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1356 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1359 /* pick up visible automation tracks */
1361 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1362 if (!(*i)->hidden()) {
1363 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1369 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1372 _view->get_inverted_selectables (sel, results);
1375 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1376 if (!(*i)->hidden()) {
1377 (*i)->get_inverted_selectables (sel, results);
1385 RouteTimeAxisView::route_group () const
1387 return _route->route_group();
1391 RouteTimeAxisView::name() const
1393 return _route->name();
1396 boost::shared_ptr<Playlist>
1397 RouteTimeAxisView::playlist () const
1399 boost::shared_ptr<Track> tr;
1401 if ((tr = track()) != 0) {
1402 return tr->playlist();
1404 return boost::shared_ptr<Playlist> ();
1409 RouteTimeAxisView::name_entry_changed ()
1411 TimeAxisView::name_entry_changed ();
1413 string x = name_entry->get_text ();
1415 if (x == _route->name()) {
1419 strip_whitespace_edges (x);
1421 if (x.length() == 0) {
1422 name_entry->set_text (_route->name());
1426 if (_session->route_name_internal (x)) {
1427 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1429 name_entry->grab_focus ();
1430 } else if (RouteUI::verify_new_route_name (x)) {
1431 _route->set_name (x);
1433 name_entry->grab_focus ();
1437 boost::shared_ptr<Region>
1438 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1440 boost::shared_ptr<Playlist> pl = playlist ();
1443 return pl->find_next_region (pos, point, dir);
1446 return boost::shared_ptr<Region> ();
1450 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1452 boost::shared_ptr<Playlist> pl = playlist ();
1455 return pl->find_next_region_boundary (pos, dir);
1462 RouteTimeAxisView::fade_range (TimeSelection& selection)
1464 boost::shared_ptr<Playlist> what_we_got;
1465 boost::shared_ptr<Track> tr = track ();
1466 boost::shared_ptr<Playlist> playlist;
1469 /* route is a bus, not a track */
1473 playlist = tr->playlist();
1475 TimeSelection time (selection);
1476 float const speed = tr->speed();
1477 if (speed != 1.0f) {
1478 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1479 (*i).start = session_frame_to_track_frame((*i).start, speed);
1480 (*i).end = session_frame_to_track_frame((*i).end, speed);
1484 playlist->clear_changes ();
1485 playlist->clear_owned_changes ();
1487 playlist->fade_range (time);
1489 vector<Command*> cmds;
1490 playlist->rdiff (cmds);
1491 _session->add_commands (cmds);
1492 _session->add_command (new StatefulDiffCommand (playlist));
1497 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1499 boost::shared_ptr<Playlist> what_we_got;
1500 boost::shared_ptr<Track> tr = track ();
1501 boost::shared_ptr<Playlist> playlist;
1504 /* route is a bus, not a track */
1508 playlist = tr->playlist();
1510 TimeSelection time (selection.time);
1511 float const speed = tr->speed();
1512 if (speed != 1.0f) {
1513 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1514 (*i).start = session_frame_to_track_frame((*i).start, speed);
1515 (*i).end = session_frame_to_track_frame((*i).end, speed);
1519 playlist->clear_changes ();
1520 playlist->clear_owned_changes ();
1524 if (playlist->cut (time) != 0) {
1525 if (Config->get_edit_mode() == Ripple)
1526 playlist->ripple(time.start(), -time.length(), NULL);
1527 // no need to exclude any regions from rippling here
1529 vector<Command*> cmds;
1530 playlist->rdiff (cmds);
1531 _session->add_commands (cmds);
1533 _session->add_command (new StatefulDiffCommand (playlist));
1538 if ((what_we_got = playlist->cut (time)) != 0) {
1539 _editor.get_cut_buffer().add (what_we_got);
1540 if (Config->get_edit_mode() == Ripple)
1541 playlist->ripple(time.start(), -time.length(), NULL);
1542 // no need to exclude any regions from rippling here
1544 vector<Command*> cmds;
1545 playlist->rdiff (cmds);
1546 _session->add_commands (cmds);
1548 _session->add_command (new StatefulDiffCommand (playlist));
1552 if ((what_we_got = playlist->copy (time)) != 0) {
1553 _editor.get_cut_buffer().add (what_we_got);
1558 if ((what_we_got = playlist->cut (time)) != 0) {
1559 if (Config->get_edit_mode() == Ripple)
1560 playlist->ripple(time.start(), -time.length(), NULL);
1561 // no need to exclude any regions from rippling here
1563 vector<Command*> cmds;
1564 playlist->rdiff (cmds);
1565 _session->add_commands (cmds);
1566 _session->add_command (new StatefulDiffCommand (playlist));
1567 what_we_got->release ();
1574 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1580 boost::shared_ptr<Playlist> pl = playlist ();
1581 const ARDOUR::DataType type = pl->data_type();
1582 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1584 if (p == selection.playlists.end()) {
1587 ctx.counts.increase_n_playlists(type);
1589 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1591 if (track()->speed() != 1.0f) {
1592 pos = session_frame_to_track_frame (pos, track()->speed());
1593 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1596 /* add multi-paste offset if applicable */
1597 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1598 const framecnt_t duration = extent.second - extent.first;
1599 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1601 pl->clear_changes ();
1602 if (Config->get_edit_mode() == Ripple) {
1603 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1604 framecnt_t amount = extent.second - extent.first;
1605 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1607 pl->paste (*p, pos, ctx.times);
1609 vector<Command*> cmds;
1611 _session->add_commands (cmds);
1613 _session->add_command (new StatefulDiffCommand (pl));
1619 struct PlaylistSorter {
1620 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1621 return a->sort_id() < b->sort_id();
1626 RouteTimeAxisView::build_playlist_menu ()
1628 using namespace Menu_Helpers;
1634 delete playlist_action_menu;
1635 playlist_action_menu = new Menu;
1636 playlist_action_menu->set_name ("ArdourContextMenu");
1638 MenuList& playlist_items = playlist_action_menu->items();
1639 playlist_action_menu->set_name ("ArdourContextMenu");
1640 playlist_items.clear();
1642 RadioMenuItem::Group playlist_group;
1643 boost::shared_ptr<Track> tr = track ();
1645 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1647 /* sort the playlists */
1649 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1651 /* add the playlists to the menu */
1652 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1653 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1654 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1655 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1657 if (tr->playlist()->id() == (*i)->id()) {
1663 playlist_items.push_back (SeparatorElem());
1664 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1665 playlist_items.push_back (SeparatorElem());
1667 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1668 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1669 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1672 // Use a label which tells the user what is happening
1673 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1674 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1678 playlist_items.push_back (SeparatorElem());
1679 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1680 playlist_items.push_back (SeparatorElem());
1682 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1686 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1688 assert (is_track());
1690 // exit if we were triggered by deactivating the old playlist
1691 if (!item->get_active()) {
1695 boost::shared_ptr<Playlist> pl (wpl.lock());
1701 if (track()->playlist() == pl) {
1702 // exit when use_playlist is called by the creation of the playlist menu
1703 // or the playlist choice is unchanged
1707 track()->use_playlist (pl);
1709 RouteGroup* rg = route_group();
1711 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1712 std::string group_string = "." + rg->name() + ".";
1714 std::string take_name = pl->name();
1715 std::string::size_type idx = take_name.find(group_string);
1717 if (idx == std::string::npos)
1720 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1722 boost::shared_ptr<RouteList> rl (rg->route_list());
1724 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1725 if ((*i) == this->route()) {
1729 std::string playlist_name = (*i)->name()+group_string+take_name;
1731 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1736 if (track->freeze_state() == Track::Frozen) {
1737 /* Don't change playlists of frozen tracks */
1741 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1743 // No playlist for this track for this take yet, make it
1744 track->use_new_playlist();
1745 track->playlist()->set_name(playlist_name);
1747 track->use_playlist(ipl);
1754 RouteTimeAxisView::update_playlist_tip ()
1756 RouteGroup* rg = route_group ();
1757 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1758 string group_string = "." + rg->name() + ".";
1760 string take_name = track()->playlist()->name();
1761 string::size_type idx = take_name.find(group_string);
1763 if (idx != string::npos) {
1764 /* find the bit containing the take number / name */
1765 take_name = take_name.substr (idx + group_string.length());
1767 /* set the playlist button tooltip to the take name */
1768 ARDOUR_UI::instance()->set_tip (
1770 string_compose(_("Take: %1.%2"),
1771 Glib::Markup::escape_text(rg->name()),
1772 Glib::Markup::escape_text(take_name))
1779 /* set the playlist button tooltip to the playlist name */
1780 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1785 RouteTimeAxisView::show_playlist_selector ()
1787 _editor.playlist_selector().show_for (this);
1791 RouteTimeAxisView::map_frozen ()
1797 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1799 switch (track()->freeze_state()) {
1801 playlist_button.set_sensitive (false);
1802 rec_enable_button->set_sensitive (false);
1805 playlist_button.set_sensitive (true);
1806 rec_enable_button->set_sensitive (true);
1812 RouteTimeAxisView::color_handler ()
1814 //case cTimeStretchOutline:
1815 if (timestretch_rect) {
1816 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1818 //case cTimeStretchFill:
1819 if (timestretch_rect) {
1820 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1826 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1827 * Will add track if necessary.
1830 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1832 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1833 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1836 /* it doesn't exist yet, so we don't care about the button state: just add it */
1837 create_automation_child (param, true);
1840 bool yn = menu->get_active();
1841 bool changed = false;
1843 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1845 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1846 will have done that for us.
1849 if (changed && !no_redraw) {
1857 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1859 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1865 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1867 if (menu && !_hidden) {
1868 ignore_toggle = true;
1869 menu->set_active (false);
1870 ignore_toggle = false;
1873 if (_route && !no_redraw) {
1879 RouteTimeAxisView::update_gain_track_visibility ()
1881 bool const showit = gain_automation_item->get_active();
1883 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1884 gain_track->set_marked_for_display (showit);
1886 /* now trigger a redisplay */
1889 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1895 RouteTimeAxisView::update_mute_track_visibility ()
1897 bool const showit = mute_automation_item->get_active();
1899 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1900 mute_track->set_marked_for_display (showit);
1902 /* now trigger a redisplay */
1905 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1911 RouteTimeAxisView::update_pan_track_visibility ()
1913 bool const showit = pan_automation_item->get_active();
1914 bool changed = false;
1916 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1917 if ((*i)->set_marked_for_display (showit)) {
1923 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1928 RouteTimeAxisView::ensure_pan_views (bool show)
1930 bool changed = false;
1931 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1933 (*i)->set_marked_for_display (false);
1936 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1940 if (!_route->panner()) {
1944 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1945 set<Evoral::Parameter>::iterator p;
1947 for (p = params.begin(); p != params.end(); ++p) {
1948 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1950 if (pan_control->parameter().type() == NullAutomation) {
1951 error << "Pan control has NULL automation type!" << endmsg;
1955 if (automation_child (pan_control->parameter ()).get () == 0) {
1957 /* we don't already have an AutomationTimeAxisView for this parameter */
1959 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1961 boost::shared_ptr<AutomationTimeAxisView> t (
1962 new AutomationTimeAxisView (_session,
1966 pan_control->parameter (),
1974 pan_tracks.push_back (t);
1975 add_automation_child (*p, t, show);
1977 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1984 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1986 if (apply_to_selection) {
1987 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1991 /* Show our automation */
1993 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1994 i->second->set_marked_for_display (true);
1996 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1999 menu->set_active(true);
2004 /* Show processor automation */
2006 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2007 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2008 if ((*ii)->view == 0) {
2009 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2012 (*ii)->menu_item->set_active (true);
2025 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2027 if (apply_to_selection) {
2028 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2032 /* Show our automation */
2034 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2035 if (i->second->has_automation()) {
2036 i->second->set_marked_for_display (true);
2038 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2040 menu->set_active(true);
2045 /* Show processor automation */
2047 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2048 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2049 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2050 (*ii)->menu_item->set_active (true);
2062 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2064 if (apply_to_selection) {
2065 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2069 /* Hide our automation */
2071 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2072 i->second->set_marked_for_display (false);
2074 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2077 menu->set_active (false);
2081 /* Hide processor automation */
2083 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2084 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2085 (*ii)->menu_item->set_active (false);
2096 RouteTimeAxisView::region_view_added (RegionView* rv)
2098 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2099 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2100 boost::shared_ptr<AutomationTimeAxisView> atv;
2102 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2107 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2108 (*i)->add_ghost(rv);
2112 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2114 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2120 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2122 parent.remove_processor_automation_node (this);
2126 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2129 remove_child (pan->view);
2133 RouteTimeAxisView::ProcessorAutomationNode*
2134 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2136 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2138 if ((*i)->processor == processor) {
2140 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2141 if ((*ii)->what == what) {
2151 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2153 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2156 ProcessorAutomationNode* pan;
2158 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2159 /* session state may never have been saved with new plugin */
2160 error << _("programming error: ")
2161 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2162 processor->name(), what.type(), (int) what.channel(), what.id() )
2164 abort(); /*NOTREACHED*/
2172 boost::shared_ptr<AutomationControl> control
2173 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2175 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2176 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2177 _editor, *this, false, parent_canvas,
2178 processor->describe_parameter (what), processor->name()));
2180 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2182 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2185 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2190 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2193 pan->menu_item->set_active (false);
2202 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2204 boost::shared_ptr<Processor> processor (p.lock ());
2206 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2207 /* The Amp processor is a special case and is dealt with separately */
2211 set<Evoral::Parameter> existing;
2213 processor->what_has_data (existing);
2215 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2217 Evoral::Parameter param (*i);
2218 boost::shared_ptr<AutomationLine> al;
2220 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2223 add_processor_automation_curve (processor, param);
2229 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2231 using namespace Menu_Helpers;
2235 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2237 _automation_tracks[param] = track;
2239 /* existing state overrides "show" argument */
2240 string s = track->gui_property ("visible");
2242 show = string_is_affirmative (s);
2245 /* this might or might not change the visibility status, so don't rely on it */
2246 track->set_marked_for_display (show);
2248 if (show && !no_redraw) {
2252 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2253 /* MIDI-related parameters are always in the menu, there's no
2254 reason to rebuild the menu just because we added a automation
2255 lane for one of them. But if we add a non-MIDI automation
2256 lane, then we need to invalidate the display menu.
2258 delete display_menu;
2264 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2266 boost::shared_ptr<Processor> processor (p.lock ());
2268 if (!processor || !processor->display_to_user ()) {
2272 /* we use this override to veto the Amp processor from the plugin menu,
2273 as its automation lane can be accessed using the special "Fader" menu
2277 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2281 using namespace Menu_Helpers;
2282 ProcessorAutomationInfo *rai;
2283 list<ProcessorAutomationInfo*>::iterator x;
2285 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2287 if (automatable.empty()) {
2291 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2292 if ((*x)->processor == processor) {
2297 if (x == processor_automation.end()) {
2299 rai = new ProcessorAutomationInfo (processor);
2300 processor_automation.push_back (rai);
2308 /* any older menu was deleted at the top of processors_changed()
2309 when we cleared the subplugin menu.
2312 rai->menu = manage (new Menu);
2313 MenuList& items = rai->menu->items();
2314 rai->menu->set_name ("ArdourContextMenu");
2318 std::set<Evoral::Parameter> has_visible_automation;
2319 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2321 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2323 ProcessorAutomationNode* pan;
2324 Gtk::CheckMenuItem* mitem;
2326 string name = processor->describe_parameter (*i);
2328 items.push_back (CheckMenuElem (name));
2329 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2331 _subplugin_menu_map[*i] = mitem;
2333 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2334 mitem->set_active(true);
2337 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2341 pan = new ProcessorAutomationNode (*i, mitem, *this);
2343 rai->lines.push_back (pan);
2347 pan->menu_item = mitem;
2351 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2354 /* add the menu for this processor, because the subplugin
2355 menu is always cleared at the top of processors_changed().
2356 this is the result of some poor design in gtkmm and/or
2360 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2365 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2366 RouteTimeAxisView::ProcessorAutomationNode* pan)
2368 bool showit = pan->menu_item->get_active();
2369 bool redraw = false;
2371 if (pan->view == 0 && showit) {
2372 add_processor_automation_curve (rai->processor, pan->what);
2376 if (pan->view && pan->view->set_marked_for_display (showit)) {
2380 if (redraw && !no_redraw) {
2386 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2388 if (c.type == RouteProcessorChange::MeterPointChange) {
2389 /* nothing to do if only the meter point has changed */
2393 using namespace Menu_Helpers;
2395 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2396 (*i)->valid = false;
2399 setup_processor_menu_and_curves ();
2401 bool deleted_processor_automation = false;
2403 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2405 list<ProcessorAutomationInfo*>::iterator tmp;
2413 processor_automation.erase (i);
2414 deleted_processor_automation = true;
2421 if (deleted_processor_automation && !no_redraw) {
2426 boost::shared_ptr<AutomationLine>
2427 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2429 ProcessorAutomationNode* pan;
2431 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2437 return boost::shared_ptr<AutomationLine>();
2441 RouteTimeAxisView::reset_processor_automation_curves ()
2443 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2449 RouteTimeAxisView::can_edit_name () const
2451 /* we do not allow track name changes if it is record enabled
2453 return !_route->record_enabled();
2457 RouteTimeAxisView::blink_rec_display (bool onoff)
2459 RouteUI::blink_rec_display (onoff);
2463 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2465 if (_ignore_set_layer_display) {
2469 if (apply_to_selection) {
2470 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2474 _view->set_layer_display (d);
2477 set_gui_property (X_("layer-display"), enum_2_string (d));
2482 RouteTimeAxisView::layer_display () const
2485 return _view->layer_display ();
2488 /* we don't know, since we don't have a _view, so just return something */
2494 boost::shared_ptr<AutomationTimeAxisView>
2495 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2497 AutomationTracks::iterator i = _automation_tracks.find(param);
2498 if (i != _automation_tracks.end()) {
2501 return boost::shared_ptr<AutomationTimeAxisView>();
2506 RouteTimeAxisView::fast_update ()
2508 gm.get_level_meter().update_meters ();
2512 RouteTimeAxisView::hide_meter ()
2515 gm.get_level_meter().hide_meters ();
2519 RouteTimeAxisView::show_meter ()
2525 RouteTimeAxisView::reset_meter ()
2527 if (Config->get_show_track_meters()) {
2528 int meter_width = 3;
2529 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2532 gm.get_level_meter().setup_meters (height - 9, meter_width);
2539 RouteTimeAxisView::clear_meter ()
2541 gm.get_level_meter().clear_meters ();
2545 RouteTimeAxisView::meter_changed ()
2547 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2549 if (_route && !no_redraw) {
2552 // reset peak when meter point changes
2553 gm.reset_peak_display();
2557 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2560 if (_route && !no_redraw) {
2566 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2568 using namespace Menu_Helpers;
2570 if (!_underlay_streams.empty()) {
2571 MenuList& parent_items = parent_menu->items();
2572 Menu* gs_menu = manage (new Menu);
2573 gs_menu->set_name ("ArdourContextMenu");
2574 MenuList& gs_items = gs_menu->items();
2576 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2578 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2579 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2580 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2586 RouteTimeAxisView::set_underlay_state()
2588 if (!underlay_xml_node) {
2592 XMLNodeList nlist = underlay_xml_node->children();
2593 XMLNodeConstIterator niter;
2594 XMLNode *child_node;
2596 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2597 child_node = *niter;
2599 if (child_node->name() != "Underlay") {
2603 XMLProperty* prop = child_node->property ("id");
2605 PBD::ID id (prop->value());
2607 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2610 add_underlay(v->view(), false);
2619 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2625 RouteTimeAxisView& other = v->trackview();
2627 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2628 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2629 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2630 abort(); /*NOTREACHED*/
2633 _underlay_streams.push_back(v);
2634 other._underlay_mirrors.push_back(this);
2636 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2638 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2640 if (!underlay_xml_node) {
2641 underlay_xml_node = xml_node->add_child("Underlays");
2644 XMLNode* node = underlay_xml_node->add_child("Underlay");
2645 XMLProperty* prop = node->add_property("id");
2646 prop->set_value(v->trackview().route()->id().to_s());
2653 RouteTimeAxisView::remove_underlay (StreamView* v)
2659 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2660 RouteTimeAxisView& other = v->trackview();
2662 if (it != _underlay_streams.end()) {
2663 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2665 if (gm == other._underlay_mirrors.end()) {
2666 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2667 abort(); /*NOTREACHED*/
2670 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2672 _underlay_streams.erase(it);
2673 other._underlay_mirrors.erase(gm);
2675 if (underlay_xml_node) {
2676 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2682 RouteTimeAxisView::set_button_names ()
2684 if (_route && _route->solo_safe()) {
2685 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2687 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2689 if (Config->get_solo_control_is_listen_control()) {
2690 switch (Config->get_listen_position()) {
2691 case AfterFaderListen:
2692 solo_button->set_text (_("A"));
2693 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2695 case PreFaderListen:
2696 solo_button->set_text (_("P"));
2697 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2701 solo_button->set_text (_("S"));
2702 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2704 mute_button->set_text (_("M"));
2708 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2710 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2711 if (i != _main_automation_menu_map.end()) {
2715 i = _subplugin_menu_map.find (param);
2716 if (i != _subplugin_menu_map.end()) {
2724 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2726 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2728 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2732 gain_track.reset (new AutomationTimeAxisView (_session,
2733 _route, _route->amp(), c, param,
2738 _route->amp()->describe_parameter(param)));
2741 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2744 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2748 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2750 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2752 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2756 mute_track.reset (new AutomationTimeAxisView (_session,
2757 _route, _route, c, param,
2762 _route->describe_parameter(param)));
2765 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2768 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2772 void add_region_to_list (RegionView* rv, RegionList* l)
2774 l->push_back (rv->region());
2778 RouteTimeAxisView::combine_regions ()
2780 /* as of may 2011, we do not offer uncombine for MIDI tracks
2783 if (!is_audio_track()) {
2791 RegionList selected_regions;
2792 boost::shared_ptr<Playlist> playlist = track()->playlist();
2794 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2796 if (selected_regions.size() < 2) {
2800 playlist->clear_changes ();
2801 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2803 _session->add_command (new StatefulDiffCommand (playlist));
2804 /* make the new region be selected */
2806 return _view->find_view (compound_region);
2810 RouteTimeAxisView::uncombine_regions ()
2812 /* as of may 2011, we do not offer uncombine for MIDI tracks
2814 if (!is_audio_track()) {
2822 RegionList selected_regions;
2823 boost::shared_ptr<Playlist> playlist = track()->playlist();
2825 /* have to grab selected regions first because the uncombine is going
2826 * to change that in the middle of the list traverse
2829 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2831 playlist->clear_changes ();
2833 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2834 playlist->uncombine (*i);
2837 _session->add_command (new StatefulDiffCommand (playlist));
2841 RouteTimeAxisView::state_id() const
2843 return string_compose ("rtav %1", _route->id().to_s());
2848 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2850 TimeAxisView::remove_child (c);
2852 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2854 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2855 if (i->second == a) {
2856 _automation_tracks.erase (i);