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)
99 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
101 , parent_canvas (canvas)
103 , button_table (3, 3)
104 , route_group_button (S_("RTAV|G"))
105 , playlist_button (S_("RTAV|P"))
106 , automation_button (S_("RTAV|A"))
107 , automation_action_menu (0)
108 , plugins_submenu_item (0)
109 , route_group_menu (0)
110 , playlist_action_menu (0)
112 , color_mode_menu (0)
113 , gm (sess, true, 75, 14)
114 , _ignore_set_layer_display (false)
115 , gain_automation_item(NULL)
116 , trim_automation_item(NULL)
117 , mute_automation_item(NULL)
118 , pan_automation_item(NULL)
120 number_label.set_name("tracknumber label");
121 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
122 number_label.set_alignment(.5, .5);
123 number_label.set_fallthrough_to_parent (true);
125 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
126 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
128 parameter_changed ("editor-stereo-only-meters");
132 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
134 if (what_changed.contains (ARDOUR::Properties::name)) {
140 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
142 RouteUI::set_route (rt);
144 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
145 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
146 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
149 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
152 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
153 gm.get_level_meter().set_no_show_all();
154 gm.get_level_meter().setup_meters(50, meter_width);
155 gm.update_gain_sensitive ();
157 string str = gui_property ("height");
159 set_height (atoi (str));
161 set_height (preset_height (HeightNormal));
164 if (!_route->is_auditioner()) {
165 if (gui_property ("visible").empty()) {
166 set_gui_property ("visible", true);
169 set_gui_property ("visible", false);
172 timestretch_rect = 0;
175 ignore_toggle = false;
177 route_group_button.set_name ("route button");
178 playlist_button.set_name ("route button");
179 automation_button.set_name ("route button");
181 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
182 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
183 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
187 if (ARDOUR::Profile->get_mixbus()) {
188 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
190 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
193 if (is_midi_track()) {
194 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
195 gm.set_fader_name ("MidiTrackFader");
197 set_tooltip(*rec_enable_button, _("Record"));
198 gm.set_fader_name ("AudioTrackFader");
201 /* set playlist button tip to the current playlist, and make it update when it changes */
202 update_playlist_tip ();
203 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
206 gm.set_fader_name ("AudioBusFader");
207 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208 controls_button_size_group->add_widget(*blank);
209 if (ARDOUR::Profile->get_mixbus() ) {
210 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
212 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
219 if (!ARDOUR::Profile->get_mixbus()) {
220 controls_meters_size_group->add_widget (gm.get_level_meter());
223 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 // mute button is always present, it is used to
234 // force the 'blank' placeholders to the proper size
235 controls_button_size_group->add_widget(*mute_button);
237 if (!_route->is_master()) {
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 Gtk::Fixed *blank = manage(new Gtk::Fixed());
245 controls_button_size_group->add_widget(*blank);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 if (ARDOUR::Profile->get_mixbus()) {
255 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
258 else if (!ARDOUR::Profile->get_trx()) {
259 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 set_tooltip(*solo_button,_("Solo"));
264 set_tooltip(*mute_button,_("Mute"));
265 set_tooltip(route_group_button, _("Route Group"));
267 mute_button->set_tweaks(ArdourButton::TrackHeader);
268 solo_button->set_tweaks(ArdourButton::TrackHeader);
269 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
270 playlist_button.set_tweaks(ArdourButton::TrackHeader);
271 automation_button.set_tweaks(ArdourButton::TrackHeader);
272 route_group_button.set_tweaks(ArdourButton::TrackHeader);
274 if (is_midi_track()) {
275 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
277 set_tooltip(automation_button, _("Automation"));
280 update_track_number_visibility();
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 if (is_track() && track()->mode() == ARDOUR::Normal) {
291 if (ARDOUR::Profile->get_mixbus()) {
292 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 else if (!ARDOUR::Profile->get_trx()) {
295 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
301 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
305 str = gui_property ("layer-display");
307 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
310 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
311 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
313 /* pick up the correct freeze state */
318 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
319 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
321 PropertyList* plist = new PropertyList();
323 plist->add (ARDOUR::Properties::group_mute, true);
324 plist->add (ARDOUR::Properties::group_solo, true);
326 route_group_menu = new RouteGroupMenu (_session, plist);
328 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
331 RouteTimeAxisView::~RouteTimeAxisView ()
333 cleanup_gui_properties ();
335 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
339 delete playlist_action_menu;
340 playlist_action_menu = 0;
345 _automation_tracks.clear ();
347 delete route_group_menu;
348 CatchDeletion (this);
352 RouteTimeAxisView::name() const
355 return _route->name();
361 RouteTimeAxisView::post_construct ()
363 /* map current state of the route */
365 update_diskstream_display ();
366 setup_processor_menu_and_curves ();
367 reset_processor_automation_curves ();
370 /** Set up the processor menu for the current set of processors, and
371 * display automation curves for any parameters which have data.
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
376 _subplugin_menu_map.clear ();
377 subplugin_menu.items().clear ();
378 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
383 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
385 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
386 if (_route->route_group()) {
387 _route->route_group()->remove (_route);
393 r.push_back (route ());
395 route_group_menu->build (r);
396 if (ev->button == 1) {
397 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
401 route_group_menu->menu()->popup (ev->button, ev->time);
408 RouteTimeAxisView::playlist_changed ()
414 RouteTimeAxisView::label_view ()
416 string x = _route->name ();
417 if (x != name_label.get_text ()) {
418 name_label.set_text (x);
420 const int64_t track_number = _route->track_number ();
421 if (track_number == 0) {
422 number_label.set_text ("");
424 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
429 RouteTimeAxisView::update_track_number_visibility ()
432 bool show_label = _session->config.get_track_name_number();
434 if (_route && _route->is_master()) {
438 if (number_label.get_parent()) {
439 controls_table.remove (number_label);
442 if (ARDOUR::Profile->get_mixbus()) {
443 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
445 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
448 // except the width of the number label is subtracted from the name-hbox, so we
449 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
450 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
452 number_label.set_size_request(tnw, -1);
453 number_label.show ();
455 number_label.hide ();
460 RouteTimeAxisView::parameter_changed (string const & p)
462 if (p == "track-name-number") {
463 update_track_number_visibility();
464 } else if (p == "editor-stereo-only-meters") {
465 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
466 gm.get_level_meter().set_max_audio_meter_count (2);
468 gm.get_level_meter().set_max_audio_meter_count (0);
474 RouteTimeAxisView::take_name_changed (void *src)
482 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
484 if (ev->button != 1) {
488 build_playlist_menu ();
489 conditionally_add_to_selection ();
490 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
496 RouteTimeAxisView::automation_click (GdkEventButton *ev)
498 if (ev->button != 1) {
502 conditionally_add_to_selection ();
503 build_automation_action_menu (false);
504 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
510 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
512 using namespace Menu_Helpers;
514 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
515 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
518 detach_menu (subplugin_menu);
520 _main_automation_menu_map.clear ();
521 delete automation_action_menu;
522 automation_action_menu = new Menu;
524 MenuList& items = automation_action_menu->items();
526 automation_action_menu->set_name ("ArdourContextMenu");
528 items.push_back (MenuElem (_("Show All Automation"),
529 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
531 items.push_back (MenuElem (_("Show Existing Automation"),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
534 items.push_back (MenuElem (_("Hide All Automation"),
535 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
537 /* Attach the plugin submenu. It may have previously been used elsewhere,
538 so it was detached above
541 if (!subplugin_menu.items().empty()) {
542 items.push_back (SeparatorElem ());
543 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
544 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
547 /* Add any route automation */
550 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
551 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
552 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
553 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
555 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
559 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
560 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
562 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
564 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
568 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
569 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
570 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
571 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
573 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
576 if (!pan_tracks.empty()) {
577 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
578 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
579 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
580 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
582 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
583 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
584 _main_automation_menu_map[*p] = pan_automation_item;
590 RouteTimeAxisView::build_display_menu ()
592 using namespace Menu_Helpers;
596 TimeAxisView::build_display_menu ();
598 /* now fill it with our stuff */
600 MenuList& items = display_menu->items();
601 display_menu->set_name ("ArdourContextMenu");
603 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
605 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
607 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
609 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
611 items.push_back (SeparatorElem());
614 detach_menu (*_size_menu);
617 items.push_back (MenuElem (_("Height"), *_size_menu));
618 items.push_back (SeparatorElem());
620 // Hook for derived classes to add type specific stuff
621 append_extra_display_menu_items ();
625 Menu* layers_menu = manage (new Menu);
626 MenuList &layers_items = layers_menu->items();
627 layers_menu->set_name("ArdourContextMenu");
629 RadioMenuItem::Group layers_group;
631 /* Find out how many overlaid/stacked tracks we have in the selection */
635 int unchangeable = 0;
636 TrackSelection const & s = _editor.get_selection().tracks;
638 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
639 StreamView* v = (*i)->view ();
644 if (v->can_change_layer_display()) {
645 switch (v->layer_display ()) {
659 /* We're not connecting to signal_toggled() here; in the case where these two items are
660 set to be in the `inconsistent' state, it seems that one or other will end up active
661 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
662 select the active one, no toggled signal is emitted so nothing happens.
665 _ignore_set_layer_display = true;
667 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
668 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
669 i->set_active (overlaid != 0 && stacked == 0);
670 i->set_inconsistent (overlaid != 0 && stacked != 0);
671 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
674 i->set_sensitive (false);
677 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
678 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
679 i->set_active (overlaid == 0 && stacked != 0);
680 i->set_inconsistent (overlaid != 0 && stacked != 0);
681 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
684 i->set_sensitive (false);
687 _ignore_set_layer_display = false;
689 items.push_back (MenuElem (_("Layers"), *layers_menu));
691 Menu* alignment_menu = manage (new Menu);
692 MenuList& alignment_items = alignment_menu->items();
693 alignment_menu->set_name ("ArdourContextMenu");
695 RadioMenuItem::Group align_group;
697 /* Same verbose hacks as for the layering options above */
703 boost::shared_ptr<Track> first_track;
705 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
706 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
707 if (!r || !r->is_track ()) {
712 first_track = r->track();
715 switch (r->track()->alignment_choice()) {
719 switch (r->track()->alignment_style()) {
720 case ExistingMaterial:
728 case UseExistingMaterial:
744 inconsistent = false;
751 if (!inconsistent && first_track) {
753 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
754 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
755 i->set_active (automatic != 0 && existing == 0 && capture == 0);
756 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
758 switch (first_track->alignment_choice()) {
760 switch (first_track->alignment_style()) {
761 case ExistingMaterial:
762 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
765 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
773 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
774 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
775 i->set_active (existing != 0 && capture == 0 && automatic == 0);
776 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
778 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
779 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
780 i->set_active (existing == 0 && capture != 0 && automatic == 0);
781 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
783 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
789 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
790 Menu* mode_menu = manage (new Menu);
791 MenuList& mode_items = mode_menu->items ();
792 mode_menu->set_name ("ArdourContextMenu");
794 RadioMenuItem::Group mode_group;
800 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
801 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
802 if (!r || !r->is_track ()) {
806 switch (r->track()->mode()) {
819 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
820 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
821 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
822 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
823 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
825 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
826 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
827 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
828 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
829 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
831 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
832 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
833 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
834 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
835 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
837 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
840 items.push_back (SeparatorElem());
842 build_playlist_menu ();
843 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
844 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
847 route_group_menu->detach ();
850 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
851 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
853 r.push_back (rtv->route ());
858 r.push_back (route ());
861 route_group_menu->build (r);
862 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
864 build_automation_action_menu (true);
865 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
867 items.push_back (SeparatorElem());
869 if (is_midi_track()) {
870 Menu* midi_menu = manage (new Menu);
871 MenuList& midi_items = midi_menu->items();
872 midi_menu->set_name (X_("ArdourContextMenu"));
874 midi_items.push_back (MenuElem (_("Channel Management"), sigc::mem_fun (*this, &RouteTimeAxisView::toggle_channel_selector)));
876 items.push_back (MenuElem (_("MIDI"), *midi_menu));
877 items.push_back (SeparatorElem());
882 TrackSelection const & s = _editor.get_selection().tracks;
883 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
884 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
889 if (r->route()->active()) {
896 items.push_back (CheckMenuElem (_("Active")));
897 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
898 bool click_sets_active = true;
899 if (active > 0 && inactive == 0) {
900 i->set_active (true);
901 click_sets_active = false;
902 } else if (active > 0 && inactive > 0) {
903 i->set_inconsistent (true);
905 i->set_sensitive(! _session->transport_rolling());
906 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
908 items.push_back (SeparatorElem());
909 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
910 if (_route && !_route->is_master()) {
911 items.push_back (SeparatorElem());
912 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
914 items.push_back (SeparatorElem());
915 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
918 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
920 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
922 if (apply_to_selection) {
923 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
926 bool needs_bounce = false;
928 if (!track()->can_use_mode (mode, needs_bounce)) {
934 cerr << "would bounce this one\n";
939 track()->set_mode (mode);
945 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
947 TimeAxisView::show_timestretch (start, end, layers, layer);
957 /* check that the time selection was made in our route, or our route group.
958 remember that route_group() == 0 implies the route is *not* in a edit group.
961 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
962 /* this doesn't apply to us */
966 /* ignore it if our edit group is not active */
968 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
973 if (timestretch_rect == 0) {
974 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
975 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
976 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
979 timestretch_rect->show ();
980 timestretch_rect->raise_to_top ();
982 double const x1 = start / _editor.get_current_zoom();
983 double const x2 = (end - 1) / _editor.get_current_zoom();
985 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
986 x2, current_height() * (layers - layer) / layers));
990 RouteTimeAxisView::hide_timestretch ()
992 TimeAxisView::hide_timestretch ();
994 if (timestretch_rect) {
995 timestretch_rect->hide ();
1000 RouteTimeAxisView::show_selection (TimeSelection& ts)
1004 /* ignore it if our edit group is not active or if the selection was started
1005 in some other track or route group (remember that route_group() == 0 means
1006 that the track is not in an route group).
1009 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
1010 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1016 TimeAxisView::show_selection (ts);
1020 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1023 bool height_changed = (height == 0) || (h != height);
1025 int meter_width = 3;
1026 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1029 gm.get_level_meter().setup_meters (gmlen, meter_width);
1031 TimeAxisView::set_height (h, m);
1034 _view->set_height ((double) current_height());
1037 if (height >= preset_height (HeightNormal)) {
1041 gm.get_gain_slider().show();
1042 mute_button->show();
1043 if (!_route || _route->is_monitor()) {
1044 solo_button->hide();
1046 solo_button->show();
1048 if (rec_enable_button)
1049 rec_enable_button->show();
1051 route_group_button.show();
1052 automation_button.show();
1054 if (is_track() && track()->mode() == ARDOUR::Normal) {
1055 playlist_button.show();
1062 gm.get_gain_slider().hide();
1063 mute_button->show();
1064 if (!_route || _route->is_monitor()) {
1065 solo_button->hide();
1067 solo_button->show();
1069 if (rec_enable_button)
1070 rec_enable_button->show();
1072 route_group_button.hide ();
1073 automation_button.hide ();
1075 if (is_track() && track()->mode() == ARDOUR::Normal) {
1076 playlist_button.hide ();
1081 if (height_changed && !no_redraw) {
1082 /* only emit the signal if the height really changed */
1088 RouteTimeAxisView::route_color_changed ()
1091 _view->apply_color (color(), StreamView::RegionColor);
1094 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1098 RouteTimeAxisView::reset_samples_per_pixel ()
1100 set_samples_per_pixel (_editor.get_current_zoom());
1104 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1109 speed = track()->speed();
1113 _view->set_samples_per_pixel (fpp * speed);
1116 TimeAxisView::set_samples_per_pixel (fpp * speed);
1120 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1122 if (!mitem->get_active()) {
1123 /* this is one of the two calls made when these radio menu items change status. this one
1124 is for the item that became inactive, and we want to ignore it.
1129 if (apply_to_selection) {
1130 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1133 track()->set_align_choice (choice);
1139 RouteTimeAxisView::rename_current_playlist ()
1141 ArdourPrompter prompter (true);
1144 boost::shared_ptr<Track> tr = track();
1145 if (!tr || tr->destructive()) {
1149 boost::shared_ptr<Playlist> pl = tr->playlist();
1154 prompter.set_title (_("Rename Playlist"));
1155 prompter.set_prompt (_("New name for playlist:"));
1156 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1157 prompter.set_initial_text (pl->name());
1158 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1161 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1164 prompter.get_result (name);
1165 if (name.length()) {
1166 if (_session->playlists->by_name (name)) {
1167 MessageDialog msg (_("Given playlist name is not unique."));
1169 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1171 pl->set_name (name);
1179 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1181 std::string ret (basename);
1183 std::string const group_string = "." + route_group()->name() + ".";
1185 // iterate through all playlists
1187 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1188 std::string tmp = (*i)->name();
1190 std::string::size_type idx = tmp.find(group_string);
1191 // find those which belong to this group
1192 if (idx != string::npos) {
1193 tmp = tmp.substr(idx + group_string.length());
1195 // and find the largest current number
1197 if (x > maxnumber) {
1206 snprintf (buf, sizeof(buf), "%d", maxnumber);
1208 ret = this->name() + "." + route_group()->name () + "." + buf;
1214 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1218 boost::shared_ptr<Track> tr = track ();
1219 if (!tr || tr->destructive()) {
1223 boost::shared_ptr<const Playlist> pl = tr->playlist();
1230 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1231 name = resolve_new_group_playlist_name(name,playlists_before_op);
1234 while (_session->playlists->by_name(name)) {
1235 name = Playlist::bump_name (name, *_session);
1239 // TODO: The prompter "new" button should be de-activated if the user
1240 // specifies a playlist name which already exists in the session.
1242 ArdourPrompter prompter (true);
1245 prompter.set_title (_("New Copy Playlist"));
1246 prompter.set_prompt (_("Name for playlist copy:"));
1248 prompter.set_title (_("New Playlist"));
1249 prompter.set_prompt (_("Name for new playlist:"));
1251 prompter.set_initial_text (name);
1252 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1253 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1254 prompter.show_all ();
1257 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1260 prompter.get_result (name);
1261 if (name.length()) {
1262 if (_session->playlists->by_name (name)) {
1263 MessageDialog msg (_("Given playlist name is not unique."));
1265 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1273 if (name.length()) {
1275 tr->use_copy_playlist ();
1277 tr->use_new_playlist ();
1279 tr->playlist()->set_name (name);
1284 RouteTimeAxisView::clear_playlist ()
1286 boost::shared_ptr<Track> tr = track ();
1287 if (!tr || tr->destructive()) {
1291 boost::shared_ptr<Playlist> pl = tr->playlist();
1296 _editor.clear_playlist (pl);
1300 RouteTimeAxisView::speed_changed ()
1302 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1306 RouteTimeAxisView::update_diskstream_display ()
1316 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1318 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1320 /* special case: select/deselect all tracks */
1322 _editor.begin_reversible_selection_op (X_("Selection Click"));
1324 if (_editor.get_selection().selected (this)) {
1325 _editor.get_selection().clear_tracks ();
1327 _editor.select_all_tracks ();
1330 _editor.commit_reversible_selection_op ();
1335 _editor.begin_reversible_selection_op (X_("Selection Click"));
1337 switch (ArdourKeyboard::selection_type (ev->state)) {
1338 case Selection::Toggle:
1339 _editor.get_selection().toggle (this);
1342 case Selection::Set:
1343 _editor.get_selection().set (this);
1346 case Selection::Extend:
1347 _editor.extend_selection_to_track (*this);
1350 case Selection::Add:
1351 _editor.get_selection().add (this);
1355 _editor.commit_reversible_selection_op ();
1359 RouteTimeAxisView::set_selected_points (PointSelection& points)
1361 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1362 (*i)->set_selected_points (points);
1364 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1366 asv->set_selected_points (points);
1371 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1374 _view->set_selected_regionviews (regions);
1378 /** Add the selectable things that we have to a list.
1379 * @param results List to add things to.
1382 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1387 speed = track()->speed();
1390 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1391 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1393 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1394 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1397 /* pick up visible automation tracks */
1399 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1400 if (!(*i)->hidden()) {
1401 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1407 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1410 _view->get_inverted_selectables (sel, results);
1413 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1414 if (!(*i)->hidden()) {
1415 (*i)->get_inverted_selectables (sel, results);
1423 RouteTimeAxisView::route_group () const
1425 return _route->route_group();
1428 boost::shared_ptr<Playlist>
1429 RouteTimeAxisView::playlist () const
1431 boost::shared_ptr<Track> tr;
1433 if ((tr = track()) != 0) {
1434 return tr->playlist();
1436 return boost::shared_ptr<Playlist> ();
1441 RouteTimeAxisView::name_entry_changed (string const& str)
1443 if (str == _route->name()) {
1449 strip_whitespace_edges (x);
1455 if (_session->route_name_internal (x)) {
1456 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1458 } else if (RouteUI::verify_new_route_name (x)) {
1459 _route->set_name (x);
1466 boost::shared_ptr<Region>
1467 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1469 boost::shared_ptr<Playlist> pl = playlist ();
1472 return pl->find_next_region (pos, point, dir);
1475 return boost::shared_ptr<Region> ();
1479 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1481 boost::shared_ptr<Playlist> pl = playlist ();
1484 return pl->find_next_region_boundary (pos, dir);
1491 RouteTimeAxisView::fade_range (TimeSelection& selection)
1493 boost::shared_ptr<Playlist> what_we_got;
1494 boost::shared_ptr<Track> tr = track ();
1495 boost::shared_ptr<Playlist> playlist;
1498 /* route is a bus, not a track */
1502 playlist = tr->playlist();
1504 TimeSelection time (selection);
1505 float const speed = tr->speed();
1506 if (speed != 1.0f) {
1507 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1508 (*i).start = session_frame_to_track_frame((*i).start, speed);
1509 (*i).end = session_frame_to_track_frame((*i).end, speed);
1513 playlist->clear_changes ();
1514 playlist->clear_owned_changes ();
1516 playlist->fade_range (time);
1518 vector<Command*> cmds;
1519 playlist->rdiff (cmds);
1520 _session->add_commands (cmds);
1521 _session->add_command (new StatefulDiffCommand (playlist));
1526 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1528 boost::shared_ptr<Playlist> what_we_got;
1529 boost::shared_ptr<Track> tr = track ();
1530 boost::shared_ptr<Playlist> playlist;
1533 /* route is a bus, not a track */
1537 playlist = tr->playlist();
1539 TimeSelection time (selection.time);
1540 float const speed = tr->speed();
1541 if (speed != 1.0f) {
1542 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1543 (*i).start = session_frame_to_track_frame((*i).start, speed);
1544 (*i).end = session_frame_to_track_frame((*i).end, speed);
1548 playlist->clear_changes ();
1549 playlist->clear_owned_changes ();
1553 if (playlist->cut (time) != 0) {
1554 if (Config->get_edit_mode() == Ripple)
1555 playlist->ripple(time.start(), -time.length(), NULL);
1556 // no need to exclude any regions from rippling here
1558 vector<Command*> cmds;
1559 playlist->rdiff (cmds);
1560 _session->add_commands (cmds);
1562 _session->add_command (new StatefulDiffCommand (playlist));
1567 if ((what_we_got = playlist->cut (time)) != 0) {
1568 _editor.get_cut_buffer().add (what_we_got);
1569 if (Config->get_edit_mode() == Ripple)
1570 playlist->ripple(time.start(), -time.length(), NULL);
1571 // no need to exclude any regions from rippling here
1573 vector<Command*> cmds;
1574 playlist->rdiff (cmds);
1575 _session->add_commands (cmds);
1577 _session->add_command (new StatefulDiffCommand (playlist));
1581 if ((what_we_got = playlist->copy (time)) != 0) {
1582 _editor.get_cut_buffer().add (what_we_got);
1587 if ((what_we_got = playlist->cut (time)) != 0) {
1588 if (Config->get_edit_mode() == Ripple)
1589 playlist->ripple(time.start(), -time.length(), NULL);
1590 // no need to exclude any regions from rippling here
1592 vector<Command*> cmds;
1593 playlist->rdiff (cmds);
1594 _session->add_commands (cmds);
1595 _session->add_command (new StatefulDiffCommand (playlist));
1596 what_we_got->release ();
1603 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1609 boost::shared_ptr<Playlist> pl = playlist ();
1610 const ARDOUR::DataType type = pl->data_type();
1611 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1613 if (p == selection.playlists.end()) {
1616 ctx.counts.increase_n_playlists(type);
1618 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1620 if (track()->speed() != 1.0f) {
1621 pos = session_frame_to_track_frame (pos, track()->speed());
1622 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1625 /* add multi-paste offset if applicable */
1626 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1627 const framecnt_t duration = extent.second - extent.first;
1628 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1630 pl->clear_changes ();
1631 pl->clear_owned_changes ();
1632 if (Config->get_edit_mode() == Ripple) {
1633 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1634 framecnt_t amount = extent.second - extent.first;
1635 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1637 pl->paste (*p, pos, ctx.times, sub_num);
1639 vector<Command*> cmds;
1641 _session->add_commands (cmds);
1643 _session->add_command (new StatefulDiffCommand (pl));
1649 struct PlaylistSorter {
1650 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1651 return a->sort_id() < b->sort_id();
1656 RouteTimeAxisView::build_playlist_menu ()
1658 using namespace Menu_Helpers;
1664 delete playlist_action_menu;
1665 playlist_action_menu = new Menu;
1666 playlist_action_menu->set_name ("ArdourContextMenu");
1668 MenuList& playlist_items = playlist_action_menu->items();
1669 playlist_action_menu->set_name ("ArdourContextMenu");
1670 playlist_items.clear();
1672 RadioMenuItem::Group playlist_group;
1673 boost::shared_ptr<Track> tr = track ();
1675 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1677 /* sort the playlists */
1679 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1681 /* add the playlists to the menu */
1682 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1683 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1684 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1685 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1687 if (tr->playlist()->id() == (*i)->id()) {
1693 playlist_items.push_back (SeparatorElem());
1694 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1695 playlist_items.push_back (SeparatorElem());
1697 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1698 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1699 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1702 // Use a label which tells the user what is happening
1703 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1704 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1708 playlist_items.push_back (SeparatorElem());
1709 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1710 playlist_items.push_back (SeparatorElem());
1712 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1716 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1718 assert (is_track());
1720 // exit if we were triggered by deactivating the old playlist
1721 if (!item->get_active()) {
1725 boost::shared_ptr<Playlist> pl (wpl.lock());
1731 if (track()->playlist() == pl) {
1732 // exit when use_playlist is called by the creation of the playlist menu
1733 // or the playlist choice is unchanged
1737 track()->use_playlist (pl);
1739 RouteGroup* rg = route_group();
1741 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1742 std::string group_string = "." + rg->name() + ".";
1744 std::string take_name = pl->name();
1745 std::string::size_type idx = take_name.find(group_string);
1747 if (idx == std::string::npos)
1750 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1752 boost::shared_ptr<RouteList> rl (rg->route_list());
1754 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1755 if ((*i) == this->route()) {
1759 std::string playlist_name = (*i)->name()+group_string+take_name;
1761 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1766 if (track->freeze_state() == Track::Frozen) {
1767 /* Don't change playlists of frozen tracks */
1771 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1773 // No playlist for this track for this take yet, make it
1774 track->use_new_playlist();
1775 track->playlist()->set_name(playlist_name);
1777 track->use_playlist(ipl);
1784 RouteTimeAxisView::update_playlist_tip ()
1786 RouteGroup* rg = route_group ();
1787 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1788 string group_string = "." + rg->name() + ".";
1790 string take_name = track()->playlist()->name();
1791 string::size_type idx = take_name.find(group_string);
1793 if (idx != string::npos) {
1794 /* find the bit containing the take number / name */
1795 take_name = take_name.substr (idx + group_string.length());
1797 /* set the playlist button tooltip to the take name */
1800 string_compose(_("Take: %1.%2"),
1801 Gtkmm2ext::markup_escape_text (rg->name()),
1802 Gtkmm2ext::markup_escape_text (take_name))
1809 /* set the playlist button tooltip to the playlist name */
1810 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1815 RouteTimeAxisView::show_playlist_selector ()
1817 _editor.playlist_selector().show_for (this);
1821 RouteTimeAxisView::map_frozen ()
1827 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1829 switch (track()->freeze_state()) {
1831 playlist_button.set_sensitive (false);
1834 playlist_button.set_sensitive (true);
1837 RouteUI::map_frozen ();
1841 RouteTimeAxisView::color_handler ()
1843 //case cTimeStretchOutline:
1844 if (timestretch_rect) {
1845 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1847 //case cTimeStretchFill:
1848 if (timestretch_rect) {
1849 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1855 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1856 * Will add track if necessary.
1859 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1861 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1862 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1865 /* it doesn't exist yet, so we don't care about the button state: just add it */
1866 create_automation_child (param, true);
1869 bool yn = menu->get_active();
1870 bool changed = false;
1872 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1874 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1875 will have done that for us.
1878 if (changed && !no_redraw) {
1886 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1888 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1894 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1896 if (menu && !_hidden) {
1897 ignore_toggle = true;
1898 menu->set_active (false);
1899 ignore_toggle = false;
1902 if (_route && !no_redraw) {
1908 RouteTimeAxisView::update_gain_track_visibility ()
1910 bool const showit = gain_automation_item->get_active();
1912 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1913 gain_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_trim_track_visibility ()
1926 bool const showit = trim_automation_item->get_active();
1928 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1929 trim_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_mute_track_visibility ()
1942 bool const showit = mute_automation_item->get_active();
1944 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1945 mute_track->set_marked_for_display (showit);
1947 /* now trigger a redisplay */
1950 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1956 RouteTimeAxisView::update_pan_track_visibility ()
1958 bool const showit = pan_automation_item->get_active();
1959 bool changed = false;
1961 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1962 if ((*i)->set_marked_for_display (showit)) {
1968 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1973 RouteTimeAxisView::ensure_pan_views (bool show)
1975 bool changed = false;
1976 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1978 (*i)->set_marked_for_display (false);
1981 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1985 if (!_route->panner()) {
1989 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1990 set<Evoral::Parameter>::iterator p;
1992 for (p = params.begin(); p != params.end(); ++p) {
1993 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1995 if (pan_control->parameter().type() == NullAutomation) {
1996 error << "Pan control has NULL automation type!" << endmsg;
2000 if (automation_child (pan_control->parameter ()).get () == 0) {
2002 /* we don't already have an AutomationTimeAxisView for this parameter */
2004 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2006 boost::shared_ptr<AutomationTimeAxisView> t (
2007 new AutomationTimeAxisView (_session,
2011 pan_control->parameter (),
2019 pan_tracks.push_back (t);
2020 add_automation_child (*p, t, show);
2022 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2029 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2031 if (apply_to_selection) {
2032 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2036 /* Show our automation */
2038 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2039 i->second->set_marked_for_display (true);
2041 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2044 menu->set_active(true);
2049 /* Show processor automation */
2051 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2052 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2053 if ((*ii)->view == 0) {
2054 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2057 (*ii)->menu_item->set_active (true);
2070 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2072 if (apply_to_selection) {
2073 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2077 /* Show our automation */
2079 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2080 if (i->second->has_automation()) {
2081 i->second->set_marked_for_display (true);
2083 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2085 menu->set_active(true);
2090 /* Show processor automation */
2092 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2093 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2094 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2095 (*ii)->menu_item->set_active (true);
2107 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2109 if (apply_to_selection) {
2110 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2114 /* Hide our automation */
2116 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2117 i->second->set_marked_for_display (false);
2119 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2122 menu->set_active (false);
2126 /* Hide processor automation */
2128 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2129 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2130 (*ii)->menu_item->set_active (false);
2141 RouteTimeAxisView::region_view_added (RegionView* rv)
2143 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2144 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2145 boost::shared_ptr<AutomationTimeAxisView> atv;
2147 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2152 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2153 (*i)->add_ghost(rv);
2157 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2159 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2165 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2167 parent.remove_processor_automation_node (this);
2171 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2174 remove_child (pan->view);
2178 RouteTimeAxisView::ProcessorAutomationNode*
2179 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2181 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2183 if ((*i)->processor == processor) {
2185 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2186 if ((*ii)->what == what) {
2196 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2198 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2201 ProcessorAutomationNode* pan;
2203 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2204 /* session state may never have been saved with new plugin */
2205 error << _("programming error: ")
2206 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2207 processor->name(), what.type(), (int) what.channel(), what.id() )
2209 abort(); /*NOTREACHED*/
2217 boost::shared_ptr<AutomationControl> control
2218 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2220 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2221 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2222 _editor, *this, false, parent_canvas,
2223 processor->describe_parameter (what), processor->name()));
2225 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2227 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2230 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2235 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2238 pan->menu_item->set_active (false);
2247 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2249 boost::shared_ptr<Processor> processor (p.lock ());
2251 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2252 /* The Amp processor is a special case and is dealt with separately */
2256 set<Evoral::Parameter> existing;
2258 processor->what_has_data (existing);
2260 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2262 Evoral::Parameter param (*i);
2263 boost::shared_ptr<AutomationLine> al;
2265 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2268 add_processor_automation_curve (processor, param);
2274 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2276 using namespace Menu_Helpers;
2280 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2282 _automation_tracks[param] = track;
2284 /* existing state overrides "show" argument */
2285 string s = track->gui_property ("visible");
2287 show = string_is_affirmative (s);
2290 /* this might or might not change the visibility status, so don't rely on it */
2291 track->set_marked_for_display (show);
2293 if (show && !no_redraw) {
2297 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2298 /* MIDI-related parameters are always in the menu, there's no
2299 reason to rebuild the menu just because we added a automation
2300 lane for one of them. But if we add a non-MIDI automation
2301 lane, then we need to invalidate the display menu.
2303 delete display_menu;
2309 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2311 boost::shared_ptr<Processor> processor (p.lock ());
2313 if (!processor || !processor->display_to_user ()) {
2317 /* we use this override to veto the Amp processor from the plugin menu,
2318 as its automation lane can be accessed using the special "Fader" menu
2322 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2326 using namespace Menu_Helpers;
2327 ProcessorAutomationInfo *rai;
2328 list<ProcessorAutomationInfo*>::iterator x;
2330 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2332 if (automatable.empty()) {
2336 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2337 if ((*x)->processor == processor) {
2342 if (x == processor_automation.end()) {
2343 rai = new ProcessorAutomationInfo (processor);
2344 processor_automation.push_back (rai);
2349 /* any older menu was deleted at the top of processors_changed()
2350 when we cleared the subplugin menu.
2353 rai->menu = manage (new Menu);
2354 MenuList& items = rai->menu->items();
2355 rai->menu->set_name ("ArdourContextMenu");
2359 std::set<Evoral::Parameter> has_visible_automation;
2360 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2362 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2364 ProcessorAutomationNode* pan;
2365 Gtk::CheckMenuItem* mitem;
2367 string name = processor->describe_parameter (*i);
2369 if (name == X_("hidden")) {
2373 items.push_back (CheckMenuElem (name));
2374 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2376 _subplugin_menu_map[*i] = mitem;
2378 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2379 mitem->set_active(true);
2382 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2386 pan = new ProcessorAutomationNode (*i, mitem, *this);
2388 rai->lines.push_back (pan);
2392 pan->menu_item = mitem;
2396 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2399 if (items.size() == 0) {
2403 /* add the menu for this processor, because the subplugin
2404 menu is always cleared at the top of processors_changed().
2405 this is the result of some poor design in gtkmm and/or
2409 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2414 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2415 RouteTimeAxisView::ProcessorAutomationNode* pan)
2417 bool showit = pan->menu_item->get_active();
2418 bool redraw = false;
2420 if (pan->view == 0 && showit) {
2421 add_processor_automation_curve (rai->processor, pan->what);
2425 if (pan->view && pan->view->set_marked_for_display (showit)) {
2429 if (redraw && !no_redraw) {
2435 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2437 if (c.type == RouteProcessorChange::MeterPointChange) {
2438 /* nothing to do if only the meter point has changed */
2442 using namespace Menu_Helpers;
2444 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2445 (*i)->valid = false;
2448 setup_processor_menu_and_curves ();
2450 bool deleted_processor_automation = false;
2452 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2454 list<ProcessorAutomationInfo*>::iterator tmp;
2462 processor_automation.erase (i);
2463 deleted_processor_automation = true;
2470 if (deleted_processor_automation && !no_redraw) {
2475 boost::shared_ptr<AutomationLine>
2476 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2478 ProcessorAutomationNode* pan;
2480 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2486 return boost::shared_ptr<AutomationLine>();
2490 RouteTimeAxisView::reset_processor_automation_curves ()
2492 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2498 RouteTimeAxisView::can_edit_name () const
2500 /* we do not allow track name changes if it is record enabled
2502 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2506 return !trk->rec_enable_control()->get_value();
2510 RouteTimeAxisView::blink_rec_display (bool onoff)
2512 RouteUI::blink_rec_display (onoff);
2516 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2518 if (_ignore_set_layer_display) {
2522 if (apply_to_selection) {
2523 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2527 _view->set_layer_display (d);
2530 set_gui_property (X_("layer-display"), enum_2_string (d));
2535 RouteTimeAxisView::layer_display () const
2538 return _view->layer_display ();
2541 /* we don't know, since we don't have a _view, so just return something */
2547 boost::shared_ptr<AutomationTimeAxisView>
2548 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2550 AutomationTracks::iterator i = _automation_tracks.find(param);
2551 if (i != _automation_tracks.end()) {
2554 return boost::shared_ptr<AutomationTimeAxisView>();
2559 RouteTimeAxisView::fast_update ()
2561 gm.get_level_meter().update_meters ();
2565 RouteTimeAxisView::hide_meter ()
2568 gm.get_level_meter().hide_meters ();
2572 RouteTimeAxisView::show_meter ()
2578 RouteTimeAxisView::reset_meter ()
2580 if (UIConfiguration::instance().get_show_track_meters()) {
2581 int meter_width = 3;
2582 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2585 gm.get_level_meter().setup_meters (height - 9, meter_width);
2592 RouteTimeAxisView::clear_meter ()
2594 gm.get_level_meter().clear_meters ();
2598 RouteTimeAxisView::meter_changed ()
2600 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2602 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2605 // reset peak when meter point changes
2606 gm.reset_peak_display();
2610 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2613 if (_route && !no_redraw) {
2619 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2621 using namespace Menu_Helpers;
2623 if (!_underlay_streams.empty()) {
2624 MenuList& parent_items = parent_menu->items();
2625 Menu* gs_menu = manage (new Menu);
2626 gs_menu->set_name ("ArdourContextMenu");
2627 MenuList& gs_items = gs_menu->items();
2629 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2631 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2632 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2633 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2639 RouteTimeAxisView::set_underlay_state()
2641 if (!underlay_xml_node) {
2645 XMLNodeList nlist = underlay_xml_node->children();
2646 XMLNodeConstIterator niter;
2647 XMLNode *child_node;
2649 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2650 child_node = *niter;
2652 if (child_node->name() != "Underlay") {
2656 XMLProperty const * prop = child_node->property ("id");
2658 PBD::ID id (prop->value());
2660 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2663 add_underlay(v->view(), false);
2672 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2678 RouteTimeAxisView& other = v->trackview();
2680 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2681 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2682 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2683 abort(); /*NOTREACHED*/
2686 _underlay_streams.push_back(v);
2687 other._underlay_mirrors.push_back(this);
2689 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2691 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2693 if (!underlay_xml_node) {
2694 underlay_xml_node = xml_node->add_child("Underlays");
2697 XMLNode* node = underlay_xml_node->add_child("Underlay");
2698 XMLProperty const * prop = node->add_property("id");
2699 prop->set_value(v->trackview().route()->id().to_s());
2706 RouteTimeAxisView::remove_underlay (StreamView* v)
2712 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2713 RouteTimeAxisView& other = v->trackview();
2715 if (it != _underlay_streams.end()) {
2716 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2718 if (gm == other._underlay_mirrors.end()) {
2719 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2720 abort(); /*NOTREACHED*/
2723 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2725 _underlay_streams.erase(it);
2726 other._underlay_mirrors.erase(gm);
2728 if (underlay_xml_node) {
2729 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2735 RouteTimeAxisView::set_button_names ()
2737 if (_route && _route->solo_safe_control()->solo_safe()) {
2738 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2740 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2742 if (Config->get_solo_control_is_listen_control()) {
2743 switch (Config->get_listen_position()) {
2744 case AfterFaderListen:
2745 solo_button->set_text (S_("AfterFader|A"));
2746 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2748 case PreFaderListen:
2749 solo_button->set_text (S_("PreFader|P"));
2750 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2754 solo_button->set_text (S_("Solo|S"));
2755 set_tooltip (*solo_button, _("Solo"));
2757 mute_button->set_text (S_("Mute|M"));
2761 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2763 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2764 if (i != _main_automation_menu_map.end()) {
2768 i = _subplugin_menu_map.find (param);
2769 if (i != _subplugin_menu_map.end()) {
2777 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2779 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2781 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2785 gain_track.reset (new AutomationTimeAxisView (_session,
2786 _route, _route->amp(), c, param,
2791 _route->amp()->describe_parameter(param)));
2794 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2797 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2801 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2803 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2804 if (!c || ! _route->trim()->active()) {
2808 trim_track.reset (new AutomationTimeAxisView (_session,
2809 _route, _route->trim(), c, param,
2814 _route->trim()->describe_parameter(param)));
2817 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2820 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2824 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2826 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2828 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2832 mute_track.reset (new AutomationTimeAxisView (_session,
2833 _route, _route, c, param,
2838 _route->describe_parameter(param)));
2841 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2844 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2848 void add_region_to_list (RegionView* rv, RegionList* l)
2850 l->push_back (rv->region());
2854 RouteTimeAxisView::combine_regions ()
2856 /* as of may 2011, we do not offer uncombine for MIDI tracks
2859 if (!is_audio_track()) {
2867 RegionList selected_regions;
2868 boost::shared_ptr<Playlist> playlist = track()->playlist();
2870 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2872 if (selected_regions.size() < 2) {
2876 playlist->clear_changes ();
2877 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2879 _session->add_command (new StatefulDiffCommand (playlist));
2880 /* make the new region be selected */
2882 return _view->find_view (compound_region);
2886 RouteTimeAxisView::uncombine_regions ()
2888 /* as of may 2011, we do not offer uncombine for MIDI tracks
2890 if (!is_audio_track()) {
2898 RegionList selected_regions;
2899 boost::shared_ptr<Playlist> playlist = track()->playlist();
2901 /* have to grab selected regions first because the uncombine is going
2902 * to change that in the middle of the list traverse
2905 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2907 playlist->clear_changes ();
2909 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2910 playlist->uncombine (*i);
2913 _session->add_command (new StatefulDiffCommand (playlist));
2917 RouteTimeAxisView::state_id() const
2919 return string_compose ("rtav %1", _route->id().to_s());
2924 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2926 TimeAxisView::remove_child (c);
2928 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2930 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2931 if (i->second == a) {
2932 _automation_tracks.erase (i);
2940 RouteTimeAxisView::color () const
2942 return route_color ();
2946 RouteTimeAxisView::marked_for_display () const
2948 return !_route->presentation_info().hidden();
2952 RouteTimeAxisView::set_marked_for_display (bool yn)
2954 return RouteUI::mark_hidden (!yn);