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)
100 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
102 , parent_canvas (canvas)
104 , button_table (3, 3)
105 , route_group_button (S_("RTAV|G"))
106 , playlist_button (S_("RTAV|P"))
107 , automation_button (S_("RTAV|A"))
108 , automation_action_menu (0)
109 , plugins_submenu_item (0)
110 , route_group_menu (0)
111 , playlist_action_menu (0)
113 , color_mode_menu (0)
114 , gm (sess, true, 75, 14)
115 , _ignore_set_layer_display (false)
116 , gain_automation_item(NULL)
117 , trim_automation_item(NULL)
118 , mute_automation_item(NULL)
119 , pan_automation_item(NULL)
121 number_label.set_name("tracknumber label");
122 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
123 number_label.set_alignment(.5, .5);
124 number_label.set_fallthrough_to_parent (true);
126 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
129 parameter_changed ("editor-stereo-only-meters");
133 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
135 RouteUI::set_route (rt);
137 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
138 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
139 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
142 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
145 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
146 gm.get_level_meter().set_no_show_all();
147 gm.get_level_meter().setup_meters(50, meter_width);
148 gm.update_gain_sensitive ();
150 string str = gui_property ("height");
152 set_height (atoi (str));
154 set_height (preset_height (HeightNormal));
157 if (!_route->is_auditioner()) {
158 if (gui_property ("visible").empty()) {
159 set_gui_property ("visible", true);
162 set_gui_property ("visible", false);
165 timestretch_rect = 0;
168 ignore_toggle = false;
170 route_group_button.set_name ("route button");
171 playlist_button.set_name ("route button");
172 automation_button.set_name ("route button");
174 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
175 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
176 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
180 if (ARDOUR::Profile->get_mixbus()) {
181 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
183 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
186 if (is_midi_track()) {
187 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
188 gm.set_fader_name ("MidiTrackFader");
190 set_tooltip(*rec_enable_button, _("Record"));
191 gm.set_fader_name ("AudioTrackFader");
194 /* set playlist button tip to the current playlist, and make it update when it changes */
195 update_playlist_tip ();
196 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
199 gm.set_fader_name ("AudioBusFader");
200 Gtk::Fixed *blank = manage(new Gtk::Fixed());
201 controls_button_size_group->add_widget(*blank);
202 if (ARDOUR::Profile->get_mixbus() ) {
203 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
205 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
210 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
212 if (!ARDOUR::Profile->get_mixbus()) {
213 controls_meters_size_group->add_widget (gm.get_level_meter());
216 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
217 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
219 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
221 if (ARDOUR::Profile->get_mixbus()) {
222 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
224 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
226 // mute button is always present, it is used to
227 // force the 'blank' placeholders to the proper size
228 controls_button_size_group->add_widget(*mute_button);
230 if (!_route->is_master()) {
231 if (ARDOUR::Profile->get_mixbus()) {
232 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
237 Gtk::Fixed *blank = manage(new Gtk::Fixed());
238 controls_button_size_group->add_widget(*blank);
239 if (ARDOUR::Profile->get_mixbus()) {
240 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 if (ARDOUR::Profile->get_mixbus()) {
248 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
251 else if (!ARDOUR::Profile->get_trx()) {
252 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
253 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
256 set_tooltip(*solo_button,_("Solo"));
257 set_tooltip(*mute_button,_("Mute"));
258 set_tooltip(route_group_button, _("Route Group"));
260 mute_button->set_tweaks(ArdourButton::TrackHeader);
261 solo_button->set_tweaks(ArdourButton::TrackHeader);
262 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
263 playlist_button.set_tweaks(ArdourButton::TrackHeader);
264 automation_button.set_tweaks(ArdourButton::TrackHeader);
265 route_group_button.set_tweaks(ArdourButton::TrackHeader);
267 if (is_midi_track()) {
268 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
270 set_tooltip(automation_button, _("Automation"));
273 update_track_number_visibility();
276 if (ARDOUR::Profile->get_mixbus()) {
277 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
279 else if (!ARDOUR::Profile->get_trx()) {
280 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
283 if (is_track() && track()->mode() == ARDOUR::Normal) {
284 if (ARDOUR::Profile->get_mixbus()) {
285 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
287 else if (!ARDOUR::Profile->get_trx()) {
288 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
295 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
299 str = gui_property ("layer-display");
301 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
304 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
305 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
307 /* pick up the correct freeze state */
312 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
313 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
315 PropertyList* plist = new PropertyList();
317 plist->add (ARDOUR::Properties::mute, true);
318 plist->add (ARDOUR::Properties::solo, true);
320 route_group_menu = new RouteGroupMenu (_session, plist);
322 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
325 RouteTimeAxisView::~RouteTimeAxisView ()
327 cleanup_gui_properties ();
329 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
333 delete playlist_action_menu;
334 playlist_action_menu = 0;
339 _automation_tracks.clear ();
341 delete route_group_menu;
342 CatchDeletion (this);
346 RouteTimeAxisView::post_construct ()
348 /* map current state of the route */
350 update_diskstream_display ();
351 setup_processor_menu_and_curves ();
352 reset_processor_automation_curves ();
355 /** Set up the processor menu for the current set of processors, and
356 * display automation curves for any parameters which have data.
359 RouteTimeAxisView::setup_processor_menu_and_curves ()
361 _subplugin_menu_map.clear ();
362 subplugin_menu.items().clear ();
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
364 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
368 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
370 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
371 if (_route->route_group()) {
372 _route->route_group()->remove (_route);
378 r.push_back (route ());
380 route_group_menu->build (r);
381 route_group_menu->menu()->popup (ev->button, ev->time);
387 RouteTimeAxisView::playlist_changed ()
393 RouteTimeAxisView::label_view ()
395 string x = _route->name ();
396 if (x != name_label.get_text ()) {
397 name_label.set_text (x);
399 const int64_t track_number = _route->track_number ();
400 if (track_number == 0) {
401 number_label.set_text ("");
403 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
408 RouteTimeAxisView::update_track_number_visibility ()
411 bool show_label = _session->config.get_track_name_number();
413 if (_route && _route->is_master()) {
417 if (number_label.get_parent()) {
418 controls_table.remove (number_label);
421 if (ARDOUR::Profile->get_mixbus()) {
422 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
424 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
426 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
427 // except the width of the number label is subtracted from the name-hbox, so we
428 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
429 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
431 number_label.set_size_request(tnw, -1);
432 number_label.show ();
433 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
435 number_label.hide ();
436 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
441 RouteTimeAxisView::parameter_changed (string const & p)
443 if (p == "track-name-number") {
444 update_track_number_visibility();
445 } else if (p == "editor-stereo-only-meters") {
446 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
447 gm.get_level_meter().set_max_audio_meter_count (2);
449 gm.get_level_meter().set_max_audio_meter_count (0);
455 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
457 if (what_changed.contains (ARDOUR::Properties::name)) {
463 RouteTimeAxisView::take_name_changed (void *src)
471 RouteTimeAxisView::playlist_click ()
473 build_playlist_menu ();
474 conditionally_add_to_selection ();
475 playlist_action_menu->popup (1, gtk_get_current_event_time());
479 RouteTimeAxisView::automation_click ()
481 conditionally_add_to_selection ();
482 build_automation_action_menu (false);
483 automation_action_menu->popup (1, gtk_get_current_event_time());
487 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
489 using namespace Menu_Helpers;
491 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
492 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
495 detach_menu (subplugin_menu);
497 _main_automation_menu_map.clear ();
498 delete automation_action_menu;
499 automation_action_menu = new Menu;
501 MenuList& items = automation_action_menu->items();
503 automation_action_menu->set_name ("ArdourContextMenu");
505 items.push_back (MenuElem (_("Show All Automation"),
506 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
508 items.push_back (MenuElem (_("Show Existing Automation"),
509 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
511 items.push_back (MenuElem (_("Hide All Automation"),
512 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
514 /* Attach the plugin submenu. It may have previously been used elsewhere,
515 so it was detached above
518 if (!subplugin_menu.items().empty()) {
519 items.push_back (SeparatorElem ());
520 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
521 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
524 /* Add any route automation */
527 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
528 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
529 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
530 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
532 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
536 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
537 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
538 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
539 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
541 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
545 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
546 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
547 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
548 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
550 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
553 if (!pan_tracks.empty()) {
554 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
555 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
556 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
557 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
559 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
560 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
561 _main_automation_menu_map[*p] = pan_automation_item;
567 RouteTimeAxisView::build_display_menu ()
569 using namespace Menu_Helpers;
573 TimeAxisView::build_display_menu ();
575 /* now fill it with our stuff */
577 MenuList& items = display_menu->items();
578 display_menu->set_name ("ArdourContextMenu");
580 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
582 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
584 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
586 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
588 items.push_back (SeparatorElem());
591 detach_menu (*_size_menu);
594 items.push_back (MenuElem (_("Height"), *_size_menu));
596 items.push_back (SeparatorElem());
598 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
599 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
600 items.push_back (SeparatorElem());
602 // Hook for derived classes to add type specific stuff
603 append_extra_display_menu_items ();
607 Menu* layers_menu = manage (new Menu);
608 MenuList &layers_items = layers_menu->items();
609 layers_menu->set_name("ArdourContextMenu");
611 RadioMenuItem::Group layers_group;
613 /* Find out how many overlaid/stacked tracks we have in the selection */
617 TrackSelection const & s = _editor.get_selection().tracks;
618 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
619 StreamView* v = (*i)->view ();
624 switch (v->layer_display ()) {
635 /* We're not connecting to signal_toggled() here; in the case where these two items are
636 set to be in the `inconsistent' state, it seems that one or other will end up active
637 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
638 select the active one, no toggled signal is emitted so nothing happens.
641 _ignore_set_layer_display = true;
643 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
644 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
645 i->set_active (overlaid != 0 && stacked == 0);
646 i->set_inconsistent (overlaid != 0 && stacked != 0);
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
649 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
650 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
651 i->set_active (overlaid == 0 && stacked != 0);
652 i->set_inconsistent (overlaid != 0 && stacked != 0);
653 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
655 _ignore_set_layer_display = false;
657 items.push_back (MenuElem (_("Layers"), *layers_menu));
659 Menu* alignment_menu = manage (new Menu);
660 MenuList& alignment_items = alignment_menu->items();
661 alignment_menu->set_name ("ArdourContextMenu");
663 RadioMenuItem::Group align_group;
665 /* Same verbose hacks as for the layering options above */
671 boost::shared_ptr<Track> first_track;
673 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
674 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
675 if (!r || !r->is_track ()) {
680 first_track = r->track();
683 switch (r->track()->alignment_choice()) {
687 switch (r->track()->alignment_style()) {
688 case ExistingMaterial:
696 case UseExistingMaterial:
712 inconsistent = false;
719 if (!inconsistent && first_track) {
721 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
722 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
723 i->set_active (automatic != 0 && existing == 0 && capture == 0);
724 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
726 switch (first_track->alignment_choice()) {
728 switch (first_track->alignment_style()) {
729 case ExistingMaterial:
730 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
733 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
741 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
742 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
743 i->set_active (existing != 0 && capture == 0 && automatic == 0);
744 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
746 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
747 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
748 i->set_active (existing == 0 && capture != 0 && automatic == 0);
749 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
751 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
757 Menu* mode_menu = manage (new Menu);
758 MenuList& mode_items = mode_menu->items ();
759 mode_menu->set_name ("ArdourContextMenu");
761 RadioMenuItem::Group mode_group;
767 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
768 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
769 if (!r || !r->is_track ()) {
773 switch (r->track()->mode()) {
786 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
787 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
788 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
789 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
790 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
792 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
793 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
794 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
795 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
796 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
798 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
799 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
800 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
801 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
802 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
804 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
806 items.push_back (SeparatorElem());
808 build_playlist_menu ();
809 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
810 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
813 route_group_menu->detach ();
816 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
817 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
819 r.push_back (rtv->route ());
824 r.push_back (route ());
827 route_group_menu->build (r);
828 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
830 build_automation_action_menu (true);
831 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
833 items.push_back (SeparatorElem());
837 TrackSelection const & s = _editor.get_selection().tracks;
838 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
839 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
844 if (r->route()->active()) {
851 items.push_back (CheckMenuElem (_("Active")));
852 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
853 bool click_sets_active = true;
854 if (active > 0 && inactive == 0) {
855 i->set_active (true);
856 click_sets_active = false;
857 } else if (active > 0 && inactive > 0) {
858 i->set_inconsistent (true);
860 i->set_sensitive(! _session->transport_rolling());
861 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
863 items.push_back (SeparatorElem());
864 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
865 if (_route && !_route->is_master()) {
866 items.push_back (SeparatorElem());
867 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
869 items.push_back (SeparatorElem());
870 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
874 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
876 if (apply_to_selection) {
877 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
880 bool needs_bounce = false;
882 if (!track()->can_use_mode (mode, needs_bounce)) {
888 cerr << "would bounce this one\n";
893 track()->set_mode (mode);
898 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
900 TimeAxisView::show_timestretch (start, end, layers, layer);
910 /* check that the time selection was made in our route, or our route group.
911 remember that route_group() == 0 implies the route is *not* in a edit group.
914 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
915 /* this doesn't apply to us */
919 /* ignore it if our edit group is not active */
921 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
926 if (timestretch_rect == 0) {
927 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
928 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
929 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
932 timestretch_rect->show ();
933 timestretch_rect->raise_to_top ();
935 double const x1 = start / _editor.get_current_zoom();
936 double const x2 = (end - 1) / _editor.get_current_zoom();
938 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
939 x2, current_height() * (layers - layer) / layers));
943 RouteTimeAxisView::hide_timestretch ()
945 TimeAxisView::hide_timestretch ();
947 if (timestretch_rect) {
948 timestretch_rect->hide ();
953 RouteTimeAxisView::show_selection (TimeSelection& ts)
957 /* ignore it if our edit group is not active or if the selection was started
958 in some other track or route group (remember that route_group() == 0 means
959 that the track is not in an route group).
962 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
963 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
969 TimeAxisView::show_selection (ts);
973 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
976 bool height_changed = (height == 0) || (h != height);
979 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
982 gm.get_level_meter().setup_meters (gmlen, meter_width);
984 TimeAxisView::set_height (h, m);
987 _view->set_height ((double) current_height());
990 if (height >= preset_height (HeightNormal)) {
994 gm.get_gain_slider().show();
996 if (!_route || _route->is_monitor()) {
1001 if (rec_enable_button)
1002 rec_enable_button->show();
1004 route_group_button.show();
1005 automation_button.show();
1007 if (is_track() && track()->mode() == ARDOUR::Normal) {
1008 playlist_button.show();
1015 gm.get_gain_slider().hide();
1016 mute_button->show();
1017 if (!_route || _route->is_monitor()) {
1018 solo_button->hide();
1020 solo_button->show();
1022 if (rec_enable_button)
1023 rec_enable_button->show();
1025 route_group_button.hide ();
1026 automation_button.hide ();
1028 if (is_track() && track()->mode() == ARDOUR::Normal) {
1029 playlist_button.hide ();
1034 if (height_changed && !no_redraw) {
1035 /* only emit the signal if the height really changed */
1041 RouteTimeAxisView::route_color_changed ()
1044 _view->apply_color (color(), StreamView::RegionColor);
1047 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1051 RouteTimeAxisView::reset_samples_per_pixel ()
1053 set_samples_per_pixel (_editor.get_current_zoom());
1057 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1062 speed = track()->speed();
1066 _view->set_samples_per_pixel (fpp * speed);
1069 TimeAxisView::set_samples_per_pixel (fpp * speed);
1073 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1075 if (!mitem->get_active()) {
1076 /* this is one of the two calls made when these radio menu items change status. this one
1077 is for the item that became inactive, and we want to ignore it.
1082 if (apply_to_selection) {
1083 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1086 track()->set_align_choice (choice);
1092 RouteTimeAxisView::rename_current_playlist ()
1094 ArdourPrompter prompter (true);
1097 boost::shared_ptr<Track> tr = track();
1098 if (!tr || tr->destructive()) {
1102 boost::shared_ptr<Playlist> pl = tr->playlist();
1107 prompter.set_title (_("Rename Playlist"));
1108 prompter.set_prompt (_("New name for playlist:"));
1109 prompter.set_initial_text (pl->name());
1110 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1111 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1113 switch (prompter.run ()) {
1114 case Gtk::RESPONSE_ACCEPT:
1115 prompter.get_result (name);
1116 if (name.length()) {
1117 pl->set_name (name);
1127 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1129 std::string ret (basename);
1131 std::string const group_string = "." + route_group()->name() + ".";
1133 // iterate through all playlists
1135 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1136 std::string tmp = (*i)->name();
1138 std::string::size_type idx = tmp.find(group_string);
1139 // find those which belong to this group
1140 if (idx != string::npos) {
1141 tmp = tmp.substr(idx + group_string.length());
1143 // and find the largest current number
1145 if (x > maxnumber) {
1154 snprintf (buf, sizeof(buf), "%d", maxnumber);
1156 ret = this->name() + "." + route_group()->name () + "." + buf;
1162 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1166 boost::shared_ptr<Track> tr = track ();
1167 if (!tr || tr->destructive()) {
1171 boost::shared_ptr<const Playlist> pl = tr->playlist();
1178 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1179 name = resolve_new_group_playlist_name(name, playlists_before_op);
1182 while (_session->playlists->by_name(name)) {
1183 name = Playlist::bump_name (name, *_session);
1186 // TODO: The prompter "new" button should be de-activated if the user
1187 // specifies a playlist name which already exists in the session.
1191 ArdourPrompter prompter (true);
1193 prompter.set_title (_("New Copy Playlist"));
1194 prompter.set_prompt (_("Name for new playlist:"));
1195 prompter.set_initial_text (name);
1196 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1197 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1198 prompter.show_all ();
1200 switch (prompter.run ()) {
1201 case Gtk::RESPONSE_ACCEPT:
1202 prompter.get_result (name);
1210 if (name.length()) {
1211 tr->use_copy_playlist ();
1212 tr->playlist()->set_name (name);
1217 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1221 boost::shared_ptr<Track> tr = track ();
1222 if (!tr || tr->destructive()) {
1226 boost::shared_ptr<const Playlist> pl = tr->playlist();
1233 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1234 name = resolve_new_group_playlist_name(name,playlists_before_op);
1237 while (_session->playlists->by_name(name)) {
1238 name = Playlist::bump_name (name, *_session);
1244 ArdourPrompter prompter (true);
1246 prompter.set_title (_("New Playlist"));
1247 prompter.set_prompt (_("Name for new playlist:"));
1248 prompter.set_initial_text (name);
1249 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1250 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1252 switch (prompter.run ()) {
1253 case Gtk::RESPONSE_ACCEPT:
1254 prompter.get_result (name);
1262 if (name.length()) {
1263 tr->use_new_playlist ();
1264 tr->playlist()->set_name (name);
1269 RouteTimeAxisView::clear_playlist ()
1271 boost::shared_ptr<Track> tr = track ();
1272 if (!tr || tr->destructive()) {
1276 boost::shared_ptr<Playlist> pl = tr->playlist();
1281 _editor.clear_playlist (pl);
1285 RouteTimeAxisView::speed_changed ()
1287 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1291 RouteTimeAxisView::update_diskstream_display ()
1301 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1303 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1305 /* special case: select/deselect all tracks */
1307 _editor.begin_reversible_selection_op (X_("Selection Click"));
1309 if (_editor.get_selection().selected (this)) {
1310 _editor.get_selection().clear_tracks ();
1312 _editor.select_all_tracks ();
1315 _editor.commit_reversible_selection_op ();
1320 _editor.begin_reversible_selection_op (X_("Selection Click"));
1322 switch (ArdourKeyboard::selection_type (ev->state)) {
1323 case Selection::Toggle:
1324 _editor.get_selection().toggle (this);
1327 case Selection::Set:
1328 _editor.get_selection().set (this);
1331 case Selection::Extend:
1332 _editor.extend_selection_to_track (*this);
1335 case Selection::Add:
1336 _editor.get_selection().add (this);
1340 _editor.commit_reversible_selection_op ();
1344 RouteTimeAxisView::set_selected_points (PointSelection& points)
1346 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1347 (*i)->set_selected_points (points);
1349 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1351 asv->set_selected_points (points);
1356 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1359 _view->set_selected_regionviews (regions);
1363 /** Add the selectable things that we have to a list.
1364 * @param results List to add things to.
1367 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1372 speed = track()->speed();
1375 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1376 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1378 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1379 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1382 /* pick up visible automation tracks */
1384 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1385 if (!(*i)->hidden()) {
1386 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1392 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1395 _view->get_inverted_selectables (sel, results);
1398 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1399 if (!(*i)->hidden()) {
1400 (*i)->get_inverted_selectables (sel, results);
1408 RouteTimeAxisView::route_group () const
1410 return _route->route_group();
1414 RouteTimeAxisView::name() const
1416 return _route->name();
1419 boost::shared_ptr<Playlist>
1420 RouteTimeAxisView::playlist () const
1422 boost::shared_ptr<Track> tr;
1424 if ((tr = track()) != 0) {
1425 return tr->playlist();
1427 return boost::shared_ptr<Playlist> ();
1432 RouteTimeAxisView::name_entry_changed ()
1434 TimeAxisView::name_entry_changed ();
1436 string x = name_entry->get_text ();
1438 if (x == _route->name()) {
1442 strip_whitespace_edges (x);
1444 if (x.length() == 0) {
1445 name_entry->set_text (_route->name());
1449 if (_session->route_name_internal (x)) {
1450 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1452 name_entry->grab_focus ();
1453 } else if (RouteUI::verify_new_route_name (x)) {
1454 _route->set_name (x);
1456 name_entry->grab_focus ();
1460 boost::shared_ptr<Region>
1461 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1463 boost::shared_ptr<Playlist> pl = playlist ();
1466 return pl->find_next_region (pos, point, dir);
1469 return boost::shared_ptr<Region> ();
1473 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1475 boost::shared_ptr<Playlist> pl = playlist ();
1478 return pl->find_next_region_boundary (pos, dir);
1485 RouteTimeAxisView::fade_range (TimeSelection& selection)
1487 boost::shared_ptr<Playlist> what_we_got;
1488 boost::shared_ptr<Track> tr = track ();
1489 boost::shared_ptr<Playlist> playlist;
1492 /* route is a bus, not a track */
1496 playlist = tr->playlist();
1498 TimeSelection time (selection);
1499 float const speed = tr->speed();
1500 if (speed != 1.0f) {
1501 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1502 (*i).start = session_frame_to_track_frame((*i).start, speed);
1503 (*i).end = session_frame_to_track_frame((*i).end, speed);
1507 playlist->clear_changes ();
1508 playlist->clear_owned_changes ();
1510 playlist->fade_range (time);
1512 vector<Command*> cmds;
1513 playlist->rdiff (cmds);
1514 _session->add_commands (cmds);
1515 _session->add_command (new StatefulDiffCommand (playlist));
1520 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1522 boost::shared_ptr<Playlist> what_we_got;
1523 boost::shared_ptr<Track> tr = track ();
1524 boost::shared_ptr<Playlist> playlist;
1527 /* route is a bus, not a track */
1531 playlist = tr->playlist();
1533 TimeSelection time (selection.time);
1534 float const speed = tr->speed();
1535 if (speed != 1.0f) {
1536 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1537 (*i).start = session_frame_to_track_frame((*i).start, speed);
1538 (*i).end = session_frame_to_track_frame((*i).end, speed);
1542 playlist->clear_changes ();
1543 playlist->clear_owned_changes ();
1547 if (playlist->cut (time) != 0) {
1548 if (Config->get_edit_mode() == Ripple)
1549 playlist->ripple(time.start(), -time.length(), NULL);
1550 // no need to exclude any regions from rippling here
1552 vector<Command*> cmds;
1553 playlist->rdiff (cmds);
1554 _session->add_commands (cmds);
1556 _session->add_command (new StatefulDiffCommand (playlist));
1561 if ((what_we_got = playlist->cut (time)) != 0) {
1562 _editor.get_cut_buffer().add (what_we_got);
1563 if (Config->get_edit_mode() == Ripple)
1564 playlist->ripple(time.start(), -time.length(), NULL);
1565 // no need to exclude any regions from rippling here
1567 vector<Command*> cmds;
1568 playlist->rdiff (cmds);
1569 _session->add_commands (cmds);
1571 _session->add_command (new StatefulDiffCommand (playlist));
1575 if ((what_we_got = playlist->copy (time)) != 0) {
1576 _editor.get_cut_buffer().add (what_we_got);
1581 if ((what_we_got = playlist->cut (time)) != 0) {
1582 if (Config->get_edit_mode() == Ripple)
1583 playlist->ripple(time.start(), -time.length(), NULL);
1584 // no need to exclude any regions from rippling here
1586 vector<Command*> cmds;
1587 playlist->rdiff (cmds);
1588 _session->add_commands (cmds);
1589 _session->add_command (new StatefulDiffCommand (playlist));
1590 what_we_got->release ();
1597 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1603 boost::shared_ptr<Playlist> pl = playlist ();
1604 const ARDOUR::DataType type = pl->data_type();
1605 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1607 if (p == selection.playlists.end()) {
1610 ctx.counts.increase_n_playlists(type);
1612 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1614 if (track()->speed() != 1.0f) {
1615 pos = session_frame_to_track_frame (pos, track()->speed());
1616 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1619 /* add multi-paste offset if applicable */
1620 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1621 const framecnt_t duration = extent.second - extent.first;
1622 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1624 pl->clear_changes ();
1625 pl->clear_owned_changes ();
1626 if (Config->get_edit_mode() == Ripple) {
1627 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1628 framecnt_t amount = extent.second - extent.first;
1629 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1631 pl->paste (*p, pos, ctx.times);
1633 vector<Command*> cmds;
1635 _session->add_commands (cmds);
1637 _session->add_command (new StatefulDiffCommand (pl));
1643 struct PlaylistSorter {
1644 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1645 return a->sort_id() < b->sort_id();
1650 RouteTimeAxisView::build_playlist_menu ()
1652 using namespace Menu_Helpers;
1658 delete playlist_action_menu;
1659 playlist_action_menu = new Menu;
1660 playlist_action_menu->set_name ("ArdourContextMenu");
1662 MenuList& playlist_items = playlist_action_menu->items();
1663 playlist_action_menu->set_name ("ArdourContextMenu");
1664 playlist_items.clear();
1666 RadioMenuItem::Group playlist_group;
1667 boost::shared_ptr<Track> tr = track ();
1669 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1671 /* sort the playlists */
1673 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1675 /* add the playlists to the menu */
1676 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1677 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1678 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1679 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1681 if (tr->playlist()->id() == (*i)->id()) {
1687 playlist_items.push_back (SeparatorElem());
1688 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1689 playlist_items.push_back (SeparatorElem());
1691 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1692 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1693 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1696 // Use a label which tells the user what is happening
1697 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1698 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1702 playlist_items.push_back (SeparatorElem());
1703 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1704 playlist_items.push_back (SeparatorElem());
1706 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1710 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1712 assert (is_track());
1714 // exit if we were triggered by deactivating the old playlist
1715 if (!item->get_active()) {
1719 boost::shared_ptr<Playlist> pl (wpl.lock());
1725 if (track()->playlist() == pl) {
1726 // exit when use_playlist is called by the creation of the playlist menu
1727 // or the playlist choice is unchanged
1731 track()->use_playlist (pl);
1733 RouteGroup* rg = route_group();
1735 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1736 std::string group_string = "." + rg->name() + ".";
1738 std::string take_name = pl->name();
1739 std::string::size_type idx = take_name.find(group_string);
1741 if (idx == std::string::npos)
1744 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1746 boost::shared_ptr<RouteList> rl (rg->route_list());
1748 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1749 if ((*i) == this->route()) {
1753 std::string playlist_name = (*i)->name()+group_string+take_name;
1755 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1760 if (track->freeze_state() == Track::Frozen) {
1761 /* Don't change playlists of frozen tracks */
1765 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1767 // No playlist for this track for this take yet, make it
1768 track->use_new_playlist();
1769 track->playlist()->set_name(playlist_name);
1771 track->use_playlist(ipl);
1778 RouteTimeAxisView::update_playlist_tip ()
1780 RouteGroup* rg = route_group ();
1781 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1782 string group_string = "." + rg->name() + ".";
1784 string take_name = track()->playlist()->name();
1785 string::size_type idx = take_name.find(group_string);
1787 if (idx != string::npos) {
1788 /* find the bit containing the take number / name */
1789 take_name = take_name.substr (idx + group_string.length());
1791 /* set the playlist button tooltip to the take name */
1794 string_compose(_("Take: %1.%2"),
1795 Gtkmm2ext::markup_escape_text (rg->name()),
1796 Gtkmm2ext::markup_escape_text (take_name))
1803 /* set the playlist button tooltip to the playlist name */
1804 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1809 RouteTimeAxisView::show_playlist_selector ()
1811 _editor.playlist_selector().show_for (this);
1815 RouteTimeAxisView::map_frozen ()
1821 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1823 switch (track()->freeze_state()) {
1825 playlist_button.set_sensitive (false);
1828 playlist_button.set_sensitive (true);
1831 RouteUI::map_frozen ();
1835 RouteTimeAxisView::color_handler ()
1837 //case cTimeStretchOutline:
1838 if (timestretch_rect) {
1839 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1841 //case cTimeStretchFill:
1842 if (timestretch_rect) {
1843 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1849 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1850 * Will add track if necessary.
1853 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1855 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1856 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1859 /* it doesn't exist yet, so we don't care about the button state: just add it */
1860 create_automation_child (param, true);
1863 bool yn = menu->get_active();
1864 bool changed = false;
1866 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1868 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1869 will have done that for us.
1872 if (changed && !no_redraw) {
1880 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1882 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1888 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1890 if (menu && !_hidden) {
1891 ignore_toggle = true;
1892 menu->set_active (false);
1893 ignore_toggle = false;
1896 if (_route && !no_redraw) {
1902 RouteTimeAxisView::update_gain_track_visibility ()
1904 bool const showit = gain_automation_item->get_active();
1906 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1907 gain_track->set_marked_for_display (showit);
1909 /* now trigger a redisplay */
1912 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1918 RouteTimeAxisView::update_trim_track_visibility ()
1920 bool const showit = trim_automation_item->get_active();
1922 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1923 trim_track->set_marked_for_display (showit);
1925 /* now trigger a redisplay */
1928 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1934 RouteTimeAxisView::update_mute_track_visibility ()
1936 bool const showit = mute_automation_item->get_active();
1938 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1939 mute_track->set_marked_for_display (showit);
1941 /* now trigger a redisplay */
1944 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1950 RouteTimeAxisView::update_pan_track_visibility ()
1952 bool const showit = pan_automation_item->get_active();
1953 bool changed = false;
1955 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1956 if ((*i)->set_marked_for_display (showit)) {
1962 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1967 RouteTimeAxisView::ensure_pan_views (bool show)
1969 bool changed = false;
1970 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1972 (*i)->set_marked_for_display (false);
1975 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1979 if (!_route->panner()) {
1983 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1984 set<Evoral::Parameter>::iterator p;
1986 for (p = params.begin(); p != params.end(); ++p) {
1987 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1989 if (pan_control->parameter().type() == NullAutomation) {
1990 error << "Pan control has NULL automation type!" << endmsg;
1994 if (automation_child (pan_control->parameter ()).get () == 0) {
1996 /* we don't already have an AutomationTimeAxisView for this parameter */
1998 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2000 boost::shared_ptr<AutomationTimeAxisView> t (
2001 new AutomationTimeAxisView (_session,
2005 pan_control->parameter (),
2013 pan_tracks.push_back (t);
2014 add_automation_child (*p, t, show);
2016 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2023 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2025 if (apply_to_selection) {
2026 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2030 /* Show our automation */
2032 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2033 i->second->set_marked_for_display (true);
2035 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2038 menu->set_active(true);
2043 /* Show processor automation */
2045 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2046 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2047 if ((*ii)->view == 0) {
2048 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2051 (*ii)->menu_item->set_active (true);
2064 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2066 if (apply_to_selection) {
2067 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2071 /* Show our automation */
2073 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2074 if (i->second->has_automation()) {
2075 i->second->set_marked_for_display (true);
2077 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2079 menu->set_active(true);
2084 /* Show processor automation */
2086 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2087 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2088 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2089 (*ii)->menu_item->set_active (true);
2101 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2103 if (apply_to_selection) {
2104 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2108 /* Hide our automation */
2110 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2111 i->second->set_marked_for_display (false);
2113 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2116 menu->set_active (false);
2120 /* Hide processor automation */
2122 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2123 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2124 (*ii)->menu_item->set_active (false);
2135 RouteTimeAxisView::region_view_added (RegionView* rv)
2137 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2138 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2139 boost::shared_ptr<AutomationTimeAxisView> atv;
2141 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2146 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2147 (*i)->add_ghost(rv);
2151 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2153 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2159 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2161 parent.remove_processor_automation_node (this);
2165 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2168 remove_child (pan->view);
2172 RouteTimeAxisView::ProcessorAutomationNode*
2173 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2175 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2177 if ((*i)->processor == processor) {
2179 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2180 if ((*ii)->what == what) {
2190 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2192 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2195 ProcessorAutomationNode* pan;
2197 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2198 /* session state may never have been saved with new plugin */
2199 error << _("programming error: ")
2200 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2201 processor->name(), what.type(), (int) what.channel(), what.id() )
2203 abort(); /*NOTREACHED*/
2211 boost::shared_ptr<AutomationControl> control
2212 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2214 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2215 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2216 _editor, *this, false, parent_canvas,
2217 processor->describe_parameter (what), processor->name()));
2219 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2221 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2224 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2229 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2232 pan->menu_item->set_active (false);
2241 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2243 boost::shared_ptr<Processor> processor (p.lock ());
2245 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2246 /* The Amp processor is a special case and is dealt with separately */
2250 set<Evoral::Parameter> existing;
2252 processor->what_has_data (existing);
2254 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2256 Evoral::Parameter param (*i);
2257 boost::shared_ptr<AutomationLine> al;
2259 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2262 add_processor_automation_curve (processor, param);
2268 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2270 using namespace Menu_Helpers;
2274 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2276 _automation_tracks[param] = track;
2278 /* existing state overrides "show" argument */
2279 string s = track->gui_property ("visible");
2281 show = string_is_affirmative (s);
2284 /* this might or might not change the visibility status, so don't rely on it */
2285 track->set_marked_for_display (show);
2287 if (show && !no_redraw) {
2291 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2292 /* MIDI-related parameters are always in the menu, there's no
2293 reason to rebuild the menu just because we added a automation
2294 lane for one of them. But if we add a non-MIDI automation
2295 lane, then we need to invalidate the display menu.
2297 delete display_menu;
2303 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2305 boost::shared_ptr<Processor> processor (p.lock ());
2307 if (!processor || !processor->display_to_user ()) {
2311 /* we use this override to veto the Amp processor from the plugin menu,
2312 as its automation lane can be accessed using the special "Fader" menu
2316 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2320 using namespace Menu_Helpers;
2321 ProcessorAutomationInfo *rai;
2322 list<ProcessorAutomationInfo*>::iterator x;
2324 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2326 if (automatable.empty()) {
2330 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2331 if ((*x)->processor == processor) {
2336 if (x == processor_automation.end()) {
2337 rai = new ProcessorAutomationInfo (processor);
2338 processor_automation.push_back (rai);
2343 /* any older menu was deleted at the top of processors_changed()
2344 when we cleared the subplugin menu.
2347 rai->menu = manage (new Menu);
2348 MenuList& items = rai->menu->items();
2349 rai->menu->set_name ("ArdourContextMenu");
2353 std::set<Evoral::Parameter> has_visible_automation;
2354 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2356 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2358 ProcessorAutomationNode* pan;
2359 Gtk::CheckMenuItem* mitem;
2361 string name = processor->describe_parameter (*i);
2363 if (name == X_("hidden")) {
2367 items.push_back (CheckMenuElem (name));
2368 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2370 _subplugin_menu_map[*i] = mitem;
2372 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2373 mitem->set_active(true);
2376 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2380 pan = new ProcessorAutomationNode (*i, mitem, *this);
2382 rai->lines.push_back (pan);
2386 pan->menu_item = mitem;
2390 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2393 if (items.size() == 0) {
2397 /* add the menu for this processor, because the subplugin
2398 menu is always cleared at the top of processors_changed().
2399 this is the result of some poor design in gtkmm and/or
2403 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2408 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2409 RouteTimeAxisView::ProcessorAutomationNode* pan)
2411 bool showit = pan->menu_item->get_active();
2412 bool redraw = false;
2414 if (pan->view == 0 && showit) {
2415 add_processor_automation_curve (rai->processor, pan->what);
2419 if (pan->view && pan->view->set_marked_for_display (showit)) {
2423 if (redraw && !no_redraw) {
2429 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2431 if (c.type == RouteProcessorChange::MeterPointChange) {
2432 /* nothing to do if only the meter point has changed */
2436 using namespace Menu_Helpers;
2438 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2439 (*i)->valid = false;
2442 setup_processor_menu_and_curves ();
2444 bool deleted_processor_automation = false;
2446 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2448 list<ProcessorAutomationInfo*>::iterator tmp;
2456 processor_automation.erase (i);
2457 deleted_processor_automation = true;
2464 if (deleted_processor_automation && !no_redraw) {
2469 boost::shared_ptr<AutomationLine>
2470 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2472 ProcessorAutomationNode* pan;
2474 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2480 return boost::shared_ptr<AutomationLine>();
2484 RouteTimeAxisView::reset_processor_automation_curves ()
2486 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2492 RouteTimeAxisView::can_edit_name () const
2494 /* we do not allow track name changes if it is record enabled
2496 return !_route->record_enabled();
2500 RouteTimeAxisView::blink_rec_display (bool onoff)
2502 RouteUI::blink_rec_display (onoff);
2506 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2508 if (_ignore_set_layer_display) {
2512 if (apply_to_selection) {
2513 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2517 _view->set_layer_display (d);
2520 set_gui_property (X_("layer-display"), enum_2_string (d));
2525 RouteTimeAxisView::layer_display () const
2528 return _view->layer_display ();
2531 /* we don't know, since we don't have a _view, so just return something */
2537 boost::shared_ptr<AutomationTimeAxisView>
2538 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2540 AutomationTracks::iterator i = _automation_tracks.find(param);
2541 if (i != _automation_tracks.end()) {
2544 return boost::shared_ptr<AutomationTimeAxisView>();
2549 RouteTimeAxisView::fast_update ()
2551 gm.get_level_meter().update_meters ();
2555 RouteTimeAxisView::hide_meter ()
2558 gm.get_level_meter().hide_meters ();
2562 RouteTimeAxisView::show_meter ()
2568 RouteTimeAxisView::reset_meter ()
2570 if (UIConfiguration::instance().get_show_track_meters()) {
2571 int meter_width = 3;
2572 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2575 gm.get_level_meter().setup_meters (height - 9, meter_width);
2582 RouteTimeAxisView::clear_meter ()
2584 gm.get_level_meter().clear_meters ();
2588 RouteTimeAxisView::meter_changed ()
2590 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2592 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2595 // reset peak when meter point changes
2596 gm.reset_peak_display();
2600 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2603 if (_route && !no_redraw) {
2609 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2611 using namespace Menu_Helpers;
2613 if (!_underlay_streams.empty()) {
2614 MenuList& parent_items = parent_menu->items();
2615 Menu* gs_menu = manage (new Menu);
2616 gs_menu->set_name ("ArdourContextMenu");
2617 MenuList& gs_items = gs_menu->items();
2619 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2621 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2622 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2623 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2629 RouteTimeAxisView::set_underlay_state()
2631 if (!underlay_xml_node) {
2635 XMLNodeList nlist = underlay_xml_node->children();
2636 XMLNodeConstIterator niter;
2637 XMLNode *child_node;
2639 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2640 child_node = *niter;
2642 if (child_node->name() != "Underlay") {
2646 XMLProperty const * prop = child_node->property ("id");
2648 PBD::ID id (prop->value());
2650 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2653 add_underlay(v->view(), false);
2662 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2668 RouteTimeAxisView& other = v->trackview();
2670 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2671 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2672 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2673 abort(); /*NOTREACHED*/
2676 _underlay_streams.push_back(v);
2677 other._underlay_mirrors.push_back(this);
2679 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2681 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2683 if (!underlay_xml_node) {
2684 underlay_xml_node = xml_node->add_child("Underlays");
2687 XMLNode* node = underlay_xml_node->add_child("Underlay");
2688 XMLProperty const * prop = node->add_property("id");
2689 prop->set_value(v->trackview().route()->id().to_s());
2696 RouteTimeAxisView::remove_underlay (StreamView* v)
2702 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2703 RouteTimeAxisView& other = v->trackview();
2705 if (it != _underlay_streams.end()) {
2706 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2708 if (gm == other._underlay_mirrors.end()) {
2709 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2710 abort(); /*NOTREACHED*/
2713 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2715 _underlay_streams.erase(it);
2716 other._underlay_mirrors.erase(gm);
2718 if (underlay_xml_node) {
2719 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2725 RouteTimeAxisView::set_button_names ()
2727 if (_route && _route->solo_safe()) {
2728 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2730 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2732 if (Config->get_solo_control_is_listen_control()) {
2733 switch (Config->get_listen_position()) {
2734 case AfterFaderListen:
2735 solo_button->set_text (S_("AfterFader|A"));
2736 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2738 case PreFaderListen:
2739 solo_button->set_text (S_("PreFader|P"));
2740 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2744 solo_button->set_text (S_("Solo|S"));
2745 set_tooltip (*solo_button, _("Solo"));
2747 mute_button->set_text (S_("Mute|M"));
2751 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2753 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2754 if (i != _main_automation_menu_map.end()) {
2758 i = _subplugin_menu_map.find (param);
2759 if (i != _subplugin_menu_map.end()) {
2767 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2769 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2771 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2775 gain_track.reset (new AutomationTimeAxisView (_session,
2776 _route, _route->amp(), c, param,
2781 _route->amp()->describe_parameter(param)));
2784 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2787 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2791 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2793 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2794 if (!c || ! _route->trim()->active()) {
2798 trim_track.reset (new AutomationTimeAxisView (_session,
2799 _route, _route->trim(), c, param,
2804 _route->trim()->describe_parameter(param)));
2807 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2810 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2814 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2816 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2818 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2822 mute_track.reset (new AutomationTimeAxisView (_session,
2823 _route, _route, c, param,
2828 _route->describe_parameter(param)));
2831 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2834 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2838 void add_region_to_list (RegionView* rv, RegionList* l)
2840 l->push_back (rv->region());
2844 RouteTimeAxisView::combine_regions ()
2846 /* as of may 2011, we do not offer uncombine for MIDI tracks
2849 if (!is_audio_track()) {
2857 RegionList selected_regions;
2858 boost::shared_ptr<Playlist> playlist = track()->playlist();
2860 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2862 if (selected_regions.size() < 2) {
2866 playlist->clear_changes ();
2867 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2869 _session->add_command (new StatefulDiffCommand (playlist));
2870 /* make the new region be selected */
2872 return _view->find_view (compound_region);
2876 RouteTimeAxisView::uncombine_regions ()
2878 /* as of may 2011, we do not offer uncombine for MIDI tracks
2880 if (!is_audio_track()) {
2888 RegionList selected_regions;
2889 boost::shared_ptr<Playlist> playlist = track()->playlist();
2891 /* have to grab selected regions first because the uncombine is going
2892 * to change that in the middle of the list traverse
2895 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2897 playlist->clear_changes ();
2899 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2900 playlist->uncombine (*i);
2903 _session->add_command (new StatefulDiffCommand (playlist));
2907 RouteTimeAxisView::state_id() const
2909 return string_compose ("rtav %1", _route->id().to_s());
2914 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2916 TimeAxisView::remove_child (c);
2918 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2920 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2921 if (i->second == a) {
2922 _automation_tracks.erase (i);