2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/pannable.h"
49 #include "ardour/panner.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
56 #include "evoral/Parameter.hpp"
58 #include "canvas/debug.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "audio_streamview.h"
64 #include "route_time_axis.h"
65 #include "automation_time_axis.h"
67 #include "gui_thread.h"
68 #include "item_counts.h"
70 #include "paste_context.h"
71 #include "playlist_selector.h"
72 #include "point_selection.h"
74 #include "public_editor.h"
75 #include "region_view.h"
76 #include "rgb_macros.h"
77 #include "selection.h"
78 #include "streamview.h"
80 #include "ui_config.h"
82 #include "route_group_menu.h"
84 #include "ardour/track.h"
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
91 using namespace Gtkmm2ext;
93 using namespace Editing;
97 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
99 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
101 , parent_canvas (canvas)
103 , button_table (3, 3)
104 , route_group_button (S_("RTAV|G"))
105 , playlist_button (S_("RTAV|P"))
106 , automation_button (S_("RTAV|A"))
107 , automation_action_menu (0)
108 , plugins_submenu_item (0)
109 , route_group_menu (0)
110 , playlist_action_menu (0)
112 , color_mode_menu (0)
113 , gm (sess, true, 75, 14)
114 , _ignore_set_layer_display (false)
115 , gain_automation_item(NULL)
116 , trim_automation_item(NULL)
117 , mute_automation_item(NULL)
118 , pan_automation_item(NULL)
120 number_label.set_name("tracknumber label");
121 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
122 number_label.set_alignment(.5, .5);
123 number_label.set_fallthrough_to_parent (true);
125 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
126 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
128 parameter_changed ("editor-stereo-only-meters");
132 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
134 if (what_changed.contains (ARDOUR::Properties::name)) {
140 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
142 RouteUI::set_route (rt);
144 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
145 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
146 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
149 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
152 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
153 gm.get_level_meter().set_no_show_all();
154 gm.get_level_meter().setup_meters(50, meter_width);
155 gm.update_gain_sensitive ();
157 string str = gui_property ("height");
159 set_height (atoi (str));
161 set_height (preset_height (HeightNormal));
164 if (!_route->is_auditioner()) {
165 if (gui_property ("visible").empty()) {
166 set_gui_property ("visible", true);
169 set_gui_property ("visible", false);
172 timestretch_rect = 0;
175 ignore_toggle = false;
177 route_group_button.set_name ("route button");
178 playlist_button.set_name ("route button");
179 automation_button.set_name ("route button");
181 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
182 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
183 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
187 if (ARDOUR::Profile->get_mixbus()) {
188 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
190 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
193 if (is_midi_track()) {
194 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
195 gm.set_fader_name ("MidiTrackFader");
197 set_tooltip(*rec_enable_button, _("Record"));
198 gm.set_fader_name ("AudioTrackFader");
201 /* set playlist button tip to the current playlist, and make it update when it changes */
202 update_playlist_tip ();
203 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
206 gm.set_fader_name ("AudioBusFader");
207 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208 controls_button_size_group->add_widget(*blank);
209 if (ARDOUR::Profile->get_mixbus() ) {
210 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
212 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
219 if (!ARDOUR::Profile->get_mixbus()) {
220 controls_meters_size_group->add_widget (gm.get_level_meter());
223 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 // mute button is always present, it is used to
234 // force the 'blank' placeholders to the proper size
235 controls_button_size_group->add_widget(*mute_button);
237 if (!_route->is_master()) {
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 Gtk::Fixed *blank = manage(new Gtk::Fixed());
245 controls_button_size_group->add_widget(*blank);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 if (ARDOUR::Profile->get_mixbus()) {
255 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
258 else if (!ARDOUR::Profile->get_trx()) {
259 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 set_tooltip(*solo_button,_("Solo"));
264 set_tooltip(*mute_button,_("Mute"));
265 set_tooltip(route_group_button, _("Route Group"));
267 mute_button->set_tweaks(ArdourButton::TrackHeader);
268 solo_button->set_tweaks(ArdourButton::TrackHeader);
269 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
270 playlist_button.set_tweaks(ArdourButton::TrackHeader);
271 automation_button.set_tweaks(ArdourButton::TrackHeader);
272 route_group_button.set_tweaks(ArdourButton::TrackHeader);
274 if (is_midi_track()) {
275 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
277 set_tooltip(automation_button, _("Automation"));
280 update_track_number_visibility();
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 if (is_track() && track()->mode() == ARDOUR::Normal) {
291 if (ARDOUR::Profile->get_mixbus()) {
292 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 else if (!ARDOUR::Profile->get_trx()) {
295 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
301 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
305 str = gui_property ("layer-display");
307 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
310 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
311 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
313 /* pick up the correct freeze state */
318 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
319 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
321 PropertyList* plist = new PropertyList();
323 plist->add (ARDOUR::Properties::group_mute, true);
324 plist->add (ARDOUR::Properties::group_solo, true);
326 route_group_menu = new RouteGroupMenu (_session, plist);
328 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
331 RouteTimeAxisView::~RouteTimeAxisView ()
333 cleanup_gui_properties ();
335 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
339 delete playlist_action_menu;
340 playlist_action_menu = 0;
345 _automation_tracks.clear ();
347 delete route_group_menu;
348 CatchDeletion (this);
352 RouteTimeAxisView::name() const
355 return _route->name();
361 RouteTimeAxisView::post_construct ()
363 /* map current state of the route */
365 update_diskstream_display ();
366 setup_processor_menu_and_curves ();
367 reset_processor_automation_curves ();
370 /** Set up the processor menu for the current set of processors, and
371 * display automation curves for any parameters which have data.
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
376 _subplugin_menu_map.clear ();
377 subplugin_menu.items().clear ();
378 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
383 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
385 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
386 if (_route->route_group()) {
387 _route->route_group()->remove (_route);
393 r.push_back (route ());
395 route_group_menu->build (r);
396 if (ev->button == 1) {
397 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
401 route_group_menu->menu()->popup (ev->button, ev->time);
408 RouteTimeAxisView::playlist_changed ()
414 RouteTimeAxisView::label_view ()
416 string x = _route->name ();
417 if (x != name_label.get_text ()) {
418 name_label.set_text (x);
420 const int64_t track_number = _route->track_number ();
421 if (track_number == 0) {
422 number_label.set_text ("");
424 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
429 RouteTimeAxisView::update_track_number_visibility ()
432 bool show_label = _session->config.get_track_name_number();
434 if (_route && _route->is_master()) {
438 if (number_label.get_parent()) {
439 controls_table.remove (number_label);
442 if (ARDOUR::Profile->get_mixbus()) {
443 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
445 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
448 // except the width of the number label is subtracted from the name-hbox, so we
449 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
450 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
452 number_label.set_size_request(tnw, -1);
453 number_label.show ();
455 number_label.hide ();
460 RouteTimeAxisView::parameter_changed (string const & p)
462 if (p == "track-name-number") {
463 update_track_number_visibility();
464 } else if (p == "editor-stereo-only-meters") {
465 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
466 gm.get_level_meter().set_max_audio_meter_count (2);
468 gm.get_level_meter().set_max_audio_meter_count (0);
474 RouteTimeAxisView::take_name_changed (void *src)
482 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
484 if (ev->button != 1) {
488 build_playlist_menu ();
489 conditionally_add_to_selection ();
490 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
496 RouteTimeAxisView::automation_click (GdkEventButton *ev)
498 if (ev->button != 1) {
502 conditionally_add_to_selection ();
503 build_automation_action_menu (false);
504 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
510 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
512 using namespace Menu_Helpers;
514 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
515 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
518 detach_menu (subplugin_menu);
520 _main_automation_menu_map.clear ();
521 delete automation_action_menu;
522 automation_action_menu = new Menu;
524 MenuList& items = automation_action_menu->items();
526 automation_action_menu->set_name ("ArdourContextMenu");
528 items.push_back (MenuElem (_("Show All Automation"),
529 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
531 items.push_back (MenuElem (_("Show Existing Automation"),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
534 items.push_back (MenuElem (_("Hide All Automation"),
535 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
537 /* Attach the plugin submenu. It may have previously been used elsewhere,
538 so it was detached above
541 if (!subplugin_menu.items().empty()) {
542 items.push_back (SeparatorElem ());
543 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
544 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
547 /* Add any route automation */
550 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
551 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
552 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
553 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
555 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
559 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
560 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
562 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
564 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
568 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
569 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
570 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
571 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
573 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
576 if (!pan_tracks.empty()) {
577 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
578 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
579 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
580 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
582 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
583 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
584 _main_automation_menu_map[*p] = pan_automation_item;
590 RouteTimeAxisView::build_display_menu ()
592 using namespace Menu_Helpers;
596 TimeAxisView::build_display_menu ();
598 /* now fill it with our stuff */
600 MenuList& items = display_menu->items();
601 display_menu->set_name ("ArdourContextMenu");
603 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
605 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
607 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
609 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
611 items.push_back (SeparatorElem());
614 detach_menu (*_size_menu);
617 items.push_back (MenuElem (_("Height"), *_size_menu));
618 items.push_back (SeparatorElem());
620 // Hook for derived classes to add type specific stuff
621 append_extra_display_menu_items ();
625 Menu* layers_menu = manage (new Menu);
626 MenuList &layers_items = layers_menu->items();
627 layers_menu->set_name("ArdourContextMenu");
629 RadioMenuItem::Group layers_group;
631 /* Find out how many overlaid/stacked tracks we have in the selection */
635 TrackSelection const & s = _editor.get_selection().tracks;
636 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
637 StreamView* v = (*i)->view ();
642 switch (v->layer_display ()) {
653 /* We're not connecting to signal_toggled() here; in the case where these two items are
654 set to be in the `inconsistent' state, it seems that one or other will end up active
655 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
656 select the active one, no toggled signal is emitted so nothing happens.
659 _ignore_set_layer_display = true;
661 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
662 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
663 i->set_active (overlaid != 0 && stacked == 0);
664 i->set_inconsistent (overlaid != 0 && stacked != 0);
665 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
667 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
668 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
669 i->set_active (overlaid == 0 && stacked != 0);
670 i->set_inconsistent (overlaid != 0 && stacked != 0);
671 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
673 _ignore_set_layer_display = false;
675 items.push_back (MenuElem (_("Layers"), *layers_menu));
677 Menu* alignment_menu = manage (new Menu);
678 MenuList& alignment_items = alignment_menu->items();
679 alignment_menu->set_name ("ArdourContextMenu");
681 RadioMenuItem::Group align_group;
683 /* Same verbose hacks as for the layering options above */
689 boost::shared_ptr<Track> first_track;
691 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
692 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
693 if (!r || !r->is_track ()) {
698 first_track = r->track();
701 switch (r->track()->alignment_choice()) {
705 switch (r->track()->alignment_style()) {
706 case ExistingMaterial:
714 case UseExistingMaterial:
730 inconsistent = false;
737 if (!inconsistent && first_track) {
739 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
740 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
741 i->set_active (automatic != 0 && existing == 0 && capture == 0);
742 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
744 switch (first_track->alignment_choice()) {
746 switch (first_track->alignment_style()) {
747 case ExistingMaterial:
748 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
751 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
759 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
760 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
761 i->set_active (existing != 0 && capture == 0 && automatic == 0);
762 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
764 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
765 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
766 i->set_active (existing == 0 && capture != 0 && automatic == 0);
767 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
769 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
775 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
776 Menu* mode_menu = manage (new Menu);
777 MenuList& mode_items = mode_menu->items ();
778 mode_menu->set_name ("ArdourContextMenu");
780 RadioMenuItem::Group mode_group;
786 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
787 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
788 if (!r || !r->is_track ()) {
792 switch (r->track()->mode()) {
805 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
806 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
807 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
808 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
809 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
811 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
812 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
813 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
814 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
815 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
817 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
818 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
819 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
820 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
821 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
823 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
826 items.push_back (SeparatorElem());
828 build_playlist_menu ();
829 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
830 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
833 route_group_menu->detach ();
836 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
837 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
839 r.push_back (rtv->route ());
844 r.push_back (route ());
847 route_group_menu->build (r);
848 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
850 build_automation_action_menu (true);
851 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
853 items.push_back (SeparatorElem());
857 TrackSelection const & s = _editor.get_selection().tracks;
858 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
859 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
864 if (r->route()->active()) {
871 items.push_back (CheckMenuElem (_("Active")));
872 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
873 bool click_sets_active = true;
874 if (active > 0 && inactive == 0) {
875 i->set_active (true);
876 click_sets_active = false;
877 } else if (active > 0 && inactive > 0) {
878 i->set_inconsistent (true);
880 i->set_sensitive(! _session->transport_rolling());
881 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
883 items.push_back (SeparatorElem());
884 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
885 if (_route && !_route->is_master()) {
886 items.push_back (SeparatorElem());
887 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
889 items.push_back (SeparatorElem());
890 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
893 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
895 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
897 if (apply_to_selection) {
898 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
901 bool needs_bounce = false;
903 if (!track()->can_use_mode (mode, needs_bounce)) {
909 cerr << "would bounce this one\n";
914 track()->set_mode (mode);
920 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
922 TimeAxisView::show_timestretch (start, end, layers, layer);
932 /* check that the time selection was made in our route, or our route group.
933 remember that route_group() == 0 implies the route is *not* in a edit group.
936 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
937 /* this doesn't apply to us */
941 /* ignore it if our edit group is not active */
943 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
948 if (timestretch_rect == 0) {
949 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
950 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
951 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
954 timestretch_rect->show ();
955 timestretch_rect->raise_to_top ();
957 double const x1 = start / _editor.get_current_zoom();
958 double const x2 = (end - 1) / _editor.get_current_zoom();
960 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
961 x2, current_height() * (layers - layer) / layers));
965 RouteTimeAxisView::hide_timestretch ()
967 TimeAxisView::hide_timestretch ();
969 if (timestretch_rect) {
970 timestretch_rect->hide ();
975 RouteTimeAxisView::show_selection (TimeSelection& ts)
979 /* ignore it if our edit group is not active or if the selection was started
980 in some other track or route group (remember that route_group() == 0 means
981 that the track is not in an route group).
984 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
985 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
991 TimeAxisView::show_selection (ts);
995 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
998 bool height_changed = (height == 0) || (h != height);
1000 int meter_width = 3;
1001 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1004 gm.get_level_meter().setup_meters (gmlen, meter_width);
1006 TimeAxisView::set_height (h, m);
1009 _view->set_height ((double) current_height());
1012 if (height >= preset_height (HeightNormal)) {
1016 gm.get_gain_slider().show();
1017 mute_button->show();
1018 if (!_route || _route->is_monitor()) {
1019 solo_button->hide();
1021 solo_button->show();
1023 if (rec_enable_button)
1024 rec_enable_button->show();
1026 route_group_button.show();
1027 automation_button.show();
1029 if (is_track() && track()->mode() == ARDOUR::Normal) {
1030 playlist_button.show();
1037 gm.get_gain_slider().hide();
1038 mute_button->show();
1039 if (!_route || _route->is_monitor()) {
1040 solo_button->hide();
1042 solo_button->show();
1044 if (rec_enable_button)
1045 rec_enable_button->show();
1047 route_group_button.hide ();
1048 automation_button.hide ();
1050 if (is_track() && track()->mode() == ARDOUR::Normal) {
1051 playlist_button.hide ();
1056 if (height_changed && !no_redraw) {
1057 /* only emit the signal if the height really changed */
1063 RouteTimeAxisView::route_color_changed ()
1066 _view->apply_color (color(), StreamView::RegionColor);
1069 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1073 RouteTimeAxisView::reset_samples_per_pixel ()
1075 set_samples_per_pixel (_editor.get_current_zoom());
1079 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1084 speed = track()->speed();
1088 _view->set_samples_per_pixel (fpp * speed);
1091 TimeAxisView::set_samples_per_pixel (fpp * speed);
1095 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1097 if (!mitem->get_active()) {
1098 /* this is one of the two calls made when these radio menu items change status. this one
1099 is for the item that became inactive, and we want to ignore it.
1104 if (apply_to_selection) {
1105 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1108 track()->set_align_choice (choice);
1114 RouteTimeAxisView::rename_current_playlist ()
1116 ArdourPrompter prompter (true);
1119 boost::shared_ptr<Track> tr = track();
1120 if (!tr || tr->destructive()) {
1124 boost::shared_ptr<Playlist> pl = tr->playlist();
1129 prompter.set_title (_("Rename Playlist"));
1130 prompter.set_prompt (_("New name for playlist:"));
1131 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1132 prompter.set_initial_text (pl->name());
1133 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1136 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1139 prompter.get_result (name);
1140 if (name.length()) {
1141 if (_session->playlists->by_name (name)) {
1142 MessageDialog msg (_("Given playlist name is not unique."));
1144 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1146 pl->set_name (name);
1154 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1156 std::string ret (basename);
1158 std::string const group_string = "." + route_group()->name() + ".";
1160 // iterate through all playlists
1162 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1163 std::string tmp = (*i)->name();
1165 std::string::size_type idx = tmp.find(group_string);
1166 // find those which belong to this group
1167 if (idx != string::npos) {
1168 tmp = tmp.substr(idx + group_string.length());
1170 // and find the largest current number
1172 if (x > maxnumber) {
1181 snprintf (buf, sizeof(buf), "%d", maxnumber);
1183 ret = this->name() + "." + route_group()->name () + "." + buf;
1189 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1193 boost::shared_ptr<Track> tr = track ();
1194 if (!tr || tr->destructive()) {
1198 boost::shared_ptr<const Playlist> pl = tr->playlist();
1205 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1206 name = resolve_new_group_playlist_name(name,playlists_before_op);
1209 while (_session->playlists->by_name(name)) {
1210 name = Playlist::bump_name (name, *_session);
1214 // TODO: The prompter "new" button should be de-activated if the user
1215 // specifies a playlist name which already exists in the session.
1217 ArdourPrompter prompter (true);
1220 prompter.set_title (_("New Copy Playlist"));
1221 prompter.set_prompt (_("Name for playlist copy:"));
1223 prompter.set_title (_("New Playlist"));
1224 prompter.set_prompt (_("Name for new playlist:"));
1226 prompter.set_initial_text (name);
1227 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1228 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1229 prompter.show_all ();
1232 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1235 prompter.get_result (name);
1236 if (name.length()) {
1237 if (_session->playlists->by_name (name)) {
1238 MessageDialog msg (_("Given playlist name is not unique."));
1240 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1248 if (name.length()) {
1250 tr->use_copy_playlist ();
1252 tr->use_new_playlist ();
1254 tr->playlist()->set_name (name);
1259 RouteTimeAxisView::clear_playlist ()
1261 boost::shared_ptr<Track> tr = track ();
1262 if (!tr || tr->destructive()) {
1266 boost::shared_ptr<Playlist> pl = tr->playlist();
1271 _editor.clear_playlist (pl);
1275 RouteTimeAxisView::speed_changed ()
1277 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1281 RouteTimeAxisView::update_diskstream_display ()
1291 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1293 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1295 /* special case: select/deselect all tracks */
1297 _editor.begin_reversible_selection_op (X_("Selection Click"));
1299 if (_editor.get_selection().selected (this)) {
1300 _editor.get_selection().clear_tracks ();
1302 _editor.select_all_tracks ();
1305 _editor.commit_reversible_selection_op ();
1310 _editor.begin_reversible_selection_op (X_("Selection Click"));
1312 switch (ArdourKeyboard::selection_type (ev->state)) {
1313 case Selection::Toggle:
1314 _editor.get_selection().toggle (this);
1317 case Selection::Set:
1318 _editor.get_selection().set (this);
1321 case Selection::Extend:
1322 _editor.extend_selection_to_track (*this);
1325 case Selection::Add:
1326 _editor.get_selection().add (this);
1330 _editor.commit_reversible_selection_op ();
1334 RouteTimeAxisView::set_selected_points (PointSelection& points)
1336 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1337 (*i)->set_selected_points (points);
1339 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1341 asv->set_selected_points (points);
1346 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1349 _view->set_selected_regionviews (regions);
1353 /** Add the selectable things that we have to a list.
1354 * @param results List to add things to.
1357 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1362 speed = track()->speed();
1365 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1366 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1368 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1369 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1372 /* pick up visible automation tracks */
1374 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1375 if (!(*i)->hidden()) {
1376 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1382 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1385 _view->get_inverted_selectables (sel, results);
1388 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1389 if (!(*i)->hidden()) {
1390 (*i)->get_inverted_selectables (sel, results);
1398 RouteTimeAxisView::route_group () const
1400 return _route->route_group();
1403 boost::shared_ptr<Playlist>
1404 RouteTimeAxisView::playlist () const
1406 boost::shared_ptr<Track> tr;
1408 if ((tr = track()) != 0) {
1409 return tr->playlist();
1411 return boost::shared_ptr<Playlist> ();
1416 RouteTimeAxisView::name_entry_changed (string const& str)
1418 if (str == _route->name()) {
1424 strip_whitespace_edges (x);
1430 if (_session->route_name_internal (x)) {
1431 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1433 } else if (RouteUI::verify_new_route_name (x)) {
1434 _route->set_name (x);
1441 boost::shared_ptr<Region>
1442 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1444 boost::shared_ptr<Playlist> pl = playlist ();
1447 return pl->find_next_region (pos, point, dir);
1450 return boost::shared_ptr<Region> ();
1454 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1456 boost::shared_ptr<Playlist> pl = playlist ();
1459 return pl->find_next_region_boundary (pos, dir);
1466 RouteTimeAxisView::fade_range (TimeSelection& selection)
1468 boost::shared_ptr<Playlist> what_we_got;
1469 boost::shared_ptr<Track> tr = track ();
1470 boost::shared_ptr<Playlist> playlist;
1473 /* route is a bus, not a track */
1477 playlist = tr->playlist();
1479 TimeSelection time (selection);
1480 float const speed = tr->speed();
1481 if (speed != 1.0f) {
1482 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1483 (*i).start = session_frame_to_track_frame((*i).start, speed);
1484 (*i).end = session_frame_to_track_frame((*i).end, speed);
1488 playlist->clear_changes ();
1489 playlist->clear_owned_changes ();
1491 playlist->fade_range (time);
1493 vector<Command*> cmds;
1494 playlist->rdiff (cmds);
1495 _session->add_commands (cmds);
1496 _session->add_command (new StatefulDiffCommand (playlist));
1501 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1503 boost::shared_ptr<Playlist> what_we_got;
1504 boost::shared_ptr<Track> tr = track ();
1505 boost::shared_ptr<Playlist> playlist;
1508 /* route is a bus, not a track */
1512 playlist = tr->playlist();
1514 TimeSelection time (selection.time);
1515 float const speed = tr->speed();
1516 if (speed != 1.0f) {
1517 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1518 (*i).start = session_frame_to_track_frame((*i).start, speed);
1519 (*i).end = session_frame_to_track_frame((*i).end, speed);
1523 playlist->clear_changes ();
1524 playlist->clear_owned_changes ();
1528 if (playlist->cut (time) != 0) {
1529 if (Config->get_edit_mode() == Ripple)
1530 playlist->ripple(time.start(), -time.length(), NULL);
1531 // no need to exclude any regions from rippling here
1533 vector<Command*> cmds;
1534 playlist->rdiff (cmds);
1535 _session->add_commands (cmds);
1537 _session->add_command (new StatefulDiffCommand (playlist));
1542 if ((what_we_got = playlist->cut (time)) != 0) {
1543 _editor.get_cut_buffer().add (what_we_got);
1544 if (Config->get_edit_mode() == Ripple)
1545 playlist->ripple(time.start(), -time.length(), NULL);
1546 // no need to exclude any regions from rippling here
1548 vector<Command*> cmds;
1549 playlist->rdiff (cmds);
1550 _session->add_commands (cmds);
1552 _session->add_command (new StatefulDiffCommand (playlist));
1556 if ((what_we_got = playlist->copy (time)) != 0) {
1557 _editor.get_cut_buffer().add (what_we_got);
1562 if ((what_we_got = playlist->cut (time)) != 0) {
1563 if (Config->get_edit_mode() == Ripple)
1564 playlist->ripple(time.start(), -time.length(), NULL);
1565 // no need to exclude any regions from rippling here
1567 vector<Command*> cmds;
1568 playlist->rdiff (cmds);
1569 _session->add_commands (cmds);
1570 _session->add_command (new StatefulDiffCommand (playlist));
1571 what_we_got->release ();
1578 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1584 boost::shared_ptr<Playlist> pl = playlist ();
1585 const ARDOUR::DataType type = pl->data_type();
1586 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1588 if (p == selection.playlists.end()) {
1591 ctx.counts.increase_n_playlists(type);
1593 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1595 if (track()->speed() != 1.0f) {
1596 pos = session_frame_to_track_frame (pos, track()->speed());
1597 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1600 /* add multi-paste offset if applicable */
1601 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1602 const framecnt_t duration = extent.second - extent.first;
1603 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1605 pl->clear_changes ();
1606 pl->clear_owned_changes ();
1607 if (Config->get_edit_mode() == Ripple) {
1608 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1609 framecnt_t amount = extent.second - extent.first;
1610 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1612 pl->paste (*p, pos, ctx.times, sub_num);
1614 vector<Command*> cmds;
1616 _session->add_commands (cmds);
1618 _session->add_command (new StatefulDiffCommand (pl));
1624 struct PlaylistSorter {
1625 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1626 return a->sort_id() < b->sort_id();
1631 RouteTimeAxisView::build_playlist_menu ()
1633 using namespace Menu_Helpers;
1639 delete playlist_action_menu;
1640 playlist_action_menu = new Menu;
1641 playlist_action_menu->set_name ("ArdourContextMenu");
1643 MenuList& playlist_items = playlist_action_menu->items();
1644 playlist_action_menu->set_name ("ArdourContextMenu");
1645 playlist_items.clear();
1647 RadioMenuItem::Group playlist_group;
1648 boost::shared_ptr<Track> tr = track ();
1650 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1652 /* sort the playlists */
1654 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1656 /* add the playlists to the menu */
1657 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1658 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1659 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1660 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1662 if (tr->playlist()->id() == (*i)->id()) {
1668 playlist_items.push_back (SeparatorElem());
1669 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1670 playlist_items.push_back (SeparatorElem());
1672 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1673 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1674 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1677 // Use a label which tells the user what is happening
1678 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1679 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1683 playlist_items.push_back (SeparatorElem());
1684 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1685 playlist_items.push_back (SeparatorElem());
1687 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1691 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1693 assert (is_track());
1695 // exit if we were triggered by deactivating the old playlist
1696 if (!item->get_active()) {
1700 boost::shared_ptr<Playlist> pl (wpl.lock());
1706 if (track()->playlist() == pl) {
1707 // exit when use_playlist is called by the creation of the playlist menu
1708 // or the playlist choice is unchanged
1712 track()->use_playlist (pl);
1714 RouteGroup* rg = route_group();
1716 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1717 std::string group_string = "." + rg->name() + ".";
1719 std::string take_name = pl->name();
1720 std::string::size_type idx = take_name.find(group_string);
1722 if (idx == std::string::npos)
1725 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1727 boost::shared_ptr<RouteList> rl (rg->route_list());
1729 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1730 if ((*i) == this->route()) {
1734 std::string playlist_name = (*i)->name()+group_string+take_name;
1736 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1741 if (track->freeze_state() == Track::Frozen) {
1742 /* Don't change playlists of frozen tracks */
1746 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1748 // No playlist for this track for this take yet, make it
1749 track->use_new_playlist();
1750 track->playlist()->set_name(playlist_name);
1752 track->use_playlist(ipl);
1759 RouteTimeAxisView::update_playlist_tip ()
1761 RouteGroup* rg = route_group ();
1762 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1763 string group_string = "." + rg->name() + ".";
1765 string take_name = track()->playlist()->name();
1766 string::size_type idx = take_name.find(group_string);
1768 if (idx != string::npos) {
1769 /* find the bit containing the take number / name */
1770 take_name = take_name.substr (idx + group_string.length());
1772 /* set the playlist button tooltip to the take name */
1775 string_compose(_("Take: %1.%2"),
1776 Gtkmm2ext::markup_escape_text (rg->name()),
1777 Gtkmm2ext::markup_escape_text (take_name))
1784 /* set the playlist button tooltip to the playlist name */
1785 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1790 RouteTimeAxisView::show_playlist_selector ()
1792 _editor.playlist_selector().show_for (this);
1796 RouteTimeAxisView::map_frozen ()
1802 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1804 switch (track()->freeze_state()) {
1806 playlist_button.set_sensitive (false);
1809 playlist_button.set_sensitive (true);
1812 RouteUI::map_frozen ();
1816 RouteTimeAxisView::color_handler ()
1818 //case cTimeStretchOutline:
1819 if (timestretch_rect) {
1820 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1822 //case cTimeStretchFill:
1823 if (timestretch_rect) {
1824 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1830 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1831 * Will add track if necessary.
1834 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1836 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1837 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1840 /* it doesn't exist yet, so we don't care about the button state: just add it */
1841 create_automation_child (param, true);
1844 bool yn = menu->get_active();
1845 bool changed = false;
1847 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1849 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1850 will have done that for us.
1853 if (changed && !no_redraw) {
1861 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1863 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1869 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1871 if (menu && !_hidden) {
1872 ignore_toggle = true;
1873 menu->set_active (false);
1874 ignore_toggle = false;
1877 if (_route && !no_redraw) {
1883 RouteTimeAxisView::update_gain_track_visibility ()
1885 bool const showit = gain_automation_item->get_active();
1887 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1888 gain_track->set_marked_for_display (showit);
1890 /* now trigger a redisplay */
1893 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1899 RouteTimeAxisView::update_trim_track_visibility ()
1901 bool const showit = trim_automation_item->get_active();
1903 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1904 trim_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_mute_track_visibility ()
1917 bool const showit = mute_automation_item->get_active();
1919 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1920 mute_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_pan_track_visibility ()
1933 bool const showit = pan_automation_item->get_active();
1934 bool changed = false;
1936 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1937 if ((*i)->set_marked_for_display (showit)) {
1943 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1948 RouteTimeAxisView::ensure_pan_views (bool show)
1950 bool changed = false;
1951 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1953 (*i)->set_marked_for_display (false);
1956 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1960 if (!_route->panner()) {
1964 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1965 set<Evoral::Parameter>::iterator p;
1967 for (p = params.begin(); p != params.end(); ++p) {
1968 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1970 if (pan_control->parameter().type() == NullAutomation) {
1971 error << "Pan control has NULL automation type!" << endmsg;
1975 if (automation_child (pan_control->parameter ()).get () == 0) {
1977 /* we don't already have an AutomationTimeAxisView for this parameter */
1979 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1981 boost::shared_ptr<AutomationTimeAxisView> t (
1982 new AutomationTimeAxisView (_session,
1986 pan_control->parameter (),
1994 pan_tracks.push_back (t);
1995 add_automation_child (*p, t, show);
1997 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2004 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2006 if (apply_to_selection) {
2007 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2011 /* Show our automation */
2013 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2014 i->second->set_marked_for_display (true);
2016 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2019 menu->set_active(true);
2024 /* Show processor automation */
2026 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2027 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2028 if ((*ii)->view == 0) {
2029 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2032 (*ii)->menu_item->set_active (true);
2045 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2047 if (apply_to_selection) {
2048 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2052 /* Show our automation */
2054 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2055 if (i->second->has_automation()) {
2056 i->second->set_marked_for_display (true);
2058 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2060 menu->set_active(true);
2065 /* Show processor automation */
2067 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2068 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2069 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2070 (*ii)->menu_item->set_active (true);
2082 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2084 if (apply_to_selection) {
2085 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2089 /* Hide our automation */
2091 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2092 i->second->set_marked_for_display (false);
2094 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2097 menu->set_active (false);
2101 /* Hide processor automation */
2103 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2104 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2105 (*ii)->menu_item->set_active (false);
2116 RouteTimeAxisView::region_view_added (RegionView* rv)
2118 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2119 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2120 boost::shared_ptr<AutomationTimeAxisView> atv;
2122 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2127 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2128 (*i)->add_ghost(rv);
2132 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2134 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2140 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2142 parent.remove_processor_automation_node (this);
2146 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2149 remove_child (pan->view);
2153 RouteTimeAxisView::ProcessorAutomationNode*
2154 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2156 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2158 if ((*i)->processor == processor) {
2160 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2161 if ((*ii)->what == what) {
2171 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2173 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2176 ProcessorAutomationNode* pan;
2178 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2179 /* session state may never have been saved with new plugin */
2180 error << _("programming error: ")
2181 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2182 processor->name(), what.type(), (int) what.channel(), what.id() )
2184 abort(); /*NOTREACHED*/
2192 boost::shared_ptr<AutomationControl> control
2193 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2195 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2196 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2197 _editor, *this, false, parent_canvas,
2198 processor->describe_parameter (what), processor->name()));
2200 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2202 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2205 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2210 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2213 pan->menu_item->set_active (false);
2222 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2224 boost::shared_ptr<Processor> processor (p.lock ());
2226 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2227 /* The Amp processor is a special case and is dealt with separately */
2231 set<Evoral::Parameter> existing;
2233 processor->what_has_data (existing);
2235 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2237 Evoral::Parameter param (*i);
2238 boost::shared_ptr<AutomationLine> al;
2240 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2243 add_processor_automation_curve (processor, param);
2249 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2251 using namespace Menu_Helpers;
2255 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2257 _automation_tracks[param] = track;
2259 /* existing state overrides "show" argument */
2260 string s = track->gui_property ("visible");
2262 show = string_is_affirmative (s);
2265 /* this might or might not change the visibility status, so don't rely on it */
2266 track->set_marked_for_display (show);
2268 if (show && !no_redraw) {
2272 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2273 /* MIDI-related parameters are always in the menu, there's no
2274 reason to rebuild the menu just because we added a automation
2275 lane for one of them. But if we add a non-MIDI automation
2276 lane, then we need to invalidate the display menu.
2278 delete display_menu;
2284 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2286 boost::shared_ptr<Processor> processor (p.lock ());
2288 if (!processor || !processor->display_to_user ()) {
2292 /* we use this override to veto the Amp processor from the plugin menu,
2293 as its automation lane can be accessed using the special "Fader" menu
2297 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2301 using namespace Menu_Helpers;
2302 ProcessorAutomationInfo *rai;
2303 list<ProcessorAutomationInfo*>::iterator x;
2305 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2307 if (automatable.empty()) {
2311 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2312 if ((*x)->processor == processor) {
2317 if (x == processor_automation.end()) {
2318 rai = new ProcessorAutomationInfo (processor);
2319 processor_automation.push_back (rai);
2324 /* any older menu was deleted at the top of processors_changed()
2325 when we cleared the subplugin menu.
2328 rai->menu = manage (new Menu);
2329 MenuList& items = rai->menu->items();
2330 rai->menu->set_name ("ArdourContextMenu");
2334 std::set<Evoral::Parameter> has_visible_automation;
2335 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2337 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2339 ProcessorAutomationNode* pan;
2340 Gtk::CheckMenuItem* mitem;
2342 string name = processor->describe_parameter (*i);
2344 if (name == X_("hidden")) {
2348 items.push_back (CheckMenuElem (name));
2349 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2351 _subplugin_menu_map[*i] = mitem;
2353 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2354 mitem->set_active(true);
2357 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2361 pan = new ProcessorAutomationNode (*i, mitem, *this);
2363 rai->lines.push_back (pan);
2367 pan->menu_item = mitem;
2371 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2374 if (items.size() == 0) {
2378 /* add the menu for this processor, because the subplugin
2379 menu is always cleared at the top of processors_changed().
2380 this is the result of some poor design in gtkmm and/or
2384 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2389 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2390 RouteTimeAxisView::ProcessorAutomationNode* pan)
2392 bool showit = pan->menu_item->get_active();
2393 bool redraw = false;
2395 if (pan->view == 0 && showit) {
2396 add_processor_automation_curve (rai->processor, pan->what);
2400 if (pan->view && pan->view->set_marked_for_display (showit)) {
2404 if (redraw && !no_redraw) {
2410 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2412 if (c.type == RouteProcessorChange::MeterPointChange) {
2413 /* nothing to do if only the meter point has changed */
2417 using namespace Menu_Helpers;
2419 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2420 (*i)->valid = false;
2423 setup_processor_menu_and_curves ();
2425 bool deleted_processor_automation = false;
2427 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2429 list<ProcessorAutomationInfo*>::iterator tmp;
2437 processor_automation.erase (i);
2438 deleted_processor_automation = true;
2445 if (deleted_processor_automation && !no_redraw) {
2450 boost::shared_ptr<AutomationLine>
2451 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2453 ProcessorAutomationNode* pan;
2455 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2461 return boost::shared_ptr<AutomationLine>();
2465 RouteTimeAxisView::reset_processor_automation_curves ()
2467 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2473 RouteTimeAxisView::can_edit_name () const
2475 /* we do not allow track name changes if it is record enabled
2477 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2481 return !trk->rec_enable_control()->get_value();
2485 RouteTimeAxisView::blink_rec_display (bool onoff)
2487 RouteUI::blink_rec_display (onoff);
2491 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2493 if (_ignore_set_layer_display) {
2497 if (apply_to_selection) {
2498 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2502 _view->set_layer_display (d);
2505 set_gui_property (X_("layer-display"), enum_2_string (d));
2510 RouteTimeAxisView::layer_display () const
2513 return _view->layer_display ();
2516 /* we don't know, since we don't have a _view, so just return something */
2522 boost::shared_ptr<AutomationTimeAxisView>
2523 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2525 AutomationTracks::iterator i = _automation_tracks.find(param);
2526 if (i != _automation_tracks.end()) {
2529 return boost::shared_ptr<AutomationTimeAxisView>();
2534 RouteTimeAxisView::fast_update ()
2536 gm.get_level_meter().update_meters ();
2540 RouteTimeAxisView::hide_meter ()
2543 gm.get_level_meter().hide_meters ();
2547 RouteTimeAxisView::show_meter ()
2553 RouteTimeAxisView::reset_meter ()
2555 if (UIConfiguration::instance().get_show_track_meters()) {
2556 int meter_width = 3;
2557 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2560 gm.get_level_meter().setup_meters (height - 9, meter_width);
2567 RouteTimeAxisView::clear_meter ()
2569 gm.get_level_meter().clear_meters ();
2573 RouteTimeAxisView::meter_changed ()
2575 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2577 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2580 // reset peak when meter point changes
2581 gm.reset_peak_display();
2585 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2588 if (_route && !no_redraw) {
2594 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2596 using namespace Menu_Helpers;
2598 if (!_underlay_streams.empty()) {
2599 MenuList& parent_items = parent_menu->items();
2600 Menu* gs_menu = manage (new Menu);
2601 gs_menu->set_name ("ArdourContextMenu");
2602 MenuList& gs_items = gs_menu->items();
2604 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2606 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2607 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2608 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2614 RouteTimeAxisView::set_underlay_state()
2616 if (!underlay_xml_node) {
2620 XMLNodeList nlist = underlay_xml_node->children();
2621 XMLNodeConstIterator niter;
2622 XMLNode *child_node;
2624 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2625 child_node = *niter;
2627 if (child_node->name() != "Underlay") {
2631 XMLProperty const * prop = child_node->property ("id");
2633 PBD::ID id (prop->value());
2635 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2638 add_underlay(v->view(), false);
2647 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2653 RouteTimeAxisView& other = v->trackview();
2655 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2656 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2657 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2658 abort(); /*NOTREACHED*/
2661 _underlay_streams.push_back(v);
2662 other._underlay_mirrors.push_back(this);
2664 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2666 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2668 if (!underlay_xml_node) {
2669 underlay_xml_node = xml_node->add_child("Underlays");
2672 XMLNode* node = underlay_xml_node->add_child("Underlay");
2673 XMLProperty const * prop = node->add_property("id");
2674 prop->set_value(v->trackview().route()->id().to_s());
2681 RouteTimeAxisView::remove_underlay (StreamView* v)
2687 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2688 RouteTimeAxisView& other = v->trackview();
2690 if (it != _underlay_streams.end()) {
2691 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2693 if (gm == other._underlay_mirrors.end()) {
2694 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2695 abort(); /*NOTREACHED*/
2698 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2700 _underlay_streams.erase(it);
2701 other._underlay_mirrors.erase(gm);
2703 if (underlay_xml_node) {
2704 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2710 RouteTimeAxisView::set_button_names ()
2712 if (_route && _route->solo_safe_control()->solo_safe()) {
2713 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2715 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2717 if (Config->get_solo_control_is_listen_control()) {
2718 switch (Config->get_listen_position()) {
2719 case AfterFaderListen:
2720 solo_button->set_text (S_("AfterFader|A"));
2721 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2723 case PreFaderListen:
2724 solo_button->set_text (S_("PreFader|P"));
2725 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2729 solo_button->set_text (S_("Solo|S"));
2730 set_tooltip (*solo_button, _("Solo"));
2732 mute_button->set_text (S_("Mute|M"));
2736 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2738 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2739 if (i != _main_automation_menu_map.end()) {
2743 i = _subplugin_menu_map.find (param);
2744 if (i != _subplugin_menu_map.end()) {
2752 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2754 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2756 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2760 gain_track.reset (new AutomationTimeAxisView (_session,
2761 _route, _route->amp(), c, param,
2766 _route->amp()->describe_parameter(param)));
2769 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2772 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2776 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2778 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2779 if (!c || ! _route->trim()->active()) {
2783 trim_track.reset (new AutomationTimeAxisView (_session,
2784 _route, _route->trim(), c, param,
2789 _route->trim()->describe_parameter(param)));
2792 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2795 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2799 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2801 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2803 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2807 mute_track.reset (new AutomationTimeAxisView (_session,
2808 _route, _route, c, param,
2813 _route->describe_parameter(param)));
2816 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2819 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2823 void add_region_to_list (RegionView* rv, RegionList* l)
2825 l->push_back (rv->region());
2829 RouteTimeAxisView::combine_regions ()
2831 /* as of may 2011, we do not offer uncombine for MIDI tracks
2834 if (!is_audio_track()) {
2842 RegionList selected_regions;
2843 boost::shared_ptr<Playlist> playlist = track()->playlist();
2845 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2847 if (selected_regions.size() < 2) {
2851 playlist->clear_changes ();
2852 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2854 _session->add_command (new StatefulDiffCommand (playlist));
2855 /* make the new region be selected */
2857 return _view->find_view (compound_region);
2861 RouteTimeAxisView::uncombine_regions ()
2863 /* as of may 2011, we do not offer uncombine for MIDI tracks
2865 if (!is_audio_track()) {
2873 RegionList selected_regions;
2874 boost::shared_ptr<Playlist> playlist = track()->playlist();
2876 /* have to grab selected regions first because the uncombine is going
2877 * to change that in the middle of the list traverse
2880 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2882 playlist->clear_changes ();
2884 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2885 playlist->uncombine (*i);
2888 _session->add_command (new StatefulDiffCommand (playlist));
2892 RouteTimeAxisView::state_id() const
2894 return string_compose ("rtav %1", _route->id().to_s());
2899 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2901 TimeAxisView::remove_child (c);
2903 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2905 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2906 if (i->second == a) {
2907 _automation_tracks.erase (i);
2915 RouteTimeAxisView::color () const
2917 return route_color ();
2921 RouteTimeAxisView::marked_for_display () const
2923 return !_route->presentation_info().hidden();
2927 RouteTimeAxisView::set_marked_for_display (bool yn)
2929 return RouteUI::mark_hidden (!yn);