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 "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 "ui_config.h"
82 #include "route_group_menu.h"
84 #include "ardour/track.h"
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
91 using namespace Gtkmm2ext;
93 using namespace Editing;
97 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::set_route (boost::shared_ptr<Route> rt)
135 RouteUI::set_route (rt);
137 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
138 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
139 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
142 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
145 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
146 gm.get_level_meter().set_no_show_all();
147 gm.get_level_meter().setup_meters(50, meter_width);
148 gm.update_gain_sensitive ();
150 string str = gui_property ("height");
152 set_height (atoi (str));
154 set_height (preset_height (HeightNormal));
157 if (!_route->is_auditioner()) {
158 if (gui_property ("visible").empty()) {
159 set_gui_property ("visible", true);
162 set_gui_property ("visible", false);
165 timestretch_rect = 0;
168 ignore_toggle = false;
170 route_group_button.set_name ("route button");
171 playlist_button.set_name ("route button");
172 automation_button.set_name ("route button");
174 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
175 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
176 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
180 if (ARDOUR::Profile->get_mixbus()) {
181 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
183 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
186 if (is_midi_track()) {
187 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
188 gm.set_fader_name ("MidiTrackFader");
190 set_tooltip(*rec_enable_button, _("Record"));
191 gm.set_fader_name ("AudioTrackFader");
194 /* set playlist button tip to the current playlist, and make it update when it changes */
195 update_playlist_tip ();
196 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
199 gm.set_fader_name ("AudioBusFader");
200 Gtk::Fixed *blank = manage(new Gtk::Fixed());
201 controls_button_size_group->add_widget(*blank);
202 if (ARDOUR::Profile->get_mixbus() ) {
203 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
205 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
210 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
212 if (!ARDOUR::Profile->get_mixbus()) {
213 controls_meters_size_group->add_widget (gm.get_level_meter());
216 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
217 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
219 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
221 if (ARDOUR::Profile->get_mixbus()) {
222 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
224 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
226 // mute button is always present, it is used to
227 // force the 'blank' placeholders to the proper size
228 controls_button_size_group->add_widget(*mute_button);
230 if (!_route->is_master()) {
231 if (ARDOUR::Profile->get_mixbus()) {
232 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
237 Gtk::Fixed *blank = manage(new Gtk::Fixed());
238 controls_button_size_group->add_widget(*blank);
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 if (ARDOUR::Profile->get_mixbus()) {
248 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
251 else if (!ARDOUR::Profile->get_trx()) {
252 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
253 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
256 set_tooltip(*solo_button,_("Solo"));
257 set_tooltip(*mute_button,_("Mute"));
258 set_tooltip(route_group_button, _("Route Group"));
260 mute_button->set_tweaks(ArdourButton::TrackHeader);
261 solo_button->set_tweaks(ArdourButton::TrackHeader);
262 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
263 playlist_button.set_tweaks(ArdourButton::TrackHeader);
264 automation_button.set_tweaks(ArdourButton::TrackHeader);
265 route_group_button.set_tweaks(ArdourButton::TrackHeader);
267 if (is_midi_track()) {
268 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
270 set_tooltip(automation_button, _("Automation"));
273 update_track_number_visibility();
276 if (ARDOUR::Profile->get_mixbus()) {
277 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 else if (!ARDOUR::Profile->get_trx()) {
280 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283 if (is_track() && track()->mode() == ARDOUR::Normal) {
284 if (ARDOUR::Profile->get_mixbus()) {
285 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
287 else if (!ARDOUR::Profile->get_trx()) {
288 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
295 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
299 str = gui_property ("layer-display");
301 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
304 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
305 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
307 /* pick up the correct freeze state */
312 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
313 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
315 PropertyList* plist = new PropertyList();
317 plist->add (ARDOUR::Properties::mute, true);
318 plist->add (ARDOUR::Properties::solo, true);
320 route_group_menu = new RouteGroupMenu (_session, plist);
322 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
325 RouteTimeAxisView::~RouteTimeAxisView ()
327 cleanup_gui_properties ();
329 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
333 delete playlist_action_menu;
334 playlist_action_menu = 0;
339 _automation_tracks.clear ();
341 delete route_group_menu;
342 CatchDeletion (this);
346 RouteTimeAxisView::name() const
349 return _route->name();
355 RouteTimeAxisView::post_construct ()
357 /* map current state of the route */
359 update_diskstream_display ();
360 setup_processor_menu_and_curves ();
361 reset_processor_automation_curves ();
364 /** Set up the processor menu for the current set of processors, and
365 * display automation curves for any parameters which have data.
368 RouteTimeAxisView::setup_processor_menu_and_curves ()
370 _subplugin_menu_map.clear ();
371 subplugin_menu.items().clear ();
372 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
373 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
377 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
379 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
380 if (_route->route_group()) {
381 _route->route_group()->remove (_route);
387 r.push_back (route ());
389 route_group_menu->build (r);
390 route_group_menu->menu()->popup (ev->button, ev->time);
396 RouteTimeAxisView::playlist_changed ()
402 RouteTimeAxisView::label_view ()
404 string x = _route->name ();
405 if (x != name_label.get_text ()) {
406 name_label.set_text (x);
408 const int64_t track_number = _route->track_number ();
409 if (track_number == 0) {
410 number_label.set_text ("");
412 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
417 RouteTimeAxisView::update_track_number_visibility ()
420 bool show_label = _session->config.get_track_name_number();
422 if (_route && _route->is_master()) {
426 if (number_label.get_parent()) {
427 controls_table.remove (number_label);
430 if (ARDOUR::Profile->get_mixbus()) {
431 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
433 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
435 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
436 // except the width of the number label is subtracted from the name-hbox, so we
437 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
438 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
440 number_label.set_size_request(tnw, -1);
441 number_label.show ();
443 number_label.hide ();
448 RouteTimeAxisView::parameter_changed (string const & p)
450 if (p == "track-name-number") {
451 update_track_number_visibility();
452 } else if (p == "editor-stereo-only-meters") {
453 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
454 gm.get_level_meter().set_max_audio_meter_count (2);
456 gm.get_level_meter().set_max_audio_meter_count (0);
462 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
464 if (what_changed.contains (ARDOUR::Properties::name)) {
470 RouteTimeAxisView::take_name_changed (void *src)
478 RouteTimeAxisView::playlist_click ()
480 build_playlist_menu ();
481 conditionally_add_to_selection ();
482 playlist_action_menu->popup (1, gtk_get_current_event_time());
486 RouteTimeAxisView::automation_click ()
488 conditionally_add_to_selection ();
489 build_automation_action_menu (false);
490 automation_action_menu->popup (1, gtk_get_current_event_time());
494 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
496 using namespace Menu_Helpers;
498 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
499 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
502 detach_menu (subplugin_menu);
504 _main_automation_menu_map.clear ();
505 delete automation_action_menu;
506 automation_action_menu = new Menu;
508 MenuList& items = automation_action_menu->items();
510 automation_action_menu->set_name ("ArdourContextMenu");
512 items.push_back (MenuElem (_("Show All Automation"),
513 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
515 items.push_back (MenuElem (_("Show Existing Automation"),
516 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
518 items.push_back (MenuElem (_("Hide All Automation"),
519 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
521 /* Attach the plugin submenu. It may have previously been used elsewhere,
522 so it was detached above
525 if (!subplugin_menu.items().empty()) {
526 items.push_back (SeparatorElem ());
527 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
528 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
531 /* Add any route automation */
534 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
535 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
536 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
537 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
539 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
543 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
544 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
545 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
546 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
548 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
552 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
553 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
554 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
555 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
557 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
560 if (!pan_tracks.empty()) {
561 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
562 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
563 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
564 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
566 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
567 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
568 _main_automation_menu_map[*p] = pan_automation_item;
574 RouteTimeAxisView::build_display_menu ()
576 using namespace Menu_Helpers;
580 TimeAxisView::build_display_menu ();
582 /* now fill it with our stuff */
584 MenuList& items = display_menu->items();
585 display_menu->set_name ("ArdourContextMenu");
587 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
589 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
591 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
593 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
595 items.push_back (SeparatorElem());
598 detach_menu (*_size_menu);
601 items.push_back (MenuElem (_("Height"), *_size_menu));
602 items.push_back (SeparatorElem());
604 // Hook for derived classes to add type specific stuff
605 append_extra_display_menu_items ();
609 Menu* layers_menu = manage (new Menu);
610 MenuList &layers_items = layers_menu->items();
611 layers_menu->set_name("ArdourContextMenu");
613 RadioMenuItem::Group layers_group;
615 /* Find out how many overlaid/stacked tracks we have in the selection */
619 TrackSelection const & s = _editor.get_selection().tracks;
620 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
621 StreamView* v = (*i)->view ();
626 switch (v->layer_display ()) {
637 /* We're not connecting to signal_toggled() here; in the case where these two items are
638 set to be in the `inconsistent' state, it seems that one or other will end up active
639 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
640 select the active one, no toggled signal is emitted so nothing happens.
643 _ignore_set_layer_display = true;
645 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
646 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
647 i->set_active (overlaid != 0 && stacked == 0);
648 i->set_inconsistent (overlaid != 0 && stacked != 0);
649 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
651 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
652 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
653 i->set_active (overlaid == 0 && stacked != 0);
654 i->set_inconsistent (overlaid != 0 && stacked != 0);
655 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
657 _ignore_set_layer_display = false;
659 items.push_back (MenuElem (_("Layers"), *layers_menu));
661 Menu* alignment_menu = manage (new Menu);
662 MenuList& alignment_items = alignment_menu->items();
663 alignment_menu->set_name ("ArdourContextMenu");
665 RadioMenuItem::Group align_group;
667 /* Same verbose hacks as for the layering options above */
673 boost::shared_ptr<Track> first_track;
675 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
676 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
677 if (!r || !r->is_track ()) {
682 first_track = r->track();
685 switch (r->track()->alignment_choice()) {
689 switch (r->track()->alignment_style()) {
690 case ExistingMaterial:
698 case UseExistingMaterial:
714 inconsistent = false;
721 if (!inconsistent && first_track) {
723 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
724 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
725 i->set_active (automatic != 0 && existing == 0 && capture == 0);
726 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
728 switch (first_track->alignment_choice()) {
730 switch (first_track->alignment_style()) {
731 case ExistingMaterial:
732 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
735 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
743 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
744 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
745 i->set_active (existing != 0 && capture == 0 && automatic == 0);
746 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
748 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
749 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
750 i->set_active (existing == 0 && capture != 0 && automatic == 0);
751 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
753 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
759 Menu* mode_menu = manage (new Menu);
760 MenuList& mode_items = mode_menu->items ();
761 mode_menu->set_name ("ArdourContextMenu");
763 RadioMenuItem::Group mode_group;
769 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
770 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
771 if (!r || !r->is_track ()) {
775 switch (r->track()->mode()) {
788 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
789 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
790 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
791 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
792 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
794 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
795 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
796 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
797 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
798 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
800 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
801 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
802 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
803 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
804 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
806 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
808 items.push_back (SeparatorElem());
810 build_playlist_menu ();
811 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
812 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
815 route_group_menu->detach ();
818 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
819 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
821 r.push_back (rtv->route ());
826 r.push_back (route ());
829 route_group_menu->build (r);
830 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
832 build_automation_action_menu (true);
833 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
835 items.push_back (SeparatorElem());
839 TrackSelection const & s = _editor.get_selection().tracks;
840 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
841 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
846 if (r->route()->active()) {
853 items.push_back (CheckMenuElem (_("Active")));
854 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
855 bool click_sets_active = true;
856 if (active > 0 && inactive == 0) {
857 i->set_active (true);
858 click_sets_active = false;
859 } else if (active > 0 && inactive > 0) {
860 i->set_inconsistent (true);
862 i->set_sensitive(! _session->transport_rolling());
863 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
865 items.push_back (SeparatorElem());
866 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
867 if (_route && !_route->is_master()) {
868 items.push_back (SeparatorElem());
869 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
871 items.push_back (SeparatorElem());
872 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
876 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
878 if (apply_to_selection) {
879 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
882 bool needs_bounce = false;
884 if (!track()->can_use_mode (mode, needs_bounce)) {
890 cerr << "would bounce this one\n";
895 track()->set_mode (mode);
900 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
902 TimeAxisView::show_timestretch (start, end, layers, layer);
912 /* check that the time selection was made in our route, or our route group.
913 remember that route_group() == 0 implies the route is *not* in a edit group.
916 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
917 /* this doesn't apply to us */
921 /* ignore it if our edit group is not active */
923 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
928 if (timestretch_rect == 0) {
929 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
930 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
931 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
934 timestretch_rect->show ();
935 timestretch_rect->raise_to_top ();
937 double const x1 = start / _editor.get_current_zoom();
938 double const x2 = (end - 1) / _editor.get_current_zoom();
940 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
941 x2, current_height() * (layers - layer) / layers));
945 RouteTimeAxisView::hide_timestretch ()
947 TimeAxisView::hide_timestretch ();
949 if (timestretch_rect) {
950 timestretch_rect->hide ();
955 RouteTimeAxisView::show_selection (TimeSelection& ts)
959 /* ignore it if our edit group is not active or if the selection was started
960 in some other track or route group (remember that route_group() == 0 means
961 that the track is not in an route group).
964 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
965 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
971 TimeAxisView::show_selection (ts);
975 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
978 bool height_changed = (height == 0) || (h != height);
981 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
984 gm.get_level_meter().setup_meters (gmlen, meter_width);
986 TimeAxisView::set_height (h, m);
989 _view->set_height ((double) current_height());
992 if (height >= preset_height (HeightNormal)) {
996 gm.get_gain_slider().show();
998 if (!_route || _route->is_monitor()) {
1001 solo_button->show();
1003 if (rec_enable_button)
1004 rec_enable_button->show();
1006 route_group_button.show();
1007 automation_button.show();
1009 if (is_track() && track()->mode() == ARDOUR::Normal) {
1010 playlist_button.show();
1017 gm.get_gain_slider().hide();
1018 mute_button->show();
1019 if (!_route || _route->is_monitor()) {
1020 solo_button->hide();
1022 solo_button->show();
1024 if (rec_enable_button)
1025 rec_enable_button->show();
1027 route_group_button.hide ();
1028 automation_button.hide ();
1030 if (is_track() && track()->mode() == ARDOUR::Normal) {
1031 playlist_button.hide ();
1036 if (height_changed && !no_redraw) {
1037 /* only emit the signal if the height really changed */
1043 RouteTimeAxisView::route_color_changed ()
1046 _view->apply_color (color(), StreamView::RegionColor);
1049 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1053 RouteTimeAxisView::reset_samples_per_pixel ()
1055 set_samples_per_pixel (_editor.get_current_zoom());
1059 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1064 speed = track()->speed();
1068 _view->set_samples_per_pixel (fpp * speed);
1071 TimeAxisView::set_samples_per_pixel (fpp * speed);
1075 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1077 if (!mitem->get_active()) {
1078 /* this is one of the two calls made when these radio menu items change status. this one
1079 is for the item that became inactive, and we want to ignore it.
1084 if (apply_to_selection) {
1085 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1088 track()->set_align_choice (choice);
1094 RouteTimeAxisView::rename_current_playlist ()
1096 ArdourPrompter prompter (true);
1099 boost::shared_ptr<Track> tr = track();
1100 if (!tr || tr->destructive()) {
1104 boost::shared_ptr<Playlist> pl = tr->playlist();
1109 prompter.set_title (_("Rename Playlist"));
1110 prompter.set_prompt (_("New name for playlist:"));
1111 prompter.set_initial_text (pl->name());
1112 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1113 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1115 switch (prompter.run ()) {
1116 case Gtk::RESPONSE_ACCEPT:
1117 prompter.get_result (name);
1118 if (name.length()) {
1119 pl->set_name (name);
1129 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1131 std::string ret (basename);
1133 std::string const group_string = "." + route_group()->name() + ".";
1135 // iterate through all playlists
1137 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1138 std::string tmp = (*i)->name();
1140 std::string::size_type idx = tmp.find(group_string);
1141 // find those which belong to this group
1142 if (idx != string::npos) {
1143 tmp = tmp.substr(idx + group_string.length());
1145 // and find the largest current number
1147 if (x > maxnumber) {
1156 snprintf (buf, sizeof(buf), "%d", maxnumber);
1158 ret = this->name() + "." + route_group()->name () + "." + buf;
1164 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1168 boost::shared_ptr<Track> tr = track ();
1169 if (!tr || tr->destructive()) {
1173 boost::shared_ptr<const Playlist> pl = tr->playlist();
1180 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1181 name = resolve_new_group_playlist_name(name, playlists_before_op);
1184 while (_session->playlists->by_name(name)) {
1185 name = Playlist::bump_name (name, *_session);
1188 // TODO: The prompter "new" button should be de-activated if the user
1189 // specifies a playlist name which already exists in the session.
1193 ArdourPrompter prompter (true);
1195 prompter.set_title (_("New Copy Playlist"));
1196 prompter.set_prompt (_("Name for new playlist:"));
1197 prompter.set_initial_text (name);
1198 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1199 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1200 prompter.show_all ();
1202 switch (prompter.run ()) {
1203 case Gtk::RESPONSE_ACCEPT:
1204 prompter.get_result (name);
1212 if (name.length()) {
1213 tr->use_copy_playlist ();
1214 tr->playlist()->set_name (name);
1219 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1223 boost::shared_ptr<Track> tr = track ();
1224 if (!tr || tr->destructive()) {
1228 boost::shared_ptr<const Playlist> pl = tr->playlist();
1235 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1236 name = resolve_new_group_playlist_name(name,playlists_before_op);
1239 while (_session->playlists->by_name(name)) {
1240 name = Playlist::bump_name (name, *_session);
1246 ArdourPrompter prompter (true);
1248 prompter.set_title (_("New Playlist"));
1249 prompter.set_prompt (_("Name for new playlist:"));
1250 prompter.set_initial_text (name);
1251 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1252 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1254 switch (prompter.run ()) {
1255 case Gtk::RESPONSE_ACCEPT:
1256 prompter.get_result (name);
1264 if (name.length()) {
1265 tr->use_new_playlist ();
1266 tr->playlist()->set_name (name);
1271 RouteTimeAxisView::clear_playlist ()
1273 boost::shared_ptr<Track> tr = track ();
1274 if (!tr || tr->destructive()) {
1278 boost::shared_ptr<Playlist> pl = tr->playlist();
1283 _editor.clear_playlist (pl);
1287 RouteTimeAxisView::speed_changed ()
1289 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1293 RouteTimeAxisView::update_diskstream_display ()
1303 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1305 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1307 /* special case: select/deselect all tracks */
1309 _editor.begin_reversible_selection_op (X_("Selection Click"));
1311 if (_editor.get_selection().selected (this)) {
1312 _editor.get_selection().clear_tracks ();
1314 _editor.select_all_tracks ();
1317 _editor.commit_reversible_selection_op ();
1322 _editor.begin_reversible_selection_op (X_("Selection Click"));
1324 switch (ArdourKeyboard::selection_type (ev->state)) {
1325 case Selection::Toggle:
1326 _editor.get_selection().toggle (this);
1329 case Selection::Set:
1330 _editor.get_selection().set (this);
1333 case Selection::Extend:
1334 _editor.extend_selection_to_track (*this);
1337 case Selection::Add:
1338 _editor.get_selection().add (this);
1342 _editor.commit_reversible_selection_op ();
1346 RouteTimeAxisView::set_selected_points (PointSelection& points)
1348 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1349 (*i)->set_selected_points (points);
1351 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1353 asv->set_selected_points (points);
1358 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1361 _view->set_selected_regionviews (regions);
1365 /** Add the selectable things that we have to a list.
1366 * @param results List to add things to.
1369 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1374 speed = track()->speed();
1377 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1378 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1380 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1381 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1384 /* pick up visible automation tracks */
1386 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1387 if (!(*i)->hidden()) {
1388 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1394 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1397 _view->get_inverted_selectables (sel, results);
1400 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1401 if (!(*i)->hidden()) {
1402 (*i)->get_inverted_selectables (sel, results);
1410 RouteTimeAxisView::route_group () const
1412 return _route->route_group();
1416 RouteTimeAxisView::name() const
1418 return _route->name();
1421 boost::shared_ptr<Playlist>
1422 RouteTimeAxisView::playlist () const
1424 boost::shared_ptr<Track> tr;
1426 if ((tr = track()) != 0) {
1427 return tr->playlist();
1429 return boost::shared_ptr<Playlist> ();
1434 RouteTimeAxisView::name_entry_changed (string const& str)
1436 if (str == _route->name()) {
1442 strip_whitespace_edges (x);
1448 if (_session->route_name_internal (x)) {
1449 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1451 } else if (RouteUI::verify_new_route_name (x)) {
1452 _route->set_name (x);
1459 boost::shared_ptr<Region>
1460 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1462 boost::shared_ptr<Playlist> pl = playlist ();
1465 return pl->find_next_region (pos, point, dir);
1468 return boost::shared_ptr<Region> ();
1472 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1474 boost::shared_ptr<Playlist> pl = playlist ();
1477 return pl->find_next_region_boundary (pos, dir);
1484 RouteTimeAxisView::fade_range (TimeSelection& selection)
1486 boost::shared_ptr<Playlist> what_we_got;
1487 boost::shared_ptr<Track> tr = track ();
1488 boost::shared_ptr<Playlist> playlist;
1491 /* route is a bus, not a track */
1495 playlist = tr->playlist();
1497 TimeSelection time (selection);
1498 float const speed = tr->speed();
1499 if (speed != 1.0f) {
1500 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1501 (*i).start = session_frame_to_track_frame((*i).start, speed);
1502 (*i).end = session_frame_to_track_frame((*i).end, speed);
1506 playlist->clear_changes ();
1507 playlist->clear_owned_changes ();
1509 playlist->fade_range (time);
1511 vector<Command*> cmds;
1512 playlist->rdiff (cmds);
1513 _session->add_commands (cmds);
1514 _session->add_command (new StatefulDiffCommand (playlist));
1519 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1521 boost::shared_ptr<Playlist> what_we_got;
1522 boost::shared_ptr<Track> tr = track ();
1523 boost::shared_ptr<Playlist> playlist;
1526 /* route is a bus, not a track */
1530 playlist = tr->playlist();
1532 TimeSelection time (selection.time);
1533 float const speed = tr->speed();
1534 if (speed != 1.0f) {
1535 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1536 (*i).start = session_frame_to_track_frame((*i).start, speed);
1537 (*i).end = session_frame_to_track_frame((*i).end, speed);
1541 playlist->clear_changes ();
1542 playlist->clear_owned_changes ();
1546 if (playlist->cut (time) != 0) {
1547 if (Config->get_edit_mode() == Ripple)
1548 playlist->ripple(time.start(), -time.length(), NULL);
1549 // no need to exclude any regions from rippling here
1551 vector<Command*> cmds;
1552 playlist->rdiff (cmds);
1553 _session->add_commands (cmds);
1555 _session->add_command (new StatefulDiffCommand (playlist));
1560 if ((what_we_got = playlist->cut (time)) != 0) {
1561 _editor.get_cut_buffer().add (what_we_got);
1562 if (Config->get_edit_mode() == Ripple)
1563 playlist->ripple(time.start(), -time.length(), NULL);
1564 // no need to exclude any regions from rippling here
1566 vector<Command*> cmds;
1567 playlist->rdiff (cmds);
1568 _session->add_commands (cmds);
1570 _session->add_command (new StatefulDiffCommand (playlist));
1574 if ((what_we_got = playlist->copy (time)) != 0) {
1575 _editor.get_cut_buffer().add (what_we_got);
1580 if ((what_we_got = playlist->cut (time)) != 0) {
1581 if (Config->get_edit_mode() == Ripple)
1582 playlist->ripple(time.start(), -time.length(), NULL);
1583 // no need to exclude any regions from rippling here
1585 vector<Command*> cmds;
1586 playlist->rdiff (cmds);
1587 _session->add_commands (cmds);
1588 _session->add_command (new StatefulDiffCommand (playlist));
1589 what_we_got->release ();
1596 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1602 boost::shared_ptr<Playlist> pl = playlist ();
1603 const ARDOUR::DataType type = pl->data_type();
1604 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1606 if (p == selection.playlists.end()) {
1609 ctx.counts.increase_n_playlists(type);
1611 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1613 if (track()->speed() != 1.0f) {
1614 pos = session_frame_to_track_frame (pos, track()->speed());
1615 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1618 /* add multi-paste offset if applicable */
1619 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1620 const framecnt_t duration = extent.second - extent.first;
1621 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1623 pl->clear_changes ();
1624 pl->clear_owned_changes ();
1625 if (Config->get_edit_mode() == Ripple) {
1626 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1627 framecnt_t amount = extent.second - extent.first;
1628 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1630 pl->paste (*p, pos, ctx.times);
1632 vector<Command*> cmds;
1634 _session->add_commands (cmds);
1636 _session->add_command (new StatefulDiffCommand (pl));
1642 struct PlaylistSorter {
1643 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1644 return a->sort_id() < b->sort_id();
1649 RouteTimeAxisView::build_playlist_menu ()
1651 using namespace Menu_Helpers;
1657 delete playlist_action_menu;
1658 playlist_action_menu = new Menu;
1659 playlist_action_menu->set_name ("ArdourContextMenu");
1661 MenuList& playlist_items = playlist_action_menu->items();
1662 playlist_action_menu->set_name ("ArdourContextMenu");
1663 playlist_items.clear();
1665 RadioMenuItem::Group playlist_group;
1666 boost::shared_ptr<Track> tr = track ();
1668 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1670 /* sort the playlists */
1672 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1674 /* add the playlists to the menu */
1675 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1676 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1677 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1678 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1680 if (tr->playlist()->id() == (*i)->id()) {
1686 playlist_items.push_back (SeparatorElem());
1687 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1688 playlist_items.push_back (SeparatorElem());
1690 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1691 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1692 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1695 // Use a label which tells the user what is happening
1696 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1697 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1701 playlist_items.push_back (SeparatorElem());
1702 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1703 playlist_items.push_back (SeparatorElem());
1705 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1709 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1711 assert (is_track());
1713 // exit if we were triggered by deactivating the old playlist
1714 if (!item->get_active()) {
1718 boost::shared_ptr<Playlist> pl (wpl.lock());
1724 if (track()->playlist() == pl) {
1725 // exit when use_playlist is called by the creation of the playlist menu
1726 // or the playlist choice is unchanged
1730 track()->use_playlist (pl);
1732 RouteGroup* rg = route_group();
1734 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1735 std::string group_string = "." + rg->name() + ".";
1737 std::string take_name = pl->name();
1738 std::string::size_type idx = take_name.find(group_string);
1740 if (idx == std::string::npos)
1743 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1745 boost::shared_ptr<RouteList> rl (rg->route_list());
1747 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1748 if ((*i) == this->route()) {
1752 std::string playlist_name = (*i)->name()+group_string+take_name;
1754 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1759 if (track->freeze_state() == Track::Frozen) {
1760 /* Don't change playlists of frozen tracks */
1764 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1766 // No playlist for this track for this take yet, make it
1767 track->use_new_playlist();
1768 track->playlist()->set_name(playlist_name);
1770 track->use_playlist(ipl);
1777 RouteTimeAxisView::update_playlist_tip ()
1779 RouteGroup* rg = route_group ();
1780 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1781 string group_string = "." + rg->name() + ".";
1783 string take_name = track()->playlist()->name();
1784 string::size_type idx = take_name.find(group_string);
1786 if (idx != string::npos) {
1787 /* find the bit containing the take number / name */
1788 take_name = take_name.substr (idx + group_string.length());
1790 /* set the playlist button tooltip to the take name */
1793 string_compose(_("Take: %1.%2"),
1794 Gtkmm2ext::markup_escape_text (rg->name()),
1795 Gtkmm2ext::markup_escape_text (take_name))
1802 /* set the playlist button tooltip to the playlist name */
1803 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1808 RouteTimeAxisView::show_playlist_selector ()
1810 _editor.playlist_selector().show_for (this);
1814 RouteTimeAxisView::map_frozen ()
1820 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1822 switch (track()->freeze_state()) {
1824 playlist_button.set_sensitive (false);
1827 playlist_button.set_sensitive (true);
1830 RouteUI::map_frozen ();
1834 RouteTimeAxisView::color_handler ()
1836 //case cTimeStretchOutline:
1837 if (timestretch_rect) {
1838 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1840 //case cTimeStretchFill:
1841 if (timestretch_rect) {
1842 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1848 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1849 * Will add track if necessary.
1852 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1854 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1855 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1858 /* it doesn't exist yet, so we don't care about the button state: just add it */
1859 create_automation_child (param, true);
1862 bool yn = menu->get_active();
1863 bool changed = false;
1865 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1867 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1868 will have done that for us.
1871 if (changed && !no_redraw) {
1879 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1881 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1887 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1889 if (menu && !_hidden) {
1890 ignore_toggle = true;
1891 menu->set_active (false);
1892 ignore_toggle = false;
1895 if (_route && !no_redraw) {
1901 RouteTimeAxisView::update_gain_track_visibility ()
1903 bool const showit = gain_automation_item->get_active();
1905 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1906 gain_track->set_marked_for_display (showit);
1908 /* now trigger a redisplay */
1911 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1917 RouteTimeAxisView::update_trim_track_visibility ()
1919 bool const showit = trim_automation_item->get_active();
1921 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1922 trim_track->set_marked_for_display (showit);
1924 /* now trigger a redisplay */
1927 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1933 RouteTimeAxisView::update_mute_track_visibility ()
1935 bool const showit = mute_automation_item->get_active();
1937 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1938 mute_track->set_marked_for_display (showit);
1940 /* now trigger a redisplay */
1943 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1949 RouteTimeAxisView::update_pan_track_visibility ()
1951 bool const showit = pan_automation_item->get_active();
1952 bool changed = false;
1954 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1955 if ((*i)->set_marked_for_display (showit)) {
1961 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1966 RouteTimeAxisView::ensure_pan_views (bool show)
1968 bool changed = false;
1969 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1971 (*i)->set_marked_for_display (false);
1974 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1978 if (!_route->panner()) {
1982 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1983 set<Evoral::Parameter>::iterator p;
1985 for (p = params.begin(); p != params.end(); ++p) {
1986 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1988 if (pan_control->parameter().type() == NullAutomation) {
1989 error << "Pan control has NULL automation type!" << endmsg;
1993 if (automation_child (pan_control->parameter ()).get () == 0) {
1995 /* we don't already have an AutomationTimeAxisView for this parameter */
1997 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1999 boost::shared_ptr<AutomationTimeAxisView> t (
2000 new AutomationTimeAxisView (_session,
2004 pan_control->parameter (),
2012 pan_tracks.push_back (t);
2013 add_automation_child (*p, t, show);
2015 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2022 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2024 if (apply_to_selection) {
2025 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2029 /* Show our automation */
2031 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2032 i->second->set_marked_for_display (true);
2034 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2037 menu->set_active(true);
2042 /* Show processor automation */
2044 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2045 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2046 if ((*ii)->view == 0) {
2047 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2050 (*ii)->menu_item->set_active (true);
2063 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2065 if (apply_to_selection) {
2066 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2070 /* Show our automation */
2072 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2073 if (i->second->has_automation()) {
2074 i->second->set_marked_for_display (true);
2076 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2078 menu->set_active(true);
2083 /* Show processor automation */
2085 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2086 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2087 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2088 (*ii)->menu_item->set_active (true);
2100 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2102 if (apply_to_selection) {
2103 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2107 /* Hide our automation */
2109 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2110 i->second->set_marked_for_display (false);
2112 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2115 menu->set_active (false);
2119 /* Hide processor automation */
2121 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2122 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2123 (*ii)->menu_item->set_active (false);
2134 RouteTimeAxisView::region_view_added (RegionView* rv)
2136 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2137 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2138 boost::shared_ptr<AutomationTimeAxisView> atv;
2140 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2145 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2146 (*i)->add_ghost(rv);
2150 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2152 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2158 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2160 parent.remove_processor_automation_node (this);
2164 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2167 remove_child (pan->view);
2171 RouteTimeAxisView::ProcessorAutomationNode*
2172 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2174 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2176 if ((*i)->processor == processor) {
2178 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2179 if ((*ii)->what == what) {
2189 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2191 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2194 ProcessorAutomationNode* pan;
2196 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2197 /* session state may never have been saved with new plugin */
2198 error << _("programming error: ")
2199 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2200 processor->name(), what.type(), (int) what.channel(), what.id() )
2202 abort(); /*NOTREACHED*/
2210 boost::shared_ptr<AutomationControl> control
2211 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2213 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2214 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2215 _editor, *this, false, parent_canvas,
2216 processor->describe_parameter (what), processor->name()));
2218 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2220 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2223 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2228 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2231 pan->menu_item->set_active (false);
2240 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2242 boost::shared_ptr<Processor> processor (p.lock ());
2244 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2245 /* The Amp processor is a special case and is dealt with separately */
2249 set<Evoral::Parameter> existing;
2251 processor->what_has_data (existing);
2253 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2255 Evoral::Parameter param (*i);
2256 boost::shared_ptr<AutomationLine> al;
2258 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2261 add_processor_automation_curve (processor, param);
2267 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2269 using namespace Menu_Helpers;
2273 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2275 _automation_tracks[param] = track;
2277 /* existing state overrides "show" argument */
2278 string s = track->gui_property ("visible");
2280 show = string_is_affirmative (s);
2283 /* this might or might not change the visibility status, so don't rely on it */
2284 track->set_marked_for_display (show);
2286 if (show && !no_redraw) {
2290 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2291 /* MIDI-related parameters are always in the menu, there's no
2292 reason to rebuild the menu just because we added a automation
2293 lane for one of them. But if we add a non-MIDI automation
2294 lane, then we need to invalidate the display menu.
2296 delete display_menu;
2302 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2304 boost::shared_ptr<Processor> processor (p.lock ());
2306 if (!processor || !processor->display_to_user ()) {
2310 /* we use this override to veto the Amp processor from the plugin menu,
2311 as its automation lane can be accessed using the special "Fader" menu
2315 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2319 using namespace Menu_Helpers;
2320 ProcessorAutomationInfo *rai;
2321 list<ProcessorAutomationInfo*>::iterator x;
2323 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2325 if (automatable.empty()) {
2329 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2330 if ((*x)->processor == processor) {
2335 if (x == processor_automation.end()) {
2336 rai = new ProcessorAutomationInfo (processor);
2337 processor_automation.push_back (rai);
2342 /* any older menu was deleted at the top of processors_changed()
2343 when we cleared the subplugin menu.
2346 rai->menu = manage (new Menu);
2347 MenuList& items = rai->menu->items();
2348 rai->menu->set_name ("ArdourContextMenu");
2352 std::set<Evoral::Parameter> has_visible_automation;
2353 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2355 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2357 ProcessorAutomationNode* pan;
2358 Gtk::CheckMenuItem* mitem;
2360 string name = processor->describe_parameter (*i);
2362 if (name == X_("hidden")) {
2366 items.push_back (CheckMenuElem (name));
2367 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2369 _subplugin_menu_map[*i] = mitem;
2371 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2372 mitem->set_active(true);
2375 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2379 pan = new ProcessorAutomationNode (*i, mitem, *this);
2381 rai->lines.push_back (pan);
2385 pan->menu_item = mitem;
2389 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2392 if (items.size() == 0) {
2396 /* add the menu for this processor, because the subplugin
2397 menu is always cleared at the top of processors_changed().
2398 this is the result of some poor design in gtkmm and/or
2402 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2407 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2408 RouteTimeAxisView::ProcessorAutomationNode* pan)
2410 bool showit = pan->menu_item->get_active();
2411 bool redraw = false;
2413 if (pan->view == 0 && showit) {
2414 add_processor_automation_curve (rai->processor, pan->what);
2418 if (pan->view && pan->view->set_marked_for_display (showit)) {
2422 if (redraw && !no_redraw) {
2428 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2430 if (c.type == RouteProcessorChange::MeterPointChange) {
2431 /* nothing to do if only the meter point has changed */
2435 using namespace Menu_Helpers;
2437 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2438 (*i)->valid = false;
2441 setup_processor_menu_and_curves ();
2443 bool deleted_processor_automation = false;
2445 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2447 list<ProcessorAutomationInfo*>::iterator tmp;
2455 processor_automation.erase (i);
2456 deleted_processor_automation = true;
2463 if (deleted_processor_automation && !no_redraw) {
2468 boost::shared_ptr<AutomationLine>
2469 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2471 ProcessorAutomationNode* pan;
2473 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2479 return boost::shared_ptr<AutomationLine>();
2483 RouteTimeAxisView::reset_processor_automation_curves ()
2485 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2491 RouteTimeAxisView::can_edit_name () const
2493 /* we do not allow track name changes if it is record enabled
2495 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2499 return !trk->rec_enable_control()->get_value();
2503 RouteTimeAxisView::blink_rec_display (bool onoff)
2505 RouteUI::blink_rec_display (onoff);
2509 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2511 if (_ignore_set_layer_display) {
2515 if (apply_to_selection) {
2516 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2520 _view->set_layer_display (d);
2523 set_gui_property (X_("layer-display"), enum_2_string (d));
2528 RouteTimeAxisView::layer_display () const
2531 return _view->layer_display ();
2534 /* we don't know, since we don't have a _view, so just return something */
2540 boost::shared_ptr<AutomationTimeAxisView>
2541 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2543 AutomationTracks::iterator i = _automation_tracks.find(param);
2544 if (i != _automation_tracks.end()) {
2547 return boost::shared_ptr<AutomationTimeAxisView>();
2552 RouteTimeAxisView::fast_update ()
2554 gm.get_level_meter().update_meters ();
2558 RouteTimeAxisView::hide_meter ()
2561 gm.get_level_meter().hide_meters ();
2565 RouteTimeAxisView::show_meter ()
2571 RouteTimeAxisView::reset_meter ()
2573 if (UIConfiguration::instance().get_show_track_meters()) {
2574 int meter_width = 3;
2575 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2578 gm.get_level_meter().setup_meters (height - 9, meter_width);
2585 RouteTimeAxisView::clear_meter ()
2587 gm.get_level_meter().clear_meters ();
2591 RouteTimeAxisView::meter_changed ()
2593 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2595 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2598 // reset peak when meter point changes
2599 gm.reset_peak_display();
2603 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2606 if (_route && !no_redraw) {
2612 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2614 using namespace Menu_Helpers;
2616 if (!_underlay_streams.empty()) {
2617 MenuList& parent_items = parent_menu->items();
2618 Menu* gs_menu = manage (new Menu);
2619 gs_menu->set_name ("ArdourContextMenu");
2620 MenuList& gs_items = gs_menu->items();
2622 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2624 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2625 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2626 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2632 RouteTimeAxisView::set_underlay_state()
2634 if (!underlay_xml_node) {
2638 XMLNodeList nlist = underlay_xml_node->children();
2639 XMLNodeConstIterator niter;
2640 XMLNode *child_node;
2642 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2643 child_node = *niter;
2645 if (child_node->name() != "Underlay") {
2649 XMLProperty const * prop = child_node->property ("id");
2651 PBD::ID id (prop->value());
2653 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2656 add_underlay(v->view(), false);
2665 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2671 RouteTimeAxisView& other = v->trackview();
2673 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2674 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2675 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2676 abort(); /*NOTREACHED*/
2679 _underlay_streams.push_back(v);
2680 other._underlay_mirrors.push_back(this);
2682 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2684 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2686 if (!underlay_xml_node) {
2687 underlay_xml_node = xml_node->add_child("Underlays");
2690 XMLNode* node = underlay_xml_node->add_child("Underlay");
2691 XMLProperty const * prop = node->add_property("id");
2692 prop->set_value(v->trackview().route()->id().to_s());
2699 RouteTimeAxisView::remove_underlay (StreamView* v)
2705 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2706 RouteTimeAxisView& other = v->trackview();
2708 if (it != _underlay_streams.end()) {
2709 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2711 if (gm == other._underlay_mirrors.end()) {
2712 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2713 abort(); /*NOTREACHED*/
2716 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2718 _underlay_streams.erase(it);
2719 other._underlay_mirrors.erase(gm);
2721 if (underlay_xml_node) {
2722 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2728 RouteTimeAxisView::set_button_names ()
2730 if (_route && _route->solo_safe_control()->solo_safe()) {
2731 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2733 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2735 if (Config->get_solo_control_is_listen_control()) {
2736 switch (Config->get_listen_position()) {
2737 case AfterFaderListen:
2738 solo_button->set_text (S_("AfterFader|A"));
2739 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2741 case PreFaderListen:
2742 solo_button->set_text (S_("PreFader|P"));
2743 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2747 solo_button->set_text (S_("Solo|S"));
2748 set_tooltip (*solo_button, _("Solo"));
2750 mute_button->set_text (S_("Mute|M"));
2754 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2756 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2757 if (i != _main_automation_menu_map.end()) {
2761 i = _subplugin_menu_map.find (param);
2762 if (i != _subplugin_menu_map.end()) {
2770 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2772 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2774 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2778 gain_track.reset (new AutomationTimeAxisView (_session,
2779 _route, _route->amp(), c, param,
2784 _route->amp()->describe_parameter(param)));
2787 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2790 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2794 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2796 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2797 if (!c || ! _route->trim()->active()) {
2801 trim_track.reset (new AutomationTimeAxisView (_session,
2802 _route, _route->trim(), c, param,
2807 _route->trim()->describe_parameter(param)));
2810 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2813 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2817 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2819 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2821 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2825 mute_track.reset (new AutomationTimeAxisView (_session,
2826 _route, _route, c, param,
2831 _route->describe_parameter(param)));
2834 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2837 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2841 void add_region_to_list (RegionView* rv, RegionList* l)
2843 l->push_back (rv->region());
2847 RouteTimeAxisView::combine_regions ()
2849 /* as of may 2011, we do not offer uncombine for MIDI tracks
2852 if (!is_audio_track()) {
2860 RegionList selected_regions;
2861 boost::shared_ptr<Playlist> playlist = track()->playlist();
2863 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2865 if (selected_regions.size() < 2) {
2869 playlist->clear_changes ();
2870 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2872 _session->add_command (new StatefulDiffCommand (playlist));
2873 /* make the new region be selected */
2875 return _view->find_view (compound_region);
2879 RouteTimeAxisView::uncombine_regions ()
2881 /* as of may 2011, we do not offer uncombine for MIDI tracks
2883 if (!is_audio_track()) {
2891 RegionList selected_regions;
2892 boost::shared_ptr<Playlist> playlist = track()->playlist();
2894 /* have to grab selected regions first because the uncombine is going
2895 * to change that in the middle of the list traverse
2898 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2900 playlist->clear_changes ();
2902 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2903 playlist->uncombine (*i);
2906 _session->add_command (new StatefulDiffCommand (playlist));
2910 RouteTimeAxisView::state_id() const
2912 return string_compose ("rtav %1", _route->id().to_s());
2917 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2919 TimeAxisView::remove_child (c);
2921 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2923 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2924 if (i->second == a) {
2925 _automation_tracks.erase (i);
2932 PresentationInfo const &
2933 RouteTimeAxisView::presentation_info () const
2935 return _route->presentation_info();
2938 boost::shared_ptr<Stripable>
2939 RouteTimeAxisView::stripable () const
2945 RouteTimeAxisView::color () const
2947 return route_color ();
2951 RouteTimeAxisView::marked_for_display () const
2953 return !_route->presentation_info().hidden();
2957 RouteTimeAxisView::set_marked_for_display (bool yn)
2959 return RouteUI::mark_hidden (!yn);