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::set_route (boost::shared_ptr<Route> rt)
134 RouteUI::set_route (rt);
136 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
137 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
138 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
141 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
144 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
145 gm.get_level_meter().set_no_show_all();
146 gm.get_level_meter().setup_meters(50, meter_width);
147 gm.update_gain_sensitive ();
149 string str = gui_property ("height");
151 set_height (atoi (str));
153 set_height (preset_height (HeightNormal));
156 if (!_route->is_auditioner()) {
157 if (gui_property ("visible").empty()) {
158 set_gui_property ("visible", true);
161 set_gui_property ("visible", false);
164 timestretch_rect = 0;
167 ignore_toggle = false;
169 route_group_button.set_name ("route button");
170 playlist_button.set_name ("route button");
171 automation_button.set_name ("route button");
173 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
174 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
175 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
179 if (ARDOUR::Profile->get_mixbus()) {
180 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
182 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
185 if (is_midi_track()) {
186 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
187 gm.set_fader_name ("MidiTrackFader");
189 set_tooltip(*rec_enable_button, _("Record"));
190 gm.set_fader_name ("AudioTrackFader");
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
199 Gtk::Fixed *blank = manage(new Gtk::Fixed());
200 controls_button_size_group->add_widget(*blank);
201 if (ARDOUR::Profile->get_mixbus() ) {
202 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
211 if (!ARDOUR::Profile->get_mixbus()) {
212 controls_meters_size_group->add_widget (gm.get_level_meter());
215 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
216 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
217 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
220 if (ARDOUR::Profile->get_mixbus()) {
221 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 // mute button is always present, it is used to
226 // force the 'blank' placeholders to the proper size
227 controls_button_size_group->add_widget(*mute_button);
229 if (!_route->is_master()) {
230 if (ARDOUR::Profile->get_mixbus()) {
231 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 Gtk::Fixed *blank = manage(new Gtk::Fixed());
237 controls_button_size_group->add_widget(*blank);
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 else if (!ARDOUR::Profile->get_trx()) {
251 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
255 set_tooltip(*solo_button,_("Solo"));
256 set_tooltip(*mute_button,_("Mute"));
257 set_tooltip(route_group_button, _("Route Group"));
259 mute_button->set_tweaks(ArdourButton::TrackHeader);
260 solo_button->set_tweaks(ArdourButton::TrackHeader);
261 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
262 playlist_button.set_tweaks(ArdourButton::TrackHeader);
263 automation_button.set_tweaks(ArdourButton::TrackHeader);
264 route_group_button.set_tweaks(ArdourButton::TrackHeader);
266 if (is_midi_track()) {
267 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
269 set_tooltip(automation_button, _("Automation"));
272 update_track_number_visibility();
275 if (ARDOUR::Profile->get_mixbus()) {
276 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 else if (!ARDOUR::Profile->get_trx()) {
279 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282 if (is_track() && track()->mode() == ARDOUR::Normal) {
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
294 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
298 str = gui_property ("layer-display");
300 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
303 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
304 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306 /* pick up the correct freeze state */
311 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
312 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314 PropertyList* plist = new PropertyList();
316 plist->add (ARDOUR::Properties::group_mute, true);
317 plist->add (ARDOUR::Properties::group_solo, true);
319 route_group_menu = new RouteGroupMenu (_session, plist);
321 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
324 RouteTimeAxisView::~RouteTimeAxisView ()
326 cleanup_gui_properties ();
328 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
332 delete playlist_action_menu;
333 playlist_action_menu = 0;
338 _automation_tracks.clear ();
340 delete route_group_menu;
341 CatchDeletion (this);
345 RouteTimeAxisView::name() const
348 return _route->name();
354 RouteTimeAxisView::post_construct ()
356 /* map current state of the route */
358 update_diskstream_display ();
359 setup_processor_menu_and_curves ();
360 reset_processor_automation_curves ();
363 /** Set up the processor menu for the current set of processors, and
364 * display automation curves for any parameters which have data.
367 RouteTimeAxisView::setup_processor_menu_and_curves ()
369 _subplugin_menu_map.clear ();
370 subplugin_menu.items().clear ();
371 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
372 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
376 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
378 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
379 if (_route->route_group()) {
380 _route->route_group()->remove (_route);
386 r.push_back (route ());
388 route_group_menu->build (r);
389 route_group_menu->menu()->popup (ev->button, ev->time);
395 RouteTimeAxisView::playlist_changed ()
401 RouteTimeAxisView::label_view ()
403 string x = _route->name ();
404 if (x != name_label.get_text ()) {
405 name_label.set_text (x);
407 const int64_t track_number = _route->track_number ();
408 if (track_number == 0) {
409 number_label.set_text ("");
411 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
416 RouteTimeAxisView::update_track_number_visibility ()
419 bool show_label = _session->config.get_track_name_number();
421 if (_route && _route->is_master()) {
425 if (number_label.get_parent()) {
426 controls_table.remove (number_label);
429 if (ARDOUR::Profile->get_mixbus()) {
430 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
432 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
434 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
435 // except the width of the number label is subtracted from the name-hbox, so we
436 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
437 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
439 number_label.set_size_request(tnw, -1);
440 number_label.show ();
442 number_label.hide ();
447 RouteTimeAxisView::parameter_changed (string const & p)
449 if (p == "track-name-number") {
450 update_track_number_visibility();
451 } else if (p == "editor-stereo-only-meters") {
452 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
453 gm.get_level_meter().set_max_audio_meter_count (2);
455 gm.get_level_meter().set_max_audio_meter_count (0);
461 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
463 if (what_changed.contains (ARDOUR::Properties::name)) {
469 RouteTimeAxisView::take_name_changed (void *src)
477 RouteTimeAxisView::playlist_click ()
479 build_playlist_menu ();
480 conditionally_add_to_selection ();
481 playlist_action_menu->popup (1, gtk_get_current_event_time());
485 RouteTimeAxisView::automation_click ()
487 conditionally_add_to_selection ();
488 build_automation_action_menu (false);
489 automation_action_menu->popup (1, gtk_get_current_event_time());
493 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
495 using namespace Menu_Helpers;
497 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
498 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
501 detach_menu (subplugin_menu);
503 _main_automation_menu_map.clear ();
504 delete automation_action_menu;
505 automation_action_menu = new Menu;
507 MenuList& items = automation_action_menu->items();
509 automation_action_menu->set_name ("ArdourContextMenu");
511 items.push_back (MenuElem (_("Show All Automation"),
512 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
514 items.push_back (MenuElem (_("Show Existing Automation"),
515 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
517 items.push_back (MenuElem (_("Hide All Automation"),
518 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
520 /* Attach the plugin submenu. It may have previously been used elsewhere,
521 so it was detached above
524 if (!subplugin_menu.items().empty()) {
525 items.push_back (SeparatorElem ());
526 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
527 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
530 /* Add any route automation */
533 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
534 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
535 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
536 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
538 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
542 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
543 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
544 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
545 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
547 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
551 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
552 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
553 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
554 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
556 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
559 if (!pan_tracks.empty()) {
560 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
561 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
562 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
563 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
565 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
566 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
567 _main_automation_menu_map[*p] = pan_automation_item;
573 RouteTimeAxisView::build_display_menu ()
575 using namespace Menu_Helpers;
579 TimeAxisView::build_display_menu ();
581 /* now fill it with our stuff */
583 MenuList& items = display_menu->items();
584 display_menu->set_name ("ArdourContextMenu");
586 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
588 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
590 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
592 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
594 items.push_back (SeparatorElem());
597 detach_menu (*_size_menu);
600 items.push_back (MenuElem (_("Height"), *_size_menu));
601 items.push_back (SeparatorElem());
603 // Hook for derived classes to add type specific stuff
604 append_extra_display_menu_items ();
608 Menu* layers_menu = manage (new Menu);
609 MenuList &layers_items = layers_menu->items();
610 layers_menu->set_name("ArdourContextMenu");
612 RadioMenuItem::Group layers_group;
614 /* Find out how many overlaid/stacked tracks we have in the selection */
618 TrackSelection const & s = _editor.get_selection().tracks;
619 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
620 StreamView* v = (*i)->view ();
625 switch (v->layer_display ()) {
636 /* We're not connecting to signal_toggled() here; in the case where these two items are
637 set to be in the `inconsistent' state, it seems that one or other will end up active
638 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
639 select the active one, no toggled signal is emitted so nothing happens.
642 _ignore_set_layer_display = true;
644 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
645 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
646 i->set_active (overlaid != 0 && stacked == 0);
647 i->set_inconsistent (overlaid != 0 && stacked != 0);
648 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
650 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
651 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
652 i->set_active (overlaid == 0 && stacked != 0);
653 i->set_inconsistent (overlaid != 0 && stacked != 0);
654 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
656 _ignore_set_layer_display = false;
658 items.push_back (MenuElem (_("Layers"), *layers_menu));
660 Menu* alignment_menu = manage (new Menu);
661 MenuList& alignment_items = alignment_menu->items();
662 alignment_menu->set_name ("ArdourContextMenu");
664 RadioMenuItem::Group align_group;
666 /* Same verbose hacks as for the layering options above */
672 boost::shared_ptr<Track> first_track;
674 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
675 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
676 if (!r || !r->is_track ()) {
681 first_track = r->track();
684 switch (r->track()->alignment_choice()) {
688 switch (r->track()->alignment_style()) {
689 case ExistingMaterial:
697 case UseExistingMaterial:
713 inconsistent = false;
720 if (!inconsistent && first_track) {
722 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
723 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
724 i->set_active (automatic != 0 && existing == 0 && capture == 0);
725 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
727 switch (first_track->alignment_choice()) {
729 switch (first_track->alignment_style()) {
730 case ExistingMaterial:
731 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
734 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
742 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
743 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
744 i->set_active (existing != 0 && capture == 0 && automatic == 0);
745 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
747 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
748 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
749 i->set_active (existing == 0 && capture != 0 && automatic == 0);
750 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
752 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
758 Menu* mode_menu = manage (new Menu);
759 MenuList& mode_items = mode_menu->items ();
760 mode_menu->set_name ("ArdourContextMenu");
762 RadioMenuItem::Group mode_group;
768 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
769 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
770 if (!r || !r->is_track ()) {
774 switch (r->track()->mode()) {
787 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
788 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
789 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
790 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
791 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
793 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
794 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
795 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
796 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
797 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
799 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
800 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
801 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
802 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
803 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
805 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
807 items.push_back (SeparatorElem());
809 build_playlist_menu ();
810 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
811 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
814 route_group_menu->detach ();
817 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
818 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
820 r.push_back (rtv->route ());
825 r.push_back (route ());
828 route_group_menu->build (r);
829 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
831 build_automation_action_menu (true);
832 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
834 items.push_back (SeparatorElem());
838 TrackSelection const & s = _editor.get_selection().tracks;
839 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
840 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
845 if (r->route()->active()) {
852 items.push_back (CheckMenuElem (_("Active")));
853 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
854 bool click_sets_active = true;
855 if (active > 0 && inactive == 0) {
856 i->set_active (true);
857 click_sets_active = false;
858 } else if (active > 0 && inactive > 0) {
859 i->set_inconsistent (true);
861 i->set_sensitive(! _session->transport_rolling());
862 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
864 items.push_back (SeparatorElem());
865 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
866 if (_route && !_route->is_master()) {
867 items.push_back (SeparatorElem());
868 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
870 items.push_back (SeparatorElem());
871 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
875 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
877 if (apply_to_selection) {
878 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
881 bool needs_bounce = false;
883 if (!track()->can_use_mode (mode, needs_bounce)) {
889 cerr << "would bounce this one\n";
894 track()->set_mode (mode);
899 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
901 TimeAxisView::show_timestretch (start, end, layers, layer);
911 /* check that the time selection was made in our route, or our route group.
912 remember that route_group() == 0 implies the route is *not* in a edit group.
915 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
916 /* this doesn't apply to us */
920 /* ignore it if our edit group is not active */
922 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
927 if (timestretch_rect == 0) {
928 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
929 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
930 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
933 timestretch_rect->show ();
934 timestretch_rect->raise_to_top ();
936 double const x1 = start / _editor.get_current_zoom();
937 double const x2 = (end - 1) / _editor.get_current_zoom();
939 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
940 x2, current_height() * (layers - layer) / layers));
944 RouteTimeAxisView::hide_timestretch ()
946 TimeAxisView::hide_timestretch ();
948 if (timestretch_rect) {
949 timestretch_rect->hide ();
954 RouteTimeAxisView::show_selection (TimeSelection& ts)
958 /* ignore it if our edit group is not active or if the selection was started
959 in some other track or route group (remember that route_group() == 0 means
960 that the track is not in an route group).
963 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
964 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
970 TimeAxisView::show_selection (ts);
974 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
977 bool height_changed = (height == 0) || (h != height);
980 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
983 gm.get_level_meter().setup_meters (gmlen, meter_width);
985 TimeAxisView::set_height (h, m);
988 _view->set_height ((double) current_height());
991 if (height >= preset_height (HeightNormal)) {
995 gm.get_gain_slider().show();
997 if (!_route || _route->is_monitor()) {
1000 solo_button->show();
1002 if (rec_enable_button)
1003 rec_enable_button->show();
1005 route_group_button.show();
1006 automation_button.show();
1008 if (is_track() && track()->mode() == ARDOUR::Normal) {
1009 playlist_button.show();
1016 gm.get_gain_slider().hide();
1017 mute_button->show();
1018 if (!_route || _route->is_monitor()) {
1019 solo_button->hide();
1021 solo_button->show();
1023 if (rec_enable_button)
1024 rec_enable_button->show();
1026 route_group_button.hide ();
1027 automation_button.hide ();
1029 if (is_track() && track()->mode() == ARDOUR::Normal) {
1030 playlist_button.hide ();
1035 if (height_changed && !no_redraw) {
1036 /* only emit the signal if the height really changed */
1042 RouteTimeAxisView::route_color_changed ()
1045 _view->apply_color (color(), StreamView::RegionColor);
1048 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1052 RouteTimeAxisView::reset_samples_per_pixel ()
1054 set_samples_per_pixel (_editor.get_current_zoom());
1058 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1063 speed = track()->speed();
1067 _view->set_samples_per_pixel (fpp * speed);
1070 TimeAxisView::set_samples_per_pixel (fpp * speed);
1074 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1076 if (!mitem->get_active()) {
1077 /* this is one of the two calls made when these radio menu items change status. this one
1078 is for the item that became inactive, and we want to ignore it.
1083 if (apply_to_selection) {
1084 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1087 track()->set_align_choice (choice);
1093 RouteTimeAxisView::rename_current_playlist ()
1095 ArdourPrompter prompter (true);
1098 boost::shared_ptr<Track> tr = track();
1099 if (!tr || tr->destructive()) {
1103 boost::shared_ptr<Playlist> pl = tr->playlist();
1108 prompter.set_title (_("Rename Playlist"));
1109 prompter.set_prompt (_("New name for playlist:"));
1110 prompter.set_initial_text (pl->name());
1111 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1112 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1114 switch (prompter.run ()) {
1115 case Gtk::RESPONSE_ACCEPT:
1116 prompter.get_result (name);
1117 if (name.length()) {
1118 pl->set_name (name);
1128 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1130 std::string ret (basename);
1132 std::string const group_string = "." + route_group()->name() + ".";
1134 // iterate through all playlists
1136 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1137 std::string tmp = (*i)->name();
1139 std::string::size_type idx = tmp.find(group_string);
1140 // find those which belong to this group
1141 if (idx != string::npos) {
1142 tmp = tmp.substr(idx + group_string.length());
1144 // and find the largest current number
1146 if (x > maxnumber) {
1155 snprintf (buf, sizeof(buf), "%d", maxnumber);
1157 ret = this->name() + "." + route_group()->name () + "." + buf;
1163 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1167 boost::shared_ptr<Track> tr = track ();
1168 if (!tr || tr->destructive()) {
1172 boost::shared_ptr<const Playlist> pl = tr->playlist();
1179 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1180 name = resolve_new_group_playlist_name(name, playlists_before_op);
1183 while (_session->playlists->by_name(name)) {
1184 name = Playlist::bump_name (name, *_session);
1187 // TODO: The prompter "new" button should be de-activated if the user
1188 // specifies a playlist name which already exists in the session.
1192 ArdourPrompter prompter (true);
1194 prompter.set_title (_("New Copy Playlist"));
1195 prompter.set_prompt (_("Name for new playlist:"));
1196 prompter.set_initial_text (name);
1197 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1198 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1199 prompter.show_all ();
1201 switch (prompter.run ()) {
1202 case Gtk::RESPONSE_ACCEPT:
1203 prompter.get_result (name);
1211 if (name.length()) {
1212 tr->use_copy_playlist ();
1213 tr->playlist()->set_name (name);
1218 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1222 boost::shared_ptr<Track> tr = track ();
1223 if (!tr || tr->destructive()) {
1227 boost::shared_ptr<const Playlist> pl = tr->playlist();
1234 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1235 name = resolve_new_group_playlist_name(name,playlists_before_op);
1238 while (_session->playlists->by_name(name)) {
1239 name = Playlist::bump_name (name, *_session);
1245 ArdourPrompter prompter (true);
1247 prompter.set_title (_("New Playlist"));
1248 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);
1253 switch (prompter.run ()) {
1254 case Gtk::RESPONSE_ACCEPT:
1255 prompter.get_result (name);
1263 if (name.length()) {
1264 tr->use_new_playlist ();
1265 tr->playlist()->set_name (name);
1270 RouteTimeAxisView::clear_playlist ()
1272 boost::shared_ptr<Track> tr = track ();
1273 if (!tr || tr->destructive()) {
1277 boost::shared_ptr<Playlist> pl = tr->playlist();
1282 _editor.clear_playlist (pl);
1286 RouteTimeAxisView::speed_changed ()
1288 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1292 RouteTimeAxisView::update_diskstream_display ()
1302 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1304 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1306 /* special case: select/deselect all tracks */
1308 _editor.begin_reversible_selection_op (X_("Selection Click"));
1310 if (_editor.get_selection().selected (this)) {
1311 _editor.get_selection().clear_tracks ();
1313 _editor.select_all_tracks ();
1316 _editor.commit_reversible_selection_op ();
1321 _editor.begin_reversible_selection_op (X_("Selection Click"));
1323 switch (ArdourKeyboard::selection_type (ev->state)) {
1324 case Selection::Toggle:
1325 _editor.get_selection().toggle (this);
1328 case Selection::Set:
1329 _editor.get_selection().set (this);
1332 case Selection::Extend:
1333 _editor.extend_selection_to_track (*this);
1336 case Selection::Add:
1337 _editor.get_selection().add (this);
1341 _editor.commit_reversible_selection_op ();
1345 RouteTimeAxisView::set_selected_points (PointSelection& points)
1347 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1348 (*i)->set_selected_points (points);
1350 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1352 asv->set_selected_points (points);
1357 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1360 _view->set_selected_regionviews (regions);
1364 /** Add the selectable things that we have to a list.
1365 * @param results List to add things to.
1368 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1373 speed = track()->speed();
1376 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1377 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1379 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1380 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1383 /* pick up visible automation tracks */
1385 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1386 if (!(*i)->hidden()) {
1387 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1393 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1396 _view->get_inverted_selectables (sel, results);
1399 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1400 if (!(*i)->hidden()) {
1401 (*i)->get_inverted_selectables (sel, results);
1409 RouteTimeAxisView::route_group () const
1411 return _route->route_group();
1415 RouteTimeAxisView::name() const
1417 return _route->name();
1420 boost::shared_ptr<Playlist>
1421 RouteTimeAxisView::playlist () const
1423 boost::shared_ptr<Track> tr;
1425 if ((tr = track()) != 0) {
1426 return tr->playlist();
1428 return boost::shared_ptr<Playlist> ();
1433 RouteTimeAxisView::name_entry_changed (string const& str)
1435 if (str == _route->name()) {
1441 strip_whitespace_edges (x);
1447 if (_session->route_name_internal (x)) {
1448 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1450 } else if (RouteUI::verify_new_route_name (x)) {
1451 _route->set_name (x);
1458 boost::shared_ptr<Region>
1459 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1461 boost::shared_ptr<Playlist> pl = playlist ();
1464 return pl->find_next_region (pos, point, dir);
1467 return boost::shared_ptr<Region> ();
1471 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1473 boost::shared_ptr<Playlist> pl = playlist ();
1476 return pl->find_next_region_boundary (pos, dir);
1483 RouteTimeAxisView::fade_range (TimeSelection& selection)
1485 boost::shared_ptr<Playlist> what_we_got;
1486 boost::shared_ptr<Track> tr = track ();
1487 boost::shared_ptr<Playlist> playlist;
1490 /* route is a bus, not a track */
1494 playlist = tr->playlist();
1496 TimeSelection time (selection);
1497 float const speed = tr->speed();
1498 if (speed != 1.0f) {
1499 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1500 (*i).start = session_frame_to_track_frame((*i).start, speed);
1501 (*i).end = session_frame_to_track_frame((*i).end, speed);
1505 playlist->clear_changes ();
1506 playlist->clear_owned_changes ();
1508 playlist->fade_range (time);
1510 vector<Command*> cmds;
1511 playlist->rdiff (cmds);
1512 _session->add_commands (cmds);
1513 _session->add_command (new StatefulDiffCommand (playlist));
1518 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1520 boost::shared_ptr<Playlist> what_we_got;
1521 boost::shared_ptr<Track> tr = track ();
1522 boost::shared_ptr<Playlist> playlist;
1525 /* route is a bus, not a track */
1529 playlist = tr->playlist();
1531 TimeSelection time (selection.time);
1532 float const speed = tr->speed();
1533 if (speed != 1.0f) {
1534 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1535 (*i).start = session_frame_to_track_frame((*i).start, speed);
1536 (*i).end = session_frame_to_track_frame((*i).end, speed);
1540 playlist->clear_changes ();
1541 playlist->clear_owned_changes ();
1545 if (playlist->cut (time) != 0) {
1546 if (Config->get_edit_mode() == Ripple)
1547 playlist->ripple(time.start(), -time.length(), NULL);
1548 // no need to exclude any regions from rippling here
1550 vector<Command*> cmds;
1551 playlist->rdiff (cmds);
1552 _session->add_commands (cmds);
1554 _session->add_command (new StatefulDiffCommand (playlist));
1559 if ((what_we_got = playlist->cut (time)) != 0) {
1560 _editor.get_cut_buffer().add (what_we_got);
1561 if (Config->get_edit_mode() == Ripple)
1562 playlist->ripple(time.start(), -time.length(), NULL);
1563 // no need to exclude any regions from rippling here
1565 vector<Command*> cmds;
1566 playlist->rdiff (cmds);
1567 _session->add_commands (cmds);
1569 _session->add_command (new StatefulDiffCommand (playlist));
1573 if ((what_we_got = playlist->copy (time)) != 0) {
1574 _editor.get_cut_buffer().add (what_we_got);
1579 if ((what_we_got = playlist->cut (time)) != 0) {
1580 if (Config->get_edit_mode() == Ripple)
1581 playlist->ripple(time.start(), -time.length(), NULL);
1582 // no need to exclude any regions from rippling here
1584 vector<Command*> cmds;
1585 playlist->rdiff (cmds);
1586 _session->add_commands (cmds);
1587 _session->add_command (new StatefulDiffCommand (playlist));
1588 what_we_got->release ();
1595 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1601 boost::shared_ptr<Playlist> pl = playlist ();
1602 const ARDOUR::DataType type = pl->data_type();
1603 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1605 if (p == selection.playlists.end()) {
1608 ctx.counts.increase_n_playlists(type);
1610 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1612 if (track()->speed() != 1.0f) {
1613 pos = session_frame_to_track_frame (pos, track()->speed());
1614 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1617 /* add multi-paste offset if applicable */
1618 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1619 const framecnt_t duration = extent.second - extent.first;
1620 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1622 pl->clear_changes ();
1623 pl->clear_owned_changes ();
1624 if (Config->get_edit_mode() == Ripple) {
1625 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1626 framecnt_t amount = extent.second - extent.first;
1627 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1629 pl->paste (*p, pos, ctx.times);
1631 vector<Command*> cmds;
1633 _session->add_commands (cmds);
1635 _session->add_command (new StatefulDiffCommand (pl));
1641 struct PlaylistSorter {
1642 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1643 return a->sort_id() < b->sort_id();
1648 RouteTimeAxisView::build_playlist_menu ()
1650 using namespace Menu_Helpers;
1656 delete playlist_action_menu;
1657 playlist_action_menu = new Menu;
1658 playlist_action_menu->set_name ("ArdourContextMenu");
1660 MenuList& playlist_items = playlist_action_menu->items();
1661 playlist_action_menu->set_name ("ArdourContextMenu");
1662 playlist_items.clear();
1664 RadioMenuItem::Group playlist_group;
1665 boost::shared_ptr<Track> tr = track ();
1667 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1669 /* sort the playlists */
1671 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1673 /* add the playlists to the menu */
1674 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1675 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1676 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1677 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1679 if (tr->playlist()->id() == (*i)->id()) {
1685 playlist_items.push_back (SeparatorElem());
1686 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1687 playlist_items.push_back (SeparatorElem());
1689 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1690 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1691 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1694 // Use a label which tells the user what is happening
1695 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1696 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1700 playlist_items.push_back (SeparatorElem());
1701 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1702 playlist_items.push_back (SeparatorElem());
1704 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1708 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1710 assert (is_track());
1712 // exit if we were triggered by deactivating the old playlist
1713 if (!item->get_active()) {
1717 boost::shared_ptr<Playlist> pl (wpl.lock());
1723 if (track()->playlist() == pl) {
1724 // exit when use_playlist is called by the creation of the playlist menu
1725 // or the playlist choice is unchanged
1729 track()->use_playlist (pl);
1731 RouteGroup* rg = route_group();
1733 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1734 std::string group_string = "." + rg->name() + ".";
1736 std::string take_name = pl->name();
1737 std::string::size_type idx = take_name.find(group_string);
1739 if (idx == std::string::npos)
1742 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1744 boost::shared_ptr<RouteList> rl (rg->route_list());
1746 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1747 if ((*i) == this->route()) {
1751 std::string playlist_name = (*i)->name()+group_string+take_name;
1753 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1758 if (track->freeze_state() == Track::Frozen) {
1759 /* Don't change playlists of frozen tracks */
1763 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1765 // No playlist for this track for this take yet, make it
1766 track->use_new_playlist();
1767 track->playlist()->set_name(playlist_name);
1769 track->use_playlist(ipl);
1776 RouteTimeAxisView::update_playlist_tip ()
1778 RouteGroup* rg = route_group ();
1779 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1780 string group_string = "." + rg->name() + ".";
1782 string take_name = track()->playlist()->name();
1783 string::size_type idx = take_name.find(group_string);
1785 if (idx != string::npos) {
1786 /* find the bit containing the take number / name */
1787 take_name = take_name.substr (idx + group_string.length());
1789 /* set the playlist button tooltip to the take name */
1792 string_compose(_("Take: %1.%2"),
1793 Gtkmm2ext::markup_escape_text (rg->name()),
1794 Gtkmm2ext::markup_escape_text (take_name))
1801 /* set the playlist button tooltip to the playlist name */
1802 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1807 RouteTimeAxisView::show_playlist_selector ()
1809 _editor.playlist_selector().show_for (this);
1813 RouteTimeAxisView::map_frozen ()
1819 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1821 switch (track()->freeze_state()) {
1823 playlist_button.set_sensitive (false);
1826 playlist_button.set_sensitive (true);
1829 RouteUI::map_frozen ();
1833 RouteTimeAxisView::color_handler ()
1835 //case cTimeStretchOutline:
1836 if (timestretch_rect) {
1837 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1839 //case cTimeStretchFill:
1840 if (timestretch_rect) {
1841 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1847 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1848 * Will add track if necessary.
1851 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1853 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1854 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1857 /* it doesn't exist yet, so we don't care about the button state: just add it */
1858 create_automation_child (param, true);
1861 bool yn = menu->get_active();
1862 bool changed = false;
1864 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1866 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1867 will have done that for us.
1870 if (changed && !no_redraw) {
1878 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1880 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1886 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1888 if (menu && !_hidden) {
1889 ignore_toggle = true;
1890 menu->set_active (false);
1891 ignore_toggle = false;
1894 if (_route && !no_redraw) {
1900 RouteTimeAxisView::update_gain_track_visibility ()
1902 bool const showit = gain_automation_item->get_active();
1904 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1905 gain_track->set_marked_for_display (showit);
1907 /* now trigger a redisplay */
1910 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1916 RouteTimeAxisView::update_trim_track_visibility ()
1918 bool const showit = trim_automation_item->get_active();
1920 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1921 trim_track->set_marked_for_display (showit);
1923 /* now trigger a redisplay */
1926 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1932 RouteTimeAxisView::update_mute_track_visibility ()
1934 bool const showit = mute_automation_item->get_active();
1936 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1937 mute_track->set_marked_for_display (showit);
1939 /* now trigger a redisplay */
1942 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1948 RouteTimeAxisView::update_pan_track_visibility ()
1950 bool const showit = pan_automation_item->get_active();
1951 bool changed = false;
1953 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1954 if ((*i)->set_marked_for_display (showit)) {
1960 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1965 RouteTimeAxisView::ensure_pan_views (bool show)
1967 bool changed = false;
1968 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1970 (*i)->set_marked_for_display (false);
1973 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1977 if (!_route->panner()) {
1981 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1982 set<Evoral::Parameter>::iterator p;
1984 for (p = params.begin(); p != params.end(); ++p) {
1985 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1987 if (pan_control->parameter().type() == NullAutomation) {
1988 error << "Pan control has NULL automation type!" << endmsg;
1992 if (automation_child (pan_control->parameter ()).get () == 0) {
1994 /* we don't already have an AutomationTimeAxisView for this parameter */
1996 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1998 boost::shared_ptr<AutomationTimeAxisView> t (
1999 new AutomationTimeAxisView (_session,
2003 pan_control->parameter (),
2011 pan_tracks.push_back (t);
2012 add_automation_child (*p, t, show);
2014 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2021 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2023 if (apply_to_selection) {
2024 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2028 /* Show our automation */
2030 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2031 i->second->set_marked_for_display (true);
2033 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2036 menu->set_active(true);
2041 /* Show processor automation */
2043 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2044 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2045 if ((*ii)->view == 0) {
2046 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2049 (*ii)->menu_item->set_active (true);
2062 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2064 if (apply_to_selection) {
2065 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2069 /* Show our automation */
2071 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2072 if (i->second->has_automation()) {
2073 i->second->set_marked_for_display (true);
2075 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2077 menu->set_active(true);
2082 /* Show processor automation */
2084 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2085 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2086 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2087 (*ii)->menu_item->set_active (true);
2099 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2101 if (apply_to_selection) {
2102 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2106 /* Hide our automation */
2108 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2109 i->second->set_marked_for_display (false);
2111 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2114 menu->set_active (false);
2118 /* Hide processor automation */
2120 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2121 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2122 (*ii)->menu_item->set_active (false);
2133 RouteTimeAxisView::region_view_added (RegionView* rv)
2135 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2136 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2137 boost::shared_ptr<AutomationTimeAxisView> atv;
2139 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2144 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2145 (*i)->add_ghost(rv);
2149 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2151 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2157 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2159 parent.remove_processor_automation_node (this);
2163 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2166 remove_child (pan->view);
2170 RouteTimeAxisView::ProcessorAutomationNode*
2171 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2173 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2175 if ((*i)->processor == processor) {
2177 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2178 if ((*ii)->what == what) {
2188 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2190 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2193 ProcessorAutomationNode* pan;
2195 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2196 /* session state may never have been saved with new plugin */
2197 error << _("programming error: ")
2198 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2199 processor->name(), what.type(), (int) what.channel(), what.id() )
2201 abort(); /*NOTREACHED*/
2209 boost::shared_ptr<AutomationControl> control
2210 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2212 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2213 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2214 _editor, *this, false, parent_canvas,
2215 processor->describe_parameter (what), processor->name()));
2217 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2219 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2222 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2227 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2230 pan->menu_item->set_active (false);
2239 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2241 boost::shared_ptr<Processor> processor (p.lock ());
2243 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2244 /* The Amp processor is a special case and is dealt with separately */
2248 set<Evoral::Parameter> existing;
2250 processor->what_has_data (existing);
2252 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2254 Evoral::Parameter param (*i);
2255 boost::shared_ptr<AutomationLine> al;
2257 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2260 add_processor_automation_curve (processor, param);
2266 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2268 using namespace Menu_Helpers;
2272 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2274 _automation_tracks[param] = track;
2276 /* existing state overrides "show" argument */
2277 string s = track->gui_property ("visible");
2279 show = string_is_affirmative (s);
2282 /* this might or might not change the visibility status, so don't rely on it */
2283 track->set_marked_for_display (show);
2285 if (show && !no_redraw) {
2289 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2290 /* MIDI-related parameters are always in the menu, there's no
2291 reason to rebuild the menu just because we added a automation
2292 lane for one of them. But if we add a non-MIDI automation
2293 lane, then we need to invalidate the display menu.
2295 delete display_menu;
2301 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2303 boost::shared_ptr<Processor> processor (p.lock ());
2305 if (!processor || !processor->display_to_user ()) {
2309 /* we use this override to veto the Amp processor from the plugin menu,
2310 as its automation lane can be accessed using the special "Fader" menu
2314 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2318 using namespace Menu_Helpers;
2319 ProcessorAutomationInfo *rai;
2320 list<ProcessorAutomationInfo*>::iterator x;
2322 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2324 if (automatable.empty()) {
2328 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2329 if ((*x)->processor == processor) {
2334 if (x == processor_automation.end()) {
2335 rai = new ProcessorAutomationInfo (processor);
2336 processor_automation.push_back (rai);
2341 /* any older menu was deleted at the top of processors_changed()
2342 when we cleared the subplugin menu.
2345 rai->menu = manage (new Menu);
2346 MenuList& items = rai->menu->items();
2347 rai->menu->set_name ("ArdourContextMenu");
2351 std::set<Evoral::Parameter> has_visible_automation;
2352 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2354 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2356 ProcessorAutomationNode* pan;
2357 Gtk::CheckMenuItem* mitem;
2359 string name = processor->describe_parameter (*i);
2361 if (name == X_("hidden")) {
2365 items.push_back (CheckMenuElem (name));
2366 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2368 _subplugin_menu_map[*i] = mitem;
2370 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2371 mitem->set_active(true);
2374 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2378 pan = new ProcessorAutomationNode (*i, mitem, *this);
2380 rai->lines.push_back (pan);
2384 pan->menu_item = mitem;
2388 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2391 if (items.size() == 0) {
2395 /* add the menu for this processor, because the subplugin
2396 menu is always cleared at the top of processors_changed().
2397 this is the result of some poor design in gtkmm and/or
2401 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2406 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2407 RouteTimeAxisView::ProcessorAutomationNode* pan)
2409 bool showit = pan->menu_item->get_active();
2410 bool redraw = false;
2412 if (pan->view == 0 && showit) {
2413 add_processor_automation_curve (rai->processor, pan->what);
2417 if (pan->view && pan->view->set_marked_for_display (showit)) {
2421 if (redraw && !no_redraw) {
2427 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2429 if (c.type == RouteProcessorChange::MeterPointChange) {
2430 /* nothing to do if only the meter point has changed */
2434 using namespace Menu_Helpers;
2436 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2437 (*i)->valid = false;
2440 setup_processor_menu_and_curves ();
2442 bool deleted_processor_automation = false;
2444 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2446 list<ProcessorAutomationInfo*>::iterator tmp;
2454 processor_automation.erase (i);
2455 deleted_processor_automation = true;
2462 if (deleted_processor_automation && !no_redraw) {
2467 boost::shared_ptr<AutomationLine>
2468 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2470 ProcessorAutomationNode* pan;
2472 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2478 return boost::shared_ptr<AutomationLine>();
2482 RouteTimeAxisView::reset_processor_automation_curves ()
2484 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2490 RouteTimeAxisView::can_edit_name () const
2492 /* we do not allow track name changes if it is record enabled
2494 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2498 return !trk->rec_enable_control()->get_value();
2502 RouteTimeAxisView::blink_rec_display (bool onoff)
2504 RouteUI::blink_rec_display (onoff);
2508 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2510 if (_ignore_set_layer_display) {
2514 if (apply_to_selection) {
2515 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2519 _view->set_layer_display (d);
2522 set_gui_property (X_("layer-display"), enum_2_string (d));
2527 RouteTimeAxisView::layer_display () const
2530 return _view->layer_display ();
2533 /* we don't know, since we don't have a _view, so just return something */
2539 boost::shared_ptr<AutomationTimeAxisView>
2540 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2542 AutomationTracks::iterator i = _automation_tracks.find(param);
2543 if (i != _automation_tracks.end()) {
2546 return boost::shared_ptr<AutomationTimeAxisView>();
2551 RouteTimeAxisView::fast_update ()
2553 gm.get_level_meter().update_meters ();
2557 RouteTimeAxisView::hide_meter ()
2560 gm.get_level_meter().hide_meters ();
2564 RouteTimeAxisView::show_meter ()
2570 RouteTimeAxisView::reset_meter ()
2572 if (UIConfiguration::instance().get_show_track_meters()) {
2573 int meter_width = 3;
2574 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2577 gm.get_level_meter().setup_meters (height - 9, meter_width);
2584 RouteTimeAxisView::clear_meter ()
2586 gm.get_level_meter().clear_meters ();
2590 RouteTimeAxisView::meter_changed ()
2592 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2594 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2597 // reset peak when meter point changes
2598 gm.reset_peak_display();
2602 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2605 if (_route && !no_redraw) {
2611 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2613 using namespace Menu_Helpers;
2615 if (!_underlay_streams.empty()) {
2616 MenuList& parent_items = parent_menu->items();
2617 Menu* gs_menu = manage (new Menu);
2618 gs_menu->set_name ("ArdourContextMenu");
2619 MenuList& gs_items = gs_menu->items();
2621 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2623 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2624 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2625 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2631 RouteTimeAxisView::set_underlay_state()
2633 if (!underlay_xml_node) {
2637 XMLNodeList nlist = underlay_xml_node->children();
2638 XMLNodeConstIterator niter;
2639 XMLNode *child_node;
2641 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2642 child_node = *niter;
2644 if (child_node->name() != "Underlay") {
2648 XMLProperty const * prop = child_node->property ("id");
2650 PBD::ID id (prop->value());
2652 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2655 add_underlay(v->view(), false);
2664 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2670 RouteTimeAxisView& other = v->trackview();
2672 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2673 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2674 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2675 abort(); /*NOTREACHED*/
2678 _underlay_streams.push_back(v);
2679 other._underlay_mirrors.push_back(this);
2681 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2683 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2685 if (!underlay_xml_node) {
2686 underlay_xml_node = xml_node->add_child("Underlays");
2689 XMLNode* node = underlay_xml_node->add_child("Underlay");
2690 XMLProperty const * prop = node->add_property("id");
2691 prop->set_value(v->trackview().route()->id().to_s());
2698 RouteTimeAxisView::remove_underlay (StreamView* v)
2704 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2705 RouteTimeAxisView& other = v->trackview();
2707 if (it != _underlay_streams.end()) {
2708 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2710 if (gm == other._underlay_mirrors.end()) {
2711 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2712 abort(); /*NOTREACHED*/
2715 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2717 _underlay_streams.erase(it);
2718 other._underlay_mirrors.erase(gm);
2720 if (underlay_xml_node) {
2721 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2727 RouteTimeAxisView::set_button_names ()
2729 if (_route && _route->solo_safe_control()->solo_safe()) {
2730 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2732 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2734 if (Config->get_solo_control_is_listen_control()) {
2735 switch (Config->get_listen_position()) {
2736 case AfterFaderListen:
2737 solo_button->set_text (S_("AfterFader|A"));
2738 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2740 case PreFaderListen:
2741 solo_button->set_text (S_("PreFader|P"));
2742 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2746 solo_button->set_text (S_("Solo|S"));
2747 set_tooltip (*solo_button, _("Solo"));
2749 mute_button->set_text (S_("Mute|M"));
2753 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2755 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2756 if (i != _main_automation_menu_map.end()) {
2760 i = _subplugin_menu_map.find (param);
2761 if (i != _subplugin_menu_map.end()) {
2769 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2771 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2773 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2777 gain_track.reset (new AutomationTimeAxisView (_session,
2778 _route, _route->amp(), c, param,
2783 _route->amp()->describe_parameter(param)));
2786 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2789 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2793 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2795 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2796 if (!c || ! _route->trim()->active()) {
2800 trim_track.reset (new AutomationTimeAxisView (_session,
2801 _route, _route->trim(), c, param,
2806 _route->trim()->describe_parameter(param)));
2809 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2812 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2816 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2818 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2820 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2824 mute_track.reset (new AutomationTimeAxisView (_session,
2825 _route, _route, c, param,
2830 _route->describe_parameter(param)));
2833 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2836 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2840 void add_region_to_list (RegionView* rv, RegionList* l)
2842 l->push_back (rv->region());
2846 RouteTimeAxisView::combine_regions ()
2848 /* as of may 2011, we do not offer uncombine for MIDI tracks
2851 if (!is_audio_track()) {
2859 RegionList selected_regions;
2860 boost::shared_ptr<Playlist> playlist = track()->playlist();
2862 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2864 if (selected_regions.size() < 2) {
2868 playlist->clear_changes ();
2869 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2871 _session->add_command (new StatefulDiffCommand (playlist));
2872 /* make the new region be selected */
2874 return _view->find_view (compound_region);
2878 RouteTimeAxisView::uncombine_regions ()
2880 /* as of may 2011, we do not offer uncombine for MIDI tracks
2882 if (!is_audio_track()) {
2890 RegionList selected_regions;
2891 boost::shared_ptr<Playlist> playlist = track()->playlist();
2893 /* have to grab selected regions first because the uncombine is going
2894 * to change that in the middle of the list traverse
2897 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2899 playlist->clear_changes ();
2901 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2902 playlist->uncombine (*i);
2905 _session->add_command (new StatefulDiffCommand (playlist));
2909 RouteTimeAxisView::state_id() const
2911 return string_compose ("rtav %1", _route->id().to_s());
2916 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2918 TimeAxisView::remove_child (c);
2920 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2922 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2923 if (i->second == a) {
2924 _automation_tracks.erase (i);
2931 PresentationInfo const &
2932 RouteTimeAxisView::presentation_info () const
2934 return _route->presentation_info();
2937 boost::shared_ptr<Stripable>
2938 RouteTimeAxisView::stripable () const
2944 RouteTimeAxisView::color () const
2946 return route_color ();
2950 RouteTimeAxisView::marked_for_display () const
2952 return !_route->presentation_info().hidden();
2956 RouteTimeAxisView::set_marked_for_display (bool yn)
2958 return RouteUI::mark_hidden (!yn);