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"
63 #include "global_signals.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 "route_group_menu.h"
82 #include "ardour/track.h"
86 using namespace ARDOUR;
87 using namespace ARDOUR_UI_UTILS;
89 using namespace Gtkmm2ext;
91 using namespace Editing;
95 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
98 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
100 , parent_canvas (canvas)
102 , button_table (3, 3)
103 , route_group_button (_("G"))
104 , playlist_button (_("P"))
105 , automation_button (_("A"))
106 , automation_action_menu (0)
107 , plugins_submenu_item (0)
108 , route_group_menu (0)
109 , playlist_action_menu (0)
111 , color_mode_menu (0)
112 , gm (sess, true, 75, 14)
113 , _ignore_set_layer_display (false)
114 , gain_automation_item(NULL)
115 , mute_automation_item(NULL)
116 , pan_automation_item(NULL)
118 number_label.set_name("tracknumber label");
119 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
120 number_label.set_alignment(.5, .5);
121 number_label.set_fallthrough_to_parent (true);
123 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
129 RouteUI::set_route (rt);
131 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
132 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
133 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
136 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
139 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
140 gm.get_level_meter().set_no_show_all();
141 gm.get_level_meter().setup_meters(50, meter_width);
142 gm.update_gain_sensitive ();
144 string str = gui_property ("height");
146 set_height (atoi (str));
148 set_height (preset_height (HeightNormal));
151 if (!_route->is_auditioner()) {
152 if (gui_property ("visible").empty()) {
153 set_gui_property ("visible", true);
156 set_gui_property ("visible", false);
160 update_solo_display ();
162 timestretch_rect = 0;
165 ignore_toggle = false;
167 route_group_button.set_name ("route button");
168 playlist_button.set_name ("route button");
169 automation_button.set_name ("route button");
171 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
172 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
173 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
177 if (ARDOUR::Profile->get_mixbus()) {
178 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
180 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
183 if (is_midi_track()) {
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
199 Gtk::Fixed *blank = manage(new Gtk::Fixed());
200 controls_button_size_group->add_widget(*blank);
201 if (ARDOUR::Profile->get_mixbus() ) {
202 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
211 if (!ARDOUR::Profile->get_mixbus()) {
212 controls_meters_size_group->add_widget (gm.get_level_meter());
215 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
216 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
217 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
220 if (ARDOUR::Profile->get_mixbus()) {
221 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 // mute button is always present, it is used to
226 // force the 'blank' placeholders to the proper size
227 controls_button_size_group->add_widget(*mute_button);
229 if (!_route->is_master()) {
230 if (ARDOUR::Profile->get_mixbus()) {
231 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 Gtk::Fixed *blank = manage(new Gtk::Fixed());
237 controls_button_size_group->add_widget(*blank);
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 else if (!ARDOUR::Profile->get_trx()) {
251 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
255 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
256 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
257 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
259 mute_button->set_tweaks(ArdourButton::TrackHeader);
260 solo_button->set_tweaks(ArdourButton::TrackHeader);
261 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
262 playlist_button.set_tweaks(ArdourButton::TrackHeader);
263 automation_button.set_tweaks(ArdourButton::TrackHeader);
264 route_group_button.set_tweaks(ArdourButton::TrackHeader);
266 if (is_midi_track()) {
267 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
269 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
272 update_track_number_visibility();
275 if (ARDOUR::Profile->get_mixbus()) {
276 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 else if (!ARDOUR::Profile->get_trx()) {
279 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282 if (is_track() && track()->mode() == ARDOUR::Normal) {
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
294 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
298 str = gui_property ("layer-display");
300 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
303 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
304 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306 /* pick up the correct freeze state */
311 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
312 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314 PropertyList* plist = new PropertyList();
316 plist->add (ARDOUR::Properties::mute, true);
317 plist->add (ARDOUR::Properties::solo, true);
319 route_group_menu = new RouteGroupMenu (_session, plist);
321 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
324 RouteTimeAxisView::~RouteTimeAxisView ()
326 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
330 delete playlist_action_menu;
331 playlist_action_menu = 0;
336 _automation_tracks.clear ();
338 delete route_group_menu;
339 CatchDeletion (this);
343 RouteTimeAxisView::post_construct ()
345 /* map current state of the route */
347 update_diskstream_display ();
348 setup_processor_menu_and_curves ();
349 reset_processor_automation_curves ();
352 /** Set up the processor menu for the current set of processors, and
353 * display automation curves for any parameters which have data.
356 RouteTimeAxisView::setup_processor_menu_and_curves ()
358 _subplugin_menu_map.clear ();
359 subplugin_menu.items().clear ();
360 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
361 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
365 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
367 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
368 if (_route->route_group()) {
369 _route->route_group()->remove (_route);
375 r.push_back (route ());
377 route_group_menu->build (r);
378 route_group_menu->menu()->popup (ev->button, ev->time);
384 RouteTimeAxisView::playlist_changed ()
390 RouteTimeAxisView::label_view ()
392 string x = _route->name ();
393 if (x != name_label.get_text ()) {
394 name_label.set_text (x);
396 const int64_t track_number = _route->track_number ();
397 if (track_number == 0) {
398 number_label.set_text ("");
400 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
405 RouteTimeAxisView::update_track_number_visibility ()
408 bool show_label = _session->config.get_track_name_number();
410 if (_route && _route->is_master()) {
414 if (number_label.get_parent()) {
415 controls_table.remove (number_label);
418 if (ARDOUR::Profile->get_mixbus()) {
419 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
421 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
424 // except the width of the number label is subtracted from the name-hbox, so we
425 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
426 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
428 number_label.set_size_request(tnw, -1);
429 number_label.show ();
430 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
432 number_label.hide ();
433 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
438 RouteTimeAxisView::parameter_changed (string const & p)
440 if (p == "track-name-number") {
441 update_track_number_visibility();
446 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
448 if (what_changed.contains (ARDOUR::Properties::name)) {
454 RouteTimeAxisView::take_name_changed (void *src)
462 RouteTimeAxisView::playlist_click ()
464 build_playlist_menu ();
465 conditionally_add_to_selection ();
466 playlist_action_menu->popup (1, gtk_get_current_event_time());
470 RouteTimeAxisView::automation_click ()
472 conditionally_add_to_selection ();
473 build_automation_action_menu (false);
474 automation_action_menu->popup (1, gtk_get_current_event_time());
478 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
480 using namespace Menu_Helpers;
482 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
483 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
486 detach_menu (subplugin_menu);
488 _main_automation_menu_map.clear ();
489 delete automation_action_menu;
490 automation_action_menu = new Menu;
492 MenuList& items = automation_action_menu->items();
494 automation_action_menu->set_name ("ArdourContextMenu");
496 items.push_back (MenuElem (_("Show All Automation"),
497 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
499 items.push_back (MenuElem (_("Show Existing Automation"),
500 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
502 items.push_back (MenuElem (_("Hide All Automation"),
503 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
505 /* Attach the plugin submenu. It may have previously been used elsewhere,
506 so it was detached above
509 if (!subplugin_menu.items().empty()) {
510 items.push_back (SeparatorElem ());
511 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
512 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
515 /* Add any route automation */
518 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
519 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
520 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
521 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
523 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
527 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
528 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
529 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
530 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
532 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
535 if (!pan_tracks.empty()) {
536 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
537 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
538 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
539 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
541 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
542 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
543 _main_automation_menu_map[*p] = pan_automation_item;
549 RouteTimeAxisView::build_display_menu ()
551 using namespace Menu_Helpers;
555 TimeAxisView::build_display_menu ();
557 /* now fill it with our stuff */
559 MenuList& items = display_menu->items();
560 display_menu->set_name ("ArdourContextMenu");
562 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
564 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
566 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
568 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
570 items.push_back (SeparatorElem());
573 detach_menu (*_size_menu);
576 items.push_back (MenuElem (_("Height"), *_size_menu));
578 items.push_back (SeparatorElem());
580 if (!Profile->get_sae()) {
581 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
582 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
583 items.push_back (SeparatorElem());
586 // Hook for derived classes to add type specific stuff
587 append_extra_display_menu_items ();
591 Menu* layers_menu = manage (new Menu);
592 MenuList &layers_items = layers_menu->items();
593 layers_menu->set_name("ArdourContextMenu");
595 RadioMenuItem::Group layers_group;
597 /* Find out how many overlaid/stacked tracks we have in the selection */
601 TrackSelection const & s = _editor.get_selection().tracks;
602 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
603 StreamView* v = (*i)->view ();
608 switch (v->layer_display ()) {
619 /* We're not connecting to signal_toggled() here; in the case where these two items are
620 set to be in the `inconsistent' state, it seems that one or other will end up active
621 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
622 select the active one, no toggled signal is emitted so nothing happens.
625 _ignore_set_layer_display = true;
627 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
628 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
629 i->set_active (overlaid != 0 && stacked == 0);
630 i->set_inconsistent (overlaid != 0 && stacked != 0);
631 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
633 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
634 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
635 i->set_active (overlaid == 0 && stacked != 0);
636 i->set_inconsistent (overlaid != 0 && stacked != 0);
637 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
639 _ignore_set_layer_display = false;
641 items.push_back (MenuElem (_("Layers"), *layers_menu));
643 if (!Profile->get_sae()) {
645 Menu* alignment_menu = manage (new Menu);
646 MenuList& alignment_items = alignment_menu->items();
647 alignment_menu->set_name ("ArdourContextMenu");
649 RadioMenuItem::Group align_group;
651 /* Same verbose hacks as for the layering options above */
657 boost::shared_ptr<Track> first_track;
659 TrackSelection const & s = _editor.get_selection().tracks;
660 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
661 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
662 if (!r || !r->is_track ()) {
667 first_track = r->track();
670 switch (r->track()->alignment_choice()) {
674 switch (r->track()->alignment_style()) {
675 case ExistingMaterial:
683 case UseExistingMaterial:
699 inconsistent = false;
708 if (!inconsistent && first_track) {
710 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
711 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
712 i->set_active (automatic != 0 && existing == 0 && capture == 0);
713 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
715 switch (first_track->alignment_choice()) {
717 switch (first_track->alignment_style()) {
718 case ExistingMaterial:
719 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
722 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
730 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
731 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
732 i->set_active (existing != 0 && capture == 0 && automatic == 0);
733 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
735 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
736 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
737 i->set_active (existing == 0 && capture != 0 && automatic == 0);
738 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
740 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
746 Menu* mode_menu = manage (new Menu);
747 MenuList& mode_items = mode_menu->items ();
748 mode_menu->set_name ("ArdourContextMenu");
750 RadioMenuItem::Group mode_group;
756 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
757 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
758 if (!r || !r->is_track ()) {
762 switch (r->track()->mode()) {
775 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
776 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
777 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
778 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
779 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
781 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
782 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
783 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
784 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
785 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
787 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
788 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
789 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
790 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
791 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
793 items.push_back (MenuElem (_("Mode"), *mode_menu));
797 items.push_back (SeparatorElem());
799 build_playlist_menu ();
800 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
801 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
804 route_group_menu->detach ();
807 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
808 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
810 r.push_back (rtv->route ());
815 r.push_back (route ());
818 route_group_menu->build (r);
819 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
821 build_automation_action_menu (true);
822 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
824 items.push_back (SeparatorElem());
828 TrackSelection const & s = _editor.get_selection().tracks;
829 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
830 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
835 if (r->route()->active()) {
842 items.push_back (CheckMenuElem (_("Active")));
843 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
844 bool click_sets_active = true;
845 if (active > 0 && inactive == 0) {
846 i->set_active (true);
847 click_sets_active = false;
848 } else if (active > 0 && inactive > 0) {
849 i->set_inconsistent (true);
851 i->set_sensitive(! _session->transport_rolling());
852 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
854 items.push_back (SeparatorElem());
855 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
856 if (!Profile->get_sae()) {
857 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
859 items.push_front (SeparatorElem());
860 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
865 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
867 if (apply_to_selection) {
868 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
871 bool needs_bounce = false;
873 if (!track()->can_use_mode (mode, needs_bounce)) {
879 cerr << "would bounce this one\n";
884 track()->set_mode (mode);
889 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
891 TimeAxisView::show_timestretch (start, end, layers, layer);
901 /* check that the time selection was made in our route, or our route group.
902 remember that route_group() == 0 implies the route is *not* in a edit group.
905 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
906 /* this doesn't apply to us */
910 /* ignore it if our edit group is not active */
912 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
917 if (timestretch_rect == 0) {
918 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
919 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
920 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
923 timestretch_rect->show ();
924 timestretch_rect->raise_to_top ();
926 double const x1 = start / _editor.get_current_zoom();
927 double const x2 = (end - 1) / _editor.get_current_zoom();
929 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
930 x2, current_height() * (layers - layer) / layers));
934 RouteTimeAxisView::hide_timestretch ()
936 TimeAxisView::hide_timestretch ();
938 if (timestretch_rect) {
939 timestretch_rect->hide ();
944 RouteTimeAxisView::show_selection (TimeSelection& ts)
948 /* ignore it if our edit group is not active or if the selection was started
949 in some other track or route group (remember that route_group() == 0 means
950 that the track is not in an route group).
953 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
954 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
960 TimeAxisView::show_selection (ts);
964 RouteTimeAxisView::set_height (uint32_t h)
967 bool height_changed = (height == 0) || (h != height);
970 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
973 gm.get_level_meter().setup_meters (gmlen, meter_width);
975 TimeAxisView::set_height (h);
978 _view->set_height ((double) current_height());
981 if (height >= preset_height (HeightNormal)) {
985 gm.get_gain_slider().show();
987 if (!_route || _route->is_monitor()) {
992 if (rec_enable_button)
993 rec_enable_button->show();
995 route_group_button.show();
996 automation_button.show();
998 if (is_track() && track()->mode() == ARDOUR::Normal) {
999 playlist_button.show();
1006 gm.get_gain_slider().hide();
1007 mute_button->show();
1008 if (!_route || _route->is_monitor()) {
1009 solo_button->hide();
1011 solo_button->show();
1013 if (rec_enable_button)
1014 rec_enable_button->show();
1016 route_group_button.hide ();
1017 automation_button.hide ();
1019 if (is_track() && track()->mode() == ARDOUR::Normal) {
1020 playlist_button.hide ();
1025 if (height_changed && !no_redraw) {
1026 /* only emit the signal if the height really changed */
1032 RouteTimeAxisView::route_color_changed ()
1035 _view->apply_color (color(), StreamView::RegionColor);
1038 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1042 RouteTimeAxisView::reset_samples_per_pixel ()
1044 set_samples_per_pixel (_editor.get_current_zoom());
1048 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1053 speed = track()->speed();
1057 _view->set_samples_per_pixel (fpp * speed);
1060 TimeAxisView::set_samples_per_pixel (fpp * speed);
1064 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1066 if (!mitem->get_active()) {
1067 /* this is one of the two calls made when these radio menu items change status. this one
1068 is for the item that became inactive, and we want to ignore it.
1073 if (apply_to_selection) {
1074 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1077 track()->set_align_choice (choice);
1083 RouteTimeAxisView::rename_current_playlist ()
1085 ArdourPrompter prompter (true);
1088 boost::shared_ptr<Track> tr = track();
1089 if (!tr || tr->destructive()) {
1093 boost::shared_ptr<Playlist> pl = tr->playlist();
1098 prompter.set_title (_("Rename Playlist"));
1099 prompter.set_prompt (_("New name for playlist:"));
1100 prompter.set_initial_text (pl->name());
1101 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1102 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1104 switch (prompter.run ()) {
1105 case Gtk::RESPONSE_ACCEPT:
1106 prompter.get_result (name);
1107 if (name.length()) {
1108 pl->set_name (name);
1118 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1120 std::string ret (basename);
1122 std::string const group_string = "." + route_group()->name() + ".";
1124 // iterate through all playlists
1126 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1127 std::string tmp = (*i)->name();
1129 std::string::size_type idx = tmp.find(group_string);
1130 // find those which belong to this group
1131 if (idx != string::npos) {
1132 tmp = tmp.substr(idx + group_string.length());
1134 // and find the largest current number
1136 if (x > maxnumber) {
1145 snprintf (buf, sizeof(buf), "%d", maxnumber);
1147 ret = this->name() + "." + route_group()->name () + "." + buf;
1153 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1157 boost::shared_ptr<Track> tr = track ();
1158 if (!tr || tr->destructive()) {
1162 boost::shared_ptr<const Playlist> pl = tr->playlist();
1169 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1170 name = resolve_new_group_playlist_name(name, playlists_before_op);
1173 while (_session->playlists->by_name(name)) {
1174 name = Playlist::bump_name (name, *_session);
1177 // TODO: The prompter "new" button should be de-activated if the user
1178 // specifies a playlist name which already exists in the session.
1182 ArdourPrompter prompter (true);
1184 prompter.set_title (_("New Copy Playlist"));
1185 prompter.set_prompt (_("Name for new playlist:"));
1186 prompter.set_initial_text (name);
1187 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1188 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1189 prompter.show_all ();
1191 switch (prompter.run ()) {
1192 case Gtk::RESPONSE_ACCEPT:
1193 prompter.get_result (name);
1201 if (name.length()) {
1202 tr->use_copy_playlist ();
1203 tr->playlist()->set_name (name);
1208 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1212 boost::shared_ptr<Track> tr = track ();
1213 if (!tr || tr->destructive()) {
1217 boost::shared_ptr<const Playlist> pl = tr->playlist();
1224 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1225 name = resolve_new_group_playlist_name(name,playlists_before_op);
1228 while (_session->playlists->by_name(name)) {
1229 name = Playlist::bump_name (name, *_session);
1235 ArdourPrompter prompter (true);
1237 prompter.set_title (_("New Playlist"));
1238 prompter.set_prompt (_("Name for new playlist:"));
1239 prompter.set_initial_text (name);
1240 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1241 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1243 switch (prompter.run ()) {
1244 case Gtk::RESPONSE_ACCEPT:
1245 prompter.get_result (name);
1253 if (name.length()) {
1254 tr->use_new_playlist ();
1255 tr->playlist()->set_name (name);
1260 RouteTimeAxisView::clear_playlist ()
1262 boost::shared_ptr<Track> tr = track ();
1263 if (!tr || tr->destructive()) {
1267 boost::shared_ptr<Playlist> pl = tr->playlist();
1272 _editor.clear_playlist (pl);
1276 RouteTimeAxisView::speed_changed ()
1278 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1282 RouteTimeAxisView::update_diskstream_display ()
1292 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1294 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1296 /* special case: select/deselect all tracks */
1298 _editor.begin_reversible_selection_op (X_("Selection Click"));
1300 if (_editor.get_selection().selected (this)) {
1301 _editor.get_selection().clear_tracks ();
1303 _editor.select_all_tracks ();
1306 _editor.commit_reversible_selection_op ();
1311 _editor.begin_reversible_selection_op (X_("Selection Click"));
1313 switch (ArdourKeyboard::selection_type (ev->state)) {
1314 case Selection::Toggle:
1315 _editor.get_selection().toggle (this);
1318 case Selection::Set:
1319 _editor.get_selection().set (this);
1322 case Selection::Extend:
1323 _editor.extend_selection_to_track (*this);
1326 case Selection::Add:
1327 _editor.get_selection().add (this);
1331 _editor.commit_reversible_selection_op ();
1335 RouteTimeAxisView::set_selected_points (PointSelection& points)
1337 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1338 (*i)->set_selected_points (points);
1343 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1346 _view->set_selected_regionviews (regions);
1350 /** Add the selectable things that we have to a list.
1351 * @param results List to add things to.
1354 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1359 speed = track()->speed();
1362 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1363 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1365 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1366 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1369 /* pick up visible automation tracks */
1371 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1372 if (!(*i)->hidden()) {
1373 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1379 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1382 _view->get_inverted_selectables (sel, results);
1385 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1386 if (!(*i)->hidden()) {
1387 (*i)->get_inverted_selectables (sel, results);
1395 RouteTimeAxisView::route_group () const
1397 return _route->route_group();
1401 RouteTimeAxisView::name() const
1403 return _route->name();
1406 boost::shared_ptr<Playlist>
1407 RouteTimeAxisView::playlist () const
1409 boost::shared_ptr<Track> tr;
1411 if ((tr = track()) != 0) {
1412 return tr->playlist();
1414 return boost::shared_ptr<Playlist> ();
1419 RouteTimeAxisView::name_entry_changed ()
1421 TimeAxisView::name_entry_changed ();
1423 string x = name_entry->get_text ();
1425 if (x == _route->name()) {
1429 strip_whitespace_edges (x);
1431 if (x.length() == 0) {
1432 name_entry->set_text (_route->name());
1436 if (_session->route_name_internal (x)) {
1437 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1439 name_entry->grab_focus ();
1440 } else if (RouteUI::verify_new_route_name (x)) {
1441 _route->set_name (x);
1443 name_entry->grab_focus ();
1447 boost::shared_ptr<Region>
1448 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1450 boost::shared_ptr<Playlist> pl = playlist ();
1453 return pl->find_next_region (pos, point, dir);
1456 return boost::shared_ptr<Region> ();
1460 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1462 boost::shared_ptr<Playlist> pl = playlist ();
1465 return pl->find_next_region_boundary (pos, dir);
1472 RouteTimeAxisView::fade_range (TimeSelection& selection)
1474 boost::shared_ptr<Playlist> what_we_got;
1475 boost::shared_ptr<Track> tr = track ();
1476 boost::shared_ptr<Playlist> playlist;
1479 /* route is a bus, not a track */
1483 playlist = tr->playlist();
1485 TimeSelection time (selection);
1486 float const speed = tr->speed();
1487 if (speed != 1.0f) {
1488 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1489 (*i).start = session_frame_to_track_frame((*i).start, speed);
1490 (*i).end = session_frame_to_track_frame((*i).end, speed);
1494 playlist->clear_changes ();
1495 playlist->clear_owned_changes ();
1497 playlist->fade_range (time);
1499 vector<Command*> cmds;
1500 playlist->rdiff (cmds);
1501 _session->add_commands (cmds);
1502 _session->add_command (new StatefulDiffCommand (playlist));
1507 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1509 boost::shared_ptr<Playlist> what_we_got;
1510 boost::shared_ptr<Track> tr = track ();
1511 boost::shared_ptr<Playlist> playlist;
1514 /* route is a bus, not a track */
1518 playlist = tr->playlist();
1520 TimeSelection time (selection.time);
1521 float const speed = tr->speed();
1522 if (speed != 1.0f) {
1523 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1524 (*i).start = session_frame_to_track_frame((*i).start, speed);
1525 (*i).end = session_frame_to_track_frame((*i).end, speed);
1529 playlist->clear_changes ();
1530 playlist->clear_owned_changes ();
1534 if (playlist->cut (time) != 0) {
1535 if (Config->get_edit_mode() == Ripple)
1536 playlist->ripple(time.start(), -time.length(), NULL);
1537 // no need to exclude any regions from rippling here
1539 vector<Command*> cmds;
1540 playlist->rdiff (cmds);
1541 _session->add_commands (cmds);
1543 _session->add_command (new StatefulDiffCommand (playlist));
1548 if ((what_we_got = playlist->cut (time)) != 0) {
1549 _editor.get_cut_buffer().add (what_we_got);
1550 if (Config->get_edit_mode() == Ripple)
1551 playlist->ripple(time.start(), -time.length(), NULL);
1552 // no need to exclude any regions from rippling here
1554 vector<Command*> cmds;
1555 playlist->rdiff (cmds);
1556 _session->add_commands (cmds);
1558 _session->add_command (new StatefulDiffCommand (playlist));
1562 if ((what_we_got = playlist->copy (time)) != 0) {
1563 _editor.get_cut_buffer().add (what_we_got);
1568 if ((what_we_got = playlist->cut (time)) != 0) {
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);
1576 _session->add_command (new StatefulDiffCommand (playlist));
1577 what_we_got->release ();
1584 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1590 boost::shared_ptr<Playlist> pl = playlist ();
1591 const ARDOUR::DataType type = pl->data_type();
1592 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1594 if (p == selection.playlists.end()) {
1597 ctx.counts.increase_n_playlists(type);
1599 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1601 if (track()->speed() != 1.0f) {
1602 pos = session_frame_to_track_frame (pos, track()->speed());
1603 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1606 /* add multi-paste offset if applicable */
1607 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1608 const framecnt_t duration = extent.second - extent.first;
1609 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1611 pl->clear_changes ();
1612 if (Config->get_edit_mode() == Ripple) {
1613 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1614 framecnt_t amount = extent.second - extent.first;
1615 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1617 pl->paste (*p, pos, ctx.times);
1619 vector<Command*> cmds;
1621 _session->add_commands (cmds);
1623 _session->add_command (new StatefulDiffCommand (pl));
1629 struct PlaylistSorter {
1630 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1631 return a->sort_id() < b->sort_id();
1636 RouteTimeAxisView::build_playlist_menu ()
1638 using namespace Menu_Helpers;
1644 delete playlist_action_menu;
1645 playlist_action_menu = new Menu;
1646 playlist_action_menu->set_name ("ArdourContextMenu");
1648 MenuList& playlist_items = playlist_action_menu->items();
1649 playlist_action_menu->set_name ("ArdourContextMenu");
1650 playlist_items.clear();
1652 RadioMenuItem::Group playlist_group;
1653 boost::shared_ptr<Track> tr = track ();
1655 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1657 /* sort the playlists */
1659 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1661 /* add the playlists to the menu */
1662 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1663 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1664 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1665 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1667 if (tr->playlist()->id() == (*i)->id()) {
1673 playlist_items.push_back (SeparatorElem());
1674 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1675 playlist_items.push_back (SeparatorElem());
1677 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1678 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1679 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1682 // Use a label which tells the user what is happening
1683 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1684 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1688 playlist_items.push_back (SeparatorElem());
1689 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1690 playlist_items.push_back (SeparatorElem());
1692 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1696 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1698 assert (is_track());
1700 // exit if we were triggered by deactivating the old playlist
1701 if (!item->get_active()) {
1705 boost::shared_ptr<Playlist> pl (wpl.lock());
1711 if (track()->playlist() == pl) {
1712 // exit when use_playlist is called by the creation of the playlist menu
1713 // or the playlist choice is unchanged
1717 track()->use_playlist (pl);
1719 RouteGroup* rg = route_group();
1721 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1722 std::string group_string = "." + rg->name() + ".";
1724 std::string take_name = pl->name();
1725 std::string::size_type idx = take_name.find(group_string);
1727 if (idx == std::string::npos)
1730 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1732 boost::shared_ptr<RouteList> rl (rg->route_list());
1734 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1735 if ((*i) == this->route()) {
1739 std::string playlist_name = (*i)->name()+group_string+take_name;
1741 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1746 if (track->freeze_state() == Track::Frozen) {
1747 /* Don't change playlists of frozen tracks */
1751 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1753 // No playlist for this track for this take yet, make it
1754 track->use_new_playlist();
1755 track->playlist()->set_name(playlist_name);
1757 track->use_playlist(ipl);
1764 RouteTimeAxisView::update_playlist_tip ()
1766 RouteGroup* rg = route_group ();
1767 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1768 string group_string = "." + rg->name() + ".";
1770 string take_name = track()->playlist()->name();
1771 string::size_type idx = take_name.find(group_string);
1773 if (idx != string::npos) {
1774 /* find the bit containing the take number / name */
1775 take_name = take_name.substr (idx + group_string.length());
1777 /* set the playlist button tooltip to the take name */
1778 ARDOUR_UI::instance()->set_tip (
1780 string_compose(_("Take: %1.%2"),
1781 Glib::Markup::escape_text(rg->name()),
1782 Glib::Markup::escape_text(take_name))
1789 /* set the playlist button tooltip to the playlist name */
1790 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1795 RouteTimeAxisView::show_playlist_selector ()
1797 _editor.playlist_selector().show_for (this);
1801 RouteTimeAxisView::map_frozen ()
1807 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1809 switch (track()->freeze_state()) {
1811 playlist_button.set_sensitive (false);
1812 rec_enable_button->set_sensitive (false);
1815 playlist_button.set_sensitive (true);
1816 rec_enable_button->set_sensitive (true);
1822 RouteTimeAxisView::color_handler ()
1824 //case cTimeStretchOutline:
1825 if (timestretch_rect) {
1826 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1828 //case cTimeStretchFill:
1829 if (timestretch_rect) {
1830 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1836 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1837 * Will add track if necessary.
1840 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1842 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1843 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1846 /* it doesn't exist yet, so we don't care about the button state: just add it */
1847 create_automation_child (param, true);
1850 bool yn = menu->get_active();
1851 bool changed = false;
1853 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1855 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1856 will have done that for us.
1859 if (changed && !no_redraw) {
1867 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1869 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1875 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1877 if (menu && !_hidden) {
1878 ignore_toggle = true;
1879 menu->set_active (false);
1880 ignore_toggle = false;
1883 if (_route && !no_redraw) {
1889 RouteTimeAxisView::update_gain_track_visibility ()
1891 bool const showit = gain_automation_item->get_active();
1893 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1894 gain_track->set_marked_for_display (showit);
1896 /* now trigger a redisplay */
1899 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1905 RouteTimeAxisView::update_mute_track_visibility ()
1907 bool const showit = mute_automation_item->get_active();
1909 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1910 mute_track->set_marked_for_display (showit);
1912 /* now trigger a redisplay */
1915 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1921 RouteTimeAxisView::update_pan_track_visibility ()
1923 bool const showit = pan_automation_item->get_active();
1924 bool changed = false;
1926 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1927 if ((*i)->set_marked_for_display (showit)) {
1933 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1938 RouteTimeAxisView::ensure_pan_views (bool show)
1940 bool changed = false;
1941 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1943 (*i)->set_marked_for_display (false);
1946 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1950 if (!_route->panner()) {
1954 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1955 set<Evoral::Parameter>::iterator p;
1957 for (p = params.begin(); p != params.end(); ++p) {
1958 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1960 if (pan_control->parameter().type() == NullAutomation) {
1961 error << "Pan control has NULL automation type!" << endmsg;
1965 if (automation_child (pan_control->parameter ()).get () == 0) {
1967 /* we don't already have an AutomationTimeAxisView for this parameter */
1969 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1971 boost::shared_ptr<AutomationTimeAxisView> t (
1972 new AutomationTimeAxisView (_session,
1976 pan_control->parameter (),
1984 pan_tracks.push_back (t);
1985 add_automation_child (*p, t, show);
1987 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1994 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1996 if (apply_to_selection) {
1997 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2001 /* Show our automation */
2003 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2004 i->second->set_marked_for_display (true);
2006 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2009 menu->set_active(true);
2014 /* Show processor automation */
2016 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2017 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2018 if ((*ii)->view == 0) {
2019 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2022 (*ii)->menu_item->set_active (true);
2035 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2037 if (apply_to_selection) {
2038 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2042 /* Show our automation */
2044 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2045 if (i->second->has_automation()) {
2046 i->second->set_marked_for_display (true);
2048 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2050 menu->set_active(true);
2055 /* Show processor automation */
2057 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2058 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2059 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2060 (*ii)->menu_item->set_active (true);
2072 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2074 if (apply_to_selection) {
2075 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2079 /* Hide our automation */
2081 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2082 i->second->set_marked_for_display (false);
2084 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2087 menu->set_active (false);
2091 /* Hide processor automation */
2093 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2094 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2095 (*ii)->menu_item->set_active (false);
2106 RouteTimeAxisView::region_view_added (RegionView* rv)
2108 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2109 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2110 boost::shared_ptr<AutomationTimeAxisView> atv;
2112 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2117 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2118 (*i)->add_ghost(rv);
2122 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2124 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2130 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2132 parent.remove_processor_automation_node (this);
2136 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2139 remove_child (pan->view);
2143 RouteTimeAxisView::ProcessorAutomationNode*
2144 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2146 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2148 if ((*i)->processor == processor) {
2150 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2151 if ((*ii)->what == what) {
2161 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2163 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2166 ProcessorAutomationNode* pan;
2168 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2169 /* session state may never have been saved with new plugin */
2170 error << _("programming error: ")
2171 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2172 processor->name(), what.type(), (int) what.channel(), what.id() )
2174 abort(); /*NOTREACHED*/
2182 boost::shared_ptr<AutomationControl> control
2183 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2185 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2186 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2187 _editor, *this, false, parent_canvas,
2188 processor->describe_parameter (what), processor->name()));
2190 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2192 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2195 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2200 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2203 pan->menu_item->set_active (false);
2212 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2214 boost::shared_ptr<Processor> processor (p.lock ());
2216 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2217 /* The Amp processor is a special case and is dealt with separately */
2221 set<Evoral::Parameter> existing;
2223 processor->what_has_data (existing);
2225 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2227 Evoral::Parameter param (*i);
2228 boost::shared_ptr<AutomationLine> al;
2230 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2233 add_processor_automation_curve (processor, param);
2239 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2241 using namespace Menu_Helpers;
2245 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2247 _automation_tracks[param] = track;
2249 /* existing state overrides "show" argument */
2250 string s = track->gui_property ("visible");
2252 show = string_is_affirmative (s);
2255 /* this might or might not change the visibility status, so don't rely on it */
2256 track->set_marked_for_display (show);
2258 if (show && !no_redraw) {
2262 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2263 /* MIDI-related parameters are always in the menu, there's no
2264 reason to rebuild the menu just because we added a automation
2265 lane for one of them. But if we add a non-MIDI automation
2266 lane, then we need to invalidate the display menu.
2268 delete display_menu;
2274 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2276 boost::shared_ptr<Processor> processor (p.lock ());
2278 if (!processor || !processor->display_to_user ()) {
2282 /* we use this override to veto the Amp processor from the plugin menu,
2283 as its automation lane can be accessed using the special "Fader" menu
2287 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2291 using namespace Menu_Helpers;
2292 ProcessorAutomationInfo *rai;
2293 list<ProcessorAutomationInfo*>::iterator x;
2295 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2297 if (automatable.empty()) {
2301 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2302 if ((*x)->processor == processor) {
2307 if (x == processor_automation.end()) {
2309 rai = new ProcessorAutomationInfo (processor);
2310 processor_automation.push_back (rai);
2318 /* any older menu was deleted at the top of processors_changed()
2319 when we cleared the subplugin menu.
2322 rai->menu = manage (new Menu);
2323 MenuList& items = rai->menu->items();
2324 rai->menu->set_name ("ArdourContextMenu");
2328 std::set<Evoral::Parameter> has_visible_automation;
2329 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2331 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2333 ProcessorAutomationNode* pan;
2334 Gtk::CheckMenuItem* mitem;
2336 string name = processor->describe_parameter (*i);
2338 items.push_back (CheckMenuElem (name));
2339 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2341 _subplugin_menu_map[*i] = mitem;
2343 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2344 mitem->set_active(true);
2347 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2351 pan = new ProcessorAutomationNode (*i, mitem, *this);
2353 rai->lines.push_back (pan);
2357 pan->menu_item = mitem;
2361 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2364 /* add the menu for this processor, because the subplugin
2365 menu is always cleared at the top of processors_changed().
2366 this is the result of some poor design in gtkmm and/or
2370 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2375 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2376 RouteTimeAxisView::ProcessorAutomationNode* pan)
2378 bool showit = pan->menu_item->get_active();
2379 bool redraw = false;
2381 if (pan->view == 0 && showit) {
2382 add_processor_automation_curve (rai->processor, pan->what);
2386 if (pan->view && pan->view->set_marked_for_display (showit)) {
2390 if (redraw && !no_redraw) {
2396 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2398 if (c.type == RouteProcessorChange::MeterPointChange) {
2399 /* nothing to do if only the meter point has changed */
2403 using namespace Menu_Helpers;
2405 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2406 (*i)->valid = false;
2409 setup_processor_menu_and_curves ();
2411 bool deleted_processor_automation = false;
2413 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2415 list<ProcessorAutomationInfo*>::iterator tmp;
2423 processor_automation.erase (i);
2424 deleted_processor_automation = true;
2431 if (deleted_processor_automation && !no_redraw) {
2436 boost::shared_ptr<AutomationLine>
2437 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2439 ProcessorAutomationNode* pan;
2441 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2447 return boost::shared_ptr<AutomationLine>();
2451 RouteTimeAxisView::reset_processor_automation_curves ()
2453 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2459 RouteTimeAxisView::can_edit_name () const
2461 /* we do not allow track name changes if it is record enabled
2463 return !_route->record_enabled();
2467 RouteTimeAxisView::blink_rec_display (bool onoff)
2469 RouteUI::blink_rec_display (onoff);
2473 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2475 if (_ignore_set_layer_display) {
2479 if (apply_to_selection) {
2480 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2484 _view->set_layer_display (d);
2487 set_gui_property (X_("layer-display"), enum_2_string (d));
2492 RouteTimeAxisView::layer_display () const
2495 return _view->layer_display ();
2498 /* we don't know, since we don't have a _view, so just return something */
2504 boost::shared_ptr<AutomationTimeAxisView>
2505 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2507 AutomationTracks::iterator i = _automation_tracks.find(param);
2508 if (i != _automation_tracks.end()) {
2511 return boost::shared_ptr<AutomationTimeAxisView>();
2516 RouteTimeAxisView::fast_update ()
2518 gm.get_level_meter().update_meters ();
2522 RouteTimeAxisView::hide_meter ()
2525 gm.get_level_meter().hide_meters ();
2529 RouteTimeAxisView::show_meter ()
2535 RouteTimeAxisView::reset_meter ()
2537 if (ARDOUR_UI::config()->get_show_track_meters()) {
2538 int meter_width = 3;
2539 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2542 gm.get_level_meter().setup_meters (height - 9, meter_width);
2549 RouteTimeAxisView::clear_meter ()
2551 gm.get_level_meter().clear_meters ();
2555 RouteTimeAxisView::meter_changed ()
2557 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2559 if (_route && !no_redraw) {
2562 // reset peak when meter point changes
2563 gm.reset_peak_display();
2567 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2570 if (_route && !no_redraw) {
2576 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2578 using namespace Menu_Helpers;
2580 if (!_underlay_streams.empty()) {
2581 MenuList& parent_items = parent_menu->items();
2582 Menu* gs_menu = manage (new Menu);
2583 gs_menu->set_name ("ArdourContextMenu");
2584 MenuList& gs_items = gs_menu->items();
2586 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2588 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2589 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2590 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2596 RouteTimeAxisView::set_underlay_state()
2598 if (!underlay_xml_node) {
2602 XMLNodeList nlist = underlay_xml_node->children();
2603 XMLNodeConstIterator niter;
2604 XMLNode *child_node;
2606 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2607 child_node = *niter;
2609 if (child_node->name() != "Underlay") {
2613 XMLProperty* prop = child_node->property ("id");
2615 PBD::ID id (prop->value());
2617 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2620 add_underlay(v->view(), false);
2629 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2635 RouteTimeAxisView& other = v->trackview();
2637 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2638 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2639 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2640 abort(); /*NOTREACHED*/
2643 _underlay_streams.push_back(v);
2644 other._underlay_mirrors.push_back(this);
2646 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2648 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2650 if (!underlay_xml_node) {
2651 underlay_xml_node = xml_node->add_child("Underlays");
2654 XMLNode* node = underlay_xml_node->add_child("Underlay");
2655 XMLProperty* prop = node->add_property("id");
2656 prop->set_value(v->trackview().route()->id().to_s());
2663 RouteTimeAxisView::remove_underlay (StreamView* v)
2669 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2670 RouteTimeAxisView& other = v->trackview();
2672 if (it != _underlay_streams.end()) {
2673 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2675 if (gm == other._underlay_mirrors.end()) {
2676 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2677 abort(); /*NOTREACHED*/
2680 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2682 _underlay_streams.erase(it);
2683 other._underlay_mirrors.erase(gm);
2685 if (underlay_xml_node) {
2686 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2692 RouteTimeAxisView::set_button_names ()
2694 if (_route && _route->solo_safe()) {
2695 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2697 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2699 if (Config->get_solo_control_is_listen_control()) {
2700 switch (Config->get_listen_position()) {
2701 case AfterFaderListen:
2702 solo_button->set_text (_("A"));
2703 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2705 case PreFaderListen:
2706 solo_button->set_text (_("P"));
2707 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2711 solo_button->set_text (_("S"));
2712 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2714 mute_button->set_text (_("M"));
2718 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2720 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2721 if (i != _main_automation_menu_map.end()) {
2725 i = _subplugin_menu_map.find (param);
2726 if (i != _subplugin_menu_map.end()) {
2734 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2736 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2738 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2742 gain_track.reset (new AutomationTimeAxisView (_session,
2743 _route, _route->amp(), c, param,
2748 _route->amp()->describe_parameter(param)));
2751 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2754 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2758 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2760 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2762 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2766 mute_track.reset (new AutomationTimeAxisView (_session,
2767 _route, _route, c, param,
2772 _route->describe_parameter(param)));
2775 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2778 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2782 void add_region_to_list (RegionView* rv, RegionList* l)
2784 l->push_back (rv->region());
2788 RouteTimeAxisView::combine_regions ()
2790 /* as of may 2011, we do not offer uncombine for MIDI tracks
2793 if (!is_audio_track()) {
2801 RegionList selected_regions;
2802 boost::shared_ptr<Playlist> playlist = track()->playlist();
2804 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2806 if (selected_regions.size() < 2) {
2810 playlist->clear_changes ();
2811 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2813 _session->add_command (new StatefulDiffCommand (playlist));
2814 /* make the new region be selected */
2816 return _view->find_view (compound_region);
2820 RouteTimeAxisView::uncombine_regions ()
2822 /* as of may 2011, we do not offer uncombine for MIDI tracks
2824 if (!is_audio_track()) {
2832 RegionList selected_regions;
2833 boost::shared_ptr<Playlist> playlist = track()->playlist();
2835 /* have to grab selected regions first because the uncombine is going
2836 * to change that in the middle of the list traverse
2839 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2841 playlist->clear_changes ();
2843 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2844 playlist->uncombine (*i);
2847 _session->add_command (new StatefulDiffCommand (playlist));
2851 RouteTimeAxisView::state_id() const
2853 return string_compose ("rtav %1", _route->id().to_s());
2858 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2860 TimeAxisView::remove_child (c);
2862 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2864 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2865 if (i->second == a) {
2866 _automation_tracks.erase (i);