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::post_construct ()
348 /* map current state of the route */
350 update_diskstream_display ();
351 setup_processor_menu_and_curves ();
352 reset_processor_automation_curves ();
355 /** Set up the processor menu for the current set of processors, and
356 * display automation curves for any parameters which have data.
359 RouteTimeAxisView::setup_processor_menu_and_curves ()
361 _subplugin_menu_map.clear ();
362 subplugin_menu.items().clear ();
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
364 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
368 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
370 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
371 if (_route->route_group()) {
372 _route->route_group()->remove (_route);
378 r.push_back (route ());
380 route_group_menu->build (r);
381 route_group_menu->menu()->popup (ev->button, ev->time);
387 RouteTimeAxisView::playlist_changed ()
393 RouteTimeAxisView::label_view ()
395 string x = _route->name ();
396 if (x != name_label.get_text ()) {
397 name_label.set_text (x);
399 const int64_t track_number = _route->track_number ();
400 if (track_number == 0) {
401 number_label.set_text ("");
403 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
408 RouteTimeAxisView::update_track_number_visibility ()
411 bool show_label = _session->config.get_track_name_number();
413 if (_route && _route->is_master()) {
417 if (number_label.get_parent()) {
418 controls_table.remove (number_label);
421 if (ARDOUR::Profile->get_mixbus()) {
422 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
424 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
426 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
427 // except the width of the number label is subtracted from the name-hbox, so we
428 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
429 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
431 number_label.set_size_request(tnw, -1);
432 number_label.show ();
434 number_label.hide ();
439 RouteTimeAxisView::parameter_changed (string const & p)
441 if (p == "track-name-number") {
442 update_track_number_visibility();
443 } else if (p == "editor-stereo-only-meters") {
444 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
445 gm.get_level_meter().set_max_audio_meter_count (2);
447 gm.get_level_meter().set_max_audio_meter_count (0);
453 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
455 if (what_changed.contains (ARDOUR::Properties::name)) {
461 RouteTimeAxisView::take_name_changed (void *src)
469 RouteTimeAxisView::playlist_click ()
471 build_playlist_menu ();
472 conditionally_add_to_selection ();
473 playlist_action_menu->popup (1, gtk_get_current_event_time());
477 RouteTimeAxisView::automation_click ()
479 conditionally_add_to_selection ();
480 build_automation_action_menu (false);
481 automation_action_menu->popup (1, gtk_get_current_event_time());
485 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
487 using namespace Menu_Helpers;
489 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
490 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
493 detach_menu (subplugin_menu);
495 _main_automation_menu_map.clear ();
496 delete automation_action_menu;
497 automation_action_menu = new Menu;
499 MenuList& items = automation_action_menu->items();
501 automation_action_menu->set_name ("ArdourContextMenu");
503 items.push_back (MenuElem (_("Show All Automation"),
504 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
506 items.push_back (MenuElem (_("Show Existing Automation"),
507 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
509 items.push_back (MenuElem (_("Hide All Automation"),
510 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
512 /* Attach the plugin submenu. It may have previously been used elsewhere,
513 so it was detached above
516 if (!subplugin_menu.items().empty()) {
517 items.push_back (SeparatorElem ());
518 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
519 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
522 /* Add any route automation */
525 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
526 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
527 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
528 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
530 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
534 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
535 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
536 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
537 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
539 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
543 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
544 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
545 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
546 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
548 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
551 if (!pan_tracks.empty()) {
552 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
553 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
554 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
555 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
557 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
558 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
559 _main_automation_menu_map[*p] = pan_automation_item;
565 RouteTimeAxisView::build_display_menu ()
567 using namespace Menu_Helpers;
571 TimeAxisView::build_display_menu ();
573 /* now fill it with our stuff */
575 MenuList& items = display_menu->items();
576 display_menu->set_name ("ArdourContextMenu");
578 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
580 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
582 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
584 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
586 items.push_back (SeparatorElem());
589 detach_menu (*_size_menu);
592 items.push_back (MenuElem (_("Height"), *_size_menu));
593 items.push_back (SeparatorElem());
595 // Hook for derived classes to add type specific stuff
596 append_extra_display_menu_items ();
600 Menu* layers_menu = manage (new Menu);
601 MenuList &layers_items = layers_menu->items();
602 layers_menu->set_name("ArdourContextMenu");
604 RadioMenuItem::Group layers_group;
606 /* Find out how many overlaid/stacked tracks we have in the selection */
610 TrackSelection const & s = _editor.get_selection().tracks;
611 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
612 StreamView* v = (*i)->view ();
617 switch (v->layer_display ()) {
628 /* We're not connecting to signal_toggled() here; in the case where these two items are
629 set to be in the `inconsistent' state, it seems that one or other will end up active
630 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
631 select the active one, no toggled signal is emitted so nothing happens.
634 _ignore_set_layer_display = true;
636 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
637 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
638 i->set_active (overlaid != 0 && stacked == 0);
639 i->set_inconsistent (overlaid != 0 && stacked != 0);
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
642 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
643 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
644 i->set_active (overlaid == 0 && stacked != 0);
645 i->set_inconsistent (overlaid != 0 && stacked != 0);
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
648 _ignore_set_layer_display = false;
650 items.push_back (MenuElem (_("Layers"), *layers_menu));
652 Menu* alignment_menu = manage (new Menu);
653 MenuList& alignment_items = alignment_menu->items();
654 alignment_menu->set_name ("ArdourContextMenu");
656 RadioMenuItem::Group align_group;
658 /* Same verbose hacks as for the layering options above */
664 boost::shared_ptr<Track> first_track;
666 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
667 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
668 if (!r || !r->is_track ()) {
673 first_track = r->track();
676 switch (r->track()->alignment_choice()) {
680 switch (r->track()->alignment_style()) {
681 case ExistingMaterial:
689 case UseExistingMaterial:
705 inconsistent = false;
712 if (!inconsistent && first_track) {
714 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
715 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
716 i->set_active (automatic != 0 && existing == 0 && capture == 0);
717 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
719 switch (first_track->alignment_choice()) {
721 switch (first_track->alignment_style()) {
722 case ExistingMaterial:
723 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
726 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
734 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
735 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
736 i->set_active (existing != 0 && capture == 0 && automatic == 0);
737 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
739 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
740 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
741 i->set_active (existing == 0 && capture != 0 && automatic == 0);
742 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
744 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
750 Menu* mode_menu = manage (new Menu);
751 MenuList& mode_items = mode_menu->items ();
752 mode_menu->set_name ("ArdourContextMenu");
754 RadioMenuItem::Group mode_group;
760 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
761 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
762 if (!r || !r->is_track ()) {
766 switch (r->track()->mode()) {
779 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
780 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
781 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
782 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
783 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
785 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
786 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
787 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
788 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
789 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
791 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
792 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
793 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
794 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
795 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
797 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
799 items.push_back (SeparatorElem());
801 build_playlist_menu ();
802 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
803 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
806 route_group_menu->detach ();
809 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
812 r.push_back (rtv->route ());
817 r.push_back (route ());
820 route_group_menu->build (r);
821 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
823 build_automation_action_menu (true);
824 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
826 items.push_back (SeparatorElem());
830 TrackSelection const & s = _editor.get_selection().tracks;
831 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
832 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
837 if (r->route()->active()) {
844 items.push_back (CheckMenuElem (_("Active")));
845 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
846 bool click_sets_active = true;
847 if (active > 0 && inactive == 0) {
848 i->set_active (true);
849 click_sets_active = false;
850 } else if (active > 0 && inactive > 0) {
851 i->set_inconsistent (true);
853 i->set_sensitive(! _session->transport_rolling());
854 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
856 items.push_back (SeparatorElem());
857 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
858 if (_route && !_route->is_master()) {
859 items.push_back (SeparatorElem());
860 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
862 items.push_back (SeparatorElem());
863 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
867 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
869 if (apply_to_selection) {
870 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
873 bool needs_bounce = false;
875 if (!track()->can_use_mode (mode, needs_bounce)) {
881 cerr << "would bounce this one\n";
886 track()->set_mode (mode);
891 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
893 TimeAxisView::show_timestretch (start, end, layers, layer);
903 /* check that the time selection was made in our route, or our route group.
904 remember that route_group() == 0 implies the route is *not* in a edit group.
907 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
908 /* this doesn't apply to us */
912 /* ignore it if our edit group is not active */
914 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
919 if (timestretch_rect == 0) {
920 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
921 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
922 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
925 timestretch_rect->show ();
926 timestretch_rect->raise_to_top ();
928 double const x1 = start / _editor.get_current_zoom();
929 double const x2 = (end - 1) / _editor.get_current_zoom();
931 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
932 x2, current_height() * (layers - layer) / layers));
936 RouteTimeAxisView::hide_timestretch ()
938 TimeAxisView::hide_timestretch ();
940 if (timestretch_rect) {
941 timestretch_rect->hide ();
946 RouteTimeAxisView::show_selection (TimeSelection& ts)
950 /* ignore it if our edit group is not active or if the selection was started
951 in some other track or route group (remember that route_group() == 0 means
952 that the track is not in an route group).
955 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
956 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
962 TimeAxisView::show_selection (ts);
966 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
969 bool height_changed = (height == 0) || (h != height);
972 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
975 gm.get_level_meter().setup_meters (gmlen, meter_width);
977 TimeAxisView::set_height (h, m);
980 _view->set_height ((double) current_height());
983 if (height >= preset_height (HeightNormal)) {
987 gm.get_gain_slider().show();
989 if (!_route || _route->is_monitor()) {
994 if (rec_enable_button)
995 rec_enable_button->show();
997 route_group_button.show();
998 automation_button.show();
1000 if (is_track() && track()->mode() == ARDOUR::Normal) {
1001 playlist_button.show();
1008 gm.get_gain_slider().hide();
1009 mute_button->show();
1010 if (!_route || _route->is_monitor()) {
1011 solo_button->hide();
1013 solo_button->show();
1015 if (rec_enable_button)
1016 rec_enable_button->show();
1018 route_group_button.hide ();
1019 automation_button.hide ();
1021 if (is_track() && track()->mode() == ARDOUR::Normal) {
1022 playlist_button.hide ();
1027 if (height_changed && !no_redraw) {
1028 /* only emit the signal if the height really changed */
1034 RouteTimeAxisView::route_color_changed ()
1037 _view->apply_color (color(), StreamView::RegionColor);
1040 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1044 RouteTimeAxisView::reset_samples_per_pixel ()
1046 set_samples_per_pixel (_editor.get_current_zoom());
1050 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1055 speed = track()->speed();
1059 _view->set_samples_per_pixel (fpp * speed);
1062 TimeAxisView::set_samples_per_pixel (fpp * speed);
1066 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1068 if (!mitem->get_active()) {
1069 /* this is one of the two calls made when these radio menu items change status. this one
1070 is for the item that became inactive, and we want to ignore it.
1075 if (apply_to_selection) {
1076 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1079 track()->set_align_choice (choice);
1085 RouteTimeAxisView::rename_current_playlist ()
1087 ArdourPrompter prompter (true);
1090 boost::shared_ptr<Track> tr = track();
1091 if (!tr || tr->destructive()) {
1095 boost::shared_ptr<Playlist> pl = tr->playlist();
1100 prompter.set_title (_("Rename Playlist"));
1101 prompter.set_prompt (_("New name for playlist:"));
1102 prompter.set_initial_text (pl->name());
1103 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1104 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1106 switch (prompter.run ()) {
1107 case Gtk::RESPONSE_ACCEPT:
1108 prompter.get_result (name);
1109 if (name.length()) {
1110 pl->set_name (name);
1120 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1122 std::string ret (basename);
1124 std::string const group_string = "." + route_group()->name() + ".";
1126 // iterate through all playlists
1128 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1129 std::string tmp = (*i)->name();
1131 std::string::size_type idx = tmp.find(group_string);
1132 // find those which belong to this group
1133 if (idx != string::npos) {
1134 tmp = tmp.substr(idx + group_string.length());
1136 // and find the largest current number
1138 if (x > maxnumber) {
1147 snprintf (buf, sizeof(buf), "%d", maxnumber);
1149 ret = this->name() + "." + route_group()->name () + "." + buf;
1155 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1159 boost::shared_ptr<Track> tr = track ();
1160 if (!tr || tr->destructive()) {
1164 boost::shared_ptr<const Playlist> pl = tr->playlist();
1171 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1172 name = resolve_new_group_playlist_name(name, playlists_before_op);
1175 while (_session->playlists->by_name(name)) {
1176 name = Playlist::bump_name (name, *_session);
1179 // TODO: The prompter "new" button should be de-activated if the user
1180 // specifies a playlist name which already exists in the session.
1184 ArdourPrompter prompter (true);
1186 prompter.set_title (_("New Copy Playlist"));
1187 prompter.set_prompt (_("Name for new playlist:"));
1188 prompter.set_initial_text (name);
1189 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1190 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1191 prompter.show_all ();
1193 switch (prompter.run ()) {
1194 case Gtk::RESPONSE_ACCEPT:
1195 prompter.get_result (name);
1203 if (name.length()) {
1204 tr->use_copy_playlist ();
1205 tr->playlist()->set_name (name);
1210 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1214 boost::shared_ptr<Track> tr = track ();
1215 if (!tr || tr->destructive()) {
1219 boost::shared_ptr<const Playlist> pl = tr->playlist();
1226 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1227 name = resolve_new_group_playlist_name(name,playlists_before_op);
1230 while (_session->playlists->by_name(name)) {
1231 name = Playlist::bump_name (name, *_session);
1237 ArdourPrompter prompter (true);
1239 prompter.set_title (_("New Playlist"));
1240 prompter.set_prompt (_("Name for new playlist:"));
1241 prompter.set_initial_text (name);
1242 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1243 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1245 switch (prompter.run ()) {
1246 case Gtk::RESPONSE_ACCEPT:
1247 prompter.get_result (name);
1255 if (name.length()) {
1256 tr->use_new_playlist ();
1257 tr->playlist()->set_name (name);
1262 RouteTimeAxisView::clear_playlist ()
1264 boost::shared_ptr<Track> tr = track ();
1265 if (!tr || tr->destructive()) {
1269 boost::shared_ptr<Playlist> pl = tr->playlist();
1274 _editor.clear_playlist (pl);
1278 RouteTimeAxisView::speed_changed ()
1280 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1284 RouteTimeAxisView::update_diskstream_display ()
1294 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1296 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1298 /* special case: select/deselect all tracks */
1300 _editor.begin_reversible_selection_op (X_("Selection Click"));
1302 if (_editor.get_selection().selected (this)) {
1303 _editor.get_selection().clear_tracks ();
1305 _editor.select_all_tracks ();
1308 _editor.commit_reversible_selection_op ();
1313 _editor.begin_reversible_selection_op (X_("Selection Click"));
1315 switch (ArdourKeyboard::selection_type (ev->state)) {
1316 case Selection::Toggle:
1317 _editor.get_selection().toggle (this);
1320 case Selection::Set:
1321 _editor.get_selection().set (this);
1324 case Selection::Extend:
1325 _editor.extend_selection_to_track (*this);
1328 case Selection::Add:
1329 _editor.get_selection().add (this);
1333 _editor.commit_reversible_selection_op ();
1337 RouteTimeAxisView::set_selected_points (PointSelection& points)
1339 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1340 (*i)->set_selected_points (points);
1342 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1344 asv->set_selected_points (points);
1349 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1352 _view->set_selected_regionviews (regions);
1356 /** Add the selectable things that we have to a list.
1357 * @param results List to add things to.
1360 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1365 speed = track()->speed();
1368 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1369 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1371 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1372 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1375 /* pick up visible automation tracks */
1377 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1378 if (!(*i)->hidden()) {
1379 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1385 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1388 _view->get_inverted_selectables (sel, results);
1391 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1392 if (!(*i)->hidden()) {
1393 (*i)->get_inverted_selectables (sel, results);
1401 RouteTimeAxisView::route_group () const
1403 return _route->route_group();
1407 RouteTimeAxisView::name() const
1409 return _route->name();
1412 boost::shared_ptr<Playlist>
1413 RouteTimeAxisView::playlist () const
1415 boost::shared_ptr<Track> tr;
1417 if ((tr = track()) != 0) {
1418 return tr->playlist();
1420 return boost::shared_ptr<Playlist> ();
1425 RouteTimeAxisView::name_entry_changed (string const& str)
1427 if (str == _route->name()) {
1433 strip_whitespace_edges (x);
1439 if (_session->route_name_internal (x)) {
1440 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1442 } else if (RouteUI::verify_new_route_name (x)) {
1443 _route->set_name (x);
1450 boost::shared_ptr<Region>
1451 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1453 boost::shared_ptr<Playlist> pl = playlist ();
1456 return pl->find_next_region (pos, point, dir);
1459 return boost::shared_ptr<Region> ();
1463 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1465 boost::shared_ptr<Playlist> pl = playlist ();
1468 return pl->find_next_region_boundary (pos, dir);
1475 RouteTimeAxisView::fade_range (TimeSelection& selection)
1477 boost::shared_ptr<Playlist> what_we_got;
1478 boost::shared_ptr<Track> tr = track ();
1479 boost::shared_ptr<Playlist> playlist;
1482 /* route is a bus, not a track */
1486 playlist = tr->playlist();
1488 TimeSelection time (selection);
1489 float const speed = tr->speed();
1490 if (speed != 1.0f) {
1491 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1492 (*i).start = session_frame_to_track_frame((*i).start, speed);
1493 (*i).end = session_frame_to_track_frame((*i).end, speed);
1497 playlist->clear_changes ();
1498 playlist->clear_owned_changes ();
1500 playlist->fade_range (time);
1502 vector<Command*> cmds;
1503 playlist->rdiff (cmds);
1504 _session->add_commands (cmds);
1505 _session->add_command (new StatefulDiffCommand (playlist));
1510 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1512 boost::shared_ptr<Playlist> what_we_got;
1513 boost::shared_ptr<Track> tr = track ();
1514 boost::shared_ptr<Playlist> playlist;
1517 /* route is a bus, not a track */
1521 playlist = tr->playlist();
1523 TimeSelection time (selection.time);
1524 float const speed = tr->speed();
1525 if (speed != 1.0f) {
1526 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1527 (*i).start = session_frame_to_track_frame((*i).start, speed);
1528 (*i).end = session_frame_to_track_frame((*i).end, speed);
1532 playlist->clear_changes ();
1533 playlist->clear_owned_changes ();
1537 if (playlist->cut (time) != 0) {
1538 if (Config->get_edit_mode() == Ripple)
1539 playlist->ripple(time.start(), -time.length(), NULL);
1540 // no need to exclude any regions from rippling here
1542 vector<Command*> cmds;
1543 playlist->rdiff (cmds);
1544 _session->add_commands (cmds);
1546 _session->add_command (new StatefulDiffCommand (playlist));
1551 if ((what_we_got = playlist->cut (time)) != 0) {
1552 _editor.get_cut_buffer().add (what_we_got);
1553 if (Config->get_edit_mode() == Ripple)
1554 playlist->ripple(time.start(), -time.length(), NULL);
1555 // no need to exclude any regions from rippling here
1557 vector<Command*> cmds;
1558 playlist->rdiff (cmds);
1559 _session->add_commands (cmds);
1561 _session->add_command (new StatefulDiffCommand (playlist));
1565 if ((what_we_got = playlist->copy (time)) != 0) {
1566 _editor.get_cut_buffer().add (what_we_got);
1571 if ((what_we_got = playlist->cut (time)) != 0) {
1572 if (Config->get_edit_mode() == Ripple)
1573 playlist->ripple(time.start(), -time.length(), NULL);
1574 // no need to exclude any regions from rippling here
1576 vector<Command*> cmds;
1577 playlist->rdiff (cmds);
1578 _session->add_commands (cmds);
1579 _session->add_command (new StatefulDiffCommand (playlist));
1580 what_we_got->release ();
1587 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1593 boost::shared_ptr<Playlist> pl = playlist ();
1594 const ARDOUR::DataType type = pl->data_type();
1595 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1597 if (p == selection.playlists.end()) {
1600 ctx.counts.increase_n_playlists(type);
1602 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1604 if (track()->speed() != 1.0f) {
1605 pos = session_frame_to_track_frame (pos, track()->speed());
1606 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1609 /* add multi-paste offset if applicable */
1610 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1611 const framecnt_t duration = extent.second - extent.first;
1612 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1614 pl->clear_changes ();
1615 pl->clear_owned_changes ();
1616 if (Config->get_edit_mode() == Ripple) {
1617 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1618 framecnt_t amount = extent.second - extent.first;
1619 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1621 pl->paste (*p, pos, ctx.times);
1623 vector<Command*> cmds;
1625 _session->add_commands (cmds);
1627 _session->add_command (new StatefulDiffCommand (pl));
1633 struct PlaylistSorter {
1634 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1635 return a->sort_id() < b->sort_id();
1640 RouteTimeAxisView::build_playlist_menu ()
1642 using namespace Menu_Helpers;
1648 delete playlist_action_menu;
1649 playlist_action_menu = new Menu;
1650 playlist_action_menu->set_name ("ArdourContextMenu");
1652 MenuList& playlist_items = playlist_action_menu->items();
1653 playlist_action_menu->set_name ("ArdourContextMenu");
1654 playlist_items.clear();
1656 RadioMenuItem::Group playlist_group;
1657 boost::shared_ptr<Track> tr = track ();
1659 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1661 /* sort the playlists */
1663 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1665 /* add the playlists to the menu */
1666 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1667 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1668 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1669 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1671 if (tr->playlist()->id() == (*i)->id()) {
1677 playlist_items.push_back (SeparatorElem());
1678 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1679 playlist_items.push_back (SeparatorElem());
1681 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1682 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1683 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1686 // Use a label which tells the user what is happening
1687 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1688 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1692 playlist_items.push_back (SeparatorElem());
1693 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1694 playlist_items.push_back (SeparatorElem());
1696 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1700 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1702 assert (is_track());
1704 // exit if we were triggered by deactivating the old playlist
1705 if (!item->get_active()) {
1709 boost::shared_ptr<Playlist> pl (wpl.lock());
1715 if (track()->playlist() == pl) {
1716 // exit when use_playlist is called by the creation of the playlist menu
1717 // or the playlist choice is unchanged
1721 track()->use_playlist (pl);
1723 RouteGroup* rg = route_group();
1725 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1726 std::string group_string = "." + rg->name() + ".";
1728 std::string take_name = pl->name();
1729 std::string::size_type idx = take_name.find(group_string);
1731 if (idx == std::string::npos)
1734 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1736 boost::shared_ptr<RouteList> rl (rg->route_list());
1738 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1739 if ((*i) == this->route()) {
1743 std::string playlist_name = (*i)->name()+group_string+take_name;
1745 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1750 if (track->freeze_state() == Track::Frozen) {
1751 /* Don't change playlists of frozen tracks */
1755 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1757 // No playlist for this track for this take yet, make it
1758 track->use_new_playlist();
1759 track->playlist()->set_name(playlist_name);
1761 track->use_playlist(ipl);
1768 RouteTimeAxisView::update_playlist_tip ()
1770 RouteGroup* rg = route_group ();
1771 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1772 string group_string = "." + rg->name() + ".";
1774 string take_name = track()->playlist()->name();
1775 string::size_type idx = take_name.find(group_string);
1777 if (idx != string::npos) {
1778 /* find the bit containing the take number / name */
1779 take_name = take_name.substr (idx + group_string.length());
1781 /* set the playlist button tooltip to the take name */
1784 string_compose(_("Take: %1.%2"),
1785 Gtkmm2ext::markup_escape_text (rg->name()),
1786 Gtkmm2ext::markup_escape_text (take_name))
1793 /* set the playlist button tooltip to the playlist name */
1794 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1799 RouteTimeAxisView::show_playlist_selector ()
1801 _editor.playlist_selector().show_for (this);
1805 RouteTimeAxisView::map_frozen ()
1811 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1813 switch (track()->freeze_state()) {
1815 playlist_button.set_sensitive (false);
1818 playlist_button.set_sensitive (true);
1821 RouteUI::map_frozen ();
1825 RouteTimeAxisView::color_handler ()
1827 //case cTimeStretchOutline:
1828 if (timestretch_rect) {
1829 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1831 //case cTimeStretchFill:
1832 if (timestretch_rect) {
1833 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1839 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1840 * Will add track if necessary.
1843 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1845 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1846 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1849 /* it doesn't exist yet, so we don't care about the button state: just add it */
1850 create_automation_child (param, true);
1853 bool yn = menu->get_active();
1854 bool changed = false;
1856 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1858 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1859 will have done that for us.
1862 if (changed && !no_redraw) {
1870 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1872 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1878 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1880 if (menu && !_hidden) {
1881 ignore_toggle = true;
1882 menu->set_active (false);
1883 ignore_toggle = false;
1886 if (_route && !no_redraw) {
1892 RouteTimeAxisView::update_gain_track_visibility ()
1894 bool const showit = gain_automation_item->get_active();
1896 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1897 gain_track->set_marked_for_display (showit);
1899 /* now trigger a redisplay */
1902 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1908 RouteTimeAxisView::update_trim_track_visibility ()
1910 bool const showit = trim_automation_item->get_active();
1912 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1913 trim_track->set_marked_for_display (showit);
1915 /* now trigger a redisplay */
1918 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1924 RouteTimeAxisView::update_mute_track_visibility ()
1926 bool const showit = mute_automation_item->get_active();
1928 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1929 mute_track->set_marked_for_display (showit);
1931 /* now trigger a redisplay */
1934 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1940 RouteTimeAxisView::update_pan_track_visibility ()
1942 bool const showit = pan_automation_item->get_active();
1943 bool changed = false;
1945 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1946 if ((*i)->set_marked_for_display (showit)) {
1952 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1957 RouteTimeAxisView::ensure_pan_views (bool show)
1959 bool changed = false;
1960 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1962 (*i)->set_marked_for_display (false);
1965 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1969 if (!_route->panner()) {
1973 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1974 set<Evoral::Parameter>::iterator p;
1976 for (p = params.begin(); p != params.end(); ++p) {
1977 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1979 if (pan_control->parameter().type() == NullAutomation) {
1980 error << "Pan control has NULL automation type!" << endmsg;
1984 if (automation_child (pan_control->parameter ()).get () == 0) {
1986 /* we don't already have an AutomationTimeAxisView for this parameter */
1988 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1990 boost::shared_ptr<AutomationTimeAxisView> t (
1991 new AutomationTimeAxisView (_session,
1995 pan_control->parameter (),
2003 pan_tracks.push_back (t);
2004 add_automation_child (*p, t, show);
2006 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2013 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2015 if (apply_to_selection) {
2016 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2020 /* Show our automation */
2022 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2023 i->second->set_marked_for_display (true);
2025 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2028 menu->set_active(true);
2033 /* Show processor automation */
2035 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2036 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2037 if ((*ii)->view == 0) {
2038 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2041 (*ii)->menu_item->set_active (true);
2054 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2056 if (apply_to_selection) {
2057 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2061 /* Show our automation */
2063 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2064 if (i->second->has_automation()) {
2065 i->second->set_marked_for_display (true);
2067 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2069 menu->set_active(true);
2074 /* Show processor automation */
2076 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2077 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2078 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2079 (*ii)->menu_item->set_active (true);
2091 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2093 if (apply_to_selection) {
2094 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2098 /* Hide our automation */
2100 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2101 i->second->set_marked_for_display (false);
2103 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2106 menu->set_active (false);
2110 /* Hide processor automation */
2112 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2113 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2114 (*ii)->menu_item->set_active (false);
2125 RouteTimeAxisView::region_view_added (RegionView* rv)
2127 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2128 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2129 boost::shared_ptr<AutomationTimeAxisView> atv;
2131 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2136 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2137 (*i)->add_ghost(rv);
2141 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2143 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2149 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2151 parent.remove_processor_automation_node (this);
2155 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2158 remove_child (pan->view);
2162 RouteTimeAxisView::ProcessorAutomationNode*
2163 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2165 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2167 if ((*i)->processor == processor) {
2169 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2170 if ((*ii)->what == what) {
2180 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2182 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2185 ProcessorAutomationNode* pan;
2187 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2188 /* session state may never have been saved with new plugin */
2189 error << _("programming error: ")
2190 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2191 processor->name(), what.type(), (int) what.channel(), what.id() )
2193 abort(); /*NOTREACHED*/
2201 boost::shared_ptr<AutomationControl> control
2202 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2204 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2205 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2206 _editor, *this, false, parent_canvas,
2207 processor->describe_parameter (what), processor->name()));
2209 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2211 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2214 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2219 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2222 pan->menu_item->set_active (false);
2231 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2233 boost::shared_ptr<Processor> processor (p.lock ());
2235 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2236 /* The Amp processor is a special case and is dealt with separately */
2240 set<Evoral::Parameter> existing;
2242 processor->what_has_data (existing);
2244 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2246 Evoral::Parameter param (*i);
2247 boost::shared_ptr<AutomationLine> al;
2249 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2252 add_processor_automation_curve (processor, param);
2258 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2260 using namespace Menu_Helpers;
2264 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2266 _automation_tracks[param] = track;
2268 /* existing state overrides "show" argument */
2269 string s = track->gui_property ("visible");
2271 show = string_is_affirmative (s);
2274 /* this might or might not change the visibility status, so don't rely on it */
2275 track->set_marked_for_display (show);
2277 if (show && !no_redraw) {
2281 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2282 /* MIDI-related parameters are always in the menu, there's no
2283 reason to rebuild the menu just because we added a automation
2284 lane for one of them. But if we add a non-MIDI automation
2285 lane, then we need to invalidate the display menu.
2287 delete display_menu;
2293 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2295 boost::shared_ptr<Processor> processor (p.lock ());
2297 if (!processor || !processor->display_to_user ()) {
2301 /* we use this override to veto the Amp processor from the plugin menu,
2302 as its automation lane can be accessed using the special "Fader" menu
2306 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2310 using namespace Menu_Helpers;
2311 ProcessorAutomationInfo *rai;
2312 list<ProcessorAutomationInfo*>::iterator x;
2314 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2316 if (automatable.empty()) {
2320 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2321 if ((*x)->processor == processor) {
2326 if (x == processor_automation.end()) {
2327 rai = new ProcessorAutomationInfo (processor);
2328 processor_automation.push_back (rai);
2333 /* any older menu was deleted at the top of processors_changed()
2334 when we cleared the subplugin menu.
2337 rai->menu = manage (new Menu);
2338 MenuList& items = rai->menu->items();
2339 rai->menu->set_name ("ArdourContextMenu");
2343 std::set<Evoral::Parameter> has_visible_automation;
2344 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2346 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2348 ProcessorAutomationNode* pan;
2349 Gtk::CheckMenuItem* mitem;
2351 string name = processor->describe_parameter (*i);
2353 if (name == X_("hidden")) {
2357 items.push_back (CheckMenuElem (name));
2358 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2360 _subplugin_menu_map[*i] = mitem;
2362 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2363 mitem->set_active(true);
2366 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2370 pan = new ProcessorAutomationNode (*i, mitem, *this);
2372 rai->lines.push_back (pan);
2376 pan->menu_item = mitem;
2380 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2383 if (items.size() == 0) {
2387 /* add the menu for this processor, because the subplugin
2388 menu is always cleared at the top of processors_changed().
2389 this is the result of some poor design in gtkmm and/or
2393 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2398 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2399 RouteTimeAxisView::ProcessorAutomationNode* pan)
2401 bool showit = pan->menu_item->get_active();
2402 bool redraw = false;
2404 if (pan->view == 0 && showit) {
2405 add_processor_automation_curve (rai->processor, pan->what);
2409 if (pan->view && pan->view->set_marked_for_display (showit)) {
2413 if (redraw && !no_redraw) {
2419 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2421 if (c.type == RouteProcessorChange::MeterPointChange) {
2422 /* nothing to do if only the meter point has changed */
2426 using namespace Menu_Helpers;
2428 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2429 (*i)->valid = false;
2432 setup_processor_menu_and_curves ();
2434 bool deleted_processor_automation = false;
2436 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2438 list<ProcessorAutomationInfo*>::iterator tmp;
2446 processor_automation.erase (i);
2447 deleted_processor_automation = true;
2454 if (deleted_processor_automation && !no_redraw) {
2459 boost::shared_ptr<AutomationLine>
2460 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2462 ProcessorAutomationNode* pan;
2464 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2470 return boost::shared_ptr<AutomationLine>();
2474 RouteTimeAxisView::reset_processor_automation_curves ()
2476 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2482 RouteTimeAxisView::can_edit_name () const
2484 /* we do not allow track name changes if it is record enabled
2486 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2490 return !trk->rec_enable_control()->get_value();
2494 RouteTimeAxisView::blink_rec_display (bool onoff)
2496 RouteUI::blink_rec_display (onoff);
2500 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2502 if (_ignore_set_layer_display) {
2506 if (apply_to_selection) {
2507 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2511 _view->set_layer_display (d);
2514 set_gui_property (X_("layer-display"), enum_2_string (d));
2519 RouteTimeAxisView::layer_display () const
2522 return _view->layer_display ();
2525 /* we don't know, since we don't have a _view, so just return something */
2531 boost::shared_ptr<AutomationTimeAxisView>
2532 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2534 AutomationTracks::iterator i = _automation_tracks.find(param);
2535 if (i != _automation_tracks.end()) {
2538 return boost::shared_ptr<AutomationTimeAxisView>();
2543 RouteTimeAxisView::fast_update ()
2545 gm.get_level_meter().update_meters ();
2549 RouteTimeAxisView::hide_meter ()
2552 gm.get_level_meter().hide_meters ();
2556 RouteTimeAxisView::show_meter ()
2562 RouteTimeAxisView::reset_meter ()
2564 if (UIConfiguration::instance().get_show_track_meters()) {
2565 int meter_width = 3;
2566 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2569 gm.get_level_meter().setup_meters (height - 9, meter_width);
2576 RouteTimeAxisView::clear_meter ()
2578 gm.get_level_meter().clear_meters ();
2582 RouteTimeAxisView::meter_changed ()
2584 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2586 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2589 // reset peak when meter point changes
2590 gm.reset_peak_display();
2594 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2597 if (_route && !no_redraw) {
2603 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2605 using namespace Menu_Helpers;
2607 if (!_underlay_streams.empty()) {
2608 MenuList& parent_items = parent_menu->items();
2609 Menu* gs_menu = manage (new Menu);
2610 gs_menu->set_name ("ArdourContextMenu");
2611 MenuList& gs_items = gs_menu->items();
2613 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2615 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2616 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2617 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2623 RouteTimeAxisView::set_underlay_state()
2625 if (!underlay_xml_node) {
2629 XMLNodeList nlist = underlay_xml_node->children();
2630 XMLNodeConstIterator niter;
2631 XMLNode *child_node;
2633 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2634 child_node = *niter;
2636 if (child_node->name() != "Underlay") {
2640 XMLProperty const * prop = child_node->property ("id");
2642 PBD::ID id (prop->value());
2644 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2647 add_underlay(v->view(), false);
2656 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2662 RouteTimeAxisView& other = v->trackview();
2664 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2665 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2666 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2667 abort(); /*NOTREACHED*/
2670 _underlay_streams.push_back(v);
2671 other._underlay_mirrors.push_back(this);
2673 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2675 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2677 if (!underlay_xml_node) {
2678 underlay_xml_node = xml_node->add_child("Underlays");
2681 XMLNode* node = underlay_xml_node->add_child("Underlay");
2682 XMLProperty const * prop = node->add_property("id");
2683 prop->set_value(v->trackview().route()->id().to_s());
2690 RouteTimeAxisView::remove_underlay (StreamView* v)
2696 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2697 RouteTimeAxisView& other = v->trackview();
2699 if (it != _underlay_streams.end()) {
2700 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2702 if (gm == other._underlay_mirrors.end()) {
2703 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2704 abort(); /*NOTREACHED*/
2707 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2709 _underlay_streams.erase(it);
2710 other._underlay_mirrors.erase(gm);
2712 if (underlay_xml_node) {
2713 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2719 RouteTimeAxisView::set_button_names ()
2721 if (_route && _route->solo_safe_control()->solo_safe()) {
2722 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2724 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2726 if (Config->get_solo_control_is_listen_control()) {
2727 switch (Config->get_listen_position()) {
2728 case AfterFaderListen:
2729 solo_button->set_text (S_("AfterFader|A"));
2730 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2732 case PreFaderListen:
2733 solo_button->set_text (S_("PreFader|P"));
2734 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2738 solo_button->set_text (S_("Solo|S"));
2739 set_tooltip (*solo_button, _("Solo"));
2741 mute_button->set_text (S_("Mute|M"));
2745 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2747 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2748 if (i != _main_automation_menu_map.end()) {
2752 i = _subplugin_menu_map.find (param);
2753 if (i != _subplugin_menu_map.end()) {
2761 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2763 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2765 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2769 gain_track.reset (new AutomationTimeAxisView (_session,
2770 _route, _route->amp(), c, param,
2775 _route->amp()->describe_parameter(param)));
2778 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2781 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2785 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2787 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2788 if (!c || ! _route->trim()->active()) {
2792 trim_track.reset (new AutomationTimeAxisView (_session,
2793 _route, _route->trim(), c, param,
2798 _route->trim()->describe_parameter(param)));
2801 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2804 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2808 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2810 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2812 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2816 mute_track.reset (new AutomationTimeAxisView (_session,
2817 _route, _route, c, param,
2822 _route->describe_parameter(param)));
2825 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2828 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2832 void add_region_to_list (RegionView* rv, RegionList* l)
2834 l->push_back (rv->region());
2838 RouteTimeAxisView::combine_regions ()
2840 /* as of may 2011, we do not offer uncombine for MIDI tracks
2843 if (!is_audio_track()) {
2851 RegionList selected_regions;
2852 boost::shared_ptr<Playlist> playlist = track()->playlist();
2854 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2856 if (selected_regions.size() < 2) {
2860 playlist->clear_changes ();
2861 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2863 _session->add_command (new StatefulDiffCommand (playlist));
2864 /* make the new region be selected */
2866 return _view->find_view (compound_region);
2870 RouteTimeAxisView::uncombine_regions ()
2872 /* as of may 2011, we do not offer uncombine for MIDI tracks
2874 if (!is_audio_track()) {
2882 RegionList selected_regions;
2883 boost::shared_ptr<Playlist> playlist = track()->playlist();
2885 /* have to grab selected regions first because the uncombine is going
2886 * to change that in the middle of the list traverse
2889 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2891 playlist->clear_changes ();
2893 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2894 playlist->uncombine (*i);
2897 _session->add_command (new StatefulDiffCommand (playlist));
2901 RouteTimeAxisView::state_id() const
2903 return string_compose ("rtav %1", _route->id().to_s());
2908 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2910 TimeAxisView::remove_child (c);
2912 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2914 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2915 if (i->second == a) {
2916 _automation_tracks.erase (i);
2923 PresentationInfo const &
2924 RouteTimeAxisView::presentation_info () const
2926 return _route->presentation_info();
2929 boost::shared_ptr<Stripable>
2930 RouteTimeAxisView::stripable () const
2936 RouteTimeAxisView::marked_for_display () const
2938 return !_route->presentation_info().hidden();
2942 RouteTimeAxisView::set_marked_for_display (bool yn)
2944 return RouteUI::mark_hidden (!yn);