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"
62 #include "audio_streamview.h"
64 #include "enums_convert.h"
65 #include "route_time_axis.h"
66 #include "automation_time_axis.h"
68 #include "gui_thread.h"
69 #include "item_counts.h"
71 #include "paste_context.h"
72 #include "playlist_selector.h"
73 #include "point_selection.h"
75 #include "public_editor.h"
76 #include "region_view.h"
77 #include "rgb_macros.h"
78 #include "selection.h"
79 #include "streamview.h"
81 #include "ui_config.h"
83 #include "route_group_menu.h"
85 #include "ardour/track.h"
89 using namespace ARDOUR;
90 using namespace ARDOUR_UI_UTILS;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
98 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
100 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
102 , parent_canvas (canvas)
104 , button_table (3, 3)
105 , route_group_button (S_("RTAV|G"))
106 , playlist_button (S_("RTAV|P"))
107 , automation_button (S_("RTAV|A"))
108 , automation_action_menu (0)
109 , plugins_submenu_item (0)
110 , route_group_menu (0)
111 , playlist_action_menu (0)
113 , color_mode_menu (0)
114 , gm (sess, true, 75, 14)
115 , _ignore_set_layer_display (false)
116 , gain_automation_item(NULL)
117 , trim_automation_item(NULL)
118 , mute_automation_item(NULL)
119 , pan_automation_item(NULL)
121 number_label.set_name("tracknumber label");
122 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
123 number_label.set_alignment(.5, .5);
124 number_label.set_fallthrough_to_parent (true);
126 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
129 parameter_changed ("editor-stereo-only-meters");
133 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
135 if (what_changed.contains (ARDOUR::Properties::name)) {
141 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
143 RouteUI::set_route (rt);
145 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
146 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
147 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
150 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
153 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
154 gm.get_level_meter().set_no_show_all();
155 gm.get_level_meter().setup_meters(50, meter_width);
156 gm.update_gain_sensitive ();
159 if (get_gui_property ("height", height)) {
162 set_height (preset_height (HeightNormal));
165 if (!_route->is_auditioner()) {
166 if (gui_property ("visible").empty()) {
167 set_gui_property ("visible", true);
170 set_gui_property ("visible", false);
173 timestretch_rect = 0;
176 ignore_toggle = false;
178 route_group_button.set_name ("route button");
179 playlist_button.set_name ("route button");
180 automation_button.set_name ("route button");
182 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
183 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
184 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
188 if (ARDOUR::Profile->get_mixbus()) {
189 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
191 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
194 if (is_midi_track()) {
195 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
196 gm.set_fader_name ("MidiTrackFader");
198 set_tooltip(*rec_enable_button, _("Record"));
199 gm.set_fader_name ("AudioTrackFader");
202 /* set playlist button tip to the current playlist, and make it update when it changes */
203 update_playlist_tip ();
204 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
207 gm.set_fader_name ("AudioBusFader");
208 Gtk::Fixed *blank = manage(new Gtk::Fixed());
209 controls_button_size_group->add_widget(*blank);
210 if (ARDOUR::Profile->get_mixbus() ) {
211 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
220 if (!ARDOUR::Profile->get_mixbus()) {
221 controls_meters_size_group->add_widget (gm.get_level_meter());
224 if (_route->is_master()) {
225 route_group_button.set_sensitive(false);
228 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
229 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
230 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
231 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
233 if (ARDOUR::Profile->get_mixbus()) {
234 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
238 // mute button is always present, it is used to
239 // force the 'blank' placeholders to the proper size
240 controls_button_size_group->add_widget(*mute_button);
242 if (!_route->is_master()) {
243 if (ARDOUR::Profile->get_mixbus()) {
244 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 Gtk::Fixed *blank = manage(new Gtk::Fixed());
250 controls_button_size_group->add_widget(*blank);
251 if (ARDOUR::Profile->get_mixbus()) {
252 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
259 if (ARDOUR::Profile->get_mixbus()) {
260 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 else if (!ARDOUR::Profile->get_trx()) {
264 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
265 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
268 set_tooltip(*solo_button,_("Solo"));
269 set_tooltip(*mute_button,_("Mute"));
270 set_tooltip(route_group_button, _("Route Group"));
272 mute_button->set_tweaks(ArdourButton::TrackHeader);
273 solo_button->set_tweaks(ArdourButton::TrackHeader);
274 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
275 playlist_button.set_tweaks(ArdourButton::TrackHeader);
276 automation_button.set_tweaks(ArdourButton::TrackHeader);
277 route_group_button.set_tweaks(ArdourButton::TrackHeader);
279 if (is_midi_track()) {
280 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
282 set_tooltip(automation_button, _("Automation"));
285 update_track_number_visibility();
288 if (ARDOUR::Profile->get_mixbus()) {
289 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
291 else if (!ARDOUR::Profile->get_trx()) {
292 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
295 if (is_track() && track()->mode() == ARDOUR::Normal) {
296 if (ARDOUR::Profile->get_mixbus()) {
297 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
299 else if (!ARDOUR::Profile->get_trx()) {
300 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
306 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
310 LayerDisplay layer_display;
311 if (get_gui_property ("layer-display", layer_display)) {
312 set_layer_display (layer_display);
315 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
316 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
318 /* pick up the correct freeze state */
323 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
324 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
326 PropertyList* plist = new PropertyList();
328 plist->add (ARDOUR::Properties::group_mute, true);
329 plist->add (ARDOUR::Properties::group_solo, true);
331 route_group_menu = new RouteGroupMenu (_session, plist);
333 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
336 RouteTimeAxisView::~RouteTimeAxisView ()
338 cleanup_gui_properties ();
340 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
344 delete playlist_action_menu;
345 playlist_action_menu = 0;
350 _automation_tracks.clear ();
352 delete route_group_menu;
353 CatchDeletion (this);
357 RouteTimeAxisView::name() const
360 return _route->name();
366 RouteTimeAxisView::post_construct ()
368 /* map current state of the route */
370 update_diskstream_display ();
371 setup_processor_menu_and_curves ();
372 reset_processor_automation_curves ();
375 /** Set up the processor menu for the current set of processors, and
376 * display automation curves for any parameters which have data.
379 RouteTimeAxisView::setup_processor_menu_and_curves ()
381 _subplugin_menu_map.clear ();
382 subplugin_menu.items().clear ();
383 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
384 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
388 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
390 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
391 if (_route->route_group()) {
392 _route->route_group()->remove (_route);
398 r.push_back (route ());
400 route_group_menu->build (r);
401 if (ev->button == 1) {
402 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
406 route_group_menu->menu()->popup (ev->button, ev->time);
413 RouteTimeAxisView::playlist_changed ()
419 RouteTimeAxisView::label_view ()
421 string x = _route->name ();
422 if (x != name_label.get_text ()) {
423 name_label.set_text (x);
425 const int64_t track_number = _route->track_number ();
426 if (track_number == 0) {
427 number_label.set_text ("");
429 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
434 RouteTimeAxisView::update_track_number_visibility ()
437 bool show_label = _session->config.get_track_name_number();
439 if (_route && _route->is_master()) {
443 if (number_label.get_parent()) {
444 controls_table.remove (number_label);
447 if (ARDOUR::Profile->get_mixbus()) {
448 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
450 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
452 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
453 // except the width of the number label is subtracted from the name-hbox, so we
454 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
455 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
457 number_label.set_size_request(tnw, -1);
458 number_label.show ();
460 number_label.hide ();
465 RouteTimeAxisView::parameter_changed (string const & p)
467 if (p == "track-name-number") {
468 update_track_number_visibility();
469 } else if (p == "editor-stereo-only-meters") {
470 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
471 gm.get_level_meter().set_max_audio_meter_count (2);
473 gm.get_level_meter().set_max_audio_meter_count (0);
479 RouteTimeAxisView::take_name_changed (void *src)
487 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
489 if (ev->button != 1) {
493 build_playlist_menu ();
494 conditionally_add_to_selection ();
495 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
501 RouteTimeAxisView::automation_click (GdkEventButton *ev)
503 if (ev->button != 1) {
507 conditionally_add_to_selection ();
508 build_automation_action_menu (false);
509 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
515 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
517 using namespace Menu_Helpers;
519 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
520 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
523 detach_menu (subplugin_menu);
525 _main_automation_menu_map.clear ();
526 delete automation_action_menu;
527 automation_action_menu = new Menu;
529 MenuList& items = automation_action_menu->items();
531 automation_action_menu->set_name ("ArdourContextMenu");
533 items.push_back (MenuElem (_("Show All Automation"),
534 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
536 items.push_back (MenuElem (_("Show Existing Automation"),
537 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
539 items.push_back (MenuElem (_("Hide All Automation"),
540 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
542 /* Attach the plugin submenu. It may have previously been used elsewhere,
543 so it was detached above
546 if (!subplugin_menu.items().empty()) {
547 items.push_back (SeparatorElem ());
548 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
549 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
552 /* Add any route automation */
555 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
556 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
557 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
558 string_is_affirmative (gain_track->gui_property ("visible")));
560 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
564 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
565 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
566 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
567 string_is_affirmative (trim_track->gui_property ("visible")));
569 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
573 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
574 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
575 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
576 string_is_affirmative (mute_track->gui_property ("visible")));
578 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
581 if (!pan_tracks.empty()) {
582 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
583 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
584 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
585 string_is_affirmative (pan_tracks.front()->gui_property ("visible")));
587 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
588 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
589 _main_automation_menu_map[*p] = pan_automation_item;
595 RouteTimeAxisView::build_display_menu ()
597 using namespace Menu_Helpers;
601 TimeAxisView::build_display_menu ();
603 /* now fill it with our stuff */
605 MenuList& items = display_menu->items();
606 display_menu->set_name ("ArdourContextMenu");
608 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
610 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
612 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
614 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
616 items.push_back (SeparatorElem());
619 detach_menu (*_size_menu);
622 items.push_back (MenuElem (_("Height"), *_size_menu));
623 items.push_back (SeparatorElem());
625 // Hook for derived classes to add type specific stuff
626 append_extra_display_menu_items ();
630 Menu* layers_menu = manage (new Menu);
631 MenuList &layers_items = layers_menu->items();
632 layers_menu->set_name("ArdourContextMenu");
634 RadioMenuItem::Group layers_group;
636 /* Find out how many overlaid/stacked tracks we have in the selection */
640 int unchangeable = 0;
641 TrackSelection const & s = _editor.get_selection().tracks;
643 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
644 StreamView* v = (*i)->view ();
649 if (v->can_change_layer_display()) {
650 switch (v->layer_display ()) {
664 /* We're not connecting to signal_toggled() here; in the case where these two items are
665 set to be in the `inconsistent' state, it seems that one or other will end up active
666 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
667 select the active one, no toggled signal is emitted so nothing happens.
670 _ignore_set_layer_display = true;
672 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
673 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
674 i->set_active (overlaid != 0 && stacked == 0);
675 i->set_inconsistent (overlaid != 0 && stacked != 0);
676 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
679 i->set_sensitive (false);
682 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
683 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
684 i->set_active (overlaid == 0 && stacked != 0);
685 i->set_inconsistent (overlaid != 0 && stacked != 0);
686 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
689 i->set_sensitive (false);
692 _ignore_set_layer_display = false;
694 items.push_back (MenuElem (_("Layers"), *layers_menu));
696 Menu* alignment_menu = manage (new Menu);
697 MenuList& alignment_items = alignment_menu->items();
698 alignment_menu->set_name ("ArdourContextMenu");
700 RadioMenuItem::Group align_group;
702 /* Same verbose hacks as for the layering options above */
708 boost::shared_ptr<Track> first_track;
710 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
711 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
712 if (!r || !r->is_track ()) {
717 first_track = r->track();
720 switch (r->track()->alignment_choice()) {
724 switch (r->track()->alignment_style()) {
725 case ExistingMaterial:
733 case UseExistingMaterial:
749 inconsistent = false;
756 if (!inconsistent && first_track) {
758 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
759 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
760 i->set_active (automatic != 0 && existing == 0 && capture == 0);
761 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
763 switch (first_track->alignment_choice()) {
765 switch (first_track->alignment_style()) {
766 case ExistingMaterial:
767 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
770 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
778 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
779 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
780 i->set_active (existing != 0 && capture == 0 && automatic == 0);
781 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
783 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
784 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
785 i->set_active (existing == 0 && capture != 0 && automatic == 0);
786 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
788 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
794 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
795 Menu* mode_menu = manage (new Menu);
796 MenuList& mode_items = mode_menu->items ();
797 mode_menu->set_name ("ArdourContextMenu");
799 RadioMenuItem::Group mode_group;
805 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
806 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
807 if (!r || !r->is_track ()) {
811 switch (r->track()->mode()) {
824 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
825 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
826 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
827 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
828 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
830 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
831 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
832 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
833 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
834 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
836 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
837 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
838 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
839 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
840 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
842 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
845 items.push_back (SeparatorElem());
847 build_playlist_menu ();
848 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
849 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
852 route_group_menu->detach ();
855 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
856 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
858 r.push_back (rtv->route ());
863 r.push_back (route ());
866 if (!_route->is_master()) {
867 route_group_menu->build (r);
868 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
871 build_automation_action_menu (true);
872 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
874 items.push_back (SeparatorElem());
878 TrackSelection const & s = _editor.get_selection().tracks;
879 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
880 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
885 if (r->route()->active()) {
892 items.push_back (CheckMenuElem (_("Active")));
893 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
894 bool click_sets_active = true;
895 if (active > 0 && inactive == 0) {
896 i->set_active (true);
897 click_sets_active = false;
898 } else if (active > 0 && inactive > 0) {
899 i->set_inconsistent (true);
901 i->set_sensitive(! _session->transport_rolling());
902 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
904 items.push_back (SeparatorElem());
905 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
906 if (_route && !_route->is_master()) {
907 items.push_back (SeparatorElem());
908 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
910 items.push_back (SeparatorElem());
911 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
914 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
916 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
918 if (apply_to_selection) {
919 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
922 bool needs_bounce = false;
924 if (!track()->can_use_mode (mode, needs_bounce)) {
930 cerr << "would bounce this one\n";
935 track()->set_mode (mode);
941 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
943 TimeAxisView::show_timestretch (start, end, layers, layer);
953 /* check that the time selection was made in our route, or our route group.
954 remember that route_group() == 0 implies the route is *not* in a edit group.
957 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
958 /* this doesn't apply to us */
962 /* ignore it if our edit group is not active */
964 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
969 if (timestretch_rect == 0) {
970 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
971 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
972 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
975 timestretch_rect->show ();
976 timestretch_rect->raise_to_top ();
978 double const x1 = start / _editor.get_current_zoom();
979 double const x2 = (end - 1) / _editor.get_current_zoom();
981 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
982 x2, current_height() * (layers - layer) / layers));
986 RouteTimeAxisView::hide_timestretch ()
988 TimeAxisView::hide_timestretch ();
990 if (timestretch_rect) {
991 timestretch_rect->hide ();
996 RouteTimeAxisView::show_selection (TimeSelection& ts)
1000 /* ignore it if our edit group is not active or if the selection was started
1001 in some other track or route group (remember that route_group() == 0 means
1002 that the track is not in an route group).
1005 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
1006 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1012 TimeAxisView::show_selection (ts);
1016 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1019 bool height_changed = (height == 0) || (h != height);
1021 int meter_width = 3;
1022 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1025 gm.get_level_meter().setup_meters (gmlen, meter_width);
1027 TimeAxisView::set_height (h, m);
1030 _view->set_height ((double) current_height());
1033 if (height >= preset_height (HeightNormal)) {
1037 gm.get_gain_slider().show();
1038 mute_button->show();
1039 if (!_route || _route->is_monitor()) {
1040 solo_button->hide();
1042 solo_button->show();
1044 if (rec_enable_button)
1045 rec_enable_button->show();
1047 route_group_button.show();
1048 automation_button.show();
1050 if (is_track() && track()->mode() == ARDOUR::Normal) {
1051 playlist_button.show();
1058 gm.get_gain_slider().hide();
1059 mute_button->show();
1060 if (!_route || _route->is_monitor()) {
1061 solo_button->hide();
1063 solo_button->show();
1065 if (rec_enable_button)
1066 rec_enable_button->show();
1068 route_group_button.hide ();
1069 automation_button.hide ();
1071 if (is_track() && track()->mode() == ARDOUR::Normal) {
1072 playlist_button.hide ();
1077 if (height_changed && !no_redraw) {
1078 /* only emit the signal if the height really changed */
1084 RouteTimeAxisView::route_color_changed ()
1087 _view->apply_color (color(), StreamView::RegionColor);
1090 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1094 RouteTimeAxisView::reset_samples_per_pixel ()
1096 set_samples_per_pixel (_editor.get_current_zoom());
1100 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1105 speed = track()->speed();
1109 _view->set_samples_per_pixel (fpp * speed);
1112 TimeAxisView::set_samples_per_pixel (fpp * speed);
1116 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1118 if (!mitem->get_active()) {
1119 /* this is one of the two calls made when these radio menu items change status. this one
1120 is for the item that became inactive, and we want to ignore it.
1125 if (apply_to_selection) {
1126 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1129 track()->set_align_choice (choice);
1135 RouteTimeAxisView::rename_current_playlist ()
1137 ArdourPrompter prompter (true);
1140 boost::shared_ptr<Track> tr = track();
1141 if (!tr || tr->destructive()) {
1145 boost::shared_ptr<Playlist> pl = tr->playlist();
1150 prompter.set_title (_("Rename Playlist"));
1151 prompter.set_prompt (_("New name for playlist:"));
1152 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1153 prompter.set_initial_text (pl->name());
1154 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1157 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1160 prompter.get_result (name);
1161 if (name.length()) {
1162 if (_session->playlists->by_name (name)) {
1163 MessageDialog msg (_("Given playlist name is not unique."));
1165 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1167 pl->set_name (name);
1175 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1177 std::string ret (basename);
1179 std::string const group_string = "." + route_group()->name() + ".";
1181 // iterate through all playlists
1183 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1184 std::string tmp = (*i)->name();
1186 std::string::size_type idx = tmp.find(group_string);
1187 // find those which belong to this group
1188 if (idx != string::npos) {
1189 tmp = tmp.substr(idx + group_string.length());
1191 // and find the largest current number
1193 if (x > maxnumber) {
1202 snprintf (buf, sizeof(buf), "%d", maxnumber);
1204 ret = this->name() + "." + route_group()->name () + "." + buf;
1210 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1214 boost::shared_ptr<Track> tr = track ();
1215 if (!tr || tr->destructive()) {
1219 boost::shared_ptr<const Playlist> pl = tr->playlist();
1226 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1227 name = resolve_new_group_playlist_name(name,playlists_before_op);
1230 while (_session->playlists->by_name(name)) {
1231 name = Playlist::bump_name (name, *_session);
1235 // TODO: The prompter "new" button should be de-activated if the user
1236 // specifies a playlist name which already exists in the session.
1238 ArdourPrompter prompter (true);
1241 prompter.set_title (_("New Copy Playlist"));
1242 prompter.set_prompt (_("Name for playlist copy:"));
1244 prompter.set_title (_("New Playlist"));
1245 prompter.set_prompt (_("Name for new playlist:"));
1247 prompter.set_initial_text (name);
1248 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1249 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1250 prompter.show_all ();
1253 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1256 prompter.get_result (name);
1257 if (name.length()) {
1258 if (_session->playlists->by_name (name)) {
1259 MessageDialog msg (_("Given playlist name is not unique."));
1261 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1269 if (name.length()) {
1271 tr->use_copy_playlist ();
1273 tr->use_new_playlist ();
1275 tr->playlist()->set_name (name);
1280 RouteTimeAxisView::clear_playlist ()
1282 boost::shared_ptr<Track> tr = track ();
1283 if (!tr || tr->destructive()) {
1287 boost::shared_ptr<Playlist> pl = tr->playlist();
1292 _editor.clear_playlist (pl);
1296 RouteTimeAxisView::speed_changed ()
1298 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1302 RouteTimeAxisView::update_diskstream_display ()
1312 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1314 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1316 /* special case: select/deselect all tracks */
1318 _editor.begin_reversible_selection_op (X_("Selection Click"));
1320 if (_editor.get_selection().selected (this)) {
1321 _editor.get_selection().clear_tracks ();
1323 _editor.select_all_tracks ();
1326 _editor.commit_reversible_selection_op ();
1331 _editor.begin_reversible_selection_op (X_("Selection Click"));
1333 switch (ArdourKeyboard::selection_type (ev->state)) {
1334 case Selection::Toggle:
1335 _editor.get_selection().toggle (this);
1338 case Selection::Set:
1339 _editor.get_selection().set (this);
1342 case Selection::Extend:
1343 _editor.extend_selection_to_track (*this);
1346 case Selection::Add:
1347 _editor.get_selection().add (this);
1351 _editor.commit_reversible_selection_op ();
1355 RouteTimeAxisView::set_selected_points (PointSelection& points)
1357 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1358 (*i)->set_selected_points (points);
1360 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1362 asv->set_selected_points (points);
1367 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1370 _view->set_selected_regionviews (regions);
1374 /** Add the selectable things that we have to a list.
1375 * @param results List to add things to.
1378 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1383 speed = track()->speed();
1386 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1387 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1389 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1390 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1393 /* pick up visible automation tracks */
1395 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1396 if (!(*i)->hidden()) {
1397 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1403 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1406 _view->get_inverted_selectables (sel, results);
1409 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1410 if (!(*i)->hidden()) {
1411 (*i)->get_inverted_selectables (sel, results);
1419 RouteTimeAxisView::route_group () const
1421 return _route->route_group();
1424 boost::shared_ptr<Playlist>
1425 RouteTimeAxisView::playlist () const
1427 boost::shared_ptr<Track> tr;
1429 if ((tr = track()) != 0) {
1430 return tr->playlist();
1432 return boost::shared_ptr<Playlist> ();
1437 RouteTimeAxisView::name_entry_changed (string const& str)
1439 if (str == _route->name()) {
1445 strip_whitespace_edges (x);
1451 if (_session->route_name_internal (x)) {
1452 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1454 } else if (RouteUI::verify_new_route_name (x)) {
1455 _route->set_name (x);
1462 boost::shared_ptr<Region>
1463 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1465 boost::shared_ptr<Playlist> pl = playlist ();
1468 return pl->find_next_region (pos, point, dir);
1471 return boost::shared_ptr<Region> ();
1475 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1477 boost::shared_ptr<Playlist> pl = playlist ();
1480 return pl->find_next_region_boundary (pos, dir);
1487 RouteTimeAxisView::fade_range (TimeSelection& selection)
1489 boost::shared_ptr<Playlist> what_we_got;
1490 boost::shared_ptr<Track> tr = track ();
1491 boost::shared_ptr<Playlist> playlist;
1494 /* route is a bus, not a track */
1498 playlist = tr->playlist();
1500 TimeSelection time (selection);
1501 float const speed = tr->speed();
1502 if (speed != 1.0f) {
1503 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1504 (*i).start = session_frame_to_track_frame((*i).start, speed);
1505 (*i).end = session_frame_to_track_frame((*i).end, speed);
1509 playlist->clear_changes ();
1510 playlist->clear_owned_changes ();
1512 playlist->fade_range (time);
1514 vector<Command*> cmds;
1515 playlist->rdiff (cmds);
1516 _session->add_commands (cmds);
1517 _session->add_command (new StatefulDiffCommand (playlist));
1522 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1524 boost::shared_ptr<Playlist> what_we_got;
1525 boost::shared_ptr<Track> tr = track ();
1526 boost::shared_ptr<Playlist> playlist;
1529 /* route is a bus, not a track */
1533 playlist = tr->playlist();
1535 TimeSelection time (selection.time);
1536 float const speed = tr->speed();
1537 if (speed != 1.0f) {
1538 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1539 (*i).start = session_frame_to_track_frame((*i).start, speed);
1540 (*i).end = session_frame_to_track_frame((*i).end, speed);
1544 playlist->clear_changes ();
1545 playlist->clear_owned_changes ();
1549 if (playlist->cut (time) != 0) {
1550 if (Config->get_edit_mode() == Ripple)
1551 playlist->ripple(time.start(), -time.length(), NULL);
1552 // no need to exclude any regions from rippling here
1554 vector<Command*> cmds;
1555 playlist->rdiff (cmds);
1556 _session->add_commands (cmds);
1558 _session->add_command (new StatefulDiffCommand (playlist));
1563 if ((what_we_got = playlist->cut (time)) != 0) {
1564 _editor.get_cut_buffer().add (what_we_got);
1565 if (Config->get_edit_mode() == Ripple)
1566 playlist->ripple(time.start(), -time.length(), NULL);
1567 // no need to exclude any regions from rippling here
1569 vector<Command*> cmds;
1570 playlist->rdiff (cmds);
1571 _session->add_commands (cmds);
1573 _session->add_command (new StatefulDiffCommand (playlist));
1577 if ((what_we_got = playlist->copy (time)) != 0) {
1578 _editor.get_cut_buffer().add (what_we_got);
1583 if ((what_we_got = playlist->cut (time)) != 0) {
1584 if (Config->get_edit_mode() == Ripple)
1585 playlist->ripple(time.start(), -time.length(), NULL);
1586 // no need to exclude any regions from rippling here
1588 vector<Command*> cmds;
1589 playlist->rdiff (cmds);
1590 _session->add_commands (cmds);
1591 _session->add_command (new StatefulDiffCommand (playlist));
1592 what_we_got->release ();
1599 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1605 boost::shared_ptr<Playlist> pl = playlist ();
1606 const ARDOUR::DataType type = pl->data_type();
1607 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1609 if (p == selection.playlists.end()) {
1612 ctx.counts.increase_n_playlists(type);
1614 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1616 if (track()->speed() != 1.0f) {
1617 pos = session_frame_to_track_frame (pos, track()->speed());
1618 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1621 /* add multi-paste offset if applicable */
1622 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1623 const framecnt_t duration = extent.second - extent.first;
1624 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1626 pl->clear_changes ();
1627 pl->clear_owned_changes ();
1628 if (Config->get_edit_mode() == Ripple) {
1629 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1630 framecnt_t amount = extent.second - extent.first;
1631 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1633 pl->paste (*p, pos, ctx.times, sub_num);
1635 vector<Command*> cmds;
1637 _session->add_commands (cmds);
1639 _session->add_command (new StatefulDiffCommand (pl));
1645 struct PlaylistSorter {
1646 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1647 return a->sort_id() < b->sort_id();
1652 RouteTimeAxisView::build_playlist_menu ()
1654 using namespace Menu_Helpers;
1660 delete playlist_action_menu;
1661 playlist_action_menu = new Menu;
1662 playlist_action_menu->set_name ("ArdourContextMenu");
1664 MenuList& playlist_items = playlist_action_menu->items();
1665 playlist_action_menu->set_name ("ArdourContextMenu");
1666 playlist_items.clear();
1668 RadioMenuItem::Group playlist_group;
1669 boost::shared_ptr<Track> tr = track ();
1671 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1673 /* sort the playlists */
1675 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1677 /* add the playlists to the menu */
1678 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1679 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1680 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1681 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1683 if (tr->playlist()->id() == (*i)->id()) {
1689 playlist_items.push_back (SeparatorElem());
1690 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1691 playlist_items.push_back (SeparatorElem());
1693 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1694 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1695 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1698 // Use a label which tells the user what is happening
1699 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1700 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1704 playlist_items.push_back (SeparatorElem());
1705 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1706 playlist_items.push_back (SeparatorElem());
1708 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1712 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1714 assert (is_track());
1716 // exit if we were triggered by deactivating the old playlist
1717 if (!item->get_active()) {
1721 boost::shared_ptr<Playlist> pl (wpl.lock());
1727 if (track()->playlist() == pl) {
1728 // exit when use_playlist is called by the creation of the playlist menu
1729 // or the playlist choice is unchanged
1733 track()->use_playlist (pl);
1735 RouteGroup* rg = route_group();
1737 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1738 std::string group_string = "." + rg->name() + ".";
1740 std::string take_name = pl->name();
1741 std::string::size_type idx = take_name.find(group_string);
1743 if (idx == std::string::npos)
1746 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1748 boost::shared_ptr<RouteList> rl (rg->route_list());
1750 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1751 if ((*i) == this->route()) {
1755 std::string playlist_name = (*i)->name()+group_string+take_name;
1757 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1762 if (track->freeze_state() == Track::Frozen) {
1763 /* Don't change playlists of frozen tracks */
1767 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1769 // No playlist for this track for this take yet, make it
1770 track->use_new_playlist();
1771 track->playlist()->set_name(playlist_name);
1773 track->use_playlist(ipl);
1780 RouteTimeAxisView::update_playlist_tip ()
1782 RouteGroup* rg = route_group ();
1783 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1784 string group_string = "." + rg->name() + ".";
1786 string take_name = track()->playlist()->name();
1787 string::size_type idx = take_name.find(group_string);
1789 if (idx != string::npos) {
1790 /* find the bit containing the take number / name */
1791 take_name = take_name.substr (idx + group_string.length());
1793 /* set the playlist button tooltip to the take name */
1796 string_compose(_("Take: %1.%2"),
1797 Gtkmm2ext::markup_escape_text (rg->name()),
1798 Gtkmm2ext::markup_escape_text (take_name))
1805 /* set the playlist button tooltip to the playlist name */
1806 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1811 RouteTimeAxisView::show_playlist_selector ()
1813 _editor.playlist_selector().show_for (this);
1817 RouteTimeAxisView::map_frozen ()
1823 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1825 switch (track()->freeze_state()) {
1827 playlist_button.set_sensitive (false);
1830 playlist_button.set_sensitive (true);
1833 RouteUI::map_frozen ();
1837 RouteTimeAxisView::color_handler ()
1839 //case cTimeStretchOutline:
1840 if (timestretch_rect) {
1841 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1843 //case cTimeStretchFill:
1844 if (timestretch_rect) {
1845 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1851 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1852 * Will add track if necessary.
1855 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1857 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1858 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1861 /* it doesn't exist yet, so we don't care about the button state: just add it */
1862 create_automation_child (param, true);
1865 bool yn = menu->get_active();
1866 bool changed = false;
1868 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1870 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1871 will have done that for us.
1874 if (changed && !no_redraw) {
1882 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1884 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1890 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1892 if (menu && !_hidden) {
1893 ignore_toggle = true;
1894 menu->set_active (false);
1895 ignore_toggle = false;
1898 if (_route && !no_redraw) {
1904 RouteTimeAxisView::update_gain_track_visibility ()
1906 bool const showit = gain_automation_item->get_active();
1909 if (gain_track->get_gui_property ("visible", visible) && visible != showit) {
1910 gain_track->set_marked_for_display (showit);
1912 /* now trigger a redisplay */
1915 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1921 RouteTimeAxisView::update_trim_track_visibility ()
1923 bool const showit = trim_automation_item->get_active();
1926 if (trim_track->get_gui_property ("visible", visible) && visible != showit) {
1927 trim_track->set_marked_for_display (showit);
1929 /* now trigger a redisplay */
1932 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1938 RouteTimeAxisView::update_mute_track_visibility ()
1940 bool const showit = mute_automation_item->get_active();
1943 if (mute_track->get_gui_property ("visible", visible) && visible != showit) {
1944 mute_track->set_marked_for_display (showit);
1946 /* now trigger a redisplay */
1949 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1955 RouteTimeAxisView::update_pan_track_visibility ()
1957 bool const showit = pan_automation_item->get_active();
1958 bool changed = false;
1960 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1961 if ((*i)->set_marked_for_display (showit)) {
1967 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1972 RouteTimeAxisView::ensure_pan_views (bool show)
1974 bool changed = false;
1975 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1977 (*i)->set_marked_for_display (false);
1980 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1984 if (!_route->panner()) {
1988 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1989 set<Evoral::Parameter>::iterator p;
1991 for (p = params.begin(); p != params.end(); ++p) {
1992 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1994 if (pan_control->parameter().type() == NullAutomation) {
1995 error << "Pan control has NULL automation type!" << endmsg;
1999 if (automation_child (pan_control->parameter ()).get () == 0) {
2001 /* we don't already have an AutomationTimeAxisView for this parameter */
2003 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2005 boost::shared_ptr<AutomationTimeAxisView> t (
2006 new AutomationTimeAxisView (_session,
2010 pan_control->parameter (),
2018 pan_tracks.push_back (t);
2019 add_automation_child (*p, t, show);
2021 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2028 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2030 if (apply_to_selection) {
2031 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2035 /* Show our automation */
2037 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2038 i->second->set_marked_for_display (true);
2040 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2043 menu->set_active(true);
2048 /* Show processor automation */
2050 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2051 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2052 if ((*ii)->view == 0) {
2053 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2056 (*ii)->menu_item->set_active (true);
2069 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2071 if (apply_to_selection) {
2072 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2076 /* Show our automation */
2078 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2079 if (i->second->has_automation()) {
2080 i->second->set_marked_for_display (true);
2082 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2084 menu->set_active(true);
2089 /* Show processor automation */
2091 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2092 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2093 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2094 (*ii)->menu_item->set_active (true);
2106 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2108 if (apply_to_selection) {
2109 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2113 /* Hide our automation */
2115 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2116 i->second->set_marked_for_display (false);
2118 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2121 menu->set_active (false);
2125 /* Hide processor automation */
2127 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2128 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2129 (*ii)->menu_item->set_active (false);
2140 RouteTimeAxisView::region_view_added (RegionView* rv)
2142 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2143 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2144 boost::shared_ptr<AutomationTimeAxisView> atv;
2146 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2151 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2152 (*i)->add_ghost(rv);
2156 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2158 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2164 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2166 parent.remove_processor_automation_node (this);
2170 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2173 remove_child (pan->view);
2177 RouteTimeAxisView::ProcessorAutomationNode*
2178 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2180 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2182 if ((*i)->processor == processor) {
2184 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2185 if ((*ii)->what == what) {
2195 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2197 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2200 ProcessorAutomationNode* pan;
2202 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2203 /* session state may never have been saved with new plugin */
2204 error << _("programming error: ")
2205 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2206 processor->name(), what.type(), (int) what.channel(), what.id() )
2208 abort(); /*NOTREACHED*/
2216 boost::shared_ptr<AutomationControl> control
2217 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2219 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2220 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2221 _editor, *this, false, parent_canvas,
2222 processor->describe_parameter (what), processor->name()));
2224 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2226 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2229 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2234 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2237 pan->menu_item->set_active (false);
2246 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2248 boost::shared_ptr<Processor> processor (p.lock ());
2250 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2251 /* The Amp processor is a special case and is dealt with separately */
2255 set<Evoral::Parameter> existing;
2257 processor->what_has_data (existing);
2259 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2261 Evoral::Parameter param (*i);
2262 boost::shared_ptr<AutomationLine> al;
2264 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2267 add_processor_automation_curve (processor, param);
2273 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2275 using namespace Menu_Helpers;
2279 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2281 _automation_tracks[param] = track;
2283 /* existing state overrides "show" argument */
2285 if (track->get_gui_property ("visible", visible)) {
2289 /* this might or might not change the visibility status, so don't rely on it */
2290 track->set_marked_for_display (show);
2292 if (show && !no_redraw) {
2296 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2297 /* MIDI-related parameters are always in the menu, there's no
2298 reason to rebuild the menu just because we added a automation
2299 lane for one of them. But if we add a non-MIDI automation
2300 lane, then we need to invalidate the display menu.
2302 delete display_menu;
2308 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2310 boost::shared_ptr<Processor> processor (p.lock ());
2312 if (!processor || !processor->display_to_user ()) {
2316 /* we use this override to veto the Amp processor from the plugin menu,
2317 as its automation lane can be accessed using the special "Fader" menu
2321 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2325 using namespace Menu_Helpers;
2326 ProcessorAutomationInfo *rai;
2327 list<ProcessorAutomationInfo*>::iterator x;
2329 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2331 if (automatable.empty()) {
2335 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2336 if ((*x)->processor == processor) {
2341 if (x == processor_automation.end()) {
2342 rai = new ProcessorAutomationInfo (processor);
2343 processor_automation.push_back (rai);
2348 /* any older menu was deleted at the top of processors_changed()
2349 when we cleared the subplugin menu.
2352 rai->menu = manage (new Menu);
2353 MenuList& items = rai->menu->items();
2354 rai->menu->set_name ("ArdourContextMenu");
2358 std::set<Evoral::Parameter> has_visible_automation;
2359 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2361 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2363 ProcessorAutomationNode* pan;
2364 Gtk::CheckMenuItem* mitem;
2366 string name = processor->describe_parameter (*i);
2368 if (name == X_("hidden")) {
2372 items.push_back (CheckMenuElem (name));
2373 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2375 _subplugin_menu_map[*i] = mitem;
2377 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2378 mitem->set_active(true);
2381 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2385 pan = new ProcessorAutomationNode (*i, mitem, *this);
2387 rai->lines.push_back (pan);
2391 pan->menu_item = mitem;
2395 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2398 if (items.size() == 0) {
2402 /* add the menu for this processor, because the subplugin
2403 menu is always cleared at the top of processors_changed().
2404 this is the result of some poor design in gtkmm and/or
2408 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2413 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2414 RouteTimeAxisView::ProcessorAutomationNode* pan)
2416 bool showit = pan->menu_item->get_active();
2417 bool redraw = false;
2419 if (pan->view == 0 && showit) {
2420 add_processor_automation_curve (rai->processor, pan->what);
2424 if (pan->view && pan->view->set_marked_for_display (showit)) {
2428 if (redraw && !no_redraw) {
2434 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2436 if (c.type == RouteProcessorChange::MeterPointChange) {
2437 /* nothing to do if only the meter point has changed */
2441 using namespace Menu_Helpers;
2443 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2444 (*i)->valid = false;
2447 setup_processor_menu_and_curves ();
2449 bool deleted_processor_automation = false;
2451 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2453 list<ProcessorAutomationInfo*>::iterator tmp;
2461 processor_automation.erase (i);
2462 deleted_processor_automation = true;
2469 if (deleted_processor_automation && !no_redraw) {
2474 boost::shared_ptr<AutomationLine>
2475 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2477 ProcessorAutomationNode* pan;
2479 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2485 return boost::shared_ptr<AutomationLine>();
2489 RouteTimeAxisView::reset_processor_automation_curves ()
2491 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2497 RouteTimeAxisView::can_edit_name () const
2499 /* we do not allow track name changes if it is record enabled
2501 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2505 return !trk->rec_enable_control()->get_value();
2509 RouteTimeAxisView::blink_rec_display (bool onoff)
2511 RouteUI::blink_rec_display (onoff);
2515 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2517 if (_ignore_set_layer_display) {
2521 if (apply_to_selection) {
2522 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2526 _view->set_layer_display (d);
2529 set_gui_property (X_("layer-display"), d);
2534 RouteTimeAxisView::layer_display () const
2537 return _view->layer_display ();
2540 /* we don't know, since we don't have a _view, so just return something */
2546 boost::shared_ptr<AutomationTimeAxisView>
2547 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2549 AutomationTracks::iterator i = _automation_tracks.find(param);
2550 if (i != _automation_tracks.end()) {
2553 return boost::shared_ptr<AutomationTimeAxisView>();
2558 RouteTimeAxisView::fast_update ()
2560 gm.get_level_meter().update_meters ();
2564 RouteTimeAxisView::hide_meter ()
2567 gm.get_level_meter().hide_meters ();
2571 RouteTimeAxisView::show_meter ()
2577 RouteTimeAxisView::reset_meter ()
2579 if (UIConfiguration::instance().get_show_track_meters()) {
2580 int meter_width = 3;
2581 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2584 gm.get_level_meter().setup_meters (height - 9, meter_width);
2591 RouteTimeAxisView::clear_meter ()
2593 gm.get_level_meter().clear_meters ();
2597 RouteTimeAxisView::meter_changed ()
2599 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2601 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2604 // reset peak when meter point changes
2605 gm.reset_peak_display();
2609 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2612 if (_route && !no_redraw) {
2618 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2620 using namespace Menu_Helpers;
2622 if (!_underlay_streams.empty()) {
2623 MenuList& parent_items = parent_menu->items();
2624 Menu* gs_menu = manage (new Menu);
2625 gs_menu->set_name ("ArdourContextMenu");
2626 MenuList& gs_items = gs_menu->items();
2628 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2630 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2631 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2632 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2638 RouteTimeAxisView::set_underlay_state()
2640 if (!underlay_xml_node) {
2644 XMLNodeList nlist = underlay_xml_node->children();
2645 XMLNodeConstIterator niter;
2646 XMLNode *child_node;
2648 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2649 child_node = *niter;
2651 if (child_node->name() != "Underlay") {
2655 XMLProperty const * prop = child_node->property ("id");
2657 PBD::ID id (prop->value());
2659 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2662 add_underlay(v->view(), false);
2671 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2677 RouteTimeAxisView& other = v->trackview();
2679 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2680 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2681 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2682 abort(); /*NOTREACHED*/
2685 _underlay_streams.push_back(v);
2686 other._underlay_mirrors.push_back(this);
2688 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2690 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2692 if (!underlay_xml_node) {
2693 underlay_xml_node = xml_node->add_child("Underlays");
2696 XMLNode* node = underlay_xml_node->add_child("Underlay");
2697 XMLProperty const * prop = node->add_property("id");
2698 prop->set_value(v->trackview().route()->id().to_s());
2705 RouteTimeAxisView::remove_underlay (StreamView* v)
2711 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2712 RouteTimeAxisView& other = v->trackview();
2714 if (it != _underlay_streams.end()) {
2715 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2717 if (gm == other._underlay_mirrors.end()) {
2718 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2719 abort(); /*NOTREACHED*/
2722 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2724 _underlay_streams.erase(it);
2725 other._underlay_mirrors.erase(gm);
2727 if (underlay_xml_node) {
2728 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2734 RouteTimeAxisView::set_button_names ()
2736 if (_route && _route->solo_safe_control()->solo_safe()) {
2737 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2739 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2741 if (Config->get_solo_control_is_listen_control()) {
2742 switch (Config->get_listen_position()) {
2743 case AfterFaderListen:
2744 solo_button->set_text (S_("AfterFader|A"));
2745 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2747 case PreFaderListen:
2748 solo_button->set_text (S_("PreFader|P"));
2749 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2753 solo_button->set_text (S_("Solo|S"));
2754 set_tooltip (*solo_button, _("Solo"));
2756 mute_button->set_text (S_("Mute|M"));
2760 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2762 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2763 if (i != _main_automation_menu_map.end()) {
2767 i = _subplugin_menu_map.find (param);
2768 if (i != _subplugin_menu_map.end()) {
2776 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2778 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2780 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2784 gain_track.reset (new AutomationTimeAxisView (_session,
2785 _route, _route->amp(), c, param,
2790 _route->amp()->describe_parameter(param)));
2793 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2796 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2800 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2802 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2803 if (!c || ! _route->trim()->active()) {
2807 trim_track.reset (new AutomationTimeAxisView (_session,
2808 _route, _route->trim(), c, param,
2813 _route->trim()->describe_parameter(param)));
2816 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2819 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2823 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2825 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2827 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2831 mute_track.reset (new AutomationTimeAxisView (_session,
2832 _route, _route, c, param,
2837 _route->describe_parameter(param)));
2840 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2843 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2847 void add_region_to_list (RegionView* rv, RegionList* l)
2849 l->push_back (rv->region());
2853 RouteTimeAxisView::combine_regions ()
2855 /* as of may 2011, we do not offer uncombine for MIDI tracks
2858 if (!is_audio_track()) {
2866 RegionList selected_regions;
2867 boost::shared_ptr<Playlist> playlist = track()->playlist();
2869 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2871 if (selected_regions.size() < 2) {
2875 playlist->clear_changes ();
2876 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2878 _session->add_command (new StatefulDiffCommand (playlist));
2879 /* make the new region be selected */
2881 return _view->find_view (compound_region);
2885 RouteTimeAxisView::uncombine_regions ()
2887 /* as of may 2011, we do not offer uncombine for MIDI tracks
2889 if (!is_audio_track()) {
2897 RegionList selected_regions;
2898 boost::shared_ptr<Playlist> playlist = track()->playlist();
2900 /* have to grab selected regions first because the uncombine is going
2901 * to change that in the middle of the list traverse
2904 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2906 playlist->clear_changes ();
2908 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2909 playlist->uncombine (*i);
2912 _session->add_command (new StatefulDiffCommand (playlist));
2916 RouteTimeAxisView::state_id() const
2918 return string_compose ("rtav %1", _route->id().to_s());
2923 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2925 TimeAxisView::remove_child (c);
2927 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2929 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2930 if (i->second == a) {
2931 _automation_tracks.erase (i);
2939 RouteTimeAxisView::color () const
2941 return route_color ();
2945 RouteTimeAxisView::marked_for_display () const
2947 return !_route->presentation_info().hidden();
2951 RouteTimeAxisView::set_marked_for_display (bool yn)
2953 return RouteUI::mark_hidden (!yn);