2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/pannable.h"
49 #include "ardour/panner.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
56 #include "evoral/Parameter.hpp"
58 #include "canvas/debug.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "audio_streamview.h"
64 #include "route_time_axis.h"
65 #include "automation_time_axis.h"
67 #include "gui_thread.h"
68 #include "item_counts.h"
70 #include "paste_context.h"
71 #include "playlist_selector.h"
72 #include "point_selection.h"
74 #include "public_editor.h"
75 #include "region_view.h"
76 #include "rgb_macros.h"
77 #include "selection.h"
78 #include "streamview.h"
80 #include "ui_config.h"
82 #include "route_group_menu.h"
84 #include "ardour/track.h"
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
91 using namespace Gtkmm2ext;
93 using namespace Editing;
97 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
99 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
101 , parent_canvas (canvas)
103 , button_table (3, 3)
104 , route_group_button (S_("RTAV|G"))
105 , playlist_button (S_("RTAV|P"))
106 , automation_button (S_("RTAV|A"))
107 , automation_action_menu (0)
108 , plugins_submenu_item (0)
109 , route_group_menu (0)
110 , playlist_action_menu (0)
112 , color_mode_menu (0)
113 , gm (sess, true, 75, 14)
114 , _ignore_set_layer_display (false)
115 , gain_automation_item(NULL)
116 , trim_automation_item(NULL)
117 , mute_automation_item(NULL)
118 , pan_automation_item(NULL)
120 number_label.set_name("tracknumber label");
121 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
122 number_label.set_alignment(.5, .5);
123 number_label.set_fallthrough_to_parent (true);
125 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
126 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
128 parameter_changed ("editor-stereo-only-meters");
132 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
134 if (what_changed.contains (ARDOUR::Properties::name)) {
140 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
142 RouteUI::set_route (rt);
144 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
145 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
146 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
149 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
152 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
153 gm.get_level_meter().set_no_show_all();
154 gm.get_level_meter().setup_meters(50, meter_width);
155 gm.update_gain_sensitive ();
157 string str = gui_property ("height");
159 set_height (atoi (str));
161 set_height (preset_height (HeightNormal));
164 if (!_route->is_auditioner()) {
165 if (gui_property ("visible").empty()) {
166 set_gui_property ("visible", true);
169 set_gui_property ("visible", false);
172 timestretch_rect = 0;
175 ignore_toggle = false;
177 route_group_button.set_name ("route button");
178 playlist_button.set_name ("route button");
179 automation_button.set_name ("route button");
181 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
182 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
183 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
187 if (ARDOUR::Profile->get_mixbus()) {
188 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
190 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
193 if (is_midi_track()) {
194 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
195 gm.set_fader_name ("MidiTrackFader");
197 set_tooltip(*rec_enable_button, _("Record"));
198 gm.set_fader_name ("AudioTrackFader");
201 /* set playlist button tip to the current playlist, and make it update when it changes */
202 update_playlist_tip ();
203 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
206 gm.set_fader_name ("AudioBusFader");
207 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208 controls_button_size_group->add_widget(*blank);
209 if (ARDOUR::Profile->get_mixbus() ) {
210 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
212 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
219 if (!ARDOUR::Profile->get_mixbus()) {
220 controls_meters_size_group->add_widget (gm.get_level_meter());
223 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 // mute button is always present, it is used to
234 // force the 'blank' placeholders to the proper size
235 controls_button_size_group->add_widget(*mute_button);
237 if (!_route->is_master()) {
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 Gtk::Fixed *blank = manage(new Gtk::Fixed());
245 controls_button_size_group->add_widget(*blank);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 if (ARDOUR::Profile->get_mixbus()) {
255 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
258 else if (!ARDOUR::Profile->get_trx()) {
259 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 set_tooltip(*solo_button,_("Solo"));
264 set_tooltip(*mute_button,_("Mute"));
265 set_tooltip(route_group_button, _("Route Group"));
267 mute_button->set_tweaks(ArdourButton::TrackHeader);
268 solo_button->set_tweaks(ArdourButton::TrackHeader);
269 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
270 playlist_button.set_tweaks(ArdourButton::TrackHeader);
271 automation_button.set_tweaks(ArdourButton::TrackHeader);
272 route_group_button.set_tweaks(ArdourButton::TrackHeader);
274 if (is_midi_track()) {
275 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
277 set_tooltip(automation_button, _("Automation"));
280 update_track_number_visibility();
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 if (is_track() && track()->mode() == ARDOUR::Normal) {
291 if (ARDOUR::Profile->get_mixbus()) {
292 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 else if (!ARDOUR::Profile->get_trx()) {
295 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
301 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
305 str = gui_property ("layer-display");
307 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
310 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
311 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
313 /* pick up the correct freeze state */
318 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
319 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
321 PropertyList* plist = new PropertyList();
323 plist->add (ARDOUR::Properties::group_mute, true);
324 plist->add (ARDOUR::Properties::group_solo, true);
326 route_group_menu = new RouteGroupMenu (_session, plist);
328 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
331 RouteTimeAxisView::~RouteTimeAxisView ()
333 cleanup_gui_properties ();
335 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
339 delete playlist_action_menu;
340 playlist_action_menu = 0;
345 _automation_tracks.clear ();
347 delete route_group_menu;
348 CatchDeletion (this);
352 RouteTimeAxisView::name() const
355 return _route->name();
361 RouteTimeAxisView::post_construct ()
363 /* map current state of the route */
365 update_diskstream_display ();
366 setup_processor_menu_and_curves ();
367 reset_processor_automation_curves ();
370 /** Set up the processor menu for the current set of processors, and
371 * display automation curves for any parameters which have data.
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
376 _subplugin_menu_map.clear ();
377 subplugin_menu.items().clear ();
378 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
383 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
385 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
386 if (_route->route_group()) {
387 _route->route_group()->remove (_route);
393 r.push_back (route ());
395 route_group_menu->build (r);
396 if (ev->button == 1) {
397 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
401 route_group_menu->menu()->popup (ev->button, ev->time);
408 RouteTimeAxisView::playlist_changed ()
414 RouteTimeAxisView::label_view ()
416 string x = _route->name ();
417 if (x != name_label.get_text ()) {
418 name_label.set_text (x);
420 const int64_t track_number = _route->track_number ();
421 if (track_number == 0) {
422 number_label.set_text ("");
424 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
429 RouteTimeAxisView::update_track_number_visibility ()
432 bool show_label = _session->config.get_track_name_number();
434 if (_route && _route->is_master()) {
438 if (number_label.get_parent()) {
439 controls_table.remove (number_label);
442 if (ARDOUR::Profile->get_mixbus()) {
443 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
445 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
448 // except the width of the number label is subtracted from the name-hbox, so we
449 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
450 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
452 number_label.set_size_request(tnw, -1);
453 number_label.show ();
455 number_label.hide ();
460 RouteTimeAxisView::parameter_changed (string const & p)
462 if (p == "track-name-number") {
463 update_track_number_visibility();
464 } else if (p == "editor-stereo-only-meters") {
465 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
466 gm.get_level_meter().set_max_audio_meter_count (2);
468 gm.get_level_meter().set_max_audio_meter_count (0);
474 RouteTimeAxisView::take_name_changed (void *src)
482 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
484 if (ev->button != 1) {
488 build_playlist_menu ();
489 conditionally_add_to_selection ();
490 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
496 RouteTimeAxisView::automation_click (GdkEventButton *ev)
498 if (ev->button != 1) {
502 conditionally_add_to_selection ();
503 build_automation_action_menu (false);
504 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
510 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
512 using namespace Menu_Helpers;
514 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
515 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
518 detach_menu (subplugin_menu);
520 _main_automation_menu_map.clear ();
521 delete automation_action_menu;
522 automation_action_menu = new Menu;
524 MenuList& items = automation_action_menu->items();
526 automation_action_menu->set_name ("ArdourContextMenu");
528 items.push_back (MenuElem (_("Show All Automation"),
529 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
531 items.push_back (MenuElem (_("Show Existing Automation"),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
534 items.push_back (MenuElem (_("Hide All Automation"),
535 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
537 /* Attach the plugin submenu. It may have previously been used elsewhere,
538 so it was detached above
541 if (!subplugin_menu.items().empty()) {
542 items.push_back (SeparatorElem ());
543 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
544 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
547 /* Add any route automation */
550 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
551 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
552 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
553 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
555 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
559 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
560 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
562 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
564 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
568 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
569 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
570 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
571 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
573 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
576 if (!pan_tracks.empty()) {
577 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
578 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
579 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
580 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
582 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
583 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
584 _main_automation_menu_map[*p] = pan_automation_item;
590 RouteTimeAxisView::build_display_menu ()
592 using namespace Menu_Helpers;
596 TimeAxisView::build_display_menu ();
598 /* now fill it with our stuff */
600 MenuList& items = display_menu->items();
601 display_menu->set_name ("ArdourContextMenu");
603 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
605 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
607 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
609 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
611 items.push_back (SeparatorElem());
614 detach_menu (*_size_menu);
617 items.push_back (MenuElem (_("Height"), *_size_menu));
618 items.push_back (SeparatorElem());
620 // Hook for derived classes to add type specific stuff
621 append_extra_display_menu_items ();
625 Menu* layers_menu = manage (new Menu);
626 MenuList &layers_items = layers_menu->items();
627 layers_menu->set_name("ArdourContextMenu");
629 RadioMenuItem::Group layers_group;
631 /* Find out how many overlaid/stacked tracks we have in the selection */
635 int unchangeable = 0;
636 TrackSelection const & s = _editor.get_selection().tracks;
638 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
639 StreamView* v = (*i)->view ();
644 if (v->can_change_layer_display()) {
645 switch (v->layer_display ()) {
659 /* We're not connecting to signal_toggled() here; in the case where these two items are
660 set to be in the `inconsistent' state, it seems that one or other will end up active
661 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
662 select the active one, no toggled signal is emitted so nothing happens.
665 _ignore_set_layer_display = true;
667 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
668 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
669 i->set_active (overlaid != 0 && stacked == 0);
670 i->set_inconsistent (overlaid != 0 && stacked != 0);
671 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
674 i->set_sensitive (false);
677 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
678 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
679 i->set_active (overlaid == 0 && stacked != 0);
680 i->set_inconsistent (overlaid != 0 && stacked != 0);
681 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
684 i->set_sensitive (false);
687 _ignore_set_layer_display = false;
689 items.push_back (MenuElem (_("Layers"), *layers_menu));
691 Menu* alignment_menu = manage (new Menu);
692 MenuList& alignment_items = alignment_menu->items();
693 alignment_menu->set_name ("ArdourContextMenu");
695 RadioMenuItem::Group align_group;
697 /* Same verbose hacks as for the layering options above */
703 boost::shared_ptr<Track> first_track;
705 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
706 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
707 if (!r || !r->is_track ()) {
712 first_track = r->track();
715 switch (r->track()->alignment_choice()) {
719 switch (r->track()->alignment_style()) {
720 case ExistingMaterial:
728 case UseExistingMaterial:
744 inconsistent = false;
751 if (!inconsistent && first_track) {
753 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
754 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
755 i->set_active (automatic != 0 && existing == 0 && capture == 0);
756 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
758 switch (first_track->alignment_choice()) {
760 switch (first_track->alignment_style()) {
761 case ExistingMaterial:
762 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
765 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
773 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
774 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
775 i->set_active (existing != 0 && capture == 0 && automatic == 0);
776 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
778 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
779 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
780 i->set_active (existing == 0 && capture != 0 && automatic == 0);
781 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
783 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
789 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
790 Menu* mode_menu = manage (new Menu);
791 MenuList& mode_items = mode_menu->items ();
792 mode_menu->set_name ("ArdourContextMenu");
794 RadioMenuItem::Group mode_group;
800 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
801 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
802 if (!r || !r->is_track ()) {
806 switch (r->track()->mode()) {
819 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
820 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
821 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
822 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
823 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
825 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
826 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
827 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
828 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
829 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
831 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
832 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
833 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
834 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
835 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
837 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
840 items.push_back (SeparatorElem());
842 build_playlist_menu ();
843 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
844 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
847 route_group_menu->detach ();
850 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
851 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
853 r.push_back (rtv->route ());
858 r.push_back (route ());
861 route_group_menu->build (r);
862 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
864 build_automation_action_menu (true);
865 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
867 items.push_back (SeparatorElem());
871 TrackSelection const & s = _editor.get_selection().tracks;
872 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
873 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
878 if (r->route()->active()) {
885 items.push_back (CheckMenuElem (_("Active")));
886 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
887 bool click_sets_active = true;
888 if (active > 0 && inactive == 0) {
889 i->set_active (true);
890 click_sets_active = false;
891 } else if (active > 0 && inactive > 0) {
892 i->set_inconsistent (true);
894 i->set_sensitive(! _session->transport_rolling());
895 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
897 items.push_back (SeparatorElem());
898 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
899 if (_route && !_route->is_master()) {
900 items.push_back (SeparatorElem());
901 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
903 items.push_back (SeparatorElem());
904 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
907 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
909 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
911 if (apply_to_selection) {
912 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
915 bool needs_bounce = false;
917 if (!track()->can_use_mode (mode, needs_bounce)) {
923 cerr << "would bounce this one\n";
928 track()->set_mode (mode);
934 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
936 TimeAxisView::show_timestretch (start, end, layers, layer);
946 /* check that the time selection was made in our route, or our route group.
947 remember that route_group() == 0 implies the route is *not* in a edit group.
950 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
951 /* this doesn't apply to us */
955 /* ignore it if our edit group is not active */
957 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
962 if (timestretch_rect == 0) {
963 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
964 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
965 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
968 timestretch_rect->show ();
969 timestretch_rect->raise_to_top ();
971 double const x1 = start / _editor.get_current_zoom();
972 double const x2 = (end - 1) / _editor.get_current_zoom();
974 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
975 x2, current_height() * (layers - layer) / layers));
979 RouteTimeAxisView::hide_timestretch ()
981 TimeAxisView::hide_timestretch ();
983 if (timestretch_rect) {
984 timestretch_rect->hide ();
989 RouteTimeAxisView::show_selection (TimeSelection& ts)
993 /* ignore it if our edit group is not active or if the selection was started
994 in some other track or route group (remember that route_group() == 0 means
995 that the track is not in an route group).
998 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
999 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1005 TimeAxisView::show_selection (ts);
1009 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1012 bool height_changed = (height == 0) || (h != height);
1014 int meter_width = 3;
1015 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1018 gm.get_level_meter().setup_meters (gmlen, meter_width);
1020 TimeAxisView::set_height (h, m);
1023 _view->set_height ((double) current_height());
1026 if (height >= preset_height (HeightNormal)) {
1030 gm.get_gain_slider().show();
1031 mute_button->show();
1032 if (!_route || _route->is_monitor()) {
1033 solo_button->hide();
1035 solo_button->show();
1037 if (rec_enable_button)
1038 rec_enable_button->show();
1040 route_group_button.show();
1041 automation_button.show();
1043 if (is_track() && track()->mode() == ARDOUR::Normal) {
1044 playlist_button.show();
1051 gm.get_gain_slider().hide();
1052 mute_button->show();
1053 if (!_route || _route->is_monitor()) {
1054 solo_button->hide();
1056 solo_button->show();
1058 if (rec_enable_button)
1059 rec_enable_button->show();
1061 route_group_button.hide ();
1062 automation_button.hide ();
1064 if (is_track() && track()->mode() == ARDOUR::Normal) {
1065 playlist_button.hide ();
1070 if (height_changed && !no_redraw) {
1071 /* only emit the signal if the height really changed */
1077 RouteTimeAxisView::route_color_changed ()
1080 _view->apply_color (color(), StreamView::RegionColor);
1083 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1087 RouteTimeAxisView::reset_samples_per_pixel ()
1089 set_samples_per_pixel (_editor.get_current_zoom());
1093 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1098 speed = track()->speed();
1102 _view->set_samples_per_pixel (fpp * speed);
1105 TimeAxisView::set_samples_per_pixel (fpp * speed);
1109 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1111 if (!mitem->get_active()) {
1112 /* this is one of the two calls made when these radio menu items change status. this one
1113 is for the item that became inactive, and we want to ignore it.
1118 if (apply_to_selection) {
1119 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1122 track()->set_align_choice (choice);
1128 RouteTimeAxisView::rename_current_playlist ()
1130 ArdourPrompter prompter (true);
1133 boost::shared_ptr<Track> tr = track();
1134 if (!tr || tr->destructive()) {
1138 boost::shared_ptr<Playlist> pl = tr->playlist();
1143 prompter.set_title (_("Rename Playlist"));
1144 prompter.set_prompt (_("New name for playlist:"));
1145 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1146 prompter.set_initial_text (pl->name());
1147 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1150 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1153 prompter.get_result (name);
1154 if (name.length()) {
1155 if (_session->playlists->by_name (name)) {
1156 MessageDialog msg (_("Given playlist name is not unique."));
1158 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1160 pl->set_name (name);
1168 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1170 std::string ret (basename);
1172 std::string const group_string = "." + route_group()->name() + ".";
1174 // iterate through all playlists
1176 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1177 std::string tmp = (*i)->name();
1179 std::string::size_type idx = tmp.find(group_string);
1180 // find those which belong to this group
1181 if (idx != string::npos) {
1182 tmp = tmp.substr(idx + group_string.length());
1184 // and find the largest current number
1186 if (x > maxnumber) {
1195 snprintf (buf, sizeof(buf), "%d", maxnumber);
1197 ret = this->name() + "." + route_group()->name () + "." + buf;
1203 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1207 boost::shared_ptr<Track> tr = track ();
1208 if (!tr || tr->destructive()) {
1212 boost::shared_ptr<const Playlist> pl = tr->playlist();
1219 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1220 name = resolve_new_group_playlist_name(name,playlists_before_op);
1223 while (_session->playlists->by_name(name)) {
1224 name = Playlist::bump_name (name, *_session);
1228 // TODO: The prompter "new" button should be de-activated if the user
1229 // specifies a playlist name which already exists in the session.
1231 ArdourPrompter prompter (true);
1234 prompter.set_title (_("New Copy Playlist"));
1235 prompter.set_prompt (_("Name for playlist copy:"));
1237 prompter.set_title (_("New Playlist"));
1238 prompter.set_prompt (_("Name for new playlist:"));
1240 prompter.set_initial_text (name);
1241 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1242 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1243 prompter.show_all ();
1246 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1249 prompter.get_result (name);
1250 if (name.length()) {
1251 if (_session->playlists->by_name (name)) {
1252 MessageDialog msg (_("Given playlist name is not unique."));
1254 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1262 if (name.length()) {
1264 tr->use_copy_playlist ();
1266 tr->use_new_playlist ();
1268 tr->playlist()->set_name (name);
1273 RouteTimeAxisView::clear_playlist ()
1275 boost::shared_ptr<Track> tr = track ();
1276 if (!tr || tr->destructive()) {
1280 boost::shared_ptr<Playlist> pl = tr->playlist();
1285 _editor.clear_playlist (pl);
1289 RouteTimeAxisView::speed_changed ()
1291 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1295 RouteTimeAxisView::update_diskstream_display ()
1305 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1307 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1309 /* special case: select/deselect all tracks */
1311 _editor.begin_reversible_selection_op (X_("Selection Click"));
1313 if (_editor.get_selection().selected (this)) {
1314 _editor.get_selection().clear_tracks ();
1316 _editor.select_all_tracks ();
1319 _editor.commit_reversible_selection_op ();
1324 _editor.begin_reversible_selection_op (X_("Selection Click"));
1326 switch (ArdourKeyboard::selection_type (ev->state)) {
1327 case Selection::Toggle:
1328 _editor.get_selection().toggle (this);
1331 case Selection::Set:
1332 _editor.get_selection().set (this);
1335 case Selection::Extend:
1336 _editor.extend_selection_to_track (*this);
1339 case Selection::Add:
1340 _editor.get_selection().add (this);
1344 _editor.commit_reversible_selection_op ();
1348 RouteTimeAxisView::set_selected_points (PointSelection& points)
1350 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1351 (*i)->set_selected_points (points);
1353 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1355 asv->set_selected_points (points);
1360 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1363 _view->set_selected_regionviews (regions);
1367 /** Add the selectable things that we have to a list.
1368 * @param results List to add things to.
1371 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1376 speed = track()->speed();
1379 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1380 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1382 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1383 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1386 /* pick up visible automation tracks */
1388 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1389 if (!(*i)->hidden()) {
1390 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1396 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1399 _view->get_inverted_selectables (sel, results);
1402 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1403 if (!(*i)->hidden()) {
1404 (*i)->get_inverted_selectables (sel, results);
1412 RouteTimeAxisView::route_group () const
1414 return _route->route_group();
1417 boost::shared_ptr<Playlist>
1418 RouteTimeAxisView::playlist () const
1420 boost::shared_ptr<Track> tr;
1422 if ((tr = track()) != 0) {
1423 return tr->playlist();
1425 return boost::shared_ptr<Playlist> ();
1430 RouteTimeAxisView::name_entry_changed (string const& str)
1432 if (str == _route->name()) {
1438 strip_whitespace_edges (x);
1444 if (_session->route_name_internal (x)) {
1445 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1447 } else if (RouteUI::verify_new_route_name (x)) {
1448 _route->set_name (x);
1455 boost::shared_ptr<Region>
1456 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1458 boost::shared_ptr<Playlist> pl = playlist ();
1461 return pl->find_next_region (pos, point, dir);
1464 return boost::shared_ptr<Region> ();
1468 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1470 boost::shared_ptr<Playlist> pl = playlist ();
1473 return pl->find_next_region_boundary (pos, dir);
1480 RouteTimeAxisView::fade_range (TimeSelection& selection)
1482 boost::shared_ptr<Playlist> what_we_got;
1483 boost::shared_ptr<Track> tr = track ();
1484 boost::shared_ptr<Playlist> playlist;
1487 /* route is a bus, not a track */
1491 playlist = tr->playlist();
1493 TimeSelection time (selection);
1494 float const speed = tr->speed();
1495 if (speed != 1.0f) {
1496 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1497 (*i).start = session_frame_to_track_frame((*i).start, speed);
1498 (*i).end = session_frame_to_track_frame((*i).end, speed);
1502 playlist->clear_changes ();
1503 playlist->clear_owned_changes ();
1505 playlist->fade_range (time);
1507 vector<Command*> cmds;
1508 playlist->rdiff (cmds);
1509 _session->add_commands (cmds);
1510 _session->add_command (new StatefulDiffCommand (playlist));
1515 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1517 boost::shared_ptr<Playlist> what_we_got;
1518 boost::shared_ptr<Track> tr = track ();
1519 boost::shared_ptr<Playlist> playlist;
1522 /* route is a bus, not a track */
1526 playlist = tr->playlist();
1528 TimeSelection time (selection.time);
1529 float const speed = tr->speed();
1530 if (speed != 1.0f) {
1531 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1532 (*i).start = session_frame_to_track_frame((*i).start, speed);
1533 (*i).end = session_frame_to_track_frame((*i).end, speed);
1537 playlist->clear_changes ();
1538 playlist->clear_owned_changes ();
1542 if (playlist->cut (time) != 0) {
1543 if (Config->get_edit_mode() == Ripple)
1544 playlist->ripple(time.start(), -time.length(), NULL);
1545 // no need to exclude any regions from rippling here
1547 vector<Command*> cmds;
1548 playlist->rdiff (cmds);
1549 _session->add_commands (cmds);
1551 _session->add_command (new StatefulDiffCommand (playlist));
1556 if ((what_we_got = playlist->cut (time)) != 0) {
1557 _editor.get_cut_buffer().add (what_we_got);
1558 if (Config->get_edit_mode() == Ripple)
1559 playlist->ripple(time.start(), -time.length(), NULL);
1560 // no need to exclude any regions from rippling here
1562 vector<Command*> cmds;
1563 playlist->rdiff (cmds);
1564 _session->add_commands (cmds);
1566 _session->add_command (new StatefulDiffCommand (playlist));
1570 if ((what_we_got = playlist->copy (time)) != 0) {
1571 _editor.get_cut_buffer().add (what_we_got);
1576 if ((what_we_got = playlist->cut (time)) != 0) {
1577 if (Config->get_edit_mode() == Ripple)
1578 playlist->ripple(time.start(), -time.length(), NULL);
1579 // no need to exclude any regions from rippling here
1581 vector<Command*> cmds;
1582 playlist->rdiff (cmds);
1583 _session->add_commands (cmds);
1584 _session->add_command (new StatefulDiffCommand (playlist));
1585 what_we_got->release ();
1592 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1598 boost::shared_ptr<Playlist> pl = playlist ();
1599 const ARDOUR::DataType type = pl->data_type();
1600 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1602 if (p == selection.playlists.end()) {
1605 ctx.counts.increase_n_playlists(type);
1607 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1609 if (track()->speed() != 1.0f) {
1610 pos = session_frame_to_track_frame (pos, track()->speed());
1611 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1614 /* add multi-paste offset if applicable */
1615 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1616 const framecnt_t duration = extent.second - extent.first;
1617 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1619 pl->clear_changes ();
1620 pl->clear_owned_changes ();
1621 if (Config->get_edit_mode() == Ripple) {
1622 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1623 framecnt_t amount = extent.second - extent.first;
1624 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1626 pl->paste (*p, pos, ctx.times, sub_num);
1628 vector<Command*> cmds;
1630 _session->add_commands (cmds);
1632 _session->add_command (new StatefulDiffCommand (pl));
1638 struct PlaylistSorter {
1639 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1640 return a->sort_id() < b->sort_id();
1645 RouteTimeAxisView::build_playlist_menu ()
1647 using namespace Menu_Helpers;
1653 delete playlist_action_menu;
1654 playlist_action_menu = new Menu;
1655 playlist_action_menu->set_name ("ArdourContextMenu");
1657 MenuList& playlist_items = playlist_action_menu->items();
1658 playlist_action_menu->set_name ("ArdourContextMenu");
1659 playlist_items.clear();
1661 RadioMenuItem::Group playlist_group;
1662 boost::shared_ptr<Track> tr = track ();
1664 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1666 /* sort the playlists */
1668 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1670 /* add the playlists to the menu */
1671 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1672 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1673 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1674 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1676 if (tr->playlist()->id() == (*i)->id()) {
1682 playlist_items.push_back (SeparatorElem());
1683 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1684 playlist_items.push_back (SeparatorElem());
1686 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1687 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1688 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1691 // Use a label which tells the user what is happening
1692 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1693 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1697 playlist_items.push_back (SeparatorElem());
1698 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1699 playlist_items.push_back (SeparatorElem());
1701 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1705 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1707 assert (is_track());
1709 // exit if we were triggered by deactivating the old playlist
1710 if (!item->get_active()) {
1714 boost::shared_ptr<Playlist> pl (wpl.lock());
1720 if (track()->playlist() == pl) {
1721 // exit when use_playlist is called by the creation of the playlist menu
1722 // or the playlist choice is unchanged
1726 track()->use_playlist (pl);
1728 RouteGroup* rg = route_group();
1730 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1731 std::string group_string = "." + rg->name() + ".";
1733 std::string take_name = pl->name();
1734 std::string::size_type idx = take_name.find(group_string);
1736 if (idx == std::string::npos)
1739 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1741 boost::shared_ptr<RouteList> rl (rg->route_list());
1743 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1744 if ((*i) == this->route()) {
1748 std::string playlist_name = (*i)->name()+group_string+take_name;
1750 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1755 if (track->freeze_state() == Track::Frozen) {
1756 /* Don't change playlists of frozen tracks */
1760 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1762 // No playlist for this track for this take yet, make it
1763 track->use_new_playlist();
1764 track->playlist()->set_name(playlist_name);
1766 track->use_playlist(ipl);
1773 RouteTimeAxisView::update_playlist_tip ()
1775 RouteGroup* rg = route_group ();
1776 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1777 string group_string = "." + rg->name() + ".";
1779 string take_name = track()->playlist()->name();
1780 string::size_type idx = take_name.find(group_string);
1782 if (idx != string::npos) {
1783 /* find the bit containing the take number / name */
1784 take_name = take_name.substr (idx + group_string.length());
1786 /* set the playlist button tooltip to the take name */
1789 string_compose(_("Take: %1.%2"),
1790 Gtkmm2ext::markup_escape_text (rg->name()),
1791 Gtkmm2ext::markup_escape_text (take_name))
1798 /* set the playlist button tooltip to the playlist name */
1799 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1804 RouteTimeAxisView::show_playlist_selector ()
1806 _editor.playlist_selector().show_for (this);
1810 RouteTimeAxisView::map_frozen ()
1816 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1818 switch (track()->freeze_state()) {
1820 playlist_button.set_sensitive (false);
1823 playlist_button.set_sensitive (true);
1826 RouteUI::map_frozen ();
1830 RouteTimeAxisView::color_handler ()
1832 //case cTimeStretchOutline:
1833 if (timestretch_rect) {
1834 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1836 //case cTimeStretchFill:
1837 if (timestretch_rect) {
1838 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1844 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1845 * Will add track if necessary.
1848 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1850 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1851 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1854 /* it doesn't exist yet, so we don't care about the button state: just add it */
1855 create_automation_child (param, true);
1858 bool yn = menu->get_active();
1859 bool changed = false;
1861 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1863 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1864 will have done that for us.
1867 if (changed && !no_redraw) {
1875 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1877 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1883 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1885 if (menu && !_hidden) {
1886 ignore_toggle = true;
1887 menu->set_active (false);
1888 ignore_toggle = false;
1891 if (_route && !no_redraw) {
1897 RouteTimeAxisView::update_gain_track_visibility ()
1899 bool const showit = gain_automation_item->get_active();
1901 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1902 gain_track->set_marked_for_display (showit);
1904 /* now trigger a redisplay */
1907 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1913 RouteTimeAxisView::update_trim_track_visibility ()
1915 bool const showit = trim_automation_item->get_active();
1917 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1918 trim_track->set_marked_for_display (showit);
1920 /* now trigger a redisplay */
1923 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1929 RouteTimeAxisView::update_mute_track_visibility ()
1931 bool const showit = mute_automation_item->get_active();
1933 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1934 mute_track->set_marked_for_display (showit);
1936 /* now trigger a redisplay */
1939 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1945 RouteTimeAxisView::update_pan_track_visibility ()
1947 bool const showit = pan_automation_item->get_active();
1948 bool changed = false;
1950 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1951 if ((*i)->set_marked_for_display (showit)) {
1957 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1962 RouteTimeAxisView::ensure_pan_views (bool show)
1964 bool changed = false;
1965 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1967 (*i)->set_marked_for_display (false);
1970 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1974 if (!_route->panner()) {
1978 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1979 set<Evoral::Parameter>::iterator p;
1981 for (p = params.begin(); p != params.end(); ++p) {
1982 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1984 if (pan_control->parameter().type() == NullAutomation) {
1985 error << "Pan control has NULL automation type!" << endmsg;
1989 if (automation_child (pan_control->parameter ()).get () == 0) {
1991 /* we don't already have an AutomationTimeAxisView for this parameter */
1993 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1995 boost::shared_ptr<AutomationTimeAxisView> t (
1996 new AutomationTimeAxisView (_session,
2000 pan_control->parameter (),
2008 pan_tracks.push_back (t);
2009 add_automation_child (*p, t, show);
2011 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2018 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2020 if (apply_to_selection) {
2021 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2025 /* Show our automation */
2027 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2028 i->second->set_marked_for_display (true);
2030 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2033 menu->set_active(true);
2038 /* Show processor automation */
2040 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2041 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2042 if ((*ii)->view == 0) {
2043 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2046 (*ii)->menu_item->set_active (true);
2059 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2061 if (apply_to_selection) {
2062 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2066 /* Show our automation */
2068 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2069 if (i->second->has_automation()) {
2070 i->second->set_marked_for_display (true);
2072 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2074 menu->set_active(true);
2079 /* Show processor automation */
2081 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2082 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2083 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2084 (*ii)->menu_item->set_active (true);
2096 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2098 if (apply_to_selection) {
2099 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2103 /* Hide our automation */
2105 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2106 i->second->set_marked_for_display (false);
2108 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2111 menu->set_active (false);
2115 /* Hide processor automation */
2117 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2118 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2119 (*ii)->menu_item->set_active (false);
2130 RouteTimeAxisView::region_view_added (RegionView* rv)
2132 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2133 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2134 boost::shared_ptr<AutomationTimeAxisView> atv;
2136 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2141 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2142 (*i)->add_ghost(rv);
2146 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2148 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2154 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2156 parent.remove_processor_automation_node (this);
2160 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2163 remove_child (pan->view);
2167 RouteTimeAxisView::ProcessorAutomationNode*
2168 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2170 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2172 if ((*i)->processor == processor) {
2174 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2175 if ((*ii)->what == what) {
2185 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2187 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2190 ProcessorAutomationNode* pan;
2192 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2193 /* session state may never have been saved with new plugin */
2194 error << _("programming error: ")
2195 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2196 processor->name(), what.type(), (int) what.channel(), what.id() )
2198 abort(); /*NOTREACHED*/
2206 boost::shared_ptr<AutomationControl> control
2207 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2209 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2210 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2211 _editor, *this, false, parent_canvas,
2212 processor->describe_parameter (what), processor->name()));
2214 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2216 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2219 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2224 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2227 pan->menu_item->set_active (false);
2236 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2238 boost::shared_ptr<Processor> processor (p.lock ());
2240 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2241 /* The Amp processor is a special case and is dealt with separately */
2245 set<Evoral::Parameter> existing;
2247 processor->what_has_data (existing);
2249 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2251 Evoral::Parameter param (*i);
2252 boost::shared_ptr<AutomationLine> al;
2254 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2257 add_processor_automation_curve (processor, param);
2263 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2265 using namespace Menu_Helpers;
2269 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2271 _automation_tracks[param] = track;
2273 /* existing state overrides "show" argument */
2274 string s = track->gui_property ("visible");
2276 show = string_is_affirmative (s);
2279 /* this might or might not change the visibility status, so don't rely on it */
2280 track->set_marked_for_display (show);
2282 if (show && !no_redraw) {
2286 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2287 /* MIDI-related parameters are always in the menu, there's no
2288 reason to rebuild the menu just because we added a automation
2289 lane for one of them. But if we add a non-MIDI automation
2290 lane, then we need to invalidate the display menu.
2292 delete display_menu;
2298 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2300 boost::shared_ptr<Processor> processor (p.lock ());
2302 if (!processor || !processor->display_to_user ()) {
2306 /* we use this override to veto the Amp processor from the plugin menu,
2307 as its automation lane can be accessed using the special "Fader" menu
2311 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2315 using namespace Menu_Helpers;
2316 ProcessorAutomationInfo *rai;
2317 list<ProcessorAutomationInfo*>::iterator x;
2319 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2321 if (automatable.empty()) {
2325 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2326 if ((*x)->processor == processor) {
2331 if (x == processor_automation.end()) {
2332 rai = new ProcessorAutomationInfo (processor);
2333 processor_automation.push_back (rai);
2338 /* any older menu was deleted at the top of processors_changed()
2339 when we cleared the subplugin menu.
2342 rai->menu = manage (new Menu);
2343 MenuList& items = rai->menu->items();
2344 rai->menu->set_name ("ArdourContextMenu");
2348 std::set<Evoral::Parameter> has_visible_automation;
2349 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2351 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2353 ProcessorAutomationNode* pan;
2354 Gtk::CheckMenuItem* mitem;
2356 string name = processor->describe_parameter (*i);
2358 if (name == X_("hidden")) {
2362 items.push_back (CheckMenuElem (name));
2363 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2365 _subplugin_menu_map[*i] = mitem;
2367 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2368 mitem->set_active(true);
2371 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2375 pan = new ProcessorAutomationNode (*i, mitem, *this);
2377 rai->lines.push_back (pan);
2381 pan->menu_item = mitem;
2385 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2388 if (items.size() == 0) {
2392 /* add the menu for this processor, because the subplugin
2393 menu is always cleared at the top of processors_changed().
2394 this is the result of some poor design in gtkmm and/or
2398 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2403 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2404 RouteTimeAxisView::ProcessorAutomationNode* pan)
2406 bool showit = pan->menu_item->get_active();
2407 bool redraw = false;
2409 if (pan->view == 0 && showit) {
2410 add_processor_automation_curve (rai->processor, pan->what);
2414 if (pan->view && pan->view->set_marked_for_display (showit)) {
2418 if (redraw && !no_redraw) {
2424 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2426 if (c.type == RouteProcessorChange::MeterPointChange) {
2427 /* nothing to do if only the meter point has changed */
2431 using namespace Menu_Helpers;
2433 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2434 (*i)->valid = false;
2437 setup_processor_menu_and_curves ();
2439 bool deleted_processor_automation = false;
2441 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2443 list<ProcessorAutomationInfo*>::iterator tmp;
2451 processor_automation.erase (i);
2452 deleted_processor_automation = true;
2459 if (deleted_processor_automation && !no_redraw) {
2464 boost::shared_ptr<AutomationLine>
2465 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2467 ProcessorAutomationNode* pan;
2469 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2475 return boost::shared_ptr<AutomationLine>();
2479 RouteTimeAxisView::reset_processor_automation_curves ()
2481 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2487 RouteTimeAxisView::can_edit_name () const
2489 /* we do not allow track name changes if it is record enabled
2491 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2495 return !trk->rec_enable_control()->get_value();
2499 RouteTimeAxisView::blink_rec_display (bool onoff)
2501 RouteUI::blink_rec_display (onoff);
2505 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2507 if (_ignore_set_layer_display) {
2511 if (apply_to_selection) {
2512 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2516 _view->set_layer_display (d);
2519 set_gui_property (X_("layer-display"), enum_2_string (d));
2524 RouteTimeAxisView::layer_display () const
2527 return _view->layer_display ();
2530 /* we don't know, since we don't have a _view, so just return something */
2536 boost::shared_ptr<AutomationTimeAxisView>
2537 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2539 AutomationTracks::iterator i = _automation_tracks.find(param);
2540 if (i != _automation_tracks.end()) {
2543 return boost::shared_ptr<AutomationTimeAxisView>();
2548 RouteTimeAxisView::fast_update ()
2550 gm.get_level_meter().update_meters ();
2554 RouteTimeAxisView::hide_meter ()
2557 gm.get_level_meter().hide_meters ();
2561 RouteTimeAxisView::show_meter ()
2567 RouteTimeAxisView::reset_meter ()
2569 if (UIConfiguration::instance().get_show_track_meters()) {
2570 int meter_width = 3;
2571 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2574 gm.get_level_meter().setup_meters (height - 9, meter_width);
2581 RouteTimeAxisView::clear_meter ()
2583 gm.get_level_meter().clear_meters ();
2587 RouteTimeAxisView::meter_changed ()
2589 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2591 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2594 // reset peak when meter point changes
2595 gm.reset_peak_display();
2599 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2602 if (_route && !no_redraw) {
2608 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2610 using namespace Menu_Helpers;
2612 if (!_underlay_streams.empty()) {
2613 MenuList& parent_items = parent_menu->items();
2614 Menu* gs_menu = manage (new Menu);
2615 gs_menu->set_name ("ArdourContextMenu");
2616 MenuList& gs_items = gs_menu->items();
2618 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2620 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2621 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2622 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2628 RouteTimeAxisView::set_underlay_state()
2630 if (!underlay_xml_node) {
2634 XMLNodeList nlist = underlay_xml_node->children();
2635 XMLNodeConstIterator niter;
2636 XMLNode *child_node;
2638 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2639 child_node = *niter;
2641 if (child_node->name() != "Underlay") {
2645 XMLProperty const * prop = child_node->property ("id");
2647 PBD::ID id (prop->value());
2649 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2652 add_underlay(v->view(), false);
2661 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2667 RouteTimeAxisView& other = v->trackview();
2669 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2670 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2671 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2672 abort(); /*NOTREACHED*/
2675 _underlay_streams.push_back(v);
2676 other._underlay_mirrors.push_back(this);
2678 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2680 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2682 if (!underlay_xml_node) {
2683 underlay_xml_node = xml_node->add_child("Underlays");
2686 XMLNode* node = underlay_xml_node->add_child("Underlay");
2687 XMLProperty const * prop = node->add_property("id");
2688 prop->set_value(v->trackview().route()->id().to_s());
2695 RouteTimeAxisView::remove_underlay (StreamView* v)
2701 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2702 RouteTimeAxisView& other = v->trackview();
2704 if (it != _underlay_streams.end()) {
2705 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2707 if (gm == other._underlay_mirrors.end()) {
2708 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2709 abort(); /*NOTREACHED*/
2712 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2714 _underlay_streams.erase(it);
2715 other._underlay_mirrors.erase(gm);
2717 if (underlay_xml_node) {
2718 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2724 RouteTimeAxisView::set_button_names ()
2726 if (_route && _route->solo_safe_control()->solo_safe()) {
2727 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2729 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2731 if (Config->get_solo_control_is_listen_control()) {
2732 switch (Config->get_listen_position()) {
2733 case AfterFaderListen:
2734 solo_button->set_text (S_("AfterFader|A"));
2735 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2737 case PreFaderListen:
2738 solo_button->set_text (S_("PreFader|P"));
2739 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2743 solo_button->set_text (S_("Solo|S"));
2744 set_tooltip (*solo_button, _("Solo"));
2746 mute_button->set_text (S_("Mute|M"));
2750 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2752 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2753 if (i != _main_automation_menu_map.end()) {
2757 i = _subplugin_menu_map.find (param);
2758 if (i != _subplugin_menu_map.end()) {
2766 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2768 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2770 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2774 gain_track.reset (new AutomationTimeAxisView (_session,
2775 _route, _route->amp(), c, param,
2780 _route->amp()->describe_parameter(param)));
2783 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2786 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2790 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2792 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2793 if (!c || ! _route->trim()->active()) {
2797 trim_track.reset (new AutomationTimeAxisView (_session,
2798 _route, _route->trim(), c, param,
2803 _route->trim()->describe_parameter(param)));
2806 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2809 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2813 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2815 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2817 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2821 mute_track.reset (new AutomationTimeAxisView (_session,
2822 _route, _route, c, param,
2827 _route->describe_parameter(param)));
2830 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2833 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2837 void add_region_to_list (RegionView* rv, RegionList* l)
2839 l->push_back (rv->region());
2843 RouteTimeAxisView::combine_regions ()
2845 /* as of may 2011, we do not offer uncombine for MIDI tracks
2848 if (!is_audio_track()) {
2856 RegionList selected_regions;
2857 boost::shared_ptr<Playlist> playlist = track()->playlist();
2859 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2861 if (selected_regions.size() < 2) {
2865 playlist->clear_changes ();
2866 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2868 _session->add_command (new StatefulDiffCommand (playlist));
2869 /* make the new region be selected */
2871 return _view->find_view (compound_region);
2875 RouteTimeAxisView::uncombine_regions ()
2877 /* as of may 2011, we do not offer uncombine for MIDI tracks
2879 if (!is_audio_track()) {
2887 RegionList selected_regions;
2888 boost::shared_ptr<Playlist> playlist = track()->playlist();
2890 /* have to grab selected regions first because the uncombine is going
2891 * to change that in the middle of the list traverse
2894 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2896 playlist->clear_changes ();
2898 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2899 playlist->uncombine (*i);
2902 _session->add_command (new StatefulDiffCommand (playlist));
2906 RouteTimeAxisView::state_id() const
2908 return string_compose ("rtav %1", _route->id().to_s());
2913 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2915 TimeAxisView::remove_child (c);
2917 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2919 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2920 if (i->second == a) {
2921 _automation_tracks.erase (i);
2929 RouteTimeAxisView::color () const
2931 return route_color ();
2935 RouteTimeAxisView::marked_for_display () const
2937 return !_route->presentation_info().hidden();
2941 RouteTimeAxisView::set_marked_for_display (bool yn)
2943 return RouteUI::mark_hidden (!yn);