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_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
182 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
183 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
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 route_group_menu->menu()->popup (ev->button, ev->time);
402 RouteTimeAxisView::playlist_changed ()
408 RouteTimeAxisView::label_view ()
410 string x = _route->name ();
411 if (x != name_label.get_text ()) {
412 name_label.set_text (x);
414 const int64_t track_number = _route->track_number ();
415 if (track_number == 0) {
416 number_label.set_text ("");
418 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
423 RouteTimeAxisView::update_track_number_visibility ()
426 bool show_label = _session->config.get_track_name_number();
428 if (_route && _route->is_master()) {
432 if (number_label.get_parent()) {
433 controls_table.remove (number_label);
436 if (ARDOUR::Profile->get_mixbus()) {
437 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
439 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
441 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
442 // except the width of the number label is subtracted from the name-hbox, so we
443 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
444 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
446 number_label.set_size_request(tnw, -1);
447 number_label.show ();
449 number_label.hide ();
454 RouteTimeAxisView::parameter_changed (string const & p)
456 if (p == "track-name-number") {
457 update_track_number_visibility();
458 } else if (p == "editor-stereo-only-meters") {
459 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
460 gm.get_level_meter().set_max_audio_meter_count (2);
462 gm.get_level_meter().set_max_audio_meter_count (0);
468 RouteTimeAxisView::take_name_changed (void *src)
476 RouteTimeAxisView::playlist_click ()
478 build_playlist_menu ();
479 conditionally_add_to_selection ();
480 playlist_action_menu->popup (1, gtk_get_current_event_time());
484 RouteTimeAxisView::automation_click ()
486 conditionally_add_to_selection ();
487 build_automation_action_menu (false);
488 automation_action_menu->popup (1, gtk_get_current_event_time());
492 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
494 using namespace Menu_Helpers;
496 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
497 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
500 detach_menu (subplugin_menu);
502 _main_automation_menu_map.clear ();
503 delete automation_action_menu;
504 automation_action_menu = new Menu;
506 MenuList& items = automation_action_menu->items();
508 automation_action_menu->set_name ("ArdourContextMenu");
510 items.push_back (MenuElem (_("Show All Automation"),
511 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
513 items.push_back (MenuElem (_("Show Existing Automation"),
514 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
516 items.push_back (MenuElem (_("Hide All Automation"),
517 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
519 /* Attach the plugin submenu. It may have previously been used elsewhere,
520 so it was detached above
523 if (!subplugin_menu.items().empty()) {
524 items.push_back (SeparatorElem ());
525 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
526 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
529 /* Add any route automation */
532 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
533 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
534 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
535 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
537 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
541 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
542 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
543 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
544 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
546 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
550 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
551 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
552 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
553 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
555 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
558 if (!pan_tracks.empty()) {
559 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
560 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
562 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
564 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
565 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
566 _main_automation_menu_map[*p] = pan_automation_item;
572 RouteTimeAxisView::build_display_menu ()
574 using namespace Menu_Helpers;
578 TimeAxisView::build_display_menu ();
580 /* now fill it with our stuff */
582 MenuList& items = display_menu->items();
583 display_menu->set_name ("ArdourContextMenu");
585 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
587 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
589 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
591 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
593 items.push_back (SeparatorElem());
596 detach_menu (*_size_menu);
599 items.push_back (MenuElem (_("Height"), *_size_menu));
600 items.push_back (SeparatorElem());
602 // Hook for derived classes to add type specific stuff
603 append_extra_display_menu_items ();
607 Menu* layers_menu = manage (new Menu);
608 MenuList &layers_items = layers_menu->items();
609 layers_menu->set_name("ArdourContextMenu");
611 RadioMenuItem::Group layers_group;
613 /* Find out how many overlaid/stacked tracks we have in the selection */
617 TrackSelection const & s = _editor.get_selection().tracks;
618 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
619 StreamView* v = (*i)->view ();
624 switch (v->layer_display ()) {
635 /* We're not connecting to signal_toggled() here; in the case where these two items are
636 set to be in the `inconsistent' state, it seems that one or other will end up active
637 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
638 select the active one, no toggled signal is emitted so nothing happens.
641 _ignore_set_layer_display = true;
643 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
644 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
645 i->set_active (overlaid != 0 && stacked == 0);
646 i->set_inconsistent (overlaid != 0 && stacked != 0);
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
649 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
650 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
651 i->set_active (overlaid == 0 && stacked != 0);
652 i->set_inconsistent (overlaid != 0 && stacked != 0);
653 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
655 _ignore_set_layer_display = false;
657 items.push_back (MenuElem (_("Layers"), *layers_menu));
659 Menu* alignment_menu = manage (new Menu);
660 MenuList& alignment_items = alignment_menu->items();
661 alignment_menu->set_name ("ArdourContextMenu");
663 RadioMenuItem::Group align_group;
665 /* Same verbose hacks as for the layering options above */
671 boost::shared_ptr<Track> first_track;
673 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
674 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
675 if (!r || !r->is_track ()) {
680 first_track = r->track();
683 switch (r->track()->alignment_choice()) {
687 switch (r->track()->alignment_style()) {
688 case ExistingMaterial:
696 case UseExistingMaterial:
712 inconsistent = false;
719 if (!inconsistent && first_track) {
721 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
722 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
723 i->set_active (automatic != 0 && existing == 0 && capture == 0);
724 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
726 switch (first_track->alignment_choice()) {
728 switch (first_track->alignment_style()) {
729 case ExistingMaterial:
730 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
733 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
741 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
742 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
743 i->set_active (existing != 0 && capture == 0 && automatic == 0);
744 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
746 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
747 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
748 i->set_active (existing == 0 && capture != 0 && automatic == 0);
749 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
751 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
757 Menu* mode_menu = manage (new Menu);
758 MenuList& mode_items = mode_menu->items ();
759 mode_menu->set_name ("ArdourContextMenu");
761 RadioMenuItem::Group mode_group;
767 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
768 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
769 if (!r || !r->is_track ()) {
773 switch (r->track()->mode()) {
786 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
787 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
789 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
790 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
792 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
793 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
794 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
795 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
796 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
798 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
799 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
800 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
801 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
802 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
804 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
806 items.push_back (SeparatorElem());
808 build_playlist_menu ();
809 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
810 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
813 route_group_menu->detach ();
816 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
817 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
819 r.push_back (rtv->route ());
824 r.push_back (route ());
827 route_group_menu->build (r);
828 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
830 build_automation_action_menu (true);
831 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
833 items.push_back (SeparatorElem());
837 TrackSelection const & s = _editor.get_selection().tracks;
838 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
839 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
844 if (r->route()->active()) {
851 items.push_back (CheckMenuElem (_("Active")));
852 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
853 bool click_sets_active = true;
854 if (active > 0 && inactive == 0) {
855 i->set_active (true);
856 click_sets_active = false;
857 } else if (active > 0 && inactive > 0) {
858 i->set_inconsistent (true);
860 i->set_sensitive(! _session->transport_rolling());
861 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
863 items.push_back (SeparatorElem());
864 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
865 if (_route && !_route->is_master()) {
866 items.push_back (SeparatorElem());
867 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
869 items.push_back (SeparatorElem());
870 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
874 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
876 if (apply_to_selection) {
877 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
880 bool needs_bounce = false;
882 if (!track()->can_use_mode (mode, needs_bounce)) {
888 cerr << "would bounce this one\n";
893 track()->set_mode (mode);
898 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
900 TimeAxisView::show_timestretch (start, end, layers, layer);
910 /* check that the time selection was made in our route, or our route group.
911 remember that route_group() == 0 implies the route is *not* in a edit group.
914 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
915 /* this doesn't apply to us */
919 /* ignore it if our edit group is not active */
921 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
926 if (timestretch_rect == 0) {
927 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
928 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
929 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
932 timestretch_rect->show ();
933 timestretch_rect->raise_to_top ();
935 double const x1 = start / _editor.get_current_zoom();
936 double const x2 = (end - 1) / _editor.get_current_zoom();
938 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
939 x2, current_height() * (layers - layer) / layers));
943 RouteTimeAxisView::hide_timestretch ()
945 TimeAxisView::hide_timestretch ();
947 if (timestretch_rect) {
948 timestretch_rect->hide ();
953 RouteTimeAxisView::show_selection (TimeSelection& ts)
957 /* ignore it if our edit group is not active or if the selection was started
958 in some other track or route group (remember that route_group() == 0 means
959 that the track is not in an route group).
962 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
963 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
969 TimeAxisView::show_selection (ts);
973 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
976 bool height_changed = (height == 0) || (h != height);
979 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
982 gm.get_level_meter().setup_meters (gmlen, meter_width);
984 TimeAxisView::set_height (h, m);
987 _view->set_height ((double) current_height());
990 if (height >= preset_height (HeightNormal)) {
994 gm.get_gain_slider().show();
996 if (!_route || _route->is_monitor()) {
1001 if (rec_enable_button)
1002 rec_enable_button->show();
1004 route_group_button.show();
1005 automation_button.show();
1007 if (is_track() && track()->mode() == ARDOUR::Normal) {
1008 playlist_button.show();
1015 gm.get_gain_slider().hide();
1016 mute_button->show();
1017 if (!_route || _route->is_monitor()) {
1018 solo_button->hide();
1020 solo_button->show();
1022 if (rec_enable_button)
1023 rec_enable_button->show();
1025 route_group_button.hide ();
1026 automation_button.hide ();
1028 if (is_track() && track()->mode() == ARDOUR::Normal) {
1029 playlist_button.hide ();
1034 if (height_changed && !no_redraw) {
1035 /* only emit the signal if the height really changed */
1041 RouteTimeAxisView::route_color_changed ()
1044 _view->apply_color (color(), StreamView::RegionColor);
1047 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1051 RouteTimeAxisView::reset_samples_per_pixel ()
1053 set_samples_per_pixel (_editor.get_current_zoom());
1057 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1062 speed = track()->speed();
1066 _view->set_samples_per_pixel (fpp * speed);
1069 TimeAxisView::set_samples_per_pixel (fpp * speed);
1073 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1075 if (!mitem->get_active()) {
1076 /* this is one of the two calls made when these radio menu items change status. this one
1077 is for the item that became inactive, and we want to ignore it.
1082 if (apply_to_selection) {
1083 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1086 track()->set_align_choice (choice);
1092 RouteTimeAxisView::rename_current_playlist ()
1094 ArdourPrompter prompter (true);
1097 boost::shared_ptr<Track> tr = track();
1098 if (!tr || tr->destructive()) {
1102 boost::shared_ptr<Playlist> pl = tr->playlist();
1107 prompter.set_title (_("Rename Playlist"));
1108 prompter.set_prompt (_("New name for playlist:"));
1109 prompter.set_initial_text (pl->name());
1110 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1111 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1113 switch (prompter.run ()) {
1114 case Gtk::RESPONSE_ACCEPT:
1115 prompter.get_result (name);
1116 if (name.length()) {
1117 pl->set_name (name);
1127 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1129 std::string ret (basename);
1131 std::string const group_string = "." + route_group()->name() + ".";
1133 // iterate through all playlists
1135 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1136 std::string tmp = (*i)->name();
1138 std::string::size_type idx = tmp.find(group_string);
1139 // find those which belong to this group
1140 if (idx != string::npos) {
1141 tmp = tmp.substr(idx + group_string.length());
1143 // and find the largest current number
1145 if (x > maxnumber) {
1154 snprintf (buf, sizeof(buf), "%d", maxnumber);
1156 ret = this->name() + "." + route_group()->name () + "." + buf;
1162 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1166 boost::shared_ptr<Track> tr = track ();
1167 if (!tr || tr->destructive()) {
1171 boost::shared_ptr<const Playlist> pl = tr->playlist();
1178 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1179 name = resolve_new_group_playlist_name(name, playlists_before_op);
1182 while (_session->playlists->by_name(name)) {
1183 name = Playlist::bump_name (name, *_session);
1186 // TODO: The prompter "new" button should be de-activated if the user
1187 // specifies a playlist name which already exists in the session.
1191 ArdourPrompter prompter (true);
1193 prompter.set_title (_("New Copy Playlist"));
1194 prompter.set_prompt (_("Name for new playlist:"));
1195 prompter.set_initial_text (name);
1196 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1197 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1198 prompter.show_all ();
1200 switch (prompter.run ()) {
1201 case Gtk::RESPONSE_ACCEPT:
1202 prompter.get_result (name);
1210 if (name.length()) {
1211 tr->use_copy_playlist ();
1212 tr->playlist()->set_name (name);
1217 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1221 boost::shared_ptr<Track> tr = track ();
1222 if (!tr || tr->destructive()) {
1226 boost::shared_ptr<const Playlist> pl = tr->playlist();
1233 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1234 name = resolve_new_group_playlist_name(name,playlists_before_op);
1237 while (_session->playlists->by_name(name)) {
1238 name = Playlist::bump_name (name, *_session);
1244 ArdourPrompter prompter (true);
1246 prompter.set_title (_("New Playlist"));
1247 prompter.set_prompt (_("Name for new playlist:"));
1248 prompter.set_initial_text (name);
1249 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1250 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1252 switch (prompter.run ()) {
1253 case Gtk::RESPONSE_ACCEPT:
1254 prompter.get_result (name);
1262 if (name.length()) {
1263 tr->use_new_playlist ();
1264 tr->playlist()->set_name (name);
1269 RouteTimeAxisView::clear_playlist ()
1271 boost::shared_ptr<Track> tr = track ();
1272 if (!tr || tr->destructive()) {
1276 boost::shared_ptr<Playlist> pl = tr->playlist();
1281 _editor.clear_playlist (pl);
1285 RouteTimeAxisView::speed_changed ()
1287 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1291 RouteTimeAxisView::update_diskstream_display ()
1301 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1303 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1305 /* special case: select/deselect all tracks */
1307 _editor.begin_reversible_selection_op (X_("Selection Click"));
1309 if (_editor.get_selection().selected (this)) {
1310 _editor.get_selection().clear_tracks ();
1312 _editor.select_all_tracks ();
1315 _editor.commit_reversible_selection_op ();
1320 _editor.begin_reversible_selection_op (X_("Selection Click"));
1322 switch (ArdourKeyboard::selection_type (ev->state)) {
1323 case Selection::Toggle:
1324 _editor.get_selection().toggle (this);
1327 case Selection::Set:
1328 _editor.get_selection().set (this);
1331 case Selection::Extend:
1332 _editor.extend_selection_to_track (*this);
1335 case Selection::Add:
1336 _editor.get_selection().add (this);
1340 _editor.commit_reversible_selection_op ();
1344 RouteTimeAxisView::set_selected_points (PointSelection& points)
1346 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1347 (*i)->set_selected_points (points);
1349 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1351 asv->set_selected_points (points);
1356 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1359 _view->set_selected_regionviews (regions);
1363 /** Add the selectable things that we have to a list.
1364 * @param results List to add things to.
1367 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1372 speed = track()->speed();
1375 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1376 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1378 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1379 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1382 /* pick up visible automation tracks */
1384 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1385 if (!(*i)->hidden()) {
1386 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1392 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1395 _view->get_inverted_selectables (sel, results);
1398 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1399 if (!(*i)->hidden()) {
1400 (*i)->get_inverted_selectables (sel, results);
1408 RouteTimeAxisView::route_group () const
1410 return _route->route_group();
1414 RouteTimeAxisView::name() const
1416 return _route->name();
1419 boost::shared_ptr<Playlist>
1420 RouteTimeAxisView::playlist () const
1422 boost::shared_ptr<Track> tr;
1424 if ((tr = track()) != 0) {
1425 return tr->playlist();
1427 return boost::shared_ptr<Playlist> ();
1432 RouteTimeAxisView::name_entry_changed (string const& str)
1434 if (str == _route->name()) {
1440 strip_whitespace_edges (x);
1446 if (_session->route_name_internal (x)) {
1447 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1449 } else if (RouteUI::verify_new_route_name (x)) {
1450 _route->set_name (x);
1457 boost::shared_ptr<Region>
1458 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1460 boost::shared_ptr<Playlist> pl = playlist ();
1463 return pl->find_next_region (pos, point, dir);
1466 return boost::shared_ptr<Region> ();
1470 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1472 boost::shared_ptr<Playlist> pl = playlist ();
1475 return pl->find_next_region_boundary (pos, dir);
1482 RouteTimeAxisView::fade_range (TimeSelection& selection)
1484 boost::shared_ptr<Playlist> what_we_got;
1485 boost::shared_ptr<Track> tr = track ();
1486 boost::shared_ptr<Playlist> playlist;
1489 /* route is a bus, not a track */
1493 playlist = tr->playlist();
1495 TimeSelection time (selection);
1496 float const speed = tr->speed();
1497 if (speed != 1.0f) {
1498 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1499 (*i).start = session_frame_to_track_frame((*i).start, speed);
1500 (*i).end = session_frame_to_track_frame((*i).end, speed);
1504 playlist->clear_changes ();
1505 playlist->clear_owned_changes ();
1507 playlist->fade_range (time);
1509 vector<Command*> cmds;
1510 playlist->rdiff (cmds);
1511 _session->add_commands (cmds);
1512 _session->add_command (new StatefulDiffCommand (playlist));
1517 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1519 boost::shared_ptr<Playlist> what_we_got;
1520 boost::shared_ptr<Track> tr = track ();
1521 boost::shared_ptr<Playlist> playlist;
1524 /* route is a bus, not a track */
1528 playlist = tr->playlist();
1530 TimeSelection time (selection.time);
1531 float const speed = tr->speed();
1532 if (speed != 1.0f) {
1533 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1534 (*i).start = session_frame_to_track_frame((*i).start, speed);
1535 (*i).end = session_frame_to_track_frame((*i).end, speed);
1539 playlist->clear_changes ();
1540 playlist->clear_owned_changes ();
1544 if (playlist->cut (time) != 0) {
1545 if (Config->get_edit_mode() == Ripple)
1546 playlist->ripple(time.start(), -time.length(), NULL);
1547 // no need to exclude any regions from rippling here
1549 vector<Command*> cmds;
1550 playlist->rdiff (cmds);
1551 _session->add_commands (cmds);
1553 _session->add_command (new StatefulDiffCommand (playlist));
1558 if ((what_we_got = playlist->cut (time)) != 0) {
1559 _editor.get_cut_buffer().add (what_we_got);
1560 if (Config->get_edit_mode() == Ripple)
1561 playlist->ripple(time.start(), -time.length(), NULL);
1562 // no need to exclude any regions from rippling here
1564 vector<Command*> cmds;
1565 playlist->rdiff (cmds);
1566 _session->add_commands (cmds);
1568 _session->add_command (new StatefulDiffCommand (playlist));
1572 if ((what_we_got = playlist->copy (time)) != 0) {
1573 _editor.get_cut_buffer().add (what_we_got);
1578 if ((what_we_got = playlist->cut (time)) != 0) {
1579 if (Config->get_edit_mode() == Ripple)
1580 playlist->ripple(time.start(), -time.length(), NULL);
1581 // no need to exclude any regions from rippling here
1583 vector<Command*> cmds;
1584 playlist->rdiff (cmds);
1585 _session->add_commands (cmds);
1586 _session->add_command (new StatefulDiffCommand (playlist));
1587 what_we_got->release ();
1594 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1600 boost::shared_ptr<Playlist> pl = playlist ();
1601 const ARDOUR::DataType type = pl->data_type();
1602 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1604 if (p == selection.playlists.end()) {
1607 ctx.counts.increase_n_playlists(type);
1609 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1611 if (track()->speed() != 1.0f) {
1612 pos = session_frame_to_track_frame (pos, track()->speed());
1613 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1616 /* add multi-paste offset if applicable */
1617 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1618 const framecnt_t duration = extent.second - extent.first;
1619 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1621 pl->clear_changes ();
1622 pl->clear_owned_changes ();
1623 if (Config->get_edit_mode() == Ripple) {
1624 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1625 framecnt_t amount = extent.second - extent.first;
1626 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1628 pl->paste (*p, pos, ctx.times);
1630 vector<Command*> cmds;
1632 _session->add_commands (cmds);
1634 _session->add_command (new StatefulDiffCommand (pl));
1640 struct PlaylistSorter {
1641 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1642 return a->sort_id() < b->sort_id();
1647 RouteTimeAxisView::build_playlist_menu ()
1649 using namespace Menu_Helpers;
1655 delete playlist_action_menu;
1656 playlist_action_menu = new Menu;
1657 playlist_action_menu->set_name ("ArdourContextMenu");
1659 MenuList& playlist_items = playlist_action_menu->items();
1660 playlist_action_menu->set_name ("ArdourContextMenu");
1661 playlist_items.clear();
1663 RadioMenuItem::Group playlist_group;
1664 boost::shared_ptr<Track> tr = track ();
1666 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1668 /* sort the playlists */
1670 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1672 /* add the playlists to the menu */
1673 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1674 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1675 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1676 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1678 if (tr->playlist()->id() == (*i)->id()) {
1684 playlist_items.push_back (SeparatorElem());
1685 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1686 playlist_items.push_back (SeparatorElem());
1688 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1689 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1690 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1693 // Use a label which tells the user what is happening
1694 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1695 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1699 playlist_items.push_back (SeparatorElem());
1700 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1701 playlist_items.push_back (SeparatorElem());
1703 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1707 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1709 assert (is_track());
1711 // exit if we were triggered by deactivating the old playlist
1712 if (!item->get_active()) {
1716 boost::shared_ptr<Playlist> pl (wpl.lock());
1722 if (track()->playlist() == pl) {
1723 // exit when use_playlist is called by the creation of the playlist menu
1724 // or the playlist choice is unchanged
1728 track()->use_playlist (pl);
1730 RouteGroup* rg = route_group();
1732 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1733 std::string group_string = "." + rg->name() + ".";
1735 std::string take_name = pl->name();
1736 std::string::size_type idx = take_name.find(group_string);
1738 if (idx == std::string::npos)
1741 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1743 boost::shared_ptr<RouteList> rl (rg->route_list());
1745 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1746 if ((*i) == this->route()) {
1750 std::string playlist_name = (*i)->name()+group_string+take_name;
1752 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1757 if (track->freeze_state() == Track::Frozen) {
1758 /* Don't change playlists of frozen tracks */
1762 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1764 // No playlist for this track for this take yet, make it
1765 track->use_new_playlist();
1766 track->playlist()->set_name(playlist_name);
1768 track->use_playlist(ipl);
1775 RouteTimeAxisView::update_playlist_tip ()
1777 RouteGroup* rg = route_group ();
1778 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1779 string group_string = "." + rg->name() + ".";
1781 string take_name = track()->playlist()->name();
1782 string::size_type idx = take_name.find(group_string);
1784 if (idx != string::npos) {
1785 /* find the bit containing the take number / name */
1786 take_name = take_name.substr (idx + group_string.length());
1788 /* set the playlist button tooltip to the take name */
1791 string_compose(_("Take: %1.%2"),
1792 Gtkmm2ext::markup_escape_text (rg->name()),
1793 Gtkmm2ext::markup_escape_text (take_name))
1800 /* set the playlist button tooltip to the playlist name */
1801 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1806 RouteTimeAxisView::show_playlist_selector ()
1808 _editor.playlist_selector().show_for (this);
1812 RouteTimeAxisView::map_frozen ()
1818 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1820 switch (track()->freeze_state()) {
1822 playlist_button.set_sensitive (false);
1825 playlist_button.set_sensitive (true);
1828 RouteUI::map_frozen ();
1832 RouteTimeAxisView::color_handler ()
1834 //case cTimeStretchOutline:
1835 if (timestretch_rect) {
1836 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1838 //case cTimeStretchFill:
1839 if (timestretch_rect) {
1840 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1846 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1847 * Will add track if necessary.
1850 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1852 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1853 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1856 /* it doesn't exist yet, so we don't care about the button state: just add it */
1857 create_automation_child (param, true);
1860 bool yn = menu->get_active();
1861 bool changed = false;
1863 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1865 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1866 will have done that for us.
1869 if (changed && !no_redraw) {
1877 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1879 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1885 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1887 if (menu && !_hidden) {
1888 ignore_toggle = true;
1889 menu->set_active (false);
1890 ignore_toggle = false;
1893 if (_route && !no_redraw) {
1899 RouteTimeAxisView::update_gain_track_visibility ()
1901 bool const showit = gain_automation_item->get_active();
1903 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1904 gain_track->set_marked_for_display (showit);
1906 /* now trigger a redisplay */
1909 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1915 RouteTimeAxisView::update_trim_track_visibility ()
1917 bool const showit = trim_automation_item->get_active();
1919 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1920 trim_track->set_marked_for_display (showit);
1922 /* now trigger a redisplay */
1925 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1931 RouteTimeAxisView::update_mute_track_visibility ()
1933 bool const showit = mute_automation_item->get_active();
1935 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1936 mute_track->set_marked_for_display (showit);
1938 /* now trigger a redisplay */
1941 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1947 RouteTimeAxisView::update_pan_track_visibility ()
1949 bool const showit = pan_automation_item->get_active();
1950 bool changed = false;
1952 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1953 if ((*i)->set_marked_for_display (showit)) {
1959 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1964 RouteTimeAxisView::ensure_pan_views (bool show)
1966 bool changed = false;
1967 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1969 (*i)->set_marked_for_display (false);
1972 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1976 if (!_route->panner()) {
1980 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1981 set<Evoral::Parameter>::iterator p;
1983 for (p = params.begin(); p != params.end(); ++p) {
1984 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1986 if (pan_control->parameter().type() == NullAutomation) {
1987 error << "Pan control has NULL automation type!" << endmsg;
1991 if (automation_child (pan_control->parameter ()).get () == 0) {
1993 /* we don't already have an AutomationTimeAxisView for this parameter */
1995 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1997 boost::shared_ptr<AutomationTimeAxisView> t (
1998 new AutomationTimeAxisView (_session,
2002 pan_control->parameter (),
2010 pan_tracks.push_back (t);
2011 add_automation_child (*p, t, show);
2013 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2020 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2022 if (apply_to_selection) {
2023 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2027 /* Show our automation */
2029 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2030 i->second->set_marked_for_display (true);
2032 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2035 menu->set_active(true);
2040 /* Show processor automation */
2042 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2043 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2044 if ((*ii)->view == 0) {
2045 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2048 (*ii)->menu_item->set_active (true);
2061 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2063 if (apply_to_selection) {
2064 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2068 /* Show our automation */
2070 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2071 if (i->second->has_automation()) {
2072 i->second->set_marked_for_display (true);
2074 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2076 menu->set_active(true);
2081 /* Show processor automation */
2083 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2084 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2085 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2086 (*ii)->menu_item->set_active (true);
2098 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2100 if (apply_to_selection) {
2101 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2105 /* Hide our automation */
2107 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2108 i->second->set_marked_for_display (false);
2110 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2113 menu->set_active (false);
2117 /* Hide processor automation */
2119 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2120 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2121 (*ii)->menu_item->set_active (false);
2132 RouteTimeAxisView::region_view_added (RegionView* rv)
2134 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2135 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2136 boost::shared_ptr<AutomationTimeAxisView> atv;
2138 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2143 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2144 (*i)->add_ghost(rv);
2148 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2150 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2156 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2158 parent.remove_processor_automation_node (this);
2162 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2165 remove_child (pan->view);
2169 RouteTimeAxisView::ProcessorAutomationNode*
2170 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2172 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2174 if ((*i)->processor == processor) {
2176 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2177 if ((*ii)->what == what) {
2187 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2189 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2192 ProcessorAutomationNode* pan;
2194 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2195 /* session state may never have been saved with new plugin */
2196 error << _("programming error: ")
2197 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2198 processor->name(), what.type(), (int) what.channel(), what.id() )
2200 abort(); /*NOTREACHED*/
2208 boost::shared_ptr<AutomationControl> control
2209 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2211 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2212 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2213 _editor, *this, false, parent_canvas,
2214 processor->describe_parameter (what), processor->name()));
2216 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2218 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2221 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2226 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2229 pan->menu_item->set_active (false);
2238 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2240 boost::shared_ptr<Processor> processor (p.lock ());
2242 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2243 /* The Amp processor is a special case and is dealt with separately */
2247 set<Evoral::Parameter> existing;
2249 processor->what_has_data (existing);
2251 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2253 Evoral::Parameter param (*i);
2254 boost::shared_ptr<AutomationLine> al;
2256 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2259 add_processor_automation_curve (processor, param);
2265 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2267 using namespace Menu_Helpers;
2271 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2273 _automation_tracks[param] = track;
2275 /* existing state overrides "show" argument */
2276 string s = track->gui_property ("visible");
2278 show = string_is_affirmative (s);
2281 /* this might or might not change the visibility status, so don't rely on it */
2282 track->set_marked_for_display (show);
2284 if (show && !no_redraw) {
2288 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2289 /* MIDI-related parameters are always in the menu, there's no
2290 reason to rebuild the menu just because we added a automation
2291 lane for one of them. But if we add a non-MIDI automation
2292 lane, then we need to invalidate the display menu.
2294 delete display_menu;
2300 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2302 boost::shared_ptr<Processor> processor (p.lock ());
2304 if (!processor || !processor->display_to_user ()) {
2308 /* we use this override to veto the Amp processor from the plugin menu,
2309 as its automation lane can be accessed using the special "Fader" menu
2313 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2317 using namespace Menu_Helpers;
2318 ProcessorAutomationInfo *rai;
2319 list<ProcessorAutomationInfo*>::iterator x;
2321 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2323 if (automatable.empty()) {
2327 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2328 if ((*x)->processor == processor) {
2333 if (x == processor_automation.end()) {
2334 rai = new ProcessorAutomationInfo (processor);
2335 processor_automation.push_back (rai);
2340 /* any older menu was deleted at the top of processors_changed()
2341 when we cleared the subplugin menu.
2344 rai->menu = manage (new Menu);
2345 MenuList& items = rai->menu->items();
2346 rai->menu->set_name ("ArdourContextMenu");
2350 std::set<Evoral::Parameter> has_visible_automation;
2351 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2353 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2355 ProcessorAutomationNode* pan;
2356 Gtk::CheckMenuItem* mitem;
2358 string name = processor->describe_parameter (*i);
2360 if (name == X_("hidden")) {
2364 items.push_back (CheckMenuElem (name));
2365 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2367 _subplugin_menu_map[*i] = mitem;
2369 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2370 mitem->set_active(true);
2373 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2377 pan = new ProcessorAutomationNode (*i, mitem, *this);
2379 rai->lines.push_back (pan);
2383 pan->menu_item = mitem;
2387 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2390 if (items.size() == 0) {
2394 /* add the menu for this processor, because the subplugin
2395 menu is always cleared at the top of processors_changed().
2396 this is the result of some poor design in gtkmm and/or
2400 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2405 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2406 RouteTimeAxisView::ProcessorAutomationNode* pan)
2408 bool showit = pan->menu_item->get_active();
2409 bool redraw = false;
2411 if (pan->view == 0 && showit) {
2412 add_processor_automation_curve (rai->processor, pan->what);
2416 if (pan->view && pan->view->set_marked_for_display (showit)) {
2420 if (redraw && !no_redraw) {
2426 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2428 if (c.type == RouteProcessorChange::MeterPointChange) {
2429 /* nothing to do if only the meter point has changed */
2433 using namespace Menu_Helpers;
2435 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2436 (*i)->valid = false;
2439 setup_processor_menu_and_curves ();
2441 bool deleted_processor_automation = false;
2443 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2445 list<ProcessorAutomationInfo*>::iterator tmp;
2453 processor_automation.erase (i);
2454 deleted_processor_automation = true;
2461 if (deleted_processor_automation && !no_redraw) {
2466 boost::shared_ptr<AutomationLine>
2467 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2469 ProcessorAutomationNode* pan;
2471 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2477 return boost::shared_ptr<AutomationLine>();
2481 RouteTimeAxisView::reset_processor_automation_curves ()
2483 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2489 RouteTimeAxisView::can_edit_name () const
2491 /* we do not allow track name changes if it is record enabled
2493 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2497 return !trk->rec_enable_control()->get_value();
2501 RouteTimeAxisView::blink_rec_display (bool onoff)
2503 RouteUI::blink_rec_display (onoff);
2507 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2509 if (_ignore_set_layer_display) {
2513 if (apply_to_selection) {
2514 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2518 _view->set_layer_display (d);
2521 set_gui_property (X_("layer-display"), enum_2_string (d));
2526 RouteTimeAxisView::layer_display () const
2529 return _view->layer_display ();
2532 /* we don't know, since we don't have a _view, so just return something */
2538 boost::shared_ptr<AutomationTimeAxisView>
2539 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2541 AutomationTracks::iterator i = _automation_tracks.find(param);
2542 if (i != _automation_tracks.end()) {
2545 return boost::shared_ptr<AutomationTimeAxisView>();
2550 RouteTimeAxisView::fast_update ()
2552 gm.get_level_meter().update_meters ();
2556 RouteTimeAxisView::hide_meter ()
2559 gm.get_level_meter().hide_meters ();
2563 RouteTimeAxisView::show_meter ()
2569 RouteTimeAxisView::reset_meter ()
2571 if (UIConfiguration::instance().get_show_track_meters()) {
2572 int meter_width = 3;
2573 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2576 gm.get_level_meter().setup_meters (height - 9, meter_width);
2583 RouteTimeAxisView::clear_meter ()
2585 gm.get_level_meter().clear_meters ();
2589 RouteTimeAxisView::meter_changed ()
2591 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2593 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2596 // reset peak when meter point changes
2597 gm.reset_peak_display();
2601 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2604 if (_route && !no_redraw) {
2610 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2612 using namespace Menu_Helpers;
2614 if (!_underlay_streams.empty()) {
2615 MenuList& parent_items = parent_menu->items();
2616 Menu* gs_menu = manage (new Menu);
2617 gs_menu->set_name ("ArdourContextMenu");
2618 MenuList& gs_items = gs_menu->items();
2620 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2622 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2623 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2624 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2630 RouteTimeAxisView::set_underlay_state()
2632 if (!underlay_xml_node) {
2636 XMLNodeList nlist = underlay_xml_node->children();
2637 XMLNodeConstIterator niter;
2638 XMLNode *child_node;
2640 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2641 child_node = *niter;
2643 if (child_node->name() != "Underlay") {
2647 XMLProperty const * prop = child_node->property ("id");
2649 PBD::ID id (prop->value());
2651 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2654 add_underlay(v->view(), false);
2663 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2669 RouteTimeAxisView& other = v->trackview();
2671 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2672 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2673 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2674 abort(); /*NOTREACHED*/
2677 _underlay_streams.push_back(v);
2678 other._underlay_mirrors.push_back(this);
2680 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2682 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2684 if (!underlay_xml_node) {
2685 underlay_xml_node = xml_node->add_child("Underlays");
2688 XMLNode* node = underlay_xml_node->add_child("Underlay");
2689 XMLProperty const * prop = node->add_property("id");
2690 prop->set_value(v->trackview().route()->id().to_s());
2697 RouteTimeAxisView::remove_underlay (StreamView* v)
2703 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2704 RouteTimeAxisView& other = v->trackview();
2706 if (it != _underlay_streams.end()) {
2707 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2709 if (gm == other._underlay_mirrors.end()) {
2710 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2711 abort(); /*NOTREACHED*/
2714 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2716 _underlay_streams.erase(it);
2717 other._underlay_mirrors.erase(gm);
2719 if (underlay_xml_node) {
2720 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2726 RouteTimeAxisView::set_button_names ()
2728 if (_route && _route->solo_safe_control()->solo_safe()) {
2729 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2731 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2733 if (Config->get_solo_control_is_listen_control()) {
2734 switch (Config->get_listen_position()) {
2735 case AfterFaderListen:
2736 solo_button->set_text (S_("AfterFader|A"));
2737 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2739 case PreFaderListen:
2740 solo_button->set_text (S_("PreFader|P"));
2741 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2745 solo_button->set_text (S_("Solo|S"));
2746 set_tooltip (*solo_button, _("Solo"));
2748 mute_button->set_text (S_("Mute|M"));
2752 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2754 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2755 if (i != _main_automation_menu_map.end()) {
2759 i = _subplugin_menu_map.find (param);
2760 if (i != _subplugin_menu_map.end()) {
2768 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2770 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2772 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2776 gain_track.reset (new AutomationTimeAxisView (_session,
2777 _route, _route->amp(), c, param,
2782 _route->amp()->describe_parameter(param)));
2785 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2788 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2792 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2794 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2795 if (!c || ! _route->trim()->active()) {
2799 trim_track.reset (new AutomationTimeAxisView (_session,
2800 _route, _route->trim(), c, param,
2805 _route->trim()->describe_parameter(param)));
2808 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2811 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2815 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2817 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2819 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2823 mute_track.reset (new AutomationTimeAxisView (_session,
2824 _route, _route, c, param,
2829 _route->describe_parameter(param)));
2832 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2835 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2839 void add_region_to_list (RegionView* rv, RegionList* l)
2841 l->push_back (rv->region());
2845 RouteTimeAxisView::combine_regions ()
2847 /* as of may 2011, we do not offer uncombine for MIDI tracks
2850 if (!is_audio_track()) {
2858 RegionList selected_regions;
2859 boost::shared_ptr<Playlist> playlist = track()->playlist();
2861 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2863 if (selected_regions.size() < 2) {
2867 playlist->clear_changes ();
2868 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2870 _session->add_command (new StatefulDiffCommand (playlist));
2871 /* make the new region be selected */
2873 return _view->find_view (compound_region);
2877 RouteTimeAxisView::uncombine_regions ()
2879 /* as of may 2011, we do not offer uncombine for MIDI tracks
2881 if (!is_audio_track()) {
2889 RegionList selected_regions;
2890 boost::shared_ptr<Playlist> playlist = track()->playlist();
2892 /* have to grab selected regions first because the uncombine is going
2893 * to change that in the middle of the list traverse
2896 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2898 playlist->clear_changes ();
2900 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2901 playlist->uncombine (*i);
2904 _session->add_command (new StatefulDiffCommand (playlist));
2908 RouteTimeAxisView::state_id() const
2910 return string_compose ("rtav %1", _route->id().to_s());
2915 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2917 TimeAxisView::remove_child (c);
2919 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2921 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2922 if (i->second == a) {
2923 _automation_tracks.erase (i);
2930 PresentationInfo const &
2931 RouteTimeAxisView::presentation_info () const
2933 return _route->presentation_info();
2936 boost::shared_ptr<Stripable>
2937 RouteTimeAxisView::stripable () const
2943 RouteTimeAxisView::color () const
2945 return route_color ();
2949 RouteTimeAxisView::marked_for_display () const
2951 return !_route->presentation_info().hidden();
2955 RouteTimeAxisView::set_marked_for_display (bool yn)
2957 return RouteUI::mark_hidden (!yn);