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 "enums_convert.h"
65 #include "route_time_axis.h"
66 #include "automation_time_axis.h"
68 #include "gui_thread.h"
69 #include "item_counts.h"
71 #include "paste_context.h"
72 #include "playlist_selector.h"
73 #include "point_selection.h"
75 #include "public_editor.h"
76 #include "region_view.h"
77 #include "rgb_macros.h"
78 #include "selection.h"
79 #include "streamview.h"
81 #include "ui_config.h"
83 #include "route_group_menu.h"
85 #include "ardour/track.h"
89 using namespace ARDOUR;
90 using namespace ARDOUR_UI_UTILS;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
98 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
100 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
102 , parent_canvas (canvas)
104 , button_table (3, 3)
105 , route_group_button (S_("RTAV|G"))
106 , playlist_button (S_("RTAV|P"))
107 , automation_button (S_("RTAV|A"))
108 , automation_action_menu (0)
109 , plugins_submenu_item (0)
110 , route_group_menu (0)
111 , playlist_action_menu (0)
113 , color_mode_menu (0)
114 , gm (sess, true, 75, 14)
115 , _ignore_set_layer_display (false)
116 , gain_automation_item(NULL)
117 , trim_automation_item(NULL)
118 , mute_automation_item(NULL)
119 , pan_automation_item(NULL)
121 number_label.set_name("tracknumber label");
122 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
123 number_label.set_alignment(.5, .5);
124 number_label.set_fallthrough_to_parent (true);
126 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
129 parameter_changed ("editor-stereo-only-meters");
133 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
135 if (what_changed.contains (ARDOUR::Properties::name)) {
141 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
143 RouteUI::set_route (rt);
145 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
146 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
147 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
150 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
153 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
154 gm.get_level_meter().set_no_show_all();
155 gm.get_level_meter().setup_meters(50, meter_width);
156 gm.update_gain_sensitive ();
159 if (get_gui_property ("height", height)) {
162 set_height (preset_height (HeightNormal));
165 if (!_route->is_auditioner()) {
166 if (gui_property ("visible").empty()) {
167 set_gui_property ("visible", true);
170 set_gui_property ("visible", false);
173 timestretch_rect = 0;
176 ignore_toggle = false;
178 route_group_button.set_name ("route button");
179 playlist_button.set_name ("route button");
180 automation_button.set_name ("route button");
182 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
183 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
184 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
188 if (ARDOUR::Profile->get_mixbus()) {
189 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
191 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
194 if (is_midi_track()) {
195 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
196 gm.set_fader_name ("MidiTrackFader");
198 set_tooltip(*rec_enable_button, _("Record"));
199 gm.set_fader_name ("AudioTrackFader");
202 /* set playlist button tip to the current playlist, and make it update when it changes */
203 update_playlist_tip ();
204 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
207 gm.set_fader_name ("AudioBusFader");
208 Gtk::Fixed *blank = manage(new Gtk::Fixed());
209 controls_button_size_group->add_widget(*blank);
210 if (ARDOUR::Profile->get_mixbus() ) {
211 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
220 if (!ARDOUR::Profile->get_mixbus()) {
221 controls_meters_size_group->add_widget (gm.get_level_meter());
224 if (_route->is_master()) {
225 route_group_button.set_sensitive(false);
228 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
229 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
230 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
231 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
233 if (ARDOUR::Profile->get_mixbus()) {
234 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
238 // mute button is always present, it is used to
239 // force the 'blank' placeholders to the proper size
240 controls_button_size_group->add_widget(*mute_button);
242 if (!_route->is_master()) {
243 if (ARDOUR::Profile->get_mixbus()) {
244 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 Gtk::Fixed *blank = manage(new Gtk::Fixed());
250 controls_button_size_group->add_widget(*blank);
251 if (ARDOUR::Profile->get_mixbus()) {
252 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
259 if (ARDOUR::Profile->get_mixbus()) {
260 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
261 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 else if (!ARDOUR::Profile->get_trx()) {
264 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
265 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
268 set_tooltip(*solo_button,_("Solo"));
269 set_tooltip(*mute_button,_("Mute"));
270 set_tooltip(route_group_button, _("Route Group"));
272 mute_button->set_tweaks(ArdourButton::TrackHeader);
273 solo_button->set_tweaks(ArdourButton::TrackHeader);
274 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
275 playlist_button.set_tweaks(ArdourButton::TrackHeader);
276 automation_button.set_tweaks(ArdourButton::TrackHeader);
277 route_group_button.set_tweaks(ArdourButton::TrackHeader);
279 if (is_midi_track()) {
280 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
282 set_tooltip(automation_button, _("Automation"));
285 update_track_number_visibility();
288 if (ARDOUR::Profile->get_mixbus()) {
289 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
291 else if (!ARDOUR::Profile->get_trx()) {
292 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
295 if (is_track() && track()->mode() == ARDOUR::Normal) {
296 if (ARDOUR::Profile->get_mixbus()) {
297 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
299 else if (!ARDOUR::Profile->get_trx()) {
300 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
306 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
310 LayerDisplay layer_display;
311 if (get_gui_property ("layer-display", layer_display)) {
312 set_layer_display (layer_display);
315 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
316 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
318 /* pick up the correct freeze state */
323 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
324 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
326 PropertyList* plist = new PropertyList();
328 plist->add (ARDOUR::Properties::group_mute, true);
329 plist->add (ARDOUR::Properties::group_solo, true);
331 route_group_menu = new RouteGroupMenu (_session, plist);
333 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
336 RouteTimeAxisView::~RouteTimeAxisView ()
338 cleanup_gui_properties ();
340 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
344 delete playlist_action_menu;
345 playlist_action_menu = 0;
350 _automation_tracks.clear ();
352 delete route_group_menu;
353 CatchDeletion (this);
357 RouteTimeAxisView::name() const
360 return _route->name();
366 RouteTimeAxisView::post_construct ()
368 /* map current state of the route */
370 update_diskstream_display ();
371 setup_processor_menu_and_curves ();
372 reset_processor_automation_curves ();
375 /** Set up the processor menu for the current set of processors, and
376 * display automation curves for any parameters which have data.
379 RouteTimeAxisView::setup_processor_menu_and_curves ()
381 _subplugin_menu_map.clear ();
382 subplugin_menu.items().clear ();
383 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
384 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
388 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
390 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
391 if (_route->route_group()) {
392 _route->route_group()->remove (_route);
398 r.push_back (route ());
400 route_group_menu->build (r);
401 if (ev->button == 1) {
402 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
406 route_group_menu->menu()->popup (ev->button, ev->time);
413 RouteTimeAxisView::playlist_changed ()
419 RouteTimeAxisView::label_view ()
421 string x = _route->name ();
422 if (x != name_label.get_text ()) {
423 name_label.set_text (x);
425 const int64_t track_number = _route->track_number ();
426 if (track_number == 0) {
427 number_label.set_text ("");
429 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
434 RouteTimeAxisView::update_track_number_visibility ()
437 bool show_label = _session->config.get_track_name_number();
439 if (_route && _route->is_master()) {
443 if (number_label.get_parent()) {
444 controls_table.remove (number_label);
447 if (ARDOUR::Profile->get_mixbus()) {
448 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
450 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
452 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
453 // except the width of the number label is subtracted from the name-hbox, so we
454 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
455 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
457 number_label.set_size_request(tnw, -1);
458 number_label.show ();
460 number_label.hide ();
465 RouteTimeAxisView::parameter_changed (string const & p)
467 if (p == "track-name-number") {
468 update_track_number_visibility();
469 } else if (p == "editor-stereo-only-meters") {
470 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
471 gm.get_level_meter().set_max_audio_meter_count (2);
473 gm.get_level_meter().set_max_audio_meter_count (0);
479 RouteTimeAxisView::take_name_changed (void *src)
487 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
489 if (ev->button != 1) {
493 build_playlist_menu ();
494 conditionally_add_to_selection ();
495 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
501 RouteTimeAxisView::automation_click (GdkEventButton *ev)
503 if (ev->button != 1) {
507 conditionally_add_to_selection ();
508 build_automation_action_menu (false);
509 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
515 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
517 using namespace Menu_Helpers;
519 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
520 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
523 detach_menu (subplugin_menu);
525 _main_automation_menu_map.clear ();
526 delete automation_action_menu;
527 automation_action_menu = new Menu;
529 MenuList& items = automation_action_menu->items();
531 automation_action_menu->set_name ("ArdourContextMenu");
533 items.push_back (MenuElem (_("Show All Automation"),
534 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
536 items.push_back (MenuElem (_("Show Existing Automation"),
537 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
539 items.push_back (MenuElem (_("Hide All Automation"),
540 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
542 /* Attach the plugin submenu. It may have previously been used elsewhere,
543 so it was detached above
546 bool single_track_selected = (!for_selection || _editor.get_selection().tracks.size() == 1);
548 if (!subplugin_menu.items().empty()) {
549 items.push_back (SeparatorElem ());
550 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
551 items.back().set_sensitive (single_track_selected);
554 /* Add any route automation */
557 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
558 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
559 gain_automation_item->set_active (single_track_selected &&
560 string_to<bool>(gain_track->gui_property ("visible")));
562 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
566 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
567 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
568 trim_automation_item->set_active (single_track_selected &&
569 string_to<bool>(trim_track->gui_property ("visible")));
571 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
575 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
576 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
577 mute_automation_item->set_active (single_track_selected &&
578 string_to<bool>(mute_track->gui_property ("visible")));
580 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
583 if (!pan_tracks.empty()) {
584 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
585 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
586 pan_automation_item->set_active (single_track_selected &&
587 string_to<bool>(pan_tracks.front()->gui_property ("visible")));
589 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
590 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
591 _main_automation_menu_map[*p] = pan_automation_item;
597 RouteTimeAxisView::build_display_menu ()
599 using namespace Menu_Helpers;
603 TimeAxisView::build_display_menu ();
605 /* now fill it with our stuff */
607 MenuList& items = display_menu->items();
608 display_menu->set_name ("ArdourContextMenu");
610 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
612 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
614 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
616 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
618 items.push_back (SeparatorElem());
621 detach_menu (*_size_menu);
624 items.push_back (MenuElem (_("Height"), *_size_menu));
625 items.push_back (SeparatorElem());
627 // Hook for derived classes to add type specific stuff
628 append_extra_display_menu_items ();
632 Menu* layers_menu = manage (new Menu);
633 MenuList &layers_items = layers_menu->items();
634 layers_menu->set_name("ArdourContextMenu");
636 RadioMenuItem::Group layers_group;
638 /* Find out how many overlaid/stacked tracks we have in the selection */
642 int unchangeable = 0;
643 TrackSelection const & s = _editor.get_selection().tracks;
645 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
646 StreamView* v = (*i)->view ();
651 if (v->can_change_layer_display()) {
652 switch (v->layer_display ()) {
666 /* We're not connecting to signal_toggled() here; in the case where these two items are
667 set to be in the `inconsistent' state, it seems that one or other will end up active
668 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
669 select the active one, no toggled signal is emitted so nothing happens.
672 _ignore_set_layer_display = true;
674 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
675 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
676 i->set_active (overlaid != 0 && stacked == 0);
677 i->set_inconsistent (overlaid != 0 && stacked != 0);
678 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
681 i->set_sensitive (false);
684 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
685 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
686 i->set_active (overlaid == 0 && stacked != 0);
687 i->set_inconsistent (overlaid != 0 && stacked != 0);
688 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
691 i->set_sensitive (false);
694 _ignore_set_layer_display = false;
696 items.push_back (MenuElem (_("Layers"), *layers_menu));
698 Menu* alignment_menu = manage (new Menu);
699 MenuList& alignment_items = alignment_menu->items();
700 alignment_menu->set_name ("ArdourContextMenu");
702 RadioMenuItem::Group align_group;
704 /* Same verbose hacks as for the layering options above */
710 boost::shared_ptr<Track> first_track;
712 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
713 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
714 if (!r || !r->is_track ()) {
719 first_track = r->track();
722 switch (r->track()->alignment_choice()) {
726 switch (r->track()->alignment_style()) {
727 case ExistingMaterial:
735 case UseExistingMaterial:
751 inconsistent = false;
758 if (!inconsistent && first_track) {
760 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
761 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
762 i->set_active (automatic != 0 && existing == 0 && capture == 0);
763 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
765 switch (first_track->alignment_choice()) {
767 switch (first_track->alignment_style()) {
768 case ExistingMaterial:
769 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
772 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
780 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
781 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
782 i->set_active (existing != 0 && capture == 0 && automatic == 0);
783 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
785 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
786 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
787 i->set_active (existing == 0 && capture != 0 && automatic == 0);
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
790 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
796 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
797 Menu* mode_menu = manage (new Menu);
798 MenuList& mode_items = mode_menu->items ();
799 mode_menu->set_name ("ArdourContextMenu");
801 RadioMenuItem::Group mode_group;
807 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
808 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
809 if (!r || !r->is_track ()) {
813 switch (r->track()->mode()) {
826 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
827 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
828 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
829 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
830 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
832 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
833 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
834 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
835 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
836 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
838 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
839 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
840 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
841 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
842 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
844 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
847 items.push_back (SeparatorElem());
849 build_playlist_menu ();
850 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
851 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
854 route_group_menu->detach ();
857 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
858 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
860 r.push_back (rtv->route ());
865 r.push_back (route ());
868 if (!_route->is_master()) {
869 route_group_menu->build (r);
870 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
873 build_automation_action_menu (true);
874 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
876 items.push_back (SeparatorElem());
880 TrackSelection const & s = _editor.get_selection().tracks;
881 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
882 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
887 if (r->route()->active()) {
894 items.push_back (CheckMenuElem (_("Active")));
895 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
896 bool click_sets_active = true;
897 if (active > 0 && inactive == 0) {
898 i->set_active (true);
899 click_sets_active = false;
900 } else if (active > 0 && inactive > 0) {
901 i->set_inconsistent (true);
903 i->set_sensitive(! _session->transport_rolling());
904 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
906 items.push_back (SeparatorElem());
907 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
908 if (_route && !_route->is_master()) {
909 items.push_back (SeparatorElem());
910 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
912 items.push_back (SeparatorElem());
913 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
916 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
918 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
920 if (apply_to_selection) {
921 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
924 bool needs_bounce = false;
926 if (!track()->can_use_mode (mode, needs_bounce)) {
932 cerr << "would bounce this one\n";
937 track()->set_mode (mode);
943 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
945 TimeAxisView::show_timestretch (start, end, layers, layer);
955 /* check that the time selection was made in our route, or our route group.
956 remember that route_group() == 0 implies the route is *not* in a edit group.
959 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
960 /* this doesn't apply to us */
964 /* ignore it if our edit group is not active */
966 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
971 if (timestretch_rect == 0) {
972 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
973 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
974 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
977 timestretch_rect->show ();
978 timestretch_rect->raise_to_top ();
980 double const x1 = start / _editor.get_current_zoom();
981 double const x2 = (end - 1) / _editor.get_current_zoom();
983 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
984 x2, current_height() * (layers - layer) / layers));
988 RouteTimeAxisView::hide_timestretch ()
990 TimeAxisView::hide_timestretch ();
992 if (timestretch_rect) {
993 timestretch_rect->hide ();
998 RouteTimeAxisView::show_selection (TimeSelection& ts)
1002 /* ignore it if our edit group is not active or if the selection was started
1003 in some other track or route group (remember that route_group() == 0 means
1004 that the track is not in an route group).
1007 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
1008 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1014 TimeAxisView::show_selection (ts);
1018 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1021 bool height_changed = (height == 0) || (h != height);
1023 int meter_width = 3;
1024 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1027 gm.get_level_meter().setup_meters (gmlen, meter_width);
1029 TimeAxisView::set_height (h, m);
1032 _view->set_height ((double) current_height());
1035 if (height >= preset_height (HeightNormal)) {
1039 gm.get_gain_slider().show();
1040 mute_button->show();
1041 if (!_route || _route->is_monitor()) {
1042 solo_button->hide();
1044 solo_button->show();
1046 if (rec_enable_button)
1047 rec_enable_button->show();
1049 route_group_button.show();
1050 automation_button.show();
1052 if (is_track() && track()->mode() == ARDOUR::Normal) {
1053 playlist_button.show();
1060 gm.get_gain_slider().hide();
1061 mute_button->show();
1062 if (!_route || _route->is_monitor()) {
1063 solo_button->hide();
1065 solo_button->show();
1067 if (rec_enable_button)
1068 rec_enable_button->show();
1070 route_group_button.hide ();
1071 automation_button.hide ();
1073 if (is_track() && track()->mode() == ARDOUR::Normal) {
1074 playlist_button.hide ();
1079 if (height_changed && !no_redraw) {
1080 /* only emit the signal if the height really changed */
1086 RouteTimeAxisView::route_color_changed ()
1089 _view->apply_color (color(), StreamView::RegionColor);
1092 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1096 RouteTimeAxisView::reset_samples_per_pixel ()
1098 set_samples_per_pixel (_editor.get_current_zoom());
1102 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1107 speed = track()->speed();
1111 _view->set_samples_per_pixel (fpp * speed);
1114 TimeAxisView::set_samples_per_pixel (fpp * speed);
1118 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1120 if (!mitem->get_active()) {
1121 /* this is one of the two calls made when these radio menu items change status. this one
1122 is for the item that became inactive, and we want to ignore it.
1127 if (apply_to_selection) {
1128 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1131 track()->set_align_choice (choice);
1137 RouteTimeAxisView::rename_current_playlist ()
1139 ArdourPrompter prompter (true);
1142 boost::shared_ptr<Track> tr = track();
1143 if (!tr || tr->destructive()) {
1147 boost::shared_ptr<Playlist> pl = tr->playlist();
1152 prompter.set_title (_("Rename Playlist"));
1153 prompter.set_prompt (_("New name for playlist:"));
1154 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1155 prompter.set_initial_text (pl->name());
1156 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1159 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1162 prompter.get_result (name);
1163 if (name.length()) {
1164 if (_session->playlists->by_name (name)) {
1165 MessageDialog msg (_("Given playlist name is not unique."));
1167 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1169 pl->set_name (name);
1177 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1179 std::string ret (basename);
1181 std::string const group_string = "." + route_group()->name() + ".";
1183 // iterate through all playlists
1185 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1186 std::string tmp = (*i)->name();
1188 std::string::size_type idx = tmp.find(group_string);
1189 // find those which belong to this group
1190 if (idx != string::npos) {
1191 tmp = tmp.substr(idx + group_string.length());
1193 // and find the largest current number
1195 if (x > maxnumber) {
1204 snprintf (buf, sizeof(buf), "%d", maxnumber);
1206 ret = this->name() + "." + route_group()->name () + "." + buf;
1212 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1216 boost::shared_ptr<Track> tr = track ();
1217 if (!tr || tr->destructive()) {
1221 boost::shared_ptr<const Playlist> pl = tr->playlist();
1228 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1229 name = resolve_new_group_playlist_name(name,playlists_before_op);
1232 while (_session->playlists->by_name(name)) {
1233 name = Playlist::bump_name (name, *_session);
1237 // TODO: The prompter "new" button should be de-activated if the user
1238 // specifies a playlist name which already exists in the session.
1240 ArdourPrompter prompter (true);
1243 prompter.set_title (_("New Copy Playlist"));
1244 prompter.set_prompt (_("Name for playlist copy:"));
1246 prompter.set_title (_("New Playlist"));
1247 prompter.set_prompt (_("Name for new playlist:"));
1249 prompter.set_initial_text (name);
1250 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1251 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1252 prompter.show_all ();
1255 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1258 prompter.get_result (name);
1259 if (name.length()) {
1260 if (_session->playlists->by_name (name)) {
1261 MessageDialog msg (_("Given playlist name is not unique."));
1263 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1271 if (name.length()) {
1273 tr->use_copy_playlist ();
1275 tr->use_new_playlist ();
1277 tr->playlist()->set_name (name);
1282 RouteTimeAxisView::clear_playlist ()
1284 boost::shared_ptr<Track> tr = track ();
1285 if (!tr || tr->destructive()) {
1289 boost::shared_ptr<Playlist> pl = tr->playlist();
1294 _editor.clear_playlist (pl);
1298 RouteTimeAxisView::speed_changed ()
1300 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1304 RouteTimeAxisView::update_diskstream_display ()
1314 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1316 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1318 /* special case: select/deselect all tracks */
1320 _editor.begin_reversible_selection_op (X_("Selection Click"));
1322 if (_editor.get_selection().selected (this)) {
1323 _editor.get_selection().clear_tracks ();
1325 _editor.select_all_tracks ();
1328 _editor.commit_reversible_selection_op ();
1333 _editor.begin_reversible_selection_op (X_("Selection Click"));
1335 switch (ArdourKeyboard::selection_type (ev->state)) {
1336 case Selection::Toggle:
1337 _editor.get_selection().toggle (this);
1340 case Selection::Set:
1341 _editor.get_selection().set (this);
1344 case Selection::Extend:
1345 _editor.extend_selection_to_track (*this);
1348 case Selection::Add:
1349 _editor.get_selection().add (this);
1353 _editor.commit_reversible_selection_op ();
1357 RouteTimeAxisView::set_selected_points (PointSelection& points)
1359 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1360 (*i)->set_selected_points (points);
1362 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1364 asv->set_selected_points (points);
1369 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1372 _view->set_selected_regionviews (regions);
1376 /** Add the selectable things that we have to a list.
1377 * @param results List to add things to.
1380 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1385 speed = track()->speed();
1388 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1389 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1391 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1392 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1395 /* pick up visible automation tracks */
1397 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1398 if (!(*i)->hidden()) {
1399 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1405 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1408 _view->get_inverted_selectables (sel, results);
1411 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1412 if (!(*i)->hidden()) {
1413 (*i)->get_inverted_selectables (sel, results);
1421 RouteTimeAxisView::route_group () const
1423 return _route->route_group();
1426 boost::shared_ptr<Playlist>
1427 RouteTimeAxisView::playlist () const
1429 boost::shared_ptr<Track> tr;
1431 if ((tr = track()) != 0) {
1432 return tr->playlist();
1434 return boost::shared_ptr<Playlist> ();
1439 RouteTimeAxisView::name_entry_changed (string const& str)
1441 if (str == _route->name()) {
1447 strip_whitespace_edges (x);
1453 if (_session->route_name_internal (x)) {
1454 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1456 } else if (RouteUI::verify_new_route_name (x)) {
1457 _route->set_name (x);
1464 boost::shared_ptr<Region>
1465 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1467 boost::shared_ptr<Playlist> pl = playlist ();
1470 return pl->find_next_region (pos, point, dir);
1473 return boost::shared_ptr<Region> ();
1477 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1479 boost::shared_ptr<Playlist> pl = playlist ();
1482 return pl->find_next_region_boundary (pos, dir);
1489 RouteTimeAxisView::fade_range (TimeSelection& selection)
1491 boost::shared_ptr<Playlist> what_we_got;
1492 boost::shared_ptr<Track> tr = track ();
1493 boost::shared_ptr<Playlist> playlist;
1496 /* route is a bus, not a track */
1500 playlist = tr->playlist();
1502 TimeSelection time (selection);
1503 float const speed = tr->speed();
1504 if (speed != 1.0f) {
1505 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1506 (*i).start = session_frame_to_track_frame((*i).start, speed);
1507 (*i).end = session_frame_to_track_frame((*i).end, speed);
1511 playlist->clear_changes ();
1512 playlist->clear_owned_changes ();
1514 playlist->fade_range (time);
1516 vector<Command*> cmds;
1517 playlist->rdiff (cmds);
1518 _session->add_commands (cmds);
1519 _session->add_command (new StatefulDiffCommand (playlist));
1524 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1526 boost::shared_ptr<Playlist> what_we_got;
1527 boost::shared_ptr<Track> tr = track ();
1528 boost::shared_ptr<Playlist> playlist;
1531 /* route is a bus, not a track */
1535 playlist = tr->playlist();
1537 TimeSelection time (selection.time);
1538 float const speed = tr->speed();
1539 if (speed != 1.0f) {
1540 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1541 (*i).start = session_frame_to_track_frame((*i).start, speed);
1542 (*i).end = session_frame_to_track_frame((*i).end, speed);
1546 playlist->clear_changes ();
1547 playlist->clear_owned_changes ();
1551 if (playlist->cut (time) != 0) {
1552 if (Config->get_edit_mode() == Ripple)
1553 playlist->ripple(time.start(), -time.length(), NULL);
1554 // no need to exclude any regions from rippling here
1556 vector<Command*> cmds;
1557 playlist->rdiff (cmds);
1558 _session->add_commands (cmds);
1560 _session->add_command (new StatefulDiffCommand (playlist));
1565 if ((what_we_got = playlist->cut (time)) != 0) {
1566 _editor.get_cut_buffer().add (what_we_got);
1567 if (Config->get_edit_mode() == Ripple)
1568 playlist->ripple(time.start(), -time.length(), NULL);
1569 // no need to exclude any regions from rippling here
1571 vector<Command*> cmds;
1572 playlist->rdiff (cmds);
1573 _session->add_commands (cmds);
1575 _session->add_command (new StatefulDiffCommand (playlist));
1579 if ((what_we_got = playlist->copy (time)) != 0) {
1580 _editor.get_cut_buffer().add (what_we_got);
1585 if ((what_we_got = playlist->cut (time)) != 0) {
1586 if (Config->get_edit_mode() == Ripple)
1587 playlist->ripple(time.start(), -time.length(), NULL);
1588 // no need to exclude any regions from rippling here
1590 vector<Command*> cmds;
1591 playlist->rdiff (cmds);
1592 _session->add_commands (cmds);
1593 _session->add_command (new StatefulDiffCommand (playlist));
1594 what_we_got->release ();
1601 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1607 boost::shared_ptr<Playlist> pl = playlist ();
1608 const ARDOUR::DataType type = pl->data_type();
1609 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1611 if (p == selection.playlists.end()) {
1614 ctx.counts.increase_n_playlists(type);
1616 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1618 if (track()->speed() != 1.0f) {
1619 pos = session_frame_to_track_frame (pos, track()->speed());
1620 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1623 /* add multi-paste offset if applicable */
1624 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1625 const framecnt_t duration = extent.second - extent.first;
1626 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1628 pl->clear_changes ();
1629 pl->clear_owned_changes ();
1630 if (Config->get_edit_mode() == Ripple) {
1631 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1632 framecnt_t amount = extent.second - extent.first;
1633 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1635 pl->paste (*p, pos, ctx.times, sub_num);
1637 vector<Command*> cmds;
1639 _session->add_commands (cmds);
1641 _session->add_command (new StatefulDiffCommand (pl));
1647 struct PlaylistSorter {
1648 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1649 return a->sort_id() < b->sort_id();
1654 RouteTimeAxisView::build_playlist_menu ()
1656 using namespace Menu_Helpers;
1662 delete playlist_action_menu;
1663 playlist_action_menu = new Menu;
1664 playlist_action_menu->set_name ("ArdourContextMenu");
1666 MenuList& playlist_items = playlist_action_menu->items();
1667 playlist_action_menu->set_name ("ArdourContextMenu");
1668 playlist_items.clear();
1670 RadioMenuItem::Group playlist_group;
1671 boost::shared_ptr<Track> tr = track ();
1673 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1675 /* sort the playlists */
1677 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1679 /* add the playlists to the menu */
1680 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1681 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1682 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1683 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1685 if (tr->playlist()->id() == (*i)->id()) {
1691 playlist_items.push_back (SeparatorElem());
1692 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1693 playlist_items.push_back (SeparatorElem());
1695 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1696 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1697 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1700 // Use a label which tells the user what is happening
1701 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1702 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1706 playlist_items.push_back (SeparatorElem());
1707 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1708 playlist_items.push_back (SeparatorElem());
1710 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1714 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1716 assert (is_track());
1718 // exit if we were triggered by deactivating the old playlist
1719 if (!item->get_active()) {
1723 boost::shared_ptr<Playlist> pl (wpl.lock());
1729 if (track()->playlist() == pl) {
1730 // exit when use_playlist is called by the creation of the playlist menu
1731 // or the playlist choice is unchanged
1735 track()->use_playlist (pl);
1737 RouteGroup* rg = route_group();
1739 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1740 std::string group_string = "." + rg->name() + ".";
1742 std::string take_name = pl->name();
1743 std::string::size_type idx = take_name.find(group_string);
1745 if (idx == std::string::npos)
1748 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1750 boost::shared_ptr<RouteList> rl (rg->route_list());
1752 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1753 if ((*i) == this->route()) {
1757 std::string playlist_name = (*i)->name()+group_string+take_name;
1759 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1764 if (track->freeze_state() == Track::Frozen) {
1765 /* Don't change playlists of frozen tracks */
1769 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1771 // No playlist for this track for this take yet, make it
1772 track->use_new_playlist();
1773 track->playlist()->set_name(playlist_name);
1775 track->use_playlist(ipl);
1782 RouteTimeAxisView::update_playlist_tip ()
1784 RouteGroup* rg = route_group ();
1785 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1786 string group_string = "." + rg->name() + ".";
1788 string take_name = track()->playlist()->name();
1789 string::size_type idx = take_name.find(group_string);
1791 if (idx != string::npos) {
1792 /* find the bit containing the take number / name */
1793 take_name = take_name.substr (idx + group_string.length());
1795 /* set the playlist button tooltip to the take name */
1798 string_compose(_("Take: %1.%2"),
1799 Gtkmm2ext::markup_escape_text (rg->name()),
1800 Gtkmm2ext::markup_escape_text (take_name))
1807 /* set the playlist button tooltip to the playlist name */
1808 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1813 RouteTimeAxisView::show_playlist_selector ()
1815 _editor.playlist_selector().show_for (this);
1819 RouteTimeAxisView::map_frozen ()
1825 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1827 switch (track()->freeze_state()) {
1829 playlist_button.set_sensitive (false);
1832 playlist_button.set_sensitive (true);
1835 RouteUI::map_frozen ();
1839 RouteTimeAxisView::color_handler ()
1841 //case cTimeStretchOutline:
1842 if (timestretch_rect) {
1843 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1845 //case cTimeStretchFill:
1846 if (timestretch_rect) {
1847 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1853 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1854 * Will add track if necessary.
1857 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1859 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1860 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1863 /* it doesn't exist yet, so we don't care about the button state: just add it */
1864 create_automation_child (param, true);
1867 bool yn = menu->get_active();
1868 bool changed = false;
1870 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1872 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1873 will have done that for us.
1876 if (changed && !no_redraw) {
1884 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1886 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1892 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1894 if (menu && !_hidden) {
1895 ignore_toggle = true;
1896 menu->set_active (false);
1897 ignore_toggle = false;
1900 if (_route && !no_redraw) {
1906 RouteTimeAxisView::update_gain_track_visibility ()
1908 bool const showit = gain_automation_item->get_active();
1911 if (gain_track->get_gui_property ("visible", visible) && visible != showit) {
1912 gain_track->set_marked_for_display (showit);
1914 /* now trigger a redisplay */
1917 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1923 RouteTimeAxisView::update_trim_track_visibility ()
1925 bool const showit = trim_automation_item->get_active();
1928 if (trim_track->get_gui_property ("visible", visible) && visible != showit) {
1929 trim_track->set_marked_for_display (showit);
1931 /* now trigger a redisplay */
1934 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1940 RouteTimeAxisView::update_mute_track_visibility ()
1942 bool const showit = mute_automation_item->get_active();
1945 if (mute_track->get_gui_property ("visible", visible) && visible != showit) {
1946 mute_track->set_marked_for_display (showit);
1948 /* now trigger a redisplay */
1951 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1957 RouteTimeAxisView::update_pan_track_visibility ()
1959 bool const showit = pan_automation_item->get_active();
1960 bool changed = false;
1962 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1963 if ((*i)->set_marked_for_display (showit)) {
1969 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1974 RouteTimeAxisView::ensure_pan_views (bool show)
1976 bool changed = false;
1977 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1979 (*i)->set_marked_for_display (false);
1982 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1986 if (!_route->panner()) {
1990 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1991 set<Evoral::Parameter>::iterator p;
1993 for (p = params.begin(); p != params.end(); ++p) {
1994 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1996 if (pan_control->parameter().type() == NullAutomation) {
1997 error << "Pan control has NULL automation type!" << endmsg;
2001 if (automation_child (pan_control->parameter ()).get () == 0) {
2003 /* we don't already have an AutomationTimeAxisView for this parameter */
2005 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2007 boost::shared_ptr<AutomationTimeAxisView> t (
2008 new AutomationTimeAxisView (_session,
2012 pan_control->parameter (),
2020 pan_tracks.push_back (t);
2021 add_automation_child (*p, t, show);
2023 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2030 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2032 if (apply_to_selection) {
2033 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2037 /* Show our automation */
2039 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2040 i->second->set_marked_for_display (true);
2042 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2045 menu->set_active(true);
2050 /* Show processor automation */
2052 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2053 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2054 if ((*ii)->view == 0) {
2055 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2058 (*ii)->menu_item->set_active (true);
2071 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2073 if (apply_to_selection) {
2074 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2078 /* Show our automation */
2080 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2081 if (i->second->has_automation()) {
2082 i->second->set_marked_for_display (true);
2084 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2086 menu->set_active(true);
2091 /* Show processor automation */
2093 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2094 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2095 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2096 (*ii)->menu_item->set_active (true);
2108 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2110 if (apply_to_selection) {
2111 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2115 /* Hide our automation */
2117 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2118 i->second->set_marked_for_display (false);
2120 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2123 menu->set_active (false);
2127 /* Hide processor automation */
2129 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2130 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2131 (*ii)->menu_item->set_active (false);
2142 RouteTimeAxisView::region_view_added (RegionView* rv)
2144 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2145 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2146 boost::shared_ptr<AutomationTimeAxisView> atv;
2148 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2153 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2154 (*i)->add_ghost(rv);
2158 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2160 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2166 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2168 parent.remove_processor_automation_node (this);
2172 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2175 remove_child (pan->view);
2179 RouteTimeAxisView::ProcessorAutomationNode*
2180 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2182 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2184 if ((*i)->processor == processor) {
2186 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2187 if ((*ii)->what == what) {
2197 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2199 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2202 ProcessorAutomationNode* pan;
2204 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2205 /* session state may never have been saved with new plugin */
2206 error << _("programming error: ")
2207 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2208 processor->name(), what.type(), (int) what.channel(), what.id() )
2210 abort(); /*NOTREACHED*/
2218 boost::shared_ptr<AutomationControl> control
2219 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2221 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2222 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2223 _editor, *this, false, parent_canvas,
2224 processor->describe_parameter (what), processor->name()));
2226 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2228 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2231 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2236 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2239 pan->menu_item->set_active (false);
2248 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2250 boost::shared_ptr<Processor> processor (p.lock ());
2252 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2253 /* The Amp processor is a special case and is dealt with separately */
2257 set<Evoral::Parameter> existing;
2259 processor->what_has_data (existing);
2261 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2263 Evoral::Parameter param (*i);
2264 boost::shared_ptr<AutomationLine> al;
2266 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2269 add_processor_automation_curve (processor, param);
2275 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2277 using namespace Menu_Helpers;
2281 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2283 _automation_tracks[param] = track;
2285 /* existing state overrides "show" argument */
2287 if (track->get_gui_property ("visible", visible)) {
2291 /* this might or might not change the visibility status, so don't rely on it */
2292 track->set_marked_for_display (show);
2294 if (show && !no_redraw) {
2298 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2299 /* MIDI-related parameters are always in the menu, there's no
2300 reason to rebuild the menu just because we added a automation
2301 lane for one of them. But if we add a non-MIDI automation
2302 lane, then we need to invalidate the display menu.
2304 delete display_menu;
2310 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2312 boost::shared_ptr<Processor> processor (p.lock ());
2314 if (!processor || !processor->display_to_user ()) {
2318 /* we use this override to veto the Amp processor from the plugin menu,
2319 as its automation lane can be accessed using the special "Fader" menu
2323 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2327 using namespace Menu_Helpers;
2328 ProcessorAutomationInfo *rai;
2329 list<ProcessorAutomationInfo*>::iterator x;
2331 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2333 if (automatable.empty()) {
2337 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2338 if ((*x)->processor == processor) {
2343 if (x == processor_automation.end()) {
2344 rai = new ProcessorAutomationInfo (processor);
2345 processor_automation.push_back (rai);
2350 /* any older menu was deleted at the top of processors_changed()
2351 when we cleared the subplugin menu.
2354 rai->menu = manage (new Menu);
2355 MenuList& items = rai->menu->items();
2356 rai->menu->set_name ("ArdourContextMenu");
2360 std::set<Evoral::Parameter> has_visible_automation;
2361 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2363 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2365 ProcessorAutomationNode* pan;
2366 Gtk::CheckMenuItem* mitem;
2368 string name = processor->describe_parameter (*i);
2370 if (name == X_("hidden")) {
2374 items.push_back (CheckMenuElem (name));
2375 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2377 _subplugin_menu_map[*i] = mitem;
2379 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2380 mitem->set_active(true);
2383 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2387 pan = new ProcessorAutomationNode (*i, mitem, *this);
2389 rai->lines.push_back (pan);
2393 pan->menu_item = mitem;
2397 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2400 if (items.size() == 0) {
2404 /* add the menu for this processor, because the subplugin
2405 menu is always cleared at the top of processors_changed().
2406 this is the result of some poor design in gtkmm and/or
2410 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2415 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2416 RouteTimeAxisView::ProcessorAutomationNode* pan)
2418 bool showit = pan->menu_item->get_active();
2419 bool redraw = false;
2421 if (pan->view == 0 && showit) {
2422 add_processor_automation_curve (rai->processor, pan->what);
2426 if (pan->view && pan->view->set_marked_for_display (showit)) {
2430 if (redraw && !no_redraw) {
2436 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2438 if (c.type == RouteProcessorChange::MeterPointChange) {
2439 /* nothing to do if only the meter point has changed */
2443 using namespace Menu_Helpers;
2445 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2446 (*i)->valid = false;
2449 setup_processor_menu_and_curves ();
2451 bool deleted_processor_automation = false;
2453 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2455 list<ProcessorAutomationInfo*>::iterator tmp;
2463 processor_automation.erase (i);
2464 deleted_processor_automation = true;
2471 if (deleted_processor_automation && !no_redraw) {
2476 boost::shared_ptr<AutomationLine>
2477 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2479 ProcessorAutomationNode* pan;
2481 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2487 return boost::shared_ptr<AutomationLine>();
2491 RouteTimeAxisView::reset_processor_automation_curves ()
2493 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2499 RouteTimeAxisView::can_edit_name () const
2501 /* we do not allow track name changes if it is record enabled
2503 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2507 return !trk->rec_enable_control()->get_value();
2511 RouteTimeAxisView::blink_rec_display (bool onoff)
2513 RouteUI::blink_rec_display (onoff);
2517 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2519 if (_ignore_set_layer_display) {
2523 if (apply_to_selection) {
2524 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2528 _view->set_layer_display (d);
2531 set_gui_property (X_("layer-display"), d);
2536 RouteTimeAxisView::layer_display () const
2539 return _view->layer_display ();
2542 /* we don't know, since we don't have a _view, so just return something */
2548 boost::shared_ptr<AutomationTimeAxisView>
2549 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2551 AutomationTracks::iterator i = _automation_tracks.find(param);
2552 if (i != _automation_tracks.end()) {
2555 return boost::shared_ptr<AutomationTimeAxisView>();
2560 RouteTimeAxisView::fast_update ()
2562 gm.get_level_meter().update_meters ();
2566 RouteTimeAxisView::hide_meter ()
2569 gm.get_level_meter().hide_meters ();
2573 RouteTimeAxisView::show_meter ()
2579 RouteTimeAxisView::reset_meter ()
2581 if (UIConfiguration::instance().get_show_track_meters()) {
2582 int meter_width = 3;
2583 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2586 gm.get_level_meter().setup_meters (height - 9, meter_width);
2593 RouteTimeAxisView::clear_meter ()
2595 gm.get_level_meter().clear_meters ();
2599 RouteTimeAxisView::meter_changed ()
2601 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2603 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2606 // reset peak when meter point changes
2607 gm.reset_peak_display();
2611 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2614 if (_route && !no_redraw) {
2620 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2622 using namespace Menu_Helpers;
2624 if (!_underlay_streams.empty()) {
2625 MenuList& parent_items = parent_menu->items();
2626 Menu* gs_menu = manage (new Menu);
2627 gs_menu->set_name ("ArdourContextMenu");
2628 MenuList& gs_items = gs_menu->items();
2630 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2632 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2633 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2634 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2640 RouteTimeAxisView::set_underlay_state()
2642 if (!underlay_xml_node) {
2646 XMLNodeList nlist = underlay_xml_node->children();
2647 XMLNodeConstIterator niter;
2648 XMLNode *child_node;
2650 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2651 child_node = *niter;
2653 if (child_node->name() != "Underlay") {
2657 XMLProperty const * prop = child_node->property ("id");
2659 PBD::ID id (prop->value());
2661 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2664 add_underlay(v->view(), false);
2673 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2679 RouteTimeAxisView& other = v->trackview();
2681 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2682 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2683 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2684 abort(); /*NOTREACHED*/
2687 _underlay_streams.push_back(v);
2688 other._underlay_mirrors.push_back(this);
2690 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2692 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2694 if (!underlay_xml_node) {
2695 underlay_xml_node = xml_node->add_child("Underlays");
2698 XMLNode* node = underlay_xml_node->add_child("Underlay");
2699 XMLProperty const * prop = node->add_property("id");
2700 prop->set_value(v->trackview().route()->id().to_s());
2707 RouteTimeAxisView::remove_underlay (StreamView* v)
2713 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2714 RouteTimeAxisView& other = v->trackview();
2716 if (it != _underlay_streams.end()) {
2717 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2719 if (gm == other._underlay_mirrors.end()) {
2720 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2721 abort(); /*NOTREACHED*/
2724 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2726 _underlay_streams.erase(it);
2727 other._underlay_mirrors.erase(gm);
2729 if (underlay_xml_node) {
2730 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2736 RouteTimeAxisView::set_button_names ()
2738 if (_route && _route->solo_safe_control()->solo_safe()) {
2739 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2741 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2743 if (Config->get_solo_control_is_listen_control()) {
2744 switch (Config->get_listen_position()) {
2745 case AfterFaderListen:
2746 solo_button->set_text (S_("AfterFader|A"));
2747 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2749 case PreFaderListen:
2750 solo_button->set_text (S_("PreFader|P"));
2751 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2755 solo_button->set_text (S_("Solo|S"));
2756 set_tooltip (*solo_button, _("Solo"));
2758 mute_button->set_text (S_("Mute|M"));
2762 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2764 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2765 if (i != _main_automation_menu_map.end()) {
2769 i = _subplugin_menu_map.find (param);
2770 if (i != _subplugin_menu_map.end()) {
2778 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2780 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2782 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2786 gain_track.reset (new AutomationTimeAxisView (_session,
2787 _route, _route->amp(), c, param,
2792 _route->amp()->describe_parameter(param)));
2795 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2798 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2802 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2804 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2805 if (!c || ! _route->trim()->active()) {
2809 trim_track.reset (new AutomationTimeAxisView (_session,
2810 _route, _route->trim(), c, param,
2815 _route->trim()->describe_parameter(param)));
2818 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2821 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2825 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2827 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2829 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2833 mute_track.reset (new AutomationTimeAxisView (_session,
2834 _route, _route, c, param,
2839 _route->describe_parameter(param)));
2842 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2845 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2849 void add_region_to_list (RegionView* rv, RegionList* l)
2851 l->push_back (rv->region());
2855 RouteTimeAxisView::combine_regions ()
2857 /* as of may 2011, we do not offer uncombine for MIDI tracks
2860 if (!is_audio_track()) {
2868 RegionList selected_regions;
2869 boost::shared_ptr<Playlist> playlist = track()->playlist();
2871 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2873 if (selected_regions.size() < 2) {
2877 playlist->clear_changes ();
2878 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2880 _session->add_command (new StatefulDiffCommand (playlist));
2881 /* make the new region be selected */
2883 return _view->find_view (compound_region);
2887 RouteTimeAxisView::uncombine_regions ()
2889 /* as of may 2011, we do not offer uncombine for MIDI tracks
2891 if (!is_audio_track()) {
2899 RegionList selected_regions;
2900 boost::shared_ptr<Playlist> playlist = track()->playlist();
2902 /* have to grab selected regions first because the uncombine is going
2903 * to change that in the middle of the list traverse
2906 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2908 playlist->clear_changes ();
2910 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2911 playlist->uncombine (*i);
2914 _session->add_command (new StatefulDiffCommand (playlist));
2918 RouteTimeAxisView::state_id() const
2920 return string_compose ("rtav %1", _route->id().to_s());
2925 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2927 TimeAxisView::remove_child (c);
2929 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2931 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2932 if (i->second == a) {
2933 _automation_tracks.erase (i);
2941 RouteTimeAxisView::color () const
2943 return route_color ();
2947 RouteTimeAxisView::marked_for_display () const
2949 return !_route->presentation_info().hidden();
2953 RouteTimeAxisView::set_marked_for_display (bool yn)
2955 return RouteUI::mark_hidden (!yn);