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 if (_route->is_master()) {
224 route_group_button.set_sensitive(false);
227 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
228 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
229 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
230 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
232 if (ARDOUR::Profile->get_mixbus()) {
233 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
235 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
237 // mute button is always present, it is used to
238 // force the 'blank' placeholders to the proper size
239 controls_button_size_group->add_widget(*mute_button);
241 if (!_route->is_master()) {
242 if (ARDOUR::Profile->get_mixbus()) {
243 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
245 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 Gtk::Fixed *blank = manage(new Gtk::Fixed());
249 controls_button_size_group->add_widget(*blank);
250 if (ARDOUR::Profile->get_mixbus()) {
251 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
253 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
258 if (ARDOUR::Profile->get_mixbus()) {
259 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
262 else if (!ARDOUR::Profile->get_trx()) {
263 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
264 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
267 set_tooltip(*solo_button,_("Solo"));
268 set_tooltip(*mute_button,_("Mute"));
269 set_tooltip(route_group_button, _("Route Group"));
271 mute_button->set_tweaks(ArdourButton::TrackHeader);
272 solo_button->set_tweaks(ArdourButton::TrackHeader);
273 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
274 playlist_button.set_tweaks(ArdourButton::TrackHeader);
275 automation_button.set_tweaks(ArdourButton::TrackHeader);
276 route_group_button.set_tweaks(ArdourButton::TrackHeader);
278 if (is_midi_track()) {
279 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
281 set_tooltip(automation_button, _("Automation"));
284 update_track_number_visibility();
287 if (ARDOUR::Profile->get_mixbus()) {
288 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 else if (!ARDOUR::Profile->get_trx()) {
291 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 if (is_track() && track()->mode() == ARDOUR::Normal) {
295 if (ARDOUR::Profile->get_mixbus()) {
296 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
298 else if (!ARDOUR::Profile->get_trx()) {
299 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
305 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
309 str = gui_property ("layer-display");
311 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
314 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
315 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
317 /* pick up the correct freeze state */
322 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
323 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
325 PropertyList* plist = new PropertyList();
327 plist->add (ARDOUR::Properties::group_mute, true);
328 plist->add (ARDOUR::Properties::group_solo, true);
330 route_group_menu = new RouteGroupMenu (_session, plist);
332 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
335 RouteTimeAxisView::~RouteTimeAxisView ()
337 cleanup_gui_properties ();
339 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
343 delete playlist_action_menu;
344 playlist_action_menu = 0;
349 _automation_tracks.clear ();
351 delete route_group_menu;
352 CatchDeletion (this);
356 RouteTimeAxisView::name() const
359 return _route->name();
365 RouteTimeAxisView::post_construct ()
367 /* map current state of the route */
369 update_diskstream_display ();
370 setup_processor_menu_and_curves ();
371 reset_processor_automation_curves ();
374 /** Set up the processor menu for the current set of processors, and
375 * display automation curves for any parameters which have data.
378 RouteTimeAxisView::setup_processor_menu_and_curves ()
380 _subplugin_menu_map.clear ();
381 subplugin_menu.items().clear ();
382 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
383 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
387 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
389 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
390 if (_route->route_group()) {
391 _route->route_group()->remove (_route);
397 r.push_back (route ());
399 route_group_menu->build (r);
400 if (ev->button == 1) {
401 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
405 route_group_menu->menu()->popup (ev->button, ev->time);
412 RouteTimeAxisView::playlist_changed ()
418 RouteTimeAxisView::label_view ()
420 string x = _route->name ();
421 if (x != name_label.get_text ()) {
422 name_label.set_text (x);
424 const int64_t track_number = _route->track_number ();
425 if (track_number == 0) {
426 number_label.set_text ("");
428 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
433 RouteTimeAxisView::update_track_number_visibility ()
436 bool show_label = _session->config.get_track_name_number();
438 if (_route && _route->is_master()) {
442 if (number_label.get_parent()) {
443 controls_table.remove (number_label);
446 if (ARDOUR::Profile->get_mixbus()) {
447 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
449 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
451 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
452 // except the width of the number label is subtracted from the name-hbox, so we
453 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
454 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
456 number_label.set_size_request(tnw, -1);
457 number_label.show ();
459 number_label.hide ();
464 RouteTimeAxisView::parameter_changed (string const & p)
466 if (p == "track-name-number") {
467 update_track_number_visibility();
468 } else if (p == "editor-stereo-only-meters") {
469 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
470 gm.get_level_meter().set_max_audio_meter_count (2);
472 gm.get_level_meter().set_max_audio_meter_count (0);
478 RouteTimeAxisView::take_name_changed (void *src)
486 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
488 if (ev->button != 1) {
492 build_playlist_menu ();
493 conditionally_add_to_selection ();
494 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
500 RouteTimeAxisView::automation_click (GdkEventButton *ev)
502 if (ev->button != 1) {
506 conditionally_add_to_selection ();
507 build_automation_action_menu (false);
508 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
514 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
516 using namespace Menu_Helpers;
518 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
519 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
522 detach_menu (subplugin_menu);
524 _main_automation_menu_map.clear ();
525 delete automation_action_menu;
526 automation_action_menu = new Menu;
528 MenuList& items = automation_action_menu->items();
530 automation_action_menu->set_name ("ArdourContextMenu");
532 items.push_back (MenuElem (_("Show All Automation"),
533 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
535 items.push_back (MenuElem (_("Show Existing Automation"),
536 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
538 items.push_back (MenuElem (_("Hide All Automation"),
539 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
541 /* Attach the plugin submenu. It may have previously been used elsewhere,
542 so it was detached above
545 if (!subplugin_menu.items().empty()) {
546 items.push_back (SeparatorElem ());
547 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
548 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
551 /* Add any route automation */
554 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
555 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
556 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
557 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
559 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
563 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
564 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
565 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
566 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
568 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
572 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
573 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
574 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
575 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
577 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
580 if (!pan_tracks.empty()) {
581 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
582 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
583 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
584 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
586 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
587 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
588 _main_automation_menu_map[*p] = pan_automation_item;
594 RouteTimeAxisView::build_display_menu ()
596 using namespace Menu_Helpers;
600 TimeAxisView::build_display_menu ();
602 /* now fill it with our stuff */
604 MenuList& items = display_menu->items();
605 display_menu->set_name ("ArdourContextMenu");
607 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
609 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
611 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
613 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
615 items.push_back (SeparatorElem());
618 detach_menu (*_size_menu);
621 items.push_back (MenuElem (_("Height"), *_size_menu));
622 items.push_back (SeparatorElem());
624 // Hook for derived classes to add type specific stuff
625 append_extra_display_menu_items ();
629 Menu* layers_menu = manage (new Menu);
630 MenuList &layers_items = layers_menu->items();
631 layers_menu->set_name("ArdourContextMenu");
633 RadioMenuItem::Group layers_group;
635 /* Find out how many overlaid/stacked tracks we have in the selection */
639 int unchangeable = 0;
640 TrackSelection const & s = _editor.get_selection().tracks;
642 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
643 StreamView* v = (*i)->view ();
648 if (v->can_change_layer_display()) {
649 switch (v->layer_display ()) {
663 /* We're not connecting to signal_toggled() here; in the case where these two items are
664 set to be in the `inconsistent' state, it seems that one or other will end up active
665 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
666 select the active one, no toggled signal is emitted so nothing happens.
669 _ignore_set_layer_display = true;
671 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
672 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
673 i->set_active (overlaid != 0 && stacked == 0);
674 i->set_inconsistent (overlaid != 0 && stacked != 0);
675 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
678 i->set_sensitive (false);
681 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
682 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
683 i->set_active (overlaid == 0 && stacked != 0);
684 i->set_inconsistent (overlaid != 0 && stacked != 0);
685 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
688 i->set_sensitive (false);
691 _ignore_set_layer_display = false;
693 items.push_back (MenuElem (_("Layers"), *layers_menu));
695 Menu* alignment_menu = manage (new Menu);
696 MenuList& alignment_items = alignment_menu->items();
697 alignment_menu->set_name ("ArdourContextMenu");
699 RadioMenuItem::Group align_group;
701 /* Same verbose hacks as for the layering options above */
707 boost::shared_ptr<Track> first_track;
709 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
710 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
711 if (!r || !r->is_track ()) {
716 first_track = r->track();
719 switch (r->track()->alignment_choice()) {
723 switch (r->track()->alignment_style()) {
724 case ExistingMaterial:
732 case UseExistingMaterial:
748 inconsistent = false;
755 if (!inconsistent && first_track) {
757 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
758 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
759 i->set_active (automatic != 0 && existing == 0 && capture == 0);
760 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
762 switch (first_track->alignment_choice()) {
764 switch (first_track->alignment_style()) {
765 case ExistingMaterial:
766 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
769 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
777 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
778 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
779 i->set_active (existing != 0 && capture == 0 && automatic == 0);
780 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
782 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
783 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
784 i->set_active (existing == 0 && capture != 0 && automatic == 0);
785 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
787 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
793 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
794 Menu* mode_menu = manage (new Menu);
795 MenuList& mode_items = mode_menu->items ();
796 mode_menu->set_name ("ArdourContextMenu");
798 RadioMenuItem::Group mode_group;
804 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
805 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
806 if (!r || !r->is_track ()) {
810 switch (r->track()->mode()) {
823 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
824 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
825 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
826 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
827 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
829 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
830 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
831 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
832 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
833 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
835 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
836 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
837 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
838 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
839 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
841 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
844 items.push_back (SeparatorElem());
846 build_playlist_menu ();
847 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
848 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
851 route_group_menu->detach ();
854 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
855 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
857 r.push_back (rtv->route ());
862 r.push_back (route ());
865 if (!_route->is_master()) {
866 route_group_menu->build (r);
867 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
870 build_automation_action_menu (true);
871 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
873 items.push_back (SeparatorElem());
877 TrackSelection const & s = _editor.get_selection().tracks;
878 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
879 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
884 if (r->route()->active()) {
891 items.push_back (CheckMenuElem (_("Active")));
892 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
893 bool click_sets_active = true;
894 if (active > 0 && inactive == 0) {
895 i->set_active (true);
896 click_sets_active = false;
897 } else if (active > 0 && inactive > 0) {
898 i->set_inconsistent (true);
900 i->set_sensitive(! _session->transport_rolling());
901 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
903 items.push_back (SeparatorElem());
904 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
905 if (_route && !_route->is_master()) {
906 items.push_back (SeparatorElem());
907 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
909 items.push_back (SeparatorElem());
910 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
913 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
915 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
917 if (apply_to_selection) {
918 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
921 bool needs_bounce = false;
923 if (!track()->can_use_mode (mode, needs_bounce)) {
929 cerr << "would bounce this one\n";
934 track()->set_mode (mode);
940 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
942 TimeAxisView::show_timestretch (start, end, layers, layer);
952 /* check that the time selection was made in our route, or our route group.
953 remember that route_group() == 0 implies the route is *not* in a edit group.
956 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
957 /* this doesn't apply to us */
961 /* ignore it if our edit group is not active */
963 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
968 if (timestretch_rect == 0) {
969 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
970 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
971 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
974 timestretch_rect->show ();
975 timestretch_rect->raise_to_top ();
977 double const x1 = start / _editor.get_current_zoom();
978 double const x2 = (end - 1) / _editor.get_current_zoom();
980 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
981 x2, current_height() * (layers - layer) / layers));
985 RouteTimeAxisView::hide_timestretch ()
987 TimeAxisView::hide_timestretch ();
989 if (timestretch_rect) {
990 timestretch_rect->hide ();
995 RouteTimeAxisView::show_selection (TimeSelection& ts)
999 /* ignore it if our edit group is not active or if the selection was started
1000 in some other track or route group (remember that route_group() == 0 means
1001 that the track is not in an route group).
1004 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
1005 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1011 TimeAxisView::show_selection (ts);
1015 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1018 bool height_changed = (height == 0) || (h != height);
1020 int meter_width = 3;
1021 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1024 gm.get_level_meter().setup_meters (gmlen, meter_width);
1026 TimeAxisView::set_height (h, m);
1029 _view->set_height ((double) current_height());
1032 if (height >= preset_height (HeightNormal)) {
1036 gm.get_gain_slider().show();
1037 mute_button->show();
1038 if (!_route || _route->is_monitor()) {
1039 solo_button->hide();
1041 solo_button->show();
1043 if (rec_enable_button)
1044 rec_enable_button->show();
1046 route_group_button.show();
1047 automation_button.show();
1049 if (is_track() && track()->mode() == ARDOUR::Normal) {
1050 playlist_button.show();
1057 gm.get_gain_slider().hide();
1058 mute_button->show();
1059 if (!_route || _route->is_monitor()) {
1060 solo_button->hide();
1062 solo_button->show();
1064 if (rec_enable_button)
1065 rec_enable_button->show();
1067 route_group_button.hide ();
1068 automation_button.hide ();
1070 if (is_track() && track()->mode() == ARDOUR::Normal) {
1071 playlist_button.hide ();
1076 if (height_changed && !no_redraw) {
1077 /* only emit the signal if the height really changed */
1083 RouteTimeAxisView::route_color_changed ()
1086 _view->apply_color (color(), StreamView::RegionColor);
1089 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1093 RouteTimeAxisView::reset_samples_per_pixel ()
1095 set_samples_per_pixel (_editor.get_current_zoom());
1099 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1104 speed = track()->speed();
1108 _view->set_samples_per_pixel (fpp * speed);
1111 TimeAxisView::set_samples_per_pixel (fpp * speed);
1115 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1117 if (!mitem->get_active()) {
1118 /* this is one of the two calls made when these radio menu items change status. this one
1119 is for the item that became inactive, and we want to ignore it.
1124 if (apply_to_selection) {
1125 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1128 track()->set_align_choice (choice);
1134 RouteTimeAxisView::rename_current_playlist ()
1136 ArdourPrompter prompter (true);
1139 boost::shared_ptr<Track> tr = track();
1140 if (!tr || tr->destructive()) {
1144 boost::shared_ptr<Playlist> pl = tr->playlist();
1149 prompter.set_title (_("Rename Playlist"));
1150 prompter.set_prompt (_("New name for playlist:"));
1151 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1152 prompter.set_initial_text (pl->name());
1153 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1156 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1159 prompter.get_result (name);
1160 if (name.length()) {
1161 if (_session->playlists->by_name (name)) {
1162 MessageDialog msg (_("Given playlist name is not unique."));
1164 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1166 pl->set_name (name);
1174 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1176 std::string ret (basename);
1178 std::string const group_string = "." + route_group()->name() + ".";
1180 // iterate through all playlists
1182 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1183 std::string tmp = (*i)->name();
1185 std::string::size_type idx = tmp.find(group_string);
1186 // find those which belong to this group
1187 if (idx != string::npos) {
1188 tmp = tmp.substr(idx + group_string.length());
1190 // and find the largest current number
1192 if (x > maxnumber) {
1201 snprintf (buf, sizeof(buf), "%d", maxnumber);
1203 ret = this->name() + "." + route_group()->name () + "." + buf;
1209 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1213 boost::shared_ptr<Track> tr = track ();
1214 if (!tr || tr->destructive()) {
1218 boost::shared_ptr<const Playlist> pl = tr->playlist();
1225 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1226 name = resolve_new_group_playlist_name(name,playlists_before_op);
1229 while (_session->playlists->by_name(name)) {
1230 name = Playlist::bump_name (name, *_session);
1234 // TODO: The prompter "new" button should be de-activated if the user
1235 // specifies a playlist name which already exists in the session.
1237 ArdourPrompter prompter (true);
1240 prompter.set_title (_("New Copy Playlist"));
1241 prompter.set_prompt (_("Name for playlist copy:"));
1243 prompter.set_title (_("New Playlist"));
1244 prompter.set_prompt (_("Name for new playlist:"));
1246 prompter.set_initial_text (name);
1247 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1248 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1249 prompter.show_all ();
1252 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1255 prompter.get_result (name);
1256 if (name.length()) {
1257 if (_session->playlists->by_name (name)) {
1258 MessageDialog msg (_("Given playlist name is not unique."));
1260 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1268 if (name.length()) {
1270 tr->use_copy_playlist ();
1272 tr->use_new_playlist ();
1274 tr->playlist()->set_name (name);
1279 RouteTimeAxisView::clear_playlist ()
1281 boost::shared_ptr<Track> tr = track ();
1282 if (!tr || tr->destructive()) {
1286 boost::shared_ptr<Playlist> pl = tr->playlist();
1291 _editor.clear_playlist (pl);
1295 RouteTimeAxisView::speed_changed ()
1297 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1301 RouteTimeAxisView::update_diskstream_display ()
1311 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1313 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1315 /* special case: select/deselect all tracks */
1317 _editor.begin_reversible_selection_op (X_("Selection Click"));
1319 if (_editor.get_selection().selected (this)) {
1320 _editor.get_selection().clear_tracks ();
1322 _editor.select_all_tracks ();
1325 _editor.commit_reversible_selection_op ();
1330 _editor.begin_reversible_selection_op (X_("Selection Click"));
1332 switch (ArdourKeyboard::selection_type (ev->state)) {
1333 case Selection::Toggle:
1334 _editor.get_selection().toggle (this);
1337 case Selection::Set:
1338 _editor.get_selection().set (this);
1341 case Selection::Extend:
1342 _editor.extend_selection_to_track (*this);
1345 case Selection::Add:
1346 _editor.get_selection().add (this);
1350 _editor.commit_reversible_selection_op ();
1354 RouteTimeAxisView::set_selected_points (PointSelection& points)
1356 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1357 (*i)->set_selected_points (points);
1359 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1361 asv->set_selected_points (points);
1366 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1369 _view->set_selected_regionviews (regions);
1373 /** Add the selectable things that we have to a list.
1374 * @param results List to add things to.
1377 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1382 speed = track()->speed();
1385 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1386 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1388 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1389 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1392 /* pick up visible automation tracks */
1394 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1395 if (!(*i)->hidden()) {
1396 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1402 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1405 _view->get_inverted_selectables (sel, results);
1408 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1409 if (!(*i)->hidden()) {
1410 (*i)->get_inverted_selectables (sel, results);
1418 RouteTimeAxisView::route_group () const
1420 return _route->route_group();
1423 boost::shared_ptr<Playlist>
1424 RouteTimeAxisView::playlist () const
1426 boost::shared_ptr<Track> tr;
1428 if ((tr = track()) != 0) {
1429 return tr->playlist();
1431 return boost::shared_ptr<Playlist> ();
1436 RouteTimeAxisView::name_entry_changed (string const& str)
1438 if (str == _route->name()) {
1444 strip_whitespace_edges (x);
1450 if (_session->route_name_internal (x)) {
1451 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1453 } else if (RouteUI::verify_new_route_name (x)) {
1454 _route->set_name (x);
1461 boost::shared_ptr<Region>
1462 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1464 boost::shared_ptr<Playlist> pl = playlist ();
1467 return pl->find_next_region (pos, point, dir);
1470 return boost::shared_ptr<Region> ();
1474 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1476 boost::shared_ptr<Playlist> pl = playlist ();
1479 return pl->find_next_region_boundary (pos, dir);
1486 RouteTimeAxisView::fade_range (TimeSelection& selection)
1488 boost::shared_ptr<Playlist> what_we_got;
1489 boost::shared_ptr<Track> tr = track ();
1490 boost::shared_ptr<Playlist> playlist;
1493 /* route is a bus, not a track */
1497 playlist = tr->playlist();
1499 TimeSelection time (selection);
1500 float const speed = tr->speed();
1501 if (speed != 1.0f) {
1502 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1503 (*i).start = session_frame_to_track_frame((*i).start, speed);
1504 (*i).end = session_frame_to_track_frame((*i).end, speed);
1508 playlist->clear_changes ();
1509 playlist->clear_owned_changes ();
1511 playlist->fade_range (time);
1513 vector<Command*> cmds;
1514 playlist->rdiff (cmds);
1515 _session->add_commands (cmds);
1516 _session->add_command (new StatefulDiffCommand (playlist));
1521 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1523 boost::shared_ptr<Playlist> what_we_got;
1524 boost::shared_ptr<Track> tr = track ();
1525 boost::shared_ptr<Playlist> playlist;
1528 /* route is a bus, not a track */
1532 playlist = tr->playlist();
1534 TimeSelection time (selection.time);
1535 float const speed = tr->speed();
1536 if (speed != 1.0f) {
1537 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1538 (*i).start = session_frame_to_track_frame((*i).start, speed);
1539 (*i).end = session_frame_to_track_frame((*i).end, speed);
1543 playlist->clear_changes ();
1544 playlist->clear_owned_changes ();
1548 if (playlist->cut (time) != 0) {
1549 if (Config->get_edit_mode() == Ripple)
1550 playlist->ripple(time.start(), -time.length(), NULL);
1551 // no need to exclude any regions from rippling here
1553 vector<Command*> cmds;
1554 playlist->rdiff (cmds);
1555 _session->add_commands (cmds);
1557 _session->add_command (new StatefulDiffCommand (playlist));
1562 if ((what_we_got = playlist->cut (time)) != 0) {
1563 _editor.get_cut_buffer().add (what_we_got);
1564 if (Config->get_edit_mode() == Ripple)
1565 playlist->ripple(time.start(), -time.length(), NULL);
1566 // no need to exclude any regions from rippling here
1568 vector<Command*> cmds;
1569 playlist->rdiff (cmds);
1570 _session->add_commands (cmds);
1572 _session->add_command (new StatefulDiffCommand (playlist));
1576 if ((what_we_got = playlist->copy (time)) != 0) {
1577 _editor.get_cut_buffer().add (what_we_got);
1582 if ((what_we_got = playlist->cut (time)) != 0) {
1583 if (Config->get_edit_mode() == Ripple)
1584 playlist->ripple(time.start(), -time.length(), NULL);
1585 // no need to exclude any regions from rippling here
1587 vector<Command*> cmds;
1588 playlist->rdiff (cmds);
1589 _session->add_commands (cmds);
1590 _session->add_command (new StatefulDiffCommand (playlist));
1591 what_we_got->release ();
1598 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1604 boost::shared_ptr<Playlist> pl = playlist ();
1605 const ARDOUR::DataType type = pl->data_type();
1606 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1608 if (p == selection.playlists.end()) {
1611 ctx.counts.increase_n_playlists(type);
1613 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1615 if (track()->speed() != 1.0f) {
1616 pos = session_frame_to_track_frame (pos, track()->speed());
1617 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1620 /* add multi-paste offset if applicable */
1621 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1622 const framecnt_t duration = extent.second - extent.first;
1623 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1625 pl->clear_changes ();
1626 pl->clear_owned_changes ();
1627 if (Config->get_edit_mode() == Ripple) {
1628 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1629 framecnt_t amount = extent.second - extent.first;
1630 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1632 pl->paste (*p, pos, ctx.times, sub_num);
1634 vector<Command*> cmds;
1636 _session->add_commands (cmds);
1638 _session->add_command (new StatefulDiffCommand (pl));
1644 struct PlaylistSorter {
1645 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1646 return a->sort_id() < b->sort_id();
1651 RouteTimeAxisView::build_playlist_menu ()
1653 using namespace Menu_Helpers;
1659 delete playlist_action_menu;
1660 playlist_action_menu = new Menu;
1661 playlist_action_menu->set_name ("ArdourContextMenu");
1663 MenuList& playlist_items = playlist_action_menu->items();
1664 playlist_action_menu->set_name ("ArdourContextMenu");
1665 playlist_items.clear();
1667 RadioMenuItem::Group playlist_group;
1668 boost::shared_ptr<Track> tr = track ();
1670 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1672 /* sort the playlists */
1674 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1676 /* add the playlists to the menu */
1677 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1678 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1679 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1680 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1682 if (tr->playlist()->id() == (*i)->id()) {
1688 playlist_items.push_back (SeparatorElem());
1689 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1690 playlist_items.push_back (SeparatorElem());
1692 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1693 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1694 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1697 // Use a label which tells the user what is happening
1698 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1699 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1703 playlist_items.push_back (SeparatorElem());
1704 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1705 playlist_items.push_back (SeparatorElem());
1707 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1711 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1713 assert (is_track());
1715 // exit if we were triggered by deactivating the old playlist
1716 if (!item->get_active()) {
1720 boost::shared_ptr<Playlist> pl (wpl.lock());
1726 if (track()->playlist() == pl) {
1727 // exit when use_playlist is called by the creation of the playlist menu
1728 // or the playlist choice is unchanged
1732 track()->use_playlist (pl);
1734 RouteGroup* rg = route_group();
1736 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1737 std::string group_string = "." + rg->name() + ".";
1739 std::string take_name = pl->name();
1740 std::string::size_type idx = take_name.find(group_string);
1742 if (idx == std::string::npos)
1745 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1747 boost::shared_ptr<RouteList> rl (rg->route_list());
1749 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1750 if ((*i) == this->route()) {
1754 std::string playlist_name = (*i)->name()+group_string+take_name;
1756 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1761 if (track->freeze_state() == Track::Frozen) {
1762 /* Don't change playlists of frozen tracks */
1766 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1768 // No playlist for this track for this take yet, make it
1769 track->use_new_playlist();
1770 track->playlist()->set_name(playlist_name);
1772 track->use_playlist(ipl);
1779 RouteTimeAxisView::update_playlist_tip ()
1781 RouteGroup* rg = route_group ();
1782 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1783 string group_string = "." + rg->name() + ".";
1785 string take_name = track()->playlist()->name();
1786 string::size_type idx = take_name.find(group_string);
1788 if (idx != string::npos) {
1789 /* find the bit containing the take number / name */
1790 take_name = take_name.substr (idx + group_string.length());
1792 /* set the playlist button tooltip to the take name */
1795 string_compose(_("Take: %1.%2"),
1796 Gtkmm2ext::markup_escape_text (rg->name()),
1797 Gtkmm2ext::markup_escape_text (take_name))
1804 /* set the playlist button tooltip to the playlist name */
1805 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1810 RouteTimeAxisView::show_playlist_selector ()
1812 _editor.playlist_selector().show_for (this);
1816 RouteTimeAxisView::map_frozen ()
1822 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1824 switch (track()->freeze_state()) {
1826 playlist_button.set_sensitive (false);
1829 playlist_button.set_sensitive (true);
1832 RouteUI::map_frozen ();
1836 RouteTimeAxisView::color_handler ()
1838 //case cTimeStretchOutline:
1839 if (timestretch_rect) {
1840 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1842 //case cTimeStretchFill:
1843 if (timestretch_rect) {
1844 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1850 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1851 * Will add track if necessary.
1854 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1856 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1857 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1860 /* it doesn't exist yet, so we don't care about the button state: just add it */
1861 create_automation_child (param, true);
1864 bool yn = menu->get_active();
1865 bool changed = false;
1867 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1869 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1870 will have done that for us.
1873 if (changed && !no_redraw) {
1881 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1883 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1889 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1891 if (menu && !_hidden) {
1892 ignore_toggle = true;
1893 menu->set_active (false);
1894 ignore_toggle = false;
1897 if (_route && !no_redraw) {
1903 RouteTimeAxisView::update_gain_track_visibility ()
1905 bool const showit = gain_automation_item->get_active();
1907 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1908 gain_track->set_marked_for_display (showit);
1910 /* now trigger a redisplay */
1913 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1919 RouteTimeAxisView::update_trim_track_visibility ()
1921 bool const showit = trim_automation_item->get_active();
1923 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1924 trim_track->set_marked_for_display (showit);
1926 /* now trigger a redisplay */
1929 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1935 RouteTimeAxisView::update_mute_track_visibility ()
1937 bool const showit = mute_automation_item->get_active();
1939 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1940 mute_track->set_marked_for_display (showit);
1942 /* now trigger a redisplay */
1945 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1951 RouteTimeAxisView::update_pan_track_visibility ()
1953 bool const showit = pan_automation_item->get_active();
1954 bool changed = false;
1956 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1957 if ((*i)->set_marked_for_display (showit)) {
1963 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1968 RouteTimeAxisView::ensure_pan_views (bool show)
1970 bool changed = false;
1971 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1973 (*i)->set_marked_for_display (false);
1976 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1980 if (!_route->panner()) {
1984 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1985 set<Evoral::Parameter>::iterator p;
1987 for (p = params.begin(); p != params.end(); ++p) {
1988 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1990 if (pan_control->parameter().type() == NullAutomation) {
1991 error << "Pan control has NULL automation type!" << endmsg;
1995 if (automation_child (pan_control->parameter ()).get () == 0) {
1997 /* we don't already have an AutomationTimeAxisView for this parameter */
1999 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2001 boost::shared_ptr<AutomationTimeAxisView> t (
2002 new AutomationTimeAxisView (_session,
2006 pan_control->parameter (),
2014 pan_tracks.push_back (t);
2015 add_automation_child (*p, t, show);
2017 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2024 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2026 if (apply_to_selection) {
2027 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2031 /* Show our automation */
2033 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2034 i->second->set_marked_for_display (true);
2036 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2039 menu->set_active(true);
2044 /* Show processor automation */
2046 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2047 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2048 if ((*ii)->view == 0) {
2049 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2052 (*ii)->menu_item->set_active (true);
2065 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2067 if (apply_to_selection) {
2068 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2072 /* Show our automation */
2074 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2075 if (i->second->has_automation()) {
2076 i->second->set_marked_for_display (true);
2078 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2080 menu->set_active(true);
2085 /* Show processor automation */
2087 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2088 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2089 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2090 (*ii)->menu_item->set_active (true);
2102 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2104 if (apply_to_selection) {
2105 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2109 /* Hide our automation */
2111 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2112 i->second->set_marked_for_display (false);
2114 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2117 menu->set_active (false);
2121 /* Hide processor automation */
2123 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2124 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2125 (*ii)->menu_item->set_active (false);
2136 RouteTimeAxisView::region_view_added (RegionView* rv)
2138 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2139 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2140 boost::shared_ptr<AutomationTimeAxisView> atv;
2142 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2147 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2148 (*i)->add_ghost(rv);
2152 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2154 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2160 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2162 parent.remove_processor_automation_node (this);
2166 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2169 remove_child (pan->view);
2173 RouteTimeAxisView::ProcessorAutomationNode*
2174 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2176 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2178 if ((*i)->processor == processor) {
2180 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2181 if ((*ii)->what == what) {
2191 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2193 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2196 ProcessorAutomationNode* pan;
2198 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2199 /* session state may never have been saved with new plugin */
2200 error << _("programming error: ")
2201 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2202 processor->name(), what.type(), (int) what.channel(), what.id() )
2204 abort(); /*NOTREACHED*/
2212 boost::shared_ptr<AutomationControl> control
2213 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2215 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2216 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2217 _editor, *this, false, parent_canvas,
2218 processor->describe_parameter (what), processor->name()));
2220 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2222 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2225 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2230 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2233 pan->menu_item->set_active (false);
2242 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2244 boost::shared_ptr<Processor> processor (p.lock ());
2246 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2247 /* The Amp processor is a special case and is dealt with separately */
2251 set<Evoral::Parameter> existing;
2253 processor->what_has_data (existing);
2255 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2257 Evoral::Parameter param (*i);
2258 boost::shared_ptr<AutomationLine> al;
2260 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2263 add_processor_automation_curve (processor, param);
2269 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2271 using namespace Menu_Helpers;
2275 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2277 _automation_tracks[param] = track;
2279 /* existing state overrides "show" argument */
2280 string s = track->gui_property ("visible");
2282 show = string_is_affirmative (s);
2285 /* this might or might not change the visibility status, so don't rely on it */
2286 track->set_marked_for_display (show);
2288 if (show && !no_redraw) {
2292 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2293 /* MIDI-related parameters are always in the menu, there's no
2294 reason to rebuild the menu just because we added a automation
2295 lane for one of them. But if we add a non-MIDI automation
2296 lane, then we need to invalidate the display menu.
2298 delete display_menu;
2304 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2306 boost::shared_ptr<Processor> processor (p.lock ());
2308 if (!processor || !processor->display_to_user ()) {
2312 /* we use this override to veto the Amp processor from the plugin menu,
2313 as its automation lane can be accessed using the special "Fader" menu
2317 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2321 using namespace Menu_Helpers;
2322 ProcessorAutomationInfo *rai;
2323 list<ProcessorAutomationInfo*>::iterator x;
2325 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2327 if (automatable.empty()) {
2331 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2332 if ((*x)->processor == processor) {
2337 if (x == processor_automation.end()) {
2338 rai = new ProcessorAutomationInfo (processor);
2339 processor_automation.push_back (rai);
2344 /* any older menu was deleted at the top of processors_changed()
2345 when we cleared the subplugin menu.
2348 rai->menu = manage (new Menu);
2349 MenuList& items = rai->menu->items();
2350 rai->menu->set_name ("ArdourContextMenu");
2354 std::set<Evoral::Parameter> has_visible_automation;
2355 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2357 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2359 ProcessorAutomationNode* pan;
2360 Gtk::CheckMenuItem* mitem;
2362 string name = processor->describe_parameter (*i);
2364 if (name == X_("hidden")) {
2368 items.push_back (CheckMenuElem (name));
2369 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2371 _subplugin_menu_map[*i] = mitem;
2373 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2374 mitem->set_active(true);
2377 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2381 pan = new ProcessorAutomationNode (*i, mitem, *this);
2383 rai->lines.push_back (pan);
2387 pan->menu_item = mitem;
2391 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2394 if (items.size() == 0) {
2398 /* add the menu for this processor, because the subplugin
2399 menu is always cleared at the top of processors_changed().
2400 this is the result of some poor design in gtkmm and/or
2404 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2409 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2410 RouteTimeAxisView::ProcessorAutomationNode* pan)
2412 bool showit = pan->menu_item->get_active();
2413 bool redraw = false;
2415 if (pan->view == 0 && showit) {
2416 add_processor_automation_curve (rai->processor, pan->what);
2420 if (pan->view && pan->view->set_marked_for_display (showit)) {
2424 if (redraw && !no_redraw) {
2430 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2432 if (c.type == RouteProcessorChange::MeterPointChange) {
2433 /* nothing to do if only the meter point has changed */
2437 using namespace Menu_Helpers;
2439 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2440 (*i)->valid = false;
2443 setup_processor_menu_and_curves ();
2445 bool deleted_processor_automation = false;
2447 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2449 list<ProcessorAutomationInfo*>::iterator tmp;
2457 processor_automation.erase (i);
2458 deleted_processor_automation = true;
2465 if (deleted_processor_automation && !no_redraw) {
2470 boost::shared_ptr<AutomationLine>
2471 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2473 ProcessorAutomationNode* pan;
2475 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2481 return boost::shared_ptr<AutomationLine>();
2485 RouteTimeAxisView::reset_processor_automation_curves ()
2487 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2493 RouteTimeAxisView::can_edit_name () const
2495 /* we do not allow track name changes if it is record enabled
2497 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2501 return !trk->rec_enable_control()->get_value();
2505 RouteTimeAxisView::blink_rec_display (bool onoff)
2507 RouteUI::blink_rec_display (onoff);
2511 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2513 if (_ignore_set_layer_display) {
2517 if (apply_to_selection) {
2518 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2522 _view->set_layer_display (d);
2525 set_gui_property (X_("layer-display"), enum_2_string (d));
2530 RouteTimeAxisView::layer_display () const
2533 return _view->layer_display ();
2536 /* we don't know, since we don't have a _view, so just return something */
2542 boost::shared_ptr<AutomationTimeAxisView>
2543 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2545 AutomationTracks::iterator i = _automation_tracks.find(param);
2546 if (i != _automation_tracks.end()) {
2549 return boost::shared_ptr<AutomationTimeAxisView>();
2554 RouteTimeAxisView::fast_update ()
2556 gm.get_level_meter().update_meters ();
2560 RouteTimeAxisView::hide_meter ()
2563 gm.get_level_meter().hide_meters ();
2567 RouteTimeAxisView::show_meter ()
2573 RouteTimeAxisView::reset_meter ()
2575 if (UIConfiguration::instance().get_show_track_meters()) {
2576 int meter_width = 3;
2577 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2580 gm.get_level_meter().setup_meters (height - 9, meter_width);
2587 RouteTimeAxisView::clear_meter ()
2589 gm.get_level_meter().clear_meters ();
2593 RouteTimeAxisView::meter_changed ()
2595 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2597 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2600 // reset peak when meter point changes
2601 gm.reset_peak_display();
2605 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2608 if (_route && !no_redraw) {
2614 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2616 using namespace Menu_Helpers;
2618 if (!_underlay_streams.empty()) {
2619 MenuList& parent_items = parent_menu->items();
2620 Menu* gs_menu = manage (new Menu);
2621 gs_menu->set_name ("ArdourContextMenu");
2622 MenuList& gs_items = gs_menu->items();
2624 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2626 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2627 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2628 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2634 RouteTimeAxisView::set_underlay_state()
2636 if (!underlay_xml_node) {
2640 XMLNodeList nlist = underlay_xml_node->children();
2641 XMLNodeConstIterator niter;
2642 XMLNode *child_node;
2644 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2645 child_node = *niter;
2647 if (child_node->name() != "Underlay") {
2651 XMLProperty const * prop = child_node->property ("id");
2653 PBD::ID id (prop->value());
2655 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2658 add_underlay(v->view(), false);
2667 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2673 RouteTimeAxisView& other = v->trackview();
2675 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2676 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2677 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2678 abort(); /*NOTREACHED*/
2681 _underlay_streams.push_back(v);
2682 other._underlay_mirrors.push_back(this);
2684 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2686 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2688 if (!underlay_xml_node) {
2689 underlay_xml_node = xml_node->add_child("Underlays");
2692 XMLNode* node = underlay_xml_node->add_child("Underlay");
2693 XMLProperty const * prop = node->add_property("id");
2694 prop->set_value(v->trackview().route()->id().to_s());
2701 RouteTimeAxisView::remove_underlay (StreamView* v)
2707 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2708 RouteTimeAxisView& other = v->trackview();
2710 if (it != _underlay_streams.end()) {
2711 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2713 if (gm == other._underlay_mirrors.end()) {
2714 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2715 abort(); /*NOTREACHED*/
2718 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2720 _underlay_streams.erase(it);
2721 other._underlay_mirrors.erase(gm);
2723 if (underlay_xml_node) {
2724 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2730 RouteTimeAxisView::set_button_names ()
2732 if (_route && _route->solo_safe_control()->solo_safe()) {
2733 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2735 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2737 if (Config->get_solo_control_is_listen_control()) {
2738 switch (Config->get_listen_position()) {
2739 case AfterFaderListen:
2740 solo_button->set_text (S_("AfterFader|A"));
2741 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2743 case PreFaderListen:
2744 solo_button->set_text (S_("PreFader|P"));
2745 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2749 solo_button->set_text (S_("Solo|S"));
2750 set_tooltip (*solo_button, _("Solo"));
2752 mute_button->set_text (S_("Mute|M"));
2756 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2758 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2759 if (i != _main_automation_menu_map.end()) {
2763 i = _subplugin_menu_map.find (param);
2764 if (i != _subplugin_menu_map.end()) {
2772 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2774 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2776 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2780 gain_track.reset (new AutomationTimeAxisView (_session,
2781 _route, _route->amp(), c, param,
2786 _route->amp()->describe_parameter(param)));
2789 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2792 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2796 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2798 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2799 if (!c || ! _route->trim()->active()) {
2803 trim_track.reset (new AutomationTimeAxisView (_session,
2804 _route, _route->trim(), c, param,
2809 _route->trim()->describe_parameter(param)));
2812 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2815 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2819 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2821 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2823 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2827 mute_track.reset (new AutomationTimeAxisView (_session,
2828 _route, _route, c, param,
2833 _route->describe_parameter(param)));
2836 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2839 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2843 void add_region_to_list (RegionView* rv, RegionList* l)
2845 l->push_back (rv->region());
2849 RouteTimeAxisView::combine_regions ()
2851 /* as of may 2011, we do not offer uncombine for MIDI tracks
2854 if (!is_audio_track()) {
2862 RegionList selected_regions;
2863 boost::shared_ptr<Playlist> playlist = track()->playlist();
2865 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2867 if (selected_regions.size() < 2) {
2871 playlist->clear_changes ();
2872 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2874 _session->add_command (new StatefulDiffCommand (playlist));
2875 /* make the new region be selected */
2877 return _view->find_view (compound_region);
2881 RouteTimeAxisView::uncombine_regions ()
2883 /* as of may 2011, we do not offer uncombine for MIDI tracks
2885 if (!is_audio_track()) {
2893 RegionList selected_regions;
2894 boost::shared_ptr<Playlist> playlist = track()->playlist();
2896 /* have to grab selected regions first because the uncombine is going
2897 * to change that in the middle of the list traverse
2900 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2902 playlist->clear_changes ();
2904 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2905 playlist->uncombine (*i);
2908 _session->add_command (new StatefulDiffCommand (playlist));
2912 RouteTimeAxisView::state_id() const
2914 return string_compose ("rtav %1", _route->id().to_s());
2919 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2921 TimeAxisView::remove_child (c);
2923 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2925 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2926 if (i->second == a) {
2927 _automation_tracks.erase (i);
2935 RouteTimeAxisView::color () const
2937 return route_color ();
2941 RouteTimeAxisView::marked_for_display () const
2943 return !_route->presentation_info().hidden();
2947 RouteTimeAxisView::set_marked_for_display (bool yn)
2949 return RouteUI::mark_hidden (!yn);